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 90fbfc0..0000000
Binary files a/data.sqlite and /dev/null differ
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;