randydev commited on
Commit
b2e77d0
·
verified ·
1 Parent(s): a20b8c1

Upload 6 files

Browse files
Akeno/utils/convert.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import time
3
+
4
+ from PIL import Image
5
+ from pyrogram.types import Message
6
+
7
+ from Akeno.utils.tools import runcmd
8
+
9
+
10
+ async def convert_to_gif(file: str, is_video: bool = False) -> str:
11
+ resultFileName = f"gif_{round(time.time())}.mp4"
12
+ if is_video:
13
+ cmd = f"ffmpeg -i '{file}' -c copy '{resultFileName}'"
14
+ else:
15
+ cmd = f"lottie_convert.py '{file}' '{resultFileName}'"
16
+ await runcmd(cmd)
17
+ return resultFileName
18
+
19
+ async def tgs_to_png(file: str) -> str:
20
+ resultFileName = f"png_{round(time.time())}.png"
21
+ cmd = f"lottie_convert.py '{file}' '{resultFileName}'"
22
+ await runcmd(cmd)
23
+ return resultFileName
24
+
25
+ async def image_to_sticker(file: str, max_size: tuple = (512, 512)) -> tuple[bool, str]:
26
+ try:
27
+ with Image.open(file) as img:
28
+ original_width, original_height = img.size
29
+
30
+ new_width = min(original_width, max_size[0])
31
+ new_height = min(original_height, max_size[1])
32
+
33
+ if original_width > max_size[0] or original_height > max_size[1]:
34
+ img = img.resize((new_width, new_height), Image.LANCZOS)
35
+
36
+ file_name = f"sticker_{int(time.time())}.png"
37
+ img.save(file_name, "PNG")
38
+
39
+ return True, file_name
40
+
41
+ except Exception as e:
42
+ return False, str(e)
43
+
44
+ async def video_to_png(
45
+ file: str, duration: float, output: str = None
46
+ ) -> tuple[str, bool]:
47
+ resultFileName = output or f"{os.path.basename(file)}.png"
48
+ cut_at = duration // 2
49
+ cmd = f"ffmpeg -ss {cut_at} -i '{file}' -vframes 1 '{resultFileName}'"
50
+ _, err, _, _ = await runcmd(cmd)
51
+ if err:
52
+ return err, False
53
+ return resultFileName, True
54
+
55
+ async def video_to_sticker(file: Message) -> tuple[str, bool]:
56
+ try:
57
+ if file.animation:
58
+ width, height = file.animation.width, file.animation.height
59
+ elif file.video:
60
+ width, height = file.video.width, file.video.height
61
+ else:
62
+ return "Unsupported media type.", False
63
+ file_path = await file.download("downloads")
64
+ output_path = os.path.join("downloads", "videoSticker.webm")
65
+ if height > width:
66
+ scale_params = f"scale=-1:512"
67
+ else:
68
+ scale_params = f"scale=512:-1"
69
+ cmd = (
70
+ f"ffmpeg -i {file_path} "
71
+ f"-vf fps=30,{scale_params} -t 3 -c:v libvpx-vp9 -b:v 256k -an -pix_fmt yuv420p -auto-alt-ref 0 -loop 0 "
72
+ f"-f webm {output_path}"
73
+ )
74
+ await runcmd(cmd)
75
+ os.remove(file_path)
76
+ return output_path, True
77
+ except Exception as e:
78
+ return f"Error during conversion: {e}", False
Akeno/utils/driver.py ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import datetime
2
+ import json
3
+ import os
4
+ import random
5
+ import re
6
+ import time
7
+ import urllib.parse
8
+ from urllib.parse import quote_plus
9
+
10
+ import httpx
11
+ import requests
12
+ from pytz import country_names, country_timezones, timezone
13
+ from selenium import webdriver
14
+ from selenium.webdriver.chrome.options import Options
15
+ from selenium.webdriver.chrome.service import Service
16
+ from selenium.webdriver.common.by import By
17
+ from selenium.webdriver.support.expected_conditions import presence_of_element_located
18
+ from selenium.webdriver.support.wait import WebDriverWait
19
+
20
+ from Akeno.utils.database import db
21
+ from Akeno.utils.formatter import format_text
22
+ from config import *
23
+
24
+
25
+ class ChromeDriver:
26
+ def __init__(self) -> None:
27
+ self.carbon_theme = [
28
+ "3024-night",
29
+ "a11y-dark",
30
+ "blackboard",
31
+ "base16-dark",
32
+ "base16-light",
33
+ "cobalt",
34
+ "duotone-dark",
35
+ "hopscotch",
36
+ "lucario",
37
+ "material",
38
+ "monokai",
39
+ "night-owl",
40
+ "nord",
41
+ "oceanic-next",
42
+ "one-light",
43
+ "one-dark",
44
+ "panda-syntax",
45
+ "paraiso-dark",
46
+ "seti",
47
+ "shades-of-purple",
48
+ "solarized+dark",
49
+ "solarized+light",
50
+ "synthwave-84",
51
+ "twilight",
52
+ "verminal",
53
+ "vscode",
54
+ "yeti",
55
+ "zenburn",
56
+ ]
57
+
58
+ def get(self):
59
+ if not CHROME_BIN:
60
+ return (
61
+ None,
62
+ "ChromeBinaryErr: No binary path found! Install Chromium or Google Chrome.",
63
+ )
64
+
65
+ try:
66
+ options = Options()
67
+ options.binary_location = Config.CHROME_BIN
68
+ options.add_argument("--disable-dev-shm-usage")
69
+ options.add_argument("--ignore-certificate-errors")
70
+ options.add_argument("--disable-gpu")
71
+ options.add_argument("--headless=new")
72
+ options.add_argument("--test-type")
73
+ options.add_argument("--no-sandbox")
74
+ options.add_argument("--window-size=1920x1080")
75
+ options.add_experimental_option(
76
+ "prefs", {"download.default_directory": "./"}
77
+ )
78
+ service = Service(CHROME_DRIVER)
79
+ driver = webdriver.Chrome(options, service)
80
+ return driver, None
81
+ except Exception as e:
82
+ return None, f"ChromeDriverErr: {e}"
83
+
84
+ def close(self, driver: webdriver.Chrome):
85
+ driver.close()
86
+ driver.quit()
87
+
88
+ @property
89
+ def get_random_carbon(self) -> str:
90
+ url = "https://carbon.now.sh/?l=auto"
91
+ url += f"&t={random.choice(self.carbon_theme)}"
92
+ url += f"&bg=rgba%28{random.randint(1, 255)}%2C{random.randint(1, 255)}%2C{random.randint(1, 255)}%2C1%29"
93
+ url += "&code="
94
+ return url
95
+
96
+ async def generate_carbon(
97
+ self, driver: webdriver.Chrome, code: str, is_random: bool = False
98
+ ) -> str:
99
+ filename = f"{round(time.time())}"
100
+ BASE_URL = (
101
+ self.get_random_carbon
102
+ if is_random
103
+ else "https://carbon.now.sh/?l=auto&code="
104
+ )
105
+
106
+ driver.get(BASE_URL + format_text(quote_plus(code)))
107
+ driver.command_executor._commands["send_command"] = (
108
+ "POST",
109
+ "/session/$sessionId/chromium/send_command",
110
+ )
111
+ params = {
112
+ "cmd": "Page.setDownloadBehavior",
113
+ "params": {"behavior": "allow", "downloadPath": DWL_DIR},
114
+ }
115
+ driver.execute("send_command", params)
116
+
117
+ driver.find_element(By.XPATH, "//button[@id='export-menu']").click()
118
+ driver.find_element(By.XPATH, "//input[@title='filename']").send_keys(filename)
119
+ driver.find_element(By.XPATH, "//button[@id='export-png']").click()
120
+
121
+ return f"{Config.DWL_DIR}/{filename}.png"
122
+
123
+
124
+ class ClimateDriver:
125
+ def __init__(self) -> None:
126
+ self.weather_api = "https://api.openweathermap.org/data/2.5/weather?lat={0}&lon={1}&appid={2}&units=metric"
127
+ self.location_api = (
128
+ "https://api.openweathermap.org/geo/1.0/direct?q={0}&limit=1&appid={1}"
129
+ )
130
+ self.pollution_api = "http://api.openweathermap.org/data/2.5/air_pollution?lat={0}&lon={1}&appid={2}"
131
+ self.AQI_DICT = {
132
+ 1: "Good",
133
+ 2: "Fair",
134
+ 3: "Moderate",
135
+ 4: "Poor",
136
+ 5: "Very Poor",
137
+ }
138
+
139
+ async def fetchLocation(self, city: str, apiKey: str):
140
+ response = httpx.get(self.location_api.format(city, apiKey))
141
+ if response.status_code == 200:
142
+ data = response.json()
143
+ if data:
144
+ return data[0]["lat"], data[0]["lon"]
145
+ return None, None
146
+
147
+ async def fetchWeather(self, city: str, apiKey: str):
148
+ lattitude, longitude = await self.fetchLocation(city, apiKey)
149
+ if not lattitude and not longitude:
150
+ return None
151
+
152
+ response = httpx.get(self.weather_api.format(lattitude, longitude, apiKey))
153
+ if response.status_code == 200:
154
+ return response.json()
155
+ return None
156
+
157
+ async def fetchAirPollution(self, city: str, apiKey: str):
158
+ lattitude, longitude = await self.fetchLocation(city, apiKey)
159
+ if not lattitude and not longitude:
160
+ return None
161
+
162
+ response = httpx.get(self.pollution_api.format(lattitude, longitude, apiKey))
163
+ if response.status_code == 200:
164
+ return response.json()
165
+ return None
166
+
167
+ async def getTime(self, timestamp: int) -> str:
168
+ tz = await db.get_env(ENV_TEMPLATE.time_zone) or "Asia/Jakarta"
169
+ tz = timezone(tz)
170
+ return datetime.datetime.fromtimestamp(timestamp, tz=tz).strftime("%I:%M %p")
171
+
172
+ def getCountry(self, country_code: str) -> str:
173
+ return country_names.get(country_code, "Unknown")
174
+
175
+ def getCountryTimezone(self, country_code: str) -> str:
176
+ timezones = country_timezones.get(country_code, [])
177
+ if timezones:
178
+ return ", ".join(timezones)
179
+ return "Unknown"
180
+
181
+ def getWindData(self, windSpeed: str, windDegree: str) -> str:
182
+ dirs = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"]
183
+ ix = round(windDegree / (360.00 / len(dirs)))
184
+ kmph = str(float(windSpeed) * 3.6) + " km/h"
185
+ return f"[{dirs[ix % len(dirs)]}] {kmph}"
186
+
187
+
188
+ class YoutubeDriver:
189
+ def __init__(self, search_terms: str, max_results: int = 5):
190
+ self.base_url = "https://youtube.com/results?search_query={0}"
191
+ self.search_terms = search_terms
192
+ self.max_results = max_results
193
+ self.videos = self._search()
194
+
195
+ def _search(self):
196
+ encoded_search = urllib.parse.quote_plus(self.search_terms)
197
+ response = requests.get(self.base_url.format(encoded_search)).text
198
+
199
+ while "ytInitialData" not in response:
200
+ response = requests.get(self.base_url.format(encoded_search)).text
201
+
202
+ results = self._parse_html(response)
203
+
204
+ if self.max_results is not None and len(results) > self.max_results:
205
+ return results[: self.max_results]
206
+
207
+ return results
208
+
209
+ def _parse_html(self, response: str):
210
+ results = []
211
+ start = response.index("ytInitialData") + len("ytInitialData") + 3
212
+ end = response.index("};", start) + 1
213
+ json_str = response[start:end]
214
+ data = json.loads(json_str)
215
+
216
+ videos = data["contents"]["twoColumnSearchResultsRenderer"]["primaryContents"][
217
+ "sectionListRenderer"
218
+ ]["contents"][0]["itemSectionRenderer"]["contents"]
219
+
220
+ for video in videos:
221
+ res = {}
222
+ if "videoRenderer" in video.keys():
223
+ video_data = video.get("videoRenderer", {})
224
+ _id = video_data.get("videoId", None)
225
+
226
+ res["id"] = _id
227
+ res["thumbnail"] = f"https://i.ytimg.com/vi/{_id}/hqdefault.jpg"
228
+ res["title"] = (
229
+ video_data.get("title", {}).get("runs", [[{}]])[0].get("text", None)
230
+ )
231
+ res["channel"] = (
232
+ video_data.get("longBylineText", {})
233
+ .get("runs", [[{}]])[0]
234
+ .get("text", None)
235
+ )
236
+ res["duration"] = video_data.get("lengthText", {}).get("simpleText", 0)
237
+ res["views"] = video_data.get("viewCountText", {}).get(
238
+ "simpleText", "Unknown"
239
+ )
240
+ res["publish_time"] = video_data.get("publishedTimeText", {}).get(
241
+ "simpleText", "Unknown"
242
+ )
243
+ res["url_suffix"] = (
244
+ video_data.get("navigationEndpoint", {})
245
+ .get("commandMetadata", {})
246
+ .get("webCommandMetadata", {})
247
+ .get("url", None)
248
+ )
249
+
250
+ results.append(res)
251
+ return results
252
+
253
+ def to_dict(self, clear_cache=True) -> list[dict]:
254
+ result = self.videos
255
+ if clear_cache:
256
+ self.videos = []
257
+ return result
258
+
259
+ @staticmethod
260
+ def check_url(url: str) -> tuple[bool, str]:
261
+ if "&" in url:
262
+ url = url[: url.index("&")]
263
+
264
+ if "?si=" in url:
265
+ url = url[: url.index("?si=")]
266
+
267
+ youtube_regex = (
268
+ r"(https?://)?(www\.)?"
269
+ r"(youtube|youtu|youtube-nocookie)\.(com|be)/"
270
+ r'(video|embed|shorts/|watch\?v=|v/|e/|u/\\w+/|\\w+/)?([^"&?\\s]{11})'
271
+ )
272
+ match = re.match(youtube_regex, url)
273
+ if match:
274
+ return True, match.group(6)
275
+ else:
276
+ return False, "Invalid YouTube URL!"
277
+
278
+ @staticmethod
279
+ def song_options() -> dict:
280
+ return {
281
+ "format": "bestaudio",
282
+ "addmetadata": True,
283
+ "key": "FFmpegMetadata",
284
+ "prefer_ffmpeg": True,
285
+ "geo_bypass": True,
286
+ "nocheckcertificate": True,
287
+ "postprocessors": [
288
+ {
289
+ "key": "FFmpegExtractAudio",
290
+ "preferredcodec": "mp3",
291
+ "preferredquality": "480",
292
+ }
293
+ ],
294
+ "outtmpl": "%(id)s",
295
+ "quiet": True,
296
+ "logtostderr": False,
297
+ }
298
+
299
+ @staticmethod
300
+ def video_options() -> dict:
301
+ return {
302
+ "format": "best",
303
+ "addmetadata": True,
304
+ "key": "FFmpegMetadata",
305
+ "prefer_ffmpeg": True,
306
+ "geo_bypass": True,
307
+ "nocheckcertificate": True,
308
+ "postprocessors": [
309
+ {
310
+ "key": "FFmpegVideoConvertor",
311
+ "preferedformat": "mp4",
312
+ }
313
+ ],
314
+ "outtmpl": "%(id)s.mp4",
315
+ "quiet": True,
316
+ "logtostderr": False,
317
+ }
318
+
319
+
320
+ class SCRAP_DATA:
321
+ """Class to get and handel scrapped data"""
322
+
323
+ def __init__(self, urls: list[str] | str) -> None:
324
+ self.urls = urls
325
+ self.path = "./scrapped/"
326
+ if not os.path.isdir(self.path):
327
+ os.makedirs("./scrapped/")
328
+
329
+ def get_images(self) -> list:
330
+ images = []
331
+ if isinstance(self.urls, str):
332
+ requested = requests.get(self.urls)
333
+ try:
334
+ name = self.path + f"img_{time.time()}.jpg"
335
+ with open(name, "wb") as f:
336
+ f.write(requested.content)
337
+ images.append(name)
338
+ except Exception as e:
339
+ requested.close()
340
+ else:
341
+ for i in self.urls:
342
+ if i:
343
+ requested = requests.get(i)
344
+ else:
345
+ continue
346
+ try:
347
+ name = self.path + f"img_{time.time()}.jpg"
348
+ with open(name, "wb") as f:
349
+ f.write(requested.content)
350
+ images.append(name)
351
+ except Exception as e:
352
+
353
+ requested.close()
354
+ continue
355
+ return images
356
+
357
+ def get_videos(self) -> list:
358
+ videos = []
359
+ if isinstance(self.urls, str):
360
+ if i:
361
+ requested = requests.get(i)
362
+ else:
363
+ return []
364
+ try:
365
+ name = self.path + f"vid_{time.time()}.mp4"
366
+ with open(name, "wb") as f:
367
+ f.write(requested.content)
368
+ videos.append(name)
369
+ except Exception as e:
370
+ requested.close()
371
+ else:
372
+ for i in self.urls:
373
+ if i:
374
+ requested = requests.get(i)
375
+ else:
376
+ continue
377
+ try:
378
+ name = self.path + f"vid_{time.time()}.mp4"
379
+ with open(name, "wb") as f:
380
+ f.write(requested.content)
381
+ videos.append(name)
382
+ except Exception as e:
383
+
384
+ requested.close()
385
+ continue
386
+ return videos
387
+
388
+
389
+ class INSTAGRAM(ChromeDriver):
390
+ """Class to scrap data from instagram"""
391
+
392
+ def __init__(self, url: str) -> None:
393
+ self.url = url
394
+ self.article = "article._aa6a"
395
+ self.ul_class = "_acay"
396
+ self.image_class = "x5yr21d"
397
+ self.video_class = "x1lliihq"
398
+ self.next_button = "button._afxw"
399
+ self.return_dict = {"image": [], "video": []}
400
+ super().__init__()
401
+
402
+ def get_all(self):
403
+ driver, error = self.get()
404
+ if not driver:
405
+ return error
406
+
407
+ driver.get(self.url)
408
+ wait = WebDriverWait(driver, 30)
409
+ image_links = []
410
+ video_links = []
411
+ try:
412
+ element = wait.until(presence_of_element_located(
413
+ (By.CLASS_NAME, self.ul_class)))
414
+
415
+ while True:
416
+ sub_element = element.find_elements(
417
+ By.CLASS_NAME, self.image_class)
418
+ for i in sub_element:
419
+ url = i.get_attribute("src")
420
+ image_links.append(url)
421
+
422
+ sub_element = element.find_elements(
423
+ By.CLASS_NAME, self.video_class)
424
+ for i in sub_element:
425
+ url = i.get_attribute("src")
426
+ video_links.append(url)
427
+
428
+ try:
429
+ driver.find_element(
430
+ By.CSS_SELECTOR, self.next_button).click()
431
+ except: # Failed to either find the element or click on next i.e. no more media left in post
432
+ break
433
+ except:
434
+ element = wait.until(presence_of_element_located((By.CSS_SELECTOR, self.article)))
435
+ try:
436
+ sub_element = element.find_element(By.TAG_NAME, "img")
437
+ url = sub_element.get_attribute("src")
438
+ image_links.append(url)
439
+ except:
440
+ sub_element = element.find_element(By.TAG_NAME, "video")
441
+ url = sub_element.get_attribute("src")
442
+ video_links.append(url)
443
+
444
+ self.close(driver)
445
+ # To remove duplicates here I am converting into set
446
+ if image_links:
447
+ image_links = list(set(image_links))
448
+ if video_links:
449
+ video_links = list(set(video_links))
450
+ for i in video_links:
451
+ image_links.remove(i)
452
+
453
+ self.return_dict.get("image").extend(image_links)
454
+ self.return_dict.get("video").extend(video_links)
455
+ return self.return_dict
456
+
457
+
458
+ Driver = ChromeDriver()
459
+ Climate = ClimateDriver()
Akeno/utils/images.py CHANGED
@@ -12,6 +12,7 @@ from unidecode import unidecode
12
 
