Add qBittorrent support.

This commit is contained in:
AB
2021-11-23 01:41:33 +03:00
parent b5ffccd7cc
commit b1e26ca591
10 changed files with 178 additions and 75 deletions

View File

@@ -1 +1 @@
__version__ = "0.1.2" __version__ = "0.2.0"

View File

@@ -6,10 +6,10 @@ from urllib import parse
from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
from telegram.ext import Updater, MessageHandler, CommandHandler, filters, CallbackQueryHandler, CallbackContext from telegram.ext import Updater, MessageHandler, CommandHandler, filters, CallbackQueryHandler, CallbackContext
from .torrent_clients import add_client, detect_client, easy_send
from .notify import update_watcher from .notify import update_watcher
from .rutracker import Torrent from .rutracker import Torrent
from .tools import format_topic from .tools import format_topic
from .transmission import easy_send
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
@@ -88,42 +88,28 @@ def main():
"Got /client request from user [%s] %s", "Got /client request from user [%s] %s",
u_id, u_id,
update.message.from_user.username) update.message.from_user.username)
try: if len(update.message.text.split()) > 1:
addr = update.message.text.split()[1] msg = add_client(update.message.text.split()[1], u_id)
log.info("Client Transmission RPC address - %s", addr) update.message.reply_text(msg)
tr = parse.urlparse(addr) else:
scheme = tr.scheme if tr.scheme else False
hostname = tr.hostname if tr.hostname else False
username = tr.username if tr.username else False
password = tr.password if tr.password else False
path = tr.path if tr.path else '/transmission/rpc'
port = tr.port if tr.port else (80 if scheme == 'http' else 443)
if not scheme or not hostname:
update.message.reply_text(
f'Can\'t understand : <b>{update.message.text}</b>. '
'Send transmission RPC address like <b>http(s)://[user:pass]host[:port][/transmission/rpc]</b>',
parse_mode='HTML',
disable_web_page_preview=True)
return
except:
tr_client = Torrent().db.get_client_rpc(u_id) tr_client = Torrent().db.get_client_rpc(u_id)
if tr_client: log.info("tr_client: %s", tr_client)
tr_line = f"Your client: <code>{tr_client[0]}://{tr_client[1]}:{tr_client[2]}{tr_client[5]}</code>\n" \ try:
user_pass = f"{tr_client[3]}:{tr_client[4]}@" if tr_client[3] and tr_client[4] else ""
tr_client_check = detect_client(f"{tr_client[0]}://{user_pass}{tr_client[1]}:{tr_client[2]}{tr_client[5] if tr_client[5] != None else ''}")
except:
tr_client_check = False
if tr_client and tr_client_check:
tr_line = f"Your have configured client.\nURL: <code>{tr_client[0]}://{tr_client[1]}:{tr_client[2]}{tr_client[5] if tr_client[5] != None else ''}</code>\n" \
f"Client: <code>{tr_client_check}</code>\nStatus: <code>Works</code>\n" \
r"/delete_client"
elif tr_client:
tr_line = f"Your have configured client.\nURL: <code>{tr_client[0]}://{tr_client[1]}:{tr_client[2]}{tr_client[5] if tr_client[5] != None else ''}</code>\n" \
f"Client: <code>{tr_client_check}</code>\nStatus: <code>Conenction failed!</code>\n" \
r"/delete_client" r"/delete_client"
else: else:
tr_line = False tr_line = "You have no configured client."
update.message.reply_text( update.message.reply_text(tr_line, parse_mode='HTML', disable_web_page_preview=True)
'Gaspar can add new topics to your private Transmission server. '
'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',
disable_web_page_preview=True)
return
if Torrent().db.add_client_rpc(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): def delete_client(update, context):
log.info( log.info(
@@ -147,7 +133,7 @@ def main():
def button(update: Update, context: CallbackContext) -> None: def button(update: Update, context: CallbackContext) -> None:
query = update.callback_query query = update.callback_query
u_id = query.message.chat['id']
torrent_id = query.data.split('.')[1] torrent_id = query.data.split('.')[1]
torrent = Torrent(torrent_id) torrent = Torrent(torrent_id)
@@ -164,7 +150,7 @@ def main():
query.edit_message_text(text=f"{msg}", parse_mode='HTML', query.edit_message_text(text=f"{msg}", parse_mode='HTML',
disable_web_page_preview=True) disable_web_page_preview=True)
else: else:
easy_send(client_id=query.from_user, torent=torrent) easy_send(user_id=u_id, torrent_hash=torrent.meta['info_hash'])
query.answer() query.answer()
query.edit_message_text(text=f"{msg}📨 <b>Sent to RPC /client</b>", parse_mode='HTML', query.edit_message_text(text=f"{msg}📨 <b>Sent to RPC /client</b>", parse_mode='HTML',
disable_web_page_preview=True) disable_web_page_preview=True)

View File

@@ -33,9 +33,10 @@ class DataBase:
""" """
self.scheme = os.environ.get('TG_SCHEME') if os.environ.get('TG_SCHEME') else '/usr/share/gaspar/scheme.sql' self.scheme = os.environ.get('TG_SCHEME') if os.environ.get('TG_SCHEME') else '/usr/share/gaspar/scheme.sql'
self.basefile = os.environ.get('TG_DB') if os.environ.get('TG_DB') else '/usr/share/gaspar/data.sqlite' self.basefile = os.environ.get('TG_DB') if os.environ.get('TG_DB') else '/usr/share/gaspar/data.sqlite'
log.debug("self.scheme: %s, self.basefile: %s", self.scheme, self.basefile)
try: try:
conn = self.connect() conn = self.connect()
log.debig("Using '%s' base file.", os.path.realpath(self.basefile)) log.debug("Using '%s' base file.", os.path.realpath(self.basefile))
except: except:
log.debug('Could not connect to DataBase.') log.debug('Could not connect to DataBase.')
return None return None
@@ -111,18 +112,17 @@ class DataBase:
self.execute(sql, attrs) self.execute(sql, attrs)
def add_client_rpc(self, user_id, scheme, hostname, port, username, password, path): def add_client_rpc(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)) log.info("%s", (user_id, scheme, hostname, port, username, password, path))
x = self.execute(sql, (user_id, scheme, hostname, port, username, password, path))
log.info("add_client_rpc: %s", x)
return True return True
else:
return False
def get_client_rpc(self, user_id): def get_client_rpc(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,)) res = self.execute(sql, (user_id,))
if len(res): if res:
return self.execute(sql, (user_id,))[0] return self.execute(sql, (user_id,))[0]
else: else:
return False return False
@@ -230,6 +230,7 @@ class DataBase:
torrents t JOIN alerts a ON a.tor_id = t.id GROUP BY t.id""" torrents t JOIN alerts a ON a.tor_id = t.id GROUP BY t.id"""
raw = self.execute(sql, ()) raw = self.execute(sql, ())
alerts = list() alerts = list()
if not isinstance(raw, int):
for alert in raw: for alert in raw:
tmp = dict() tmp = dict()
tmp['id'] = alert[3] tmp['id'] = alert[3]

