Upload 6 files
Browse files- Akeno/utils/convert.py +78 -0
- Akeno/utils/driver.py +459 -0
- Akeno/utils/images.py +1 -0
- Akeno/utils/media.py +186 -0
- Akeno/utils/sticker.py +135 -0
- Akeno/utils/tools.py +76 -11
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
|
3 |
-
import asyncio
|
4 |
import math
|
5 |
import os
|
6 |
-
import
|
7 |
-
import
|
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 |
-
|
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
|