From 9cb6a7d77d196678c1a48896a38584d9c3aec693 Mon Sep 17 00:00:00 2001 From: yasirarism <55983182+yasirarism@users.noreply.github.com> Date: Wed, 5 Jul 2023 13:10:44 +0700 Subject: [PATCH] Major change with own custom client decorator (#170) * Add kbbi and carbon * add blacklist command * style: format code with black and isort (#168) Format code with black and isort This commit fixes the style issues introduced in bffcf61 according to the output from Black and isort. Details: https://app.deepsource.com/gh/yasirarism/MissKatyPyro/transform/4ddb51cc-a1ca-432a-95c4-1fb5388b405a/ Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * okk * style: format code with black and isort (#169) Format code with black and isort This commit fixes the style issues introduced in 9355c09 according to the output from Black and isort. Details: https://app.deepsource.com/gh/yasirarism/MissKatyPyro/transform/172890f8-d001-4812-8380-9a666a9a3bd5/ Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> * jmm --------- Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com> --- database/blacklist_db.py | 33 ++++ misskaty/__init__.py | 2 +- misskaty/core/custom_filter.py | 8 - misskaty/core/decorator/__init__.py | 1 + misskaty/core/message_utils.py | 67 -------- misskaty/core/misskaty_patch/__init__.py | 2 +- .../core/misskaty_patch/bound/__init__.py | 1 + .../misskaty_patch/decorators/__init__.py | 4 + .../misskaty_patch/decorators/callback.py | 100 ++++++++++++ .../core/misskaty_patch/decorators/command.py | 129 +++++++++++++++ .../core/misskaty_patch/listen/__init__.py | 1 + .../core/misskaty_patch/methods/__init__.py | 1 + .../core/misskaty_patch/utils/__init__.py | 4 + .../core/misskaty_patch/utils/admin_utils.py | 154 ++++++++++++++++++ .../core/misskaty_patch/utils/get_user.py | 122 ++++++++++++++ .../misskaty_patch/utils/handler_error.py | 86 ++++++++++ misskaty/helper/pyro_progress.py | 1 - misskaty/plugins/admin.py | 45 +++-- misskaty/plugins/afk.py | 11 +- misskaty/plugins/ban_user_or_chat.py | 2 +- misskaty/plugins/blacklist_chat.py | 120 ++++++++++++++ misskaty/plugins/chatbot_ai.py | 12 +- misskaty/plugins/currency.py | 14 +- misskaty/plugins/dev.py | 12 +- misskaty/plugins/filter_request.py | 2 +- misskaty/plugins/fun.py | 6 +- misskaty/plugins/imdb_search.py | 38 ++--- misskaty/plugins/media_extractor.py | 4 +- misskaty/plugins/misc_tools.py | 89 ++++++++-- misskaty/plugins/subscene_dl.py | 2 +- misskaty/plugins/ubot_plugin.py | 2 +- misskaty/plugins/ytdl_plugins.py | 10 +- misskaty/vars.py | 4 - requirements.txt | 2 +- update.py | 25 ++- 35 files changed, 913 insertions(+), 203 deletions(-) create mode 100644 database/blacklist_db.py delete mode 100644 misskaty/core/custom_filter.py delete mode 100644 misskaty/core/message_utils.py create mode 100644 misskaty/core/misskaty_patch/decorators/__init__.py create mode 100644 misskaty/core/misskaty_patch/decorators/callback.py create mode 100644 misskaty/core/misskaty_patch/decorators/command.py create mode 100644 misskaty/core/misskaty_patch/utils/admin_utils.py create mode 100644 misskaty/core/misskaty_patch/utils/get_user.py create mode 100644 misskaty/core/misskaty_patch/utils/handler_error.py create mode 100644 misskaty/plugins/blacklist_chat.py diff --git a/database/blacklist_db.py b/database/blacklist_db.py new file mode 100644 index 00000000..209767b1 --- /dev/null +++ b/database/blacklist_db.py @@ -0,0 +1,33 @@ +from database import dbname +from typing import List + +blacklist_filtersdb = dbname["blacklistFilters"] + +async def get_blacklisted_words(chat_id: int) -> List[str]: + _filters = await blacklist_filtersdb.find_one({"chat_id": chat_id}) + if not _filters: + return [] + return _filters["filters"] + +async def save_blacklist_filter(chat_id: int, word: str): + word = word.lower().strip() + _filters = await get_blacklisted_words(chat_id) + _filters.append(word) + await blacklist_filtersdb.update_one( + {"chat_id": chat_id}, + {"$set": {"filters": _filters}}, + upsert=True, + ) + +async def delete_blacklist_filter(chat_id: int, word: str) -> bool: + filtersd = await get_blacklisted_words(chat_id) + word = word.lower().strip() + if word in filtersd: + filtersd.remove(word) + await blacklist_filtersdb.update_one( + {"chat_id": chat_id}, + {"$set": {"filters": filtersd}}, + upsert=True, + ) + return True + return False \ No newline at end of file diff --git a/misskaty/__init__.py b/misskaty/__init__.py index 44c2fbbe..3bfb6205 100644 --- a/misskaty/__init__.py +++ b/misskaty/__init__.py @@ -40,7 +40,7 @@ MOD_NOLOAD = ["subscene_dl"] HELPABLE = {} cleanmode = {} botStartTime = time.time() -misskaty_version = "v2.9.3 - Stable" +misskaty_version = "v2.10.1 - Stable" # Pyrogram Bot Client app = Client( diff --git a/misskaty/core/custom_filter.py b/misskaty/core/custom_filter.py deleted file mode 100644 index 27887beb..00000000 --- a/misskaty/core/custom_filter.py +++ /dev/null @@ -1,8 +0,0 @@ -from pyrogram import filters - - -def pesanedit(_, __, m): - return bool(m.edit_date) - - -edited = filters.create(pesanedit) diff --git a/misskaty/core/decorator/__init__.py b/misskaty/core/decorator/__init__.py index dfae49e9..c759d8e5 100644 --- a/misskaty/core/decorator/__init__.py +++ b/misskaty/core/decorator/__init__.py @@ -1,3 +1,4 @@ +# skipcq from .errors import * from .misc import * from .permissions import * diff --git a/misskaty/core/message_utils.py b/misskaty/core/message_utils.py deleted file mode 100644 index 47f2484f..00000000 --- a/misskaty/core/message_utils.py +++ /dev/null @@ -1,67 +0,0 @@ -import asyncio -from logging import getLogger - -from pyrogram.errors import ( - ChatAdminRequired, - ChatWriteForbidden, - FloodWait, - MessageDeleteForbidden, - MessageEmpty, - MessageIdInvalid, - MessageNotModified, -) - -LOGGER = getLogger(__name__) - -# handler for TG function, so need write exception in every code - - -# Send MSG Pyro -async def kirimPesan(msg, text, **kwargs): - try: - return await msg.reply(text, **kwargs) - except FloodWait as e: - LOGGER.warning(str(e)) - await asyncio.sleep(e.value) - return await kirimPesan(msg, text, **kwargs) - except (ChatWriteForbidden, ChatAdminRequired): - LOGGER.info( - f"Leaving from {msg.chat.title} [{msg.chat.id}] because doesn't have admin permission." - ) - return await msg.chat.leave() - except Exception as e: - LOGGER.error(str(e)) - return - - -# Edit MSG Pyro -async def editPesan(msg, text, **kwargs): - try: - return await msg.edit(text, **kwargs) - except FloodWait as e: - LOGGER.warning(str(e)) - await asyncio.sleep(e.value) - return await editPesan(msg, text, **kwargs) - except (MessageNotModified, MessageIdInvalid, MessageEmpty): - return - except (ChatWriteForbidden, ChatAdminRequired): - LOGGER.info( - f"Leaving from {msg.chat.title} [{msg.chat.id}] because doesn't have admin permission." - ) - return await msg.chat.leave() - except Exception as e: - LOGGER.error(str(e)) - return - - -async def hapusPesan(msg): - try: - return await msg.delete() - except (MessageDeleteForbidden, ChatAdminRequired): - return - except FloodWait as e: - LOGGER.warning(str(e)) - await asyncio.sleep(e.value) - return await hapusPesan(msg) - except Exception as e: - LOGGER.error(str(e)) diff --git a/misskaty/core/misskaty_patch/__init__.py b/misskaty/core/misskaty_patch/__init__.py index 9898ef7c..130afb7f 100644 --- a/misskaty/core/misskaty_patch/__init__.py +++ b/misskaty/core/misskaty_patch/__init__.py @@ -1 +1 @@ -from . import bound, listen, methods +from . import bound, listen, methods, decorators diff --git a/misskaty/core/misskaty_patch/bound/__init__.py b/misskaty/core/misskaty_patch/bound/__init__.py index 76a3dce9..416f4b66 100644 --- a/misskaty/core/misskaty_patch/bound/__init__.py +++ b/misskaty/core/misskaty_patch/bound/__init__.py @@ -1 +1,2 @@ +# skipcq from .message import Message diff --git a/misskaty/core/misskaty_patch/decorators/__init__.py b/misskaty/core/misskaty_patch/decorators/__init__.py new file mode 100644 index 00000000..f6c25e9a --- /dev/null +++ b/misskaty/core/misskaty_patch/decorators/__init__.py @@ -0,0 +1,4 @@ +from .callback import callback +from .command import command + +__all__ = ["callback", "command"] diff --git a/misskaty/core/misskaty_patch/decorators/callback.py b/misskaty/core/misskaty_patch/decorators/callback.py new file mode 100644 index 00000000..24e129ca --- /dev/null +++ b/misskaty/core/misskaty_patch/decorators/callback.py @@ -0,0 +1,100 @@ +# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram +# Copyright (C) 2021 - 2022 Jayant Hegde Kageri + +# This file is part of tgEasy. + +# tgEasy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# tgEasy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with tgEasy. If not, see . + +import typing +import pyrogram +from pyrogram.methods import Decorators +from ..utils import handle_error + +def callback( + self, + data: typing.Union[str, list], + self_admin: typing.Union[bool, bool] = False, + filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None, + *args, + **kwargs, + ): + """ + ### `Client.callback` + + - A decorater to Register Callback Quiries in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_callback_query(pyrogram.filters.regex('^data.*'))` + - Parameters: + - data (str || list): + - The callback query to be handled for a function + + - self_admin (bool) **optional**: + - If True, the command will only executeed if the Bot is Admin in the Chat, By Default False + + - filter (`~pyrogram.filters`) **optional**: + - Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters. + + #### Example + .. code-block:: python + import pyrogram + + app = pyrogram.Client() + + @app.command("start") + async def start(client, message): + await message.reply_text( + f"Hello {message.from_user.mention}", + reply_markup=pyrogram.types.InlineKeyboardMarkup([[ + pyrogram.types.InlineKeyboardButton( + "Click Here", + "data" + ) + ]]) + ) + + @app.callback("data") + async def data(client, CallbackQuery): + await CallbackQuery.answer("Hello :)", show_alert=True) + """ + if filter: + filter = pyrogram.filters.regex(f"^{data}.*") & args["filter"] + else: + filter = pyrogram.filters.regex(f"^{data}.*") + + def wrapper(func): + async def decorator(client, CallbackQuery: pyrogram.types.CallbackQuery): + if self_admin: + me = await client.get_chat_member( + CallbackQuery.message.chat.id, (await client.get_me()).id + ) + if me.status not in ( + pyrogram.enums.ChatMemberStatus.OWNER, + pyrogram.enums.ChatMemberStatus.ADMINISTRATOR, + ): + return await CallbackQuery.message.edit_text( + "I must be admin to execute this Command" + ) + try: + await func(client, CallbackQuery) + except pyrogram.errors.exceptions.forbidden_403.ChatAdminRequired: + pass + except BaseException as e: + return await handle_error(e, CallbackQuery) + + self.add_handler( + pyrogram.handlers.CallbackQueryHandler(decorator, filter) + ) + return decorator + + return wrapper + +Decorators.on_cb = callback \ No newline at end of file diff --git a/misskaty/core/misskaty_patch/decorators/command.py b/misskaty/core/misskaty_patch/decorators/command.py new file mode 100644 index 00000000..6a4f072c --- /dev/null +++ b/misskaty/core/misskaty_patch/decorators/command.py @@ -0,0 +1,129 @@ +import typing +import pyrogram +from ..utils import handle_error +from pyrogram.methods import Decorators +from misskaty.vars import COMMAND_HANDLER + +def command( + self, + command: typing.Union[str, list], + is_disabled: typing.Union[bool, bool] = False, + pm_only: typing.Union[bool, bool] = False, + group_only: typing.Union[bool, bool] = False, + self_admin: typing.Union[bool, bool] = False, + self_only: typing.Union[bool, bool] = False, + no_channel: typing.Union[bool, bool] = False, + handler: typing.Optional[list] = None, + filter: typing.Union[pyrogram.filters.Filter, pyrogram.filters.Filter] = None, + *args, + **kwargs + ): + """ + ### `tgClient.command` + - A decorater to Register Commands in simple way and manage errors in that Function itself, alternative for `@pyrogram.Client.on_message(pyrogram.filters.command('command'))` + - Parameters: + - command (str || list): + - The command to be handled for a function + + - group_only (bool) **optional**: + - If True, the command will only executed in Groups only, By Default False. + + - pm_only (bool) **optional**: + - If True, the command will only executed in Private Messages only, By Default False. + + - self_only (bool) **optional**: + - If True, the command will only excute if used by Self only, By Default False. + + - handler (list) **optional**: + - If set, the command will be handled by the specified Handler, By Default `Config.HANDLERS`. + + - self_admin (bool) **optional**: + - If True, the command will only executeed if the Bot is Admin in the Chat, By Default False + + - filter (`~pyrogram.filters`) **optional**: + - Pyrogram Filters, hope you know about this, for Advaced usage. Use `and` for seaperating filters. + + #### Example + .. code-block:: python + import pyrogram + + app = pyrogram.Client() + + @app.on_cmd("start", is_disabled=False, group_only=False, pm_only=False, self_admin=False, self_only=False, pyrogram.filters.chat("777000") and pyrogram.filters.text) + async def start(client, message): + await message.reply_text(f"Hello {message.from_user.mention}") + """ + if handler is None: + handler = COMMAND_HANDLER + if filter: + if self_only: + filter = ( + pyrogram.filters.command(command, prefixes=handler) + & filter + & pyrogram.filters.me + ) + else: + filter = ( + pyrogram.filters.command(command, prefixes=handler) + & filter + & pyrogram.filters.me + ) + else: + if self_only: + filter = ( + pyrogram.filters.command(command, prefixes=handler) + & pyrogram.filters.me + ) + else: + filter = pyrogram.filters.command(command, prefixes=handler) + + def wrapper(func): + async def decorator(client, message: pyrogram.types.Message): + if is_disabled: + return await message.reply_text("Sorry, this command has been disabled by owner.") + if not message.from_user: + return await message.reply_text("I'm cannot identify user. Use my command in private chat.") + if ( + self_admin + and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP + ): + return await message.reply_text( + "This command can be used in supergroups only." + ) + if self_admin: + me = await client.get_chat_member( + message.chat.id, (await client.get_me()).id + ) + if me.status not in ( + pyrogram.enums.ChatMemberStatus.OWNER, + pyrogram.enums.ChatMemberStatus.ADMINISTRATOR, + ): + return await message.reply_text( + "I must be admin to execute this Command" + ) + if ( + group_only + and message.chat.type != pyrogram.enums.ChatType.SUPERGROUP + ): + return await message.reply_text( + "This command can be used in supergroups only." + ) + if pm_only and message.chat.type != pyrogram.enums.ChatType.PRIVATE: + return await message.reply_text( + "This command can be used in PMs only." + ) + try: + await func(client, message) + except pyrogram.errors.exceptions.forbidden_403.ChatWriteForbidden: + await client.leave_chat(message.chat.id) + except BaseException as exception: + return await handle_error(exception, message) + + self.add_handler( + pyrogram.handlers.MessageHandler(callback=decorator, filters=filter) + ) + return decorator + + return wrapper + +Decorators.on_cmd = command \ No newline at end of file diff --git a/misskaty/core/misskaty_patch/listen/__init__.py b/misskaty/core/misskaty_patch/listen/__init__.py index 1d0375d9..1657d965 100644 --- a/misskaty/core/misskaty_patch/listen/__init__.py +++ b/misskaty/core/misskaty_patch/listen/__init__.py @@ -1 +1,2 @@ +# skipcq from .listen import Chat, Client, MessageHandler, User diff --git a/misskaty/core/misskaty_patch/methods/__init__.py b/misskaty/core/misskaty_patch/methods/__init__.py index 5b492ca4..3a8c831f 100644 --- a/misskaty/core/misskaty_patch/methods/__init__.py +++ b/misskaty/core/misskaty_patch/methods/__init__.py @@ -1,3 +1,4 @@ +# skipcq from .edit_message_text import edit_message_text from .send_as_file import send_as_file from .send_message import send_message diff --git a/misskaty/core/misskaty_patch/utils/__init__.py b/misskaty/core/misskaty_patch/utils/__init__.py index a788cb66..4cf62927 100644 --- a/misskaty/core/misskaty_patch/utils/__init__.py +++ b/misskaty/core/misskaty_patch/utils/__init__.py @@ -1 +1,5 @@ +# skipcq +from .admin_utils import check_rights, is_admin +from .get_user import get_user +from .handler_error import handle_error from .utils import PyromodConfig, patch, patchable diff --git a/misskaty/core/misskaty_patch/utils/admin_utils.py b/misskaty/core/misskaty_patch/utils/admin_utils.py new file mode 100644 index 00000000..e70ff73e --- /dev/null +++ b/misskaty/core/misskaty_patch/utils/admin_utils.py @@ -0,0 +1,154 @@ +# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram +# Copyright (C) 2021 - 2022 Jayant Hegde Kageri + +# This file is part of tgEasy. + +# tgEasy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# tgEasy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with tgEasy. If not, see . + +import typing +import pyrogram + + +async def check_rights( + chat_id: typing.Union[int, int], + user_id: typing.Union[int, int], + rights: typing.Union[str, list], + client, +) -> bool: + """ + ### `check_rights` + - Checks the Rights of an User + - This is an Helper Function for `adminsOnly` + + - Parameters: + - chat_id (int): + - The Chat ID of Which Chat have to check the Rights. + + - user_id (int): + - The User ID of Whose Rights have to Check. + + - rights (str): + - The Rights have to Check. + + - client (`pyrogram.Client`): + - From which Client to Check the Rights. + + - Returns: + - `True` if the User have the Right. + - `False` if the User don't have the Right. + + #### Example + .. code-block:: python + import pyrogram + + app = pyrogram.Client() + + @app.command("ban", group_only=True, self_admin=True) + async def ban(client, message): + if not await check_rights(message.chat.id, message.from_user.id, "can_restrict_members"): + return await message.reply_text("You don't have necessary rights to use this Command.") + user = await get_user(message) + await message.chat.kick_member(user.id) + """ + try: + user = await client.get_chat_member(chat_id, user_id) + except Exception: + return False + if user.status == "user": + return False + if user.status in ( + pyrogram.enums.ChatMemberStatus.OWNER, + pyrogram.enums.ChatMemberStatus.ADMINISTRATOR, + ): + permission = [] + if user.privileges.can_manage_chat: + permission.append("can_manage_chat") + + if user.privileges.can_delete_messages: + permission.append("can_delete_messages") + + if user.privileges.can_manage_video_chats: + permission.append("can_manage_video_chats") + + if user.privileges.can_restrict_members: + permission.append("can_restrict_members") + + if user.privileges.can_promote_members: + permission.append("can_promote_members") + + if user.privileges.can_change_info: + permission.append("can_change_info") + + if user.privileges.can_post_messages: + permission.append("can_post_messages") + + if user.privileges.can_edit_messages: + permission.append("can_edit_messages") + + if user.privileges.can_invite_users: + permission.append("can_invite_users") + + if user.privileges.can_pin_messages: + permission.append("can_pin_messages") + + if user.privileges.is_anonymous: + permission.append("is_anonymous") + + if isinstance(rights, str): + return rights in permission + if isinstance(rights, list): + for right in rights: + return right in permission + return False + + +async def is_admin( + chat_id: typing.Union[int, str], user_id: typing.Union[int, str], client +) -> bool: + """ + ### `is_admin` + - A Functions to Check if the User is Admin or not + + - Parameters: + - chat_id (int): + - The Chat ID of Which Chat have to check the Admin Status. + + - user_id (int): + - The User ID of Whose Admin Status have to Check. + + - client (`pyrogram.Client`): + - From which Client to Check the Admin Status. + + - Returns: + - `True` if the User is Admin. + - `False` if the User is't Admin. + #### Example + .. code-block:: python + import pyrogram + + app = pyrogram.Client() + + @app.command("ban", group_only=True, self_admin=True) + @app.adminsOnly("can_restrict_members") + async def ban(client, message): + if await is_admin(message.chat.id, (await get_user(mesasge)).id): + return await message.reply_text("You can't Ban Admins.") + await message.chat.kick_member((await get_user(message)).id) + await message.reply_text("User has been Banned.") + """ + try: + user = await client.get_chat_member(chat_id, user_id) + except Exception: + return False + return user.status in (pyrogram.enums.ChatMemberStatus.OWNER, pyrogram.enums.ChatMemberStatus.ADMINISTRATOR,) \ No newline at end of file diff --git a/misskaty/core/misskaty_patch/utils/get_user.py b/misskaty/core/misskaty_patch/utils/get_user.py new file mode 100644 index 00000000..a2a0fd1a --- /dev/null +++ b/misskaty/core/misskaty_patch/utils/get_user.py @@ -0,0 +1,122 @@ +# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram +# Copyright (C) 2021 - 2022 Jayant Hegde Kageri + +# This file is part of tgEasy. + +# tgEasy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# tgEasy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with tgEasy. If not, see . + +import contextlib +import typing + +import pyrogram + + +async def get_user( + m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery] +) -> pyrogram.types.User or bool: + """ + ### `tgEasy.get_user` + - Gets a User from Message/RepliedMessage/CallbackQuery + - Parameters: + - m (`~pyrogram.types.Message` || `~pyrogram.types.CallbackQuery`) + - Returns: + - `pyrogram.types.User` on Success + - `False` on Error + + #### Example + .. code-block:: python + from tgEasy import get_user, command, adminsOnly + + @command("ban", group_only=True, self_admin=True) + @adminsOnly("can_restrict_members") + async def ban(client, message): + user = await get_user(message) + await message.chat.kick_member(user.id) + """ + if isinstance(m, pyrogram.types.Message): + message = m + client = m._client + if isinstance(m, pyrogram.types.CallbackQuery): + message = m.message + client = message._client + if message.reply_to_message: + if message.reply_to_message.sender_chat: + return False + return await client.get_users(message.reply_to_message.from_user.id) + + command = message.command[1] if len(message.command) > 1 else None + if command and (command.startswith("@") or command.isdigit()): + with contextlib.suppress( + pyrogram.errors.exceptions.bad_request_400.UsernameNotOccupied, + pyrogram.errors.exceptions.bad_request_400.UsernameInvalid, + pyrogram.errors.exceptions.bad_request_400.PeerIdInvalid, + IndexError, + ): + return await client.get_users(message.command[1]) + if message.entities: + for mention in message.entities: + if mention.type == "text_mention": + user = mention.user.id + break + with contextlib.suppress(Exception): + return await client.get_users(user) + return False + + +async def get_user_adv( + m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery] +) -> pyrogram.types.User or bool: + """ + ### `tgEasy.get_user_adv` + - A Function to Get the User from the Message/CallbackQuery, If there is None arguments, returns the From User. + - Parameters: + - m (`pyrogram.types.Message` || `pyrogram.types.CallbackQuery`): + - Message or Callbackquery. + - Returns: + - `pyrogram.types.User` on Success + - `False` on Error + + #### Example + .. code-block:: python + from tgEasy import command, get_user_adv + + @command("id") + async def id(client, message): + user = await get_user_adv(message) + await message.reply_text(f"Your ID is `{user.id}`") + """ + if isinstance(m, pyrogram.types.Message): + message = m + if isinstance(m, pyrogram.types.CallbackQuery): + message = m.message + if message.sender_chat: + return False + with contextlib.suppress(IndexError, AttributeError): + if len(message.command) > 1: + if message.command[1].startswith("@"): + return await get_user(message) + if message.command[1].isdigit(): + return await get_user(message) + if "text_mention" in message.entities: + return await get_user(message) + if "from_user" in str(message.reply_to_message): + return await get_user(message) + with contextlib.suppress(Exception): + if "sender_chat" in str(message.reply_to_message): + return False + if "from_user" in str(message.reply_to_message): + return await message._client.get_users( + message.reply_to_message.from_user.id + ) + return await message._client.get_users(message.from_user.id) \ No newline at end of file diff --git a/misskaty/core/misskaty_patch/utils/handler_error.py b/misskaty/core/misskaty_patch/utils/handler_error.py new file mode 100644 index 00000000..d3b83ef3 --- /dev/null +++ b/misskaty/core/misskaty_patch/utils/handler_error.py @@ -0,0 +1,86 @@ +# tgEasy - Easy for a brighter Shine. A monkey pather add-on for Pyrogram +# Copyright (C) 2021 - 2022 Jayant Hegde Kageri + +# This file is part of tgEasy. + +# tgEasy is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# tgEasy is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public License +# along with tgEasy. If not, see . +import contextlib +import os +import typing +import logging +import pyrogram +import traceback +from datetime import datetime +from misskaty.vars import LOG_CHANNEL + + +async def handle_error( + error, m: typing.Union[pyrogram.types.Message, pyrogram.types.CallbackQuery] +): + """ + ### `handle_error` + - A Function to Handle the Errors in Functions. + - This Sends the Error Log to the Log Group and Replies Sorry Message for the Users. + - This is Helper for all of the functions for handling the Errors. + + - Parameters: + - error: + - The Exceptation. + + - m (`pyrogram.types.Message` or `pyrogram.types.CallbackQuery`): + - The Message or Callback Query where the Error occurred. + + #### Exapmle + .. code-block:: python + import pyrogram + + app = tgClient(pyrogram.Client()) + + @app.command("start") + async def start(client, message): + try: + await message.reply_text("Hi :D') # I intentionally made an bug for Example :/ + except Exceptation as e: + return await handle_error(e, message) + """ + + logging = logging.getLogger(__name__) + logging.exception(traceback.format_exc()) + + day = datetime.now() + tgl_now = datetime.now() + cap_day = f"{day.strftime('%A')}, {tgl_now.strftime('%d %B %Y %H:%M:%S')}" + + with open(f"crash_{tgl_now.strftime('%d %B %Y')}.txt", "w+", encoding="utf-8") as log: + log.write(traceback.format_exc()) + log.close() + if isinstance(m, pyrogram.types.Message): + with contextlib.suppress(Exception): + await m.reply_text( + "An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience" + ) + await m._client.send_document( + LOG_CHANNEL, f"crash_{tgl_now.strftime('%d %B %Y')}.txt", caption=f"Crash Report of this Bot\n{cap_day}" + ) + if isinstance(m, pyrogram.types.CallbackQuery): + with contextlib.suppress(Exception): + await m.message.delete() + await m.message.reply_text( + "An Internal Error Occurred while Processing your Command, the Logs have been sent to the Owners of this Bot. Sorry for Inconvenience" + ) + await m.message._client.send_document( + LOG_CHANNEL, f"crash_{tgl_now.strftime('%d %B %Y')}.txt", caption=f"Crash Report of this Bot\n{cap_day}" + ) + os.remove(f"crash_{tgl_now.strftime('%d %B %Y')}.txt") + return True \ No newline at end of file diff --git a/misskaty/helper/pyro_progress.py b/misskaty/helper/pyro_progress.py index a67fd72b..23e2aa4f 100644 --- a/misskaty/helper/pyro_progress.py +++ b/misskaty/helper/pyro_progress.py @@ -14,7 +14,6 @@ async def progress_for_pyrogram(current, total, ud_type, message, start, dc_id): now = time.time() diff = now - start if round(diff % 10.00) == 0 or current == total: - # if round(current / total * 100, 0) % 5 == 0: percentage = current * 100 / total elapsed_time = round(diff) if elapsed_time == 0: diff --git a/misskaty/plugins/admin.py b/misskaty/plugins/admin.py index 70238ba1..17550218 100644 --- a/misskaty/plugins/admin.py +++ b/misskaty/plugins/admin.py @@ -106,7 +106,7 @@ async def admin_cache_func(_, cmu): # Purge CMD -@app.on_message(filters.command("purge", COMMAND_HANDLER) & filters.group) +@app.on_cmd("purge") @require_admin(permissions=["can_delete_messages"], allow_in_private=True) @ratelimiter @use_chat_lang() @@ -162,7 +162,7 @@ async def purge(self: Client, ctx: Message, strings) -> "Message": # Kick members -@app.on_message(filters.command(["kick", "dkick"], COMMAND_HANDLER) & filters.group) +@app.on_cmd(["kick", "dkick"], group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -197,9 +197,7 @@ async def kickFunc(client: Client, ctx: Message, strings) -> "Message": # Ban/DBan/TBan User -@app.on_message( - filters.command(["ban", "dban", "tban"], COMMAND_HANDLER) & filters.group -) +@app.on_cmd(["ban", "dban", "tban"], group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -261,7 +259,7 @@ async def banFunc(client, message, strings): # Unban members -@app.on_message(filters.command("unban", COMMAND_HANDLER) & filters.group) +@app.on_cmd("unban", group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -389,7 +387,7 @@ async def list_unban_(c, message, strings): # Delete messages -@app.on_message(filters.command("del", COMMAND_HANDLER) & filters.group) +@app.on_cmd("del", group_only=True) @adminsOnly("can_delete_messages") @ratelimiter @use_chat_lang() @@ -406,9 +404,7 @@ async def deleteFunc(_, message, strings): # Promote Members -@app.on_message( - filters.command(["promote", "fullpromote"], COMMAND_HANDLER) & filters.group -) +@app.on_cmd(["promote", "fullpromote"], group_only=True) @adminsOnly("can_promote_members") @ratelimiter @use_chat_lang() @@ -462,7 +458,7 @@ async def promoteFunc(client, message, strings): # Demote Member -@app.on_message(filters.command("demote", COMMAND_HANDLER) & filters.group) +@app.on_cmd("demote", group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -494,7 +490,7 @@ async def demote(client, message, strings): # Pin Messages -@app.on_message(filters.command(["pin", "unpin"], COMMAND_HANDLER) & filters.group) +@app.on_cmd(["pin", "unpin"]) @adminsOnly("can_pin_messages") @ratelimiter @use_chat_lang() @@ -524,7 +520,7 @@ async def pin(_, message, strings): # Mute members -@app.on_message(filters.command(["mute", "tmute"], COMMAND_HANDLER) & filters.group) +@app.on_cmd(["mute", "tmute"], group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -577,7 +573,7 @@ async def mute(client, message, strings): # Unmute members -@app.on_message(filters.command("unmute", COMMAND_HANDLER) & filters.group) +@app.on_cmd("unmute", group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -592,7 +588,7 @@ async def unmute(_, message, strings): await message.reply_text(strings("unmute_msg").format(umention=umention)) -@app.on_message(filters.command(["warn", "dwarn"], COMMAND_HANDLER) & filters.group) +@app.on_cmd(["warn", "dwarn"], group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -711,7 +707,7 @@ async def unban_user(_, cq, strings): # Remove Warn -@app.on_message(filters.command("rmwarn", COMMAND_HANDLER) & filters.group) +@app.on_cmd("rmwarn", group_only=True) @adminsOnly("can_restrict_members") @ratelimiter @use_chat_lang() @@ -734,8 +730,7 @@ async def remove_warnings(_, message, strings): # Warns -@app.on_message(filters.command("warns", COMMAND_HANDLER) & filters.group) -@capture_err +@app.on_cmd("warns", group_only=True) @ratelimiter @use_chat_lang() async def check_warns(_, message, strings): @@ -766,7 +761,7 @@ async def check_warns(_, message, strings): @capture_err @ratelimiter @use_chat_lang() -async def report_user(self: Client, ctx: Message, strings) -> "Message": +async def report_user(_, ctx: Message, strings) -> "Message": if not ctx.reply_to_message: return await ctx.reply_text(strings("report_no_reply")) reply = ctx.reply_to_message @@ -804,9 +799,9 @@ async def report_user(self: Client, ctx: Message, strings) -> "Message": await ctx.reply_msg(text, reply_to_message_id=ctx.reply_to_message.id) -@app.on_message(filters.command("set_chat_title", COMMAND_HANDLER) & ~filters.private) +@app.on_cmd("set_chat_title", group_only=True) @adminsOnly("can_change_info") -async def set_chat_title(self: Client, ctx: Message): +async def set_chat_title(_, ctx: Message): if len(ctx.command) < 2: return await ctx.reply_text("**Usage:**\n/set_chat_title NEW NAME") old_title = ctx.chat.title @@ -817,9 +812,9 @@ async def set_chat_title(self: Client, ctx: Message): ) -@app.on_message(filters.command("set_user_title", COMMAND_HANDLER) & ~filters.private) +@app.on_cmd("set_user_title", group_only=True) @adminsOnly("can_change_info") -async def set_user_title(self: Client, ctx: Message): +async def set_user_title(_, ctx: Message): if not ctx.reply_to_message: return await ctx.reply_text("Reply to user's message to set his admin title") if not ctx.reply_to_message.from_user: @@ -837,9 +832,9 @@ async def set_user_title(self: Client, ctx: Message): ) -@app.on_message(filters.command("set_chat_photo", COMMAND_HANDLER) & ~filters.private) +@app.on_cmd("set_chat_photo", group_only=True) @adminsOnly("can_change_info") -async def set_chat_photo(self: Client, ctx: Message): +async def set_chat_photo(_, ctx: Message): reply = ctx.reply_to_message if not reply: diff --git a/misskaty/plugins/afk.py b/misskaty/plugins/afk.py index 48d7efd0..976d126e 100644 --- a/misskaty/plugins/afk.py +++ b/misskaty/plugins/afk.py @@ -18,12 +18,10 @@ from pyrogram.types import Message from database.afk_db import add_afk, cleanmode_off, cleanmode_on, is_afk, remove_afk from misskaty import app -from misskaty.core.decorator.errors import capture_err from misskaty.core.decorator.permissions import adminsOnly from misskaty.core.decorator.ratelimiter import ratelimiter from misskaty.helper import get_readable_time2 from misskaty.helper.localization import use_chat_lang -from misskaty.vars import COMMAND_HANDLER from utils import put_cleanmode __MODULE__ = "AFK" @@ -34,11 +32,10 @@ Just type something in group to remove AFK Status.""" # Handle set AFK Command -@capture_err -@app.on_message(filters.command(["afk"], COMMAND_HANDLER)) +@app.on_cmd("afk") @ratelimiter @use_chat_lang() -async def active_afk(self: Client, ctx: Message, strings): +async def active_afk(_, ctx: Message, strings): if ctx.sender_chat: return await ctx.reply_msg(strings("no_channel"), del_in=6) user_id = ctx.from_user.id @@ -209,11 +206,11 @@ async def active_afk(self: Client, ctx: Message, strings): await put_cleanmode(ctx.chat.id, send.id) -@app.on_message(filters.command("afkdel", COMMAND_HANDLER) & filters.group) +@app.on_cmd("afkdel", group_only=True) @ratelimiter @adminsOnly("can_change_info") @use_chat_lang() -async def afk_state(self: Client, ctx: Message, strings): +async def afk_state(_, ctx: Message, strings): if not ctx.from_user: return if len(ctx.command) == 1: diff --git a/misskaty/plugins/ban_user_or_chat.py b/misskaty/plugins/ban_user_or_chat.py index 624ea558..3ac53dcf 100644 --- a/misskaty/plugins/ban_user_or_chat.py +++ b/misskaty/plugins/ban_user_or_chat.py @@ -14,7 +14,7 @@ async def ban_reply(self: Client, ctx: Message): return ban = await db.get_ban_status(ctx.from_user.id) if (ban.get("is_banned") and ctx.chat.type.value == "private") or ( - ban.get("is_banned") and ctx.chat.type.value == "supergroup" and ctx.command + ban.get("is_banned") and ctx.chat.type.value == "supergroup" and bool(ctx.command) ): await ctx.reply_msg( f'I am sorry, You are banned to use Me. \nBan Reason: {ban["ban_reason"]}' diff --git a/misskaty/plugins/blacklist_chat.py b/misskaty/plugins/blacklist_chat.py new file mode 100644 index 00000000..bc60b302 --- /dev/null +++ b/misskaty/plugins/blacklist_chat.py @@ -0,0 +1,120 @@ +""" +MIT License + +Copyright (c) 2023 TheHamkerCat + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" +import re +from time import time + +from pyrogram import filters +from pyrogram.types import ChatPermissions +from misskaty.core.decorator.errors import capture_err +from misskaty import app +from misskaty.vars import SUDO +from misskaty.core.decorator.permissions import adminsOnly +from misskaty.core.decorator.permissions import list_admins +from database.blacklist_db import ( + delete_blacklist_filter, + get_blacklisted_words, + save_blacklist_filter, +) + + +__MODULE__ = "Blacklist" +__HELP__ = """ +/blacklisted - Get All The Blacklisted Words In The Chat. +/blacklist [WORD|SENTENCE] - Blacklist A Word Or A Sentence. +/whitelist [WORD|SENTENCE] - Whitelist A Word Or A Sentence. +""" + + +@app.on_message(filters.command("blacklist") & ~filters.private) +@adminsOnly("can_restrict_members") +async def save_filters(_, message): + if len(message.command) < 2: + return await message.reply_text("Usage:\n/blacklist [WORD|SENTENCE]") + word = message.text.split(None, 1)[1].strip() + if not word: + return await message.reply_text("**Usage**\n__/blacklist [WORD|SENTENCE]__") + chat_id = message.chat.id + await save_blacklist_filter(chat_id, word) + await message.reply_text(f"__**Blacklisted {word}.**__") + + +@app.on_message(filters.command("blacklisted") & ~filters.private) +@capture_err +async def get_filterss(_, message): + data = await get_blacklisted_words(message.chat.id) + if not data: + await message.reply_text("**No blacklisted words in this chat.**") + else: + msg = f"List of blacklisted words in {message.chat.title} :\n" + for word in data: + msg += f"**-** `{word}`\n" + await message.reply_text(msg) + + +@app.on_message(filters.command("whitelist") & ~filters.private) +@adminsOnly("can_restrict_members") +async def del_filter(_, message): + if len(message.command) < 2: + return await message.reply_text("Usage:\n/whitelist [WORD|SENTENCE]") + word = message.text.split(None, 1)[1].strip() + if not word: + return await message.reply_text("Usage:\n/whitelist [WORD|SENTENCE]") + chat_id = message.chat.id + deleted = await delete_blacklist_filter(chat_id, word) + if deleted: + return await message.reply_text(f"**Whitelisted {word}.**") + await message.reply_text("**No such blacklist filter.**") + + +@app.on_message(filters.text & ~filters.private, group=8) +@capture_err +async def blacklist_filters_re(_, message): + text = message.text.lower().strip() + if not text: + return + chat_id = message.chat.id + user = message.from_user + if not user: + return + if user.id in SUDO: + return + list_of_filters = await get_blacklisted_words(chat_id) + for word in list_of_filters: + pattern = r"( |^|[^\w])" + re.escape(word) + r"( |$|[^\w])" + if re.search(pattern, text, flags=re.IGNORECASE): + if user.id in await list_admins(chat_id): + return + try: + await message.chat.restrict_member( + user.id, + ChatPermissions(), + until_date=int(time() + 3600), + ) + except Exception: + return + return await app.send_message( + chat_id, + f"Muted {user.mention} [`{user.id}`] for 1 hour " + + f"due to a blacklist match on {word}.", + ) \ No newline at end of file diff --git a/misskaty/plugins/chatbot_ai.py b/misskaty/plugins/chatbot_ai.py index c34ae3aa..64547e32 100644 --- a/misskaty/plugins/chatbot_ai.py +++ b/misskaty/plugins/chatbot_ai.py @@ -7,7 +7,6 @@ import html import openai from aiohttp import ClientSession -from pyrogram import Client, filters from pyrogram.errors import MessageTooLong from pyrogram.types import Message @@ -16,15 +15,16 @@ from misskaty.core.decorator.ratelimiter import ratelimiter from misskaty.helper import check_time_gap, post_to_telegraph from misskaty.helper.http import http from misskaty.helper.localization import use_chat_lang -from misskaty.vars import COMMAND_HANDLER, OPENAI_API, SUDO +from misskaty.vars import OPENAI_API, SUDO openai.api_key = OPENAI_API # This only for testing things, since maybe in future it will got blocked -@app.on_message(filters.command("bard", COMMAND_HANDLER)) +@app.on_cmd("bard", is_disabled=True) @use_chat_lang() -async def bard_chatbot(self: Client, ctx: Message, strings): +@ratelimiter +async def bard_chatbot(_, ctx: Message, strings): if len(ctx.command) == 1: return await ctx.reply_msg( strings("no_question").format(cmd=ctx.command[0]), quote=True, del_in=5 @@ -41,10 +41,10 @@ async def bard_chatbot(self: Client, ctx: Message, strings): await msg.edit_msg(str(e)) -@app.on_message(filters.command("ask", COMMAND_HANDLER)) +@app.on_cmd("ask") @ratelimiter @use_chat_lang() -async def openai_chatbot(self: Client, ctx: Message, strings): +async def openai_chatbot(_, ctx: Message, strings): if len(ctx.command) == 1: return await ctx.reply_msg( strings("no_question").format(cmd=ctx.command[0]), quote=True, del_in=5 diff --git a/misskaty/plugins/currency.py b/misskaty/plugins/currency.py index 0b196867..53ce50eb 100644 --- a/misskaty/plugins/currency.py +++ b/misskaty/plugins/currency.py @@ -4,12 +4,12 @@ # * Copyright ©YasirPedia All rights reserved import logging -from pyrogram import Client, filters from pyrogram.types import Message from misskaty import app +from misskaty.core.decorator.ratelimiter import ratelimiter from misskaty.helper.http import http -from misskaty.vars import COMMAND_HANDLER, CURRENCY_API +from misskaty.vars import CURRENCY_API __MODULE__ = "Currency" __HELP__ = """ @@ -19,8 +19,9 @@ __HELP__ = """ LOGGER = logging.getLogger(__name__) -@app.on_message(filters.command(["currency"], COMMAND_HANDLER)) -async def currency(self: Client, ctx: Message): +@app.on_cmd("currency") +@ratelimiter +async def currency(_, ctx: Message): if CURRENCY_API is None: return await ctx.reply_msg( "Oops!!get the API from HERE & add it to config vars (CURRENCY_API)", @@ -32,10 +33,7 @@ async def currency(self: Client, ctx: Message): del_in=6, ) - teks = ctx.text.split() - amount = teks[1] - currency_from = teks[2] - currency_to = teks[3] + amount, currency_from, currency_to = ctx.text.split() if amount.isdigit() or ( amount.replace(".", "", 1).isdigit() and amount.count(".") < 2 ): diff --git a/misskaty/plugins/dev.py b/misskaty/plugins/dev.py index 0bf348c7..dbba9233 100644 --- a/misskaty/plugins/dev.py +++ b/misskaty/plugins/dev.py @@ -20,7 +20,6 @@ from PIL import Image, ImageDraw, ImageFont from psutil import Process, boot_time, cpu_count, cpu_percent from psutil import disk_usage as disk_usage_percent from psutil import net_io_counters, virtual_memory -from pykeyboard import InlineButton, InlineKeyboard from pyrogram import Client from pyrogram import __version__ as pyrover from pyrogram import enums, filters @@ -56,6 +55,7 @@ __HELP__ = """ /unbanuser [chat id] - Unban user and make their can use bot again /gban - To Ban A User Globally. /ungban - To remove ban user globbaly. +/restart - update and restart bot. **For Public Use** /stats - Check statistic bot @@ -493,17 +493,11 @@ async def cmd_eval(self: Client, ctx: Message, strings) -> Optional[str]: # Update and restart bot -@app.on_message(filters.command(["update"], COMMAND_HANDLER) & filters.user(SUDO)) +@app.on_message(filters.command(["restart"], COMMAND_HANDLER) & filters.user(SUDO)) @use_chat_lang() async def update_restart(self: Client, ctx: Message, strings) -> "Message": - try: - out = (await shell_exec("git pull"))[0] - if "Already up to date." in str(out): - return await ctx.reply_msg(strings("already_up")) - await ctx.reply_msg(f"{out}") - except Exception as e: - return await ctx.reply_msg(str(e)) msg = await ctx.reply_msg(strings("up_and_rest")) + await shell_exec("python3 update.py") with open("restart.pickle", "wb") as status: pickle.dump([ctx.chat.id, msg.id], status) os.execvp(sys.executable, [sys.executable, "-m", "misskaty"]) diff --git a/misskaty/plugins/filter_request.py b/misskaty/plugins/filter_request.py index bcc65784..9aa5ded7 100644 --- a/misskaty/plugins/filter_request.py +++ b/misskaty/plugins/filter_request.py @@ -142,7 +142,7 @@ async def clear_reqdict(): shutil.rmtree("GensSS", ignore_errors=True) -# @app.on_message(filters.regex(r"makasi|thank|terimakasih|terima kasih|mksh", re.I) & filters.chat(chat)) +@app.on_message(filters.regex(r"makasi|thank|terimakasih|terima kasih|mksh", re.I) & filters.chat(chat)) async def thankregex(_, message): pesan = [ f"Sama-sama {message.from_user.first_name}", diff --git a/misskaty/plugins/fun.py b/misskaty/plugins/fun.py index ddc90ac7..260e3a39 100644 --- a/misskaty/plugins/fun.py +++ b/misskaty/plugins/fun.py @@ -28,7 +28,7 @@ async def draw_meme_text(image_path, text): current_h, pad = 10, 5 if upper_text: for u_text in textwrap.wrap(upper_text, width=15): - u_width, u_height = draw.getbbox(u_text, font=m_font) + u_width, u_height = draw.textsize(u_text, font=m_font) draw.text( xy=(((i_width - u_width) / 2) - 1, int((current_h / 640) * i_width)), @@ -72,7 +72,7 @@ async def draw_meme_text(image_path, text): current_h += u_height + pad if lower_text: for l_text in textwrap.wrap(lower_text, width=15): - u_width, u_height = draw.getbbox(l_text, font=m_font) + u_width, u_height = draw.textsize(l_text, font=m_font) draw.text( xy=( @@ -177,4 +177,4 @@ async def memify(client, message): @use_chat_lang() async def dice(c, m, strings): dices = await c.send_dice(m.chat.id, reply_to_message_id=m.id) - await dices.reply_msg(strings("result").format(number=dices.dice.value), quote=True) + await dices.reply_msg(strings("result").format(number=dices.dice.value), quote=True) \ No newline at end of file diff --git a/misskaty/plugins/imdb_search.py b/misskaty/plugins/imdb_search.py index d3194976..a8b3394f 100644 --- a/misskaty/plugins/imdb_search.py +++ b/misskaty/plugins/imdb_search.py @@ -10,7 +10,7 @@ from urllib.parse import quote_plus from bs4 import BeautifulSoup from deep_translator import GoogleTranslator from pykeyboard import InlineButton, InlineKeyboard -from pyrogram import Client, enums, filters +from pyrogram import Client, enums from pyrogram.errors import ( MediaCaptionTooLong, MediaEmpty, @@ -27,13 +27,11 @@ from pyrogram.types import ( Message, ) -from database.imdb_db import * -from misskaty import BOT_USERNAME, app -from misskaty.core.decorator.errors import capture_err +from database.imdb_db import add_imdbset, is_imdbset, remove_imdbset +from misskaty import app from misskaty.core.decorator.ratelimiter import ratelimiter from misskaty.core.misskaty_patch.listen.listen import ListenerTimeout from misskaty.helper import GENRES_EMOJI, get_random_string, http, search_jw -from misskaty.vars import COMMAND_HANDLER from utils import demoji LOGGER = logging.getLogger(__name__) @@ -44,10 +42,9 @@ headers = { # IMDB Choose Language -@app.on_message(filters.command(["imdb"], COMMAND_HANDLER)) -@capture_err +@app.on_cmd("imdb") @ratelimiter -async def imdb_choose(self: Client, ctx: Message): +async def imdb_choose(_, ctx: Message): if len(ctx.command) == 1: return await ctx.reply_msg( f"ℹ️ Please add query after CMD!\nEx: /{ctx.command[0]} Jurassic World", @@ -91,10 +88,9 @@ async def imdb_choose(self: Client, ctx: Message): pass -@app.on_callback_query(filters.regex("^imdbset")) +@app.on_cb("imdbset") @ratelimiter -@capture_err -async def imdblangset(self: Client, query: CallbackQuery): +async def imdblangset(_, query: CallbackQuery): i, uid = query.data.split("#") if query.from_user.id != int(uid): return await query.answer("⚠️ Access Denied!", True) @@ -123,10 +119,9 @@ async def imdblangset(self: Client, query: CallbackQuery): pass -@app.on_callback_query(filters.regex("^setimdb")) +@app.on_cb("setimdb") @ratelimiter -@capture_err -async def imdbsetlang(self: Client, query: CallbackQuery): +async def imdbsetlang(_, query: CallbackQuery): i, lang, uid = query.data.split("#") if query.from_user.id != int(uid): return await query.answer("⚠️ Access Denied!", True) @@ -278,10 +273,9 @@ async def imdb_search_en(kueri, message): ) -@app.on_callback_query(filters.regex("^imdbcari")) +@app.on_cb("imdbcari") @ratelimiter -@capture_err -async def imdbcari(self: Client, query: CallbackQuery): +async def imdbcari(_, query: CallbackQuery): BTN = [] i, lang, msg, uid = query.data.split("#") if lang == "ind": @@ -404,9 +398,8 @@ async def imdbcari(self: Client, query: CallbackQuery): ) -@app.on_callback_query(filters.regex("^imdbres_id")) +@app.on_cb("imdbres_id") @ratelimiter -@capture_err async def imdb_id_callback(self: Client, query: CallbackQuery): i, userid, movie = query.data.split("#") if query.from_user.id != int(userid): @@ -515,7 +508,7 @@ async def imdb_id_callback(self: Client, query: CallbackQuery): res_str += "\n" if ott != "": res_str += f"Tersedia di:\n{ott}\n" - res_str += f"©️ IMDb by @{BOT_USERNAME}" + res_str += f"©️ IMDb by @{self.me.username}" if trailer := r_json.get("trailer"): trailer_url = trailer["url"] markup = InlineKeyboardMarkup( @@ -562,9 +555,8 @@ async def imdb_id_callback(self: Client, query: CallbackQuery): pass -@app.on_callback_query(filters.regex("^imdbres_en")) +@app.on_cb("imdbres_en") @ratelimiter -@capture_err async def imdb_en_callback(self: Client, query: CallbackQuery): i, userid, movie = query.data.split("#") if query.from_user.id != int(userid): @@ -672,7 +664,7 @@ async def imdb_en_callback(self: Client, query: CallbackQuery): res_str += "\n" if ott != "": res_str += f"Available On:\n{ott}\n" - res_str += f"©️ IMDb by @{BOT_USERNAME}" + res_str += f"©️ IMDb by @{self.me.username}" if trailer := r_json.get("trailer"): trailer_url = trailer["url"] markup = InlineKeyboardMarkup( diff --git a/misskaty/plugins/media_extractor.py b/misskaty/plugins/media_extractor.py index 74723a4d..7c0b32b5 100644 --- a/misskaty/plugins/media_extractor.py +++ b/misskaty/plugins/media_extractor.py @@ -150,9 +150,7 @@ async def convertsrt(self: Client, ctx: Message, strings): f"ConvertSub: {filename} by {ctx.from_user.first_name if ctx.from_user else ctx.sender_chat.title} [{ctx.from_user.id if ctx.from_user else ctx.sender_chat.id}]" ) suffix = "srt" if ctx.command[0] == "converttosrt" else "ass" - (await shell_exec(f"ffmpeg -i '{dl}' 'downloads/{filename}.{suffix}'"))[ - 0 - ] # skipcq: PYL-W0106 + await shell_exec(f"ffmpeg -i '{dl}' 'downloads/{filename}.{suffix}'") c_time = time() await ctx.reply_document( f"downloads/{filename}.{suffix}", diff --git a/misskaty/plugins/misc_tools.py b/misskaty/plugins/misc_tools.py index ed121dfd..d0d5f440 100644 --- a/misskaty/plugins/misc_tools.py +++ b/misskaty/plugins/misc_tools.py @@ -19,7 +19,12 @@ from gtts import gTTS from PIL import Image from pyrogram import Client, filters from pyrogram.errors import MessageTooLong, UserNotParticipant -from pyrogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) from misskaty import BOT_USERNAME, app from misskaty.core.decorator.errors import capture_err @@ -33,6 +38,8 @@ LOGGER = getLogger(__name__) __MODULE__ = "Misc" __HELP__ = """ +/carbon [text or reply to text or caption] - Make beautiful snippet code on carbon from text. +/kbbi [keyword] - Search definition on KBBI (For Indonesian People) /sof [query] - Search your problem in StackOverflow. /google [query] - Search using Google Search. (/tr, /trans, /translate) [lang code] - Translate text using Google Translate. @@ -54,6 +61,70 @@ def remove_html_tags(text): return re.sub(clean, "", text) +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edge/107.0.1418.42" +} + + +async def get_content(url): + async with aiohttp.ClientSession() as session: + r = await session.get(url, headers=headers) + return await r.read() + + +@app.on_cmd("kbbi") +async def kbbi_search(_, ctx: Client): + if len(ctx.command) == 1: + return await ctx.reply_msg("Please add keyword to search definition in kbbi") + r = (await http.get(f"https://yasirapi.eu.org/kbbi?kata={ctx.input}")).json() + if nomsg := r.get("detail"): + return await ctx.reply_msg(nomsg) + kbbi_btn = InlineKeyboardMarkup( + [[InlineKeyboardButton(text="Open in Web", url=r.get("link"))]] + ) + res = "Definisi:\n" + for num, a in enumerate(r.get("result"), start=1): + submakna = "".join(f"{a}, " for a in a["makna"][0]["submakna"])[:-2] + contoh = "".join(f"{a}, " for a in a["makna"][0]["contoh"])[:-2] + kt_dasar = "".join(f"{a}, " for a in a["kata_dasar"])[:-2] + bt_takbaku = "".join(f"{a}, " for a in a["bentuk_tidak_baku"])[:-2] + res += f"{a['nama']} ({a['makna'][0]['kelas'][0]['nama']}: {a['makna'][0]['kelas'][0]['deskripsi']})\nKata Dasar: {kt_dasar if kt_dasar else '-'}\nBentuk Tidak Baku: {bt_takbaku if bt_takbaku else '-'}\nSubmakna: {submakna}\nContoh: {contoh if contoh else '-'}\n\n" + await ctx.reply(f"{res}By YasirPedia API", reply_markup=kbbi_btn) + + +@app.on_cmd("carbon") +async def carbon_make(self: Client, ctx: Message): + if len(ctx.command) == 1 and not ctx.reply_to_message: + return await ctx.reply( + "Please reply text to make carbon or add text after command." + ) + if not ctx.reply_to_message: + return await ctx.reply( + "Please reply text to make carbon or add text after command." + ) + if ctx.reply_to_message.text: + text = ctx.reply_to_message.text + elif ctx.reply_to_message.caption: + text = ctx.reply_to_message.caption + else: + text = ctx.input + json_data = { + "code": text, + "backgroundColor": "#1F816D", + } + + response = await http.post( + "https://carbon.yasirapi.eu.org/api/cook", json=json_data + ) + fname = ( + f"carbonBY_{ctx.from_user.id if ctx.from_user else ctx.sender_chat.title}.png" + ) + with open(fname, "wb") as e: + e.write(response.content) + await ctx.reply_photo(fname, caption=f"Generated by @{self.me.username}") + os.remove(fname) + + @app.on_message(filters.command("readqr", COMMAND_HANDLER)) @ratelimiter async def readqr(c, m): @@ -130,9 +201,6 @@ async def gsearch(client, message): query = message.text.split(" ", maxsplit=1)[1] msg = await message.reply_text(f"**Googling** for `{query}` ...") try: - headers = { - "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 Edge/107.0.1418.42" - } html = await http.get( f"https://www.google.com/search?q={query}&gl=id&hl=id&num=17", headers=headers, @@ -210,7 +278,7 @@ async def translate(client, message): @app.on_message(filters.command(["tts"], COMMAND_HANDLER)) @capture_err @ratelimiter -async def tts(_, message): +async def tts_convert(_, message): if message.reply_to_message and ( message.reply_to_message.text or message.reply_to_message.caption ): @@ -415,17 +483,6 @@ async def close_callback(bot: Client, query: CallbackQuery): pass -headers = { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/600.1.17 (KHTML, like Gecko) Version/7.1 Safari/537.85.10" -} - - -async def get_content(url): - async with aiohttp.ClientSession() as session: - r = await session.get(url, headers=headers) - return await r.read() - - async def mdlapi(title): link = f"https://kuryana.vercel.app/search/q/{title}" async with aiohttp.ClientSession() as ses, ses.get(link) as result: diff --git a/misskaty/plugins/subscene_dl.py b/misskaty/plugins/subscene_dl.py index 7dc14a37..d9a57463 100644 --- a/misskaty/plugins/subscene_dl.py +++ b/misskaty/plugins/subscene_dl.py @@ -37,7 +37,7 @@ async def getTitleSub(msg, kueri, CurrentPage, user): lists = soup.find("div", {"class": "search-result"}) entry = lists.find_all("div", {"class": "title"}) # if "Tidak Ditemukan" in entry[0].text: - # await editPesan(msg, f"Sorry, could not find any result for: {kueri}") + # await msg.edit_msg(f"Sorry, could not find any result for: {kueri}") # return None, 0, None for sub in entry: title = sub.find("a").text diff --git a/misskaty/plugins/ubot_plugin.py b/misskaty/plugins/ubot_plugin.py index 633e6429..20c5b904 100644 --- a/misskaty/plugins/ubot_plugin.py +++ b/misskaty/plugins/ubot_plugin.py @@ -70,7 +70,7 @@ async def del_msg(client, message): ) -# @user.on_edited_message(filters.text & filters.chat(-1001455886928)) +@user.on_edited_message(filters.text & filters.chat(-1001455886928)) async def edit_msg(client, message): try: ustat = ( diff --git a/misskaty/plugins/ytdl_plugins.py b/misskaty/plugins/ytdl_plugins.py index d8cf62d2..3534ed90 100644 --- a/misskaty/plugins/ytdl_plugins.py +++ b/misskaty/plugins/ytdl_plugins.py @@ -21,7 +21,6 @@ from pyrogram.types import ( from misskaty import app from misskaty.core.decorator.errors import capture_err from misskaty.core.decorator.ratelimiter import ratelimiter -from misskaty.core.misskaty_patch.listen.listen import ListenerTimeout from misskaty.helper.http import http from misskaty.helper.localization import use_chat_lang from misskaty.vars import COMMAND_HANDLER, LOG_CHANNEL @@ -35,13 +34,10 @@ def rand_key(): return str(uuid4())[:8] -@app.on_message(filters.command(["ytsearch"], COMMAND_HANDLER) & ~filters.channel) -@capture_err +@app.on_cmd("ytsearch", no_channel=True) @ratelimiter @use_chat_lang() async def ytsearch(self: Client, ctx: Message, strings): - if ctx.sender_chat: - return await ctx.reply_msg(strings("no_channel")) if len(ctx.command) == 1: return await ctx.reply_msg(strings("no_query")) query = ctx.text.split(" ", maxsplit=1)[1] @@ -82,7 +78,7 @@ async def ytsearch(self: Client, ctx: Message, strings): @app.on_message( filters.command(["ytdown"], COMMAND_HANDLER) - | filters.regex(YT_REGEX) & ~filters.channel + | filters.regex(YT_REGEX) & ~filters.channel & ~filters.via_bot ) @capture_err @ratelimiter @@ -110,7 +106,7 @@ async def ytdownv2(self: Client, ctx: Message, strings): await ctx.reply_msg(f"Opps, ERROR: {str(err)}") -@app.on_callback_query(filters.regex(r"^yt_listall")) +@app.on_cb(filters.regex(r"^yt_listall")) @ratelimiter @use_chat_lang() async def ytdl_listall_callback(self: Client, cq: CallbackQuery, strings): diff --git a/misskaty/vars.py b/misskaty/vars.py index fc9ec30e..24d8825b 100644 --- a/misskaty/vars.py +++ b/misskaty/vars.py @@ -2,14 +2,10 @@ # * @date 2023-06-21 22:12:27 # * @projectName MissKatyPyro # * Copyright ©YasirPedia All rights reserved -import os import sys from logging import getLogger from os import environ -import requests -from dotenv import load_dotenv - LOGGER = getLogger(__name__) diff --git a/requirements.txt b/requirements.txt index 65b5e1a5..60ea25b7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,7 +18,7 @@ psutil python-dateutil telegraph hachoir -Pillow==10.0.0 +Pillow==9.5.0 httpx[http2] git+https://github.com/yasirarism/vcsi git+https://github.com/yasirarism/iytdl diff --git a/update.py b/update.py index 9b7ba4cb..99f98bd3 100644 --- a/update.py +++ b/update.py @@ -1,13 +1,26 @@ -import logging +from logging import INFO, StreamHandler, basicConfig, getLogger, handlers import os import subprocess -import time import dotenv import requests from git import Repo -LOGGER = logging.getLogger(__name__) +if os.path.exists("MissKatyLogs.txt"): + with open("MissKatyLogs.txt", "r+") as f: + f.truncate(0) + +basicConfig( + level=INFO, + format="[%(asctime)s - %(levelname)s] - %(name)s.%(funcName)s - %(message)s", + datefmt="%d-%b-%y %H:%M:%S", + handlers=[ + handlers.RotatingFileHandler("MissKatyLogs.txt", mode="w+", maxBytes=1000000), + StreamHandler(), + ], +) + +LOGGER = getLogger(__name__) ENV_URL = os.environ.get("ENV_URL") try: @@ -49,12 +62,6 @@ if all([UPSTREAM_REPO_URL, UPSTREAM_REPO_BRANCH]): except Exception as e: LOGGER.error(e) pass - # time.sleep(6) - # update = subprocess.run(['pip3', 'install', '-U', '-r', 'requirements.txt']) - # if update.returncode == 0: - # LOGGER.info("Successfully update package pip python") - # else: - # LOGGER.warning("Unsuccessfully update package pip python") else: LOGGER.warning( "UPSTREAM_REPO_URL or UPSTREAM_REPO_BRANCH is not defined, Skipping auto update"