92 lines
4.6 KiB
Python
92 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""Типизация записей: не «категория источника», а тип самого поста.
|
||
|
||
Категория отвечает «откуда», тип — «что именно»: слив, триал, софт, промо,
|
||
перепродажа, CVE, релиз, гайд, датасет, модель, вакансия, цена, вопрос, новость.
|
||
Одна запись может иметь несколько типов, порядок — по весу совпадений.
|
||
"""
|
||
import re
|
||
|
||
# (key, label, weight, [regex, ...])
|
||
RULES = [
|
||
("leak", "слив", 10, [
|
||
r"\bслив\w*", r"утечк\w+", r"\bleak(ed|s)?\b", r"\bdump(ed)?\b", r"база\s+(данных|слит)",
|
||
r"\bcombo(list)?\b", r"stealer\s*log", r"\bdb\s+dump\b", r"пасс\w*\s+от\s+",
|
||
]),
|
||
("trial", "триал", 9, [
|
||
r"\btrial\b", r"триал\w*", r"пробн\w+\s+период", r"free\s+(credits?|tier|plan)",
|
||
r"бесплатн\w+\s+(кредит|токен|доступ|период|тариф)", r"\bfreebies?\b", r"щедр\w+\s+лимит",
|
||
r"\$\d+\s*(free|бесплат)", r"no\s+credit\s+card",
|
||
]),
|
||
("tool", "софт", 8, [
|
||
r"\bсофт\w*", r"\btool(s|kit)?\b", r"скрипт\w*", r"\bparser\b", r"\bчекер\b", r"\bбот\w*",
|
||
r"\bcrawler\b", r"\bscraper\b", r"\bautomation\b", r"автоматизац\w+", r"\bcli\b",
|
||
r"\bsdk\b", r"open[\s-]?source\s+(tool|project)",
|
||
]),
|
||
("promo", "промо", 7, [
|
||
r"промокод\w*", r"\bpromo\w*", r"\bcoupon\b", r"скидк\w+", r"\bdiscount\b", r"giveaway",
|
||
r"раздач\w+", r"\bairdrop\b", r"бонус\w*\s+(код|за\s+регистрац)", r"-?\d{1,2}%\s*off",
|
||
]),
|
||
("resale", "перепродажа", 7, [
|
||
r"\bпродам\b", r"\bпродаж\w+", r"\bresell\w*", r"\bв\s+аренду\b", r"\bкуплю\b",
|
||
r"\bsell(ing)?\s+(account|key|access)", r"перепрод\w+", r"\bоптом\b", r"\bв\s+наличии\b.*\$",
|
||
]),
|
||
("cve", "уязвимость", 10, [
|
||
r"\bCVE-\d{4}-\d{3,7}\b", r"\bvulnerab\w+", r"уязвим\w+", r"\bRCE\b", r"\bXSS\b",
|
||
r"\bSQLi?\b", r"\bexploit\w*", r"\b0day\b", r"\bpatch\w*\s+tuesday", r"security\s+advisory",
|
||
]),
|
||
("release", "релиз", 6, [
|
||
r"\brelease[sd]?\b", r"\bрелиз\w*", r"\bchangelog\b", r"\bv\d+\.\d+(\.\d+)?\b",
|
||
r"вышл\w+\s+версия", r"\bnow\s+available\b", r"\bGA\b", r"\bpreview\s+build\b",
|
||
]),
|
||
("guide", "гайд", 5, [
|
||
r"\bгайд\w*", r"\btutorial\b", r"\bhow[\s-]to\b", r"инструкц\w+", r"\bруководств\w+",
|
||
r"\bкак\s+(сделать|настроить|поднять|получить)", r"\bwalkthrough\b", r"\bcheat\s*sheet\b",
|
||
]),
|
||
("dataset", "датасет", 6, [
|
||
r"\bdataset\w*", r"датасет\w*", r"\bcorpus\b", r"\bbenchmark\s+suite\b", r"\bannotated\b",
|
||
]),
|
||
("model", "модель", 5, [
|
||
r"\bweights\b", r"\bcheckpoint\w*", r"\.gguf\b", r"\.safetensors\b", r"\bllm\b",
|
||
r"\bмодел\w+\s+(вышла|обучен| weights)", r"\bquantiz\w+", r"\bfine-?tun\w+",
|
||
]),
|
||
("job", "вакансия", 6, [
|
||
r"\bваканси\w+", r"\bhiring\b", r"\bjob\s+(offer|post)", r"\bremote\b.*\$",
|
||
r"\bsalary\b", r"\$\d+\s*/\s*(hr|hour)", r"\bищу\s+работу\b", r"\bfor\s+hire\b",
|
||
]),
|
||
("price", "цена", 4, [
|
||
r"\bцен[аы]\b", r"\bpricing\b", r"\bтариф\w*", r"\$\d", r"\d+\s*₽", r"\d+\s*€",
|
||
r"\bper\s+(month|year|1m)\b", r"/\s*1m\s+tokens",
|
||
]),
|
||
("question", "вопрос", 3, [
|
||
r"\?\s*$", r"^\s*(как|почему|что|где|кто|можно\s+ли)\b", r"\bhelp\b", r"\bподскажите\b",
|
||
]),
|
||
]
|
||
|
||
COMPILED = [(k, l, w, [re.compile(p, re.I | re.U) for p in pats]) for k, l, w, pats in RULES]
|
||
LABELS = {k: l for k, l, _, _ in RULES}
|
||
LABELS["news"] = "новость"
|
||
KEYS = [k for k, _, _, _ in RULES]
|
||
|
||
|
||
def classify(title, text, limit=3):
|
||
"""Список типов записи по убыванию веса. Если ничего не сработало — ['news']."""
|
||
t = (title or "") + "\n" + (text or "")[:2000]
|
||
if not t.strip():
|
||
return ["news"]
|
||
hits = []
|
||
for key, label, w, pats in COMPILED:
|
||
n = 0
|
||
for p in pats:
|
||
if p.search(t):
|
||
n += 1
|
||
if n:
|
||
hits.append((w + n, key))
|
||
if not hits:
|
||
return ["news"]
|
||
hits.sort(reverse=True)
|
||
return [k for _, k in hits[:limit]]
|
||
|
||
|
||
def labels(keys):
|
||
return [LABELS.get(k, k) for k in keys or []] |