From 46e59d3eb61d5e60a1479a05954c465f80f7e410 Mon Sep 17 00:00:00 2001 From: AB Date: Sun, 24 May 2020 21:14:54 +0300 Subject: [PATCH] It works --- .gitignore | 1 + data.sqlite | Bin 28672 -> 0 bytes database.py | 174 +++++++++++++++------------------------------------ main.py | 40 +++++++++--- notify.py | 52 +++++++++++++++ rutracker.py | 33 ++++++++-- scheme.sql | 3 +- 7 files changed, 163 insertions(+), 140 deletions(-) delete mode 100644 data.sqlite create mode 100644 notify.py diff --git a/.gitignore b/.gitignore index c18dd8d..5237699 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ __pycache__/ +data.sqlite diff --git a/data.sqlite b/data.sqlite deleted file mode 100644 index 90fbfc02df981457f51f56c3fe6b47d5abde6c10..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28672 zcmeI(!Ef4D90zc_A+XS7wAukhQPwx6sh}nWLqg4|Gr?@p5}H!lE~+BS2`|M8!HM6a zS$0cSX@}i**gr7sKiCdaw_SG}Jx04sJN90w$Np>}2?$N9)JwDP6VKT1;op0Feomxk z;NIO8M{>69db_q{IpvC?s>-K~DT*>iHA1ytLbT)8eo^~7|M5Jg+`N4<6?>(GFaNE) z|3mE0sfEdZCk;A4fdB*`009U<00Izz00iEqKs_9ZPfx2yA4_|)$?Ychr08xVOI6dT znXG0MS4@`ZHYQjy%6g?OxMw+aRx`h>&7HPN_q6e}Ep_zacqAT=tH(3JF#ee$*n|fM z3EIw&LE)DxHS;sG%GRpOt44L5ePOPjjpm=wvUmB=B(@z-NNYq#(>~vRHo>(}IDX@X zx*kkV>^Et-V+p?B=B+J0bT~dR<)O}`Kjs{rnL4({BJufo_33^PlUO|7lEP{@g7y!G zk3$1d6T{to6VE*uO~Q=(wL8ldin3}}C>;|Cr=FPbQ^P8EsNP>$2^w45uGO$bV?-Y< z@7lYR&=FmGu8={pyM2Bn;e5>#6O^2R_#S_3NoRLN54|HxNL#i=qL(a+!FipogtMU1 z-E+1mfNWBP?se6Fl@+9p&S-UeqN(GXY9u~8t3FK+2>0nOjTZGKFFXz<=-i)6(!Ul` z(Ny|UIKDKiDo(4;zv^AMc3Zl^vDJ68wEmvIwtvt6XEQ}rja^shfdT;tKmY;|fB*y_ z009U<00I#BzY82aP$y?LK9~uGVposHKU&ZWxm;c=8RfEmD_brax#D86py`IGX{Btb zP`;&`=2G4;i&}XppIOK+YFZ(qXEg8gpa0sJe(_Bmq&cuq7J@_7k2i+1?Hd z4$~EWz^Yt&E_+BlDfZ~0S=?Z2bOW#LZQ8VkXY;J&h%NWAXYVx*SyI#K^>mLM0SG_<0uX=z z1Rwwb2tWV=5P-l%5{Qn?O!RJH_@DpNm;O;8009U<00Izz00bZa0SG_<0uZ>s0$BfF z;N;@1K>z{}fB*y_009U<00Izz00eph-QWM8D6tceKmY;|fB*y_009U<00Izz00bcL z&Iu%*{rHbNu3Ve_q}`<7|JS)_cYf;p(RtSSt@C~7m*?Mhet-U5=U06G|ITF=6NUf; WAOHafKmY;|fB*y_009WRvA}DW3Yz8s diff --git a/database.py b/database.py index b0a955f..1eefcca 100644 --- a/database.py +++ b/database.py @@ -7,7 +7,10 @@ import sqlite3 import logging -log = logging.getLogger(__name__) +log = logging.getLogger("gaspar.%s" % __name__) + +class DBInitException(Exception): + """ Exception at DB Init """ # class DataBase create or use existent SQLite database file. It provides @@ -39,9 +42,11 @@ class DataBase: cursor.executescript(sql) except Exception as e: log.debug('Could not create scheme - %s', e) + raise DBInitException else: log.debug("Error! cannot create the database connection.") - log.info('DB created.') + raise DBInitException + log.info('DB connected.') self.close(conn) def connect(self, basefile): @@ -81,130 +86,9 @@ class DataBase: #log.debug("Close connection to %s", self.basefile) conn.close() - def add_mod(self, file_meta, author='Anonymous'): - secure_name = file_meta['secure_name'] - real_name = file_meta['real_name'] - mime = file_meta['mime'] - file_hash = file_meta['hash'] - title = file_meta['title'] - sample = file_meta['sample'] - message = file_meta['message'] - metaphone = file_meta['metaphone'] - sql = """INSERT OR IGNORE INTO - mods('secure_name', 'real_name', 'mime', 'hash', - 'author', 'title', 'sample', 'message', 'metaphone') - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""" - self.execute(sql, ( - secure_name, - real_name, - mime, - file_hash, - author, - title, - sample, - message, - metaphone, - )) - return True - - def get_mods(self, limit, offset): - sql = """SELECT - rowid, real_name, title, mime, - strftime('%s', date) as str_time, author, date, hash, secure_name - FROM mods LIMIT ?,?""" - mods = list() - result = self.execute(sql, (offset, limit)) - for mod in result: - mods.append( - { - 'id': mod[0], - 'real_name': mod[1], - 'title': mod[2], - 'mimetype': mod[3], - 'str_time': mod[4], - 'author': mod[5], - 'time': mod[6], - 'hash': mod[7], - 'secure_name': mod[8], - } - ) - return mods - - def get_mod(self, mod_id): - sql = """SELECT - rowid, real_name, secure_name, mime, - strftime('%s', date) as str_time, author, date, - hash, title, sample, message - FROM mods WHERE rowid = ?""" - result = self.execute(sql, (mod_id,)) - if result: - meta = result[0] - mod = { - 'id': meta[0], - 'real_name': meta[1], - 'secure_name': meta[2], - 'mimetype': meta[3], - 'time': meta[4], - 'author': meta[5], - 'str_time': meta[6], - 'hash': meta[7], - 'title': meta[8], - 'sample': meta[9], - 'message': meta[10], - } - else: - mod = list() - return mod - - def find_mod(self, param=None): - """ - Looking for mod dublicates. - :param param: name or hash of module to search. - :type param: string - :return: list - """ - sql = """SELECT rowid FROM mods WHERE real_name == ? OR - hash == ? ORDER BY rowid DESC LIMIT 1""" - result = self.execute(sql, (param, param)) - return result - - def search(self, query): - """ - Perform module search through the base. - """ - sql = """SELECT rowid, secure_name, title, mime, date, - strftime('%s', date) as str_time FROM mods - WHERE - secure_name LIKE ? OR - title LIKE ? OR - message LIKE ? OR - sample LIKE ?""" - query_mask = f"%{query}%" - result = self.execute(sql, tuple(query_mask for i in range(0, 4))) - log.debug(result) - return result - - def signin(self, name, password): - """ - auth client - """ - result = {"status": False, 'message': 'User is invalid.'} - sql = "SELECT name, password FROM users WHERE name = ?" - ret = self.execute(sql, (name,)) - if len(ret) == 0: - result = {'status': False, 'message': 'User doesn\'t exist'} - elif len(ret) == 1: - stored_hash = ret[0][1] - print(stored_hash, password) - print(verify_password(stored_hash, password)) - if verify_password(stored_hash, password): - result = {"status": True, 'message': 'User is valid.'} - return result - def copy_to_history(self, tor_id): sql = "SELECT * FROM torrents WHERE id = ?" attrs = self.execute(sql, (tor_id,))[0] - print(attrs) sql = """INSERT OR IGNORE INTO torrents_history( 'id', 'info_hash', @@ -215,7 +99,7 @@ class DataBase: 'tor_status', 'seeders', 'topic_title', - 'seeder_last_seen', + 'seeder_last_seen' ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )""" self.execute(sql, attrs) @@ -288,5 +172,45 @@ class DataBase: chat_instance['first_name'], chat_instance['last_name'], )) - return True + def save_alert(self, user_id, tor_id): + sql = """INSERT OR IGNORE INTO alerts( + 'user_id', + 'tor_id' + ) VALUES (?, ?)""" + self.execute(sql, ( + user_id, + tor_id + )) + + def get_alerts(self, user_id=None): + if user_id: + sql = """SELECT t.size, t.reg_time, t.topic_title, t.id, t.info_hash FROM + torrents t LEFT JOIN alerts a ON a.tor_id = t.id + WHERE a.user_id = ?""" + raw = self.execute(sql, ( + user_id, + )) + else: + sql = """SELECT t.size, t.reg_time, t.topic_title, t.id, t.info_hash FROM + torrents t LEFT JOIN alerts a ON a.tor_id = t.id GROUP BY t.id""" + raw = self.execute(sql, ()) + alerts = list() + for alert in raw: + tmp = dict() + tmp['id'] = alert[3] + tmp['reg_time'] = alert[1] + tmp['topic_title'] = alert[2] + tmp['size'] = alert[0] + tmp['info_hash'] = alert[4] + alerts.append(tmp) + return alerts + + def get_subscribers(self, tor_id): + sql = "SELECT user_id FROM alerts WHERE tor_id = ?" + subs = list() + for sub in self.execute(sql, (tor_id,)): + subs.append(sub[0]) + return subs + + diff --git a/main.py b/main.py index 99b5ed5..c76fc19 100644 --- a/main.py +++ b/main.py @@ -2,17 +2,21 @@ import os import logging from urllib import parse from rutracker import Torrent +from notify import update_watcher from datetime import datetime from database import DataBase from telegram import * from telegram.ext import Updater, MessageHandler, CommandHandler, filters logging.basicConfig( - level=logging.DEBUG, + level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s') -log = logging.getLogger(__name__) +log = logging.getLogger("gaspar.%s" % __name__) + +torrent = Torrent() def sizeof_fmt(num, suffix='B'): + num = int(num) for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: if abs(num) < 1024.0: return "%3.1f%s%s" % (num, unit, suffix) @@ -34,30 +38,48 @@ def main(): else: update.message.reply_text("Send me a URL to rutracker.org topic.") return + log.info( + "Got /add request from user [%s] %s", + update.message.chat['id'], + update.message.from_user.username) torrent = Torrent(tor_id) torrent.db.save_tor(torrent.meta) torrent.db.save_user(update.message.chat) torrent.db.save_alert(update.message.chat['id'], torrent.meta['id']) -# log.debug(torrent.is_outdated()) -# log.debug(torrent.update()) reg_time = datetime.utcfromtimestamp(int(torrent.meta['reg_time']) - ).strftime('%b-%d') + ).strftime('%b-%d-%Y') msg = f"""{torrent.meta['topic_title']} Size: {sizeof_fmt(torrent.meta['size'])} Hash: {torrent.meta['info_hash']} Updated: {reg_time}""" - log.info(msg) update.message.reply_text(msg, parse_mode='HTML') - def hello(update, context): - update.message.reply_text( - 'Hello {}'.format(update.message.from_user.first_name)) + def list_alerts(update, context): + log.info( + "Got /list request from user [%s] %s", + update.message.chat['id'], + update.message.from_user.username) + alerts = torrent.db.get_alerts(update.message.chat['id']) + if len(alerts) == 0: + update.message.reply_text("You have no configured alerts.") + return True + msg = "Configured alerts:\n" + for alert in alerts: + reg_time = datetime.utcfromtimestamp(int(alert['reg_time']) + ).strftime('%b-%d-%Y') + msg += f"""{alert['topic_title']} + 💿Size: {sizeof_fmt(alert['size'])} + #️⃣Hash: {alert['info_hash']} + 📅Updated: {reg_time}\n""" + update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True) updater = Updater(token, use_context=True) + update_watcher(updater.bot) updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add)) + updater.dispatcher.add_handler(CommandHandler('list', list_alerts)) updater.start_polling() updater.idle() diff --git a/notify.py b/notify.py new file mode 100644 index 0000000..0ccbe44 --- /dev/null +++ b/notify.py @@ -0,0 +1,52 @@ +import time +import threading +import logging +from rutracker import Torrent +from datetime import datetime + +UPDATE_INTERVAL = 6 * 60 * 60 # in secs + +log = logging.getLogger("gaspar.%s" % __name__) + +torrent = Torrent() + +def sizeof_fmt(num, suffix='B'): + num = int(num) + for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix) + +def update(tor_id): + torrent.tor_id = tor_id + if torrent.is_outdated(): + log.info("%s if outdated. Updating.", torrent.meta['topic_title']) + torrent.update() + return True + else: + return False + +def update_watcher(bot): + def __thread(): + while True: + alerts = list() + raw = torrent.db.get_alerts() + for alert in raw: + alerts.append(alert['id']) + log.info("Checking for updates. Configured interval: %sh , [%s secs]", UPDATE_INTERVAL/60/60, UPDATE_INTERVAL) + if update(alert['id']): + log.info("Found update for [%s] %s", torrent.meta['id'], torrent.meta['topic_title']) + reg_time = datetime.utcfromtimestamp(int(torrent.meta['reg_time']) + ).strftime('%b-%d-%Y') + msg = f"""Topic updated\n{torrent.meta['topic_title']} + 💿Size: {sizeof_fmt(torrent.meta['size'])} + #️⃣Hash: {torrent.meta['info_hash']} + 📅Updated: {reg_time}\n""" + subs = torrent.db.get_subscribers(alert['id']) + for sub in subs: + bot.sendMessage(sub, msg, parse_mode='HTML', disable_web_page_preview=True) + time.sleep(UPDATE_INTERVAL / 60 / 60 / 4.) + time.sleep(UPDATE_INTERVAL) + update_thread = threading.Thread(target=__thread) + update_thread.start() diff --git a/rutracker.py b/rutracker.py index 0ad5d44..d74e466 100644 --- a/rutracker.py +++ b/rutracker.py @@ -3,14 +3,27 @@ from database import DataBase import logging import re -log = logging.getLogger(__name__) +log = logging.getLogger("gaspar.%s" % __name__) class Torrent: - def __init__(self, tor_id): + def __init__(self, tor_id=None): self.db = DataBase("scheme.sql") self.api_url = "http://api.rutracker.org/v1/" - self.meta = self.get_tor_topic_data(tor_id) - log.debug("Torrent info: %s", self.meta) + self.tor_id = tor_id + self.meta = None + if self.tor_id != None: + self.get_tor_topic_data(self.tor_id) + log.debug("Torrent info: %s", self.meta) + + @property + def tor_id(self): + return self.__tor_id + + @tor_id.setter + def tor_id(self, tor_id): + self.__tor_id = tor_id + if tor_id: + self.get_tor_topic_data(tor_id) def get_tor_topic_data(self, tor_id): data = dict() @@ -20,17 +33,27 @@ class Torrent: data = json.loads(url.read().decode()) data = data["result"][tor_id] data["id"] = tor_id - return data + log.info("Getting info for [%s] %s%s", tor_id, data["topic_title"][:60], '...') + self.meta = data def is_outdated(self): + if not self.tor_id: + log.warn("Torrent id not presented.") + return False stored_reg_time = int(self.db.get_attr(self.meta["id"], 'reg_time')) actual_reg_time = self.meta["reg_time"] return actual_reg_time != stored_reg_time def update(self): + if not self.tor_id: + log.warn("Torrent id not presented.") + return False self.db.update(self.meta) def episodes(self): + if not self.tor_id: + log.warn("Torrent id not presented.") + return False ep_str = re.search(r"\[\d+(\+\d+)?(-\d+)?( +)?(из)?( +)?\d+(\+\d+)?(-\d+)?\]", self.meta["topic_title"]).group(0) return ep_str diff --git a/scheme.sql b/scheme.sql index 2e6074e..694b18d 100644 --- a/scheme.sql +++ b/scheme.sql @@ -33,7 +33,8 @@ CREATE TABLE IF NOT EXISTS "users" ( ); CREATE TABLE IF NOT EXISTS "alerts" ( user_id TEXT, - tor_id TEXT + tor_id TEXT, + UNIQUE(user_id, tor_id) ); COMMIT;