13
  from Akeno.utils.formatter import format_text, limit_per_page
14
 
 
15
  def convert_to_png(image: str) -> str:
16
  output_img = f"png_{round(time.time())}.png"
17
  img = Image.open(image)
 
12
 
13
  from Akeno.utils.formatter import format_text, limit_per_page
14
 
15
+
16
  def convert_to_png(image: str) -> str:
17
  output_img = f"png_{round(time.time())}.png"
18
  img = Image.open(image)
Akeno/utils/media.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from typing import Union
3
+
4
+ import requests
5
+ from pyrogram import Client
6
+ from pyrogram.file_id import FileId
7
+ from pyrogram.raw.functions.messages import UploadMedia
8
+ from pyrogram.raw.types import DocumentAttributeFilename, InputDocument, InputMediaUploadedDocument
9
+ from pyrogram.types import Animation, Audio, Document, Message, Photo, Sticker, Video
10
+
11
+
12
+ async def get_metedata(media: Union[Animation, Audio, Document, Photo, Sticker, Video]):
13
+ output = "📄 MetaData:\n\n"
14
+ if isinstance(media, Animation):
15
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
16
+ output += f"<b>Width:</b> <code>{media.width}</code>\n"
17
+ output += f"<b>Height:</b> <code>{media.height}</code>\n"
18
+ output += (
19
+ f"<b>Duration:</b> <code>{media.duration}</code>\n"
20
+ )
21
+ output += (
22
+ f"<b>File Name:</b> <code>{media.file_name}</code>\n"
23
+ )
24
+ output += (
25
+ f"<b>Mime Type:</b> <code>{media.mime_type}</code>\n"
26
+ )
27
+ output += (
28
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
29
+ )
30
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
31
+ output += f"<b>File Type:</b> <code>Animation</code>\n"
32
+ elif isinstance(media, Audio):
33
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
34
+ output += (
35
+ f"<b>Duration:</b> <code>{media.duration}</code>\n"
36
+ )
37
+ output += (
38
+ f"<b>Performer:</b> <code>{media.performer}</code>\n"
39
+ )
40
+ output += f"<b>Title:</b> <code>{media.title}</code>\n"
41
+ output += (
42
+ f"<b>File Name:</b> <code>{media.file_name}</code>\n"
43
+ )
44
+ output += (
45
+ f"<b>Mime Type:</b> <code>{media.mime_type}</code>\n"
46
+ )
47
+ output += (
48
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
49
+ )
50
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
51
+ output += f"<b>File Type:</b> <code>Audio</code>\n"
52
+ elif isinstance(media, Document):
53
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
54
+ output += (
55
+ f"<b>File Name:</b> <code>{media.file_name}</code>\n"
56
+ )
57
+ output += (
58
+ f"<b>Mime Type:</b> <code>{media.mime_type}</code>\n"
59
+ )
60
+ output += (
61
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
62
+ )
63
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
64
+ output += f"<b>File Type:</b> <code>Document</code>\n"
65
+ elif isinstance(media, Photo):
66
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
67
+ output += f"<b>Width:</b> <code>{media.width}</code>\n"
68
+ output += f"<b>Height:</b> <code>{media.height}</code>\n"
69
+ output += f"<b>File Name:</b> <code>photo.jpg</code>\n"
70
+ output += f"<b>Mime Type:</b> <code>image/jpeg</code>\n"
71
+ output += (
72
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
73
+ )
74
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
75
+ output += f"<b>File Type:</b> <code>Photo</code>\n"
76
+ elif isinstance(media, Sticker):
77
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
78
+ output += f"<b>Width:</b> <code>{media.width}</code>\n"
79
+ output += f"<b>Height:</b> <code>{media.height}</code>\n"
80
+ output += (
81
+ f"<b>File Name:</b> <code>{media.file_name}</code>\n"
82
+ )
83
+ output += (
84
+ f"<b>Mime Type:</b> <code>{media.mime_type}</code>\n"
85
+ )
86
+ output += (
87
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
88
+ )
89
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
90
+ output += f"<b>Emoji:</b> <code>{media.emoji}</code>\n"
91
+ output += (
92
+ f"<b>Set Name:</b> <code>{media.set_name}</code>\n"
93
+ )
94
+ output += f"<b>File Type:</b> <code>Sticker</code>\n"
95
+ elif isinstance(media, Video):
96
+ output += f"<b>File ID:</b> <code>{media.file_id}</code>\n"
97
+ output += f"<b>Width:</b> <code>{media.width}</code>\n"
98
+ output += f"<b>Height:</b> <code>{media.height}</code>\n"
99
+ output += (
100
+ f"<b>Duration:</b> <code>{media.duration}</code>\n"
101
+ )
102
+ output += (
103
+ f"<b>File Name:</b> <code>{media.file_name}</code>\n"
104
+ )
105
+ output += (
106
+ f"<b>Mime Type:</b> <code>{media.mime_type}</code>\n"
107
+ )
108
+ output += (
109
+ f"<b>File Size:</b> <code>{media.file_size}</code>\n"
110
+ )
111
+ output += f"<b>Date:</b> <code>{media.date}</code>\n"
112
+ output += f"<b>File Type:</b> <code>Video</code>\n"
113
+ else:
114
+ return None
115
+
116
+ return output
117
+
118
+
119
+ def get_media_text_ocr(filename: str, api_key: str, language: str = "eng") -> dict:
120
+ payload = {
121
+ "isOverlayRequired": False,
122
+ "apikey": api_key,
123
+ "language": language,
124
+ }
125
+
126
+ with open(filename, "rb") as f:
127
+ r = requests.post(
128
+ "https://api.ocr.space/parse/image",
129
+ files={filename: f},
130
+ data=payload,
131
+ )
132
+
133
+ return r.json()
134
+
135
+
136
+ async def upload_media(client: Client, chat_id: int, file: str) -> InputDocument:
137
+ media = await client.invoke(
138
+ UploadMedia(
139
+ peer=(await client.resolve_peer(chat_id)),
140
+ media=InputMediaUploadedDocument(
141
+ file=(await client.save_file(file)),
142
+ mime_type=client.guess_mime_type(file) or "application/zip",
143
+ attributes=[
144
+ DocumentAttributeFilename(file_name=os.path.basename(file))
145
+ ],
146
+ force_file=True,
147
+ ),
148
+ ),
149
+ )
150
+
151
+ return InputDocument(
152
+ id=media.document.id,
153
+ access_hash=media.document.access_hash,
154
+ file_reference=media.document.file_reference,
155
+ )
156
+
157
+
158
+ async def get_media_from_id(file_id: str) -> InputDocument:
159
+ file = FileId.decode(file_id)
160
+
161
+ return InputDocument(
162
+ id=file.media_id,
163
+ access_hash=file.access_hash,
164
+ file_reference=file.file_reference,
165
+ )
166
+
167
+
168
+ async def get_media_fileid(message: Message) -> str | None:
169
+ file_id = None
170
+ if message.photo:
171
+ file_id = message.photo.file_id
172
+ elif message.animation:
173
+ file_id = message.animation.file_id
174
+ elif message.audio:
175
+ file_id = message.audio.file_id
176
+ elif message.document:
177
+ file_id = message.document.file_id
178
+ elif message.video:
179
+ file_id = message.video.file_id
180
+ elif message.sticker:
181
+ file_id = message.sticker.file_id
182
+ elif message.video_note:
183
+ file_id = message.video_note.file_id
184
+ elif message.voice:
185
+ file_id = message.voice.file_id
186
+ return file_id
Akeno/utils/sticker.py ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Tuple
2
+
3
+ from emoji import EMOJI_DATA
4
+ from pyrogram import Client
5
+ from pyrogram.raw import base, types
6
+ from pyrogram.raw.functions.messages import GetStickerSet
7
+ from pyrogram.raw.functions.stickers import AddStickerToSet, CreateStickerSet, RemoveStickerFromSet
8
+ from pyrogram.types import Message
9
+
10
+ from Akeno.utils.media import get_media_from_id, upload_media
11
+
12
+
13
+ def is_emoji(text: str) -> bool:
14
+ return any(c in EMOJI_DATA for c in text)
15
+
16
+
17
+ def get_emoji_and_id(message: Message) -> Tuple[int, str]:
18
+ pack_id = None
19
+ pack_emoji = None
20
+
21
+ for command in message.command:
22
+ if command.isdigit():
23
+ pack_id = int(command)
24
+ elif is_emoji(command):
25
+ pack_emoji = command
26
+
27
+ if pack_id is None:
28
+ pack_id = 1
29
+
30
+ if pack_emoji is None:
31
+ sticker = message.reply_to_message.sticker
32
+ try:
33
+ pack_emoji = sticker.emoji if sticker and sticker.emoji else "🍀"
34
+ except:
35
+ pack_emoji = "🍀"
36
+
37
+ return pack_id, pack_emoji
38
+
39
+
40
+ def check_sticker_data(replied: Message) -> Tuple[str | None, bool, bool, bool, int]:
41
+ pack_type = None
42
+ is_animated = False
43
+ is_video = False
44
+ is_static = False
45
+ pack_limit = 50
46
+
47
+ if replied.sticker:
48
+ if replied.sticker.is_animated:
49
+ pack_type, is_animated = "animated", True
50
+ elif replied.sticker.is_video:
51
+ pack_type, is_video = "video", True
52
+ else:
53
+ pack_type, is_static, pack_limit = "static", True, 120
54
+
55
+ elif replied.photo:
56
+ pack_type, is_static, pack_limit = "static", True, 120
57
+
58
+ elif replied.video or replied.animation:
59
+ pack_type, is_video = "video", True
60
+
61
+ elif replied.document:
62
+ mime_type = replied.document.mime_type.lower()
63
+ if mime_type.startswith("video/"):
64
+ pack_type, is_video = "video", True
65
+ elif mime_type.startswith("image/"):
66
+ pack_type, is_static, pack_limit = "static", True, 120
67
+ elif mime_type in ["application/x-tgsticker", "application/x-bad-tgsticker"]:
68
+ pack_type, is_animated = "animated", True
69
+
70
+ return pack_type, is_animated, is_video, is_static, pack_limit
71
+
72
+
73
+ async def create_sticker(
74
+ client: Client,
75
+ chat_id: int,
76
+ file: str,
77
+ emoji: str,
78
+ ) -> types.InputStickerSetItem:
79
+ sticker = await upload_media(client, chat_id, file)
80
+
81
+ return types.InputStickerSetItem(
82
+ document=sticker,
83
+ emoji=emoji,
84
+ )
85
+
86
+
87
+ async def remove_sticker(client: Client, stickerid: str) -> base.messages.StickerSet:
88
+ sticker = await get_media_from_id(stickerid)
89
+ return await client.invoke(RemoveStickerFromSet(sticker=sticker))
90
+
91
+
92
+ async def get_sticker_set(client: Client, name: str) -> base.messages.StickerSet | None:
93
+ try:
94
+ return await client.invoke(
95
+ GetStickerSet(
96
+ stickerset=types.InputStickerSetShortName(short_name=name),
97
+ hash=0,
98
+ )
99
+ )
100
+ except:
101
+ return None
102
+
103
+
104
+ async def add_sticker(
105
+ client: Client,
106
+ stickerset: base.messages.StickerSet,
107
+ sticker: base.InputStickerSetItem,
108
+ ) -> base.messages.StickerSet:
109
+ return await client.invoke(
110
+ AddStickerToSet(
111
+ stickerset=types.InputStickerSetShortName(short_name=stickerset.set.short_name),
112
+ sticker=sticker,
113
+ )
114
+ )
115
+
116
+
117
+ async def new_sticker_set(
118
+ client: Client,
119
+ user_id: int,
120
+ title: str,
121
+ short_name: str,
122
+ stickers: list[base.InputStickerSetItem],
123
+ animated: bool,
124
+ video: bool,
125
+ ) -> base.messages.StickerSet:
126
+ return await client.invoke(
127
+ CreateStickerSet(
128
+ user_id=(await client.resolve_peer(user_id)),
129
+ title=title,
130
+ short_name=short_name,
131
+ stickers=stickers,
132
+ animated=animated,
133
+ videos=video,
134
+ )
135
+ )
Akeno/utils/tools.py CHANGED
@@ -1,27 +1,32 @@
1
  import asyncio
