This commit is contained in:
AB
2020-05-24 15:35:15 +03:00
commit 21a67c3562
7 changed files with 428 additions and 0 deletions

Binary file not shown.

Binary file not shown.

BIN
data.sqlite Normal file

Binary file not shown.

293
database.py Normal file
View File

@ -0,0 +1,293 @@
"""
.. module:: models
:synopsis: Contains database action primitives.
.. moduleauthor:: AB <github.com/house-of-vanity>
"""
import sqlite3
import logging
log = logging.getLogger(__name__)
# class DataBase create or use existent SQLite database file. It provides
# high-level methods for database.
class DataBase:
"""This class create or use existent SQLite database file. It provides
high-level methods for database."""
def __init__(self, scheme, basefile='data.sqlite'):
"""
Constructor creates new SQLite database if
it doesn't exist. Uses SQL code from file for DB init.
:param scheme: sql filename
:type scheme: string
:return: None
"""
self.scheme = ''
self.basefile = basefile
try:
conn = self.connect(basefile=basefile)
except:
log.debug('Could not connect to DataBase.')
return None
with open(scheme, 'r') as scheme_sql:
sql = scheme_sql.read()
self.scheme = sql
if conn is not None:
try:
cursor = conn.cursor()
cursor.executescript(sql)
except Exception as e:
log.debug('Could not create scheme - %s', e)
else:
log.debug("Error! cannot create the database connection.")
log.info('DB created.')
self.close(conn)
def connect(self, basefile):
"""
Create connect object for basefile
:param basefile: SQLite database filename
:type basefile: string
:return: sqlite3 connect object
"""
#log.debug("Open connection to %s", basefile)
return sqlite3.connect(basefile, check_same_thread=False)
def execute(self, sql, params):
"""
Execute SQL code. First of all connect to self.basefile. Close
connection after execution.
:param sql: SQL code
:type sql: string
:return: list of response. Empty list when no rows are available.
"""
conn = self.connect(basefile=self.basefile)
log.debug("Executing: %s %s", sql, params)
cursor = conn.cursor()
cursor.execute(sql, params)
conn.commit()
result = cursor.fetchall()
self.close(conn)
return result
def close(self, conn):
"""
Close connection object instance.
:param conn: sqlite3 connection object
:type conn: object
:return: None
"""
#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',
'forum_id',
'poster_id',
'size',
'reg_time',
'tor_status',
'seeders',
'topic_title',
'seeder_last_seen'
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"""
self.execute(sql, attrs)
def get_attr(self, tor_id, attr):
sql = """SELECT %s FROM torrents WHERE id = ? ORDER BY reg_time DESC LIMIT 1""" % attr
return self.execute(sql, (tor_id,))[0][0]
def update(self, tor_data):
self.copy_to_history(tor_data["id"])
sql = """UPDATE torrents SET
'info_hash' = ?,
'forum_id' = ?,
'poster_id' = ?,
'size' = ?,
'reg_time' = ?,
'tor_status' = ?,
'seeders' = ?,
'topic_title' = ?,
'seeder_last_seen' = ?
WHERE id = ?
"""
self.execute(sql, (
tor_data["info_hash"],
tor_data["forum_id"],
tor_data["poster_id"],
int(tor_data["size"]),
int(tor_data["reg_time"]),
tor_data["tor_status"],
tor_data["seeders"],
tor_data["topic_title"],
tor_data["seeder_last_seen"],
tor_data["id"],
))
def save_tor(self, tor_data, chat_instance):
sql = """INSERT OR IGNORE INTO torrents(
'id',
'info_hash',
'forum_id',
'poster_id',
'size',
'reg_time',
'tor_status',
'seeders',
'topic_title',
'seeder_last_seen',
'user_id'
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ? )"""
self.execute(sql, (
tor_data["id"],
tor_data["info_hash"],
tor_data["forum_id"],
tor_data["poster_id"],
int(tor_data["size"]),
int(tor_data["reg_time"]),
tor_data["tor_status"],
tor_data["seeders"],
tor_data["topic_title"],
tor_data["seeder_last_seen"],
chat_instance['id'],
))
sql = """INSERT OR IGNORE INTO users(
'id',
'username',
'first_name',
'last_name'
) VALUES (?, ?, ?, ?)"""
self.execute(sql, (
chat_instance['id'],
chat_instance['username'],
chat_instance['first_name'],
chat_instance['last_name'],
))
return True

61
main.py Normal file
View File

@ -0,0 +1,61 @@
from rutracker import Torrent
from datetime import datetime
from database import DataBase
from telegram import *
from telegram.ext import Updater, MessageHandler, CommandHandler, filters
from urllib import parse
import logging
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log = logging.getLogger(__name__)
def sizeof_fmt(num, suffix='B'):
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 main():
"""Run bot."""
def add(update, context):
if 'https://rutracker.org' in update.message.text:
try:
tor_id = parse.parse_qs(parse.urlsplit(update.message.text).query)['t'][0]
except KeyError:
log.warn("URL provided doesn't contains any torrent id.")
update.message.reply_text("URL provided doesn't contains any torrent id.")
return
else:
update.message.reply_text("Send me a URL to rutracker.org topic.")
return
log.debug(update.message.chat)
torrent = Torrent(tor_id, update.message.chat)
reg_time = datetime.utcfromtimestamp(int(torrent.meta['reg_time'])
).strftime('%b-%d')
msg = f"""{torrent.meta['topic_title']}
<b>Size:</b> {sizeof_fmt(torrent.meta['size'])}
<b>Hash: </b> {torrent.meta['info_hash']}
<b>Updated: </b>{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))
updater = Updater("539189256:AAHqrL6nTmv5g0mGoPQownO0vrNRvhPFq7I", use_context=True)
updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add))
updater.start_polling()
updater.idle()
if __name__ == '__main__':
main()

