update
swadhinbiswas committed 2 years ago
2e0fc11
1 parent 8c4f84e
commit 2e0fc11
9 files changed
+671 -19
Showing 9 changed files+671-19
app/__init__.pyapp/bot/helper/ext-utils/help_message.pyapp/bot/helper/ext_utils/__init__.pyapp/bot/helper/ext_utils/bot_utils.pyapp/bot/helper/ext_utils/dbhandelar.pyapp/bot/helper/ext_utils/help_message.pyapp/bot/helper/ext_utils/status.pyapp/bot/helper/ext_utils/telegraph_helper.pyapp/settings/config.py
βΌapp/__init__.py
+0-0
0 | 0 |
βΌapp/bot/helper/ext-utils/help_message.py
+0-19
| @@ -1,19 +0,0 @@ | ||
1 | -mirror=""" | |
2 | -<b>π° Mirror Bot</b> | |
3 | -<b>π° Version:</b> <code>1.0.0</code> | |
4 | -/cmd link | |
5 | - | |
6 | -<b>By replying to link/file</b>: <code>mirror</code> | |
7 | - | |
8 | -/cmd -n new name -e -up upload destination | |
9 | -<b>Example:</b> <code>/cmd -n new name -e -up upload destination</code> | |
10 | - | |
11 | -<b>NOTE:</b> | |
12 | -1. Commands that start with <b>qb</b> are ONLY for torrents. | |
13 | - | |
14 | -""" | |
15 | - | |
16 | -yt="""<b>π° YouTube Downloader</b> | |
17 | - | |
18 | - | |
19 | -""" | |
20 | 0 | |
βΌapp/bot/helper/ext-utils/__init__.py β app/bot/helper/ext_utils/__init__.py
+0-0
0 | 0 |
βΌapp/bot/helper/ext_utils/bot_utils.py
+225-0
| @@ -0,0 +1,225 @@ | ||
1 | +from aiohttp import ClientSession | |
2 | + | |
3 | +from asyncio import ( | |
4 | + create_subprocess_exec, | |
5 | + create_subprocess_shell, | |
6 | + run_coroutine_threadsafe, | |
7 | + sleep, | |
8 | +) | |
9 | + | |
10 | +from asyncio.subprocess import PIPE | |
11 | +from concurrent.futures import ThreadPoolExecutor | |
12 | +from functools import partial, wraps | |
13 | + | |
14 | +from bot import user_data,config_dict,bot_loop | |
15 | + | |
16 | +from bot.helper.ext_utils.bot_utils import ( | |
17 | + YT_HELP_DICT, | |
18 | + MIRROR_HELP_DICT,) | |
19 | +from app.bot.helper.ext_utils import telegraph | |
20 | +from bot.helper.telegram_helper.button_build import ButtonMaker | |
21 | + | |
22 | +THREADPOOL = ThreadPoolExecutor(max_workers=1000) | |
23 | + | |
24 | +COMMAND_USAGE = {} | |
25 | + | |
26 | + | |
27 | +class setInterval: | |
28 | + def __init__(self, interval, action, *args, **kwargs): | |
29 | + self.interval = interval | |
30 | + self.action = action | |
31 | + self.task = bot_loop.create_task(self._set_interval(*args, **kwargs)) | |
32 | + | |
33 | + async def _set_interval(self, *args, **kwargs): | |
34 | + while True: | |
35 | + await sleep(self.interval) | |
36 | + await self.action(*args, **kwargs) | |
37 | + | |
38 | + def cancel(self): | |
39 | + self.task.cancel() | |
40 | + | |
41 | + | |
42 | +def create_help_buttons(): | |
43 | + buttons = ButtonMaker() | |
44 | + for name in list(MIRROR_HELP_DICT.keys())[1:]: | |
45 | + buttons.ibutton(name, f"help mirror {name}") | |
46 | + buttons.ibutton("Close", "help close") | |
47 | + COMMAND_USAGE["mirror"] = [MIRROR_HELP_DICT["main"], buttons.build_menu(3)] | |
48 | + buttons.reset() | |
49 | + for name in list(YT_HELP_DICT.keys())[1:]: | |
50 | + buttons.ibutton(name, f"help yt {name}") | |
51 | + buttons.ibutton("Close", "help close") | |
52 | + COMMAND_USAGE["yt"] = [YT_HELP_DICT["main"], buttons.build_menu(3)] | |
53 | + buttons.reset() | |
54 | + for name in list(CLONE_HELP_DICT.keys())[1:]: | |
55 | + buttons.ibutton(name, f"help clone {name}") | |
56 | + buttons.ibutton("Close", "help close") | |
57 | + COMMAND_USAGE["clone"] = [CLONE_HELP_DICT["main"], buttons.build_menu(3)] | |
58 | + | |
59 | + | |
60 | +def bt_selection_buttons(id_): | |
61 | + gid = id_[:12] if len(id_) > 20 else id_ | |
62 | + pincode = "".join([n for n in id_ if n.isdigit()][:4]) | |
63 | + buttons = ButtonMaker() | |
64 | + BASE_URL = config_dict["BASE_URL"] | |
65 | + if config_dict["WEB_PINCODE"]: | |
66 | + buttons.ubutton("Select Files", f"{BASE_URL}/app/files/{id_}") | |
67 | + buttons.ibutton("Pincode", f"btsel pin {gid} {pincode}") | |
68 | + else: | |
69 | + buttons.ubutton( | |
70 | + "Select Files", f"{BASE_URL}/app/files/{id_}?pin_code={pincode}" | |
71 | + ) | |
72 | + buttons.ibutton("Done Selecting", f"btsel done {gid} {id_}") | |
73 | + buttons.ibutton("Cancel", f"btsel cancel {gid}") | |
74 | + return buttons.build_menu(2) | |
75 | + | |
76 | + | |
77 | +async def get_telegraph_list(telegraph_content): | |
78 | + path = [ | |
79 | + ( | |
80 | + await telegraph.create_page( | |
81 | + title="Mirror-Leech-Bot Drive Search", content=content | |
82 | + ) | |
83 | + )["path"] | |
84 | + for content in telegraph_content | |
85 | + ] | |
86 | + if len(path) > 1: | |
87 | + await telegraph.edit_telegraph(path, telegraph_content) | |
88 | + buttons = ButtonMaker() | |
89 | + buttons.ubutton("π VIEW", f"https://telegra.ph/{path[0]}") | |
90 | + return buttons.build_menu(1) | |
91 | + | |
92 | + | |
93 | +def arg_parser(items, arg_base): | |
94 | + if not items: | |
95 | + return | |
96 | + bool_arg_set = { | |
97 | + "-b", | |
98 | + "-e", | |
99 | + "-z", | |
100 | + "-s", | |
101 | + "-j", | |
102 | + "-d", | |
103 | + "-sv", | |
104 | + "-ss", | |
105 | + "-f", | |
106 | + "-fd", | |
107 | + "-fu", | |
108 | + "-sync", | |
109 | + "-ml", | |
110 | + } | |
111 | + t = len(items) | |
112 | + i = 0 | |
113 | + arg_start = -1 | |
114 | + | |
115 | + while i + 1 <= t: | |
116 | + part = items[i] | |
117 | + if part in arg_base: | |
118 | + if arg_start == -1: | |
119 | + arg_start = i | |
120 | + if ( | |
121 | + i + 1 == t | |
122 | + and part in bool_arg_set | |
123 | + or part in ["-s", "-j", "-f", "-fd", "-fu", "-sync", "-ml"] | |
124 | + ): | |
125 | + arg_base[part] = True | |
126 | + else: | |
127 | + sub_list = [] | |
128 | + for j in range(i + 1, t): | |
129 | + item = items[j] | |
130 | + if item in arg_base: | |
131 | + if part in bool_arg_set and not sub_list: | |
132 | + arg_base[part] = True | |
133 | + break | |
134 | + sub_list.append(item) | |
135 | + i += 1 | |
136 | + if sub_list: | |
137 | + arg_base[part] = " ".join(sub_list) | |
138 | + i += 1 | |
139 | + if "link" in arg_base and items[0] not in arg_base: | |
140 | + link = [] | |
141 | + if arg_start == -1: | |
142 | + link.extend(iter(items)) | |
143 | + else: | |
144 | + link.extend(items[r] for r in range(arg_start)) | |
145 | + if link: | |
146 | + arg_base["link"] = " ".join(link) | |
147 | + | |
148 | + | |
149 | +def getSizeBytes(size): | |
150 | + size = size.lower() | |
151 | + if size.endswith("mb"): | |
152 | + size = size.split("mb")[0] | |
153 | + size = int(float(size) * 1048576) | |
154 | + elif size.endswith("gb"): | |
155 | + size = size.split("gb")[0] | |
156 | + size = int(float(size) * 1073741824) | |
157 | + else: | |
158 | + size = 0 | |
159 | + return size | |
160 | + | |
161 | + | |
162 | +async def get_content_type(url): | |
163 | + try: | |
164 | + async with ClientSession() as session: | |
165 | + async with session.get(url, allow_redirects=True, ssl=False) as response: | |
166 | + return response.headers.get("Content-Type") | |
167 | + except: | |
168 | + return None | |
169 | + | |
170 | + | |
171 | +def update_user_ldata(id_, key, value): | |
172 | + user_data.setdefault(id_, {}) | |
173 | + user_data[id_][key] = value | |
174 | + | |
175 | + | |
176 | +async def retry_function(func, *args, **kwargs): | |
177 | + try: | |
178 | + return await func(*args, **kwargs) | |
179 | + except: | |
180 | + return await retry_function(func, *args, **kwargs) | |
181 | + | |
182 | + | |
183 | +async def cmd_exec(cmd, shell=False): | |
184 | + if shell: | |
185 | + proc = await create_subprocess_shell(cmd, stdout=PIPE, stderr=PIPE) | |
186 | + else: | |
187 | + proc = await create_subprocess_exec(*cmd, stdout=PIPE, stderr=PIPE) | |
188 | + stdout, stderr = await proc.communicate() | |
189 | + try: | |
190 | + stdout = stdout.decode().strip() | |
191 | + except: | |
192 | + stdout = "Unable to decode the response!" | |
193 | + try: | |
194 | + stderr = stderr.decode().strip() | |
195 | + except: | |
196 | + stderr = "Unable to decode the error!" | |
197 | + return stdout, stderr, proc.returncode | |
198 | + | |
199 | + | |
200 | +def new_task(func): | |
201 | + @wraps(func) | |
202 | + def wrapper(*args, **kwargs): | |
203 | + return bot_loop.create_task(func(*args, **kwargs)) | |
204 | + | |
205 | + return wrapper | |
206 | + | |
207 | + | |
208 | +async def sync_to_async(func, *args, wait=True, **kwargs): | |
209 | + pfunc = partial(func, *args, **kwargs) | |
210 | + future = bot_loop.run_in_executor(THREADPOOL, pfunc) | |
211 | + return await future if wait else future | |
212 | + | |
213 | + | |
214 | +def async_to_sync(func, *args, wait=True, **kwargs): | |
215 | + future = run_coroutine_threadsafe(func(*args, **kwargs), bot_loop) | |
216 | + return future.result() if wait else future | |
217 | + | |
218 | + | |
219 | +def new_thread(func): | |
220 | + @wraps(func) | |
221 | + def wrapper(*args, wait=False, **kwargs): | |
222 | + future = run_coroutine_threadsafe(func(*args, **kwargs), bot_loop) | |
223 | + return future.result() if wait else future | |
224 | + | |
225 | + return wrapper | |
0 | 226 | |
βΌapp/bot/helper/ext_utils/dbhandelar.py
+37-0
| @@ -0,0 +1,37 @@ | ||
1 | +from aiofiles import open as aiopen | |
2 | +from aiofiles.os import path as aiopath,makedirs | |
3 | +from dotenv import load_dotenv,dotenv_values | |
4 | +from motor.motor_asyncio import AsyncIOMotorClient | |
5 | +from pymongo.errors import PyMongoError | |
6 | +from app.settings.config import MONGO_DB_URI,USER_DATA,LOGGER | |
7 | + | |
8 | +class DbManager: | |
9 | + def __init__(self): | |
10 | + self._err=False | |
11 | + self._db=None | |
12 | + self._conn=None | |
13 | + self._connect() | |
14 | + | |
15 | + def connect(self): | |
16 | + try: | |
17 | + self._conn = AsyncIOMotorClient(DATABASE_URL) | |
18 | + self._db = self._conn.mltb | |
19 | + except PyMongoError as e: | |
20 | + LOGGER.error(f"Error in DB connection: {e}") | |
21 | + self._err = True | |
22 | + | |
23 | + | |
24 | + async def close(self): | |
25 | + if self._conn: | |
26 | + self._conn.close() | |
27 | + LOGGER.info("DB Connection Closed") | |
28 | + | |
29 | + async def get_metadata(self): | |
30 | + return await self._db.metadata.find_all() | |
31 | + | |
32 | + | |
33 | + | |
34 | + | |
35 | + | |
36 | +if MONGO_DB_URI: | |
37 | + bot_loop.run_until_complete(DbManager().connect()) | |
0 | 38 | |
βΌapp/bot/helper/ext_utils/help_message.py
+96-0
| @@ -0,0 +1,96 @@ | ||
1 | +mirror=""" | |
2 | +<b>π° Mirror Bot</b> | |
3 | +<b>π° Version:</b> <code>1.0.0</code> | |
4 | +/cmd link | |
5 | + | |
6 | +<b>By replying to link/file</b>: <code>mirror</code> | |
7 | + | |
8 | +/cmd -n new name -e -up upload destination | |
9 | +<b>Example:</b> <code>/cmd -n new name -e -up upload destination</code> | |
10 | + | |
11 | +<b>NOTE:</b> | |
12 | +1. Commands that start with <b>qb</b> are ONLY for torrents. | |
13 | + | |
14 | +""" | |
15 | + | |
16 | +yt="""<b>π° YouTube Downloader</b> | |
17 | +"<b>Send link along with command line</b>: | |
18 | + | |
19 | +/cmd link | |
20 | +<b>By replying to link</b>: | |
21 | +/cmd -n new name -z password -opt x:y|x1:y1 | |
22 | + | |
23 | +Check here all supported <a href='https://github.com/swadhinbiswas/Streamz/blob/main/supportedsite.md'>SITES</a> | |
24 | +Check all yt-dlp api options from this <a href='https://github.com/yt-dlp/yt-dlp/blob/master/yt_dlp/YoutubeDL.py#L184'>FILE</a> to convert cli arguments to api options. | |
25 | + | |
26 | +""" | |
27 | +thumb = """<b>Thumbnail for current task</b>: -t | |
28 | + | |
29 | +/cmd link -t tg-message-link(doc or photo)""" | |
30 | + | |
31 | +split_size = """<b>Split size for current task</b>: -sp | |
32 | + | |
33 | +/cmd link -sp (500mb or 2gb or 4000000000) | |
34 | +Note: Only mb and gb are supported or write in bytes without unit!""" | |
35 | + | |
36 | +upload="""<b>Upload Your File </b>: -up | |
37 | + | |
38 | +/cmd link<code> yourlink </code>""" | |
39 | + | |
40 | + | |
41 | +YT_HELP_DICT = { | |
42 | + "main": yt, | |
43 | + "New-Name": f"{new_name}\nNote: Don't add file extension", | |
44 | + "Zip": zip_arg, | |
45 | + "Quality": qual, | |
46 | + "Options": yt_opt, | |
47 | + "Multi-Link": multi_link, | |
48 | + "Same-Directory": same_dir, | |
49 | + "Thumb": thumb, | |
50 | + "Split-Size": split_size, | |
51 | + "Upload-Destination": upload, | |
52 | + "Rclone-Flags": rcf, | |
53 | + "Bulk": bulk, | |
54 | + "Sample-Video": sample_video, | |
55 | + "Screenshot": screenshot, | |
56 | + "Convert-Media": convert_media, | |
57 | + "Force-Start": force_start, | |
58 | + "Name-Substitute": name_sub, | |
59 | + "Mixed-Leech": mixed_leech, | |
60 | +} | |
61 | + | |
62 | +MIRROR_HELP_DICT = { | |
63 | + "main": mirror, | |
64 | + "New-Name": new_name, | |
65 | + "DL-Auth": "<b>Direct link authorization</b>: -au -ap\n\n/cmd link -au username -ap password", | |
66 | + "Headers": "<b>Direct link custom headers</b>: -h\n\n/cmd link -h key: value key1: value1", | |
67 | + "Extract/Zip": extract_zip, | |
68 | + "Torrent-Files": "<b>Bittorrent/JDownloader File Selection</b>: -s\n\n/cmd link -s or by replying to file/link", | |
69 | + "Torrent-Seed": seed, | |
70 | + "Multi-Link": multi_link, | |
71 | + "Same-Directory": same_dir, | |
72 | + "Thumb": thumb, | |
73 | + "Split-Size": split_size, | |
74 | + "Upload-Destination": upload, | |
75 | + "Rclone-Flags": rcf, | |
76 | + "Bulk": bulk, | |
77 | + "Join": join, | |
78 | + "Rclone-DL": rlone_dl, | |
79 | + "Tg-Links": tg_links, | |
80 | + "Sample-Video": sample_video, | |
81 | + "Screenshot": screenshot, | |
82 | + "Convert-Media": convert_media, | |
83 | + "Force-Start": force_start, | |
84 | + "User-Download": user_download, | |
85 | + "Name-Substitute": name_sub, | |
86 | + "Mixed-Leech": mixed_leech, | |
87 | +} | |
88 | + | |
89 | + | |
90 | + | |
91 | +PASSWORD_ERROR_MESSAGE = """ | |
92 | +<b>This link requires a password!</b> | |
93 | +- Insert <b>::</b> after the link and write the password after the sign. | |
94 | + | |
95 | +<b>Example:</b> link::my password | |
96 | +""" | |
0 | 97 | |
βΌapp/bot/helper/ext_utils/status.py
+227-0
| @@ -0,0 +1,227 @@ | ||
1 | +from html import escape | |
2 | +from psutil import virtual_memory, cpu_percent, disk_usage | |
3 | +from time import time | |
4 | +from asyncio import iscoroutinefunction | |
5 | + | |
6 | + | |
7 | +from bot import ( | |
8 | + DOWNLOAD_DIR, | |
9 | + task_dict, | |
10 | + task_dict_lock, | |
11 | + botStartTime, | |
12 | + config_dict, | |
13 | + status_dict, | |
14 | +) | |
15 | + | |
16 | +from bot.helper.ext_utils.bot_utils import sync_to_async | |
17 | +from bot.helper.telegram_helper.button_build import ButtonMaker | |
18 | + | |
19 | +SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"] | |
20 | + | |
21 | + | |
22 | + | |
23 | +class MirrorStatus: | |
24 | + STATUS_UPLOADING = "Upload" | |
25 | + STATUS_DOWNLOADING = "Download" | |
26 | + STATUS_CLONING = "Clone" | |
27 | + STATUS_QUEUEDL = "QueueDl" | |
28 | + STATUS_QUEUEUP = "QueueUp" | |
29 | + STATUS_PAUSED = "Pause" | |
30 | + STATUS_ARCHIVING = "Archive" | |
31 | + STATUS_EXTRACTING = "Extract" | |
32 | + STATUS_SPLITTING = "Split" | |
33 | + STATUS_CHECKING = "CheckUp" | |
34 | + STATUS_SEEDING = "Seed" | |
35 | + STATUS_SAMVID = "SamVid" | |
36 | + STATUS_CONVERTING = "Convert" | |
37 | + | |
38 | + | |
39 | + | |
40 | + | |
41 | +STATUSES = { | |
42 | + "ALL": "All", | |
43 | + "DL": MirrorStatus.STATUS_DOWNLOADING, | |
44 | + "UP": MirrorStatus.STATUS_UPLOADING, | |
45 | + "QD": MirrorStatus.STATUS_QUEUEDL, | |
46 | + "QU": MirrorStatus.STATUS_QUEUEUP, | |
47 | + "AR": MirrorStatus.STATUS_ARCHIVING, | |
48 | + "EX": MirrorStatus.STATUS_EXTRACTING, | |
49 | + "SD": MirrorStatus.STATUS_SEEDING, | |
50 | + "CM": MirrorStatus.STATUS_CONVERTING, | |
51 | + "CL": MirrorStatus.STATUS_CLONING, | |
52 | + "DF": "Delete File", | |
53 | +} | |
54 | + | |
55 | +async def getTaskByGid(gid: str): | |
56 | + async with task_dict_lock: | |
57 | + for tk in task_dict.values(): | |
58 | + if hasattr(tk, "seeding"): | |
59 | + await sync_to_async(tk.update) | |
60 | + if tk.gid() == gid: | |
61 | + return tk | |
62 | + return None | |
63 | + | |
64 | + | |
65 | +def getSpecificTasks(status, userId): | |
66 | + if status == "All": | |
67 | + if userId: | |
68 | + return [tk for tk in task_dict.values() if tk.listener.userId == userId] | |
69 | + else: | |
70 | + return list(task_dict.values()) | |
71 | + elif userId: | |
72 | + return [ | |
73 | + tk | |
74 | + for tk in task_dict.values() | |
75 | + if tk.listener.userId == userId | |
76 | + and ( | |
77 | + (st := tk.status()) | |
78 | + and st == status | |
79 | + or status == MirrorStatus.STATUS_DOWNLOADING | |
80 | + and st not in STATUSES.values() | |
81 | + ) | |
82 | + ] | |
83 | + else: | |
84 | + return [ | |
85 | + tk | |
86 | + for tk in task_dict.values() | |
87 | + if (st := tk.status()) | |
88 | + and st == status | |
89 | + or status == MirrorStatus.STATUS_DOWNLOADING | |
90 | + and st not in STATUSES.values() | |
91 | + ] | |
92 | + | |
93 | + | |
94 | +async def getAllTasks(req_status: str, userId): | |
95 | + async with task_dict_lock: | |
96 | + return await sync_to_async(getSpecificTasks, req_status, userId) | |
97 | + | |
98 | + | |
99 | +def get_readable_file_size(size_in_bytes: int): | |
100 | + if size_in_bytes is None: | |
101 | + return "0B" | |
102 | + index = 0 | |
103 | + while size_in_bytes >= 1024 and index < len(SIZE_UNITS) - 1: | |
104 | + size_in_bytes /= 1024 | |
105 | + index += 1 | |
106 | + return ( | |
107 | + f"{size_in_bytes:.2f}{SIZE_UNITS[index]}" | |
108 | + if index > 0 | |
109 | + else f"{size_in_bytes:.2f}B" | |
110 | + ) | |
111 | + | |
112 | + | |
113 | +def get_readable_time(seconds: int): | |
114 | + periods = [("d", 86400), ("h", 3600), ("m", 60), ("s", 1)] | |
115 | + result = "" | |
116 | + for period_name, period_seconds in periods: | |
117 | + if seconds >= period_seconds: | |
118 | + period_value, seconds = divmod(seconds, period_seconds) | |
119 | + result += f"{int(period_value)}{period_name}" | |
120 | + return result | |
121 | + | |
122 | + | |
123 | +def speed_string_to_bytes(size_text: str): | |
124 | + size = 0 | |
125 | + size_text = size_text.lower() | |
126 | + if "k" in size_text: | |
127 | + size += float(size_text.split("k")[0]) * 1024 | |
128 | + elif "m" in size_text: | |
129 | + size += float(size_text.split("m")[0]) * 1048576 | |
130 | + elif "g" in size_text: | |
131 | + size += float(size_text.split("g")[0]) * 1073741824 | |
132 | + elif "t" in size_text: | |
133 | + size += float(size_text.split("t")[0]) * 1099511627776 | |
134 | + elif "b" in size_text: | |
135 | + size += float(size_text.split("b")[0]) | |
136 | + return size | |
137 | + | |
138 | + | |
139 | +def get_progress_bar_string(pct): | |
140 | + pct = float(pct.strip("%")) | |
141 | + p = min(max(pct, 0), 100) | |
142 | + cFull = int(p // 8) | |
143 | + p_str = "β " * cFull | |
144 | + p_str += "β‘" * (12 - cFull) | |
145 | + return f"[{p_str}]" | |
146 | + | |
147 | + | |
148 | +async def get_readable_message(sid, is_user, page_no=1, status="All", page_step=1): | |
149 | + msg = "" | |
150 | + button = None | |
151 | + | |
152 | + tasks = await sync_to_async(getSpecificTasks, status, sid if is_user else None) | |
153 | + | |
154 | + STATUS_LIMIT = config_dict["STATUS_LIMIT"] | |
155 | + tasks_no = len(tasks) | |
156 | + pages = (max(tasks_no, 1) + STATUS_LIMIT - 1) // STATUS_LIMIT | |
157 | + if page_no > pages: | |
158 | + page_no = (page_no - 1) % pages + 1 | |
159 | + status_dict[sid]["page_no"] = page_no | |
160 | + elif page_no < 1: | |
161 | + page_no = pages - (abs(page_no) % pages) | |
162 | + status_dict[sid]["page_no"] = page_no | |
163 | + start_position = (page_no - 1) * STATUS_LIMIT | |
164 | + | |
165 | + for index, task in enumerate( | |
166 | + tasks[start_position : STATUS_LIMIT + start_position], start=1 | |
167 | + ): | |
168 | + tstatus = await sync_to_async(task.status) if status == "All" else status | |
169 | + if task.listener.isSuperChat: | |
170 | + msg += f"<b>{index + start_position}.<a href='{task.listener.message.link}'>{tstatus}</a>: </b>" | |
171 | + else: | |
172 | + msg += f"<b>{index + start_position}.{tstatus}: </b>" | |
173 | + msg += f"<code>{escape(f'{task.name()}')}</code>" | |
174 | + if tstatus not in [ | |
175 | + MirrorStatus.STATUS_SPLITTING, | |
176 | + MirrorStatus.STATUS_SEEDING, | |
177 | + MirrorStatus.STATUS_SAMVID, | |
178 | + MirrorStatus.STATUS_CONVERTING, | |
179 | + MirrorStatus.STATUS_QUEUEUP, | |
180 | + ]: | |
181 | + progress = ( | |
182 | + await task.progress() | |
183 | + if iscoroutinefunction(task.progress) | |
184 | + else task.progress() | |
185 | + ) | |
186 | + msg += f"\n{get_progress_bar_string(progress)} {progress}" | |
187 | + msg += f"\n<b>Processed:</b> {task.processed_bytes()} of {task.size()}" | |
188 | + msg += f"\n<b>Speed:</b> {task.speed()} | <b>ETA:</b> {task.eta()}" | |
189 | + if hasattr(task, "seeders_num"): | |
190 | + try: | |
191 | + msg += f"\n<b>Seeders:</b> {task.seeders_num()} | <b>Leechers:</b> {task.leechers_num()}" | |
192 | + except: | |
193 | + pass | |
194 | + elif tstatus == MirrorStatus.STATUS_SEEDING: | |
195 | + msg += f"\n<b>Size: </b>{task.size()}" | |
196 | + msg += f"\n<b>Speed: </b>{task.seed_speed()}" | |
197 | + msg += f" | <b>Uploaded: </b>{task.uploaded_bytes()}" | |
198 | + msg += f"\n<b>Ratio: </b>{task.ratio()}" | |
199 | + msg += f" | <b>Time: </b>{task.seeding_time()}" | |
200 | + else: | |
201 | + msg += f"\n<b>Size: </b>{task.size()}" | |
202 | + msg += f"\n<b>Gid: </b><code>{task.gid()}</code>\n\n" | |
203 | + | |
204 | + if len(msg) == 0: | |
205 | + if status == "All": | |
206 | + return None, None | |
207 | + else: | |
208 | + msg = f"No Active {status} Tasks!\n\n" | |
209 | + buttons = ButtonMaker() | |
210 | + if not is_user: | |
211 | + buttons.ibutton("π", f"status {sid} ov", position="header") | |
212 | + if len(tasks) > STATUS_LIMIT: | |
213 | + msg += f"<b>Page:</b> {page_no}/{pages} | <b>Tasks:</b> {tasks_no} | <b>Step:</b> {page_step}\n" | |
214 | + buttons.ibutton("<<", f"status {sid} pre", position="header") | |
215 | + buttons.ibutton(">>", f"status {sid} nex", position="header") | |
216 | + if tasks_no > 30: | |
217 | + for i in [1, 2, 4, 6, 8, 10, 15]: | |
218 | + buttons.ibutton(i, f"status {sid} ps {i}", position="footer") | |
219 | + if status != "All" or tasks_no > 20: | |
220 | + for label, status_value in list(STATUSES.items())[:9]: | |
221 | + if status_value != status: | |
222 | + buttons.ibutton(label, f"status {sid} st {status_value}") | |
223 | + buttons.ibutton("β»οΈ", f"status {sid} ref", position="header") | |
224 | + button = buttons.build_menu(8) | |
225 | + msg += f"<b>CPU:</b> {cpu_percent()}% | <b>FREE:</b> {get_readable_file_size(disk_usage(DOWNLOAD_DIR).free)}" | |
226 | + msg += f"\n<b>RAM:</b> {virtual_memory().percent}% | <b>UPTIME:</b> {get_readable_time(time() - botStartTime)}" | |
227 | + return msg, button | |
0 | 228 | |
βΌapp/bot/helper/ext_utils/telegraph_helper.py
+86-0
| @@ -0,0 +1,86 @@ | ||
1 | +from asyncio import sleep | |
2 | +from secrets import token_urlsafe | |
3 | +from telegraph.aio import Telegraph | |
4 | +from telegraph.exceptions import RetryAfterError | |
5 | +from app.settings.config import Website_URL, Bot_URL | |
6 | + | |
7 | +from bot import LOGGER | |
8 | + | |
9 | + | |
10 | +class TelegraphHelper: | |
11 | + def __init__(self, author_name=None, author_url=None): | |
12 | + self._telegraph = Telegraph(domain="graph.org") | |
13 | + self._author_name = author_name | |
14 | + self._author_url = author_url | |
15 | + | |
16 | + async def create_account(self): | |
17 | + LOGGER.info("Creating Telegraph Account") | |
18 | + try: | |
19 | + await self._telegraph.create_account( | |
20 | + short_name=token_urlsafe(8), | |
21 | + author_name=self._author_name, | |
22 | + author_url=self._author_url, | |
23 | + ) | |
24 | + except Exception as e: | |
25 | + LOGGER.error(f"Failed to create Telegraph Account: {e}") | |
26 | + | |
27 | + async def create_page(self, title, content): | |
28 | + try: | |
29 | + return await self._telegraph.create_page( | |
30 | + title=title, | |
31 | + author_name=self._author_name, | |
32 | + author_url=self._author_url, | |
33 | + html_content=content, | |
34 | + ) | |
35 | + except RetryAfterError as st: | |
36 | + LOGGER.warning( | |
37 | + f"Telegraph Flood control exceeded. I will sleep for {st.retry_after} seconds." | |
38 | + ) | |
39 | + await sleep(st.retry_after) | |
40 | + return await self.create_page(title, content) | |
41 | + | |
42 | + async def edit_page(self, path, title, content): | |
43 | + try: | |
44 | + return await self._telegraph.edit_page( | |
45 | + path=path, | |
46 | + title=title, | |
47 | + author_name=self._author_name, | |
48 | + author_url=self._author_url, | |
49 | + html_content=content, | |
50 | + ) | |
51 | + except RetryAfterError as st: | |
52 | + LOGGER.warning( | |
53 | + f"Telegraph Flood control exceeded. I will sleep for {st.retry_after} seconds." | |
54 | + ) | |
55 | + await sleep(st.retry_after) | |
56 | + return await self.edit_page(path, title, content) | |
57 | + | |
58 | + async def edit_telegraph(self, path, telegraph_content): | |
59 | + nxt_page = 1 | |
60 | + prev_page = 0 | |
61 | + num_of_path = len(path) | |
62 | + for content in telegraph_content: | |
63 | + if nxt_page == 1: | |
64 | + content += ( | |
65 | + f'<b><a href="https://telegra.ph/{path[nxt_page]}">Next</a></b>' | |
66 | + ) | |
67 | + nxt_page += 1 | |
68 | + else: | |
69 | + if prev_page <= num_of_path: | |
70 | + content += f'<b><a href="https://telegra.ph/{path[prev_page]}">Prev</a></b>' | |
71 | + prev_page += 1 | |
72 | + if nxt_page < num_of_path: | |
73 | + content += f'<b> | <a href="https://telegra.ph/{path[nxt_page]}">Next</a></b>' | |
74 | + nxt_page += 1 | |
75 | + await self.edit_page( | |
76 | + path=path[prev_page], | |
77 | + title="Mirror-leech-bot Torrent Search", | |
78 | + content=content, | |
79 | + ) | |
80 | + return | |
81 | + | |
82 | + | |
83 | +telegraph = TelegraphHelper( | |
84 | + "The Bot for Mirror Data Leeching", f"{Bot_URL}", | |
85 | + "Streamz", f"{Website_URL}" | |
86 | +) | |
0 | 87 | |
βΌapp/settings/config.py
+0-0
0 | 0 |