2
- import shlex
3
- import asyncio
4
  import math
5
  import os
6
- import cv2
7
- import requests
8
- from typing import Tuple
9
  import textwrap
 
 
 
10
 
 
 
 
 
 
11
  from PIL import Image, ImageDraw, ImageFont
12
- from io import BytesIO
13
  from pymediainfo import MediaInfo
14
- from bs4 import BeautifulSoup as bs
15
-
16
  from pyrogram import *
17
  from pyrogram.enums import *
18
  from pyrogram.errors import *
19
  from pyrogram.raw.functions.messages import *
20
  from pyrogram.raw.types import *
21
  from pyrogram.types import *
 
22
 
23
- DEVS = [1191668125]
24
 
 
25
 
26
  def global_no_spam_title(message: Message):
27
  chat = message.chat
@@ -149,7 +154,7 @@ async def resize_media(media: str, video: bool, fast_forward: bool) -> str:
149
  image.save(resized_photo)
150
  os.remove(media)
151
  return resized_photo
152
-
153
  def get_text(message: Message) -> [None, str]:
154
  """Extract Text From Commands"""
155
  text_to_return = message.text
@@ -300,7 +305,7 @@ def GetUserMentionable(user: User):
300
  username = "<a href='tg://user?id={}'>{}</a>".format(user.id, name_string)
