File size: 3,625 Bytes
e9015b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
098b6a4
e9015b1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
from __future__ import annotations

from dataclasses import dataclass
from typing import Iterable


GENRE_MODERN = "modern"
GENRE_CLASSICAL = "classical"
GENRE_MIXED_GUARD = "mixed_guard"
GENRE_UNKNOWN_GUARD = "unknown_guard"


@dataclass(frozen=True)
class GenreDecision:
    route: str
    classical_score: int
    modern_score: int
    evidence: tuple[str, ...]
    reason: str

    @property
    def is_modern(self) -> bool:
        return self.route == GENRE_MODERN

    @property
    def is_classical(self) -> bool:
        return self.route == GENRE_CLASSICAL


BOOK_MODERN_HINTS = ("没钱修什么仙",)

HARD_CLASSICAL_SIGNALS = (
    "修士", "修真", "修仙", "元婴", "金丹", "筑基", "真君", "法宝", "丹药", "灵气",
    "仙人", "仙子", "剑修", "渡劫", "结丹", "化神", "真人", "道君", "宗门", "灵根",
    "本座", "贫道", "道友", "天劫", "神识", "真元", "灵石", "符箓", "阵法", "飞剑",
    "皇帝", "王爷", "陛下", "皇后", "太后", "公主", "太子", "侯爷", "世子",
    "江湖", "武林", "内力", "剑客", "掌门", "少侠", "师尊", "师父", "前辈",
    "师兄", "师弟", "师姐", "师妹", "师叔", "师伯", "师祖", "徒儿", "道友",
)

HARD_MODERN_SIGNALS = (
    "公司", "大学", "高中", "初中", "小学", "学校", "老师", "班主任", "同学", "学生",
    "课堂", "上课", "课程", "高一", "高二", "高三", "面试", "招生", "学费", "补习班",
    "电话", "手机", "电脑", "网络", "汽车", "地铁", "飞机", "酒店", "警察", "医院",
    "护士", "短信", "微信", "视频", "直播", "电视", "银行", "信用卡", "互联网",
    "程序", "软件", "老板", "经理", "项目", "咖啡", "贷款", "借款", "逾期",
    "财务公司", "平台", "总裁", "董事长", "办公室",
)


def _joined_sources(rows_or_text: Iterable[tuple[int, str, str]] | str) -> str:
    if isinstance(rows_or_text, str):
        return rows_or_text
    return "\n".join(str(source) for _, source, _ in rows_or_text)


def _hits(text: str, terms: tuple[str, ...]) -> tuple[str, ...]:
    return tuple(term for term in terms if term in text)


def classify_genre(rows_or_text: Iterable[tuple[int, str, str]] | str) -> GenreDecision:
    text = _joined_sources(rows_or_text)
    book_hits = _hits(text, BOOK_MODERN_HINTS)
    if book_hits:
        return GenreDecision(
            route=GENRE_MODERN,
            classical_score=0,
            modern_score=len(book_hits),
            evidence=book_hits,
            reason="book_modern_hint",
        )

    classical_hits = _hits(text, HARD_CLASSICAL_SIGNALS)
    modern_hits = _hits(text, HARD_MODERN_SIGNALS)
    classical_score = len(classical_hits)
    modern_score = len(modern_hits)
    evidence = (*classical_hits[:8], *modern_hits[:8])

    if classical_score and not modern_score:
        return GenreDecision(GENRE_CLASSICAL, classical_score, modern_score, evidence, "classical_signal")
    if modern_score and not classical_score:
        return GenreDecision(GENRE_MODERN, classical_score, modern_score, evidence, "modern_signal")
    if classical_score and modern_score:
        return GenreDecision(GENRE_MIXED_GUARD, classical_score, modern_score, evidence, "mixed_signal")
    return GenreDecision(GENRE_UNKNOWN_GUARD, classical_score, modern_score, evidence, "no_signal")


def v9_route_for_decision(decision: GenreDecision) -> str:
    if decision.route == GENRE_MODERN:
        return "modern_school"
    if decision.route == GENRE_UNKNOWN_GUARD:
        return "unknown_copy_guard"
    return "xianxia_copy_guard"