store: обновлять тело записи при повторной отдаче того же url (панели теряли актуальный free_count); tools/push_top.py — топ находок в бота
This commit is contained in:
@@ -110,7 +110,15 @@ def add(items):
|
|||||||
dup += 1
|
dup += 1
|
||||||
continue
|
continue
|
||||||
u = (getattr(it, "url", "") or "").strip()
|
u = (getattr(it, "url", "") or "").strip()
|
||||||
if u and c.execute("SELECT 1 FROM items WHERE url = ? LIMIT 1", (u,)).fetchone():
|
row = c.execute("SELECT id FROM items WHERE url = ? LIMIT 1", (u,)).fetchone() if u else None
|
||||||
|
if row:
|
||||||
|
# Источник переотдал тот же url с новым телом (пост отредактировали,
|
||||||
|
# у панели изменилось число бесплатных моделей) — обновляем текст,
|
||||||
|
# иначе в выдаче висят устаревшие цифры. FTS правим вручную:
|
||||||
|
# триггеров на UPDATE в схеме нет.
|
||||||
|
c.execute("UPDATE items SET title=?, text=?, extra=?, score=?, seen=? WHERE id=?", (
|
||||||
|
it.title, it.text, json.dumps(it.extra, ensure_ascii=False)[:2000], it.score, ts, row[0]))
|
||||||
|
c.execute("UPDATE items_fts SET title=?, text=? WHERE rowid=?", (it.title, it.text, row[0]))
|
||||||
dup += 1
|
dup += 1
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|||||||
104
tools/push_top.py
Normal file
104
tools/push_top.py
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
"""Отправить топ находок в Telegram через бота проекта.
|
||||||
|
|
||||||
|
Запуск: python tools/push_top.py [--limit 12] [--chat <id>] [--dry]
|
||||||
|
Токен берётся из .bot_token, получатель — --chat или OMNI_CHAT (дефолт 7448683285).
|
||||||
|
Ничего не постит в каналы: только личное сообщение получателю.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import html
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import sys
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
sys.path.insert(0, str(ROOT))
|
||||||
|
|
||||||
|
from omni import abuse # noqa: E402
|
||||||
|
|
||||||
|
TAG_RU = {"promo": "промо", "trial": "триал", "keys": "ключи", "leak": "слив",
|
||||||
|
"resale": "перепродажа", "accs": "аккаунты", "credits": "кредиты",
|
||||||
|
"invite": "инвайты", "ltd": "lifetime", "freeapi": "free-API",
|
||||||
|
"combo": "комбо", "tool": "тулза"}
|
||||||
|
|
||||||
|
|
||||||
|
def token() -> str:
|
||||||
|
p = ROOT / ".bot_token"
|
||||||
|
t = p.read_text(encoding="utf-8").strip() if p.exists() else os.environ.get("OMNI_BOT_TOKEN", "")
|
||||||
|
if not t:
|
||||||
|
raise SystemExit("нет .bot_token")
|
||||||
|
return t
|
||||||
|
|
||||||
|
|
||||||
|
def counts():
|
||||||
|
c = sqlite3.connect(str(ROOT / "data" / "omni.sqlite3"))
|
||||||
|
items = c.execute("SELECT COUNT(*) FROM items").fetchone()[0]
|
||||||
|
ab = c.execute("SELECT COUNT(*) FROM abuse_idx").fetchone()[0]
|
||||||
|
uniq = c.execute("SELECT COUNT(DISTINCT sig) FROM abuse_idx").fetchone()[0]
|
||||||
|
return items, ab, uniq
|
||||||
|
|
||||||
|
|
||||||
|
def build(limit: int) -> str:
|
||||||
|
res = abuse.scan(limit=max(limit * 4, 60))
|
||||||
|
picks, seen = [], set()
|
||||||
|
for it in res["items"]:
|
||||||
|
key = it.get("sig") or it["id"]
|
||||||
|
if key in seen:
|
||||||
|
continue
|
||||||
|
seen.add(key)
|
||||||
|
picks.append(it)
|
||||||
|
if len(picks) >= limit:
|
||||||
|
break
|
||||||
|
items, ab, uniq = counts()
|
||||||
|
out = ["<b>OMNI · ТОП НАХОДОК</b>",
|
||||||
|
f"в базе {items:,} записей · находок {ab:,} · уникальных {uniq:,}".replace(",", " "),
|
||||||
|
""]
|
||||||
|
for i, it in enumerate(picks, 1):
|
||||||
|
raw = it.get("tags") or []
|
||||||
|
raw = raw.split(",") if isinstance(raw, str) else list(raw)
|
||||||
|
tags = ", ".join(TAG_RU.get(t, t) for t in raw if t)
|
||||||
|
title = html.escape((it.get("title") or it.get("text") or "")[:110].strip())
|
||||||
|
tags_s = f" · <i>{html.escape(tags)}</i>" if tags else ""
|
||||||
|
dup = f" · копий {it['dups']}" if it.get("dups") else ""
|
||||||
|
link = it.get("url") or ""
|
||||||
|
head = f"{i}. <b>{it.get('abuse_score', 0)}</b>{tags_s}{dup}"
|
||||||
|
out.append(head)
|
||||||
|
out.append(title)
|
||||||
|
if link:
|
||||||
|
out.append(f'<a href="{html.escape(link)}">{html.escape(link[:78])}</a>')
|
||||||
|
out.append("")
|
||||||
|
return "\n".join(out)[:3900]
|
||||||
|
|
||||||
|
|
||||||
|
def send(text: str, chat: str) -> dict:
|
||||||
|
data = urllib.parse.urlencode({"chat_id": chat, "text": text, "parse_mode": "HTML",
|
||||||
|
"disable_web_page_preview": "true"}).encode()
|
||||||
|
url = f"https://api.telegram.org/bot{token()}/sendMessage"
|
||||||
|
with urllib.request.urlopen(urllib.request.Request(url, data=data), timeout=40) as r:
|
||||||
|
return json.load(r)
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--limit", type=int, default=12)
|
||||||
|
ap.add_argument("--chat", default=os.environ.get("OMNI_CHAT", "7448683285"))
|
||||||
|
ap.add_argument("--dry", action="store_true")
|
||||||
|
a = ap.parse_args()
|
||||||
|
text = build(a.limit)
|
||||||
|
if a.dry:
|
||||||
|
print(text)
|
||||||
|
return 0
|
||||||
|
r = send(text, a.chat)
|
||||||
|
res = r.get("result") or {}
|
||||||
|
print("ok", r.get("ok"), "| message_id", res.get("message_id"),
|
||||||
|
"| chat", (res.get("chat") or {}).get("id"), "| длина", len(text))
|
||||||
|
return 0 if r.get("ok") else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user