View File

@@ -5,7 +5,7 @@ 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 send_to_client_rpc from .torrent_clients import easy_send
UPDATE_INTERVAL = 2 * 60 * 60 # in secs. UPDATE_INTERVAL = 2 * 60 * 60 # in secs.
@@ -58,8 +58,8 @@ def update_watcher(bot):
for sub in subs: for sub in subs:
try: try:
scheme, hostname, port, username, password, path = torrent.db.get_client_rpc(sub) scheme, hostname, port, username, password, path = torrent.db.get_client_rpc(sub)
if send_to_client_rpc(scheme, hostname, port, username, password, path, torrent.meta['info_hash']): if easy_send(torrent.meta['info_hash'], sub):
log.info("Push update to client Transmission RPC for %s", torrent.meta['info_hash']) log.info("Push update to client %s", torrent.meta['info_hash'])
msg = f"{msg}📨 <b>Sent to RPC /client</b>" msg = f"{msg}📨 <b>Sent to RPC /client</b>"
else: else:
log.warning("Failed push update to client Transmission RPC for %s", log.warning("Failed push update to client Transmission RPC for %s",

29
gaspar/qbittorrent.py Normal file
View File

@@ -0,0 +1,29 @@
import qbittorrentapi as qbt
import logging
log = logging.getLogger(__name__)
def easy_send(torent, client_id):
try:
scheme, hostname, port, username, password, path = torent.db.get_client_rpc(client_id['id'])
if send_to_client_rpc(scheme, hostname, port, username, password, path, torent.meta['info_hash']):
log.info("Push update to qBittorrent client for %s", torent.meta['topic_title'])
except Exception as e:
log.warning("Failed push update to qBittorrent client for %s: %s",
torent.meta['topic_title'], e)
def send_to_client_rpc(scheme, hostname, port, username, password, path, tor_hash):
try:
host = f"{scheme}://{hostname}{path if path != None else ''}"
c = qbt.Client(
host=host,
port=port,
username=username,
password=password)
m = f'magnet:?xt=urn:btih:{tor_hash}'
c.torrents_add(urls=m)
return True
except Excepion as exc:
log.warn("Failed to send to qBittorrent: %s", host)
return False

View File

@@ -40,9 +40,9 @@ CREATE TABLE IF NOT EXISTS "tr_clients" (
user_id TEXT, user_id TEXT,
scheme TEXT, scheme TEXT,
hostname TEXT, hostname TEXT,
port TEXT, port INTEGER,
username TEXT, username TEXT DEFAULT NULL,
password TEXT, password TEXT DEFAULT NULL,
path TEXT, path TEXT,
UNIQUE(user_id) UNIQUE(user_id)
); );

96
gaspar/torrent_clients.py Normal file
View File

@@ -0,0 +1,96 @@
#from .transmission import easy_send
#from transmission_rpc import Client
import qbittorrentapi as qbt
import transmission_rpc as transm
from urllib import parse
import logging
from .rutracker import Torrent
from .transmission import send_to_client_rpc as send_to_transm
from .qbittorrent import send_to_client_rpc as send_to_qbt
log = logging.getLogger(__name__)
def easy_send(torrent_hash, user_id):
try:
torrent = Torrent()
scheme, hostname, port, username, password, path = torrent.db.get_client_rpc(user_id)
user_pass = f"{username}:{password}@" if password and username else ""
tr_client_check = detect_client(f"{scheme}://{user_pass}{hostname}:{port}{path if path != None else ''}")
if tr_client_check == "Transmission":
if send_to_transm(scheme, hostname, port, username, password, path, torrent_hash):
log.info(f"Push update to client {tr_client_check} RPC for %s", torrent_hash)
elif tr_client_check == "qBittorrent":
if send_to_qbt(scheme, hostname, port, username, password, path, torrent_hash):
log.info(f"Push update to client {tr_client_check} RPC for %s", torrent_hash)
else:
return False
except Exception as e:
log.warning("Failed push update to client for %s: %s",
torrent_hash, e)
return True
def _parse_address(address):
client = {}
try:
tr = parse.urlparse(address)
client["scheme"] = tr.scheme if tr.scheme else False
client["hostname"] = tr.hostname if tr.hostname else False
client["username"] = tr.username if tr.username else None
client["password"] = tr.password if tr.password else None
client["path"] = tr.path if tr.path else None
client["port"] = tr.port if tr.port else (80 if client["scheme"] == 'http' else 443)
except Exception as e:
log.debug("_parse_address: %s", e)
pass
return client
def detect_client(address):
client = False
tr = _parse_address(address)
# Check for qBittorrent v4.1.0+
try:
qbt_client = qbt.Client(
host=f"{tr['scheme']}://{tr['hostname']}",
port=tr['port'],
username=tr['username'],
password=tr['password'])
try:
qbt_client.auth_log_in()
except qbt.LoginFailed as e:
pass
client = "qBittorrent"
except Exception as e:
log.debug("detect_client.qBittorrent: %s", e)
pass
# Check for Transmission
try:
c = transm.Client(
host=tr['hostname'],
port=tr['port'],
username=tr['username'],
password=tr['password'],
protocol=tr['scheme'],
path=tr['path'])
c.rpc_version
client = "Transmission"
except Exception as e:
log.debug("detect_client.Transmission: %s", e)
pass
log.info(f"Detected {client}")
return client
def add_client(address, u_id):
tr = _parse_address(address)
client = detect_client(address)
if not client:
return client
if Torrent().db.add_client_rpc(
u_id,
tr['scheme'],
tr['hostname'],
tr['port'],
tr['username'],
tr['password'],
tr['path']):
return f"Added {client}"

View File

@@ -3,16 +3,6 @@ import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def easy_send(torent, client_id):
try:
scheme, hostname, port, username, password, path = torent.db.get_client_rpc(client_id['id'])
if send_to_client_rpc(scheme, hostname, port, username, password, path, torent.meta['info_hash']):
log.info("Push update to client Transmission RPC for %s", torent.meta['topic_title'])
except Exception as e:
log.warning("Failed push update to client Transmission RPC for %s: %s",
torent.meta['topic_title'], e)
def send_to_client_rpc(scheme, hostname, port, username, password, path, tor_hash): def send_to_client_rpc(scheme, hostname, port, username, password, path, tor_hash):
try: try:
c = Client( c = Client(

View File

@@ -1,2 +1,3 @@
python-telegram-bot==13.4 python-telegram-bot==13.4
transmission-rpc==2.0.4 transmission-rpc==2.0.4
qbittorrent-api==v2021.8.23

View File

@@ -28,7 +28,7 @@ setup(
packages=['gaspar'], packages=['gaspar'],
long_description=read('README'), long_description=read('README'),
classifiers=[ classifiers=[
"Development Status :: 3 - Alpha", "Development Status :: 3 - Beta",
"Topic :: Utilities", "Topic :: Utilities",
], ],
entry_points={ entry_points={