301
 
302
  return username
303
-
304
  def resize_image(image):
305
  im = Image.open(image)
306
  maxsize = (512, 512)
@@ -381,3 +386,63 @@ class Media_Info:
381
  else None
382
  )
383
  return dict_
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import asyncio
2
+ import contextlib
 
3
  import math
4
  import os
5
+ import shlex
6
+ import shutil
 
7
  import textwrap
8
+ import time
9
+ from io import BytesIO
10
+ from typing import Tuple
11
 
12
+ import cv2
13
+ import requests
14
+ from bs4 import BeautifulSoup as bs
15
+ from git import Repo
16
+ from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
17
  from PIL import Image, ImageDraw, ImageFont
 
18
  from pymediainfo import MediaInfo
 
 
19
  from pyrogram import *
20
  from pyrogram.enums import *
21
  from pyrogram.errors import *
22
  from pyrogram.raw.functions.messages import *
23
  from pyrogram.raw.types import *
24
  from pyrogram.types import *
25
+ from pyrogram.types import Message
26
 
27
+ from Akeno.utils.formatter import humanbytes, readable_time
28
 
29
+ DEVS = [1191668125]
30
 
31
  def global_no_spam_title(message: Message):
32
  chat = message.chat
 
154
  image.save(resized_photo)
155
  os.remove(media)
