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)
|