Improve client command.

This commit is contained in:
AB
2021-02-27 02:29:08 +03:00
parent 441223a315
commit 2b12ecec02
7 changed files with 199 additions and 133 deletions

14
README
View File

@ -9,10 +9,20 @@
Gaspar telegram bot written in python. Gaspar telegram bot written in python.
Fetures: Fetures:
* notify about topic update on rutracker via telegram. * Notify about topic update on rutracker.org via Telegram.
* is able to push new magnet link into your Transmission RPC client. * Can push new magnet link into your Transmission web client.
running instance - @let_you_know_bot running instance - @let_you_know_bot
⣿⣿⣿⡷⠊⡢⡹⣦⡑⢂⢕⢂⢕⢂⢕⢂⠕⠔⠌⠝⠛⠶⠶⢶⣦⣄⢂⢕⢂⢕
⣿⣿⠏⣠⣾⣦⡐⢌⢿⣷⣦⣅⡑⠕⠡⠐⢿⠿⣛⠟⠛⠛⠛⠛⠡⢷⡈⢂⢕⢂
⠟⣡⣾⣿⣿⣿⣿⣦⣑⠝⢿⣿⣿⣿⣿⣿⡵⢁⣤⣶⣶⣿⢿⢿⢿⡟⢻⣤⢑⢂
⣾⣿⣿⡿⢟⣛⣻⣿⣿⣿⣦⣬⣙⣻⣿⣿⣷⣿⣿⢟⢝⢕⢕⢕⢕⢽⣿⣿⣷⣔
⣿⣿⠵⠚⠉⢀⣀⣀⣈⣿⣿⣿⣿⣿⣿⣿⣿⣿⣗⢕⢕⢕⢕⢕⢕⣽⣿⣿⣿⣿
⢷⣂⣠⣴⣾⡿⡿⡻⡻⣿⣿⣴⣿⣿⣿⣿⣿⣿⣷⣵⣵⣵⣷⣿⣿⣿⣿⣿⣿⡿
⢌⠻⣿⡿⡫⡪⡪⡪⡪⣺⣿⣿⣿⣿⣿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃
⠣⡁⠹⡪⡪⡪⡪⣪⣾⣿⣿⣿⣿⠋⠐⢉⢍⢄⢌⠻⣿⣿⣿⣿⣿⣿⣿⣿⠏⠈
⡣⡘⢄⠙⣾⣾⣾⣿⣿⣿⣿⣿⣿⡀⢐⢕⢕⢕⢕⢕⡘⣿⣿⣿⣿⣿⣿⠏⠠⠈
⠌⢊⢂⢣⠹⣿⣿⣿⣿⣿⣿⣿⣿⣧⢐⢕⢕⢕⢕⢕⢅⣿⣿⣿⣿⡿⢋⢜⠠⠈

View File

