Path: blob/master/bot/modules/rss.py
1621 views
from httpx import AsyncClient1from apscheduler.triggers.interval import IntervalTrigger2from asyncio import Lock, sleep3from datetime import datetime, timedelta4from feedparser import parse as feed_parse5from functools import partial6from io import BytesIO7from pyrogram.filters import create8from pyrogram.handlers import MessageHandler9from time import time10from re import compile, I1112from .. import scheduler, rss_dict, LOGGER13from ..core.config_manager import Config14from ..helper.ext_utils.bot_utils import new_task, arg_parser, get_size_bytes15from ..helper.ext_utils.status_utils import get_readable_file_size16from ..helper.ext_utils.db_handler import database17from ..helper.ext_utils.exceptions import RssShutdownException18from ..helper.ext_utils.help_messages import RSS_HELP_MESSAGE19from ..helper.telegram_helper.button_build import ButtonMaker20from ..helper.telegram_helper.filters import CustomFilters21from ..helper.telegram_helper.message_utils import (22send_message,23edit_message,24send_rss,25send_file,26delete_message,27)2829rss_dict_lock = Lock()30handler_dict = {}31size_regex = compile(r"(\d+(\.\d+)?\s?(GB|MB|KB|GiB|MiB|KiB))", I)3233headers = {34"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",35"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",36"Accept-Language": "en-US,en;q=0.5",37}383940async def rss_menu(event):41user_id = event.from_user.id42buttons = ButtonMaker()43buttons.data_button("Subscribe", f"rss sub {user_id}")44buttons.data_button("Subscriptions", f"rss list {user_id} 0")45buttons.data_button("Get Items", f"rss get {user_id}")46buttons.data_button("Edit", f"rss edit {user_id}")47buttons.data_button("Pause", f"rss pause {user_id}")48buttons.data_button("Resume", f"rss resume {user_id}")49buttons.data_button("Unsubscribe", f"rss unsubscribe {user_id}")50if await CustomFilters.sudo("", event):51buttons.data_button("All Subscriptions", f"rss listall {user_id} 0")52buttons.data_button("Pause All", f"rss allpause {user_id}")53buttons.data_button("Resume All", f"rss allresume {user_id}")54buttons.data_button("Unsubscribe All", f"rss allunsub {user_id}")55buttons.data_button("Delete User", f"rss deluser {user_id}")56if scheduler.running:57buttons.data_button("Shutdown Rss", f"rss shutdown {user_id}")58else:59buttons.data_button("Start Rss", f"rss start {user_id}")60buttons.data_button("Close", f"rss close {user_id}")61button = buttons.build_menu(2)62msg = f"Rss Menu | Users: {len(rss_dict)} | Running: {scheduler.running}"63return msg, button646566async def update_rss_menu(query):67msg, button = await rss_menu(query)68await edit_message(query.message, msg, button)697071@new_task72async def get_rss_menu(_, message):73msg, button = await rss_menu(message)74await send_message(message, msg, button)757677@new_task78async def rss_sub(_, message, pre_event):79user_id = message.from_user.id80handler_dict[user_id] = False81if username := message.from_user.username:82tag = f"@{username}"83else:84tag = message.from_user.mention85msg = ""86items = message.text.split("\n")87for index, item in enumerate(items, start=1):88args = item.split()89if len(args) < 2:90await send_message(91message,92f"{item}. Wrong Input format. Read help message before adding new subscription!",93)94continue95title = args[0].strip()96if (user_feeds := rss_dict.get(user_id, False)) and title in user_feeds:97await send_message(98message, f"This title {title} already subscribed! Choose another title!"99)100continue101feed_link = args[1].strip()102if feed_link.startswith(("-inf", "-exf", "-c")):103await send_message(104message,105f"Wrong input in line {index}! Add Title! Read the example!",106)107continue108inf_lists = []109exf_lists = []110if len(args) > 2:111arg_base = {"-c": None, "-inf": None, "-exf": None, "-stv": None}112arg_parser(args[2:], arg_base)113cmd = arg_base["-c"]114inf = arg_base["-inf"]115exf = arg_base["-exf"]116stv = arg_base["-stv"]117if stv is not None:118stv = stv.lower() == "true"119if inf is not None:120filters_list = inf.split("|")121for x in filters_list:122y = x.split(" or ")123inf_lists.append(y)124if exf is not None:125filters_list = exf.split("|")126for x in filters_list:127y = x.split(" or ")128exf_lists.append(y)129else:130inf = None131exf = None132cmd = None133stv = False134try:135async with AsyncClient(136headers=headers, follow_redirects=True, timeout=60, verify=False137) as client:138res = await client.get(feed_link)139html = res.text140rss_d = feed_parse(html)141last_title = rss_d.entries[0]["title"]142if rss_d.entries[0].get("size"):143size = int(rss_d.entries[0]["size"])144elif rss_d.entries[0].get("summary"):145summary = rss_d.entries[0]["summary"]146matches = size_regex.findall(summary)147sizes = [match[0] for match in matches]148size = get_size_bytes(sizes[0])149else:150size = 0151msg += "<b>Subscribed!</b>"152msg += f"\n<b>Title: </b><code>{title}</code>\n<b>Feed Url: </b>{feed_link}"153msg += f"\n<b>latest record for </b>{rss_d.feed.title}:"154msg += (155f"\nName: <code>{last_title.replace('>', '').replace('<', '')}</code>"156)157try:158last_link = rss_d.entries[0]["links"][1]["href"]159except IndexError:160last_link = rss_d.entries[0]["link"]161msg += f"\n<b>Link: </b><code>{last_link}</code>"162if size:163msg += f"\nSize: {get_readable_file_size(size)}"164msg += f"\n<b>Command: </b><code>{cmd}</code>"165msg += f"\n<b>Filters:-</b>\ninf: <code>{inf}</code>\nexf: <code>{exf}</code>\n<b>sensitive: </b>{stv}"166async with rss_dict_lock:167if rss_dict.get(user_id, False):168rss_dict[user_id][title] = {169"link": feed_link,170"last_feed": last_link,171"last_title": last_title,172"inf": inf_lists,173"exf": exf_lists,174"paused": False,175"command": cmd,176"sensitive": stv,177"tag": tag,178}179else:180rss_dict[user_id] = {181title: {182"link": feed_link,183"last_feed": last_link,184"last_title": last_title,185"inf": inf_lists,186"exf": exf_lists,187"paused": False,188"command": cmd,189"sensitive": stv,190"tag": tag,191}192}193LOGGER.info(194f"Rss Feed Added: id: {user_id} - title: {title} - link: {feed_link} - c: {cmd} - inf: {inf} - exf: {exf} - stv: {stv}"195)196except (IndexError, AttributeError) as e:197emsg = f"The link: {feed_link} doesn't seem to be a RSS feed or it's region-blocked!"198await send_message(message, emsg + "\nError: " + str(e))199except Exception as e:200await send_message(message, str(e))201if msg:202await database.rss_update(user_id)203await send_message(message, msg)204is_sudo = await CustomFilters.sudo("", message)205if scheduler.state == 2:206scheduler.resume()207elif is_sudo and not scheduler.running:208add_job()209scheduler.start()210await update_rss_menu(pre_event)211212213async def get_user_id(title):214async with rss_dict_lock:215return next(216(217(True, user_id)218for user_id, feed in rss_dict.items()219if feed["title"] == title220),221(False, False),222)223224225@new_task226async def rss_update(_, message, pre_event, state):227user_id = message.from_user.id228handler_dict[user_id] = False229titles = message.text.split()230is_sudo = await CustomFilters.sudo("", message)231updated = []232for title in titles:233title = title.strip()234if not (res := rss_dict[user_id].get(title, False)):235if is_sudo:236res, user_id = await get_user_id(title)237if not res:238user_id = message.from_user.id239await send_message(message, f"{title} not found!")240continue241istate = rss_dict[user_id][title].get("paused", False)242if istate and state == "pause" or not istate and state == "resume":243await send_message(message, f"{title} already {state}d!")244continue245async with rss_dict_lock:246updated.append(title)247if state == "unsubscribe":248del rss_dict[user_id][title]249elif state == "pause":250rss_dict[user_id][title]["paused"] = True251elif state == "resume":252rss_dict[user_id][title]["paused"] = False253if state == "resume":254if scheduler.state == 2:255scheduler.resume()256elif is_sudo and not scheduler.running:257add_job()258scheduler.start()259if is_sudo and Config.DATABASE_URL and user_id != message.from_user.id:260await database.rss_update(user_id)261if not rss_dict[user_id]:262async with rss_dict_lock:263del rss_dict[user_id]264await database.rss_delete(user_id)265if not rss_dict:266await database.trunc_table("rss")267if updated:268LOGGER.info(f"Rss link with Title(s): {updated} has been {state}d!")269await send_message(270message,271f"Rss links with Title(s): <code>{updated}</code> has been {state}d!",272)273if rss_dict.get(user_id):274await database.rss_update(user_id)275await update_rss_menu(pre_event)276277278async def rss_list(query, start, all_users=False):279user_id = query.from_user.id280buttons = ButtonMaker()281if all_users:282list_feed = f"<b>All subscriptions | Page: {int(start / 5)} </b>"283async with rss_dict_lock:284keysCount = sum(len(v.keys()) for v in rss_dict.values())285index = 0286for titles in rss_dict.values():287for index, (title, data) in enumerate(288list(titles.items())[start : 5 + start]289):290list_feed += f"\n\n<b>Title:</b> <code>{title}</code>\n"291list_feed += f"<b>Feed Url:</b> <code>{data['link']}</code>\n"292list_feed += f"<b>Command:</b> <code>{data['command']}</code>\n"293list_feed += f"<b>Inf:</b> <code>{data['inf']}</code>\n"294list_feed += f"<b>Exf:</b> <code>{data['exf']}</code>\n"295list_feed += f"<b>Sensitive:</b> <code>{data.get('sensitive', False)}</code>\n"296list_feed += f"<b>Paused:</b> <code>{data['paused']}</code>\n"297list_feed += f"<b>User:</b> {data['tag'].replace('@', '', 1)}"298index += 1299if index == 5:300break301else:302list_feed = f"<b>Your subscriptions | Page: {int(start / 5)} </b>"303async with rss_dict_lock:304keysCount = len(rss_dict.get(user_id, {}).keys())305for title, data in list(rss_dict[user_id].items())[start : 5 + start]:306list_feed += f"\n\n<b>Title:</b> <code>{title}</code>\n<b>Feed Url: </b><code>{data['link']}</code>\n"307list_feed += f"<b>Command:</b> <code>{data['command']}</code>\n"308list_feed += f"<b>Inf:</b> <code>{data['inf']}</code>\n"309list_feed += f"<b>Exf:</b> <code>{data['exf']}</code>\n"310list_feed += (311f"<b>Sensitive:</b> <code>{data.get('sensitive', False)}</code>\n"312)313list_feed += f"<b>Paused:</b> <code>{data['paused']}</code>\n"314buttons.data_button("Back", f"rss back {user_id}")315buttons.data_button("Close", f"rss close {user_id}")316if keysCount > 5:317for x in range(0, keysCount, 5):318buttons.data_button(319f"{int(x / 5)}", f"rss list {user_id} {x}", position="footer"320)321button = buttons.build_menu(2)322if query.message.text.html == list_feed:323return324await edit_message(query.message, list_feed, button)325326327@new_task328async def rss_get(_, message, pre_event):329user_id = message.from_user.id330handler_dict[user_id] = False331args = message.text.split()332if len(args) < 2:333await send_message(334message,335f"{args}. Wrong Input format. You should add number of the items you want to get. Read help message before adding new subscription!",336)337await update_rss_menu(pre_event)338return339try:340title = args[0]341count = int(args[1])342data = rss_dict[user_id].get(title, False)343if data and count > 0:344try:345msg = await send_message(346message, f"Getting the last <b>{count}</b> item(s) from {title}"347)348async with AsyncClient(349headers=headers, follow_redirects=True, timeout=60, verify=False350) as client:351res = await client.get(data["link"])352html = res.text353rss_d = feed_parse(html)354item_info = ""355for item_num in range(count):356try:357link = rss_d.entries[item_num]["links"][1]["href"]358except IndexError:359link = rss_d.entries[item_num]["link"]360item_info += f"<b>Name: </b><code>{rss_d.entries[item_num]['title'].replace('>', '').replace('<', '')}</code>\n"361item_info += f"<b>Link: </b><code>{link}</code>\n\n"362item_info_ecd = item_info.encode()363if len(item_info_ecd) > 4000:364with BytesIO(item_info_ecd) as out_file:365out_file.name = f"rssGet {title} items_no. {count}.txt"366await send_file(message, out_file)367await delete_message(msg)368else:369await edit_message(msg, item_info)370except IndexError as e:371LOGGER.error(str(e))372await edit_message(373msg, "Parse depth exceeded. Try again with a lower value."374)375except Exception as e:376LOGGER.error(str(e))377await edit_message(msg, str(e))378else:379await send_message(message, "Enter a valid title. Title not found!")380except Exception as e:381LOGGER.error(str(e))382await send_message(message, f"Enter a valid value!. {e}")383await update_rss_menu(pre_event)384385386@new_task387async def rss_edit(_, message, pre_event):388user_id = message.from_user.id389handler_dict[user_id] = False390items = message.text.split("\n")391updated = False392for item in items:393args = item.split()394title = args[0].strip()395if len(args) < 2:396await send_message(397message,398f"{item}. Wrong Input format. Read help message before editing!",399)400continue401elif not rss_dict[user_id].get(title, False):402await send_message(message, "Enter a valid title. Title not found!")403continue404updated = True405inf_lists = []406exf_lists = []407arg_base = {"-c": None, "-inf": None, "-exf": None, "-stv": None}408arg_parser(args[1:], arg_base)409cmd = arg_base["-c"]410inf = arg_base["-inf"]411exf = arg_base["-exf"]412stv = arg_base["-stv"]413async with rss_dict_lock:414if stv is not None:415stv = stv.lower() == "true"416rss_dict[user_id][title]["sensitive"] = stv417if cmd is not None:418if cmd.lower() == "none":419cmd = None420rss_dict[user_id][title]["command"] = cmd421if inf is not None:422if inf.lower() != "none":423filters_list = inf.split("|")424for x in filters_list:425y = x.split(" or ")426inf_lists.append(y)427rss_dict[user_id][title]["inf"] = inf_lists428if exf is not None:429if exf.lower() != "none":430filters_list = exf.split("|")431for x in filters_list:432y = x.split(" or ")433exf_lists.append(y)434rss_dict[user_id][title]["exf"] = exf_lists435if updated:436await database.rss_update(user_id)437await update_rss_menu(pre_event)438439440@new_task441async def rss_delete(_, message, pre_event):442handler_dict[message.from_user.id] = False443users = message.text.split()444for user in users:445user = int(user)446async with rss_dict_lock:447del rss_dict[user]448await database.rss_delete(user)449await update_rss_menu(pre_event)450451452async def event_handler(client, query, pfunc):453user_id = query.from_user.id454handler_dict[user_id] = True455start_time = time()456457async def event_filter(_, __, event):458user = event.from_user or event.sender_chat459return bool(460user.id == user_id and event.chat.id == query.message.chat.id and event.text461)462463handler = client.add_handler(MessageHandler(pfunc, create(event_filter)), group=-1)464while handler_dict[user_id]:465await sleep(0.5)466if time() - start_time > 60:467handler_dict[user_id] = False468await update_rss_menu(query)469client.remove_handler(*handler)470471472@new_task473async def rss_listener(client, query):474user_id = query.from_user.id475message = query.message476data = query.data.split()477if int(data[2]) != user_id and not await CustomFilters.sudo("", query):478await query.answer(479text="You don't have permission to use these buttons!", show_alert=True480)481elif data[1] == "close":482await query.answer()483handler_dict[user_id] = False484await delete_message(message.reply_to_message)485await delete_message(message)486elif data[1] == "back":487await query.answer()488handler_dict[user_id] = False489await update_rss_menu(query)490elif data[1] == "sub":491await query.answer()492handler_dict[user_id] = False493buttons = ButtonMaker()494buttons.data_button("Back", f"rss back {user_id}")495buttons.data_button("Close", f"rss close {user_id}")496button = buttons.build_menu(2)497await edit_message(message, RSS_HELP_MESSAGE, button)498pfunc = partial(rss_sub, pre_event=query)499await event_handler(client, query, pfunc)500elif data[1] == "list":501handler_dict[user_id] = False502if len(rss_dict.get(int(data[2]), {})) == 0:503await query.answer(text="No subscriptions!", show_alert=True)504else:505await query.answer()506start = int(data[3])507await rss_list(query, start)508elif data[1] == "get":509handler_dict[user_id] = False510if len(rss_dict.get(int(data[2]), {})) == 0:511await query.answer(text="No subscriptions!", show_alert=True)512else:513await query.answer()514buttons = ButtonMaker()515buttons.data_button("Back", f"rss back {user_id}")516buttons.data_button("Close", f"rss close {user_id}")517button = buttons.build_menu(2)518await edit_message(519message,520"Send one title with value separated by space get last X items.\nTitle Value\nTimeout: 60 sec.",521button,522)523pfunc = partial(rss_get, pre_event=query)524await event_handler(client, query, pfunc)525elif data[1] in ["unsubscribe", "pause", "resume"]:526handler_dict[user_id] = False527if len(rss_dict.get(int(data[2]), {})) == 0:528await query.answer(text="No subscriptions!", show_alert=True)529else:530await query.answer()531buttons = ButtonMaker()532buttons.data_button("Back", f"rss back {user_id}")533if data[1] == "pause":534buttons.data_button("Pause AllMyFeeds", f"rss uallpause {user_id}")535elif data[1] == "resume":536buttons.data_button("Resume AllMyFeeds", f"rss uallresume {user_id}")537elif data[1] == "unsubscribe":538buttons.data_button("Unsub AllMyFeeds", f"rss uallunsub {user_id}")539buttons.data_button("Close", f"rss close {user_id}")540button = buttons.build_menu(2)541await edit_message(542message,543f"Send one or more rss titles separated by space to {data[1]}.\nTimeout: 60 sec.",544button,545)546pfunc = partial(rss_update, pre_event=query, state=data[1])547await event_handler(client, query, pfunc)548elif data[1] == "edit":549handler_dict[user_id] = False550if len(rss_dict.get(int(data[2]), {})) == 0:551await query.answer(text="No subscriptions!", show_alert=True)552else:553await query.answer()554buttons = ButtonMaker()555buttons.data_button("Back", f"rss back {user_id}")556buttons.data_button("Close", f"rss close {user_id}")557button = buttons.build_menu(2)558msg = """Send one or more rss titles with new filters or command separated by new line.559Examples:560Title1 -c mirror -up remote:path/subdir -exf none -inf 1080 or 720 -stv true561Title2 -c none -inf none -stv false562Title3 -c mirror -rcf xxx -up xxx -z pswd -stv false563Note: Only what you provide will be edited, the rest will be the same like example 2: exf will stay same as it is.564Timeout: 60 sec. Argument -c for command and arguments565"""566await edit_message(message, msg, button)567pfunc = partial(rss_edit, pre_event=query)568await event_handler(client, query, pfunc)569elif data[1].startswith("uall"):570handler_dict[user_id] = False571if len(rss_dict.get(int(data[2]), {})) == 0:572await query.answer(text="No subscriptions!", show_alert=True)573return574await query.answer()575if data[1].endswith("unsub"):576async with rss_dict_lock:577del rss_dict[int(data[2])]578await database.rss_delete(int(data[2]))579await update_rss_menu(query)580elif data[1].endswith("pause"):581async with rss_dict_lock:582for info in rss_dict[int(data[2])].values():583info["paused"] = True584await database.rss_update(int(data[2]))585elif data[1].endswith("resume"):586async with rss_dict_lock:587for info in rss_dict[int(data[2])].values():588info["paused"] = False589if scheduler.state == 2:590scheduler.resume()591await database.rss_update(int(data[2]))592await update_rss_menu(query)593elif data[1].startswith("all"):594if len(rss_dict) == 0:595await query.answer(text="No subscriptions!", show_alert=True)596return597await query.answer()598if data[1].endswith("unsub"):599async with rss_dict_lock:600rss_dict.clear()601await database.trunc_table("rss")602await update_rss_menu(query)603elif data[1].endswith("pause"):604async with rss_dict_lock:605for user_feeds in rss_dict.values():606for feed in user_feeds.values():607feed["paused"] = True608if scheduler.running:609scheduler.pause()610await database.rss_update_all()611elif data[1].endswith("resume"):612async with rss_dict_lock:613for user_feeds in rss_dict.values():614for feed in user_feeds.values():615feed["paused"] = False616if scheduler.state == 2:617scheduler.resume()618elif not scheduler.running:619add_job()620scheduler.start()621await update_rss_menu(query)622await database.rss_update_all()623elif data[1] == "deluser":624if len(rss_dict) == 0:625await query.answer(text="No subscriptions!", show_alert=True)626else:627await query.answer()628buttons = ButtonMaker()629buttons.data_button("Back", f"rss back {user_id}")630buttons.data_button("Close", f"rss close {user_id}")631button = buttons.build_menu(2)632msg = "Send one or more user_id separated by space to delete their resources.\nTimeout: 60 sec."633await edit_message(message, msg, button)634pfunc = partial(rss_delete, pre_event=query)635await event_handler(client, query, pfunc)636elif data[1] == "listall":637if not rss_dict:638await query.answer(text="No subscriptions!", show_alert=True)639else:640await query.answer()641start = int(data[3])642await rss_list(query, start, all_users=True)643elif data[1] == "shutdown":644if scheduler.running:645await query.answer()646scheduler.shutdown(wait=False)647await sleep(0.5)648await update_rss_menu(query)649else:650await query.answer(text="Already Stopped!", show_alert=True)651elif data[1] == "start":652if not scheduler.running:653await query.answer()654add_job()655scheduler.start()656await update_rss_menu(query)657else:658await query.answer(text="Already Running!", show_alert=True)659660661async def rss_monitor():662chat = Config.RSS_CHAT663if not chat:664LOGGER.warning("RSS_CHAT not added! Shutting down rss scheduler...")665scheduler.shutdown(wait=False)666return667if len(rss_dict) == 0:668scheduler.pause()669return670all_paused = True671rss_topic_id = rss_chat_id = None672if isinstance(chat, int):673rss_chat_id = chat674elif "|" in chat:675rss_chat_id, rss_topic_id = list(676map(677lambda x: int(x) if x.lstrip("-").isdigit() else x,678chat.split("|", 1),679)680)681elif chat.lstrip("-").isdigit():682rss_chat_id = int(chat)683for user, items in list(rss_dict.items()):684for title, data in items.items():685try:686if data["paused"]:687continue688tries = 0689while True:690try:691async with AsyncClient(692headers=headers,693follow_redirects=True,694timeout=60,695verify=False,696) as client:697res = await client.get(data["link"])698html = res.text699break700except:701tries += 1702if tries > 3:703raise704continue705rss_d = feed_parse(html)706if not rss_d.entries:707LOGGER.warning(708f"No entries found for > Feed Title: {title} - Feed Link: {data['link']}"709)710continue711entry0 = rss_d.entries[0]712links = entry0.get("links", [])713if len(links) > 1:714last_link = links[1].get("href")715elif links:716last_link = links[0].get("href")717else:718last_link = entry0.get("link")719last_title = entry0.get("title")720all_paused = False721if data["last_feed"] == last_link or data["last_title"] == last_title:722continue723feed_count = 0724while True:725try:726await sleep(10)727except:728raise RssShutdownException("Rss Monitor Stopped!")729try:730item_title = rss_d.entries[feed_count]["title"]731try:732url = rss_d.entries[feed_count]["links"][1]["href"]733except IndexError:734url = rss_d.entries[feed_count]["link"]735if data["last_feed"] == url or data["last_title"] == item_title:736break737if rss_d.entries[feed_count].get("size"):738size = int(rss_d.entries[feed_count]["size"])739elif rss_d.entries[feed_count].get("summary"):740summary = rss_d.entries[feed_count]["summary"]741matches = size_regex.findall(summary)742sizes = [match[0] for match in matches]743size = get_size_bytes(sizes[0])744else:745size = 0746except IndexError:747LOGGER.warning(748f"Reached Max index no. {feed_count} for this feed: {title}. Maybe you need to use less RSS_DELAY to not miss some torrents"749)750break751parse = True752for flist in data["inf"]:753if (754data.get("sensitive", False)755and all(x.lower() not in item_title.lower() for x in flist)756) or (757not data.get("sensitive", False)758and all(x not in item_title for x in flist)759):760parse = False761feed_count += 1762break763if not parse:764continue765for flist in data["exf"]:766if (767data.get("sensitive", False)768and any(x.lower() in item_title.lower() for x in flist)769) or (770not data.get("sensitive", False)771and any(x in item_title for x in flist)772):773parse = False774feed_count += 1775break776if not parse:777continue778if command := data["command"]:779if (780size781and Config.RSS_SIZE_LIMIT782and Config.RSS_SIZE_LIMIT < size783):784feed_count += 1785continue786cmd = command.split(maxsplit=1)787cmd.insert(1, url)788feed_msg = " ".join(cmd)789if not feed_msg.startswith("/"):790feed_msg = f"/{feed_msg}"791else:792feed_msg = f"<b>Name: </b><code>{item_title.replace('>', '').replace('<', '')}</code>"793feed_msg += f"\n\n<b>Link: </b><code>{url}</code>"794if size:795feed_msg += f"\n<b>Size: </b>{get_readable_file_size(size)}"796feed_msg += (797f"\n<b>Tag: </b><code>{data['tag']}</code> <code>{user}</code>"798)799await send_rss(feed_msg, rss_chat_id, rss_topic_id)800feed_count += 1801async with rss_dict_lock:802if user not in rss_dict or not rss_dict[user].get(title, False):803continue804rss_dict[user][title].update(805{"last_feed": last_link, "last_title": last_title}806)807await database.rss_update(user)808LOGGER.info(f"Feed Name: {title}")809LOGGER.info(f"Last item: {last_link}")810except RssShutdownException as ex:811LOGGER.info(ex)812break813except Exception as e:814LOGGER.error(f"{e} - Feed Name: {title} - Feed Link: {data['link']}")815continue816if all_paused:817scheduler.pause()818819820def add_job():821scheduler.add_job(822rss_monitor,823trigger=IntervalTrigger(seconds=Config.RSS_DELAY),824id="0",825name="RSS",826misfire_grace_time=15,827max_instances=1,828next_run_time=datetime.now() + timedelta(seconds=20),829replace_existing=True,830)831832833add_job()834scheduler.start()835836837