37
rutracker.py Normal file
View File

@ -0,0 +1,37 @@
import urllib.request, json
from database import DataBase
import logging
import re
log = logging.getLogger(__name__)
class Torrent:
def __init__(self, tor_id, chat_instance):
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.db.save_tor(self.meta, chat_instance)
def get_tor_topic_data(self, tor_id):
data = dict()
with urllib.request.urlopen(
"{}/get_tor_topic_data?by=topic_id&val={}".format(
self.api_url, tor_id)) as url:
data = json.loads(url.read().decode())
data = data["result"][tor_id]
data["id"] = tor_id
return data
def is_outdated(self):
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):
self.db.update(self.meta)
def episodes(self):
ep_str = re.search(r"\[\d+(\+\d+)?(-\d+)?( +)?(из)?( +)?\d+(\+\d+)?(-\d+)?\]", self.meta["topic_title"]).group(0)
return ep_str

37
scheme.sql Normal file
View File

@ -0,0 +1,37 @@
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "torrents" (
"id" TEXT NOT NULL,
"info_hash" TEXT NOT NULL,
"forum_id" TEXT NOT NULL,
"poster_id" TEXT,
"size" INT NOT NULL,
"reg_time" INT NOT NULL,
"tor_status" TEXT,
"seeders" TEXT,
"topic_title" TEXT,
"seeder_last_seen" TEXT,
"user_id" TEXT,
PRIMARY KEY("id")
);
CREATE TABLE IF NOT EXISTS "torrents_history" (
rowid INTEGER PRIMARY KEY AUTOINCREMENT,
"id" TEXT NOT NULL,
"info_hash" TEXT NOT NULL,
"forum_id" TEXT NOT NULL,
"poster_id" TEXT,
"size" INT NOT NULL,
"reg_time" INT NOT NULL,
"tor_status" TEXT,
"seeders" TEXT,
"topic_title" TEXT,
"user_id" TEXT,
"seeder_last_seen" TEXT
);
CREATE TABLE IF NOT EXISTS "users" (
id INTEGER PRIMARY KEY,
username TEXT,
first_name TEXT,
last_name TEXT
);
COMMIT;