diff --git a/.gitignore b/.gitignore index 5c6490e0c..2a3874b40 100644 --- a/.gitignore +++ b/.gitignore @@ -140,3 +140,6 @@ gen unknown_errors.txt logs/ bin/ +resources/base_profile_pic.jpg +resources/mdfy_profile_pic.jpg +pictest.py diff --git a/README.md b/README.md index ae9732e8d..0da9a22e4 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ async def testing(message: Message): * [@uaudIth](https://t.me/uaudIth) * [@K_E_N_W_A_Y](https://t.me/K_E_N_W_A_Y) * [@nawwasl](https://t.me/nawwasl) -* [@THARUKA](https://t.me/TharukaN97) +* [@TharukaN97](https://t.me/TharukaN97) +* [@Supun97](https://t.me/Supun97) * [@gotstc](https://t.me/gotstc) ### Copyright & License 👮 diff --git a/app.json b/app.json index 1044cecdf..5e4a9e51f 100644 --- a/app.json +++ b/app.json @@ -43,11 +43,6 @@ "description": "Your Languge ( ex: if english => 'en' )", "required": false }, - "SCREENSHOT_API": { - "description": "get API key from 'https://screenshotlayer.com'", - "required": false - - }, "CURRENCY_API": { "description": "get API key from 'https://free.currencyconverterapi.com'", "required": false @@ -97,6 +92,12 @@ "buildpacks": [ { "url": "https://github.com/jonathanong/heroku-buildpack-ffmpeg-latest.git" + }, { + "url": "https://github.com/opendoor-labs/heroku-buildpack-p7zip" + }, { + "url": "https://github.com/heroku/heroku-buildpack-google-chrome" + }, { + "url": "https://github.com/heroku/heroku-buildpack-chromedriver" }, { "url": "https://github.com/heroku/heroku-buildpack-apt.git" }, { diff --git a/config.env.sample b/config.env.sample index d2bd1faa8..2f5abea2b 100644 --- a/config.env.sample +++ b/config.env.sample @@ -44,18 +44,18 @@ DOWN_PATH = "downloads/" PREFERRED_LANGUAGE = "" -# get API key from 'https://screenshotlayer.com' -SCREENSHOT_API = "" - # get API Key from 'https://free.currencyconverterapi.com/' CURRENCY_API = "" + # add default city for weather WEATHER_DEFCITY = "" + # Weather API get it from 'https://openweathermap.org/' OPEN_WEATHER_MAP = "" + # GDrive Folder ID G_DRIVE_PARENT_ID = "" diff --git a/resources/font.ttf b/resources/font.ttf new file mode 100644 index 000000000..68d0b2907 Binary files /dev/null and b/resources/font.ttf differ diff --git a/userge/config.py b/userge/config.py index e00072330..29d280f9f 100644 --- a/userge/config.py +++ b/userge/config.py @@ -39,9 +39,7 @@ class Config: - """ - Configs to setup Userge. - """ + """Configs to setup Userge""" API_ID = int(os.environ.get("API_ID", 12345)) @@ -75,6 +73,10 @@ class Config: G_DRIVE_IS_TD = bool(os.environ.get("G_DRIVE_IS_TD", False)) + GOOGLE_CHROME_DRIVER = os.environ.get("GOOGLE_CHROME_DRIVER", None) + + GOOGLE_CHROME_BIN = os.environ.get("GOOGLE_CHROME_BIN", None) + LOG_CHANNEL_ID = int(os.environ.get("LOG_CHANNEL_ID", 0)) UPSTREAM_REPO = os.environ.get("UPSTREAM_REPO", "https://github.com/UsergeTeam/Userge") @@ -91,6 +93,8 @@ class Config: WELCOME_DELETE_TIMEOUT = 120 + AUTOPIC_TIMEOUT = 60 + ALLOWED_CHATS = Filters.chat([]) CMD_TRIGGER = os.environ.get("CMD_TRIGGER", '.') @@ -112,26 +116,19 @@ class Config: if Config.HEROKU_API_KEY: _LOG.info("Checking Heroku App...") - for heroku_app in heroku3.from_key(Config.HEROKU_API_KEY).apps(): if heroku_app and Config.HEROKU_APP_NAME and \ heroku_app.name == Config.HEROKU_APP_NAME: - _LOG.info("Heroku App : %s Found...", heroku_app.name) - Config.HEROKU_APP = heroku_app Config.HEROKU_GIT_URL = heroku_app.git_url.replace( "https://", "https://api:" + Config.HEROKU_API_KEY + "@") - if not os.path.isdir(os.path.join(os.getcwd(), '.git')): tmp_heroku_git_path = os.path.join(os.getcwd(), 'tmp_heroku_git') - _LOG.info("Cloning Heroku GIT...") - Repo.clone_from(Config.HEROKU_GIT_URL, tmp_heroku_git_path) shutil.move(os.path.join(tmp_heroku_git_path, '.git'), os.getcwd()) shutil.rmtree(tmp_heroku_git_path) - break if not os.path.exists('bin'): @@ -145,7 +142,6 @@ class Config: "bin/cmrudl"} _LOG.info("Checking BINs...") - for binary, path in _BINS.items(): if not os.path.exists(path): _LOG.debug("Downloading %s...", binary) diff --git a/userge/core/client.py b/userge/core/client.py index 136b5dd02..1b85ebc34 100644 --- a/userge/core/client.py +++ b/userge/core/client.py @@ -41,6 +41,7 @@ class Userge(RawClient): def __init__(self) -> None: self._help_dict: Dict[str, Dict[str, str]] = {} self._imported: List[ModuleType] = [] + self._tasks: List[Callable[[Any], Any]] = [] self._channel = self.getCLogger(__name__) _LOG.info(_LOG_STR, "Setting Userge Configs") super().__init__(Config.HU_STRING_SESSION, @@ -60,8 +61,7 @@ def getCLogger(self, name: str) -> CLogger: def conversation(self, chat_id: Union[str, int], - *, - timeout: Union[int, float] = 10, + *, timeout: Union[int, float] = 10, limit: int = 10) -> Conv: """\nThis returns new conversation object. @@ -87,8 +87,7 @@ async def send_read_acknowledge(self, chat_id: Union[int, str], message: Union[List[RawMessage], Optional[RawMessage]] = None, - *, - max_id: Optional[int] = None, + *, max_id: Optional[int] = None, clear_mentions: bool = False) -> bool: """\nMarks messages as read and optionally clears mentions. @@ -135,7 +134,7 @@ async def send_read_acknowledge(self, return await self.read_history(chat_id=chat_id, max_id=max_id) return False - async def get_user_dict(self, user_id: int) -> Dict[str, str]: + async def get_user_dict(self, user_id: Union[int, str]) -> Dict[str, str]: """This will return user `Dict` which contains `id`(chat id), `fname`(first name), `lname`(last name), `flname`(full name), `uname`(username) and `mention`. @@ -307,14 +306,15 @@ def on_cmd(self, filter_my_trigger = Filters.create(lambda _, query: \ query.text.startswith(trigger) if trigger else True) sudo_filter = Filters.create(lambda _, query: \ - query.from_user and query.from_user.id in Config.SUDO_USERS and \ - (query.text.startswith(Config.SUDO_TRIGGER) if trigger else True)) + (query.from_user + and query.from_user.id in Config.SUDO_USERS + and (query.text.startswith(Config.SUDO_TRIGGER) if trigger else True))) sudo_cmd_filter = Filters.create(lambda _, __: \ cname.lstrip(trigger) in Config.ALLOWED_COMMANDS) if filter_me: - filters_ = filters_ & ( - ((Filters.outgoing | Filters.me) & filter_my_trigger) | \ - (Filters.incoming & sudo_filter & sudo_cmd_filter)) + filters_ = (filters_ + & (((Filters.outgoing | Filters.me) & filter_my_trigger) + | (Filters.incoming & sudo_filter & sudo_cmd_filter))) return self._build_decorator(log=f"On {pattern}", filters=filters_, group=group, **kwargs) @@ -341,6 +341,11 @@ def on_left_member(self, filters=Filters.left_chat_member & leaving_chats, group=group) + def add_task(self, func: Callable[[Any], Any]) -> Callable[[Any], Any]: + """add tasks""" + self._tasks.append(func) + return func + def get_help(self, key: str = '', all_cmds: bool = False) -> Tuple[Union[str, List[str]], Union[bool, str]]: @@ -349,8 +354,10 @@ def get_help(self, """ if not key and not all_cmds: return sorted(list(self._help_dict)), True # names of all modules - if not key.startswith(Config.CMD_TRIGGER) and key in self._help_dict and \ - (len(self._help_dict[key]) > 1 or list(self._help_dict[key])[0].lstrip(Config.CMD_TRIGGER) != key): + if (not key.startswith(Config.CMD_TRIGGER) + and key in self._help_dict + and (len(self._help_dict[key]) > 1 + or list(self._help_dict[key])[0].lstrip(Config.CMD_TRIGGER) != key)): return sorted(list(self._help_dict[key])), False # all commands for that module dict_ = {x: y for _, i in self._help_dict.items() for x, y in i.items()} @@ -443,9 +450,9 @@ def _build_decorator(self, log: str, filters: Filters, group: int, - **kwargs: Union[str, bool, Dict[ - str, Union[str, List[str], Dict[ - str, str]]]]) -> Callable[[_PYROFUNC], _PYROFUNC]: + **kwargs: Union[str, bool, + Dict[str, Union[str, List[str], Dict[str, str]]]] + ) -> Callable[[_PYROFUNC], _PYROFUNC]: def _decorator(func: _PYROFUNC) -> _PYROFUNC: async def _template(_: RawClient, __: RawMessage) -> None: await func(Message(_, __, **kwargs)) @@ -490,7 +497,7 @@ async def reload_plugins(self) -> int: _LOG.info(_LOG_STR, f"Reloaded {len(reloaded)} Plugins => {reloaded}") return len(reloaded) - async def restart(self) -> None: + async def restart(self, update_req: bool = False) -> None: """Restart the Userge""" _LOG.info(_LOG_STR, "Restarting Userge") await self.stop() @@ -500,13 +507,29 @@ async def restart(self) -> None: os.close(handler.fd) except Exception as c_e: _LOG.error(_LOG_STR, c_e) + if update_req: + os.system("pip3 install -r requirements.txt") os.execl(sys.executable, sys.executable, '-m', 'userge') sys.exit() def begin(self) -> None: """This will start the Userge""" - _LOG.info(_LOG_STR, "Starting Userge") nest_asyncio.apply() Conv.init(self) - self.run() + loop = asyncio.get_event_loop() + run = loop.run_until_complete + _LOG.info(_LOG_STR, "Starting Userge") + run(self.start()) + running_tasks: List[asyncio.Task] = [] + for task in self._tasks: + running_tasks.append(loop.create_task(task())) + _LOG.info(_LOG_STR, "Idling Userge") + run(Userge.idle()) _LOG.info(_LOG_STR, "Exiting Userge") + for task in running_tasks: + task.cancel() + run(self.stop()) + for task in asyncio.all_tasks(): + task.cancel() + run(loop.shutdown_asyncgens()) + loop.close() diff --git a/userge/plugins/fun/autopic.py b/userge/plugins/fun/autopic.py new file mode 100644 index 000000000..730109ff7 --- /dev/null +++ b/userge/plugins/fun/autopic.py @@ -0,0 +1,115 @@ +# Copyright (C) 2020 by UsergeTeam@Github, < https://github.com/UsergeTeam >. +# +# This file is part of < https://github.com/UsergeTeam/Userge > project, +# and is released under the "GNU v3.0 License Agreement". +# Please see < https://github.com/uaudith/Userge/blob/master/LICENSE > +# +# All rights reserved. + + +import os +import base64 +import asyncio +import datetime +import textwrap +from shutil import copyfile + +import aiofiles +from PIL import Image, ImageFont, ImageDraw + +from userge import userge, Message, Config, get_collection + +SAVED_SETTINGS = get_collection("CONFIGS") + +__tmp__ = SAVED_SETTINGS.find_one({'_id': 'UPDATE_PIC'}) + +UPDATE_PIC = False +BASE_PIC = "resources/base_profile_pic.jpg" +MDFY_PIC = "resources/mdfy_profile_pic.jpg" +if __tmp__: + UPDATE_PIC = __tmp__['on'] + if not os.path.exists(BASE_PIC): + with open(BASE_PIC, "wb") as media_file_: + media_file_.write(base64.b64decode(__tmp__['media'])) + +del __tmp__ + +LOG = userge.getLogger(__name__) + + +@userge.on_cmd("autopic", about={ + 'header': "set profile picture", + 'usage': "{tr}autopic\n{tr}autopic [image path]\nset timeout using {tr}sappto"}) +async def autopic(message: Message): + global UPDATE_PIC + await message.edit('`processing...`') + if UPDATE_PIC: + if isinstance(UPDATE_PIC, asyncio.Task): + UPDATE_PIC.cancel() + UPDATE_PIC = False + SAVED_SETTINGS.update_one({'_id': 'UPDATE_PIC'}, + {"$set": {'on': False}}, upsert=True) + await message.edit('auto profile picture updation has been **stopped**', + del_in=5, log=__name__) + return + image_path = message.input_str + store = False + if os.path.exists(BASE_PIC) and not image_path: + pass + elif not image_path: + profile_photo = await userge.get_profile_photos("me", limit=1) + if not profile_photo: + await message.err("sorry, couldn't find any picture!") + return + await userge.download_media(profile_photo[0], file_name=BASE_PIC) + store = True + else: + if not os.path.exists(image_path): + await message.err("input path not found!") + return + if os.path.exists(BASE_PIC): + os.remove(BASE_PIC) + copyfile(image_path, BASE_PIC) + store = True + data_dict = {'on': True} + if store: + async with aiofiles.open(BASE_PIC, "rb") as media_file: + media = base64.b64encode(await media_file.read()) + data_dict['media'] = media + SAVED_SETTINGS.update_one({'_id': 'UPDATE_PIC'}, + {"$set": data_dict}, upsert=True) + await message.edit( + 'auto profile picture updation has been **started**', del_in=3, log=__name__) + UPDATE_PIC = asyncio.create_task(apic_worker()) + + +@userge.add_task +async def apic_worker(): + user_dict = await userge.get_user_dict('me') + user = '@' + user_dict['uname'] if user_dict['uname'] else user_dict['flname'] + while UPDATE_PIC: + img = Image.open(BASE_PIC) + i_width, i_height = img.size + s_font = ImageFont.truetype("resources/font.ttf", int((35 / 640)*i_width)) + l_font = ImageFont.truetype("resources/font.ttf", int((50 / 640)*i_width)) + draw = ImageDraw.Draw(img) + current_h, pad = 10, 0 + for user in textwrap.wrap(user, width=20): + u_width, u_height = draw.textsize(user, font=l_font) + draw.text(xy=((i_width - u_width) / 2, int((current_h / 640)*i_width)), text=user, + font=l_font, fill=(255, 255, 255)) + current_h += u_height + pad + tim = datetime.datetime.now( + tz=datetime.timezone(datetime.timedelta(minutes=30, hours=5))) + date_time = (f"DATE: {tim.day}.{tim.month}.{tim.year}\n" + f"TIME: {tim.hour}:{tim.minute}:{tim.second}\n" + "UTC+5:30") + d_width, d_height = draw.textsize(date_time, font=s_font) + draw.multiline_text( + xy=((i_width - d_width) / 2, i_height - d_height - int((20 / 640)*i_width)), + text=date_time, fill=(255, 255, 255), font=s_font, align="center") + img.convert('RGB').save(MDFY_PIC) + await userge.set_profile_photo(MDFY_PIC) + os.remove(MDFY_PIC) + LOG.info("profile photo has been updated!") + await asyncio.sleep(Config.AUTOPIC_TIMEOUT) diff --git a/userge/plugins/fun/carbon.py b/userge/plugins/fun/carbon.py new file mode 100644 index 000000000..0cb89257e --- /dev/null +++ b/userge/plugins/fun/carbon.py @@ -0,0 +1,48 @@ +# Copyright (C) 2020 by UsergeTeam@Github, < https://github.com/UsergeTeam >. +# +# This file is part of < https://github.com/UsergeTeam/Userge > project, +# and is released under the "GNU v3.0 License Agreement". +# Please see < https://github.com/uaudith/Userge/blob/master/LICENSE > +# +# All rights reserved. + + +import random +import asyncio + +from pyrogram.errors.exceptions.bad_request_400 import YouBlockedUser + +from userge import userge, Message + + +@userge.on_cmd("carbon", about={ + 'header': "create a carbon", + 'usage': "{tr}carbon [text | reply to msg]"}) +async def carbon_(message: Message): + replied = message.reply_to_message + if replied: + text = replied.text + else: + text = message.input_str + if not text: + await message.err("input not found!") + return + await message.edit("`creating a carbon...`") + async with userge.conversation("CarbonNowShBot", timeout=30) as conv: + try: + await conv.send_message(text) + except YouBlockedUser: + await message.edit('first **unblock** @CarbonNowShBot') + return + response = await conv.get_response(mark_read=True) + await response.click(x=random.randint(0, 2), y=random.randint(0, 8)) + response = await conv.get_response(mark_read=True) + while not response.media: + response = await conv.get_response(mark_read=True) + caption = "\n".join(response.caption.split("\n")[0:2]) + file_id = response.document.file_id + await message.delete() + await userge.send_document(chat_id=message.chat.id, + document=file_id, + caption='`' + caption + '`', + reply_to_message_id=replied.message_id if replied else None) diff --git a/userge/plugins/fun/kang.py b/userge/plugins/fun/kang.py index 03d24bc62..f2e201442 100644 --- a/userge/plugins/fun/kang.py +++ b/userge/plugins/fun/kang.py @@ -15,6 +15,7 @@ from PIL import Image from pyrogram.api.functions.messages import GetStickerSet from pyrogram.api.types import InputStickerSetShortName +from pyrogram.errors.exceptions.bad_request_400 import YouBlockedUser from userge import userge, Message, Config, pool @@ -85,7 +86,11 @@ def get_response(): if (" A Telegram user has created " "the Sticker Set.") not in htmlstr: async with userge.conversation('Stickers') as conv: - await conv.send_message('/addsticker') + try: + await conv.send_message('/addsticker') + except YouBlockedUser: + await message.edit('first **unblock** @Stickers') + return await conv.get_response(mark_read=True) await conv.send_message(packname) msg = await conv.get_response(mark_read=True) @@ -134,7 +139,11 @@ def get_response(): else: await message.edit("`Brewing a new Pack...`") async with userge.conversation('Stickers') as conv: - await conv.send_message(cmd) + try: + await conv.send_message(cmd) + except YouBlockedUser: + await message.edit('first **unblock** @Stickers') + return await conv.get_response(mark_read=True) await conv.send_message(packnick) await conv.get_response(mark_read=True) diff --git a/userge/plugins/fun/memes.py b/userge/plugins/fun/memes.py index 91897fa88..2797ec6a3 100644 --- a/userge/plugins/fun/memes.py +++ b/userge/plugins/fun/memes.py @@ -257,11 +257,32 @@ "( ^_^)o自自o(^_^ )", "ಠ‿ಠ", "ヽ(´▽`)/", "ᵒᴥᵒ#", "( ͡° ͜ʖ ͡°)", "┬─┬ ノ( ゜-゜ノ)", "ヽ(´ー`)ノ", "☜(⌒▽⌒)☞", "ε=ε=ε=┌(;*´Д`)ノ", "(╬ ಠ益ಠ)", "┬─┬⃰͡ (ᵔᵕᵔ͜ )", "┻━┻ ︵ヽ(`Д´)ノ︵ ┻━┻", r"¯\_(ツ)_/¯", "ʕᵔᴥᵔʔ", "(`・ω・´)", "ʕ•ᴥ•ʔ", "ლ(`ー´ლ)", "ʕʘ̅͜ʘ̅ʔ", "( ゚Д゚)", r"¯\(°_o)/¯", "(。◕‿◕。)", - "(ノಠ ∩ಠ)ノ彡( \\o°o)\\", "“ヽ(´▽`)ノ”",) + "(ノಠ ∩ಠ)ノ彡( \\o°o)\\", "“ヽ(´▽`)ノ”", "( ͡° ͜ʖ ͡°)", "¯\_(ツ)_/¯", "( ͡°( ͡° ͜ʖ( ͡° ͜ʖ ͡°)ʖ ͡°) ͡°)", + "ʕ•ᴥ•ʔ", "(▀̿Ĺ̯▀̿ ̿)", "(ง ͠° ͟ل͜ ͡°)ง", "༼ つ ◕_◕ ༽つ", "ಠ_ಠ", "(☞ ͡° ͜ʖ ͡°)☞", + "¯\_༼ ି ~ ି ༽_/¯", "c༼ ͡° ͜ʖ ͡° ༽⊃") +HAPPY = ("( ͡° ͜ʖ ͡°)", "(ʘ‿ʘ)", "(✿´‿`)", "=͟͟͞͞٩(๑☉ᴗ☉)੭ु⁾⁾", "(*⌒▽⌒*)θ~♪", + "°˖✧◝(⁰▿⁰)◜✧˖°", "✌(-‿-)✌", "⌒°(❛ᴗ❛)°⌒", "(゚<|\(・ω・)/|>゚)", "ヾ(o✪‿✪o)シ") -@userge.on_cmd(r"(?:[kK]ek|:/)$", - about={'header': "Check yourself, hint: `:/`"}, name='kek',trigger='') +THINKING = ("(҂⌣̀_⌣́)", "(;¬_¬)", "(-。-;", "┌[ O ʖ̯ O ]┐", "〳 ͡° Ĺ̯ ͡° 〵") + +WAVING = ("(ノ^∇^)", "(;-_-)/", "@(o・ェ・)@ノ", "ヾ(^-^)ノ", "ヾ(◍’౪`◍)ノ゙♡", "(ό‿ὸ)ノ", "(ヾ(´・ω・`)") + +WTF = ("༎ຶ‿༎ຶ", "(‿ˠ‿)", "╰U╯☜(◉ɷ◉ )", "(;´༎ຶ益༎ຶ`)♡", "╭∩╮(︶ε︶*)chu", "( ^◡^)っ (‿|‿)") + +LOVE = ("乂❤‿❤乂", "(。♥‿♥。)", "( ͡~ ͜ʖ ͡°)", "໒( ♥ ◡ ♥ )७", "༼♥ل͜♥༽") + +CONFUSED = ("(・_・ヾ", "「(゚ペ)", "﴾͡๏̯͡๏﴿", "( ̄■ ̄;)!?", "▐ ˵ ͠° (oo) °͠ ˵ ▐", "(-_-)ゞ゛") + +DEAD = ("(✖╭╮✖)", "✖‿✖", "(+_+)", "(✖﹏✖)", "∑(✘Д✘๑)") + +SAD = ("(@´_`@)", "⊙︿⊙", "(▰˘︹˘▰)", "●︿●", "( ´_ノ` )", "彡(-_-;)彡") + +DOG = ("-ᄒᴥᄒ-", "◖⚆ᴥ⚆◗") + + +@userge.on_cmd(r"(?:Kek|:/)$", + about={'header': "Check yourself, hint: `:/`"}, name='Kek',trigger='') async def kek_(message: Message): """kek""" kek = ["/", "\\"] @@ -270,8 +291,8 @@ async def kek_(message: Message): await message.edit(":" + kek[i % 2]) -@userge.on_cmd(r"(?:[lL]ol|-_-)$", - about={'header': "Check yourself, hint: `-_-`"}, name='lol',trigger='') +@userge.on_cmd(r"(?:Lol|-_-)$", + about={'header': "Check yourself, hint: `-_-`"}, name='Lol',trigger='') async def lol_(message: Message): """lol""" lol = "-_ " @@ -282,8 +303,8 @@ async def lol_(message: Message): await message.edit(lol, parse_mode="html") -@userge.on_cmd(r"(?:[fF]un|;_;)$", - about={'header': "Check yourself, hint: `;_;`"}, name="fun", trigger='') +@userge.on_cmd(r"(?:Fun|;_;)$", + about={'header': "Check yourself, hint: `;_;`"}, name="Fun", trigger='') async def fun_(message: Message): """fun""" fun = ";_ " @@ -294,7 +315,7 @@ async def fun_(message: Message): await message.edit(fun, parse_mode="html") -@userge.on_cmd("[oO]of$", about={'header': "Ooooof"}, name='oof', trigger='') +@userge.on_cmd("Oof$", about={'header': "Ooooof"}, name='Oof', trigger='') async def Oof_(message: Message): """Oof""" Oof = "Oo " @@ -303,7 +324,7 @@ async def Oof_(message: Message): await message.edit(Oof) -@userge.on_cmd("[hH]mm$", about={'header': "Hmmmmm"}, name='hmm', trigger='') +@userge.on_cmd("Hmm$", about={'header': "Hmmmmm"}, name='Hmm', trigger='') async def Hmm_(message: Message): """Hmm""" Hmm = "Hm " @@ -312,43 +333,33 @@ async def Hmm_(message: Message): await message.edit(Hmm) -@userge.on_cmd("fp$", about={'header': "Facepalm :P"}) -async def facepalm_(message: Message): - """facepalm_""" +async def check_and_send(message: Message, *args, **kwargs): replied = message.reply_to_message if replied: await asyncio.gather( message.delete(), - replied.reply("🤦‍♂") + replied.reply(*args, **kwargs) ) else: - await message.edit("🤦‍♂") + await message.edit(*args, **kwargs) + + +@userge.on_cmd("fp$", about={'header': "Facepalm :P"}) +async def facepalm_(message: Message): + """facepalm_""" + await check_and_send(message, "🤦‍♂") @userge.on_cmd("cry$", about={'header': "y u du dis, i cri"}) async def cry_(message: Message): """cry""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(CRI), parse_mode="html") - ) - else: - await message.edit(choice(CRI), parse_mode="html") + await check_and_send(message, choice(CRI), parse_mode="html") @userge.on_cmd("insult$", about={'header': "Check yourself ;)"}) async def insult_(message: Message): """insult""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(INSULT_STRINGS), parse_mode="html") - ) - else: - await message.edit(choice(INSULT_STRINGS), parse_mode="html") + await check_and_send(message, choice(INSULT_STRINGS), parse_mode="html") @userge.on_cmd("hi", about={ @@ -382,82 +393,65 @@ async def hi_(message: Message): await message.edit(pay) -@userge.on_cmd("react$", about={'header': "Make your userbot react to everything"}) +@userge.on_cmd("react", about={ + 'header': "Make your userbot react to everything", + 'types': ['happy', 'thinking', 'waving', 'wtf', 'love', 'confused', 'dead', 'sad', 'dog'], + 'usage': "{tr}react [type]", + 'examples': ["{tr}react", "{tr}react dead"]}) async def react_(message: Message): """react""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(FACEREACTS), parse_mode="html") - ) + type_ = message.input_str + if "happy" in type_: + out = choice(HAPPY) + elif "thinking" in type_: + out = choice(THINKING) + elif "waving" in type_: + out = choice(WAVING) + elif "wtf" in type_: + out = choice(WTF) + elif "love" in type_: + out = choice(LOVE) + elif "confused" in type_: + out = choice(CONFUSED) + elif "dead" in type_: + out = choice(DEAD) + elif "sad" in type_: + out = choice(SAD) + elif "dog" in type_: + out = choice(DOG) else: - await message.edit(choice(FACEREACTS), parse_mode="html") + out = choice(FACEREACTS) + await check_and_send(message, out, parse_mode="html") @userge.on_cmd("shg$", about={'header': "Shrug at it !!"}) async def shrugger(message: Message): """shrugger""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(SHGS), parse_mode="html") - ) - else: - await message.edit(choice(SHGS), parse_mode="html") + await check_and_send(message, choice(SHGS), parse_mode="html") @userge.on_cmd("chase$", about={'header': "You better start running"}) async def chase_(message: Message): """chase""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(CHASE_STR), parse_mode="html") - ) - else: - await message.edit(choice(CHASE_STR), parse_mode="html") + await check_and_send(message, choice(CHASE_STR), parse_mode="html") @userge.on_cmd("run$", about={'header': "Let Me Run, run, RUNNN!"}) async def run_(message: Message): """run""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(RUNS_STR), parse_mode="html") - ) - else: - await message.edit(choice(RUNS_STR), parse_mode="html") + await check_and_send(message, choice(RUNS_STR), parse_mode="html") @userge.on_cmd("metoo$", about={'header': "Haha yes"}) async def metoo_(message: Message): """metoo""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply(choice(METOOSTR), parse_mode="html") - ) - else: - await message.edit(choice(METOOSTR), parse_mode="html") + await check_and_send(message, choice(METOOSTR), parse_mode="html") @userge.on_cmd("10iq$", about={'header': "You retard !!"}, name="10iq") async def iqless(message: Message): """iqless""" - replied = message.reply_to_message - if replied: - await asyncio.gather( - message.delete(), - replied.reply("♿") - ) - else: - await message.edit("♿") + await check_and_send(message, "♿") @userge.on_cmd("moon$", about={'header': "kensar moon animation"}) diff --git a/userge/plugins/utils/quote.py b/userge/plugins/fun/quote.py similarity index 61% rename from userge/plugins/utils/quote.py rename to userge/plugins/fun/quote.py index dd77dc2ff..d57d3c236 100644 --- a/userge/plugins/utils/quote.py +++ b/userge/plugins/fun/quote.py @@ -9,6 +9,8 @@ import asyncio +from pyrogram.errors.exceptions.bad_request_400 import YouBlockedUser + from userge import userge, Message @@ -21,12 +23,19 @@ async def quotecmd(message: Message): args = message.input_str replied = message.reply_to_message async with userge.conversation('QuotLyBot') as conv: - if replied: - await userge.forward_messages(chat_id=conv.chat_id, - from_chat_id=message.chat.id, - message_ids=replied.message_id) - else: - await conv.send_message(args) + try: + if replied: + await userge.forward_messages(chat_id=conv.chat_id, + from_chat_id=message.chat.id, + message_ids=replied.message_id) + else: + if not args: + await message.err('input not found!') + return + await conv.send_message(args) + except YouBlockedUser: + await message.edit('first **unblock** @QuotLyBot') + return quote = await conv.get_response(mark_read=True) await userge.forward_messages(chat_id=message.chat.id, from_chat_id=conv.chat_id, diff --git a/userge/plugins/misc/upload.py b/userge/plugins/misc/upload.py index ecb597662..f715fcd38 100644 --- a/userge/plugins/misc/upload.py +++ b/userge/plugins/misc/upload.py @@ -70,19 +70,24 @@ async def doc_upload(chat_id, path): c_time = time.time() thumb = await get_thumb() await userge.send_chat_action(chat_id, "upload_document") - msg = await userge.send_document( - chat_id=chat_id, - document=str(path), - thumb=thumb, - caption=path.name, - parse_mode="html", - disable_notification=True, - progress=progress, - progress_args=( - "uploading", userge, message, c_time + try: + msg = await userge.send_document( + chat_id=chat_id, + document=str(path), + thumb=thumb, + caption=path.name, + parse_mode="html", + disable_notification=True, + progress=progress, + progress_args=( + "uploading", userge, message, c_time, str(path.name) + ) ) - ) - await finalize(chat_id, message, msg, start_t) + except Exception as u_e: + await message.edit(u_e) + raise u_e + else: + await finalize(chat_id, message, msg, start_t) async def vid_upload(chat_id, path): @@ -94,21 +99,26 @@ async def vid_upload(chat_id, path): start_t = datetime.now() c_time = time.time() await userge.send_chat_action(chat_id, "upload_video") - msg = await userge.send_video( - chat_id=chat_id, - video=strpath, - duration=metadata.get("duration").seconds, - thumb=thumb, - caption=path.name, - parse_mode="html", - disable_notification=True, - progress=progress, - progress_args=( - "uploading", userge, message, c_time + try: + msg = await userge.send_video( + chat_id=chat_id, + video=strpath, + duration=metadata.get("duration").seconds, + thumb=thumb, + caption=path.name, + parse_mode="html", + disable_notification=True, + progress=progress, + progress_args=( + "uploading", userge, message, c_time, str(path.name) + ) ) - ) - await remove_thumb(thumb) - await finalize(chat_id, message, msg, start_t) + except Exception as u_e: + await message.edit(u_e) + raise u_e + else: + await remove_thumb(thumb) + await finalize(chat_id, message, msg, start_t) async def audio_upload(chat_id, path): @@ -126,22 +136,27 @@ async def audio_upload(chat_id, path): if metadata.has("artist"): artist = metadata.get("artist") await userge.send_chat_action(chat_id, "upload_audio") - msg = await userge.send_audio( - chat_id=chat_id, - audio=strpath, - thumb=thumb, - caption=path.name, - title=title, - performer=artist, - duration=metadata.get("duration").seconds, - parse_mode="html", - disable_notification=True, - progress=progress, - progress_args=( - "uploading", userge, message, c_time + try: + msg = await userge.send_audio( + chat_id=chat_id, + audio=strpath, + thumb=thumb, + caption=path.name, + title=title, + performer=artist, + duration=metadata.get("duration").seconds, + parse_mode="html", + disable_notification=True, + progress=progress, + progress_args=( + "uploading", userge, message, c_time, str(path.name) + ) ) - ) - await finalize(chat_id, message, msg, start_t) + except Exception as u_e: + await message.edit(u_e) + raise u_e + else: + await finalize(chat_id, message, msg, start_t) async def get_thumb(path: str = ''): @@ -172,4 +187,4 @@ async def finalize(chat_id: int, message: Message, msg: Message, start_t: int): else: end_t = datetime.now() ms = (end_t - start_t).seconds - await message.edit(f"Uploaded in {ms} seconds") \ No newline at end of file + await message.edit(f"Uploaded in {ms} seconds") diff --git a/userge/plugins/tools/timeout.py b/userge/plugins/tools/timeout.py index 85ead6f0c..f8c6df885 100644 --- a/userge/plugins/tools/timeout.py +++ b/userge/plugins/tools/timeout.py @@ -13,6 +13,7 @@ __tmp_msg__ = SAVED_SETTINGS.find_one({'_id': 'MSG_DELETE_TIMEOUT'}) __tmp_wel__ = SAVED_SETTINGS.find_one({'_id': 'WELCOME_DELETE_TIMEOUT'}) +__tmp_pp__ = SAVED_SETTINGS.find_one({'_id': 'AUTOPIC_TIMEOUT'}) if __tmp_msg__: Config.MSG_DELETE_TIMEOUT = __tmp_msg__['data'] @@ -20,7 +21,10 @@ if __tmp_wel__: Config.WELCOME_DELETE_TIMEOUT = __tmp_wel__['data'] -del __tmp_msg__, __tmp_wel__ +if __tmp_pp__: + Config.AUTOPIC_TIMEOUT = __tmp_pp__['data'] + +del __tmp_msg__, __tmp_wel__, __tmp_pp__ @userge.on_cmd("sdelto (\\d+)", about={ @@ -46,7 +50,7 @@ async def view_delete_timeout(message: Message): """view delete timeout""" if Config.MSG_DELETE_TIMEOUT: await message.edit( - f"`Currently messages will be deleted after {Config.MSG_DELETE_TIMEOUT} seconds!`", + f"`Messages will be deleted after {Config.MSG_DELETE_TIMEOUT} seconds!`", del_in=5) else: await message.edit(f"`Auto message deletion disabled!`", del_in=3) @@ -75,7 +79,34 @@ async def view_welcome_timeout(message: Message): """view welcome/left timeout""" if Config.WELCOME_DELETE_TIMEOUT: await message.edit( - f"`Currently welcome/left messages will be deleted after {Config.WELCOME_DELETE_TIMEOUT} seconds!`", + f"`Welcome/Left messages will be deleted after " + f"{Config.WELCOME_DELETE_TIMEOUT} seconds!`", del_in=5) else: await message.edit(f"`Auto welcome/left message deletion disabled!`", del_in=3) + + +@userge.on_cmd("sapicto (\\d+)", about={ + 'header': "Set auto profile picture timeout", + 'usage': "{tr}sapicto [timeout in seconds]", + 'examples': "{tr}sapicto 60"}) +async def set_app_timeout(message: Message): + """set auto profile picture timeout""" + t_o = int(message.matches[0].group(1)) + if t_o < 15: + await message.err("too short! (min > 15sec)") + return + await message.edit("`Setting auto profile picture timeout...`") + Config.AUTOPIC_TIMEOUT = t_o + SAVED_SETTINGS.update_one( + {'_id': 'AUTOPIC_TIMEOUT'}, {"$set": {'data': t_o}}, upsert=True) + await message.edit( + f"`Set auto profile picture timeout as {t_o} seconds!`", del_in=3) + + +@userge.on_cmd("vapicto", about={'header': "View auto profile picture timeout"}) +async def view_app_timeout(message: Message): + """view profile picture timeout""" + await message.edit( + f"`Profile picture will be updated after {Config.AUTOPIC_TIMEOUT} seconds!`", + del_in=5) diff --git a/userge/plugins/tools/updater.py b/userge/plugins/tools/updater.py index d8ab8d0e5..db33f8685 100644 --- a/userge/plugins/tools/updater.py +++ b/userge/plugins/tools/updater.py @@ -91,7 +91,7 @@ async def check_update(message: Message): await message.edit( '**Userge Successfully Updated!**\n' '`Now restarting... Wait for a while!`', del_in=3) - asyncio.get_event_loop().create_task(userge.restart()) + asyncio.get_event_loop().create_task(userge.restart(update_req=True)) return if not Config.HEROKU_GIT_URL: await message.err("please set heroku things...") diff --git a/userge/plugins/utils/webss.py b/userge/plugins/utils/webss.py index f0d4c138e..242552f85 100644 --- a/userge/plugins/utils/webss.py +++ b/userge/plugins/utils/webss.py @@ -8,49 +8,62 @@ import os -from time import time -import requests -from userge import userge, Message, Config +from re import match +from asyncio import sleep + +import aiofiles +from selenium import webdriver -CHANNEL = userge.getCLogger(__name__) +from userge import userge, Message, Config @userge.on_cmd("webss", about={'header': "Get snapshot of a website"}) async def webss(message: Message): - if Config.SCREENSHOT_API is None: - await message.edit( - "Damn!\nI forgot to get the api from (here)[https://screenshotlayer.com]", - del_in=0) + link_match = match(r'\bhttps?://.*\.\S+', message.input_str) + if not link_match: + await message.err("`I need a valid link to take screenshots from.`") return - await message.edit("`Processing`") - suc, data = await getimg(message.input_str) - if suc: - await message.edit('Uploading..') - await userge.send_chat_action(message.chat.id, "upload_photo") - - msg = await userge.send_document(message.chat.id, data, caption=message.input_str) - await CHANNEL.fwd_msg(msg) - - await message.delete() - await userge.send_chat_action(message.chat.id, "cancel") - if os.path.isfile(data): - os.remove(data) - else: - await message.err(data, del_in=6) - - -async def getimg(url): - requrl = "https://api.screenshotlayer.com/api/capture" - requrl += "?access_key={}&url={}&fullpage={}&viewport={}" - response = requests.get( - requrl.format(Config.SCREENSHOT_API, url, '1', "2560x1440"), - stream=True - ) - if 'image' in response.headers["content-type"]: - fname = f"screenshot_{time()}.png" - with open(fname, "wb") as file: - for chunk in response.iter_content(chunk_size=128): - file.write(chunk) - return True, fname - else: - return False, response.text + link = link_match.group() + if Config.GOOGLE_CHROME_BIN is None: + await message.err("need to install Google Chrome. Module Stopping") + return + await message.edit("`Processing ...`") + chrome_options = webdriver.ChromeOptions() + chrome_options.binary_location = Config.GOOGLE_CHROME_BIN + chrome_options.add_argument('--ignore-certificate-errors') + chrome_options.add_argument("--test-type") + chrome_options.add_argument("--headless") + chrome_options.add_argument('--no-sandbox') + chrome_options.add_argument('--disable-dev-shm-usage') + chrome_options.add_argument("--no-sandbox") + chrome_options.add_argument('--disable-gpu') + driver = webdriver.Chrome(chrome_options=chrome_options) + driver.get(link) + height = driver.execute_script( + "return Math.max(document.body.scrollHeight, document.body.offsetHeight, " + "document.documentElement.clientHeight, document.documentElement.scrollHeight, " + "document.documentElement.offsetHeight);") + width = driver.execute_script( + "return Math.max(document.body.scrollWidth, document.body.offsetWidth, " + "document.documentElement.clientWidth, document.documentElement.scrollWidth, " + "document.documentElement.offsetWidth);") + driver.set_window_size(width + 125, height + 125) + wait_for = height / 1000 + await message.edit(f"`Generating screenshot of the page...`" + f"\n`Height of page = {height}px`" + f"\n`Width of page = {width}px`" + f"\n`Waiting ({int(wait_for)}s) for the page to load.`") + await sleep(int(wait_for)) + im_png = driver.get_screenshot_as_png() + driver.close() + message_id = message.message_id + if message.reply_to_message: + message_id = message.reply_to_message.message_id + file_path = os.path.join(Config.DOWN_PATH, "webss.png") + async with aiofiles.open(file_path, 'wb') as out_file: + await out_file.write(im_png) + await userge.send_document(chat_id=message.chat.id, + document=file_path, + caption=link, + reply_to_message_id=message_id) + os.remove(file_path) diff --git a/userge/plugins/utils/welcome.py b/userge/plugins/utils/welcome.py index 573e1ffcb..a11dcd50f 100644 --- a/userge/plugins/utils/welcome.py +++ b/userge/plugins/utils/welcome.py @@ -12,6 +12,7 @@ import base64 import asyncio +import aiofiles from hachoir.metadata import extractMetadata from hachoir.parser import createParser from pyrogram import Message as RawMessage @@ -206,8 +207,8 @@ async def raw_set(message: Message, name, collection, chats): "trying to download", userge, message, c_time ) ) - with open(tmp_path, "rb") as media_file: - media = base64.b64encode(media_file.read()) + async with aiofiles.open(tmp_path, "rb") as media_file: + media = base64.b64encode(await media_file.read()) file_name = os.path.basename(tmp_path) os.remove(tmp_path) @@ -299,8 +300,8 @@ async def raw_say(message: Message, name, collection): file_name = found['name'] media = found['media'] tmp_media_path = os.path.join(Config.DOWN_PATH, file_name) - with open(tmp_media_path, "wb") as media_file: - media_file.write(base64.b64decode(media)) + async with aiofiles.open(tmp_media_path, "wb") as media_file: + await media_file.write(base64.b64decode(media)) file_id, file_ref = await send_proper_type(message, caption, file_type, tmp_media_path) collection.update_one({'_id': message.chat.id}, {"$set": {'fid': file_id, 'fref': file_ref}}, diff --git a/userge/utils/exceptions.py b/userge/utils/exceptions.py index 13b9e0b89..d54d94ea9 100644 --- a/userge/utils/exceptions.py +++ b/userge/utils/exceptions.py @@ -9,11 +9,7 @@ from asyncio.exceptions import CancelledError class StopConversation(CancelledError): - """ - Exception to raise if conversation terminated. - """ + """Exception to raise if conversation terminated""" class ProcessCanceled(Exception): - """ - Custom Exception to terminate threads. - """ + """Custom Exception to terminate threads""" diff --git a/userge/utils/progress.py b/userge/utils/progress.py index 6cf28444a..7294531b0 100644 --- a/userge/utils/progress.py +++ b/userge/utils/progress.py @@ -20,7 +20,8 @@ async def progress(current: int, ud_type: str, client: 'userge.Userge', message: 'userge.Message', - start: int) -> None: + start: int, + file_name: str = '') -> None: if message.process_is_canceled: await client.stop_transmission() now = time.time() @@ -30,7 +31,7 @@ async def progress(current: int, speed = current / diff time_to_completion = time_formatter(int((total - current) / speed)) progress_str = \ - "__{}__\n" + \ + "__{}__ : `{}`\n" + \ "```[{}{}]```\n" + \ "**Progress** : `{}%`\n" + \ "**Completed** : `{}`\n" + \ @@ -39,6 +40,7 @@ async def progress(current: int, "**ETA** : `{}`" progress_str = progress_str.format( ud_type, + file_name, ''.join(["█" for i in range(floor(percentage / 5))]), ''.join(["░" for i in range(20 - floor(percentage / 5))]), round(percentage, 2), diff --git a/userge/versions.py b/userge/versions.py index ddaa7d262..678ecb212 100644 --- a/userge/versions.py +++ b/userge/versions.py @@ -14,7 +14,7 @@ __version_mjaor__ = 0 __version_minor__ = 1 __version_micro__ = 4 -__version_beta__ = 4 +__version_beta__ = 5 __version__ = "{}.{}.{}".format(__version_mjaor__, __version_minor__,