156
  return resized_photo
157
+
158
  def get_text(message: Message) -> [None, str]:
159
  """Extract Text From Commands"""
160
  text_to_return = message.text
 
305
  username = "<a href='tg://user?id={}'>{}</a>".format(user.id, name_string)
306
 
307
  return username
308
+
309
  def resize_image(image):
310
  im = Image.open(image)
311
  maxsize = (512, 512)
 
386
  else None
387
  )
388
  return dict_
389
+
390
+ async def get_files_from_directory(directory: str):
391
+ all_files = []
392
+ for path, _, files in os.walk(directory):
393
+ for file in files:
394
+ all_files.append(os.path.join(path, file))
395
+ return all_files
396
+
397
+ async def runcmd(cmd: str) -> Tuple[str, str, int, int]:
398
+ args = shlex.split(cmd)
399
+ process = await asyncio.create_subprocess_exec(
400
+ *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
401
+ )
402
+ stdout, stderr = await process.communicate()
403
+ return (
404
+ stdout.decode("utf-8", "replace").strip(),
405
+ stderr.decode("utf-8", "replace").strip(),
406
+ process.returncode,
407
+ process.pid,
408
+ )
409
+
410
+ async def update_dotenv(key: str, value: str) -> None:
411
+ with open(".env", "r") as file:
412
+ data = file.readlines()
413
+ for index, line in enumerate(data):
414
+ if line.startswith(f"{key}="):
415
+ data[index] = f"{key}={value}\n"
416
+ break
417
+ with open(".env", "w") as file:
418
+ file.writelines(data)
419
+
420
+ async def gen_changelogs(repo: Repo, branch: str) -> str:
421
+ changelogs = ""
422
+ commits = list(repo.iter_commits(branch))[:5]
423
+ for index, commit in enumerate(commits):
424
+ changelogs += f"**{index + 1}.** `{commit.summary}`\n"
425
+ return changelogs
426
+
427
+ async def initialize_git(git_repo: str):
428
+ force = False
429
+ try:
430
+ repo = Repo()
431
+ except NoSuchPathError as pathErr:
432
+ repo.__del__()
433
+ return False, pathErr, force
434
+ except GitCommandError as gitErr:
435
+ repo.__del__()
436
+ return False, gitErr, force
437
+ except InvalidGitRepositoryError:
438
+ repo = Repo.init()
439
+ origin = repo.create_remote("upstream", f"https://github.com/{git_repo}")
440
+ origin.fetch()
441
+ repo.create_head("master", origin.refs.master)
442
+ repo.heads.master.set_tracking_branch(origin.refs.master)
443
+ repo.heads.master.checkout(True)
444
+ force = True
445
+ with contextlib.suppress(BaseException):
446
+ repo.create_remote("upstream", f"https://github.com/{git_repo}")
447
+
448
+ return True, repo, force