mirror of
https://github.com/house-of-vanity/gaspar.git
synced 2025-07-07 02:34:06 +00:00
Init
This commit is contained in:
BIN
__pycache__/database.cpython-38.pyc
Normal file
BIN
__pycache__/database.cpython-38.pyc
Normal file
Binary file not shown.
BIN
__pycache__/rutracker.cpython-38.pyc
Normal file
BIN
__pycache__/rutracker.cpython-38.pyc
Normal file
Binary file not shown.
BIN
data.sqlite
Normal file
BIN
data.sqlite
Normal file
Binary file not shown.
293
database.py
Normal file
293
database.py
Normal 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
61
main.py
Normal 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
37
rutracker.py
Normal 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
37
scheme.sql
Normal 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;
|
||||
|
Reference in New Issue
Block a user