randydev commited on
Commit
249f4fc
·
verified ·
1 Parent(s): a330d96

Upload scripts.py

Browse files
Files changed (1) hide show
  1. Akeno/utils/scripts.py +324 -0
Akeno/utils/scripts.py ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Moon-Userbot - telegram userbot
2
+ # Copyright (C) 2020-present Moon Userbot Organization
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+ import asyncio
18
+ import importlib
19
+ import math
20
+ import os
21
+ import re
22
+ import shlex
23
+ import subprocess
24
+ import sys
25
+ import time
26
+ import traceback
27
+ from io import BytesIO
28
+ from types import ModuleType
29
+ from typing import Dict, Tuple
30
+
31
+ import psutil
32
+ from PIL import Image
33
+ from pyrogram import Client, enums, errors
34
+ from pyrogram.errors import FloodWait, MessageNotModified
35
+ from pyrogram.types import Message
36
+
37
+ META_COMMENTS = re.compile(r"^ *# *meta +(\S+) *: *(.*?)\s*$", re.MULTILINE)
38
+ interact_with_to_delete = []
39
+
40
+ def time_formatter(milliseconds: int) -> str:
41
+ """Time Formatter"""
42
+ seconds, milliseconds = divmod(int(milliseconds), 1000)
43
+ minutes, seconds = divmod(seconds, 60)
44
+ hours, minutes = divmod(minutes, 60)
45
+ days, hours = divmod(hours, 24)
46
+ tmp = (
47
+ ((str(days) + " day(s), ") if days else "")
48
+ + ((str(hours) + " hour(s), ") if hours else "")
49
+ + ((str(minutes) + " minute(s), ") if minutes else "")
50
+ + ((str(seconds) + " second(s), ") if seconds else "")
51
+ + ((str(milliseconds) + " millisecond(s), ") if milliseconds else "")
52
+ )
53
+ return tmp[:-2]
54
+
55
+
56
+ def humanbytes(size):
57
+ """Convert Bytes To Bytes So That Human Can Read It"""
58
+ if not size:
59
+ return ""
60
+ power = 2**10
61
+ raised_to_pow = 0
62
+ dict_power_n = {0: "", 1: "Ki", 2: "Mi", 3: "Gi", 4: "Ti"}
63
+ while size > power:
64
+ size /= power
65
+ raised_to_pow += 1
66
+ return str(round(size, 2)) + " " + dict_power_n[raised_to_pow] + "B"
67
+
68
+
69
+ async def edit_or_send_as_file(
70
+ tex: str,
71
+ message: Message,
72
+ client: Client,
73
+ caption: str = "<code>Result!</code>",
74
+ file_name: str = "result",
75
+ ):
76
+ """Send As File If Len Of Text Exceeds Tg Limit Else Edit Message"""
77
+ if not tex:
78
+ await message.edit("<code>Wait, What?</code>")
79
+ return
80
+ if len(tex) > 1024:
81
+ await message.edit("<code>OutPut is Too Large, Sending As File!</code>")
82
+ file_names = f"{file_name}.txt"
83
+ with open(file_names, "w") as fn:
84
+ fn.write(tex)
85
+ await client.send_document(message.chat.id, file_names, caption=caption)
86
+ await message.delete()
87
+ if os.path.exists(file_names):
88
+ os.remove(file_names)
89
+ return
90
+ return await message.edit(tex)
91
+
92
+
93
+ def get_text(message: Message) -> None | str:
94
+ """Extract Text From Commands"""
95
+ text_to_return = message.text
96
+ if message.text is None:
97
+ return None
98
+ if " " in text_to_return:
99
+ try:
100
+ return message.text.split(None, 1)[1]
101
+ except IndexError:
102
+ return None
103
+ else:
104
+ return None
105
+
106
+
107
+ async def progress(current, total, message, start, type_of_ps, file_name=None):
108
+ """Progress Bar For Showing Progress While Uploading / Downloading File - Normal"""
109
+ now = time.time()
110
+ diff = now - start
111
+ if round(diff % 10.00) == 0 or current == total:
112
+ percentage = current * 100 / total
113
+ speed = current / diff
114
+ elapsed_time = round(diff) * 1000
115
+ if elapsed_time == 0:
116
+ return
117
+ time_to_completion = round((total - current) / speed) * 1000
118
+ estimated_total_time = elapsed_time + time_to_completion
119
+ progress_str = f"{''.join(['▰' for i in range(math.floor(percentage / 10))])}"
120
+ progress_str += (
121
+ f"{''.join(['▱' for i in range(10 - math.floor(percentage / 10))])}"
122
+ )
123
+ progress_str += f"{round(percentage, 2)}%\n"
124
+ tmp = f"{progress_str}{humanbytes(current)} of {humanbytes(total)}\n"
125
+ tmp += f"ETA: {time_formatter(estimated_total_time)}"
126
+ if file_name:
127
+ try:
128
+ await message.edit(
129
+ f"{type_of_ps}\n**File Name:** `{file_name}`\n{tmp}"
130
+ )
131
+ except FloodWait as e:
132
+ await asyncio.sleep(e.x)
133
+ except MessageNotModified:
134
+ pass
135
+ else:
136
+ try:
137
+ await message.edit(
138
+ f"{type_of_ps}\n{tmp}", parse_mode=enums.ParseMode.MARKDOWN
139
+ )
140
+ except FloodWait as e:
141
+ await asyncio.sleep(e.x)
142
+ except MessageNotModified:
143
+ pass
144
+
145
+
146
+ async def run_cmd(prefix: str) -> Tuple[str, str, int, int]:
147
+ """Run Commands"""
148
+ args = shlex.split(prefix)
149
+ process = await asyncio.create_subprocess_exec(
150
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
151
+ )
152
+ stdout, stderr = await process.communicate()
153
+ return (
154
+ stdout.decode("utf-8", "replace").strip(),
155
+ stderr.decode("utf-8", "replace").strip(),
156
+ process.returncode,
157
+ process.pid,
158
+ )
159
+
160
+
161
+ def mediainfo(media):
162
+ xx = str((str(media)).split("(", maxsplit=1)[0])
163
+ m = ""
164
+ if xx == "MessageMediaDocument":
165
+ mim = media.document.mime_type
166
+ if mim == "application/x-tgsticker":
167
+ m = "sticker animated"
168
+ elif "image" in mim:
169
+ if mim == "image/webp":
170
+ m = "sticker"
171
+ elif mim == "image/gif":
172
+ m = "gif as doc"
173
+ else:
174
+ m = "pic as doc"
175
+ elif "video" in mim:
176
+ if "DocumentAttributeAnimated" in str(media):
177
+ m = "gif"
178
+ elif "DocumentAttributeVideo" in str(media):
179
+ i = str(media.document.attributes[0])
180
+ if "supports_streaming=True" in i:
181
+ m = "video"
182
+ m = "video as doc"
183
+ else:
184
+ m = "video"
185
+ elif "audio" in mim:
186
+ m = "audio"
187
+ else:
188
+ m = "document"
189
+ elif xx == "MessageMediaPhoto":
190
+ m = "pic"
191
+ elif xx == "MessageMediaWebPage":
192
+ m = "web"
193
+ return m
194
+
195
+
196
+ async def edit_or_reply(message, txt):
197
+ """Edit Message If Its From Self, Else Reply To Message"""
198
+ if not message:
199
+ return await message.edit(txt)
200
+ if not message.from_user:
201
+ return await message.edit(txt)
202
+ return await message.edit(txt)
203
+
204
+
205
+ def format_exc(e: Exception, suffix="") -> str:
206
+ traceback.print_exc()
207
+ err = traceback.format_exc()
208
+ if isinstance(e, errors.RPCError):
209
+ return (
210
+ f"<b>Telegram API error!</b>\n"
211
+ f"<code>[{e.CODE} {e.ID or e.NAME}] — {e.MESSAGE.format(value=e.value)}</code>\n\n<b>{suffix}</b>"
212
+ )
213
+ return f"<b>Error!</b>\n" f"<code>{err}</code>"
214
+
215
+
216
+ def import_library(library_name: str, package_name: str = None):
217
+ """
218
+ Loads a library, or installs it in ImportError case
219
+ :param library_name: library name (import example...)
220
+ :param package_name: package name in PyPi (pip install example)
221
+ :return: loaded module
222
+ """
223
+ if package_name is None:
224
+ package_name = library_name
225
+ requirements_list.append(package_name)
226
+
227
+ try:
228
+ return importlib.import_module(library_name)
229
+ except ImportError as exc:
230
+ completed = subprocess.run(
231
+ [sys.executable, "-m", "pip", "install", "--upgrade", package_name], check=True)
232
+ if completed.returncode != 0:
233
+ raise AssertionError(
234
+ f"Failed to install library {package_name} (pip exited with code {completed.returncode})"
235
+ ) from exc
236
+ return importlib.import_module(library_name)
237
+
238
+
239
+ def uninstall_library(package_name: str):
240
+ """
241
+ Uninstalls a library
242
+ :param package_name: package name in PyPi (pip uninstall example)
243
+ """
244
+ completed = subprocess.run(
245
+ [sys.executable, "-m", "pip", "uninstall", "-y", package_name], check=True)
246
+ if completed.returncode != 0:
247
+ raise AssertionError(
248
+ f"Failed to uninstall library {package_name} (pip exited with code {completed.returncode})"
249
+ )
250
+
251
+
252
+ def resize_image(
253
+ input_img, output=None, img_type="PNG", size: int = 512, size2: int = None
254
+ ):
255
+ if output is None:
256
+ output = BytesIO()
257
+ output.name = f"sticker.{img_type.lower()}"
258
+
259
+ with Image.open(input_img) as img:
260
+ # We used to use thumbnail(size) here, but it returns with a *max* dimension of 512,512
261
+ # rather than making one side exactly 512, so we have to calculate dimensions manually :(
262
+ if size2 is not None:
263
+ size = (size, size2)
264
+ elif img.width == img.height:
265
+ size = (size, size)
266
+ elif img.width < img.height:
267
+ size = (max(size * img.width // img.height, 1), size)
268
+ else:
269
+ size = (size, max(size * img.height // img.width, 1))
270
+
271
+ img.resize(size).save(output, img_type)
272
+
273
+ return output
274
+
275
+
276
+ def resize_new_image(image_path, output_path, desired_width=None, desired_height=None):
277
+ """
278
+ Resize an image to the desired dimensions while maintaining the aspect ratio.
279
+
280
+ Args:
281
+ image_path (str): Path to the input image file.
282
+ output_path (str): Path to save the resized image.
283
+ desired_width (int, optional): Desired width in pixels. If not provided, the aspect ratio will be maintained.
284
+ desired_height (int, optional): Desired height in pixels. If not provided, the aspect ratio will be maintained.
285
+ """
286
+ image = Image.open(image_path)
287
+
288
+ width, height = image.size
289
+
290
+ aspect_ratio = width / height
291
+
292
+ if desired_width and desired_height:
293
+ new_width, new_height = desired_width, desired_height
294
+ elif desired_height:
295
+ new_width, new_height = int(desired_height * aspect_ratio), desired_height
296
+ else:
297
+ new_width, new_height = 150, 150
298
+
299
+ resized_image = image.resize((new_width, new_height), Image.Resampling.LANCZOS)
300
+
301
+ resized_image.save(output_path)
302
+ if os.path.exists(image_path):
303
+ os.remove(image_path)
304
+
305
+
306
+ def parse_meta_comments(code: str) -> Dict[str, str]:
307
+ try:
308
+ groups = META_COMMENTS.search(code).groups()
309
+ except AttributeError:
310
+ return {}
311
+
312
+ return {groups[i]: groups[i + 1] for i in range(0, len(groups), 2)}
313
+
314
+
315
+ def ReplyCheck(message: Message):
316
+ reply_id = None
317
+
318
+ if message.reply_to_message:
319
+ reply_id = message.reply_to_message.id
320
+
321
+ elif not message.from_user.is_self:
322
+ reply_id = message.id
323
+
324
+ return reply_id