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