mirror of
https://github.com/house-of-vanity/gaspar.git
synced 2025-07-07 02:34:06 +00:00
It works
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
|
data.sqlite
|
||||||
|
BIN
data.sqlite
BIN
data.sqlite
Binary file not shown.
174
database.py
174
database.py
@ -7,7 +7,10 @@
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
import logging
|
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
|
# class DataBase create or use existent SQLite database file. It provides
|
||||||
@ -39,9 +42,11 @@ class DataBase:
|
|||||||
cursor.executescript(sql)
|
cursor.executescript(sql)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.debug('Could not create scheme - %s', e)
|
log.debug('Could not create scheme - %s', e)
|
||||||
|
raise DBInitException
|
||||||
else:
|
else:
|
||||||
log.debug("Error! cannot create the database connection.")
|
log.debug("Error! cannot create the database connection.")
|
||||||
log.info('DB created.')
|
raise DBInitException
|
||||||
|
log.info('DB connected.')
|
||||||
self.close(conn)
|
self.close(conn)
|
||||||
|
|
||||||
def connect(self, basefile):
|
def connect(self, basefile):
|
||||||
@ -81,130 +86,9 @@ class DataBase:
|
|||||||
#log.debug("Close connection to %s", self.basefile)
|
#log.debug("Close connection to %s", self.basefile)
|
||||||
conn.close()
|
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):
|
def copy_to_history(self, tor_id):
|
||||||
sql = "SELECT * FROM torrents WHERE id = ?"
|
sql = "SELECT * FROM torrents WHERE id = ?"
|
||||||
attrs = self.execute(sql, (tor_id,))[0]
|
attrs = self.execute(sql, (tor_id,))[0]
|
||||||
print(attrs)
|
|
||||||
sql = """INSERT OR IGNORE INTO torrents_history(
|
sql = """INSERT OR IGNORE INTO torrents_history(
|
||||||
'id',
|
'id',
|
||||||
'info_hash',
|
'info_hash',
|
||||||
@ -215,7 +99,7 @@ class DataBase:
|
|||||||
'tor_status',
|
'tor_status',
|
||||||
'seeders',
|
'seeders',
|
||||||
'topic_title',
|
'topic_title',
|
||||||
'seeder_last_seen',
|
'seeder_last_seen'
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"""
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"""
|
||||||
self.execute(sql, attrs)
|
self.execute(sql, attrs)
|
||||||
|
|
||||||
@ -288,5 +172,45 @@ class DataBase:
|
|||||||
chat_instance['first_name'],
|
chat_instance['first_name'],
|
||||||
chat_instance['last_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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
40
main.py
40
main.py
@ -2,17 +2,21 @@ import os
|
|||||||
import logging
|
import logging
|
||||||
from urllib import parse
|
from urllib import parse
|
||||||
from rutracker import Torrent
|
from rutracker import Torrent
|
||||||
|
from notify import update_watcher
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from database import DataBase
|
from database import DataBase
|
||||||
from telegram import *
|
from telegram import *
|
||||||
from telegram.ext import Updater, MessageHandler, CommandHandler, filters
|
from telegram.ext import Updater, MessageHandler, CommandHandler, filters
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.INFO,
|
||||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
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'):
|
def sizeof_fmt(num, suffix='B'):
|
||||||
|
num = int(num)
|
||||||
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
|
for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
|
||||||
if abs(num) < 1024.0:
|
if abs(num) < 1024.0:
|
||||||
return "%3.1f%s%s" % (num, unit, suffix)
|
return "%3.1f%s%s" % (num, unit, suffix)
|
||||||
@ -34,30 +38,48 @@ def main():
|
|||||||
else:
|
else:
|
||||||
update.message.reply_text("Send me a URL to rutracker.org topic.")
|
update.message.reply_text("Send me a URL to rutracker.org topic.")
|
||||||
return
|
return
|
||||||
|
log.info(
|
||||||
|
"Got /add request from user [%s] %s",
|
||||||
|
update.message.chat['id'],
|
||||||
|
update.message.from_user.username)
|
||||||
torrent = Torrent(tor_id)
|
torrent = Torrent(tor_id)
|
||||||
torrent.db.save_tor(torrent.meta)
|
torrent.db.save_tor(torrent.meta)
|
||||||
torrent.db.save_user(update.message.chat)
|
torrent.db.save_user(update.message.chat)
|
||||||
torrent.db.save_alert(update.message.chat['id'], torrent.meta['id'])
|
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'])
|
reg_time = datetime.utcfromtimestamp(int(torrent.meta['reg_time'])
|
||||||
).strftime('%b-%d')
|
).strftime('%b-%d-%Y')
|
||||||
msg = f"""{torrent.meta['topic_title']}
|
msg = f"""{torrent.meta['topic_title']}
|
||||||
<b>Size:</b> {sizeof_fmt(torrent.meta['size'])}
|
<b>Size:</b> {sizeof_fmt(torrent.meta['size'])}
|
||||||
<b>Hash: </b> {torrent.meta['info_hash']}
|
<b>Hash: </b> {torrent.meta['info_hash']}
|
||||||
<b>Updated: </b>{reg_time}"""
|
<b>Updated: </b>{reg_time}"""
|
||||||
log.info(msg)
|
|
||||||
update.message.reply_text(msg, parse_mode='HTML')
|
update.message.reply_text(msg, parse_mode='HTML')
|
||||||
|
|
||||||
|
|
||||||
def hello(update, context):
|
def list_alerts(update, context):
|
||||||
update.message.reply_text(
|
log.info(
|
||||||
'Hello {}'.format(update.message.from_user.first_name))
|
"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 = "<b>Configured alerts:</b>\n"
|
||||||
|
for alert in alerts:
|
||||||
|
reg_time = datetime.utcfromtimestamp(int(alert['reg_time'])
|
||||||
|
).strftime('%b-%d-%Y')
|
||||||
|
msg += f"""<a href='https://rutracker.org/forum/viewtopic.php?t={alert['id']}'><b>{alert['topic_title']}</b></a>
|
||||||
|
<b>💿Size:</b> {sizeof_fmt(alert['size'])}
|
||||||
|
<b>#️⃣Hash: </b> {alert['info_hash']}
|
||||||
|
<b>📅Updated: </b>{reg_time}\n"""
|
||||||
|
update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True)
|
||||||
|
|
||||||
|
|
||||||
updater = Updater(token, use_context=True)
|
updater = Updater(token, use_context=True)
|
||||||
|
update_watcher(updater.bot)
|
||||||
|
|
||||||
updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add))
|
updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add))
|
||||||
|
updater.dispatcher.add_handler(CommandHandler('list', list_alerts))
|
||||||
|
|
||||||
updater.start_polling()
|
updater.start_polling()
|
||||||
updater.idle()
|
updater.idle()
|
||||||
|
52
notify.py
Normal file
52
notify.py
Normal file
@ -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"""<i>Topic updated</i>\n<a href='https://rutracker.org/forum/viewtopic.php?t={torrent.meta['id']}'><b>{torrent.meta['topic_title']}</b></a>
|
||||||
|
<b>💿Size:</b> {sizeof_fmt(torrent.meta['size'])}
|
||||||
|
<b>#️⃣Hash: </b> {torrent.meta['info_hash']}
|
||||||
|
<b>📅Updated: </b>{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()
|
33
rutracker.py
33
rutracker.py
@ -3,14 +3,27 @@ from database import DataBase
|
|||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger("gaspar.%s" % __name__)
|
||||||
|
|
||||||
class Torrent:
|
class Torrent:
|
||||||
def __init__(self, tor_id):
|
def __init__(self, tor_id=None):
|
||||||
self.db = DataBase("scheme.sql")
|
self.db = DataBase("scheme.sql")
|
||||||
self.api_url = "http://api.rutracker.org/v1/"
|
self.api_url = "http://api.rutracker.org/v1/"
|
||||||
self.meta = self.get_tor_topic_data(tor_id)
|
self.tor_id = tor_id
|
||||||
log.debug("Torrent info: %s", self.meta)
|
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):
|
def get_tor_topic_data(self, tor_id):
|
||||||
data = dict()
|
data = dict()
|
||||||
@ -20,17 +33,27 @@ class Torrent:
|
|||||||
data = json.loads(url.read().decode())
|
data = json.loads(url.read().decode())
|
||||||
data = data["result"][tor_id]
|
data = data["result"][tor_id]
|
||||||
data["id"] = 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):
|
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'))
|
stored_reg_time = int(self.db.get_attr(self.meta["id"], 'reg_time'))
|
||||||
actual_reg_time = self.meta["reg_time"]
|
actual_reg_time = self.meta["reg_time"]
|
||||||
return actual_reg_time != stored_reg_time
|
return actual_reg_time != stored_reg_time
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
|
if not self.tor_id:
|
||||||
|
log.warn("Torrent id not presented.")
|
||||||
|
return False
|
||||||
self.db.update(self.meta)
|
self.db.update(self.meta)
|
||||||
|
|
||||||
def episodes(self):
|
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)
|
ep_str = re.search(r"\[\d+(\+\d+)?(-\d+)?( +)?(из)?( +)?\d+(\+\d+)?(-\d+)?\]", self.meta["topic_title"]).group(0)
|
||||||
return ep_str
|
return ep_str
|
||||||
|
|
||||||
|
@ -33,7 +33,8 @@ CREATE TABLE IF NOT EXISTS "users" (
|
|||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS "alerts" (
|
CREATE TABLE IF NOT EXISTS "alerts" (
|
||||||
user_id TEXT,
|
user_id TEXT,
|
||||||
tor_id TEXT
|
tor_id TEXT,
|
||||||
|
UNIQUE(user_id, tor_id)
|
||||||
);
|
);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user