@ -4,12 +4,15 @@
.. moduleauthor:: AB <github.com/house-of-vanity> .. moduleauthor:: AB <github.com/house-of-vanity>
""" """
import sqlite3
import logging import logging
import os import os
import sqlite3
from .transmission import check_connection
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class DBInitException(Exception): class DBInitException(Exception):
""" Exception at DB Init """ """ Exception at DB Init """
@ -19,6 +22,7 @@ class DBInitException(Exception):
class DataBase: class DataBase:
"""This class create or use existent SQLite database file. It provides """This class create or use existent SQLite database file. It provides
high-level methods for database.""" high-level methods for database."""
def __init__(self): def __init__(self):
""" """
Constructor creates new SQLite database if Constructor creates new SQLite database if
@ -106,13 +110,25 @@ class DataBase:
self.execute(sql, attrs) self.execute(sql, attrs)
def add_client(self, user_id, scheme, hostname, port, username, password, path): def add_client(self, user_id, scheme, hostname, port, username, password, path):
if check_connection(scheme, hostname, port, username, password, path):
sql = """INSERT OR REPLACE INTO tr_clients(user_id, scheme, hostname, port, username, password, path) sql = """INSERT OR REPLACE INTO tr_clients(user_id, scheme, hostname, port, username, password, path)
VALUES(?, ?, ?, ?, ?, ?, ?);""" VALUES(?, ?, ?, ?, ?, ?, ?);"""
self.execute(sql, (user_id, scheme, hostname, port, username, password, path)) self.execute(sql, (user_id, scheme, hostname, port, username, password, path))
return True
else:
return False
def get_client(self, user_id): def get_client(self, user_id):
sql = "SELECT scheme, hostname, port, username, password, path FROM tr_clients WHERE user_id = ?" sql = "SELECT scheme, hostname, port, username, password, path FROM tr_clients WHERE user_id = ?"
res = self.execute(sql, (user_id,))
if len(res):
return self.execute(sql, (user_id,))[0] return self.execute(sql, (user_id,))[0]
else:
return False
def drop_client(self, user_id):
sql = "DELETE FROM tr_clients WHERE user_id = ?"
self.execute(sql, (user_id,))
def get_attr(self, tor_id, attr): def get_attr(self, tor_id, attr):
sql = """SELECT %s FROM torrents WHERE id = ? ORDER BY reg_time DESC LIMIT 1""" % attr sql = """SELECT %s FROM torrents WHERE id = ? ORDER BY reg_time DESC LIMIT 1""" % attr
@ -188,6 +204,7 @@ class DataBase:
chat_instance['first_name'], chat_instance['first_name'],
chat_instance['last_name'], chat_instance['last_name'],
)) ))
def save_alert(self, user_id, tor_id): def save_alert(self, user_id, tor_id):
sql = """INSERT OR IGNORE INTO alerts( sql = """INSERT OR IGNORE INTO alerts(
'user_id', 'user_id',
@ -227,6 +244,3 @@ class DataBase:
for sub in self.execute(sql, (tor_id,)): for sub in self.execute(sql, (tor_id,)):
subs.append(sub[0]) subs.append(sub[0])
return subs return subs

View File

@ -1,12 +1,12 @@
import logging
import os import os
import sys import sys
import logging
from urllib import parse from urllib import parse
from telegram import *
from telegram.ext import Updater, MessageHandler, CommandHandler, PrefixHandler, filters from telegram.ext import Updater, MessageHandler, CommandHandler, filters
from .rutracker import Torrent
from .notify import update_watcher from .notify import update_watcher
from .database import DataBase from .rutracker import Torrent
from .tools import format_topic from .tools import format_topic
logging.basicConfig( logging.basicConfig(
@ -14,14 +14,15 @@ logging.basicConfig(
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(__name__)
token = os.environ.get('TG_TOKEN') token = os.environ.get('TG_TOKEN')
if not token: if not token:
log.error("Env var TG_TOKEN isn't set.") log.error("Env var TG_TOKEN isn't set.")
sys.exit(1) sys.exit(1)
def main(): def main():
"""Run bot.""" """Run bot."""
def add(update, context): def add(update, context):
if 'https://rutracker.org' in update.message.text: if 'https://rutracker.org' in update.message.text:
try: try:
@ -50,7 +51,6 @@ def main():
pre='You will be alerted about\n') pre='You will be alerted about\n')
update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True) update.message.reply_text(msg, parse_mode='HTML', disable_web_page_preview=True)
def list_alerts(update, context): def list_alerts(update, context):
log.info( log.info(
"Got /list request from user [%s] %s", "Got /list request from user [%s] %s",
@ -90,19 +90,38 @@ def main():
if not scheme or not hostname: if not scheme or not hostname:
update.message.reply_text( update.message.reply_text(
f'Can\'t understand : <b>{update.message.text}</b>. ' f'Can\'t understand : <b>{update.message.text}</b>. '
'Send transmission RPC address like <b>http(s)://[user:pass]host[:port][/rpc_path]</b>', 'Send transmission RPC address like <b>http(s)://[user:pass]host[:port][/transmission/rpc]</b>',
parse_mode='HTML', parse_mode='HTML',
disable_web_page_preview=True) disable_web_page_preview=True)
return return
except: except:
tr_client = Torrent().db.get_client(u_id)
log.info(tr_client)
if tr_client:
tr_line = f"Your client: <code>{tr_client[0]}://{tr_client[1]}:{tr_client[2]}{tr_client[5]}</code>\n" \
r"/delete_client"
else:
tr_line = False
update.message.reply_text( update.message.reply_text(
'Gaspar is able to add new topics to your private Transmission server.' 'Gaspar can add new topics to your private Transmission server. '
'Send transmission RPC address like <b>http(s)://[user:pass]host[:port][/rpc_path]</b>', 'Send transmission RPC address like \n<b>http(s)://[user:pass]host[:port][/transmission/rpc]</b>\n'
f'{tr_line if tr_line else "You have no configured client."}',
parse_mode='HTML', parse_mode='HTML',
disable_web_page_preview=True) disable_web_page_preview=True)
return return
Torrent().db.add_client(u_id, scheme, hostname, port, username, password, path) if Torrent().db.add_client(u_id, scheme, hostname, port, username, password, path):
update.message.reply_text(f'Client reachable and saved.')
else:
update.message.reply_text(f'Client unreachable.')
def delete_client(update, context):
log.info(
"Got /delete request from user [%s] %s",
update.message.chat['id'],
update.message.from_user.username)
Torrent().db.drop_client(update.message.chat['id'])
update.message.reply_text(f'Client deleted.')
def delete(update, context): def delete(update, context):
log.info( log.info(
@ -112,15 +131,16 @@ def main():
tor_id = update.message.text.split('_')[1] tor_id = update.message.text.split('_')[1]
try: try:
Torrent().db.delete_tor(update.message.chat['id'], tor_id) Torrent().db.delete_tor(update.message.chat['id'], tor_id)
context.bot.sendMessage(update.message.chat['id'], f'Deleted {tor_id}') update.message.reply_text(f'Deleted {tor_id}')
except: except:
context.bot.sendMessage(update.message.chat['id'], f'Faled to delete {tor_id}') update.message.reply_text(f'Faled to delete {tor_id}')
updater = Updater(token, use_context=True) updater = Updater(token, use_context=True)
update_watcher(updater.bot) update_watcher(updater.bot)
updater.dispatcher.add_handler(CommandHandler('list', list_alerts)) updater.dispatcher.add_handler(CommandHandler('list', list_alerts))
updater.dispatcher.add_handler(CommandHandler('client', handle_client)) updater.dispatcher.add_handler(CommandHandler('client', handle_client))
updater.dispatcher.add_handler(CommandHandler('delete_client', delete_client))
updater.dispatcher.add_handler(MessageHandler(filters.Filters.regex(r'/delete_'), delete)) updater.dispatcher.add_handler(MessageHandler(filters.Filters.regex(r'/delete_'), delete))
updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add)) updater.dispatcher.add_handler(MessageHandler(filters.Filters.text, add))

View File

@ -1,7 +1,8 @@
import time
import threading
import logging import logging
import threading
import time
from datetime import datetime from datetime import datetime
from .rutracker import Torrent from .rutracker import Torrent
from .tools import format_topic from .tools import format_topic
from .transmission import add_tor from .transmission import add_tor
@ -12,6 +13,7 @@ log = logging.getLogger(__name__)
torrent = Torrent() torrent = Torrent()
def sizeof_fmt(num, suffix='B'): def sizeof_fmt(num, suffix='B'):
num = int(num) num = int(num)
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']: for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
@ -20,6 +22,7 @@ def sizeof_fmt(num, suffix='B'):
num /= 1024.0 num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix) return "%.1f%s%s" % (num, 'Yi', suffix)
def update(tor_id): def update(tor_id):
torrent.tor_id = tor_id torrent.tor_id = tor_id
if torrent.is_outdated(): if torrent.is_outdated():
@ -29,6 +32,7 @@ def update(tor_id):
else: else:
return False return False
def update_watcher(bot): def update_watcher(bot):
def __thread(): def __thread():
while True: while True:
@ -36,7 +40,8 @@ def update_watcher(bot):
raw = torrent.db.get_alerts() raw = torrent.db.get_alerts()
for alert in raw: for alert in raw:
alerts.append(alert['id']) alerts.append(alert['id'])
log.info("Checking for updates. Configured interval: %sh , [%s secs]", UPDATE_INTERVAL/60/60, UPDATE_INTERVAL) log.info("Checking for updates. Configured interval: %sh , [%s secs]", UPDATE_INTERVAL / 60 / 60,
UPDATE_INTERVAL)
log.info("Checking alert %s", alert['topic_title']) log.info("Checking alert %s", alert['topic_title'])
if update(alert['id']): if update(alert['id']):
log.info("Found update for [%s] %s", torrent.meta['id'], torrent.meta['topic_title']) log.info("Found update for [%s] %s", torrent.meta['id'], torrent.meta['topic_title'])
@ -57,7 +62,8 @@ def update_watcher(bot):
log.info("Push update to client Transmission RPC for %s", torrent.meta['info_hash']) log.info("Push update to client Transmission RPC for %s", torrent.meta['info_hash'])
msg = f"{msg}\n* Added to your Transmission: {scheme}://{hostname}:{port}/{path}" msg = f"{msg}\n* Added to your Transmission: {scheme}://{hostname}:{port}/{path}"
else: else:
log.warning("Failed push update to client Transmission RPC for %s", torrent.meta['info_hash']) log.warning("Failed push update to client Transmission RPC for %s",
torrent.meta['info_hash'])
except: except:
pass pass
bot.sendMessage(sub, msg, parse_mode='HTML', disable_web_page_preview=True) bot.sendMessage(sub, msg, parse_mode='HTML', disable_web_page_preview=True)
@ -66,5 +72,6 @@ def update_watcher(bot):
else: else:
log.info("There is no update for %s", alert['topic_title']) log.info("There is no update for %s", alert['topic_title'])
time.sleep(UPDATE_INTERVAL) time.sleep(UPDATE_INTERVAL)
update_thread = threading.Thread(target=__thread) update_thread = threading.Thread(target=__thread)
update_thread.start() update_thread.start()

View File

@ -1,5 +1,4 @@
import json import json
import os
import logging import logging
import re import re
import urllib.request import urllib.request
@ -8,6 +7,7 @@ from .database import DataBase
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Torrent: class Torrent:
def __init__(self, tor_id=None): def __init__(self, tor_id=None):
self.db = DataBase() self.db = DataBase()
@ -28,7 +28,6 @@ class Torrent:
if tor_id: if tor_id:
return self.get_tor_topic_data(tor_id) return 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()
with urllib.request.urlopen( with urllib.request.urlopen(
@ -62,6 +61,6 @@ class Torrent:
if not self.tor_id: if not self.tor_id:
log.warn("Torrent id not presented.") log.warn("Torrent id not presented.")
return False 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

View File

@ -1,5 +1,6 @@
from datetime import datetime from datetime import datetime
def format_topic(tor_id, topic_title, size, info_hash, reg_time, pre=''): def format_topic(tor_id, topic_title, size, info_hash, reg_time, pre=''):
def sizeof_fmt(num, suffix='B'): def sizeof_fmt(num, suffix='B'):
num = int(num) num = int(num)
@ -8,6 +9,7 @@ def format_topic(tor_id, topic_title, size, info_hash, reg_time, pre=''):
return "%3.1f%s%s" % (num, unit, suffix) return "%3.1f%s%s" % (num, unit, suffix)
num /= 1024.0 num /= 1024.0
return "%.1f%s%s" % (num, 'Yi', suffix) return "%.1f%s%s" % (num, 'Yi', suffix)
size = sizeof_fmt(size) size = sizeof_fmt(size)
reg_time = datetime.utcfromtimestamp(int(reg_time) reg_time = datetime.utcfromtimestamp(int(reg_time)
).strftime('%b-%d-%Y') ).strftime('%b-%d-%Y')

View File

@ -1,5 +1,6 @@
from transmission_rpc import Client from transmission_rpc import Client
def add_tor(scheme, hostname, port, username, password, path, tor_hash): def add_tor(scheme, hostname, port, username, password, path, tor_hash):
try: try:
c = Client( c = Client(
@ -15,3 +16,16 @@ def add_tor(scheme, hostname, port, username, password, path, tor_hash):
except: except:
return False return False
def check_connection(scheme, hostname, port, username, password, path):
try:
c = Client(
host=hostname,
port=port,
username=username,
password=password,
protocol=scheme,
path=path)
return True if c.rpc_version else False
except:
return False