Spaces:
Build error
Build error
Commit
Β·
0cee3b2
1
Parent(s):
5bdbbe8
Upload 3 files
Browse files- unzipper/client/caching.py +52 -0
- unzipper/client/patcher.py +84 -0
- unzipper/client/pyro_client.py +175 -0
unzipper/client/caching.py
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ===================================================================== #
|
2 |
+
# Copyright (c) 2022 Itz-fork #
|
3 |
+
# #
|
4 |
+
# This program is distributed in the hope that it will be useful, #
|
5 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
6 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #
|
7 |
+
# See the GNU General Public License for more details. #
|
8 |
+
# #
|
9 |
+
# You should have received a copy of the GNU General Public License #
|
10 |
+
# along with this program. If not, see <http://www.gnu.org/licenses/> #
|
11 |
+
# ===================================================================== #
|
12 |
+
|
13 |
+
import logging
|
14 |
+
from json import loads
|
15 |
+
from asyncio import get_event_loop
|
16 |
+
|
17 |
+
|
18 |
+
# Cache dicts
|
19 |
+
USER_LANG = {}
|
20 |
+
STRINGS = {}
|
21 |
+
|
22 |
+
|
23 |
+
def update_languages_cache():
|
24 |
+
from unzipper.database.language import get_user_languages
|
25 |
+
|
26 |
+
async def _iter_and_update():
|
27 |
+
async for doc in await get_user_languages():
|
28 |
+
USER_LANG[doc["_id"]] = doc["lang"]
|
29 |
+
|
30 |
+
loop = get_event_loop()
|
31 |
+
loop.run_until_complete(_iter_and_update())
|
32 |
+
|
33 |
+
|
34 |
+
def update_text_strings():
|
35 |
+
def _read_json(file, as_items=False):
|
36 |
+
with open(file) as f:
|
37 |
+
return loads(f.read()).items() if as_items else loads(f.read())
|
38 |
+
|
39 |
+
subfolders = _read_json("unzipper/localization/languages.json", True)
|
40 |
+
for lcode, fnm in subfolders:
|
41 |
+
str_list = _read_json(f"unzipper/localization/{lcode}/messages.json")
|
42 |
+
btn_strs = _read_json(f"unzipper/localization/defaults/buttons.json")
|
43 |
+
STRINGS[lcode] = str_list
|
44 |
+
STRINGS["buttons"] = btn_strs
|
45 |
+
|
46 |
+
|
47 |
+
def update_cache():
|
48 |
+
logging.info(" >> Updating text strings cache...")
|
49 |
+
update_text_strings()
|
50 |
+
|
51 |
+
logging.info(" >> Updating language cache...")
|
52 |
+
update_languages_cache()
|
unzipper/client/patcher.py
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ===================================================================== #
|
2 |
+
# Copyright (c) 2022 Itz-fork #
|
3 |
+
# #
|
4 |
+
# This program is distributed in the hope that it will be useful, #
|
5 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
6 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #
|
7 |
+
# See the GNU General Public License for more details. #
|
8 |
+
# #
|
9 |
+
# You should have received a copy of the GNU General Public License #
|
10 |
+
# along with this program. If not, see <http://www.gnu.org/licenses/> #
|
11 |
+
# ===================================================================== #
|
12 |
+
|
13 |
+
import logging
|
14 |
+
from os import path, remove
|
15 |
+
from .pyro_client import UnzipperBot
|
16 |
+
from unzipper.database.thumbnail import get_thumbnail
|
17 |
+
from unzipper.helpers_nexa.utils import run_shell_cmds
|
18 |
+
from unzipper.database.users import add_user, is_user_in_db, is_user_in_bdb
|
19 |
+
from config import Config
|
20 |
+
|
21 |
+
|
22 |
+
class PatchMethods:
|
23 |
+
def __init__(self) -> None:
|
24 |
+
super().__init__()
|
25 |
+
|
26 |
+
async def check_user(self: UnzipperBot, message):
|
27 |
+
"""
|
28 |
+
Checks database checks of new users
|
29 |
+
"""
|
30 |
+
# Checking if user is banned
|
31 |
+
is_banned = await is_user_in_bdb(int(message.from_user.id))
|
32 |
+
if is_banned:
|
33 |
+
await message.reply("**Sorry You're Banned!** \n\nReport this at @Nexa_bots if you think this is a mistake")
|
34 |
+
raise UserIsBanned
|
35 |
+
# Chacking if user already in db
|
36 |
+
is_in_db = await is_user_in_db(int(message.from_user.id))
|
37 |
+
if not is_in_db:
|
38 |
+
try:
|
39 |
+
await add_user(int(message.from_user.id))
|
40 |
+
await self.send_message(
|
41 |
+
chat_id=Config.LOGS_CHANNEL,
|
42 |
+
text=f"**#NEW_USER** π \n\n**User Profile:** `{message.from_user.mention}` \n**User ID:** `{message.from_user.id}` \n**Profile Url:** [Click here](tg://user?id={message.from_user.id})",
|
43 |
+
disable_web_page_preview=True
|
44 |
+
)
|
45 |
+
except Exception as e:
|
46 |
+
logging.warn(
|
47 |
+
f"Unable to add user to the database due to: \n{e}")
|
48 |
+
|
49 |
+
async def get_or_gen_thumb(self: UnzipperBot, uid: int, doc_f: str, isvid: bool = False):
|
50 |
+
"""
|
51 |
+
Get saved thumbnail from the database. If there isn't any thumbnail saved, None will be returned.
|
52 |
+
For video files, a thumbnail will be generated using ffmpeg
|
53 |
+
|
54 |
+
Parameters:
|
55 |
+
|
56 |
+
- `uid` - User id
|
57 |
+
- `doc_f` - File path
|
58 |
+
- `isvid` - Pass True if file is a video
|
59 |
+
"""
|
60 |
+
dbthumb = await get_thumbnail(int(uid), True)
|
61 |
+
if dbthumb:
|
62 |
+
return dbthumb
|
63 |
+
elif isvid:
|
64 |
+
thmb_pth = f"Dump/thumbnail_{path.basename(doc_f)}.jpg"
|
65 |
+
if path.exists(thmb_pth):
|
66 |
+
remove(thmb_pth)
|
67 |
+
await run_shell_cmds(f"ffmpeg -ss 00:00:01.00 -i {doc_f} -vf 'scale=320:320:force_original_aspect_ratio=decrease' -vframes 1 {thmb_pth}")
|
68 |
+
return thmb_pth
|
69 |
+
else:
|
70 |
+
return None
|
71 |
+
|
72 |
+
|
73 |
+
class UserIsBanned(Exception):
|
74 |
+
def __init__(self) -> None:
|
75 |
+
super().__init__("You're banned from using this bot!")
|
76 |
+
|
77 |
+
|
78 |
+
def init_patch():
|
79 |
+
"""
|
80 |
+
Apply custom methods defined in CustomMethods class to pyrogram.Client class
|
81 |
+
"""
|
82 |
+
for ckey, cval in PatchMethods.__dict__.items():
|
83 |
+
if ckey[:2] != "__":
|
84 |
+
setattr(UnzipperBot, ckey, cval)
|
unzipper/client/pyro_client.py
ADDED
@@ -0,0 +1,175 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# ===================================================================== #
|
2 |
+
# Copyright (c) 2022 Itz-fork #
|
3 |
+
# #
|
4 |
+
# This program is distributed in the hope that it will be useful, #
|
5 |
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
6 |
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. #
|
7 |
+
# See the GNU General Public License for more details. #
|
8 |
+
# #
|
9 |
+
# You should have received a copy of the GNU General Public License #
|
10 |
+
# along with this program. If not, see <http://www.gnu.org/licenses/> #
|
11 |
+
# ===================================================================== #
|
12 |
+
|
13 |
+
import logging
|
14 |
+
from time import time
|
15 |
+
from asyncio import sleep
|
16 |
+
from typing import Callable
|
17 |
+
from os import path, remove, stat
|
18 |
+
|
19 |
+
from config import Config
|
20 |
+
from pyrogram import Client
|
21 |
+
from .caching import STRINGS
|
22 |
+
from gofile2 import Async_Gofile
|
23 |
+
from pyrogram.errors import FloodWait
|
24 |
+
from pyrogram.types import CallbackQuery, Message
|
25 |
+
from unzipper.database.language import get_language
|
26 |
+
from unzipper.database.upload_mode import get_upload_mode
|
27 |
+
from unzipper.helpers_nexa.utils import (TimeFormatter, progress_for_pyrogram,
|
28 |
+
rm_mark_chars, run_shell_cmds)
|
29 |
+
|
30 |
+
|
31 |
+
class UnzipperBot(Client):
|
32 |
+
"""
|
33 |
+
Unzipper bot client
|
34 |
+
"""
|
35 |
+
version = "v1.0.2"
|
36 |
+
|
37 |
+
def __init__(self):
|
38 |
+
super().__init__("UnzipperBot",
|
39 |
+
api_id=Config.APP_ID,
|
40 |
+
api_hash=Config.API_HASH,
|
41 |
+
bot_token=Config.BOT_TOKEN,
|
42 |
+
plugins=dict(root="unzipper/modules"),
|
43 |
+
sleep_threshold=10)
|
44 |
+
|
45 |
+
######## Decorators ########
|
46 |
+
def handle_erros(self, func: Callable) -> Callable:
|
47 |
+
"""
|
48 |
+
Handle erros and database updates of users
|
49 |
+
"""
|
50 |
+
|
51 |
+
async def decorator(client: Client, message: Message):
|
52 |
+
lang = await get_language(message.chat.id)
|
53 |
+
try:
|
54 |
+
await self.check_user(message)
|
55 |
+
return await func(client, message, STRINGS[lang])
|
56 |
+
except Exception as e:
|
57 |
+
logging.warn(e)
|
58 |
+
await self.send_message(message.chat.id, STRINGS[lang]["failed_main"].format(e))
|
59 |
+
|
60 |
+
return decorator
|
61 |
+
|
62 |
+
def handle_query(self, func: Callable) -> Callable:
|
63 |
+
"""
|
64 |
+
Handle callback queries
|
65 |
+
"""
|
66 |
+
|
67 |
+
async def decorator(client: Client, query: CallbackQuery):
|
68 |
+
lang = await get_language(query.message.chat.id)
|
69 |
+
try:
|
70 |
+
await func(client, query, STRINGS[lang])
|
71 |
+
except Exception as e:
|
72 |
+
logging.warn(e)
|
73 |
+
await self.send_message(query.message.chat.id, STRINGS[lang]["failed_main"].format(e))
|
74 |
+
|
75 |
+
return decorator
|
76 |
+
|
77 |
+
######## File utils ########
|
78 |
+
async def send_file(self, c_id: int, doc_f: str, query: CallbackQuery, lang: str = "en", del_status: bool = False):
|
79 |
+
"""
|
80 |
+
Send a file to the user
|
81 |
+
|
82 |
+
Parameters:
|
83 |
+
|
84 |
+
- `c_id` - Chat id
|
85 |
+
- `doc_f` - File path
|
86 |
+
- `query` - CallBackQuery object
|
87 |
+
- `del_status` - Whether if you want to delete progress message or not
|
88 |
+
"""
|
89 |
+
|
90 |
+
try:
|
91 |
+
# This is my kingdom...
|
92 |
+
cum = await get_upload_mode(c_id)
|
93 |
+
# Checks if url file size is bigger than 2GB (Telegram limit)
|
94 |
+
file_size = stat(doc_f).st_size
|
95 |
+
if file_size > Config.TG_MAX_SIZE:
|
96 |
+
# Uploads the file to gofile.io
|
97 |
+
upmsg = await self.send_message(c_id, text=STRINGS[lang]["alert_file_too_large"])
|
98 |
+
try:
|
99 |
+
ga = Async_Gofile()
|
100 |
+
gfio = await ga.upload(doc_f)
|
101 |
+
from unzipper import Buttons
|
102 |
+
await upmsg.edit("**Your file has been uploaded to gofile! Click on the below button to download it π**", reply_markup=await Buttons.make_button("Gofile link π", url=gfio["downloadPage"]))
|
103 |
+
except:
|
104 |
+
await upmsg.edit("`Upload failed, Better luck next time π!`")
|
105 |
+
remove(doc_f)
|
106 |
+
return
|
107 |
+
|
108 |
+
tgupmsg = await self.send_message(c_id, STRINGS[lang]["processing"])
|
109 |
+
stm = time()
|
110 |
+
|
111 |
+
# Uplaod type: Video
|
112 |
+
if cum == "video":
|
113 |
+
sthumb = await self.get_or_gen_thumb(c_id, doc_f, True)
|
114 |
+
vid_duration = await run_shell_cmds(f"ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 {doc_f}")
|
115 |
+
await self.send_video(
|
116 |
+
chat_id=c_id,
|
117 |
+
video=doc_f,
|
118 |
+
caption="**Extracted by @NexaUnzipper_Bot**",
|
119 |
+
duration=int(
|
120 |
+
vid_duration) if vid_duration.isnumeric() else 0,
|
121 |
+
thumb=sthumb,
|
122 |
+
progress=progress_for_pyrogram,
|
123 |
+
progress_args=("**Trying to upload π** \n", tgupmsg, stm))
|
124 |
+
# Upload type: Document
|
125 |
+
else:
|
126 |
+
sthumb = await self.get_or_gen_thumb(c_id, doc_f)
|
127 |
+
await self.send_document(
|
128 |
+
chat_id=c_id,
|
129 |
+
document=doc_f,
|
130 |
+
caption="**Extracted by @NexaUnzipper_Bot**",
|
131 |
+
thumb=sthumb,
|
132 |
+
progress=progress_for_pyrogram,
|
133 |
+
progress_args=("**Trying to upload π** \n", tgupmsg, stm))
|
134 |
+
etm = time()
|
135 |
+
|
136 |
+
# Delete or edit the progress message
|
137 |
+
await tgupmsg.delete() if del_status else await tgupmsg.edit(STRINGS[lang]["ok_upload"].format(path.basename(doc_f), TimeFormatter(round(etm - stm))))
|
138 |
+
# Cleanup
|
139 |
+
try:
|
140 |
+
remove(doc_f)
|
141 |
+
if sthumb:
|
142 |
+
remove(sthumb)
|
143 |
+
except:
|
144 |
+
pass
|
145 |
+
except FloodWait as f:
|
146 |
+
await sleep(f.x)
|
147 |
+
return await self.send_file(c_id, doc_f, query)
|
148 |
+
except FileNotFoundError:
|
149 |
+
return await self.answer_query(query, "Sorry! I can't find that file", True)
|
150 |
+
except BaseException as e:
|
151 |
+
logging.warn(e)
|
152 |
+
await self.answer_query(query, f"**Error:** \n`{e}`")
|
153 |
+
|
154 |
+
######## Utils ########
|
155 |
+
async def answer_query(self, query: CallbackQuery, text: str, alert: bool = False, *args, **kwargs):
|
156 |
+
"""
|
157 |
+
Answer CallbackQuery with better error handling
|
158 |
+
|
159 |
+
Parameters:
|
160 |
+
|
161 |
+
- `query` - CallBackQuery object
|
162 |
+
- `text` - Message text
|
163 |
+
- `alert` - Pass True if you want to show the message as an alert
|
164 |
+
"""
|
165 |
+
try:
|
166 |
+
if alert:
|
167 |
+
await query.answer(await rm_mark_chars(text), show_alert=True, *args, **kwargs)
|
168 |
+
else:
|
169 |
+
await query.message.edit(text, *args, **kwargs)
|
170 |
+
except:
|
171 |
+
try:
|
172 |
+
await query.message.delete()
|
173 |
+
except:
|
174 |
+
pass
|
175 |
+
await self.send_message(query.message.chat.id, text, *args, **kwargs)
|