ai: перевод абузных постов и саммари подборки (bot buttons, /sum, /tr, /api/summary, cache)
This commit is contained in:
137
omni/bot.py
137
omni/bot.py
@@ -14,7 +14,7 @@
|
||||
/auto [n] автодобавление новых фидов
|
||||
/sub [@канал] подписать чат/канал на новые абузы
|
||||
/unsub [@канал] отписаться
|
||||
/post [@канал] опубликовать абузы сейчас
|
||||
/post [@канал] опубликовать абузы сейчас\n /sum [тег] ИИ-саммари подборки\n /tr <id|слово> перевод записи
|
||||
/stats цифры
|
||||
"""
|
||||
from __future__ import annotations
|
||||
@@ -28,6 +28,7 @@ import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from . import abuse as abuse_mod
|
||||
from . import ai as ai_mod
|
||||
from . import discover as discover_mod
|
||||
from . import feeds, net, source_state, sources, store
|
||||
|
||||
@@ -189,6 +190,9 @@ KB_MAIN = {"inline_keyboard": [
|
||||
[{"text": "Подписка: вкл", "callback_data": "sub:on"}, {"text": "выкл", "callback_data": "sub:off"}],
|
||||
[{"text": "Формат: кратко", "callback_data": "fmt:short"},
|
||||
{"text": "полно", "callback_data": "fmt:full"}],
|
||||
[{"text": "ИИ-саммари", "callback_data": "ai:sum"},
|
||||
{"text": "Перевод: вкл/выкл", "callback_data": "ai:tr"}],
|
||||
[{"text": "Саммари в подписке: вкл/выкл", "callback_data": "ai:sm"}],
|
||||
[{"text": "Пост в канал", "callback_data": "post:now"}],
|
||||
]}
|
||||
|
||||
@@ -275,8 +279,11 @@ def _scan_safe(limit=10, tag=None, window=6000):
|
||||
return d
|
||||
|
||||
|
||||
def _line(it, fmt="short"):
|
||||
title = _esc((it.get("title") or "")[:150])
|
||||
def _line(it, fmt="short", tr=False):
|
||||
raw = (it.get("title") or "")[:150]
|
||||
if tr:
|
||||
raw = ai_mod.translate(raw) or raw
|
||||
title = _esc(raw)
|
||||
src = _esc(it.get("source") or "")
|
||||
tags = _esc(",".join(it.get("tags") or []))
|
||||
head = "<b>%s</b>\n<code>%s</code> · %s" % (title, src, tags)
|
||||
@@ -287,17 +294,111 @@ def _line(it, fmt="short"):
|
||||
return head
|
||||
|
||||
|
||||
def abuse_text(tag=None, limit=6, fmt="short"):
|
||||
AI_SQL = "CREATE TABLE IF NOT EXISTS bot_ai (chat_id INTEGER PRIMARY KEY, tr INTEGER DEFAULT 0, sm INTEGER DEFAULT 0)"
|
||||
|
||||
|
||||
def ai_prefs(chat_id):
|
||||
c = _conn()
|
||||
c.execute(AI_SQL)
|
||||
c.commit()
|
||||
r = c.execute("SELECT tr, sm FROM bot_ai WHERE chat_id=?", (chat_id,)).fetchone()
|
||||
c.close()
|
||||
return (r[0], r[1]) if r else (0, 0)
|
||||
|
||||
|
||||
def ai_set(chat_id, field, val):
|
||||
c = _conn()
|
||||
c.execute(AI_SQL)
|
||||
c.execute("INSERT INTO bot_ai(chat_id, tr, sm) VALUES(?,0,0) ON CONFLICT(chat_id) DO NOTHING", (chat_id,))
|
||||
c.execute("UPDATE bot_ai SET %s=? WHERE chat_id=?" % ("tr" if field == "tr" else "sm"), (val, chat_id))
|
||||
c.commit()
|
||||
c.close()
|
||||
|
||||
|
||||
def ai_scan_summary(limit=12, tag=None):
|
||||
"""ИИ-саммари свежих абузов. None если ИИ недоступен."""
|
||||
d = _scan_safe(limit=limit, tag=tag)
|
||||
items = d.get("items") or []
|
||||
if not items:
|
||||
return None, 0
|
||||
posts = ["%s — %s" % (i.get("title") or "", i.get("source") or "") for i in items]
|
||||
return ai_mod.summarize(posts, tag=tag), len(items)
|
||||
|
||||
|
||||
def cmd_sum(chat_id, arg):
|
||||
tag = arg.strip().split()[0] if arg.strip() else None
|
||||
if not ai_mod.available():
|
||||
return _send(chat_id, "ИИ не подключён: задай OMNI_AI_KEY (или ключ reformboss в конфиге Hermes)", reply_markup=KB_MAIN)
|
||||
txt, n = ai_scan_summary(limit=14, tag=tag)
|
||||
if not txt:
|
||||
return _send(chat_id, "саммари не собралось", reply_markup=KB_MAIN)
|
||||
_send(chat_id, "<b>саммари · %s · %d записей</b>\n\n%s" % (tag or "все", n, _esc(txt)), reply_markup=KB_MAIN)
|
||||
|
||||
|
||||
def cmd_tr(chat_id, arg):
|
||||
"""Перевод записи: /tr <id|слово>."""
|
||||
q = arg.strip()
|
||||
if not q:
|
||||
return _send(chat_id, "что перевести? /tr 4933 или /tr muse", reply_markup=KB_MAIN)
|
||||
if not ai_mod.available():
|
||||
return _send(chat_id, "ИИ не подключён: задай OMNI_AI_KEY", reply_markup=KB_MAIN)
|
||||
title = text = src = url = None
|
||||
if q.isdigit():
|
||||
import sqlite3
|
||||
from .config import DB
|
||||
c = sqlite3.connect(str(DB), timeout=20)
|
||||
r = c.execute("SELECT title, text, source, url FROM items WHERE id=?", (int(q),)).fetchone()
|
||||
c.close()
|
||||
if r:
|
||||
title, text, src, url = r[0], r[1] or "", r[2], r[3]
|
||||
if title is None:
|
||||
import sqlite3
|
||||
from .config import DB
|
||||
c = sqlite3.connect(str(DB), timeout=20)
|
||||
r = c.execute("SELECT title, text, source, url FROM items WHERE title LIKE ? "
|
||||
"ORDER BY id DESC LIMIT 1", ("%" + q + "%",)).fetchone()
|
||||
c.close()
|
||||
if r:
|
||||
title, text, src, url = r[0], r[1] or "", r[2], r[3]
|
||||
if title is None:
|
||||
rows = store.search(q, limit=1)
|
||||
if rows:
|
||||
r = rows[0]
|
||||
if isinstance(r, dict):
|
||||
title, src, url = r.get("title"), r.get("source"), r.get("url")
|
||||
text = r.get("text") or ""
|
||||
else:
|
||||
src, title, url = r[2], r[3], r[5]
|
||||
text = ""
|
||||
if not title:
|
||||
return _send(chat_id, "не нашёл запись", reply_markup=KB_MAIN)
|
||||
payload = title if len(title) > 120 else (title + "\n" + (text or "")[:600]).strip()
|
||||
ru = ai_mod.translate(payload)
|
||||
if not ru:
|
||||
return _send(chat_id, "перевод не получился, попробуй ещё раз", reply_markup=KB_MAIN)
|
||||
out = "<b>%s</b>\n<code>%s</code>\n\n%s" % (_esc(title[:150]), _esc(src), _esc(ru))
|
||||
if url:
|
||||
out += '\n<a href="%s">источник</a>' % _esc(url)
|
||||
_send(chat_id, out, reply_markup=KB_MAIN)
|
||||
|
||||
|
||||
def abuse_text(tag=None, limit=6, fmt="short", tr=False):
|
||||
d = _scan_safe(limit=limit, tag=tag)
|
||||
if not d["items"]:
|
||||
return None
|
||||
head = "абузы · %s · %d" % (tag or "все", d["total"])
|
||||
return head + "\n\n" + "\n\n".join(_line(i, fmt) for i in d["items"])
|
||||
if tr:
|
||||
head += " · перевод"
|
||||
lines = []
|
||||
for n, i in enumerate(d["items"]):
|
||||
lines.append(_line(i, fmt, tr and n < 5))
|
||||
return head + "\n\n" + "\n\n".join(lines)
|
||||
|
||||
|
||||
def cmd_abuse(chat_id, arg):
|
||||
tag = arg.strip().split()[0] if arg.strip() else None
|
||||
txt = abuse_text(tag, fmt=prefs_get(chat_id))
|
||||
tr = bool(ai_prefs(chat_id)[0]) and ai_mod.available()
|
||||
txt = abuse_text(tag, fmt=prefs_get(chat_id), tr=tr)
|
||||
_send(chat_id, txt or "абузов не видно", reply_markup=KB_MAIN)
|
||||
|
||||
|
||||
@@ -356,7 +457,17 @@ def push_new(per_chat=5, window=2000):
|
||||
continue
|
||||
chunk = fresh[-per_chat:]
|
||||
fmt = prefs_get(target) if isinstance(target, int) else "short"
|
||||
text = ("<b>новые абузы · %d</b>\n\n" % len(fresh)) + "\n\n".join(_line(i, fmt) for i in chunk)
|
||||
tr = sm = 0
|
||||
if isinstance(target, int):
|
||||
tr, sm = ai_prefs(target)
|
||||
tr = bool(tr) and ai_mod.available()
|
||||
pre = ""
|
||||
if sm and ai_mod.available():
|
||||
posts = ["%s — %s" % (i.get("title") or "", i.get("source") or "") for i in chunk]
|
||||
summary = ai_mod.summarize(posts)
|
||||
if summary:
|
||||
pre = "<b>саммари</b>\n%s\n\n" % _esc(summary)
|
||||
text = pre + ("<b>новые абузы · %d</b>\n\n" % len(fresh)) + "\n\n".join(_line(i, fmt, tr) for i in chunk)
|
||||
try:
|
||||
_send(target, text[:MAX_LEN])
|
||||
sent += 1
|
||||
@@ -382,7 +493,7 @@ CMDS = {"/start": cmd_start, "/help": cmd_start, "/new": cmd_new, "/abuse": cmd_
|
||||
"/find": cmd_find, "/src": cmd_src, "/add": cmd_add, "/run": cmd_run,
|
||||
"/auto": cmd_auto, "/stats": cmd_stats,
|
||||
"/sub": cmd_sub, "/unsub": cmd_unsub, "/subs": cmd_subs,
|
||||
"/post": cmd_test}
|
||||
"/post": cmd_test, "/sum": cmd_sum, "/tr": cmd_tr}
|
||||
|
||||
|
||||
def handle(update):
|
||||
@@ -440,6 +551,16 @@ def handle_cb(cb):
|
||||
return _send(chat_id, "формат: %s" % ("кратко" if data[4:] == "short" else "полно"), reply_markup=KB_MAIN)
|
||||
if data == "post:now":
|
||||
return cmd_test(chat_id, "")
|
||||
if data == "ai:sum":
|
||||
return cmd_sum(chat_id, "")
|
||||
if data == "ai:tr":
|
||||
tr, _sm = ai_prefs(chat_id)
|
||||
ai_set(chat_id, "tr", 0 if tr else 1)
|
||||
return _send(chat_id, "перевод абузов: %s" % ("выкл" if tr else "вкл"), reply_markup=KB_MAIN)
|
||||
if data == "ai:sm":
|
||||
_tr, sm = ai_prefs(chat_id)
|
||||
ai_set(chat_id, "sm", 0 if sm else 1)
|
||||
return _send(chat_id, "саммари в подписке: %s" % ("выкл" if sm else "вкл"), reply_markup=KB_MAIN)
|
||||
|
||||
|
||||
def run_forever():
|
||||
|
||||
Reference in New Issue
Block a user