diff --git a/Powers/__main__.py b/Powers/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..4bca9cf734bf02000a2d2b5261eca41a99c34880 --- /dev/null +++ b/Powers/__main__.py @@ -0,0 +1,4 @@ +from Powers.bot_class import Gojo + +if __name__ == "__main__": + Gojo().run() \ No newline at end of file diff --git a/Powers/bot_class.py b/Powers/bot_class.py new file mode 100644 index 0000000000000000000000000000000000000000..37ad78c86a954bd21cc56406ab4fc4481a52ec5b --- /dev/null +++ b/Powers/bot_class.py @@ -0,0 +1,116 @@ +from platform import python_version +from threading import RLock +from time import gmtime, strftime, time + +from pyrogram import Client, __version__ +from pyrogram.raw.all import layer + +from Powers import ( + API_HASH, + APP_ID, + BOT_TOKEN, + LOG_DATETIME, + LOGFILE, + LOGGER, + MESSAGE_DUMP, + NO_LOAD, + UPTIME, + WORKERS, + load_cmds, +) +from Powers.database import MongoDB +from Powers.plugins import all_plugins +from Powers.tr_engine import lang_dict +from Powers.vars import Config + +INITIAL_LOCK = RLock() + +# Check if MESSAGE_DUMP is correct +if MESSAGE_DUMP == -100 or not str(MESSAGE_DUMP).startswith("-100"): + raise Exception( + "Please enter a vaild Supergroup ID, A Supergroup ID starts with -100", + ) + + +class Gojo(Client): + """Starts the Pyrogram Client on the Bot Token when we do 'python3 -m Powers'""" + + def __init__(self): + name = self.__class__.__name__.lower() + + super().__init__( + "Gojo_Satarou", + bot_token=BOT_TOKEN, + plugins=dict(root=f"{name}.plugins", exclude=NO_LOAD), + api_id=APP_ID, + api_hash=API_HASH, + workers=WORKERS, + ) + + async def start(self): + """Start the bot.""" + await super().start() + + meh = await self.get_me() # Get bot info from pyrogram client + LOGGER.info("Starting bot...") + Config.BOT_ID = meh.id + Config.BOT_NAME = meh.first_name + Config.BOT_USERNAME = meh.username + + startmsg = await self.send_message(MESSAGE_DUMP, "Starting Bot...") + + # Load Languages + lang_status = len(lang_dict) >= 1 + LOGGER.info(f"Loading Languages: {lang_status}\n") + + # Show in Log that bot has started + LOGGER.info( + f"Pyrogram v{__version__} (Layer - {layer}) started on {meh.username}", + ) + LOGGER.info(f"Python Version: {python_version()}\n") + + # Get cmds and keys + cmd_list = await load_cmds(await all_plugins()) + + LOGGER.info(f"Plugins Loaded: {cmd_list}") + + # Send a message to MESSAGE_DUMP telling that the + # bot has started and has loaded all plugins! + await startmsg.edit_text( + ( + f"@{meh.username} started on Pyrogram v{__version__} (Layer - {layer})\n" + f"\nPython: {python_version()}\n" + "\nLoaded Plugins:\n" + f"{cmd_list}\n" + ), + ) + + LOGGER.info("Bot Started Successfully!\n") + + async def stop(self): + """Stop the bot and send a message to MESSAGE_DUMP telling that the bot has stopped.""" + runtime = strftime("%Hh %Mm %Ss", gmtime(time() - UPTIME)) + LOGGER.info("Uploading logs before stopping...!\n") + # Send Logs to MESSAGE_DUMP and LOG_CHANNEL + await self.send_document( + MESSAGE_DUMP, + document=LOGFILE, + caption=( + "Bot Stopped!\n\n" f"Uptime: {runtime}\n" f"{LOG_DATETIME}" + ), + ) + if MESSAGE_DUMP: + # LOG_CHANNEL is not necessary + await self.send_document( + MESSAGE_DUMP, + document=LOGFILE, + caption=f"Uptime: {runtime}", + ) + await super().stop() + MongoDB.close() + LOGGER.info( + f"""Bot Stopped. + Logs have been uploaded to the MESSAGE_DUMP Group! + Runtime: {runtime}s\n + """, + ) diff --git a/Powers/core/decorators/errors.py b/Powers/core/decorators/errors.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Powers/core/filters.py b/Powers/core/filters.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Powers/core/types/__init__.py b/Powers/core/types/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Powers/database/__init__.py b/Powers/database/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..eed4ce9c8038400668e44db39cce7a4442c582c6 --- /dev/null +++ b/Powers/database/__init__.py @@ -0,0 +1,75 @@ +from sys import exit as exiter + +from pymongo import MongoClient +from pymongo.errors import PyMongoError + +from Powers import DB_NAME, DB_URI, LOGGER + +try: + Powers_db_client = MongoClient(DB_URI) +except PyMongoError as f: + LOGGER.error(f"Error in Mongodb: {f}") + exiter(1) +Powers_main_db = Powers_db_client[DB_NAME] + + +class MongoDB: + """Class for interacting with Bot database.""" + + def __init__(self, collection) -> None: + self.collection = Powers_main_db[collection] + + # Insert one entry into collection + def insert_one(self, document): + result = self.collection.insert_one(document) + return repr(result.inserted_id) + + # Find one entry from collection + def find_one(self, query): + result = self.collection.find_one(query) + if result: + return result + return False + + # Find entries from collection + def find_all(self, query=None): + if query is None: + query = {} + return list(self.collection.find(query)) + + # Count entries from collection + def count(self, query=None): + if query is None: + query = {} + return self.collection.count_documents(query) + + # Delete entry/entries from collection + def delete_one(self, query): + self.collection.delete_many(query) + return self.collection.count_documents({}) + + # Replace one entry in collection + def replace(self, query, new_data): + old = self.collection.find_one(query) + _id = old["_id"] + self.collection.replace_one({"_id": _id}, new_data) + new = self.collection.find_one({"_id": _id}) + return old, new + + # Update one entry from collection + def update(self, query, update): + result = self.collection.update_one(query, {"$set": update}) + new_document = self.collection.find_one(query) + return result.modified_count, new_document + + @staticmethod + def close(): + return Powers_db_client.close() + + +def __connect_first(): + _ = MongoDB("test") + LOGGER.info("Initialized Database!\n") + + +__connect_first() diff --git a/Powers/database/antispam_db.py b/Powers/database/antispam_db.py new file mode 100644 index 0000000000000000000000000000000000000000..3acfd26dc9c1c042195f6b5712d94aac64be79e3 --- /dev/null +++ b/Powers/database/antispam_db.py @@ -0,0 +1,73 @@ +from datetime import datetime +from threading import RLock + +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() +ANTISPAM_BANNED = set() + + +class GBan(MongoDB): + """Class for managing Gbans in bot.""" + + db_name = "gbans" + + def __init__(self) -> None: + super().__init__(self.db_name) + + def check_gban(self, user_id: int): + with INSERTION_LOCK: + return bool(self.find_one({"_id": user_id})) + + def add_gban(self, user_id: int, reason: str, by_user: int): + global ANTISPAM_BANNED + with INSERTION_LOCK: + # Check if user is already gbanned or not + if self.find_one({"_id": user_id}): + return self.update_gban_reason(user_id, reason) + + # If not already gbanned, then add to gban + time_rn = datetime.now() + return self.insert_one( + { + "_id": user_id, + "reason": reason, + "by": by_user, + "time": time_rn, + }, + ) + + def remove_gban(self, user_id: int): + global ANTISPAM_BANNED + with INSERTION_LOCK: + # Check if user is already gbanned or not + if self.find_one({"_id": user_id}): + return self.delete_one({"_id": user_id}) + + return "User not gbanned!" + + def get_gban(self, user_id: int): + if self.check_gban(user_id): + curr = self.find_one({"_id": user_id}) + if curr: + return True, curr["reason"] + return False, "" + + def update_gban_reason(self, user_id: int, reason: str): + with INSERTION_LOCK: + return self.update( + {"_id": user_id}, + {"reason": reason}, + ) + + def count_gbans(self): + with INSERTION_LOCK: + return self.count() + + def load_from_db(self): + with INSERTION_LOCK: + return self.find_all() + + def list_gbans(self): + with INSERTION_LOCK: + return self.find_all() diff --git a/Powers/database/approve_db.py b/Powers/database/approve_db.py new file mode 100644 index 0000000000000000000000000000000000000000..dbcbf760b4d6eebe2a4969d251db09d14dedd18b --- /dev/null +++ b/Powers/database/approve_db.py @@ -0,0 +1,106 @@ +from threading import RLock + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Approve(MongoDB): + """Class for managing Approves in Chats in Bot.""" + + # Database name to connect to to preform operations + db_name = "approve" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def check_approve(self, user_id: int): + with INSERTION_LOCK: + return bool(user_id in self.chat_info["users"]) + + def add_approve(self, user_id: int, user_name: str): + with INSERTION_LOCK: + self.chat_info["users"].append((user_id, user_name)) + if not self.check_approve(user_id): + return self.update( + {"_id": self.chat_id}, + {"users": self.chat_info["users"]}, + ) + return True + + def remove_approve(self, user_id: int): + with INSERTION_LOCK: + if self.check_approve(user_id): + user_full = next( + user for user in self.chat_info["users"] if user[0] == user_id + ) + self.chat_info["users"].pop(user_full) + return self.update( + {"_id": self.chat_id}, + {"users": self.chat_info["users"]}, + ) + return True + + def unapprove_all(self): + with INSERTION_LOCK: + return self.delete_one( + {"_id": self.chat_id}, + ) + + def list_approved(self): + with INSERTION_LOCK: + return self.chat_info["users"] + + def count_approved(self): + with INSERTION_LOCK: + return len(self.chat_info["users"]) + + def load_from_db(self): + return self.find_all() + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = {"_id": self.chat_id, "users": []} + self.insert_one(new_data) + LOGGER.info(f"Initialized Approve Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def count_all_approved(): + with INSERTION_LOCK: + collection = MongoDB(Approve.db_name) + all_data = collection.find_all() + return sum(len(i["users"]) for i in all_data if len(i["users"]) >= 1) + + @staticmethod + def count_approved_chats(): + with INSERTION_LOCK: + collection = MongoDB(Approve.db_name) + all_data = collection.find_all() + return sum(len(i["users"]) >= 1 for i in all_data) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"users": []} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Approve Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) diff --git a/Powers/database/blacklist_db.py b/Powers/database/blacklist_db.py new file mode 100644 index 0000000000000000000000000000000000000000..895f3ca0f2afd6c9338700635dfba5ce990a4a6c --- /dev/null +++ b/Powers/database/blacklist_db.py @@ -0,0 +1,148 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Blacklist(MongoDB): + """Class to manage database for blacklists for chats.""" + + # Database name to connect to to preform operations + db_name = "blacklists" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def check_word_blacklist_status(self, word: str): + with INSERTION_LOCK: + bl_words = self.chat_info["triggers"] + return bool(word in bl_words) + + def add_blacklist(self, trigger: str): + with INSERTION_LOCK: + if not self.check_word_blacklist_status(trigger): + return self.update( + {"_id": self.chat_id}, + { + "_id": self.chat_id, + "triggers": self.chat_info["triggers"] + [trigger], + }, + ) + + def remove_blacklist(self, trigger: str): + with INSERTION_LOCK: + if self.check_word_blacklist_status(trigger): + self.chat_info["triggers"].remove(trigger) + return self.update( + {"_id": self.chat_id}, + { + "_id": self.chat_id, + "triggers": self.chat_info["triggers"], + }, + ) + + def get_blacklists(self): + with INSERTION_LOCK: + return self.chat_info["triggers"] + + @staticmethod + def count_blacklists_all(): + with INSERTION_LOCK: + collection = MongoDB(Blacklist.db_name) + curr = collection.find_all() + return sum(len(chat["triggers"]) for chat in curr) + + @staticmethod + def count_blackists_chats(): + with INSERTION_LOCK: + collection = MongoDB(Blacklist.db_name) + curr = collection.find_all() + return sum(1 for chat in curr if chat["triggers"]) + + def set_action(self, action: str): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"_id": self.chat_id, "action": action}, + ) + + def get_action(self): + with INSERTION_LOCK: + return self.chat_info["action"] + + def set_reason(self, reason: str): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"_id": self.chat_id, "reason": reason}, + ) + + def get_reason(self): + with INSERTION_LOCK: + return self.chat_info["reason"] + + @staticmethod + def count_action_bl_all(action: str): + with INSERTION_LOCK: + collection = MongoDB(Blacklist.db_name) + all_data = collection.find_all({"action": action}) + return sum(len(i["triggers"]) >= 1 for i in all_data) + + def rm_all_blacklist(self): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"triggers": []}, + ) + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = new_data = { + "_id": self.chat_id, + "triggers": [], + "action": "none", + "reason": "Automated blacklisted word: {{}}", + } + self.insert_one(new_data) + LOGGER.info(f"Initialized Blacklist Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = { + "triggers": [], + "action": "none", + "reason": "Automated blacklisted word: {{}}", + } + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Blacklist Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_blacklists(): + start = time() + LOGGER.info("Starting Blacklists Database Repair...") + collection = MongoDB(Blacklist.db_name) + Blacklist.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/database/chats_db.py b/Powers/database/chats_db.py new file mode 100644 index 0000000000000000000000000000000000000000..b4029a4141fc2a62ca1cac9777e27c5159664ae1 --- /dev/null +++ b/Powers/database/chats_db.py @@ -0,0 +1,141 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Chats(MongoDB): + """Class to manage users for bot.""" + + # Database name to connect to to preform operations + db_name = "chats" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def user_is_in_chat(self, user_id: int): + return bool(user_id in set(self.chat_info["users"])) + + def update_chat(self, chat_name: str, user_id: int): + with INSERTION_LOCK: + + if chat_name == self.chat_info["chat_name"] and self.user_is_in_chat( + user_id, + ): + return True + + if chat_name != self.chat_info["chat_name"] and self.user_is_in_chat( + user_id, + ): + return self.update( + {"_id": self.chat_id}, + {"chat_name": chat_name}, + ) + + if chat_name == self.chat_info["chat_name"] and not self.user_is_in_chat( + user_id, + ): + self.chat_info["users"].append(user_id) + return self.update( + {"_id": self.chat_id}, + {"users": self.chat_info["users"]}, + ) + + users_old = self.chat_info["users"] + users_old.append(user_id) + users = list(set(users_old)) + return self.update( + {"_id": self.chat_id}, + { + "_id": self.chat_id, + "chat_name": chat_name, + "users": users, + }, + ) + + def count_chat_users(self): + with INSERTION_LOCK: + return len(self.chat_info["users"]) or 0 + + def chat_members(self): + with INSERTION_LOCK: + return self.chat_info["users"] + + @staticmethod + def remove_chat(chat_id: int): + with INSERTION_LOCK: + collection = MongoDB(Chats.db_name) + collection.delete_one({"_id": chat_id}) + + @staticmethod + def count_chats(): + with INSERTION_LOCK: + collection = MongoDB(Chats.db_name) + return collection.count() or 0 + + @staticmethod + def list_chats_by_id(): + with INSERTION_LOCK: + collection = MongoDB(Chats.db_name) + chats = collection.find_all() + chat_list = {i["_id"] for i in chats} + return list(chat_list) + + @staticmethod + def list_chats_full(): + with INSERTION_LOCK: + collection = MongoDB(Chats.db_name) + return collection.find_all() + + @staticmethod + def get_chat_info(chat_id: int): + with INSERTION_LOCK: + collection = MongoDB(Chats.db_name) + return collection.find_one({"_id": chat_id}) + + def load_from_db(self): + with INSERTION_LOCK: + return self.find_all() + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = {"_id": self.chat_id, "chat_name": "", "users": []} + self.insert_one(new_data) + LOGGER.info(f"Initialized Chats Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"chat_name": "", "users": []} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Chats Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_chats(): + start = time() + LOGGER.info("Starting Chats Database Repair...") + collection = MongoDB(Chats.db_name) + Chats.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/database/disable_db.py b/Powers/database/disable_db.py new file mode 100644 index 0000000000000000000000000000000000000000..142f4f9241d37c29ba22f167f5759e1932b3f788 --- /dev/null +++ b/Powers/database/disable_db.py @@ -0,0 +1,147 @@ +from threading import RLock + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() +DISABLED_CMDS = {} + + +class Disabling(MongoDB): + """Class to manage database for Disabling for chats.""" + + # Database name to connect to to preform operations + db_name = "disabled" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def check_cmd_status(self, cmd: str): + with INSERTION_LOCK: + # cmds = self.chat_info["commands"] + cmds = DISABLED_CMDS[self.chat_id]["commands"] + # return bool(cmd in cmds) + return bool(cmd in cmds) + + def add_disable(self, cmd: str): + with INSERTION_LOCK: + if not self.check_cmd_status(cmd): + # DISABLED_CMDS[self.chat_id]["commands"].append(cmd) + return self.update( + {"_id": self.chat_id}, + { + "_id": self.chat_id, + "commands": self.chat_info["commands"] + [cmd], + }, + ) + + def remove_disabled(self, comm: str): + with INSERTION_LOCK: + if self.check_cmd_status(comm): + self.chat_info["commands"].remove(comm) + DISABLED_CMDS[self.chat_id]["commands"].remove(comm) + return self.update( + {"_id": self.chat_id}, + { + "_id": self.chat_id, + "commands": self.chat_info["commands"], + }, + ) + + def get_disabled(self): + with INSERTION_LOCK: + global DISABLED_CMDS + try: + cmds = DISABLED_CMDS[self.chat_id]["commands"] + except KeyError: + cmds = self.chat_info["commands"] + DISABLED_CMDS[self.chat_id]["commands"] = cmds + return cmds + + @staticmethod + def count_disabled_all(): + with INSERTION_LOCK: + collection = MongoDB(Disabling.db_name) + curr = collection.find_all() + return sum(len(chat["commands"]) for chat in curr) + + @staticmethod + def count_disabling_chats(): + with INSERTION_LOCK: + collection = MongoDB(Disabling.db_name) + curr = collection.find_all() + return sum(1 for chat in curr if chat["commands"]) + + def set_action(self, action: str): + with INSERTION_LOCK: + global DISABLED_CMDS + DISABLED_CMDS[self.chat_id]["action"] = action + return self.update( + {"_id": self.chat_id}, + {"_id": self.chat_id, "action": action}, + ) + + def get_action(self): + with INSERTION_LOCK: + global DISABLED_CMDS + try: + action = DISABLED_CMDS[self.chat_id]["action"] + except KeyError: + action = self.chat_info["action"] + DISABLED_CMDS[self.chat_id]["action"] = action + return action + + @staticmethod + def count_action_dis_all(action: str): + with INSERTION_LOCK: + collection = MongoDB(Disabling.db_name) + all_data = collection.find_all({"action": action}) + return sum(len(i["commands"]) >= 1 for i in all_data) + + def rm_all_disabled(self): + with INSERTION_LOCK: + DISABLED_CMDS[self.chat_id]["commands"] = [] + return self.update( + {"_id": self.chat_id}, + {"commands": []}, + ) + + def __ensure_in_db(self): + try: + chat_data = DISABLED_CMDS[self.chat_id] + except KeyError: + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = new_data = { + "_id": self.chat_id, + "commands": [], + "action": "none", + } + self.insert_one(new_data) + LOGGER.info(f"Initialized Disabling Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + global DISABLED_CMDS # global only when we are modifying the value + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + DISABLED_CMDS[new_chat_id] = DISABLED_CMDS[self.chat_id] + del DISABLED_CMDS[self.chat_id] + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + +def __load_disable_cache(): + global DISABLED_CMDS + collection = MongoDB(Disabling.db_name) + all_data = collection.find_all() + DISABLED_CMDS = { + i["_id"]: {"action": i["action"], "commands": i["commands"]} for i in all_data + } + + +__load_disable_cache() diff --git a/Powers/database/filters_db.py b/Powers/database/filters_db.py new file mode 100644 index 0000000000000000000000000000000000000000..d3fda53d9daeb4ae9f8a07953fbb9f97879dc742 --- /dev/null +++ b/Powers/database/filters_db.py @@ -0,0 +1,103 @@ +from threading import RLock + +from Powers.database import MongoDB +from Powers.utils.msg_types import Types + +INSERTION_LOCK = RLock() + + +class Filters(MongoDB): + db_name = "chat_filters" + + def __init__(self) -> None: + super().__init__(self.db_name) + + def save_filter( + self, + chat_id: int, + keyword: str, + filter_reply: str, + msgtype: int = Types.TEXT, + fileid="", + ): + with INSERTION_LOCK: + # Database update + curr = self.find_one({"chat_id": chat_id, "keyword": keyword}) + if curr: + return False + return self.insert_one( + { + "chat_id": chat_id, + "keyword": keyword, + "filter_reply": filter_reply, + "msgtype": msgtype, + "fileid": fileid, + }, + ) + + def get_filter(self, chat_id: int, keyword: str): + with INSERTION_LOCK: + curr = self.find_one({"chat_id": chat_id, "keyword": keyword}) + if curr: + return curr + return "Filter does not exist!" + + def get_all_filters(self, chat_id: int): + with INSERTION_LOCK: + curr = self.find_all({"chat_id": chat_id}) + if curr: + filter_list = {i["keyword"] for i in curr} + return list(filter_list) + return [] + + def rm_filter(self, chat_id: int, keyword: str): + with INSERTION_LOCK: + curr = self.find_one({"chat_id": chat_id, "keyword": keyword}) + if curr: + self.delete_one(curr) + return True + return False + + def rm_all_filters(self, chat_id: int): + with INSERTION_LOCK: + return self.delete_one({"chat_id": chat_id}) + + def count_filters_all(self): + with INSERTION_LOCK: + return self.count() + + def count_filter_aliases(self): + with INSERTION_LOCK: + curr = self.find_all() + if curr: + return len( + [z for z in (i["keyword"].split("|") for i in curr) if len(z) >= 2], + ) + return 0 + + def count_filters_chats(self): + with INSERTION_LOCK: + filters = self.find_all() + chats_ids = {i["chat_id"] for i in filters} + return len(chats_ids) + + def count_all_filters(self): + with INSERTION_LOCK: + return self.count() + + def count_filter_type(self, ntype): + with INSERTION_LOCK: + return self.count({"msgtype": ntype}) + + def load_from_db(self): + with INSERTION_LOCK: + return self.find_all() + + # Migrate if chat id changes! + def migrate_chat(self, old_chat_id: int, new_chat_id: int): + with INSERTION_LOCK: + old_chat_db = self.find_one({"_id": old_chat_id}) + if old_chat_db: + new_data = old_chat_db.update({"_id": new_chat_id}) + self.delete_one({"_id": old_chat_id}) + self.insert_one(new_data) diff --git a/Powers/database/greetings_db.py b/Powers/database/greetings_db.py new file mode 100644 index 0000000000000000000000000000000000000000..3419bed4d1c9e676ddb101dff465489ae1bede18 --- /dev/null +++ b/Powers/database/greetings_db.py @@ -0,0 +1,146 @@ +from threading import RLock + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Greetings(MongoDB): + """Class for managing antichannelpins in chats.""" + + # Database name to connect to to preform operations + db_name = "welcome_chats" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + # Get settings from database + def get_welcome_status(self): + with INSERTION_LOCK: + return self.chat_info["welcome"] + + def get_goodbye_status(self): + with INSERTION_LOCK: + return self.chat_info["goodbye"] + + def get_current_cleanservice_settings(self): + with INSERTION_LOCK: + return self.chat_info["cleanservice"] + + def get_current_cleanwelcome_settings(self): + with INSERTION_LOCK: + return self.chat_info["cleanwelcome"] + + def get_current_cleangoodbye_settings(self): + with INSERTION_LOCK: + return self.chat_info["cleangoodbye"] + + def get_welcome_text(self): + with INSERTION_LOCK: + return self.chat_info["welcome_text"] + + def get_goodbye_text(self): + with INSERTION_LOCK: + return self.chat_info["goodbye_text"] + + def get_current_cleanwelcome_id(self): + with INSERTION_LOCK: + return self.chat_info["cleanwelcome_id"] + + def get_current_cleangoodbye_id(self): + with INSERTION_LOCK: + return self.chat_info["cleangoodbye_id"] + + # Set settings in database + def set_current_welcome_settings(self, status: bool): + with INSERTION_LOCK: + return self.update({"_id": self.chat_id}, {"welcome": status}) + + def set_current_goodbye_settings(self, status: bool): + with INSERTION_LOCK: + return self.update({"_id": self.chat_id}, {"goodbye": status}) + + def set_welcome_text(self, welcome_text: str): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"welcome_text": welcome_text}, + ) + + def set_goodbye_text(self, goodbye_text: str): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"goodbye_text": goodbye_text}, + ) + + def set_current_cleanservice_settings(self, status: bool): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"cleanservice": status}, + ) + + def set_current_cleanwelcome_settings(self, status: bool): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"cleanwelcome": status}, + ) + + def set_current_cleangoodbye_settings(self, status: bool): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"cleangoodbye": status}, + ) + + def set_cleanwlcm_id(self, status: int): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"cleanwelcome_id": status}, + ) + + def set_cleangoodbye_id(self, status: int): + with INSERTION_LOCK: + return self.update( + {"_id": self.chat_id}, + {"cleangoodbye_id": status}, + ) + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = { + "_id": self.chat_id, + "cleanwelcome": False, + "cleanwelcome_id": None, + "cleangoodbye_id": None, + "cleangoodbye": False, + "cleanservice": False, + "goodbye_text": "Sad to see you leaving {first}.\nTake Care!", + "welcome_text": "Hey {first}, welcome to {chatname}!", + "welcome": True, + "goodbye": True, + } + self.insert_one(new_data) + LOGGER.info(f"Initialized Greetings Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def count_chats(query: str): + with INSERTION_LOCK: + collection = MongoDB(Greetings.db_name) + return collection.count({query: True}) diff --git a/Powers/database/group_blacklist.py b/Powers/database/group_blacklist.py new file mode 100644 index 0000000000000000000000000000000000000000..5ab7eaa01194048ac7803eaa2af4459e074c5d9c --- /dev/null +++ b/Powers/database/group_blacklist.py @@ -0,0 +1,46 @@ +from threading import RLock + +from Powers.database import MongoDB +from Powers.database.chats_db import Chats + +INSERTION_LOCK = RLock() +BLACKLIST_CHATS = [] + + +class GroupBlacklist(MongoDB): + """Class to blacklist chats where bot will exit.""" + + db_name = "group_blacklists" + + def __init__(self) -> None: + super().__init__(self.db_name) + + def add_chat(self, chat_id: int): + with INSERTION_LOCK: + global BLACKLIST_CHATS + try: + Chats.remove_chat(chat_id) # Delete chat from database + except KeyError: + pass + BLACKLIST_CHATS.append(chat_id) + BLACKLIST_CHATS.sort() + return self.insert_one({"_id": chat_id, "blacklist": True}) + + def remove_chat(self, chat_id: int): + with INSERTION_LOCK: + global BLACKLIST_CHATS + BLACKLIST_CHATS.remove(chat_id) + BLACKLIST_CHATS.sort() + return self.delete_one({"_id": chat_id}) + + def list_all_chats(self): + with INSERTION_LOCK: + try: + BLACKLIST_CHATS.sort() + return BLACKLIST_CHATS + except Exception: + all_chats = self.find_all() + return [chat["_id"] for chat in all_chats] + + def get_from_db(self): + return self.find_all() diff --git a/Powers/database/lang_db.py b/Powers/database/lang_db.py new file mode 100644 index 0000000000000000000000000000000000000000..a931e3e7bf2aae5a824042e6c915b589279819a0 --- /dev/null +++ b/Powers/database/lang_db.py @@ -0,0 +1,93 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + +# Locall cache languages for users!! +LANG_CACHE = {} + + +class Langs(MongoDB): + """Class for language options in bot.""" + + db_name = "langs" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def get_chat_type(self): + return "supergroup" if str(self.chat_id).startswith("-100") else "user" + + def set_lang(self, lang: str): + with INSERTION_LOCK: + global LANG_CACHE + LANG_CACHE[self.chat_id] = lang + self.chat_info["lang"] = lang + return self.update( + {"_id": self.chat_id}, + {"lang": self.chat_info["lang"]}, + ) + + def get_lang(self): + with INSERTION_LOCK: + return self.chat_info["lang"] + + @staticmethod + def load_from_db(): + with INSERTION_LOCK: + collection = MongoDB(Langs.db_name) + return collection.find_all() + + def __ensure_in_db(self): + try: + chat_data = {"_id": self.chat_id, "lang": LANG_CACHE[self.chat_id]} + except KeyError: + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + chat_type = self.get_chat_type() + new_data = {"_id": self.chat_id, "lang": "en", "chat_type": chat_type} + self.insert_one(new_data) + LOGGER.info(f"Initialized Language Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"lang": "en", "chat_type": ""} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Langs Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_all_langs(): + start = time() + LOGGER.info("Starting Langs Database Repair...") + collection = MongoDB(Langs.db_name) + Langs.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") + + +def __load_lang_cache(): + global LANG_CACHE + collection = MongoDB(Langs.db_name) + all_data = collection.find_all() + LANG_CACHE = {i["_id"]: i["lang"] for i in all_data} diff --git a/Powers/database/notes_db.py b/Powers/database/notes_db.py new file mode 100644 index 0000000000000000000000000000000000000000..a614614b7a0a27dc52445838388b057cc79da0b8 --- /dev/null +++ b/Powers/database/notes_db.py @@ -0,0 +1,141 @@ +from hashlib import md5 +from threading import RLock +from time import time + +from Powers.database import MongoDB +from Powers.utils.msg_types import Types + +INSERTION_LOCK = RLock() + + +class Notes(MongoDB): + db_name = "notes" + + def __init__(self) -> None: + super().__init__(self.db_name) + + def save_note( + self, + chat_id: int, + note_name: str, + note_value: str, + msgtype: int = Types.TEXT, + fileid="", + ): + with INSERTION_LOCK: + curr = self.find_one( + {"chat_id": chat_id, "note_name": note_name}, + ) + if curr: + return False + hash_gen = md5( + (note_name + note_value + str(chat_id) + str(int(time()))).encode(), + ).hexdigest() + return self.insert_one( + { + "chat_id": chat_id, + "note_name": note_name, + "note_value": note_value, + "hash": hash_gen, + "msgtype": msgtype, + "fileid": fileid, + }, + ) + + def get_note(self, chat_id: int, note_name: str): + with INSERTION_LOCK: + curr = self.find_one( + {"chat_id": chat_id, "note_name": note_name}, + ) + if curr: + return curr + return "Note does not exist!" + + def get_note_by_hash(self, note_hash: str): + return self.find_one({"hash": note_hash}) + + def get_all_notes(self, chat_id: int): + with INSERTION_LOCK: + curr = self.find_all({"chat_id": chat_id}) + note_list = [(note["note_name"], note["hash"]) for note in curr] + note_list.sort() + return note_list + + def rm_note(self, chat_id: int, note_name: str): + with INSERTION_LOCK: + curr = self.find_one( + {"chat_id": chat_id, "note_name": note_name}, + ) + if curr: + self.delete_one(curr) + return True + return False + + def rm_all_notes(self, chat_id: int): + with INSERTION_LOCK: + return self.delete_one({"chat_id": chat_id}) + + def count_notes(self, chat_id: int): + with INSERTION_LOCK: + curr = self.find_all({"chat_id": chat_id}) + if curr: + return len(curr) + return 0 + + def count_notes_chats(self): + with INSERTION_LOCK: + notes = self.find_all() + chats_ids = [chat["chat_id"] for chat in notes] + return len(set(chats_ids)) + + def count_all_notes(self): + with INSERTION_LOCK: + return self.count() + + def count_notes_type(self, ntype): + with INSERTION_LOCK: + return self.count({"msgtype": ntype}) + + # Migrate if chat id changes! + def migrate_chat(self, old_chat_id: int, new_chat_id: int): + with INSERTION_LOCK: + old_chat_db = self.find_one({"_id": old_chat_id}) + if old_chat_db: + new_data = old_chat_db.update({"_id": new_chat_id}) + self.delete_one({"_id": old_chat_id}) + self.insert_one(new_data) + + +class NotesSettings(MongoDB): + db_name = "notes_settings" + + def __init__(self) -> None: + super().__init__(self.db_name) + + def set_privatenotes(self, chat_id: int, status: bool = False): + curr = self.find_one({"_id": chat_id}) + if curr: + return self.update({"_id": chat_id}, {"privatenotes": status}) + return self.insert_one({"_id": chat_id, "privatenotes": status}) + + def get_privatenotes(self, chat_id: int): + curr = self.find_one({"_id": chat_id}) + if curr: + return curr["privatenotes"] + self.update({"_id": chat_id}, {"privatenotes": False}) + return False + + def list_chats(self): + return self.find_all({"privatenotes": True}) + + def count_chats(self): + return len(self.find_all({"privatenotes": True})) + + # Migrate if chat id changes! + def migrate_chat(self, old_chat_id: int, new_chat_id: int): + with INSERTION_LOCK: + old_chat_db = self.find_one({"_id": old_chat_id}) + if old_chat_db: + new_data = old_chat_db.update({"_id": new_chat_id}) + self.delete_one({"_id": old_chat_id}) + self.insert_one(new_data) diff --git a/Powers/database/pins_db.py b/Powers/database/pins_db.py new file mode 100644 index 0000000000000000000000000000000000000000..ade8999eb8abf3c27f8571f4d7ac87895daa15a3 --- /dev/null +++ b/Powers/database/pins_db.py @@ -0,0 +1,113 @@ +from threading import RLock + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Pins(MongoDB): + """Class for managing antichannelpins in chats.""" + + # Database name to connect to to preform operations + db_name = "antichannelpin" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def get_settings(self): + with INSERTION_LOCK: + return self.chat_info + + def antichannelpin_on(self): + with INSERTION_LOCK: + return self.set_on("antichannelpin") + + def cleanlinked_on(self): + with INSERTION_LOCK: + return self.set_on("cleanlinked") + + def antichannelpin_off(self): + with INSERTION_LOCK: + return self.set_off("antichannelpin") + + def cleanlinked_off(self): + with INSERTION_LOCK: + return self.set_off("cleanlinked") + + def set_on(self, atype: str): + with INSERTION_LOCK: + otype = "cleanlinked" if atype == "antichannelpin" else "antichannelpin" + return self.update( + {"_id": self.chat_id}, + {atype: True, otype: False}, + ) + + def set_off(self, atype: str): + with INSERTION_LOCK: + otype = "cleanlinked" if atype == "antichannelpin" else "antichannelpin" + return self.update( + {"_id": self.chat_id}, + {atype: False, otype: False}, + ) + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = { + "_id": self.chat_id, + "antichannelpin": False, + "cleanlinked": False, + } + self.insert_one(new_data) + LOGGER.info(f"Initialized Pins Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + # ----- Static Methods ----- + @staticmethod + def count_chats(atype: str): + with INSERTION_LOCK: + collection = MongoDB(Pins.db_name) + return collection.count({atype: True}) + + @staticmethod + def list_chats(query: str): + with INSERTION_LOCK: + collection = MongoDB(Pins.db_name) + return collection.find_all({query: True}) + + @staticmethod + def load_from_db(): + with INSERTION_LOCK: + collection = MongoDB(Pins.db_name) + return collection.find_all() + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"antichannelpin": False, "cleanlinked": False} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Pins Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_pins_chats(): + LOGGER.info("Starting Pins Database Repair...") + collection = MongoDB(Pins.db_name) + Pins.repair_db(collection) diff --git a/Powers/database/reporting_db.py b/Powers/database/reporting_db.py new file mode 100644 index 0000000000000000000000000000000000000000..07a4e433a82fb506eb240c5c3cbda890b04ead65 --- /dev/null +++ b/Powers/database/reporting_db.py @@ -0,0 +1,78 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Reporting(MongoDB): + """Class for managing report settings of users and groups.""" + + db_name = "reporting" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def get_chat_type(self): + return "supergroup" if str(self.chat_id).startswith("-100") else "user" + + def set_settings(self, status: bool = True): + with INSERTION_LOCK: + self.chat_info["status"] = status + return self.update( + {"_id": self.chat_id}, + {"status": self.chat_info["status"]}, + ) + + def get_settings(self): + with INSERTION_LOCK: + return self.chat_info["status"] + + @staticmethod + def load_from_db(): + with INSERTION_LOCK: + collection = MongoDB(Reporting.db_name) + return collection.find_all() or [] + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + chat_type = self.get_chat_type() + new_data = {"_id": self.chat_id, "status": True, "chat_type": chat_type} + self.insert_one(new_data) + LOGGER.info(f"Initialized Language Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"status": True, "chat_type": ""} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Reporting Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_all_reporting_settings(): + start = time() + LOGGER.info("Starting Reports Database Repair...") + collection = MongoDB(Reporting.db_name) + Reporting.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/database/rules_db.py b/Powers/database/rules_db.py new file mode 100644 index 0000000000000000000000000000000000000000..3e82a247cf597c0f7b52c925e1f1fbc5096faa89 --- /dev/null +++ b/Powers/database/rules_db.py @@ -0,0 +1,102 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Rules(MongoDB): + """Class for rules for chats in bot.""" + + db_name = "rules" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def get_rules(self): + with INSERTION_LOCK: + return self.chat_info["rules"] + + def set_rules(self, rules: str): + with INSERTION_LOCK: + self.chat_info["rules"] = rules + self.update({"_id": self.chat_id}, {"rules": rules}) + + def get_privrules(self): + with INSERTION_LOCK: + return self.chat_info["privrules"] + + def set_privrules(self, privrules: bool): + with INSERTION_LOCK: + self.chat_info["privrules"] = privrules + self.update({"_id": self.chat_id}, {"privrules": privrules}) + + def clear_rules(self): + with INSERTION_LOCK: + return self.delete_one({"_id": self.chat_id}) + + @staticmethod + def count_chats_with_rules(): + with INSERTION_LOCK: + collection = MongoDB(Rules.db_name) + return collection.count({"rules": {"$regex": ".*"}}) + + @staticmethod + def count_privrules_chats(): + with INSERTION_LOCK: + collection = MongoDB(Rules.db_name) + return collection.count({"privrules": True}) + + @staticmethod + def count_grouprules_chats(): + with INSERTION_LOCK: + collection = MongoDB(Rules.db_name) + return collection.count({"privrules": False}) + + @staticmethod + def load_from_db(): + with INSERTION_LOCK: + collection = MongoDB(Rules.db_name) + return collection.find_all() + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = {"_id": self.chat_id, "privrules": False, "rules": ""} + self.insert_one(new_data) + LOGGER.info(f"Initialized Language Document for chat {self.chat_id}") + return new_data + return chat_data + + # Migrate if chat id changes! + def migrate_chat(self, new_chat_id: int): + old_chat_db = self.find_one({"_id": self.chat_id}) + new_data = old_chat_db.update({"_id": new_chat_id}) + self.insert_one(new_data) + self.delete_one({"_id": self.chat_id}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"privrules": False, "rules": ""} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Rules Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_all_rules(): + start = time() + LOGGER.info("Starting Rules Database Repair...") + collection = MongoDB(Rules.db_name) + Rules.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/database/users_db.py b/Powers/database/users_db.py new file mode 100644 index 0000000000000000000000000000000000000000..f5aacffc369e651ddb5b9ca273703fff208e5321 --- /dev/null +++ b/Powers/database/users_db.py @@ -0,0 +1,101 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Users(MongoDB): + """Class to manage users for bot.""" + + db_name = "users" + + def __init__(self, user_id: int) -> None: + super().__init__(self.db_name) + self.user_id = user_id + self.user_info = self.__ensure_in_db() + + def update_user(self, name: str, username: str = None): + with INSERTION_LOCK: + if name != self.user_info["name"] or username != self.user_info["username"]: + return self.update( + {"_id": self.user_id}, + {"username": username, "name": name}, + ) + return True + + def delete_user(self): + with INSERTION_LOCK: + return self.delete_one({"_id": self.user_id}) + + @staticmethod + def count_users(): + with INSERTION_LOCK: + collection = MongoDB(Users.db_name) + return collection.count() + + def get_my_info(self): + with INSERTION_LOCK: + return self.user_info + + @staticmethod + def list_users(): + with INSERTION_LOCK: + collection = MongoDB(Users.db_name) + return collection.find_all() + + @staticmethod + def get_user_info(user_id: int or str): + with INSERTION_LOCK: + collection = MongoDB(Users.db_name) + if isinstance(user_id, int): + curr = collection.find_one({"_id": user_id}) + elif isinstance(user_id, str): + # user_id[1:] because we don't want the '@' in the username search! + curr = collection.find_one({"username": user_id[1:]}) + else: + curr = None + + if curr: + return curr + + return {} + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.user_id}) + if not chat_data: + new_data = {"_id": self.user_id, "username": "", "name": "unknown_till_now"} + self.insert_one(new_data) + LOGGER.info(f"Initialized User Document for {self.user_id}") + return new_data + return chat_data + + @staticmethod + def load_from_db(): + with INSERTION_LOCK: + collection = MongoDB(Users.db_name) + return collection.find_all() + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"username": "", "name": "unknown_till_now"} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Users Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_users(): + start = time() + LOGGER.info("Starting Users Database Repair...") + collection = MongoDB(Users.db_name) + Users.repair_db(collection) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/database/warns_db.py b/Powers/database/warns_db.py new file mode 100644 index 0000000000000000000000000000000000000000..2c39cb9cb83b76ce797dacdb7379cbe6705ccf5f --- /dev/null +++ b/Powers/database/warns_db.py @@ -0,0 +1,177 @@ +from threading import RLock +from time import time + +from Powers import LOGGER +from Powers.database import MongoDB + +INSERTION_LOCK = RLock() + + +class Warns(MongoDB): + db_name = "chat_warns" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + + def warn_user(self, user_id: int, warn_reason=None): + with INSERTION_LOCK: + self.user_info = self.__ensure_in_db(user_id) + self.user_info["warns"].append(warn_reason) + self.user_info["num_warns"] = len(self.user_info["warns"]) + self.update( + {"chat_id": self.chat_id, "user_id": user_id}, + { + "warns": self.user_info["warns"], + "num_warns": self.user_info["num_warns"], + }, + ) + return self.user_info["warns"], self.user_info["num_warns"] + + def remove_warn(self, user_id: int): + with INSERTION_LOCK: + self.user_info = self.__ensure_in_db(user_id) + self.user_info["warns"].pop() + self.user_info["num_warns"] = len(self.user_info["warns"]) + self.update( + {"chat_id": self.chat_id, "user_id": user_id}, + { + "warns": self.user_info["warns"], + "num_warns": self.user_info["num_warns"], + }, + ) + return self.user_info["warns"], self.user_info["num_warns"] + + def reset_warns(self, user_id: int): + with INSERTION_LOCK: + self.user_info = self.__ensure_in_db(user_id) + return self.delete_one({"chat_id": self.chat_id, "user_id": user_id}) + + def get_warns(self, user_id: int): + with INSERTION_LOCK: + self.user_info = self.__ensure_in_db(user_id) + return self.user_info["warns"], len(self.user_info["warns"]) + + @staticmethod + def count_all_chats_using_warns(): + with INSERTION_LOCK: + collection = MongoDB(Warns.db_name) + curr = collection.find_all() + return len({i["chat_id"] for i in curr}) + + @staticmethod + def count_warned_users(): + with INSERTION_LOCK: + collection = MongoDB(Warns.db_name) + curr = collection.find_all() + return len({i["user_id"] for i in curr if i["num_warns"] >= 1}) + + @staticmethod + def count_warns_total(): + with INSERTION_LOCK: + collection = MongoDB(Warns.db_name) + curr = collection.find_all() + return sum(i["num_warns"] for i in curr if i["num_warns"] >= 1) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = { + "warns": [], + "num_warns": 0, + } + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Approve Database - setting '{key}:{val}' for {data['user_id']} in {data['chat_id']}", + ) + collection.update( + {"chat_id": data["chat_id"], "user_id": data["user_id"]}, + {key: val}, + ) + + def __ensure_in_db(self, user_id: int): + chat_data = self.find_one({"chat_id": self.chat_id, "user_id": user_id}) + if not chat_data: + new_data = { + "chat_id": self.chat_id, + "user_id": user_id, + "warns": [], + "num_warns": 0, + } + self.insert_one(new_data) + LOGGER.info(f"Initialized Warn Document for {user_id} in {self.chat_id}") + return new_data + return chat_data + + +class WarnSettings(MongoDB): + db_name = "chat_warn_settings" + + def __init__(self, chat_id: int) -> None: + super().__init__(self.db_name) + self.chat_id = chat_id + self.chat_info = self.__ensure_in_db() + + def __ensure_in_db(self): + chat_data = self.find_one({"_id": self.chat_id}) + if not chat_data: + new_data = {"_id": self.chat_id, "warn_mode": "none", "warn_limit": 3} + self.insert_one(new_data) + LOGGER.info(f"Initialized Warn Settings Document for {self.chat_id}") + return new_data + return chat_data + + def get_warnings_settings(self): + with INSERTION_LOCK: + return self.chat_info + + def set_warnmode(self, warn_mode: str = "none"): + with INSERTION_LOCK: + self.update({"_id": self.chat_id}, {"warn_mode": warn_mode}) + return warn_mode + + def get_warnmode(self): + with INSERTION_LOCK: + return self.chat_info["warn_mode"] + + def set_warnlimit(self, warn_limit: int = 3): + with INSERTION_LOCK: + self.update({"_id": self.chat_id}, {"warn_limit": warn_limit}) + return warn_limit + + def get_warnlimit(self): + with INSERTION_LOCK: + return self.chat_info["warn_limit"] + + @staticmethod + def count_action_chats(mode: str): + collection = MongoDB(WarnSettings.db_name) + return collection.count({"warn_mode": mode}) + + @staticmethod + def repair_db(collection): + all_data = collection.find_all() + keys = {"warn_mode": "none", "warn_limit": 3} + for data in all_data: + for key, val in keys.items(): + try: + _ = data[key] + except KeyError: + LOGGER.warning( + f"Repairing Approve Database - setting '{key}:{val}' for {data['_id']}", + ) + collection.update({"_id": data["_id"]}, {key: val}) + + +def __pre_req_warns(): + start = time() + LOGGER.info("Starting Warns Database Repair...") + collection_warns = MongoDB(Warns.db_name) + collection_warn_settings = MongoDB(WarnSettings.db_name) + Warns.repair_db(collection_warns) + WarnSettings.repair_db(collection_warn_settings) + LOGGER.info(f"Done in {round((time() - start), 3)}s!") diff --git a/Powers/modules/__init__.py b/Powers/modules/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Powers/plugins/__init__.py b/Powers/plugins/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..0d944791dd400f63a745dac4ec9f0d6170c060eb --- /dev/null +++ b/Powers/plugins/__init__.py @@ -0,0 +1,14 @@ +async def all_plugins(): + # This generates a list of plugins in this folder for the * in __main__ to + # work. + + from glob import glob + from os.path import basename, dirname, isfile + + mod_paths = glob(dirname(__file__) + "/*.py") + all_plugs = [ + basename(f)[:-3] + for f in mod_paths + if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py") + ] + return sorted(all_plugs) diff --git a/Powers/plugins/admin.py b/Powers/plugins/admin.py new file mode 100644 index 0000000000000000000000000000000000000000..7cc8e82eeb56884ad5268bd9f76b0b8861237c06 --- /dev/null +++ b/Powers/plugins/admin.py @@ -0,0 +1,769 @@ +from asyncio import sleep +from html import escape +from os import remove +from traceback import format_exc + +from pyrogram import filters +from pyrogram.errors import ( + ChatAdminInviteRequired, + ChatAdminRequired, + FloodWait, + RightForbidden, + RPCError, + UserAdminInvalid, +) +from pyrogram.types import Message + +from Powers import DEV_USERS, LOGGER, OWNER_ID, SUPPORT_GROUP, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.database.approve_db import Approve +from Powers.database.reporting_db import Reporting +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, TEMP_ADMIN_CACHE_BLOCK, admin_cache_reload +from Powers.utils.custom_filters import ( + DEV_LEVEL, + admin_filter, + command, + owner_filter, + promote_filter, +) +from Powers.utils.extract_user import extract_user +from Powers.utils.parser import mention_html +from Powers.vars import Config + + +@Gojo.on_message(command("adminlist")) +async def adminlist_show(_, m: Message): + global ADMIN_CACHE + if m.chat.type != "supergroup": + return await m.reply_text( + "This command is made to be used in groups only!", + ) + try: + try: + admin_list = ADMIN_CACHE[m.chat.id] + note = tlang(m, "admin.adminlist.note_cached") + except KeyError: + admin_list = await admin_cache_reload(m, "adminlist") + note = tlang(m, "admin.adminlist.note_updated") + + adminstr = (tlang(m, "admin.adminlist.adminstr")).format( + chat_title=m.chat.title, + ) + "\n\n" + + bot_admins = [i for i in admin_list if (i[1].lower()).endswith("bot")] + user_admins = [i for i in admin_list if not (i[1].lower()).endswith("bot")] + + # format is like: (user_id, username/name,anonyamous or not) + mention_users = [ + ( + admin[1] + if admin[1].startswith("@") + else (await mention_html(admin[1], admin[0])) + ) + for admin in user_admins + if not admin[2] # if non-anonyamous admin + ] + mention_users.sort(key=lambda x: x[1]) + + mention_bots = [ + ( + admin[1] + if admin[1].startswith("@") + else (await mention_html(admin[1], admin[0])) + ) + for admin in bot_admins + ] + mention_bots.sort(key=lambda x: x[1]) + + adminstr += "User Admins:\n" + adminstr += "\n".join(f"- {i}" for i in mention_users) + adminstr += "\n\nBots:\n" + adminstr += "\n".join(f"- {i}" for i in mention_bots) + + await m.reply_text(adminstr + "\n\n" + note) + LOGGER.info(f"Adminlist cmd use in {m.chat.id} by {m.from_user.id}") + + except Exception as ef: + if str(ef) == str(m.chat.id): + await m.reply_text(tlang(m, "admin.adminlist.use_admin_cache")) + else: + ef = str(ef) + f"{admin_list}\n" + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("zombies") & owner_filter) +async def zombie_clean(c: Gojo, m: Message): + + zombie = 0 + + wait = await m.reply_text("Searching ... and banning ...") + async for member in c.iter_chat_members(m.chat.id): + if member.user.is_deleted: + zombie += 1 + try: + await c.kick_chat_member(m.chat.id, member.user.id) + except UserAdminInvalid: + zombie -= 1 + except FloodWait as e: + await sleep(e.x) + if zombie == 0: + return await wait.edit_text("Group is clean!") + return await wait.edit_text( + f"{zombie} Zombies found and has been banned!", + ) + + +@Gojo.on_message(command("admincache")) +async def reload_admins(_, m: Message): + global TEMP_ADMIN_CACHE_BLOCK + + if m.chat.type != "supergroup": + return await m.reply_text( + "This command is made to be used in groups only!", + ) + + if ( + (m.chat.id in set(TEMP_ADMIN_CACHE_BLOCK.keys())) + and (m.from_user.id not in SUPPORT_STAFF) + and TEMP_ADMIN_CACHE_BLOCK[m.chat.id] == "manualblock" + ): + await m.reply_text("Can only reload admin cache once per 10 mins!") + return + + try: + await admin_cache_reload(m, "admincache") + TEMP_ADMIN_CACHE_BLOCK[m.chat.id] = "manualblock" + await m.reply_text(tlang(m, "admin.adminlist.reloaded_admins")) + LOGGER.info(f"Admincache cmd use in {m.chat.id} by {m.from_user.id}") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(filters.regex(r"^(?i)@admin(s)?") & filters.group) +async def tag_admins(_, m: Message): + db = Reporting(m.chat.id) + if not db.get_settings(): + return + + try: + admin_list = ADMIN_CACHE[m.chat.id] + except KeyError: + admin_list = await admin_cache_reload(m, "adminlist") + + user_admins = [i for i in admin_list if not (i[1].lower()).endswith("bot")] + mention_users = [(await mention_html("\u2063", admin[0])) for admin in user_admins] + mention_users.sort(key=lambda x: x[1]) + mention_str = "".join(mention_users) + await m.reply_text( + ( + f"{(await mention_html(m.from_user.first_name, m.from_user.id))}" + f" reported the message to admins!{mention_str}" + ), + ) + + +@Gojo.on_message(command("fullpromote") & promote_filter) +async def fullpromote_usr(c: Gojo, m: Message): + global ADMIN_CACHE + + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.promote.no_target")) + return + + try: + user_id, user_first_name, user_name = await extract_user(c, m) + except Exception: + return + + bot = await c.get_chat_member(m.chat.id, Config.BOT_ID) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, how can I even promote myself?") + return + + if not bot.can_promote_members: + return await m.reply_text( + "I don't have enough permissions!", + ) # This should be here + + user = await c.get_chat_member(m.chat.id, m.from_user.id) + if m.from_user.id not in [DEV_USERS, OWNER_ID] and user.status != "creator": + return await m.reply_text("This command can only be used by chat owner.") + # If user is alreay admin + try: + admin_list = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_list = { + i[0] for i in (await admin_cache_reload(m, "promote_cache_update")) + } + + if user_id in admin_list: + await m.reply_text( + "This user is already an admin, how am I supposed to re-promote them?", + ) + return + + try: + await m.chat.promote_member( + user_id=user_id, + can_change_info=bot.can_change_info, + can_invite_users=bot.can_invite_users, + can_delete_messages=bot.can_delete_messages, + can_restrict_members=bot.can_restrict_members, + can_pin_messages=bot.can_pin_messages, + can_promote_members=bot.can_promote_members, + can_manage_chat=bot.can_manage_chat, + can_manage_voice_chats=bot.can_manage_voice_chats, + ) + + title = "" + if len(m.text.split()) == 3 and not m.reply_to_message: + title = m.text.split()[2] + elif len(m.text.split()) == 2 and m.reply_to_message: + title = m.text.split()[1] + if title and len(title) > 16: + title = title[0:16] # trim title to 16 characters + + try: + await c.set_administrator_title(m.chat.id, user_id, title) + except RPCError as e: + LOGGER.error(e) + + LOGGER.info( + f"{m.from_user.id} fullpromoted {user_id} in {m.chat.id} with title '{title}'", + ) + + await m.reply_text( + (tlang(m, "admin.promote.promoted_user")).format( + promoter=(await mention_html(m.from_user.first_name, m.from_user.id)), + promoted=(await mention_html(user_first_name, user_id)), + chat_title=f"{escape(m.chat.title)} title set to {title}" + if title + else f"{escape(m.chat.title)} title set to Admin", + ), + ) + + # If user is approved, disapprove them as they willbe promoted and get even more rights + if Approve(m.chat.id).check_approve(user_id): + Approve(m.chat.id).remove_approve(user_id) + + # ----- Add admin to temp cache ----- + try: + inp1 = user_name or user_first_name + admins_group = ADMIN_CACHE[m.chat.id] + admins_group.append((user_id, inp1)) + ADMIN_CACHE[m.chat.id] = admins_group + except KeyError: + await admin_cache_reload(m, "promote_key_error") + + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.promote.bot_no_right")) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RPCError as e: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=e, + ), + ) + LOGGER.error(e) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("promote") & promote_filter) +async def promote_usr(c: Gojo, m: Message): + + global ADMIN_CACHE + + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.promote.no_target")) + return + + try: + user_id, user_first_name, user_name = await extract_user(c, m) + except Exception: + return + + bot = await c.get_chat_member(m.chat.id, Config.BOT_ID) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, how can I even promote myself?") + return + + if not bot.can_promote_members: + return await m.reply_text( + "I don't have enough permissions", + ) # This should be here + # If user is alreay admin + try: + admin_list = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_list = { + i[0] for i in (await admin_cache_reload(m, "promote_cache_update")) + } + + if user_id in admin_list: + await m.reply_text( + "This user is already an admin, how am I supposed to re-promote them?", + ) + return + + try: + await m.chat.promote_member( + user_id=user_id, + can_change_info=bot.can_change_info, + can_invite_users=bot.can_invite_users, + can_delete_messages=bot.can_delete_messages, + can_restrict_members=bot.can_restrict_members, + can_pin_messages=bot.can_pin_messages, + # can_promote_members=bot.can_promote_members, + can_manage_chat=bot.can_manage_chat, + can_manage_voice_chats=bot.can_manage_voice_chats, + ) + + title = "Itadori" # Deafult title + if len(m.text.split()) == 3 and not m.reply_to_message: + title = m.text.split()[2] + elif len(m.text.split()) == 2 and m.reply_to_message: + title = m.text.split()[1] + if title and len(title) > 16: + title = title[0:16] # trim title to 16 characters + + try: + await c.set_administrator_title(m.chat.id, user_id, title) + except RPCError as e: + LOGGER.error(e) + + LOGGER.info( + f"{m.from_user.id} promoted {user_id} in {m.chat.id} with title '{title}'", + ) + + await m.reply_text( + (tlang(m, "admin.promote.promoted_user")).format( + promoter=(await mention_html(m.from_user.first_name, m.from_user.id)), + promoted=(await mention_html(user_first_name, user_id)), + chat_title=f"{escape(m.chat.title)} title set to {title}" + if title + else f"{escape(m.chat.title)} title set to Admin", + ), + ) + + # If user is approved, disapprove them as they willbe promoted and get even more rights + if Approve(m.chat.id).check_approve(user_id): + Approve(m.chat.id).remove_approve(user_id) + + # ----- Add admin to temp cache ----- + try: + inp1 = user_name or user_first_name + admins_group = ADMIN_CACHE[m.chat.id] + admins_group.append((user_id, inp1)) + ADMIN_CACHE[m.chat.id] = admins_group + except KeyError: + await admin_cache_reload(m, "promote_key_error") + + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.promote.bot_no_right")) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RPCError as e: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=e, + ), + ) + LOGGER.error(e) + LOGGER.error(format_exc()) + return + + + +@Gojo.on_message(command("spromote") & promote_filter) +async def superpromote_usr(c: Gojo, m: Message): + + global ADMIN_CACHE + + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.promote.no_target")) + return + + try: + user_id, user_first_name, user_name = await extract_user(c, m) + except Exception: + return + + bot = await c.get_chat_member(m.chat.id, Config.BOT_ID) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, how can I even promote myself?") + return + + if not bot.can_promote_members: + return await m.reply_text( + "I don't have enough permissions", + ) # This should be here + # If user is alreay admin + try: + admin_list = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_list = { + i[0] for i in (await admin_cache_reload(m, "promote_cache_update")) + } + + if user_id in admin_list: + await m.reply_text( + "This user is already an admin, how am I supposed to re-promote them?", + ) + return + + try: + await m.chat.promote_member( + user_id=user_id, + can_change_info=bot.can_change_info, + can_invite_users=bot.can_invite_users, + can_delete_messages=bot.can_delete_messages, + can_restrict_members=bot.can_restrict_members, + can_pin_messages=bot.can_pin_messages, + can_promote_members=bot.can_promote_members, + can_manage_chat=bot.can_manage_chat, + can_manage_voice_chats=bot.can_manage_voice_chats, + ) + + title = "Gojo" # Deafult title + if len(m.text.split()) == 3 and not m.reply_to_message: + title = m.text.split()[2] + elif len(m.text.split()) == 2 and m.reply_to_message: + title = m.text.split()[1] + if title and len(title) > 16: + title = title[0:16] # trim title to 16 characters + + try: + await c.set_administrator_title(m.chat.id, user_id, title) + except RPCError as e: + LOGGER.error(e) + + LOGGER.info( + f"{m.from_user.id} super promoted {user_id} in {m.chat.id} with title '{title}'", + ) + + await m.reply_text( + (tlang(m, "admin.promote.promoted_user")).format( + promoter=(await mention_html(m.from_user.first_name, m.from_user.id)), + promoted=(await mention_html(user_first_name, user_id)), + chat_title=f"{escape(m.chat.title)} title set to {title}" + if title + else f"{escape(m.chat.title)} title set to Admin", + ), + ) + + # If user is approved, disapprove them as they willbe promoted and get even more rights + if Approve(m.chat.id).check_approve(user_id): + Approve(m.chat.id).remove_approve(user_id) + + # ----- Add admin to temp cache ----- + try: + inp1 = user_name or user_first_name + admins_group = ADMIN_CACHE[m.chat.id] + admins_group.append((user_id, inp1)) + ADMIN_CACHE[m.chat.id] = admins_group + except KeyError: + await admin_cache_reload(m, "promote_key_error") + + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.promote.bot_no_right")) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RPCError as e: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=e, + ), + ) + LOGGER.error(e) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("demote") & promote_filter) +async def demote_usr(c: Gojo, m: Message): + + global ADMIN_CACHE + + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.demote.no_target")) + return + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if user_id == Config.BOT_ID: + await m.reply_text("Get an admin to demote me!") + return + + # If user not alreay admin + try: + admin_list = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_list = { + i[0] for i in (await admin_cache_reload(m, "demote_cache_update")) + } + + if user_id not in admin_list: + await m.reply_text( + "This user is not an admin, how am I supposed to re-demote them?", + ) + return + + try: + await m.chat.promote_member( + user_id=user_id, + can_change_info=False, + can_invite_users=False, + can_delete_messages=False, + can_restrict_members=False, + can_pin_messages=False, + can_promote_members=False, + can_manage_chat=False, + can_manage_voice_chats=False, + ) + LOGGER.info(f"{m.from_user.id} demoted {user_id} in {m.chat.id}") + + # ----- Remove admin from cache ----- + try: + admin_list = ADMIN_CACHE[m.chat.id] + user = next(user for user in admin_list if user[0] == user_id) + admin_list.remove(user) + ADMIN_CACHE[m.chat.id] = admin_list + except (KeyError, StopIteration): + await admin_cache_reload(m, "demote_key_stopiter_error") + + await m.reply_text( + (tlang(m, "admin.demote.demoted_user")).format( + demoter=( + await mention_html( + m.from_user.first_name, + m.from_user.id, + ) + ), + demoted=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ), + ) + + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.demote.bot_no_right")) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + + + +@Gojo.on_message(command("invitelink")) +async def get_invitelink(c: Gojo, m: Message): + # Bypass the bot devs, sudos and owner + if m.from_user.id not in DEV_LEVEL: + user = await m.chat.get_member(m.from_user.id) + + if not user.can_invite_users and user.status != "creator": + await m.reply_text(tlang(m, "admin.no_user_invite_perm")) + return False + + try: + link = await c.export_chat_invite_link(m.chat.id) + await m.reply_text( + (tlang(m, "admin.invitelink")).format( + chat_name=m.chat.id, + link=link, + ), + disable_web_page_preview=True, + ) + LOGGER.info(f"{m.from_user.id} exported invite link in {m.chat.id}") + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except ChatAdminInviteRequired: + await m.reply_text(tlang(m, "admin.no_invite_perm")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.no_user_invite_perm")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("setgtitle") & admin_filter) +async def setgtitle(_, m: Message): + user = await m.chat.get_member(m.from_user.id) + + if not user.can_change_info and user.status != "creator": + await m.reply_text( + "You don't have enough permission to use this command!", + ) + return False + + if len(m.command) < 1: + return await m.reply_text("Please read /help for using it!") + + gtit = m.text.split(None, 1)[1] + try: + await m.chat.set_title(gtit) + except Exception as e: + return await m.reply_text(f"Error: {e}") + return await m.reply_text( + f"Successfully Changed Group Title From {m.chat.title} To {gtit}", + ) + + +@Gojo.on_message(command("setgdes") & admin_filter) +async def setgdes(_, m: Message): + + user = await m.chat.get_member(m.from_user.id) + if not user.can_change_info and user.status != "creator": + await m.reply_text( + "You don't have enough permission to use this command!", + ) + return False + + if len(m.command) < 1: + return await m.reply_text("Please read /help for using it!") + + desp = m.text.split(None, 1)[1] + try: + await m.chat.set_description(desp) + except Exception as e: + return await m.reply_text(f"Error: {e}") + return await m.reply_text( + f"Successfully Changed Group description From {m.chat.description} To {desp}", + ) + + +@Gojo.on_message(command("title") & admin_filter) +async def set_user_title(c: Gojo, m: Message): + + user = await m.chat.get_member(m.from_user.id) + if not user.can_promote_members and user.status != "creator": + await m.reply_text( + "You don't have enough permission to use this command!", + ) + return False + + if len(m.text.split()) == 1 and not m.reply_to_message: + return await m.reply_text("To whom??") + + if m.reply_to_message: + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + return await m.reply_text("Cannot find user!") + + if user_id == Config.BOT_ID: + return await m.reply_text("Huh, why ?") + + if not reason: + return await m.reply_text("Read /help please!") + + from_user = await c.get_users(user_id) + title = reason + try: + await c.set_administrator_title(m.chat.id, from_user.id, title) + except Exception as e: + return await m.reply_text(f"Error: {e}") + return await m.reply_text( + f"Successfully Changed {from_user.mention}'s Admin Title To {title}", + ) + + +@Gojo.on_message(command("setgpic") & admin_filter) +async def setgpic(c: Gojo, m: Message): + user = await m.chat.get_member(m.from_user.id) + if not user.can_change_info and user.status != "creator": + await m.reply_text( + "You don't have enough permission to use this command!", + ) + return False + if not m.reply_to_message: + return await m.reply_text("Reply to a photo to set it as chat photo") + if not m.reply_to_message.photo and not m.reply_to_message.document: + return await m.reply_text("Reply to a photo to set it as chat photo") + photo = await m.reply_to_message.download() + try: + await m.chat.set_photo(photo) + except Exception as e: + remove(photo) + return await m.reply_text(f"Error: {e}") + await m.reply_text("Successfully Changed Group Photo!") + remove(photo) + + +__PLUGIN__ = "admin" + +__alt_name__ = [ + "admins", + "promote", + "spromote", + "demote", + "adminlist", + "setgpic", + "title", + "setgtitle", + "fullpromote", + "invitelink", + "setgdes", + "zombies", +] diff --git a/Powers/plugins/antispam.py b/Powers/plugins/antispam.py new file mode 100644 index 0000000000000000000000000000000000000000..ae1a6c389411d007e54317e3dc125fd91e0724e5 --- /dev/null +++ b/Powers/plugins/antispam.py @@ -0,0 +1,181 @@ +from datetime import datetime +from io import BytesIO +from traceback import format_exc + +from pyrogram.errors import MessageTooLong, PeerIdInvalid, UserIsBlocked +from pyrogram.types import Message + +from Powers import LOGGER, MESSAGE_DUMP, SUPPORT_GROUP, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.database.antispam_db import GBan +from Powers.database.users_db import Users +from Powers.tr_engine import tlang +from Powers.utils.clean_file import remove_markdown_and_html +from Powers.utils.custom_filters import command +from Powers.utils.extract_user import extract_user +from Powers.utils.parser import mention_html +from Powers.vars import Config + +# Initialize +db = GBan() + + +@Gojo.on_message(command(["gban", "globalban"], sudo_cmd=True)) +async def gban(c: Gojo, m: Message): + if len(m.text.split()) == 1: + await m.reply_text(tlang(m, "antispam.gban.how_to")) + return + + if len(m.text.split()) == 2 and not m.reply_to_message: + await m.reply_text(tlang(m, "antispam.gban.enter_reason")) + return + + user_id, user_first_name, _ = await extract_user(c, m) + + if m.reply_to_message: + gban_reason = m.text.split(None, 1)[1] + else: + gban_reason = m.text.split(None, 2)[2] + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "antispam.part_of_support")) + return + + if user_id == Config.BOT_ID: + await m.reply_text(tlang(m, "antispam.gban.not_self")) + return + + if db.check_gban(user_id): + db.update_gban_reason(user_id, gban_reason) + await m.reply_text( + (tlang(m, "antispam.gban.updated_reason")).format( + gban_reason=gban_reason, + ), + ) + return + + db.add_gban(user_id, gban_reason, m.from_user.id) + await m.reply_text( + (tlang(m, "antispam.gban.added_to_watch")).format( + first_name=user_first_name, + ), + ) + LOGGER.info(f"{m.from_user.id} gbanned {user_id} from {m.chat.id}") + log_msg = (tlang(m, "antispam.gban.log_msg")).format( + chat_id=m.chat.id, + ban_admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + gbanned_user=(await mention_html(user_first_name, user_id)), + gban_user_id=user_id, + time=(datetime.utcnow().strftime("%H:%M - %d-%m-%Y")), + ) + await c.send_message(MESSAGE_DUMP, log_msg) + try: + # Send message to user telling that he's gbanned + await c.send_message( + user_id, + (tlang(m, "antispam.gban.user_added_to_watch")).format( + gban_reason=gban_reason, + SUPPORT_GROUP=SUPPORT_GROUP, + ), + ) + except UserIsBlocked: + LOGGER.error("Could not send PM Message, user blocked bot") + except PeerIdInvalid: + LOGGER.error( + "Haven't seen this user anywhere, mind forwarding one of their messages to me?", + ) + except Exception as ef: # TO DO: Improve Error Detection + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message( + command(["ungban", "unglobalban", "globalunban"], sudo_cmd=True), +) +async def ungban(c: Gojo, m: Message): + if len(m.text.split()) == 1: + await m.reply_text(tlang(m, "antispam.pass_user_id")) + return + + user_id, user_first_name, _ = await extract_user(c, m) + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "antispam.part_of_support")) + return + + if user_id == Config.BOT_ID: + await m.reply_text(tlang(m, "antispam.ungban.not_self")) + return + + if db.check_gban(user_id): + db.remove_gban(user_id) + await m.reply_text( + (tlang(m, "antispam.ungban.removed_from_list")).format( + first_name=user_first_name, + ), + ) + LOGGER.info(f"{m.from_user.id} ungbanned {user_id} from {m.chat.id}") + log_msg = (tlang(m, "amtispam.ungban.log_msg")).format( + chat_id=m.chat.id, + ungban_admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + ungbaned_user=(await mention_html(user_first_name, user_id)), + ungbanned_user_id=user_id, + time=(datetime.utcnow().strftime("%H:%M - %d-%m-%Y")), + ) + await c.send_message(MESSAGE_DUMP, log_msg) + try: + # Send message to user telling that he's ungbanned + await c.send_message( + user_id, + (tlang(m, "antispam.ungban.user_removed_from_list")), + ) + except Exception as ef: # TODO: Improve Error Detection + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + await m.reply_text(tlang(m, "antispam.ungban.non_gbanned")) + return + + +@Gojo.on_message( + command(["numgbans", "countgbans", "gbancount", "gbanscount"], sudo_cmd=True), +) +async def gban_count(_, m: Message): + await m.reply_text( + (tlang(m, "antispam.num_gbans")).format(count=(db.count_gbans())), + ) + LOGGER.info(f"{m.from_user.id} counting gbans in {m.chat.id}") + return + + +@Gojo.on_message( + command(["gbanlist", "globalbanlist"], sudo_cmd=True), +) +async def gban_list(_, m: Message): + banned_users = db.load_from_db() + + if not banned_users: + await m.reply_text(tlang(m, "antispam.none_gbanned")) + return + + banfile = tlang(m, "antispam.here_gbanned_start") + for user in banned_users: + banfile += f"[x] {Users.get_user_info(user['_id'])['name']} - {user['_id']}\n" + if user["reason"]: + banfile += f"Reason: {user['reason']}\n" + + try: + await m.reply_text(banfile) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(banfile))) as f: + f.name = "gbanlist.txt" + await m.reply_document( + document=f, + caption=tlang(m, "antispam.here_gbanned_start"), + ) + + LOGGER.info(f"{m.from_user.id} exported gbanlist in {m.chat.id}") + + return diff --git a/Powers/plugins/approve.py b/Powers/plugins/approve.py new file mode 100644 index 0000000000000000000000000000000000000000..fdeb2c7fe0a1be642b391852c8b61b99eed11c0d --- /dev/null +++ b/Powers/plugins/approve.py @@ -0,0 +1,234 @@ +from pyrogram import filters +from pyrogram.errors import PeerIdInvalid, RPCError, UserNotParticipant +from pyrogram.types import CallbackQuery, ChatPermissions, Message + +from Powers import LOGGER, SUPPORT_GROUP +from Powers.bot_class import Gojo +from Powers.database.approve_db import Approve +from Powers.utils.custom_filters import admin_filter, command, owner_filter +from Powers.utils.extract_user import extract_user +from Powers.utils.kbhelpers import ikb +from Powers.utils.parser import mention_html + + +@Gojo.on_message(command("approve") & admin_filter) +async def approve_user(c: Gojo, m: Message): + db = Approve(m.chat.id) + + chat_title = m.chat.title + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text( + "I don't know who you're talking about, you're going to need to specify a user!", + ) + return + try: + member = await m.chat.get_member(user_id) + except UserNotParticipant: + await m.reply_text("This user is not in this chat!") + return + + except RPCError as ef: + await m.reply_text( + f"Error: {ef}\nReport it to @{SUPPORT_GROUP}", + ) + return + if member.status in ("administrator", "creator"): + await m.reply_text( + "User is already admin - blacklists and locks already don't apply to them.", + ) + return + already_approved = db.check_approve(user_id) + if already_approved: + await m.reply_text( + f"{(await mention_html(user_first_name, user_id))} is already approved in {chat_title}", + ) + return + db.add_approve(user_id, user_first_name) + LOGGER.info(f"{user_id} approved by {m.from_user.id} in {m.chat.id}") + + # Allow all permissions + try: + await m.chat.unban_member(user_id=user_id) + except RPCError as g: + await m.reply_text(f"Error: {g}") + return + await m.reply_text( + ( + f"{(await mention_html(user_first_name, user_id))} has been approved in {chat_title}!\n" + "They will now be ignored by blacklists, locks and antiflood!" + ), + ) + return + + +@Gojo.on_message( + command(["disapprove", "unapprove"]) & admin_filter, +) +async def disapprove_user(c: Gojo, m: Message): + db = Approve(m.chat.id) + + chat_title = m.chat.title + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + already_approved = db.check_approve(user_id) + if not user_id: + await m.reply_text( + "I don't know who you're talking about, you're going to need to specify a user!", + ) + return + try: + member = await m.chat.get_member(user_id) + except UserNotParticipant: + if already_approved: # If user is approved and not in chat, unapprove them. + db.remove_approve(user_id) + LOGGER.info(f"{user_id} disapproved in {m.chat.id} as UserNotParticipant") + await m.reply_text("This user is not in this chat, unapproved them.") + return + except RPCError as ef: + await m.reply_text( + f"Error: {ef}\nReport it to @{SUPPORT_GROUP}", + ) + return + + if member.status in ("administrator", "creator"): + await m.reply_text("This user is an admin, they can't be disapproved.") + return + + if not already_approved: + await m.reply_text( + f"{(await mention_html(user_first_name, user_id))} isn't approved yet!", + ) + return + + db.remove_approve(user_id) + LOGGER.info(f"{user_id} disapproved by {m.from_user.id} in {m.chat.id}") + + # Set permission same as of current user by fetching them from chat! + await m.chat.restrict_member( + user_id=user_id, + permissions=m.chat.permissions, + ) + + await m.reply_text( + f"{(await mention_html(user_first_name, user_id))} is no longer approved in {chat_title}.", + ) + return + + +@Gojo.on_message(command("approved") & admin_filter) +async def check_approved(_, m: Message): + db = Approve(m.chat.id) + + chat = m.chat + chat_title = chat.title + msg = "The following users are approved:\n" + approved_people = db.list_approved() + + if not approved_people: + await m.reply_text(f"No users are approved in {chat_title}.") + return + + for user_id, user_name in approved_people.items(): + try: + await chat.get_member(user_id) # Check if user is in chat or not + except UserNotParticipant: + db.remove_approve(user_id) + continue + except PeerIdInvalid: + pass + msg += f"- `{user_id}`: {user_name}\n" + await m.reply_text(msg) + LOGGER.info(f"{m.from_user.id} checking approved users in {m.chat.id}") + return + + +@Gojo.on_message(command("approval") & filters.group) +async def check_approval(c: Gojo, m: Message): + db = Approve(m.chat.id) + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + check_approve = db.check_approve(user_id) + LOGGER.info(f"{m.from_user.id} checking approval of {user_id} in {m.chat.id}") + + if not user_id: + await m.reply_text( + "I don't know who you're talking about, you're going to need to specify a user!", + ) + return + if check_approve: + await m.reply_text( + f"{(await mention_html(user_first_name, user_id))} is an approved user. Locks, antiflood, and blacklists won't apply to them.", + ) + else: + await m.reply_text( + f"{(await mention_html(user_first_name, user_id))} is not an approved user. They are affected by normal commands.", + ) + return + + +@Gojo.on_message( + command("unapproveall") & filters.group & owner_filter, +) +async def unapproveall_users(_, m: Message): + db = Approve(m.chat.id) + + all_approved = db.list_approved() + if not all_approved: + await m.reply_text("No one is approved in this chat.") + return + + await m.reply_text( + "Are you sure you want to remove everyone who is approved in this chat?", + reply_markup=ikb( + [[("⚠️ Confirm", "unapprove_all"), ("❌ Cancel", "close_admin")]], + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^unapprove_all$")) +async def unapproveall_callback(_, q: CallbackQuery): + user_id = q.from_user.id + db = Approve(q.message.chat.id) + approved_people = db.list_approved() + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + db.unapprove_all() + for i in approved_people: + await q.message.chat.restrict_member( + user_id=i[0], + permissions=q.message.chat.permissions, + ) + await q.message.delete() + LOGGER.info(f"{user_id} disapproved all users in {q.message.chat.id}") + await q.answer("Disapproved all users!", show_alert=True) + return + + +__PLUGIN__ = "approve" + +_DISABLE_CMDS_ = ["approval"] + +__alt_name__ = ["approved"] diff --git a/Powers/plugins/bans.py b/Powers/plugins/bans.py new file mode 100644 index 0000000000000000000000000000000000000000..c20ccf2bf45bab467ce73acce7f4e1e5fa256912 --- /dev/null +++ b/Powers/plugins/bans.py @@ -0,0 +1,901 @@ +from traceback import format_exc +import random + +from pyrogram.errors import ( + ChatAdminRequired, + PeerIdInvalid, + RightForbidden, + RPCError, + UserAdminInvalid, +) +from pyrogram.filters import regex +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +from Powers import LOGGER, OWNER_ID, SUPPORT_GROUP, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.utils.fun_strings import BAN_GIFS, KICK_GIFS +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, admin_cache_reload +from Powers.utils.custom_filters import command, restrict_filter +from Powers.utils.extract_user import extract_user +from Powers.utils.parser import mention_html +from Powers.utils.string import extract_time +from Powers.vars import Config + + +BAN_MEDIA = random.choice(BAN_GIFS) +KICK_MEDIA = random.choice(KICK_GIFS) + +@Gojo.on_message(command("tban") & restrict_filter) +async def tban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "Provide me an user id or username or atleast reply to user")) + await m.stop_propagation() + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to ban") + return + if user_id == Config.BOT_ID: + await m.reply_text("WTF?? Why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "If I will ban this user....then who will going to manage me?")) + LOGGER.info( + f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + r_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to ban this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + reason = split_reason[1] if len(split_reason) > 1 else "" + + bantime = await extract_time(m, time_val) + + if not bantime: + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "I am not going to ban an admin")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} tbanned {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id, until_date=int(bantime)) + txt = (tlang(m, "Successfully banned the user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + banned=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unban", + callback_data=f"unban_={user_id}", + ), + ], + ], + ) + await m.reply_animation(reply_to_message_id = r_id, animation = BAN_MEDIA, caption = txt, reply_markup=keyboard, parse_mode="html") + # await m.reply_text(txt, reply_markup=keyboard, reply_to_message_id=r_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "You are not an admin here so stay in your limit bud....")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "I don't have rights to ban members....."))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "It's a general error contact support staff to know more...")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("stban") & restrict_filter) +async def stban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "Provide me an user id or username or atleast reply to user")) + await m.stop_propagation() + + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to ban") + return + if user_id == Config.BOT_ID: + await m.reply_text("What the heck? Why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "I am not going to ban one of my support staff")) + LOGGER.info( + f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to ban this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + reason = split_reason[1] if len(split_reason) > 1 else "" + + bantime = await extract_time(m, time_val) + + if not bantime: + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "I am not going to ban an admin...")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} stbanned {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id, until_date=int(bantime)) + await m.delete() + if m.reply_to_message: + await m.reply_to_message.delete() + return + return + except ChatAdminRequired: + await m.reply_text(tlang(m, "Stay in your limits.....")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "I don't have power to ban...."))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("dtban") & restrict_filter) +async def dtban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.ban.no_target")) + await m.stop_propagation() + + if not m.reply_to_message: + await m.reply_text( + "Reply to a message with this command to temp ban and delete the message.", + ) + await m.stop_propagation() + + user_id = m.reply_to_message.from_user.id + user_first_name = m.reply_to_message.from_user.first_name + + if not user_id: + await m.reply_text("Cannot find user to ban") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to ban this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + reason = split_reason[1] if len(split_reason) > 1 else "" + + bantime = await extract_time(m, time_val) + + if not bantime: + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.ban.admin_cannot_ban")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} dtbanned {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id, until_date=int(bantime)) + await m.reply_to_message.delete() + txt = (tlang(m, "admin.ban.banned_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + banned=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unban", + callback_data=f"unban_={user_id}", + ), + ], + ], + ) + await c.send_animation(chat_id = m.chat.id, animation = BAN_MEDIA, caption = txt, reply_markup=keyboard, parse_mode="html") + # await c.send_message(m.chat.id, txt, reply_markup=keyboard) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "admin.ban.bot_no_right"))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("kick") & restrict_filter) +async def kick_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.kick.no_target")) + return + + reason = None + + if m.reply_to_message: + r_id = m.reply_to_message.message_id + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + r_id = m.message_id + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to kick") + return + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I kick myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to kick {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "kick") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.kick.admin_cannot_kick")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} kicked {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id) + txt = (tlang(m, "admin.kick.kicked_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + kicked=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + # await m.reply_text(txt, reply_to_message_id=r_id) + await m.reply_animation(reply_to_message_id = r_id, animation = KICK_MEDIA, caption = txt, parse_mode="html") + await m.chat.unban_member(user_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.kick.bot_no_right")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("skick") & restrict_filter) +async def skick_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.kick.no_target")) + return + + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to kick") + return + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I kick myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to skick {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "kick") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.kick.admin_cannot_kick")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} skicked {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id) + await m.delete() + if m.reply_to_message: + await m.reply_to_message.delete() + await m.chat.unban_member(user_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.kick.bot_no_right")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("dkick") & restrict_filter) +async def dkick_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.kick.no_target")) + return + if not m.reply_to_message: + return await m.reply_text("Reply to a message to delete it and kick the user!") + + reason = None + + user_id = m.reply_to_message.from_user.id + user_first_name = m.reply_to_message.from_user.first_name + + if not user_id: + await m.reply_text("Cannot find user to kick") + return + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I kick myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to dkick {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "kick") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.kick.admin_cannot_kick")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} dkicked {user_id} in {m.chat.id}") + await m.reply_to_message.delete() + await m.chat.ban_member(user_id) + txt = (tlang(m, "admin.kick.kicked_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + kicked=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + await c.send_message(m.chat.id, txt) + await c.send_animation(chat_id = m.chat.id, animation = KICK_MEDIA, caption = txt, parse_mode="html") + await m.chat.unban_member(user_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.kick.bot_no_right")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("unban") & restrict_filter) +async def unban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.unban.no_target")) + await m.stop_propagation() + + if m.reply_to_message and not m.reply_to_message.from_user: + user_id, user_first_name = ( + m.reply_to_message.sender_chat.id, + m.reply_to_message.sender_chat.title, + ) + else: + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + reason = None + + try: + await m.chat.unban_member(user_id) + txt = (tlang(m, "admin.unban.unbanned_user")).format( + admin=m.from_user.mention, + unbanned=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + await m.reply_text(txt) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "admin.unban.bot_no_right"))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(command("sban") & restrict_filter) +async def sban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.ban.no_target")) + await m.stop_propagation() + + if m.reply_to_message and not m.reply_to_message.from_user: + user_id = m.reply_to_message.sender_chat.id + else: + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to ban") + return + if user_id == m.chat.id: + await m.reply_text("That's an admin!") + await m.stop_propagation() + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to sban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.ban.admin_cannot_ban")) + await m.stop_propagation() + + try: + LOGGER.info(f"{m.from_user.id} sbanned {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id) + await m.delete() + if m.reply_to_message: + await m.reply_to_message.delete() + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "admin.ban.bot_no_right"))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("dban") & restrict_filter) +async def dban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.ban.no_target")) + await m.stop_propagation() + + if not m.reply_to_message: + return await m.reply_text("Reply to a message to delete it and ban the user!") + + if m.reply_to_message and not m.reply_to_message.from_user: + user_id, user_first_name = ( + m.reply_to_message.sender_chat.id, + m.reply_to_message.sender_chat.title, + ) + else: + user_id, user_first_name = ( + m.reply_to_message.from_user.id, + m.reply_to_message.from_user.first_name, + ) + + if not user_id: + await m.reply_text("Cannot find user to ban") + return + if user_id == m.chat.id: + await m.reply_text("That's an admin!") + await m.stop_propagation() + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to dban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.ban.admin_cannot_ban")) + await m.stop_propagation() + + reason = None + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + + try: + LOGGER.info(f"{m.from_user.id} dbanned {user_id} in {m.chat.id}") + await m.reply_to_message.delete() + await m.chat.ban_member(user_id) + txt = (tlang(m, "admin.ban.banned_user")).format( + admin=m.from_user.mention, + banned=m.reply_to_message.from_user.mention, + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unban", + callback_data=f"unban_={user_id}", + ), + ], + ], + ) + await c.send_message(m.chat.id, txt, reply_markup=keyboard) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "admin.ban.bot_no_right"))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_message(command("ban") & restrict_filter) +async def ban_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text(tlang(m, "admin.ban.no_target")) + await m.stop_propagation() + + if m.reply_to_message and not m.reply_to_message.from_user: + user_id, user_first_name = ( + m.reply_to_message.sender_chat.id, + m.reply_to_message.sender_chat.title, + ) + else: + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to ban") + await m.stop_propagation() + if user_id == m.chat.id: + await m.reply_text("That's an admin!") + await m.stop_propagation() + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I ban myself?") + await m.stop_propagation() + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.stop_propagation() + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "ban") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.ban.admin_cannot_ban")) + await m.stop_propagation() + + reason = None + if m.reply_to_message: + r_id = m.reply_to_message.message_id + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + r_id = m.message_id + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + + try: + LOGGER.info(f"{m.from_user.id} banned {user_id} in {m.chat.id}") + await m.chat.ban_member(user_id) + txt = (tlang(m, "admin.ban.banned_user")).format( + admin=m.from_user.mention, + banned=(await mention_html(user_first_name, user_id)), + chat_title=m.chat.title, + ) + txt += f"\nReason: {reason}" if reason else "" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unban", + callback_data=f"unban_={user_id}", + ), + ], + ], + ) + await m.reply_text(txt, reply_markup=keyboard, reply_to_message_id=r_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except PeerIdInvalid: + await m.reply_text( + "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?", + ) + except UserAdminInvalid: + await m.reply_text(tlang(m, "admin.user_admin_invalid")) + except RightForbidden: + await m.reply_text(tlang(m, tlang(m, "admin.ban.bot_no_right"))) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + +@Gojo.on_callback_query(regex("^unban_")) +async def unbanbutton(c: Gojo, q: CallbackQuery): + splitter = (str(q.data).replace("unban_", "")).split("=") + user_id = int(splitter[1]) + user = await q.message.chat.get_member(q.from_user.id) + + if not user.can_restrict_members and q.from_user.id != OWNER_ID: + await q.answer( + "You don't have enough permission to do this!\nStay in your limits!", + show_alert=True, + ) + return + whoo = await c.get_chat(user_id) + doneto = whoo.first_name if whoo.first_name else whoo.title + try: + await q.message.chat.unban_member(user_id) + except RPCError as e: + await q.message.edit_text(f"Error: {e}") + return + await q.message.edit_text(f"{q.from_user.mention} unbanned {doneto}!") + return + + +@Gojo.on_message(command("kickme")) +async def kickme(_, m: Message): + reason = None + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + try: + LOGGER.info(f"{m.from_user.id} kickme used by {m.from_user.id} in {m.chat.id}") + await m.chat.ban_member(m.from_user.id) + txt = "Why not let me help you!" + txt += f"\nReason: {reason}" if reason else "" + await m.reply_text(txt) + await m.chat.unban_member(m.from_user.id) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + return + + +__PLUGIN__ = "bans" + +_DISABLE_CMDS_ = ["kickme"] + +__alt_name__ = [ + "ban", + "unban", + "kickme", + "kick", + "tban", +] diff --git a/Powers/plugins/blacklist.py b/Powers/plugins/blacklist.py new file mode 100644 index 0000000000000000000000000000000000000000..0ea7fa9c7397019e36ff81a34675856ba23f87c7 --- /dev/null +++ b/Powers/plugins/blacklist.py @@ -0,0 +1,218 @@ +from html import escape + +from pyrogram import filters +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.blacklist_db import Blacklist +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import command, owner_filter, restrict_filter +from Powers.utils.kbhelpers import ikb + + +@Gojo.on_message(command("blacklist") & filters.group) +async def view_blacklist(_, m: Message): + db = Blacklist(m.chat.id) + + LOGGER.info(f"{m.from_user.id} checking blacklists in {m.chat.id}") + + chat_title = m.chat.title + blacklists_chat = (tlang(m, "blacklist.curr_blacklist_initial")).format( + chat_title=chat_title, + ) + all_blacklisted = db.get_blacklists() + + if not all_blacklisted: + await m.reply_text( + (tlang(m, "blacklist.no_blacklist")).format( + chat_title=chat_title, + ), + ) + return + + blacklists_chat += "\n".join( + f" • {escape(i)}" for i in all_blacklisted + ) + + await m.reply_text(blacklists_chat) + return + + +@Gojo.on_message(command("addblacklist") & restrict_filter) +async def add_blacklist(_, m: Message): + db = Blacklist(m.chat.id) + + if len(m.text.split()) < 2: + await m.reply_text(tlang(m, "general.check_help")) + return + + bl_words = ((m.text.split(None, 1)[1]).lower()).split() + all_blacklisted = db.get_blacklists() + already_added_words, rep_text = [], "" + + for bl_word in bl_words: + if bl_word in all_blacklisted: + already_added_words.append(bl_word) + continue + db.add_blacklist(bl_word) + + if already_added_words: + rep_text = ( + ", ".join([f"{i}" for i in bl_words]) + + " already added in blacklist, skipped them!" + ) + LOGGER.info(f"{m.from_user.id} added new blacklists ({bl_words}) in {m.chat.id}") + await m.reply_text( + (tlang(m, "blacklist.added_blacklist")).format( + trigger=", ".join(f"{i}" for i in bl_words), + ) + + (f"\n{rep_text}" if rep_text else ""), + ) + + await m.stop_propagation() + + +@Gojo.on_message( + command(["blwarning", "blreason", "blacklistreason"]) & restrict_filter, +) +async def blacklistreason(_, m: Message): + db = Blacklist(m.chat.id) + + if len(m.text.split()) == 1: + curr = db.get_reason() + await m.reply_text( + f"The current reason for blacklists warn is:\n{curr}", + ) + else: + reason = m.text.split(None, 1)[1] + db.set_reason(reason) + await m.reply_text( + f"Updated reason for blacklists warn is:\n{reason}", + ) + return + + +@Gojo.on_message( + command(["rmblacklist", "unblacklist"]) & restrict_filter, +) +async def rm_blacklist(_, m: Message): + db = Blacklist(m.chat.id) + + if len(m.text.split()) < 2: + await m.reply_text(tlang(m, "general.check_help")) + return + + chat_bl = db.get_blacklists() + non_found_words, rep_text = [], "" + bl_words = ((m.text.split(None, 1)[1]).lower()).split() + + for bl_word in bl_words: + if bl_word not in chat_bl: + non_found_words.append(bl_word) + continue + db.remove_blacklist(bl_word) + + if non_found_words == bl_words: + return await m.reply_text("Blacklists not found!") + + if non_found_words: + rep_text = ( + "Could not find " + ", ".join(f"{i}" for i in non_found_words) + ) + " in blcklisted words, skipped them." + + LOGGER.info(f"{m.from_user.id} removed blacklists ({bl_words}) in {m.chat.id}") + await m.reply_text( + (tlang(m, "blacklist.rm_blacklist")).format( + bl_words=", ".join(f"{i}" for i in bl_words), + ) + + (f"\n{rep_text}" if rep_text else ""), + ) + + await m.stop_propagation() + + +@Gojo.on_message( + command(["blaction", "blacklistaction", "blacklistmode"]) & restrict_filter, +) +async def set_bl_action(_, m: Message): + db = Blacklist(m.chat.id) + + if len(m.text.split()) == 2: + action = m.text.split(None, 1)[1] + valid_actions = ("ban", "kick", "mute", "warn", "none") + if action not in valid_actions: + await m.reply_text( + ( + "Choose a valid blacklist action from " + + ", ".join(f"{i}" for i in valid_actions) + ), + ) + + return + db.set_action(action) + LOGGER.info( + f"{m.from_user.id} set blacklist action to '{action}' in {m.chat.id}", + ) + await m.reply_text( + (tlang(m, "blacklist.action_set")).format(action=action), + ) + elif len(m.text.split()) == 1: + action = db.get_action() + LOGGER.info(f"{m.from_user.id} checking blacklist action in {m.chat.id}") + await m.reply_text( + (tlang(m, "blacklist.action_get")).format(action=action), + ) + else: + await m.reply_text(tlang(m, "general.check_help")) + + return + + +@Gojo.on_message( + command("rmallblacklist") & owner_filter, +) +async def rm_allblacklist(_, m: Message): + db = Blacklist(m.chat.id) + + all_bls = db.get_blacklists() + if not all_bls: + await m.reply_text("No notes are blacklists in this chat") + return + + await m.reply_text( + "Are you sure you want to clear all blacklists?", + reply_markup=ikb( + [[("⚠️ Confirm", "rm_allblacklist"), ("❌ Cancel", "close_admin")]], + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^rm_allblacklist$")) +async def rm_allbl_callback(_, q: CallbackQuery): + user_id = q.from_user.id + db = Blacklist(q.message.chat.id) + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + db.rm_all_blacklist() + await q.message.delete() + LOGGER.info(f"{user_id} removed all blacklists in {q.message.chat.id}") + await q.answer("Cleared all Blacklists!", show_alert=True) + return + + +__PLUGIN__ = "blacklist" + +__alt_name__ = ["blacklists", "blaction"] diff --git a/Powers/plugins/botstaff.py b/Powers/plugins/botstaff.py new file mode 100644 index 0000000000000000000000000000000000000000..b19aed32343d8183075f17da11e27c1085e01ffe --- /dev/null +++ b/Powers/plugins/botstaff.py @@ -0,0 +1,54 @@ +from pyrogram.errors import RPCError +from pyrogram.types import Message + +from Powers import DEV_USERS, LOGGER, OWNER_ID, SUDO_USERS, WHITELIST_USERS +from Powers.bot_class import Gojo +from Powers.utils.custom_filters import command +from Powers.utils.parser import mention_html + + +@Gojo.on_message(command("botstaff", dev_cmd=True)) +async def botstaff(c: Gojo, m: Message): + try: + owner = await c.get_users(OWNER_ID) + reply = f"🌟 Owner: {(await mention_html(owner.first_name, OWNER_ID))} ({OWNER_ID})\n" + except RPCError: + pass + true_dev = list(set(DEV_USERS) - {OWNER_ID}) + reply += "\nDevelopers ⚡️:\n" + if true_dev == []: + reply += "No Dev Users\n" + else: + for each_user in true_dev: + user_id = int(each_user) + try: + user = await c.get_users(user_id) + reply += f"• {(await mention_html(user.first_name, user_id))} ({user_id})\n" + except RPCError: + pass + true_sudo = list(set(SUDO_USERS) - set(DEV_USERS)) + reply += "\nSudo Users 🐉:\n" + if true_sudo == []: + reply += "No Sudo Users\n" + else: + for each_user in true_sudo: + user_id = int(each_user) + try: + user = await c.get_users(user_id) + reply += f"• {(await mention_html(user.first_name, user_id))} ({user_id})\n" + except RPCError: + pass + reply += "\nWhitelisted Users 🐺:\n" + if WHITELIST_USERS == []: + reply += "No additional whitelisted users\n" + else: + for each_user in WHITELIST_USERS: + user_id = int(each_user) + try: + user = await c.get_users(user_id) + reply += f"• {(await mention_html(user.first_name, user_id))} ({user_id})\n" + except RPCError: + pass + await m.reply_text(reply) + LOGGER.info(f"{m.from_user.id} fetched botstaff in {m.chat.id}") + return diff --git a/Powers/plugins/chat_blacklist.py b/Powers/plugins/chat_blacklist.py new file mode 100644 index 0000000000000000000000000000000000000000..b6c25b90814b9602558d1ba169e6fd6dee23231b --- /dev/null +++ b/Powers/plugins/chat_blacklist.py @@ -0,0 +1,86 @@ +from traceback import format_exc + +from pyrogram.errors import PeerIdInvalid, RPCError +from pyrogram.types import Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.group_blacklist import GroupBlacklist +from Powers.utils.custom_filters import command + +# initialise database +db = GroupBlacklist() + + +@Gojo.on_message(command("blchat", dev_cmd=True)) +async def blacklist_chat(c: Gojo, m: Message): + if len(m.text.split()) >= 2: + chat_ids = m.text.split()[1:] + replymsg = await m.reply_text(f"Adding {len(chat_ids)} chats to blacklist") + LOGGER.info(f"{m.from_user.id} blacklisted {chat_ids} groups for bot") + for chat in chat_ids: + try: + get_chat = await c.get_chat(chat) + chat_id = get_chat.id + db.add_chat(chat_id) + except PeerIdInvalid: + await replymsg.edit_text( + "Haven't seen this group in this session, maybe try again later?", + ) + except RPCError as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + await replymsg.edit_text( + f"Added the following chats to Blacklist.\n{', '.join(chat_ids)}.", + ) + return + + +@Gojo.on_message( + command(["rmblchat", "unblchat"], dev_cmd=True), +) +async def unblacklist_chat(c: Gojo, m: Message): + if len(m.text.split()) >= 2: + chat_ids = m.text.split()[1:] + replymsg = await m.reply_text(f"Removing {len(chat_ids)} chats from blacklist") + LOGGER.info(f"{m.from_user.id} removed blacklisted {chat_ids} groups for bot") + bl_chats = db.list_all_chats() + for chat in chat_ids: + try: + get_chat = await c.get_chat(chat) + chat_id = get_chat.id + if chat_id not in bl_chats: + # If chat is not blaklisted, continue loop + continue + db.remove_chat(chat_id) + except PeerIdInvalid: + await replymsg.edit_text( + "Haven't seen this group in this session, maybe try again later?", + ) + except RPCError as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + await replymsg.edit_text( + f"Removed the following chats to Blacklist.\n{', '.join(chat_ids)}.", + ) + return + + +@Gojo.on_message( + command(["blchatlist", "blchats"], dev_cmd=True), +) +async def list_blacklist_chats(_, m: Message): + bl_chats = db.list_all_chats() + LOGGER.info(f"{m.from_user.id} checking group blacklists in {m.chat.id}") + if bl_chats: + txt = ( + ( + "These Chats are Blacklisted:\n" + + "\n".join(f"{i}" for i in bl_chats) + ), + ) + + else: + txt = "No chats are currently blacklisted!" + await m.reply_text(txt) + return diff --git a/Powers/plugins/dev.py b/Powers/plugins/dev.py new file mode 100644 index 0000000000000000000000000000000000000000..18c826394aaa828616da5ea8829426af89e9360a --- /dev/null +++ b/Powers/plugins/dev.py @@ -0,0 +1,360 @@ +import sys +from asyncio import create_subprocess_shell, sleep, subprocess +from io import BytesIO, StringIO +from time import gmtime, strftime, time +from traceback import format_exc + +from pyrogram.errors import ( + ChannelInvalid, + ChannelPrivate, + ChatAdminRequired, + FloodWait, + MessageTooLong, + PeerIdInvalid, + RPCError, +) +from pyrogram.types import Message +from speedtest import Speedtest + +from Powers import LOGFILE, LOGGER, MESSAGE_DUMP, UPTIME +from Powers.bot_class import Gojo +from Powers.database.chats_db import Chats +from Powers.tr_engine import tlang +from Powers.utils.clean_file import remove_markdown_and_html +from Powers.utils.custom_filters import command +from Powers.utils.http_helper import HTTPx +from Powers.utils.kbhelpers import ikb +from Powers.utils.parser import mention_markdown +from Powers.vars import Config + + +@Gojo.on_message(command("ping", sudo_cmd=True)) +async def ping(_, m: Message): + LOGGER.info(f"{m.from_user.id} used ping cmd in {m.chat.id}") + start = time() + replymsg = await m.reply_text((tlang(m, "utils.ping.pinging")), quote=True) + delta_ping = time() - start + await replymsg.edit_text(f"Pong!\n{delta_ping * 1000:.3f} ms") + return + + +@Gojo.on_message(command("logs", dev_cmd=True)) +async def send_log(c: Gojo, m: Message): + replymsg = await m.reply_text("Sending logs...!") + await c.send_message( + MESSAGE_DUMP, + f"#LOGS\n\n**User:** {(await mention_markdown(m.from_user.first_name, m.from_user.id))}", + ) + # Send logs + with open(LOGFILE) as f: + raw = (await (f.read()))[1] + await m.reply_document( + document=LOGFILE, + quote=True, + ) + await replymsg.delete() + return + + +@Gojo.on_message(command("ginfo", sudo_cmd=True)) +async def group_info(c: Gojo, m: Message): + if len(m.text.split()) != 2: + await m.reply_text( + f"It works like this: {Config.PREFIX_HANDLER} chat_id", + ) + return + + chat_id = m.text.split(None, 1)[1] + + replymsg = await m.reply_text("Fetching info about group...!") + grp_data = await c.get_chat(chat_id) + msg = ( + f"Information for group: {chat_id}\n\n" + f"Group Name: {grp_data['title']}\n" + f"Members Count: {grp_data['members_count']}\n" + f"Type: {grp_data['type']}\n" + f"Group ID: {grp_data['id']}" + ) + await replymsg.edit_text(msg) + return + + +@Gojo.on_message(command("speedtest", dev_cmd=True)) +async def test_speed(c: Gojo, m: Message): + await c.send_message( + MESSAGE_DUMP, + f"#SPEEDTEST\n\n**User:** {(await mention_markdown(m.from_user.first_name, m.from_user.id))}", + ) + sent = await m.reply_text(tlang(m, "dev.speedtest.start_speedtest")) + s = Speedtest() + bs = s.get_best_server() + dl = round(s.download() / 1024 / 1024, 2) + ul = round(s.upload() / 1024 / 1024, 2) + await sent.edit_text( + (tlang(m, "dev.speedtest.speedtest_txt")).format( + host=bs["sponsor"], + ping=int(bs["latency"]), + download=dl, + upload=ul, + ), + ) + return + + +@Gojo.on_message(command("neofetch", dev_cmd=True)) +async def neofetch_stats(_, m: Message): + cmd = "neofetch --stdout" + + process = await create_subprocess_shell( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + e = stderr.decode() + if not e: + e = "No Error" + OUTPUT = stdout.decode() + if not OUTPUT: + OUTPUT = "No Output" + + try: + await m.reply_text(OUTPUT, quote=True) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(OUTPUT))) as f: + f.name = "neofetch.txt" + await m.reply_document(document=f, caption="neofetch result") + await m.delete() + return + + +@Gojo.on_message(command(["eval", "py"], dev_cmd=True)) +async def evaluate_code(c: Gojo, m: Message): + if len(m.text.split()) == 1: + await m.reply_text(tlang(m, "dev.execute_cmd_err")) + return + sm = await m.reply_text("`Processing...`") + cmd = m.text.split(None, maxsplit=1)[1] + + reply_to_id = m.message_id + if m.reply_to_message: + reply_to_id = m.reply_to_message.message_id + + old_stderr = sys.stderr + old_stdout = sys.stdout + redirected_output = sys.stdout = StringIO() + redirected_error = sys.stderr = StringIO() + stdout, stderr, exc = None, None, None + + try: + await aexec(cmd, c, m) + except Exception as ef: + LOGGER.error(ef) + exc = format_exc() + + stdout = redirected_output.getvalue() + stderr = redirected_error.getvalue() + sys.stdout = old_stdout + sys.stderr = old_stderr + + evaluation = "" + if exc: + evaluation = exc + elif stderr: + evaluation = stderr + elif stdout: + evaluation = stdout + else: + evaluation = "Success" + + final_output = f"EVAL: {cmd}\n\nOUTPUT:\n{evaluation.strip()} \n" + + try: + await sm.edit(final_output) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(final_output))) as f: + f.name = "py.txt" + await m.reply_document( + document=f, + caption=cmd, + disable_notification=True, + reply_to_message_id=reply_to_id, + ) + await sm.delete() + + return + + +async def aexec(code, c, m): + exec("async def __aexec(c, m): " + "".join(f"\n {l}" for l in code.split("\n"))) + return await locals()["__aexec"](c, m) + + +@Gojo.on_message(command(["exec", "sh"], dev_cmd=True)) +async def execution(_, m: Message): + if len(m.text.split()) == 1: + await m.reply_text(tlang(m, "dev.execute_cmd_err")) + return + sm = await m.reply_text("`Processing...`") + cmd = m.text.split(maxsplit=1)[1] + reply_to_id = m.message_id + if m.reply_to_message: + reply_to_id = m.reply_to_message.message_id + + process = await create_subprocess_shell( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + stdout, stderr = await process.communicate() + e = stderr.decode() + if not e: + e = "No Error" + o = stdout.decode() + if not o: + o = "No Output" + + OUTPUT = "" + OUTPUT += f"QUERY:\nCommand:\n{cmd} \n" + OUTPUT += f"PID: {process.pid}\n\n" + OUTPUT += f"stderr: \n{e}\n\n" + OUTPUT += f"stdout: \n{o}" + + try: + await sm.edit_text(OUTPUT) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(OUTPUT))) as f: + f.name = "sh.txt" + await m.reply_document( + document=f, + caption=cmd, + disable_notification=True, + reply_to_message_id=reply_to_id, + ) + await sm.delete() + return + + +@Gojo.on_message(command("ip", dev_cmd=True)) +async def public_ip(c: Gojo, m: Message): + ip = await HTTPx.get("https://api.ipify.org") + await c.send_message( + MESSAGE_DUMP, + f"#IP\n\n**User:** {(await mention_markdown(m.from_user.first_name, m.from_user.id))}", + ) + await m.reply_text( + (tlang(m, "dev.bot_ip")).format(ip=f"{ip.text}"), + quote=True, + ) + return + + +@Gojo.on_message(command("chatlist", dev_cmd=True)) +async def chats(c: Gojo, m: Message): + exmsg = await m.reply_text(tlang(m, "dev.chatlist.exporting")) + await c.send_message( + MESSAGE_DUMP, + f"#CHATLIST\n\n**User:** {(await mention_markdown(m.from_user.first_name, m.from_user.id))}", + ) + all_chats = (Chats.list_chats_full()) or {} + chatfile = tlang(m, "dev.chatlist.header") + P = 1 + for chat in all_chats: + try: + chat_info = await c.get_chat(chat["_id"]) + chat_members = chat_info.members_count + try: + invitelink = chat_info.invite_link + except KeyError: + invitelink = "No Link!" + chatfile += f"{P}. {chat['chat_name']} | {chat['_id']} | {chat_members} | {invitelink}\n" + P += 1 + except ChatAdminRequired: + pass + except (ChannelPrivate, ChannelInvalid): + Chats.remove_chat(chat["_id"]) + except PeerIdInvalid: + LOGGER.warning(f"Peer not found {chat['_id']}") + except FloodWait as ef: + LOGGER.error("FloodWait required, Sleeping for 60s") + LOGGER.error(ef) + sleep(60) + except RPCError as ef: + LOGGER.error(ef) + await m.reply_text(f"**Error:**\n{ef}") + + with BytesIO(str.encode(await remove_markdown_and_html(chatfile))) as f: + f.name = "chatlist.txt" + await m.reply_document( + document=f, + caption=(tlang(m, "dev.chatlist.chats_in_db")), + ) + await exmsg.delete() + return + + +@Gojo.on_message(command("uptime", dev_cmd=True)) +async def uptime(_, m: Message): + up = strftime("%Hh %Mm %Ss", gmtime(time() - UPTIME)) + await m.reply_text((tlang(m, "dev.uptime")).format(uptime=up), quote=True) + return + + +@Gojo.on_message(command("leavechat", dev_cmd=True)) +async def leave_chat(c: Gojo, m: Message): + if len(m.text.split()) != 2: + await m.reply_text("Supply a chat id which I should leave!", quoet=True) + return + + chat_id = m.text.split(None, 1)[1] + + replymsg = await m.reply_text(f"Trying to leave chat {chat_id}...", quote=True) + try: + await c.leave_chat(chat_id) + await replymsg.edit_text(f"Left {chat_id}.") + except PeerIdInvalid: + await replymsg.edit_text("Haven't seen this group in this session!") + except RPCError as ef: + LOGGER.error(ef) + await replymsg.edit_text(f"Failed to leave chat!\nError: {ef}.") + return + + +@Gojo.on_message(command("chatbroadcast", dev_cmd=True)) +async def chat_broadcast(c: Gojo, m: Message): + if m.reply_to_message: + msg = m.reply_to_message.text.markdown + else: + await m.reply_text("Reply to a message to broadcast it") + return + + exmsg = await m.reply_text("Started broadcasting!") + all_chats = (Chats.list_chats_by_id()) or {} + err_str, done_broadcast = "", 0 + + for chat in all_chats: + try: + await c.send_message(chat, msg, disable_web_page_preview=True) + done_broadcast += 1 + await sleep(0.1) + except RPCError as ef: + LOGGER.error(ef) + err_str += str(ef) + continue + + await exmsg.edit_text( + f"Done broadcasting ✅\nSent message to {done_broadcast} chats", + ) + + if err_str: + with BytesIO(str.encode(await remove_markdown_and_html(err_str))) as f: + f.name = "error_broadcast.txt" + await m.reply_document( + document=f, + caption="Broadcast Error", + ) + + return + + +_DISABLE_CMDS_ = ["ping"] diff --git a/Powers/plugins/disable.py b/Powers/plugins/disable.py new file mode 100644 index 0000000000000000000000000000000000000000..10e304de97844d59c8d73a16d8538a60106898c8 --- /dev/null +++ b/Powers/plugins/disable.py @@ -0,0 +1,156 @@ +from html import escape + +from pyrogram import filters +from pyrogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +from Powers import HELP_COMMANDS, LOGGER +from Powers.bot_class import Gojo +from Powers.database.disable_db import Disabling +from Powers.utils.custom_filters import ( + admin_filter, + can_change_filter, + command, + owner_filter, +) + + +@Gojo.on_message(command("disable") & can_change_filter) +async def disableit(_, m: Message): + if len(m.text.split()) < 2: + return await m.reply_text("What to disable?") + disable_cmd_keys = sorted( + k + for j in [HELP_COMMANDS[i]["disablable"] for i in list(HELP_COMMANDS.keys())] + for k in j + ) + + db = Disabling(m.chat.id) + disable_list = db.get_disabled() + LOGGER.info(f"{m.from_user.id} used disabled cmd in {m.chat.id}") + + if str(m.text.split(None, 1)[1]) in disable_list: + return await m.reply_text("It's already disabled!") + + if str((m.text.split(None, 1)[1]).lower()) in disable_cmd_keys: + db.add_disable((str(m.text.split(None, 1)[1])).lower()) + return await m.reply_text(f"Disabled {m.text.split(None, 1)[1]}!") + if str(m.text.split(None, 1)[1]) not in disable_cmd_keys: + return await m.reply_text("Can't do it sorry !") + + +@Gojo.on_message(command("disabledel") & can_change_filter) +async def set_dsbl_action(_, m: Message): + db = Disabling(m.chat.id) + + status = db.get_action() + if status == "none": + cur = False + else: + cur = True + args = m.text.split(" ", 1) + + LOGGER.info(f"{m.from_user.id} disabledel used in {m.chat.id}") + + if len(args) >= 2: + if args[1].lower() == "on": + db.set_action("del") + await m.reply_text("Oke done!") + elif args[1].lower() == "off": + db.set_action("none") + await m.reply_text("Oke i will not delete!") + else: + await m.reply_text("what are you trying to do ??") + else: + await m.reply_text(f"Current settings:- {cur}") + return + + +@Gojo.on_message(command("enable") & can_change_filter) +async def enableit(_, m: Message): + if len(m.text.split()) < 2: + return await m.reply_text("What to enable?") + db = Disabling(m.chat.id) + disable_list = db.get_disabled() + if str(m.text.split(None, 1)[1]) not in disable_list: + return await m.reply_text("It's not disabled!") + db.remove_disabled((str(m.text.split(None, 1)[1])).lower()) + LOGGER.info(f"{m.from_user.id} enabled something in {m.chat.id}") + return await m.reply_text(f"Enabled {m.text.split(None, 1)[1]}!") + + +@Gojo.on_message(command("disableable") & can_change_filter) +async def disabling(_, m: Message): + disable_cmd_keys = sorted( + k + for j in [HELP_COMMANDS[i]["disablable"] for i in list(HELP_COMMANDS.keys())] + for k in j + ) + tes = "List of commnds that can be disabled:\n" + tes += "\n".join(f" • {escape(i)}" for i in disable_cmd_keys) + LOGGER.info(f"{m.from_user.id} checked disableable {m.chat.id}") + return await m.reply_text(tes) + + +@Gojo.on_message(command("disabled") & admin_filter) +async def disabled(_, m: Message): + db = Disabling(m.chat.id) + disable_list = db.get_disabled() + if not disable_list: + await m.reply_text("No disabled items!") + return + tex = "Disabled commands:\n" + tex += "\n".join(f" • {escape(i)}" for i in disable_list) + LOGGER.info(f"{m.from_user.id} checked disabled {m.chat.id}") + return await m.reply_text(tex) + + +@Gojo.on_message(command("enableall") & owner_filter) +async def rm_alldisbl(_, m: Message): + db = Disabling(m.chat.id) + all_dsbl = db.get_disabled() + if not all_dsbl: + await m.reply_text("No disabled commands in this chat") + return + await m.reply_text( + "Are you sure you want to enable all?", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Confirm", + callback_data="enableallcmds", + ), + InlineKeyboardButton("Cancel", callback_data="close_admin"), + ], + ], + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^enableallcmds$")) +async def enablealll(_, q: CallbackQuery): + user_id = q.from_user.id + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + db = Disabling(q.message.chat.id) + db.rm_all_disabled() + LOGGER.info(f"{user_id} enabled all in {q.message.chat.id}") + await q.message.edit_text("Enabled all!", show_alert=True) + return diff --git a/Powers/plugins/filters.py b/Powers/plugins/filters.py new file mode 100644 index 0000000000000000000000000000000000000000..3203e7ec9c68c0823abf7bec255aadc8d338f8ef --- /dev/null +++ b/Powers/plugins/filters.py @@ -0,0 +1,301 @@ +from re import escape as re_escape +from secrets import choice +from traceback import format_exc + +from pyrogram import filters +from pyrogram.errors import RPCError +from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, Message + +from Powers.bot_class import LOGGER, Gojo +from Powers.database.filters_db import Filters +from Powers.utils.cmd_senders import send_cmd +from Powers.utils.custom_filters import admin_filter, command, owner_filter +from Powers.utils.kbhelpers import ikb +from Powers.utils.msg_types import Types, get_filter_type +from Powers.utils.regex_utils import regex_searcher +from Powers.utils.string import ( + build_keyboard, + escape_mentions_using_curly_brackets, + parse_button, + split_quotes, +) + +# Initialise +db = Filters() + + +@Gojo.on_message(command("filters") & filters.group & ~filters.bot) +async def view_filters(_, m: Message): + LOGGER.info(f"{m.from_user.id} checking filters in {m.chat.id}") + + filters_chat = f"Filters in {m.chat.title}:\n" + all_filters = db.get_all_filters(m.chat.id) + actual_filters = [j for i in all_filters for j in i.split("|")] + + if not actual_filters: + await m.reply_text(f"There are no filters in {m.chat.title}") + return + + filters_chat += "\n".join( + [ + f" • {' | '.join([f'{i}' for i in i.split('|')])}" + for i in all_filters + ], + ) + return await m.reply_text(filters_chat, disable_web_page_preview=True) + + +@Gojo.on_message(command(["filter", "addfilter"]) & admin_filter & ~filters.bot) +async def add_filter(_, m: Message): + + args = m.text.split(" ", 1) + all_filters = db.get_all_filters(m.chat.id) + actual_filters = {j for i in all_filters for j in i.split("|")} + + if (len(all_filters) >= 50) and (len(actual_filters) >= 150): + await m.reply_text( + "Only 50 filters and 150 aliases are allowed per chat!\nTo add more filters, remove the existing ones.", + ) + return + + if not m.reply_to_message and len(m.text.split()) < 3: + return await m.reply_text("Please read help section for how to save a filter!") + + if m.reply_to_message and len(args) < 2: + return await m.reply_text("Please read help section for how to save a filter!") + + extracted = await split_quotes(args[1]) + keyword = extracted[0].lower() + + for k in keyword.split("|"): + if k in actual_filters: + return await m.reply_text(f"Filter {k} already exists!") + + if not keyword: + return await m.reply_text( + f"{m.text}\n\nError: You must give a name for this Filter!", + ) + + if keyword.startswith("<") or keyword.startswith(">"): + return await m.reply_text("Cannot save a filter which starts with '<' or '>'") + + eee, msgtype, file_id = await get_filter_type(m) + lol = eee if m.reply_to_message else extracted[1] + teks = lol if msgtype == Types.TEXT else eee + + if not m.reply_to_message and msgtype == Types.TEXT and len(m.text.split()) < 3: + return await m.reply_text( + f"{m.text}\n\nError: There is no text in here!", + ) + + if not teks and not msgtype: + return await m.reply_text( + 'Please provide keyword for this filter reply with!\nEnclose filter in "double quotes"', + ) + + if not msgtype: + return await m.reply_text( + "Please provide data for this filter reply with!", + ) + + add = db.save_filter(m.chat.id, keyword, teks, msgtype, file_id) + LOGGER.info(f"{m.from_user.id} added new filter ({keyword}) in {m.chat.id}") + if add: + await m.reply_text( + f"Saved filter for '{', '.join(keyword.split('|'))}' in {m.chat.title}!", + ) + await m.stop_propagation() + + +@Gojo.on_message(command(["stop", "unfilter"]) & admin_filter & ~filters.bot) +async def stop_filter(_, m: Message): + args = m.command + + if len(args) < 1: + return await m.reply_text("What should I stop replying to?") + + chat_filters = db.get_all_filters(m.chat.id) + act_filters = {j for i in chat_filters for j in i.split("|")} + + if not chat_filters: + return await m.reply_text("No filters active here!") + + for keyword in act_filters: + if keyword == m.text.split(None, 1)[1].lower(): + db.rm_filter(m.chat.id, m.text.split(None, 1)[1].lower()) + LOGGER.info(f"{m.from_user.id} removed filter ({keyword}) in {m.chat.id}") + await m.reply_text( + f"Okay, I'll stop replying to that filter and it's aliases in {m.chat.title}.", + ) + await m.stop_propagation() + + await m.reply_text( + "That's not a filter - Click: /filters to get currently active filters.", + ) + await m.stop_propagation() + + +@Gojo.on_message( + command( + ["rmallfilters", "removeallfilters", "stopall", "stopallfilters"], + ) + & owner_filter, +) +async def rm_allfilters(_, m: Message): + all_bls = db.get_all_filters(m.chat.id) + if not all_bls: + return await m.reply_text("No filters to stop in this chat.") + + return await m.reply_text( + "Are you sure you want to clear all filters?", + reply_markup=ikb( + [[("⚠️ Confirm", "rm_allfilters"), ("❌ Cancel", "close_admin")]], + ), + ) + + +@Gojo.on_callback_query(filters.regex("^rm_allfilters$")) +async def rm_allfilters_callback(_, q: CallbackQuery): + user_id = q.from_user.id + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + db.rm_all_filters(q.message.chat.id) + await q.message.edit_text(f"Cleared all filters for {q.message.chat.title}") + LOGGER.info(f"{user_id} removed all filter from {q.message.chat.id}") + await q.answer("Cleared all Filters!", show_alert=True) + return + + +async def send_filter_reply(c: Gojo, m: Message, trigger: str): + """Reply with assigned filter for the trigger""" + getfilter = db.get_filter(m.chat.id, trigger) + if m and not m.from_user: + return + + if not getfilter: + return await m.reply_text( + "Error: Cannot find a type for this filter!!", + quote=True, + ) + + msgtype = getfilter["msgtype"] + if not msgtype: + return await m.reply_text("Error: Cannot find a type for this filter!!") + + try: + # support for random filter texts + splitter = "%%%" + filter_reply = getfilter["filter_reply"].split(splitter) + filter_reply = choice(filter_reply) + except KeyError: + filter_reply = "" + + parse_words = [ + "first", + "last", + "fullname", + "id", + "mention", + "username", + "chatname", + ] + text = await escape_mentions_using_curly_brackets(m, filter_reply, parse_words) + teks, button = await parse_button(text) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + textt = teks + try: + if msgtype == Types.TEXT: + if button: + try: + await m.reply_text( + textt, + # parse_mode="markdown", + reply_markup=button, + disable_web_page_preview=True, + quote=True, + ) + return + except RPCError as ef: + await m.reply_text( + "An error has occured! Cannot parse note.", + quote=True, + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + else: + await m.reply_text( + textt, + # parse_mode="markdown", + quote=True, + disable_web_page_preview=True, + ) + return + + elif msgtype in ( + Types.STICKER, + Types.VIDEO_NOTE, + Types.CONTACT, + Types.ANIMATED_STICKER, + ): + await (await send_cmd(c, msgtype))( + m.chat.id, + getfilter["fileid"], + reply_markup=button, + reply_to_message_id=m.message_id, + ) + else: + await (await send_cmd(c, msgtype))( + m.chat.id, + getfilter["fileid"], + caption=textt, + # parse_mode="markdown", + reply_markup=button, + reply_to_message_id=m.message_id, + ) + except Exception as ef: + await m.reply_text(f"Error in filters: {ef}") + return msgtype + + return msgtype + + +@Gojo.on_message(filters.text & filters.group & ~filters.bot, group=69) +async def filters_watcher(c: Gojo, m: Message): + + chat_filters = db.get_all_filters(m.chat.id) + actual_filters = {j for i in chat_filters for j in i.split("|")} + + for trigger in actual_filters: + pattern = r"( |^|[^\w])" + re_escape(trigger) + r"( |$|[^\w])" + match = await regex_searcher(pattern, m.text.lower()) + if match: + try: + msgtype = await send_filter_reply(c, m, trigger) + LOGGER.info(f"Replied with {msgtype} to {trigger} in {m.chat.id}") + except Exception as ef: + await m.reply_text(f"Error: {ef}") + LOGGER.error(ef) + LOGGER.error(format_exc()) + break + continue + return + + +__PLUGIN__ = "filters" + +_DISABLE_CMDS_ = ["filters"] + +__alt_name__ = ["filters", "autoreply"] diff --git a/Powers/plugins/formatting.py b/Powers/plugins/formatting.py new file mode 100644 index 0000000000000000000000000000000000000000..5d073bdb1ab632dc1afb60559f7a71aeb48e43d5 --- /dev/null +++ b/Powers/plugins/formatting.py @@ -0,0 +1,84 @@ +from pyrogram import filters +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import command +from Powers.utils.kbhelpers import ikb + + +async def gen_formatting_kb(m): + return ikb( + [ + [ + ("Markdown Formatting", "formatting.md_formatting"), + ("Fillings", "formatting.fillings"), + ], + [("Random Content", "formatting.random_content")], + [(("« " + (tlang(m, "general.back_btn"))), "commands")], + ], + ) + + +@Gojo.on_message( + command(["markdownhelp", "formatting"]) & filters.private, +) +async def markdownhelp(_, m: Message): + await m.reply_text( + tlang(m, f"plugins.{__PLUGIN__}.help"), + quote=True, + reply_markup=(await gen_formatting_kb(m)), + ) + LOGGER.info(f"{m.from_user.id} used cmd '{m.command}' in {m.chat.id}") + return + + +@Gojo.on_callback_query(filters.regex("^formatting.")) +async def get_formatting_info(_, q: CallbackQuery): + cmd = q.data.split(".")[1] + kb = ikb([[((tlang(q, "general.back_btn")), "back.formatting")]]) + + if cmd == "md_formatting": + await q.message.edit_text( + tlang(q, "formatting.md_help"), + reply_markup=kb, + parse_mode="html", + ) + elif cmd == "fillings": + await q.message.edit_text( + tlang(q, "formatting.filling_help"), + reply_markup=kb, + parse_mode="html", + ) + elif cmd == "random_content": + await q.message.edit_text( + tlang(q, "formatting.random_help"), + reply_markup=kb, + parse_mode="html", + ) + + await q.answer() + return + + +@Gojo.on_callback_query(filters.regex("^back.")) +async def send_mod_help(_, q: CallbackQuery): + await q.message.edit_text( + (tlang(q, "plugins.formatting.help")), + reply_markup=(await gen_formatting_kb(q.message)), + ) + await q.answer() + return + + +__PLUGIN__ = "formatting" + +__alt_name__ = ["formatting", "markdownhelp", "markdown"] +__buttons__ = [ + [ + ("Markdown Formatting", "formatting.md_formatting"), + ("Fillings", "formatting.fillings"), + ], + [("Random Content", "formatting.random_content")], +] diff --git a/Powers/plugins/fun.py b/Powers/plugins/fun.py new file mode 100644 index 0000000000000000000000000000000000000000..4eb824da6829b1189899db2d0f2a043a97341324 --- /dev/null +++ b/Powers/plugins/fun.py @@ -0,0 +1,195 @@ +from html import escape +from secrets import choice +import random + +from pyrogram.errors import MessageTooLong +from pyrogram.types import Message + +from Powers import LOGGER, DEV_USERS +from Powers.bot_class import Gojo +from Powers.tr_engine import tlang +from Powers.utils import fun_strings +from Powers.utils.custom_filters import command +from Powers.utils.extract_user import extract_user + + + +@Gojo.on_message(command("shout")) +async def fun_shout(_, m: Message): + if len(m.text.split()) == 1: + await m.reply_text( + (tlang(m, "general.check_help")), + quote=True, + ) + return + try: + text = " ".join(m.text.split(None, 1)[1]) + result = [" ".join(list(text))] + for pos, symbol in enumerate(text[1:]): + result.append(symbol + " " + " " * pos + symbol) + result = list("\n".join(result)) + result[0] = text[0] + result = "".join(result) + msg = "```\n" + result + "```" + await m.reply_text(msg, parse_mode="markdown") + LOGGER.info(f"{m.from_user.id} shouted in {m.chat.id}") + return + except MessageTooLong as e: + await m.reply_text(f"Error: {e}") + return + + +@Gojo.on_message(command("runs")) +async def fun_run(_, m: Message): + await m.reply_text(choice(fun_strings.RUN_STRINGS)) + LOGGER.info(f"{m.from_user.id} runed in {m.chat.id}") + return + + +@Gojo.on_message(command("slap")) +async def fun_slap(c: Gojo, m: Message): + me = await c.get_me() + + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + + curr_user = escape(m.from_user.first_name) + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if user_id == me.id: + temp = choice(fun_strings.SLAP_GOJO_TEMPLATES) + else: + temp = choice(fun_strings.SLAP_TEMPLATES) + + if user_id: + user1 = curr_user + user2 = escape(user_first_name) + + else: + user1 = me.first_name + user2 = curr_user + + item = choice(fun_strings.ITEMS) + hit = choice(fun_strings.HIT) + throw = choice(fun_strings.THROW) + + reply = temp.format(user1=user1, user2=user2, item=item, hits=hit, throws=throw) + await reply_text(reply) + LOGGER.info(f"{m.from_user.id} slaped in {m.chat.id}") + return + + +@Gojo.on_message(command("roll")) +async def fun_roll(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(choice(range(1, 7))) + LOGGER.info(f"{m.from_user.id} roll in {m.chat.id}") + return + + +@Gojo.on_message(command("toss")) +async def fun_toss(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(choice(fun_strings.TOSS)) + LOGGER.info(f"{m.from_user.id} tossed in {m.chat.id}") + return + +@Gojo.on_message(command("insult")) +async def insult(c : Gojo , m: Message): + try: + user_id, user_first_name, _ = await extract_user(c, m) + except: + return + if user_id in DEV_USERS: + await m.reply_text("Sorry! I can't insult my devs....") + Insult_omp = random.choice(fun_strings.INSULT_STRINGS) + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(Insult_omp) + LOGGER.info(f"{m.from_user.id} insulted {user_first_name} in {m.chat.id}") + + + +@Gojo.on_message(command("shrug")) +async def fun_shrug(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(r"¯\_(ツ)_/¯") + LOGGER.info(f"{m.from_user.id} shruged in {m.chat.id}") + return + + +@Gojo.on_message(command("bluetext")) +async def fun_bluetext(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text( + "/BLUE /TEXT\n/MUST /CLICK\n/I /AM /A /STUPID /ANIMAL /THAT /IS /ATTRACTED /TO /COLORS", + ) + LOGGER.info(f"{m.from_user.id} bluetexted in {m.chat.id}") + return + + +@Gojo.on_message(command("decide")) +async def fun_decide(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(choice(fun_strings.DECIDE)) + LOGGER.info(f"{m.from_user.id} decided in {m.chat.id}") + return + + +@Gojo.on_message(command("react")) +async def fun_table(_, m: Message): + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + await reply_text(choice(fun_strings.REACTIONS)) + LOGGER.info(f"{m.from_user.id} reacted in {m.chat.id}") + return + + +@Gojo.on_message(command("weebify")) +async def weebify(_, m: Message): + if len(m.text.split()) >= 2: + args = m.text.split(None, 1)[1] + elif m.reply_to_message and len(m.text.split()) == 1: + args = m.reply_to_message.text + else: + await m.reply_text( + "Please reply to a message or enter text after command to weebify it.", + ) + return + if not args: + await m.reply_text(tlang(m, "utils.weebify.weebify_what")) + return + + # Use split to convert to list + # Not using list itself becuase black changes it to long format... + normiefont = "a b c d e f g h i j k l m n o p q r s t u v w x y z".split() + weebyfont = "卂 乃 匚 刀 乇 下 厶 卄 工 丁 长 乚 从 𠘨 口 尸 㔿 尺 丂 丅 凵 リ 山 乂 丫 乙".split() + + string = " ".join(args).lower() + for normiecharacter in string: + if normiecharacter in normiefont: + weebycharacter = weebyfont[normiefont.index(normiecharacter)] + string = string.replace(normiecharacter, weebycharacter) + + await m.reply_text( + (tlang(m, "utils.weebify.weebified_string").format(string=string)), + ) + LOGGER.info(f"{m.from_user.id} weebified '{args}' in {m.chat.id}") + return + + +__PLUGIN__ = "fun" + +_DISABLE_CMDS_ = [ + "weebify", + "decide", + "react", + "bluetext", + "toss", + "roll", + "slap", + "runs", + "shout", + "insult", + "shrug", +] diff --git a/Powers/plugins/greetings.py b/Powers/plugins/greetings.py new file mode 100644 index 0000000000000000000000000000000000000000..e085ebffb6660336006f068ccbfd1274d371d52b --- /dev/null +++ b/Powers/plugins/greetings.py @@ -0,0 +1,479 @@ +from html import escape +from secrets import choice + +from pyrogram import filters +from pyrogram.errors import ChatAdminRequired, RPCError +from pyrogram.types import ChatMemberUpdated, InlineKeyboardMarkup, Message + +from Powers import OWNER_ID +from Powers.bot_class import Gojo +from Powers.database.antispam_db import GBan +from Powers.database.greetings_db import Greetings +from Powers.utils.custom_filters import admin_filter, bot_admin_filter, command +from Powers.utils.msg_types import Types, get_wlcm_type +from Powers.utils.parser import escape_markdown, mention_html +from Powers.utils.string import ( + build_keyboard, + escape_invalid_curly_brackets, + parse_button, +) +from Powers.vars import Config + +# Initialize +gdb = GBan() + + +async def escape_mentions_using_curly_brackets_wl( + m: ChatMemberUpdated, + n: bool, + text: str, + parse_words: list, +) -> str: + teks = await escape_invalid_curly_brackets(text, parse_words) + if n: + user = m.new_chat_member.user if m.new_chat_member else m.from_user + else: + user = m.old_chat_member.user if m.old_chat_member else m.from_user + if teks: + teks = teks.format( + first=escape(user.first_name), + last=escape(user.last_name or user.first_name), + fullname=" ".join( + [ + escape(user.first_name), + escape(user.last_name), + ] + if user.last_name + else [escape(user.first_name)], + ), + username=( + "@" + (await escape_markdown(escape(user.username))) + if user.username + else (await (mention_html(escape(user.first_name), user.id))) + ), + mention=await (mention_html(escape(user.first_name), user.id)), + chatname=escape(m.chat.title) + if m.chat.type != "private" + else escape(user.first_name), + id=user.id, + ) + else: + teks = "" + + return teks + + +@Gojo.on_message(command("cleanwelcome") & admin_filter) +async def cleanwlcm(_, m: Message): + db = Greetings(m.chat.id) + status = db.get_current_cleanwelcome_settings() + args = m.text.split(" ", 1) + + if len(args) >= 2: + if args[1].lower() == "on": + db.set_current_cleanwelcome_settings(True) + await m.reply_text("Turned on!") + return + if args[1].lower() == "off": + db.set_current_cleanwelcome_settings(False) + await m.reply_text("Turned off!") + return + await m.reply_text("what are you trying to do ??") + return + await m.reply_text(f"Current settings:- {status}") + return + + +@Gojo.on_message(command("cleangoodbye") & admin_filter) +async def cleangdbye(_, m: Message): + db = Greetings(m.chat.id) + status = db.get_current_cleangoodbye_settings() + args = m.text.split(" ", 1) + + if len(args) >= 2: + if args[1].lower() == "on": + db.set_current_cleangoodbye_settings(True) + await m.reply_text("Turned on!") + return + if args[1].lower() == "off": + db.set_current_cleangoodbye_settings(False) + await m.reply_text("Turned off!") + return + await m.reply_text("what are you trying to do ??") + return + await m.reply_text(f"Current settings:- {status}") + return + + +@Gojo.on_message(command("cleanservice") & admin_filter) +async def cleanservice(_, m: Message): + db = Greetings(m.chat.id) + status = db.get_current_cleanservice_settings() + args = m.text.split(" ", 1) + + if len(args) >= 2: + if args[1].lower() == "on": + db.set_current_cleanservice_settings(True) + await m.reply_text("Turned on!") + return + if args[1].lower() == "off": + db.set_current_cleanservice_settings(False) + await m.reply_text("Turned off!") + return + await m.reply_text("what are you trying to do ??") + return + await m.reply_text(f"Current settings:- {status}") + return + + +@Gojo.on_message(command("setwelcome") & admin_filter & bot_admin_filter) +async def save_wlcm(_, m: Message): + db = Greetings(m.chat.id) + if m and not m.from_user: + return + args = m.text.split(None, 1) + + if len(args) >= 4096: + await m.reply_text( + "Word limit exceed !!", + ) + return + if not (m.reply_to_message and m.reply_to_message.text) and len(m.command) == 0: + await m.reply_text( + "Error: There is no text in here! and only text with buttons are supported currently !", + ) + return + text, msgtype, _ = await get_wlcm_type(m) + + if not m.reply_to_message and msgtype == Types.TEXT and len(m.command) <= 2: + await m.reply_text(f"{m.text}\n\nError: There is no data in here!") + return + + if not text and not msgtype: + await m.reply_text( + "Please provide some data!", + ) + return + + if not msgtype: + await m.reply_text("Please provide some data for this to reply with!") + return + + db.set_welcome_text(text) + await m.reply_text("Saved welcome!") + return + + +@Gojo.on_message(command("setgoodbye") & admin_filter & bot_admin_filter) +async def save_gdbye(_, m: Message): + db = Greetings(m.chat.id) + if m and not m.from_user: + return + args = m.text.split(None, 1) + + if len(args) >= 4096: + await m.reply_text( + "Word limit exceeds !!", + ) + return + if not (m.reply_to_message and m.reply_to_message.text) and len(m.command) == 0: + await m.reply_text( + "Error: There is no text in here! and only text with buttons are supported currently !", + ) + return + text, msgtype, _ = await get_wlcm_type(m) + + if not m.reply_to_message and msgtype == Types.TEXT and len(m.command) <= 2: + await m.reply_text(f"{m.text}\n\nError: There is no data in here!") + return + + if not text and not msgtype: + await m.reply_text( + "Please provide some data!", + ) + return + + if not msgtype: + await m.reply_text("Please provide some data for this to reply with!") + return + + db.set_goodbye_text(text) + await m.reply_text("Saved goodbye!") + return + + +@Gojo.on_message(command("resetgoodbye") & admin_filter & bot_admin_filter) +async def resetgb(_, m: Message): + db = Greetings(m.chat.id) + if m and not m.from_user: + return + text = "Sad to see you leaving {first}.\nTake Care!" + db.set_goodbye_text(text) + await m.reply_text("Ok Done!") + return + + +@Gojo.on_message(command("resetwelcome") & admin_filter & bot_admin_filter) +async def resetwlcm(_, m: Message): + db = Greetings(m.chat.id) + if m and not m.from_user: + return + text = "Hey {first}, welcome to {chatname}!" + db.set_welcome_text(text) + await m.reply_text("Done!") + return + + +@Gojo.on_message(filters.service & filters.group, group=59) +async def cleannnnn(_, m: Message): + db = Greetings(m.chat.id) + clean = db.get_current_cleanservice_settings() + try: + if clean: + await m.delete() + except Exception: + pass + + +@Gojo.on_chat_member_updated(filters.group, group=69) +async def member_has_joined(c: Gojo, member: ChatMemberUpdated): + + if ( + member.new_chat_member + and member.new_chat_member.status not in {"banned", "left", "restricted"} + and not member.old_chat_member + ): + pass + else: + return + + user = member.new_chat_member.user if member.new_chat_member else member.from_user + + db = Greetings(member.chat.id) + banned_users = gdb.check_gban(user.id) + try: + if user.id == Config.BOT_ID: + return + if user.id == OWNER_ID: + await c.send_message( + member.chat.id, + "Wew My Owner has also joined the chat!", + ) + return + if banned_users: + await member.chat.ban_member(user.id) + await c.send_message( + member.chat.id, + f"{user.mention} was globally banned so i banned!", + ) + return + if user.is_bot: + return # ignore bots + except ChatAdminRequired: + return + status = db.get_welcome_status() + oo = db.get_welcome_text() + parse_words = [ + "first", + "last", + "fullname", + "username", + "mention", + "id", + "chatname", + ] + hmm = await escape_mentions_using_curly_brackets_wl(member, True, oo, parse_words) + if status: + tek, button = await parse_button(hmm) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + + if "%%%" in tek: + filter_reply = tek.split("%%%") + teks = choice(filter_reply) + else: + teks = tek + ifff = db.get_current_cleanwelcome_id() + gg = db.get_current_cleanwelcome_settings() + if ifff and gg: + try: + await c.delete_messages(member.chat.id, int(ifff)) + except RPCError: + pass + try: + jj = await c.send_message( + member.chat.id, + text=teks, + reply_markup=button, + disable_web_page_preview=True, + ) + if jj: + db.set_cleanwlcm_id(int(jj.message_id)) + except RPCError as e: + print(e) + return + else: + return + + +@Gojo.on_chat_member_updated(filters.group, group=99) +async def member_has_left(c: Gojo, member: ChatMemberUpdated): + + if ( + not member.new_chat_member + and member.old_chat_member.status not in {"banned", "restricted"} + and member.old_chat_member + ): + pass + else: + return + db = Greetings(member.chat.id) + status = db.get_goodbye_status() + oo = db.get_goodbye_text() + parse_words = [ + "first", + "last", + "fullname", + "id", + "username", + "mention", + "chatname", + ] + + user = member.old_chat_member.user if member.old_chat_member else member.from_user + + hmm = await escape_mentions_using_curly_brackets_wl(member, False, oo, parse_words) + if status: + tek, button = await parse_button(hmm) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + + if "%%%" in tek: + filter_reply = tek.split("%%%") + teks = choice(filter_reply) + else: + teks = tek + ifff = db.get_current_cleangoodbye_id() + iii = db.get_current_cleangoodbye_settings() + if ifff and iii: + try: + await c.delete_messages(member.chat.id, int(ifff)) + except RPCError: + pass + if user.id == OWNER_ID: + await c.send_message( + member.chat.id, + "Will miss you :)", + ) + return + try: + ooo = await c.send_message( + member.chat.id, + text=teks, + reply_markup=button, + disable_web_page_preview=True, + ) + if ooo: + db.set_cleangoodbye_id(int(ooo.message_id)) + return + except RPCError as e: + print(e) + return + else: + return + + +@Gojo.on_message(command("welcome") & admin_filter) +async def welcome(c: Gojo, m: Message): + db = Greetings(m.chat.id) + status = db.get_welcome_status() + oo = db.get_welcome_text() + args = m.text.split(" ", 1) + + if m and not m.from_user: + return + + if len(args) >= 2: + if args[1].lower() == "noformat": + await m.reply_text( + f"""Current welcome settings:- + Welcome power: {status} + Clean Welcome: {db.get_current_cleanwelcome_settings()} + Cleaning service: {db.get_current_cleanservice_settings()} + Welcome text in no formating: + """, + ) + await c.send_message(m.chat.id, text=oo, parse_mode=None) + return + if args[1].lower() == "on": + db.set_current_welcome_settings(True) + await m.reply_text("Turned on!") + return + if args[1].lower() == "off": + db.set_current_welcome_settings(False) + await m.reply_text("Turned off!") + return + await m.reply_text("what are you trying to do ??") + return + await m.reply_text( + f"""Current welcome settings:- + Welcome power: {status} + Clean Welcome: {db.get_current_cleanwelcome_settings()} + Cleaning service: {db.get_current_cleanservice_settings()} + Welcome text: + """, + ) + tek, button = await parse_button(oo) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + await c.send_message(m.chat.id, text=tek, reply_markup=button) + return + + +@Gojo.on_message(command("goodbye") & admin_filter) +async def goodbye(c: Gojo, m: Message): + db = Greetings(m.chat.id) + status = db.get_goodbye_status() + oo = db.get_goodbye_text() + args = m.text.split(" ", 1) + if m and not m.from_user: + return + if len(args) >= 2: + if args[1].lower() == "noformat": + await m.reply_text( + f"""Current goodbye settings:- + Goodbye power: {status} + Clean Goodbye: {db.get_current_cleangoodbye_settings()} + Cleaning service: {db.get_current_cleanservice_settings()} + Goodbye text in no formating: + """, + ) + await c.send_message(m.chat.id, text=oo, parse_mode=None) + return + if args[1].lower() == "on": + db.set_current_goodbye_settings(True) + await m.reply_text("Turned on!") + return + if args[1].lower() == "off": + db.set_current_goodbye_settings(False) + await m.reply_text("Turned off!") + return + await m.reply_text("what are you trying to do ??") + return + await m.reply_text( + f"""Current Goodbye settings:- + Goodbye power: {status} + Clean Goodbye: {db.get_current_cleangoodbye_settings()} + Cleaning service: {db.get_current_cleanservice_settings()} + Goodbye text: + """, + ) + tek, button = await parse_button(oo) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + await c.send_message(m.chat.id, text=tek, reply_markup=button) + return + + +__PLUGIN__ = "greetings" +__alt_name__ = ["welcome", "goodbye", "cleanservice"] diff --git a/Powers/plugins/initial.py b/Powers/plugins/initial.py new file mode 100644 index 0000000000000000000000000000000000000000..9cd94195234a84423497b8f90590123ed3ba8c5e --- /dev/null +++ b/Powers/plugins/initial.py @@ -0,0 +1,114 @@ +from pyrogram import filters +from pyrogram.errors import RPCError +from pyrogram.types import Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.approve_db import Approve +from Powers.database.blacklist_db import Blacklist +from Powers.database.chats_db import Chats +from Powers.database.disable_db import Disabling +from Powers.database.filters_db import Filters +from Powers.database.greetings_db import Greetings +from Powers.database.lang_db import Langs +from Powers.database.notes_db import Notes, NotesSettings +from Powers.database.pins_db import Pins +from Powers.database.reporting_db import Reporting +from Powers.database.rules_db import Rules +from Powers.database.users_db import Users + + +@Gojo.on_message(filters.group, group=4) +async def initial_works(_, m: Message): + chatdb = Chats(m.chat.id) + try: + if m.migrate_to_chat_id or m.migrate_from_chat_id: + new_chat = m.migrate_to_chat_id or m.chat.id + try: + await migrate_chat(m, new_chat) + except RPCError as ef: + LOGGER.error(ef) + return + elif m.reply_to_message and not m.forward_from: + chatdb.update_chat( + m.chat.title, + m.reply_to_message.from_user.id, + ) + Users(m.reply_to_message.from_user.id).update_user( + ( + f"{m.reply_to_message.from_user.first_name} {m.reply_to_message.from_user.last_name}" + if m.reply_to_message.from_user.last_name + else m.reply_to_message.from_user.first_name + ), + m.reply_to_message.from_user.username, + ) + elif m.forward_from and not m.reply_to_message: + chatdb.update_chat( + m.chat.title, + m.forward_from.id, + ) + Users(m.forward_from.id).update_user( + ( + f"{m.forward_from.first_name} {m.forward_from.last_name}" + if m.forward_from.last_name + else m.forward_from.first_name + ), + m.forward_from.username, + ) + elif m.reply_to_message: + chatdb.update_chat( + m.chat.title, + m.reply_to_message.forward_from.id, + ) + Users(m.forward_from.id).update_user( + ( + f"{m.reply_to_message.forward_from.first_name} {m.reply_to_message.forward_from.last_name}" + if m.reply_to_message.forward_from.last_name + else m.reply_to_message.forward_from.first_name + ), + m.forward_from.username, + ) + else: + chatdb.update_chat(m.chat.title, m.from_user.id) + Users(m.from_user.id).update_user( + ( + f"{m.from_user.first_name} {m.from_user.last_name}" + if m.from_user.last_name + else m.from_user.first_name + ), + m.from_user.username, + ) + except AttributeError: + pass # Skip attribute errors! + return + + +async def migrate_chat(m: Message, new_chat: int) -> None: + LOGGER.info(f"Migrating from {m.chat.id} to {new_chat}...") + langdb = Langs(m.chat.id) + notedb = Notes() + gdb = Greetings(m.chat.id) + ruledb = Rules(m.chat.id) + userdb = Users(m.chat.id) + chatdb = Chats(m.chat.id) + bldb = Blacklist(m.chat.id) + approvedb = Approve(m.chat.id) + reportdb = Reporting(m.chat.id) + notes_settings = NotesSettings() + pins_db = Pins(m.chat.id) + fldb = Filters() + disabl = Disabling(m.chat.id) + disabl.migrate_chat(new_chat) + gdb.migrate_chat(new_chat) + chatdb.migrate_chat(new_chat) + userdb.migrate_chat(new_chat) + langdb.migrate_chat(new_chat) + ruledb.migrate_chat(new_chat) + bldb.migrate_chat(new_chat) + notedb.migrate_chat(m.chat.id, new_chat) + approvedb.migrate_chat(new_chat) + reportdb.migrate_chat(new_chat) + notes_settings.migrate_chat(m.chat.id, new_chat) + pins_db.migrate_chat(new_chat) + fldb.migrate_chat(m.chat.id, new_chat) + LOGGER.info(f"Successfully migrated from {m.chat.id} to {new_chat}!") diff --git a/Powers/plugins/langs.py b/Powers/plugins/langs.py new file mode 100644 index 0000000000000000000000000000000000000000..008f705aa0e8891dde9c765a84219c4536f35a34 --- /dev/null +++ b/Powers/plugins/langs.py @@ -0,0 +1,119 @@ +from asyncio import sleep + +from pyrogram import filters +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.lang_db import Langs +from Powers.tr_engine import lang_dict, tlang +from Powers.utils.custom_filters import admin_filter, command +from Powers.utils.kbhelpers import ikb + + +async def gen_langs_kb(): + langs = sorted(list(lang_dict.keys())) + return [ + [ + ( + f"{lang_dict[lang]['main']['language_flag']} {lang_dict[lang]['main']['language_name']} ({lang_dict[lang]['main']['lang_sample']})", + f"set_lang.{lang}", + ) + for lang in langs + ], + [ + ( + "🌎 Help us with translations!", + "https://crowdin.com/project/Gojo_Satarou", + "url", + ), + ], + ] + + +@Gojo.on_callback_query(filters.regex("^chlang$")) +async def chlang_callback(_, q: CallbackQuery): + kb = await gen_langs_kb() + kb.append([(f"« {(tlang(q, 'general.back_btn'))}", "start_back")]) + + await q.message.edit_text( + (tlang(q, "langs.changelang")), + reply_markup=ikb(kb), + ) + await q.answer() + return + + +@Gojo.on_callback_query(filters.regex("^close$"), group=3) +async def close_btn_callback(_, q: CallbackQuery): + await q.message.delete() + try: + await q.message.reply_to_message.delete() + except Exception as ef: + LOGGER.error(f"Error: Cannot delete message\n{ef}") + await q.answer() + return + + +@Gojo.on_callback_query(filters.regex("^set_lang.")) +async def set_lang_callback(_, q: CallbackQuery): + lang_code = q.data.split(".")[1] + + Langs(q.message.chat.id).set_lang(lang_code) + await sleep(0.1) + + if q.message.chat.type == "private": + keyboard = ikb([[(f"« {(tlang(q, 'general.back_btn'))}", "start_back")]]) + else: + keyboard = None + await q.message.edit_text( + f"🌐 {((tlang(q, 'langs.changed')).format(lang_code=lang_code))}", + reply_markup=keyboard, + ) + await q.answer() + return + + +@Gojo.on_message( + command(["lang", "setlang"]) & (admin_filter | filters.private), + group=7, +) +async def set_lang(_, m: Message): + args = m.text.split() + + if len(args) > 2: + await m.reply_text(tlang(m, "langs.correct_usage")) + return + if len(args) == 2: + lang_code = args[1] + avail_langs = set(lang_dict.keys()) + if lang_code not in avail_langs: + await m.reply_text( + f"Please choose a valid language code from: {', '.join(avail_langs)}", + ) + return + Langs(m.chat.id).set_lang(lang_code) + LOGGER.info(f"{m.from_user.id} change language to {lang_code} in {m.chat.id}") + await m.reply_text( + f"🌐 {((tlang(m, 'langs.changed')).format(lang_code=lang_code))}", + ) + return + await m.reply_text( + (tlang(m, "langs.changelang")), + reply_markup=ikb(await gen_langs_kb()), + ) + return + + +__PLUGIN__ = "language" + +__alt_name__ = ["lang", "langs", "languages"] +__buttons__ = [ + [ + ( + "🌎 Help us with translations!", + "https://t.me/gojo_updates", + "url", + ), + ], +] diff --git a/Powers/plugins/locks.py b/Powers/plugins/locks.py new file mode 100644 index 0000000000000000000000000000000000000000..a04da1e3e17a413cadad774a97ae8d2ca1944d40 --- /dev/null +++ b/Powers/plugins/locks.py @@ -0,0 +1,322 @@ +from asyncio import sleep + +from pyrogram.errors import ChatAdminRequired, ChatNotModified, RPCError +from pyrogram.types import ChatPermissions, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.approve_db import Approve +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import command, restrict_filter + + +@Gojo.on_message(command("locktypes")) +async def lock_types(_, m: Message): + await m.reply_text( + ( + "**Lock Types:**\n" + " - `all` = Everything\n" + " - `msg` = Messages\n" + " - `media` = Media, such as Photo and Video.\n" + " - `polls` = Polls\n" + " - `invite` = Add users to Group\n" + " - `pin` = Pin Messages\n" + " - `info` = Change Group Info\n" + " - `webprev` = Web Page Previews\n" + " - `inlinebots`, `inline` = Inline bots\n" + " - `animations` = Animations\n" + " - `games` = Game Bots\n" + " - `stickers` = Stickers" + ), + ) + return + + +@Gojo.on_message(command("lock") & restrict_filter) +async def lock_perm(c: Gojo, m: Message): + if len(m.text.split()) < 2: + await m.reply_text("Please enter a permission to lock!") + return + lock_type = m.text.split(None, 1)[1] + chat_id = m.chat.id + + if not lock_type: + await m.reply_text(tlang(m, "locks.locks_perm_sp")) + return + + get_perm = m.chat.permissions + + msg = get_perm.can_send_messages + media = get_perm.can_send_media_messages + webprev = get_perm.can_add_web_page_previews + polls = get_perm.can_send_polls + info = get_perm.can_change_info + invite = get_perm.can_invite_users + pin = get_perm.can_pin_messages + stickers = animations = games = inlinebots = None + + if lock_type == "all": + try: + await c.set_chat_permissions(chat_id, ChatPermissions()) + LOGGER.info(f"{m.from_user.id} locked all permissions in {m.chat.id}") + except ChatNotModified: + pass + except ChatAdminRequired: + await m.reply_text(tlang(m, "general.no_perm_admin")) + await m.reply_text("🔒 " + (tlang(m, "locks.lock_all"))) + await prevent_approved(m) + return + + if lock_type == "msg": + msg = False + perm = "messages" + + elif lock_type == "media": + media = False + perm = "audios, documents, photos, videos, video notes, voice notes" + + elif lock_type == "stickers": + stickers = False + perm = "stickers" + + elif lock_type == "animations": + animations = False + perm = "animations" + + elif lock_type == "games": + games = False + perm = "games" + + elif lock_type in ("inlinebots", "inline"): + inlinebots = False + perm = "inline bots" + + elif lock_type == "webprev": + webprev = False + perm = "web page previews" + + elif lock_type == "polls": + polls = False + perm = "polls" + + elif lock_type == "info": + info = False + perm = "info" + + elif lock_type == "invite": + invite = False + perm = "invite" + + elif lock_type == "pin": + pin = False + perm = "pin" + + else: + await m.reply_text(tlang(m, "locks.invalid_lock")) + return + + try: + await c.set_chat_permissions( + chat_id, + ChatPermissions( + can_send_messages=msg, + can_send_media_messages=media, + can_send_other_messages=any([stickers, animations, games, inlinebots]), + can_add_web_page_previews=webprev, + can_send_polls=polls, + can_change_info=info, + can_invite_users=invite, + can_pin_messages=pin, + ), + ) + LOGGER.info(f"{m.from_user.id} locked selected permissions in {m.chat.id}") + except ChatNotModified: + pass + except ChatAdminRequired: + await m.reply_text(tlang(m, "general.no_perm_admin")) + await m.reply_text( + "🔒 " + (tlang(m, "locks.locked_perm").format(perm=perm)), + ) + await prevent_approved(m) + return + + +@Gojo.on_message(command("locks") & restrict_filter) +async def view_locks(_, m: Message): + chkmsg = await m.reply_text(tlang(m, "locks.check_perm_msg")) + v_perm = m.chat.permissions + + async def convert_to_emoji(val: bool): + if val: + return "✅" + return "❌" + + vmsg = await convert_to_emoji(v_perm.can_send_messages) + vmedia = await convert_to_emoji(v_perm.can_send_media_messages) + vother = await convert_to_emoji(v_perm.can_send_other_messages) + vwebprev = await convert_to_emoji(v_perm.can_add_web_page_previews) + vpolls = await convert_to_emoji(v_perm.can_send_polls) + vinfo = await convert_to_emoji(v_perm.can_change_info) + vinvite = await convert_to_emoji(v_perm.can_invite_users) + vpin = await convert_to_emoji(v_perm.can_pin_messages) + + if v_perm is not None: + try: + permission_view_str = (tlang(m, "locks.view_perm")).format( + vmsg=vmsg, + vmedia=vmedia, + vother=vother, + vwebprev=vwebprev, + vpolls=vpolls, + vinfo=vinfo, + vinvite=vinvite, + vpin=vpin, + ) + LOGGER.info(f"{m.from_user.id} used locks cmd in {m.chat.id}") + await chkmsg.edit_text(permission_view_str) + + except RPCError as e_f: + await chkmsg.edit_text(tlang(m, "general.something_wrong")) + await m.reply_text(e_f) + return + + +@Gojo.on_message(command("unlock") & restrict_filter) +async def unlock_perm(c: Gojo, m: Message): + if len(m.text.split()) < 2: + await m.reply_text("Please enter a permission to unlock!") + return + unlock_type = m.text.split(None, 1)[1] + chat_id = m.chat.id + + if not unlock_type: + await m.reply_text(tlang(m, "locks.unlocks_perm_sp")) + return + + if unlock_type == "all": + try: + await c.set_chat_permissions( + chat_id, + ChatPermissions( + can_send_messages=True, + can_send_media_messages=True, + can_send_other_messages=True, + can_add_web_page_previews=True, + can_send_polls=True, + can_change_info=True, + can_invite_users=True, + can_pin_messages=True, + ), + ) + LOGGER.info(f"{m.from_user.id} unlocked all permissions in {m.chat.id}") + except ChatNotModified: + pass + except ChatAdminRequired: + await m.reply_text(tlang(m, "general.no_perm_admin")) + await m.reply_text("🔓 " + (tlang(m, "locks.unlock_all"))) + await prevent_approved(m) + return + + get_uperm = m.chat.permissions + + umsg = get_uperm.can_send_messages + umedia = get_uperm.can_send_media_messages + uwebprev = get_uperm.can_add_web_page_previews + upolls = get_uperm.can_send_polls + uinfo = get_uperm.can_change_info + uinvite = get_uperm.can_invite_users + upin = get_uperm.can_pin_messages + ustickers = uanimations = ugames = uinlinebots = None + + if unlock_type == "msg": + umsg = True + uperm = "messages" + + elif unlock_type == "media": + umedia = True + uperm = "audios, documents, photos, videos, video notes, voice notes" + + elif unlock_type == "stickers": + ustickers = True + uperm = "stickers" + + elif unlock_type == "animations": + uanimations = True + uperm = "animations" + + elif unlock_type == "games": + ugames = True + uperm = "games" + + elif unlock_type in ("inlinebots", "inline"): + uinlinebots = True + uperm = "inline bots" + + elif unlock_type == "webprev": + uwebprev = True + uperm = "web page previews" + + elif unlock_type == "polls": + upolls = True + uperm = "polls" + + elif unlock_type == "info": + uinfo = True + uperm = "info" + + elif unlock_type == "invite": + uinvite = True + uperm = "invite" + + elif unlock_type == "pin": + upin = True + uperm = "pin" + + else: + await m.reply_text(tlang(m, "locks.invalid_lock")) + return + + try: + LOGGER.info(f"{m.from_user.id} unlocked selected permissions in {m.chat.id}") + await c.set_chat_permissions( + chat_id, + ChatPermissions( + can_send_messages=umsg, + can_send_media_messages=umedia, + can_send_other_messages=any( + [ustickers, uanimations, ugames, uinlinebots], + ), + can_add_web_page_previews=uwebprev, + can_send_polls=upolls, + can_change_info=uinfo, + can_invite_users=uinvite, + can_pin_messages=upin, + ), + ) + except ChatNotModified: + pass + except ChatAdminRequired: + await m.reply_text(tlang(m, "general.no_perm_admin")) + await m.reply_text( + "🔓 " + (tlang(m, "locks.unlocked_perm").format(uperm=uperm)), + ) + await prevent_approved(m) + return + + +async def prevent_approved(m: Message): + approved_users = Approve(m.chat.id).list_approved() + ul = [user[0] for user in approved_users] + for i in ul: + try: + await m.chat.unban_member(user_id=i) + except (ChatAdminRequired, ChatNotModified, RPCError): + continue + LOGGER.info(f"Approved {i} in {m.chat.id}") + await sleep(0.1) + return + + +__PLUGIN__ = "locks" + +__alt_name__ = ["grouplock", "lock", "grouplocks"] diff --git a/Powers/plugins/muting.py b/Powers/plugins/muting.py new file mode 100644 index 0000000000000000000000000000000000000000..a5fc3fa10968f4f25ad4a77c001ea9a5125f5f42 --- /dev/null +++ b/Powers/plugins/muting.py @@ -0,0 +1,609 @@ +from pyrogram.errors import ( + ChatAdminRequired, + RightForbidden, + RPCError, + UserNotParticipant, +) +from pyrogram.filters import regex +from pyrogram.types import ( + CallbackQuery, + ChatPermissions, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +from Powers import LOGGER, OWNER_ID, SUPPORT_GROUP, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, admin_cache_reload +from Powers.utils.custom_filters import command, restrict_filter +from Powers.utils.extract_user import extract_user +from Powers.utils.parser import mention_html +from Powers.utils.string import extract_time +from Powers.vars import Config + + +@Gojo.on_message(command("tmute") & restrict_filter) +async def tmute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to mute !") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + r_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to mute this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + + reason = split_reason[1] if len(split_reason) > 1 else "" + + mutetime = await extract_time(m, time_val) + + if not mutetime: + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + mutetime, + ) + LOGGER.info(f"{m.from_user.id} tmuted {user_id} in {m.chat.id}") + txt = (tlang(m, "admin.mute.muted_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + muted=(await mention_html(user_first_name, user_id)), + ) + if reason: + txt += f"\nReason: {reason}" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unmute", + callback_data=f"unmute_={user_id}", + ), + ], + ], + ) + await m.reply_text(txt, reply_markup=keyboard, reply_to_message_id=r_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("dtmute") & restrict_filter) +async def dtmute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + + if not m.reply_to_message: + return await m.reply_text("No replied message and user to delete and mute!") + + reason = None + user_id = m.reply_to_message.from_user.id + user_first_name = m.reply_to_message.from_user.first_name + + if not user_id: + await m.reply_text("Cannot find user to mute !") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to mute this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + reason = split_reason[1] if len(split_reason) > 1 else "" + + mutetime = await extract_time(m, time_val) + + if not mutetime: + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + mutetime, + ) + LOGGER.info(f"{m.from_user.id} dtmuted {user_id} in {m.chat.id}") + await m.reply_to_message.delete() + txt = (tlang(m, "admin.mute.muted_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + muted=(await mention_html(user_first_name, user_id)), + ) + if reason: + txt += f"\nReason: {reason}" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unmute", + callback_data=f"unmute_={user_id}", + ), + ], + ], + ) + await c.send_message(m.chat.id, txt, reply_markup=keyboard) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("stmute") & restrict_filter) +async def stmute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to mute !") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + if m.reply_to_message and len(m.text.split()) >= 2: + reason = m.text.split(None, 2)[1] + elif not m.reply_to_message and len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + await m.reply_text("Read /help !!") + return + + if not reason: + await m.reply_text("You haven't specified a time to mute this user for!") + return + + split_reason = reason.split(None, 1) + time_val = split_reason[0].lower() + reason = split_reason[1] if len(split_reason) > 1 else "" + + mutetime = await extract_time(m, time_val) + + if not mutetime: + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + mutetime, + ) + LOGGER.info(f"{m.from_user.id} stmuted {user_id} in {m.chat.id}") + await m.delete() + if m.reply_to_message: + await m.reply_to_message.delete() + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("mute") & restrict_filter) +async def mute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + + reason = None + if m.reply_to_message: + r_id = m.reply_to_message.message_id + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + r_id = m.message_id + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to mute") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + ) + LOGGER.info(f"{m.from_user.id} muted {user_id} in {m.chat.id}") + txt = (tlang(m, "admin.mute.muted_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + muted=(await mention_html(user_first_name, user_id)), + ) + if reason: + txt += f"\nReason: {reason}" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unmute", + callback_data=f"unmute_={user_id}", + ), + ], + ], + ) + await m.reply_text(txt, reply_markup=keyboard, reply_to_message_id=r_id) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("smute") & restrict_filter) +async def smute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + + try: + user_id, _, _ = await extract_user(c, m) + except Exception: + return + + if not user_id: + await m.reply_text("Cannot find user to mute") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + ) + LOGGER.info(f"{m.from_user.id} smuted {user_id} in {m.chat.id}") + await m.delete() + if m.reply_to_message: + await m.reply_to_message.delete() + return + return + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("dmute") & restrict_filter) +async def dmute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't mute nothing!") + return + if not m.reply_to_message: + return await m.reply_text("No replied message and user to delete and mute!") + + reason = None + if m.reply_to_message: + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + user_id = m.reply_to_message.from_user.id + user_first_name = m.reply_to_message.from_user.first_name + + if not user_id: + await m.reply_text("Cannot find user to mute") + return + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I mute myself?") + return + + if user_id in SUPPORT_STAFF: + LOGGER.info( + f"{m.from_user.id} trying to mute {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = await admin_cache_reload(m, "mute") + + if user_id in admins_group: + await m.reply_text(tlang(m, "admin.mute.admin_cannot_mute")) + return + + try: + await m.chat.restrict_member( + user_id, + ChatPermissions(), + ) + LOGGER.info(f"{m.from_user.id} dmuted {user_id} in {m.chat.id}") + await m.reply_to_message.delete() + txt = (tlang(m, "admin.mute.muted_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + muted=(await mention_html(user_first_name, user_id)), + ) + if reason: + txt += f"\nReason: {reason}" + keyboard = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Unmute", + callback_data=f"unmute_={user_id}", + ), + ], + ], + ) + await c.send_message(m.chat.id, txt, reply_markup=keyboard) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "admin.mute.bot_no_right")) + except UserNotParticipant: + await m.reply_text("How can I mute a user who is not a part of this chat?") + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("unmute") & restrict_filter) +async def unmute_usr(c: Gojo, m: Message): + if len(m.text.split()) == 1 and not m.reply_to_message: + await m.reply_text("I can't unmute nothing!") + return + + try: + user_id, user_first_name, _ = await extract_user(c, m) + except Exception: + return + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I unmute myself if you are using me?") + return + + try: + await m.chat.unban_member(user_id) + LOGGER.info(f"{m.from_user.id} unmuted {user_id} in {m.chat.id}") + await m.reply_text( + (tlang(m, "admin.unmute.unmuted_user")).format( + admin=(await mention_html(m.from_user.first_name, m.from_user.id)), + unmuted=(await mention_html(user_first_name, user_id)), + ), + ) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except UserNotParticipant: + await m.reply_text("How can I unmute a user who is not a part of this chat?") + except RightForbidden: + await m.reply_text(tlang(m, "admin.unmute.bot_no_right")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + return + + +@Gojo.on_callback_query(regex("^unmute_")) +async def unmutebutton(c: Gojo, q: CallbackQuery): + splitter = (str(q.data).replace("unmute_", "")).split("=") + user_id = int(splitter[1]) + user = await q.message.chat.get_member(q.from_user.id) + + if not user.can_restrict_members and user.id != OWNER_ID: + await q.answer( + "You don't have enough permission to do this!\nStay in your limits!", + show_alert=True, + ) + return + whoo = await c.get_users(user_id) + try: + await q.message.chat.unban_member(user_id) + except RPCError as e: + await q.message.edit_text(f"Error: {e}") + return + await q.message.edit_text(f"{q.from_user.mention} unmuted {whoo.mention}!") + return + + +__PLUGIN__ = "muting" + +__alt_name__ = [ + "mute", + "tmute", + "unmute", +] diff --git a/Powers/plugins/notes.py b/Powers/plugins/notes.py new file mode 100644 index 0000000000000000000000000000000000000000..05c7c89724f1d06559a68b4c8138cc630658de57 --- /dev/null +++ b/Powers/plugins/notes.py @@ -0,0 +1,426 @@ +from secrets import choice +from traceback import format_exc + +from pyrogram import filters +from pyrogram.errors import RPCError +from pyrogram.types import CallbackQuery, InlineKeyboardMarkup, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.notes_db import Notes, NotesSettings +from Powers.utils.cmd_senders import send_cmd +from Powers.utils.custom_filters import admin_filter, command, owner_filter +from Powers.utils.kbhelpers import ikb +from Powers.utils.msg_types import Types, get_note_type +from Powers.utils.string import ( + build_keyboard, + escape_mentions_using_curly_brackets, + parse_button, +) +from Powers.vars import Config + +# Initialise +db = Notes() +db_settings = NotesSettings() + + +@Gojo.on_message(command("save") & admin_filter & ~filters.bot) +async def save_note(_, m: Message): + existing_notes = {i[0] for i in db.get_all_notes(m.chat.id)} + name, text, data_type, content = await get_note_type(m) + total_notes = db.get_all_notes(m.chat.id) + + if len(total_notes) >= 1000: + await m.reply_text( + "Only 1000 Notes are allowed per chat!\nTo add more Notes, remove the existing ones.", + ) + return + + if not name: + await m.reply_text( + f"{m.text}\n\nError: You must give a name for this note!", + ) + return + note_name = name.lower() + if note_name in existing_notes: + await m.reply_text(f"This note ({note_name}) already exists!") + return + + if note_name.startswith("<") or note_name.startswith(">"): + await m.reply_text("Cannot save a note which starts with '<' or '>'") + return + + if not m.reply_to_message and data_type == Types.TEXT and len(m.text.split()) < 3: + await m.reply_text(f"{m.text}\n\nError: There is no text in here!") + return + + if not data_type: + await m.reply_text( + f"{m.text}\n\nError: There is no data in here!", + ) + return + + db.save_note(m.chat.id, note_name, text, data_type, content) + LOGGER.info(f"{m.from_user.id} saved note ({note_name}) in {m.chat.id}") + await m.reply_text( + f"Saved note {note_name}!\nGet it with /get {note_name} or #{note_name}", + ) + return + + +async def get_note_func(c: Gojo, m: Message, note_name, priv_notes_status): + """Get the note in normal mode, with parsing enabled.""" + reply_text = m.reply_to_message.reply_text if m.reply_to_message else m.reply_text + reply_msg_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + if m and not m.from_user: + return + + if priv_notes_status: + + note_hash = next(i[1] for i in db.get_all_notes(m.chat.id) if i[0] == note_name) + await reply_text( + f"Click on the button to get the note {note_name}", + reply_markup=ikb( + [ + [ + ( + "Click Me!", + f"https://t.me/{Config.BOT_USERNAME}?start=note_{m.chat.id}_{note_hash}", + "url", + ), + ], + ], + ), + ) + return + + getnotes = db.get_note(m.chat.id, note_name) + + msgtype = getnotes["msgtype"] + if not msgtype: + await reply_text("Error: Cannot find a type for this note!!") + return + + try: + # support for random notes texts + splitter = "%%%" + note_reply = getnotes["note_value"].split(splitter) + note_reply = choice(note_reply) + except KeyError: + note_reply = "" + + parse_words = [ + "first", + "last", + "fullname", + "id", + "username", + "mention", + "chatname", + ] + text = await escape_mentions_using_curly_brackets(m, note_reply, parse_words) + teks, button = await parse_button(text) + button = await build_keyboard(button) + button = InlineKeyboardMarkup(button) if button else None + textt = teks + + try: + if msgtype == Types.TEXT: + if button: + try: + await reply_text( + textt, + # parse_mode="markdown", + reply_markup=button, + disable_web_page_preview=True, + quote=True, + ) + return + except RPCError as ef: + await reply_text( + "An error has occured! Cannot parse note.", + quote=True, + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + else: + await reply_text( + textt, + # parse_mode="markdown", + quote=True, + disable_web_page_preview=True, + ) + return + elif msgtype in ( + Types.STICKER, + Types.VIDEO_NOTE, + Types.CONTACT, + Types.ANIMATED_STICKER, + ): + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + reply_markup=button, + reply_to_message_id=reply_msg_id, + ) + elif button: + try: + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + caption=textt, + # parse_mode="markdown", + reply_markup=button, + reply_to_message_id=reply_msg_id, + ) + return + except RPCError as ef: + await m.reply_text( + textt, + # parse_mode="markdown", + reply_markup=button, + disable_web_page_preview=True, + reply_to_message_id=reply_msg_id, + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + else: + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + caption=textt, + # parse_mode="markdown", + reply_markup=button, + reply_to_message_id=reply_msg_id, + ) + LOGGER.info( + f"{m.from_user.id} fetched note {note_name} (type - {getnotes}) in {m.chat.id}", + ) + except Exception as e: + await m.reply_text(f"Error in notes: {e}") + return + + +async def get_raw_note(c: Gojo, m: Message, note: str): + """Get the note in raw format, so it can updated by just copy and pasting.""" + all_notes = {i[0] for i in db.get_all_notes(m.chat.id)} + if m and not m.from_user: + return + + if note not in all_notes: + await m.reply_text("This note does not exists!") + return + + getnotes = db.get_note(m.chat.id, note) + msg_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + msgtype = getnotes["msgtype"] + if not getnotes: + await m.reply_text("Error: Cannot find a type for this note!!") + return + + if msgtype == Types.TEXT: + teks = getnotes["note_value"] + await m.reply_text(teks, parse_mode=None, reply_to_message_id=msg_id) + elif msgtype in ( + Types.STICKER, + Types.VIDEO_NOTE, + Types.CONTACT, + Types.ANIMATED_STICKER, + ): + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + reply_to_message_id=msg_id, + ) + else: + teks = getnotes["note_value"] or "" + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + caption=teks, + parse_mode=None, + reply_to_message_id=msg_id, + ) + LOGGER.info( + f"{m.from_user.id} fetched raw note {note} (type - {getnotes}) in {m.chat.id}", + ) + return + + +@Gojo.on_message(filters.regex(r"^#[^\s]+") & filters.group & ~filters.bot) +async def hash_get(c: Gojo, m: Message): + # If not from user, then return + + try: + note = (m.text[1:]).lower() + except TypeError: + return + + all_notes = {i[0] for i in db.get_all_notes(m.chat.id)} + + if note not in all_notes: + # don't reply to all messages starting with # + return + + priv_notes_status = db_settings.get_privatenotes(m.chat.id) + await get_note_func(c, m, note, priv_notes_status) + return + + +@Gojo.on_message(command("get") & filters.group & ~filters.bot) +async def get_note(c: Gojo, m: Message): + + if len(m.text.split()) == 2: + priv_notes_status = db_settings.get_privatenotes(m.chat.id) + note = ((m.text.split())[1]).lower() + all_notes = {i[0] for i in db.get_all_notes(m.chat.id)} + + if note not in all_notes: + await m.reply_text("This note does not exists!") + return + + await get_note_func(c, m, note, priv_notes_status) + elif len(m.text.split()) == 3 and (m.text.split())[2] in ["noformat", "raw"]: + note = ((m.text.split())[1]).lower() + await get_raw_note(c, m, note) + else: + await m.reply_text("Give me a note tag!") + return + + return + + +@Gojo.on_message(command(["privnotes", "privatenotes"]) & admin_filter & ~filters.bot) +async def priv_notes(_, m: Message): + + chat_id = m.chat.id + if len(m.text.split()) == 2: + option = (m.text.split())[1] + if option in ("on", "yes"): + db_settings.set_privatenotes(chat_id, True) + LOGGER.info(f"{m.from_user.id} enabled privatenotes in {m.chat.id}") + msg = "Set private notes to On" + elif option in ("off", "no"): + db_settings.set_privatenotes(chat_id, False) + LOGGER.info(f"{m.from_user.id} disabled privatenotes in {m.chat.id}") + msg = "Set private notes to Off" + else: + msg = "Enter correct option" + await m.reply_text(msg) + elif len(m.text.split()) == 1: + curr_pref = db_settings.get_privatenotes(m.chat.id) + msg = msg = f"Private Notes: {curr_pref}" + LOGGER.info(f"{m.from_user.id} fetched privatenotes preference in {m.chat.id}") + await m.reply_text(msg) + else: + await m.replt_text("Check help on how to use this command!") + + return + + +@Gojo.on_message(command("notes") & filters.group & ~filters.bot) +async def local_notes(_, m: Message): + LOGGER.info(f"{m.from_user.id} listed all notes in {m.chat.id}") + getnotes = db.get_all_notes(m.chat.id) + + if not getnotes: + await m.reply_text(f"There are no notes in {m.chat.title}.") + return + + msg_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + curr_pref = db_settings.get_privatenotes(m.chat.id) + if curr_pref: + + pm_kb = ikb( + [ + [ + ( + "All Notes", + f"https://t.me/{Config.BOT_USERNAME}?start=notes_{m.chat.id}", + "url", + ), + ], + ], + ) + await m.reply_text( + "Click on the button below to get notes!", + quote=True, + reply_markup=pm_kb, + ) + return + + rply = f"Notes in {m.chat.title}:\n" + for x in getnotes: + rply += f"-> #{x[0]}\n" + rply += "\nYou can get a note by #notename or /get notename" + + await m.reply_text(rply, reply_to_message_id=msg_id) + return + + +@Gojo.on_message(command("clear") & admin_filter & ~filters.bot) +async def clear_note(_, m: Message): + + if len(m.text.split()) <= 1: + await m.reply_text("What do you want to clear?") + return + + note = m.text.split()[1].lower() + getnote = db.rm_note(m.chat.id, note) + LOGGER.info(f"{m.from_user.id} cleared note ({note}) in {m.chat.id}") + if not getnote: + await m.reply_text("This note does not exist!") + return + + await m.reply_text(f"Note '`{note}`' deleted!") + return + + +@Gojo.on_message(command("clearall") & owner_filter & ~filters.bot) +async def clear_allnote(_, m: Message): + + all_notes = {i[0] for i in db.get_all_notes(m.chat.id)} + if not all_notes: + await m.reply_text("No notes are there in this chat") + return + + await m.reply_text( + "Are you sure you want to clear all notes?", + reply_markup=ikb( + [[("⚠️ Confirm", "clear_notes"), ("❌ Cancel", "close_admin")]], + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^clear_notes$")) +async def clearallnotes_callback(_, q: CallbackQuery): + user_id = q.from_user.id + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + db.rm_all_notes(q.message.chat.id) + LOGGER.info(f"{user_id} removed all notes in {q.message.chat.id}") + await q.message.edit_text("Cleared all notes!") + return + + +__PLUGIN__ = "notes" + +_DISABLE_CMDS_ = ["notes"] + +__alt_name__ = ["groupnotes", "snips", "notes"] diff --git a/Powers/plugins/pin.py b/Powers/plugins/pin.py new file mode 100644 index 0000000000000000000000000000000000000000..42d4c91ee0ed08ce162f07d4241193409b8d4984 --- /dev/null +++ b/Powers/plugins/pin.py @@ -0,0 +1,245 @@ +from html import escape as escape_html + +from pyrogram.errors import ChatAdminRequired, RightForbidden, RPCError +from pyrogram.filters import regex +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER, SUPPORT_GROUP +from Powers.bot_class import Gojo +from Powers.database.pins_db import Pins +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import admin_filter, command +from Powers.utils.kbhelpers import ikb +from Powers.utils.string import build_keyboard, parse_button + + +@Gojo.on_message(command("pin") & admin_filter) +async def pin_message(_, m: Message): + pin_args = m.text.split(None, 1) + if m.reply_to_message: + try: + disable_notification = True + + if len(pin_args) >= 2 and pin_args[1] in ["alert", "notify", "loud"]: + disable_notification = False + + await m.reply_to_message.pin( + disable_notification=disable_notification, + ) + LOGGER.info( + f"{m.from_user.id} pinned msgid-{m.reply_to_message.message_id} in {m.chat.id}", + ) + + if m.chat.username: + # If chat has a username, use this format + link_chat_id = m.chat.username + message_link = ( + f"https://t.me/{link_chat_id}/{m.reply_to_message.message_id}" + ) + elif (str(m.chat.id)).startswith("-100"): + # If chat does not have a username, use this + link_chat_id = (str(m.chat.id)).replace("-100", "") + message_link = ( + f"https://t.me/c/{link_chat_id}/{m.reply_to_message.message_id}" + ) + await m.reply_text( + tlang(m, "pin.pinned_msg").format(message_link=message_link), + disable_web_page_preview=True, + ) + + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "pin.no_rights_pin")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + else: + await m.reply_text("Reply to a message to pin it!") + + return + + +@Gojo.on_message(command("unpin") & admin_filter) +async def unpin_message(c: Gojo, m: Message): + try: + if m.reply_to_message: + await c.unpin_chat_message(m.chat.id, m.reply_to_message.message_id) + LOGGER.info( + f"{m.from_user.id} unpinned msgid: {m.reply_to_message.message_id} in {m.chat.id}", + ) + await m.reply_text(tlang(m, "pin.unpinned_last_msg")) + else: + await c.unpin_chat_message(m.chat.id) + await m.reply_text(tlang(m, "Unpinned last pinned message!")) + except ChatAdminRequired: + await m.reply_text(tlang(m, "admin.not_admin")) + except RightForbidden: + await m.reply_text(tlang(m, "pin.no_rights_unpin")) + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + + return + + +@Gojo.on_message(command("unpinall") & admin_filter) +async def unpinall_message(_, m: Message): + await m.reply_text( + "Do you really want to unpin all messages in this chat?", + reply_markup=ikb([[("Yes", "unpin_all_in_this_chat"), ("No", "close_admin")]]), + ) + return + + +@Gojo.on_callback_query(regex("^unpin_all_in_this_chat$")) +async def unpinall_calllback(c: Gojo, q: CallbackQuery): + user_id = q.from_user.id + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + try: + await c.unpin_all_chat_messages(q.message.chat.id) + LOGGER.info(f"{q.from_user.id} unpinned all messages in {q.message.chat.id}") + await q.message.edit_text(tlang(q, "pin.unpinned_all_msg")) + except ChatAdminRequired: + await q.message.edit_text(tlang(q, "admin.notadmin")) + except RightForbidden: + await q.message.edit_text(tlang(q, "pin.no_rights_unpin")) + except RPCError as ef: + await q.message.edit_text( + (tlang(q, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + LOGGER.error(ef) + return + + +@Gojo.on_message(command("antichannelpin") & admin_filter) +async def anti_channel_pin(_, m: Message): + pinsdb = Pins(m.chat.id) + + if len(m.text.split()) == 1: + status = pinsdb.get_settings()["antichannelpin"] + await m.reply_text( + tlang(m, "pin.antichannelpin.current_status").format( + status=status, + ), + ) + return + + if len(m.text.split()) == 2: + if m.command[1] in ("yes", "on", "true"): + pinsdb.antichannelpin_on() + LOGGER.info(f"{m.from_user.id} enabled antichannelpin in {m.chat.id}") + msg = tlang(m, "pin.antichannelpin.turned_on") + elif m.command[1] in ("no", "off", "false"): + pinsdb.antichannelpin_off() + LOGGER.info(f"{m.from_user.id} disabled antichannelpin in {m.chat.id}") + msg = tlang(m, "pin.antichannelpin.turned_off") + else: + await m.reply_text(tlang(m, "general.check_help")) + return + + await m.reply_text(msg) + return + + +@Gojo.on_message(command("pinned") & admin_filter) +async def pinned_message(c: Gojo, m: Message): + chat_title = m.chat.title + chat = await c.get_chat(chat_id=m.chat.id) + msg_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + if chat.pinned_message: + pinned_id = chat.pinned_message.message_id + if m.chat.username: + link_chat_id = m.chat.username + message_link = f"https://t.me/{link_chat_id}/{pinned_id}" + elif (str(m.chat.id)).startswith("-100"): + link_chat_id = (str(m.chat.id)).replace("-100", "") + message_link = f"https://t.me/c/{link_chat_id}/{pinned_id}" + + await m.reply_text( + f"The pinned message of {escape_html(chat_title)} is [here]({message_link}).", + reply_to_message_id=msg_id, + disable_web_page_preview=True, + ) + else: + await m.reply_text(f"There is no pinned message in {escape_html(chat_title)}.") + + +@Gojo.on_message(command("cleanlinked") & admin_filter) +async def clean_linked(_, m: Message): + pinsdb = Pins(m.chat.id) + + if len(m.text.split()) == 1: + status = pinsdb.get_settings()["cleanlinked"] + await m.reply_text( + tlang(m, "pin.antichannelpin.current_status").format( + status=status, + ), + ) + return + + if len(m.text.split()) == 2: + if m.command[1] in ("yes", "on", "true"): + pinsdb.cleanlinked_on() + LOGGER.info(f"{m.from_user.id} enabled CleanLinked in {m.chat.id}") + msg = "Turned on CleanLinked! Now all the messages from linked channel will be deleted!" + elif m.command[1] in ("no", "off", "false"): + pinsdb.cleanlinked_off() + LOGGER.info(f"{m.from_user.id} disabled CleanLinked in {m.chat.id}") + msg = "Turned off CleanLinked! Messages from linked channel will not be deleted!" + else: + await m.reply_text(tlang(m, "general.check_help")) + return + + await m.reply_text(msg) + return + + +@Gojo.on_message(command("permapin") & admin_filter) +async def perma_pin(_, m: Message): + if m.reply_to_message or len(m.text.split()) > 1: + LOGGER.info(f"{m.from_user.id} used permampin in {m.chat.id}") + if m.reply_to_message: + text = m.reply_to_message.text + elif len(m.text.split()) > 1: + text = m.text.split(None, 1)[1] + teks, button = await parse_button(text) + button = await build_keyboard(button) + button = ikb(button) if button else None + z = await m.reply_text(teks, reply_markup=button) + await z.pin() + else: + await m.reply_text("Reply to a message or enter text to pin it.") + await m.delete() + return + + +__PLUGIN__ = "pins" + +__alt_name__ = ["pin", "unpin"] diff --git a/Powers/plugins/purge.py b/Powers/plugins/purge.py new file mode 100644 index 0000000000000000000000000000000000000000..e96b1b6b1dddabcb78b438da8e120da4ad3894d4 --- /dev/null +++ b/Powers/plugins/purge.py @@ -0,0 +1,121 @@ +from asyncio import sleep + +from pyrogram.errors import MessageDeleteForbidden, RPCError +from pyrogram.types import Message + +from Powers import SUPPORT_GROUP +from Powers.bot_class import Gojo +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import admin_filter, command + + +@Gojo.on_message(command("purge") & admin_filter) +async def purge(c: Gojo, m: Message): + if m.chat.type != "supergroup": + await m.reply_text(tlang(m, "purge.err_basic")) + return + + if m.reply_to_message: + message_ids = list(range(m.reply_to_message.message_id, m.message_id)) + + def divide_chunks(l: list, n: int = 100): + for i in range(0, len(l), n): + yield l[i : i + n] + + # Dielete messages in chunks of 100 messages + m_list = list(divide_chunks(message_ids)) + + try: + for plist in m_list: + await c.delete_messages( + chat_id=m.chat.id, + message_ids=plist, + revoke=True, + ) + await m.delete() + except MessageDeleteForbidden: + await m.reply_text(tlang(m, "purge.old_msg_err")) + return + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + + count_del_msg = len(message_ids) + + z = await m.reply_text( + (tlang(m, "purge.purge_msg_count")).format( + msg_count=count_del_msg, + ), + ) + await sleep(3) + await z.delete() + return + await m.reply_text("Reply to a message to start purge !") + return + + +@Gojo.on_message(command("spurge") & admin_filter) +async def spurge(c: Gojo, m: Message): + if m.chat.type != "supergroup": + await m.reply_text(tlang(m, "purge.err_basic")) + return + + if m.reply_to_message: + message_ids = list(range(m.reply_to_message.message_id, m.message_id)) + + def divide_chunks(l: list, n: int = 100): + for i in range(0, len(l), n): + yield l[i : i + n] + + # Dielete messages in chunks of 100 messages + m_list = list(divide_chunks(message_ids)) + + try: + for plist in m_list: + await c.delete_messages( + chat_id=m.chat.id, + message_ids=plist, + revoke=True, + ) + await m.delete() + except MessageDeleteForbidden: + await m.reply_text(tlang(m, "purge.old_msg_err")) + return + except RPCError as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + return + await m.reply_text("Reply to a message to start spurge !") + return + + +@Gojo.on_message( + command("del") & admin_filter, + group=9, +) +async def del_msg(c: Gojo, m: Message): + if m.chat.type != "supergroup": + return + + if m.reply_to_message: + await m.delete() + await c.delete_messages( + chat_id=m.chat.id, + message_ids=m.reply_to_message.message_id, + ) + else: + await m.reply_text(tlang(m, "purge.what_del")) + return + + +__PLUGIN__ = "purges" + +__alt_name__ = ["purge", "del", "spurge"] diff --git a/Powers/plugins/report.py b/Powers/plugins/report.py new file mode 100644 index 0000000000000000000000000000000000000000..1e8032b3deac58aa9aa7963ee3db0b4a03da85b4 --- /dev/null +++ b/Powers/plugins/report.py @@ -0,0 +1,202 @@ +from traceback import format_exc + +from pyrogram import filters +from pyrogram.errors import RPCError +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.database.reporting_db import Reporting +from Powers.utils.custom_filters import admin_filter, command +from Powers.utils.kbhelpers import ikb +from Powers.utils.parser import mention_html + + +@Gojo.on_message( + command("reports") & (filters.private | admin_filter), +) +async def report_setting(_, m: Message): + args = m.text.split() + db = Reporting(m.chat.id) + + if m.chat.type == "private": + if len(args) >= 2: + option = args[1].lower() + if option in ("yes", "on", "true"): + db.set_settings(True) + LOGGER.info(f"{m.from_user.id} enabled reports for them") + await m.reply_text( + "Turned on reporting! You'll be notified whenever anyone reports something in groups you are admin.", + ) + + elif option in ("no", "off", "false"): + db.set_settings(False) + LOGGER.info(f"{m.from_user.id} disabled reports for them") + await m.reply_text("Turned off reporting! You wont get any reports.") + else: + await m.reply_text( + f"Your current report preference is: `{(db.get_settings())}`", + ) + elif len(args) >= 2: + option = args[1].lower() + if option in ("yes", "on", "true"): + db.set_settings(True) + LOGGER.info(f"{m.from_user.id} enabled reports in {m.chat.id}") + await m.reply_text( + "Turned on reporting! Admins who have turned on reports will be notified when /report " + "or @admin is called.", + quote=True, + ) + + elif option in ("no", "off", "false"): + db.set_settings(False) + LOGGER.info(f"{m.from_user.id} disabled reports in {m.chat.id}") + await m.reply_text( + "Turned off reporting! No admins will be notified on /report or @admin.", + quote=True, + ) + else: + await m.reply_text( + f"This group's current setting is: `{(db.get_settings())}`", + ) + + +@Gojo.on_message(command("report") & filters.group) +async def report_watcher(c: Gojo, m: Message): + if m.chat.type != "supergroup": + return + + if not m.from_user: + return + + me = await c.get_me() + db = Reporting(m.chat.id) + + if (m.chat and m.reply_to_message) and (db.get_settings()): + reported_msg_id = m.reply_to_message.message_id + reported_user = m.reply_to_message.from_user + chat_name = m.chat.title or m.chat.username + admin_list = await c.get_chat_members(m.chat.id, filter="administrators") + + if reported_user.id == me.id: + await m.reply_text("Nice try.") + return + + if reported_user.id in SUPPORT_STAFF: + await m.reply_text("Uh? You reporting my support team?") + return + + if m.chat.username: + msg = ( + f"⚠️ Report: {m.chat.title}\n" + f" • Report by: {(await mention_html(m.from_user.first_name, m.from_user.id))} ({m.from_user.id})\n" + f" • Reported user: {(await mention_html(reported_user.first_name, reported_user.id))} ({reported_user.id})\n" + ) + + else: + msg = f"{(await mention_html(m.from_user.first_name, m.from_user.id))} is calling for admins in '{chat_name}'!\n" + + link_chat_id = str(m.chat.id).replace("-100", "") + link = f"https://t.me/c/{link_chat_id}/{reported_msg_id}" # message link + + reply_markup = ikb( + [ + [("➡ Message", link, "url")], + [ + ( + "⚠ Kick", + f"report_{m.chat.id}=kick={reported_user.id}={reported_msg_id}", + ), + ( + "⛔️ Ban", + f"report_{m.chat.id}=ban={reported_user.id}={reported_msg_id}", + ), + ], + [ + ( + "❎ Delete Message", + f"report_{m.chat.id}=del={reported_user.id}={reported_msg_id}", + ), + ], + ], + ) + + LOGGER.info( + f"{m.from_user.id} reported msgid-{m.reply_to_message.message_id} to admins in {m.chat.id}", + ) + await m.reply_text( + ( + f"{(await mention_html(m.from_user.first_name, m.from_user.id))} " + "reported the message to the admins." + ), + quote=True, + ) + + for admin in admin_list: + if ( + admin.user.is_bot or admin.user.is_deleted + ): # can't message bots or deleted accounts + continue + if Reporting(admin.user.id).get_settings(): + try: + await c.send_message( + admin.user.id, + msg, + reply_markup=reply_markup, + disable_web_page_preview=True, + ) + try: + await m.reply_to_message.forward(admin.user.id) + if len(m.text.split()) > 1: + await m.forward(admin.user.id) + except Exception: + pass + except Exception: + pass + except RPCError as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + return "" + + +@Gojo.on_callback_query(filters.regex("^report_")) +async def report_buttons(c: Gojo, q: CallbackQuery): + splitter = (str(q.data).replace("report_", "")).split("=") + chat_id = int(splitter[0]) + action = str(splitter[1]) + user_id = int(splitter[2]) + message_id = int(splitter[3]) + if action == "kick": + try: + await c.ban_chat_member(chat_id, user_id) + await q.answer("✅ Succesfully kicked") + await c.unban_chat_member(chat_id, user_id) + return + except RPCError as err: + await q.answer( + f"🛑 Failed to Kick\nError:\n{err}", + show_alert=True, + ) + elif action == "ban": + try: + await c.ban_chat_member(chat_id, user_id) + await q.answer("✅ Succesfully Banned") + return + except RPCError as err: + await q.answer(f"🛑 Failed to Ban\nError:\n`{err}`", show_alert=True) + elif action == "del": + try: + await c.delete_messages(chat_id, message_id) + await q.answer("✅ Message Deleted") + return + except RPCError as err: + await q.answer( + f"🛑 Failed to delete message!\nError:\n`{err}`", + show_alert=True, + ) + return + + +__PLUGIN__ = "reporting" + +__alt_name__ = ["reports", "report"] diff --git a/Powers/plugins/rules.py b/Powers/plugins/rules.py new file mode 100644 index 0000000000000000000000000000000000000000..71588dd52e8aae5c23223036c51439e25edd73b0 --- /dev/null +++ b/Powers/plugins/rules.py @@ -0,0 +1,153 @@ +from pyrogram import filters +from pyrogram.types import CallbackQuery, Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.rules_db import Rules +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import admin_filter, command +from Powers.utils.kbhelpers import ikb +from Powers.vars import Config + + +@Gojo.on_message(command("rules") & filters.group) +async def get_rules(_, m: Message): + db = Rules(m.chat.id) + msg_id = m.reply_to_message.message_id if m.reply_to_message else m.message_id + + rules = db.get_rules() + LOGGER.info(f"{m.from_user.id} fetched rules in {m.chat.id}") + if m and not m.from_user: + return + + if not rules: + await m.reply_text( + (tlang(m, "rules.no_rules")), + quote=True, + ) + return + + priv_rules_status = db.get_privrules() + + if priv_rules_status: + pm_kb = ikb( + [ + [ + ( + "Rules", + f"https://t.me/{Config.BOT_USERNAME}?start=rules_{m.chat.id}", + "url", + ), + ], + ], + ) + await m.reply_text( + (tlang(m, "rules.pm_me")), + quote=True, + reply_markup=pm_kb, + reply_to_message_id=msg_id, + ) + return + + formated = rules + + await m.reply_text( + (tlang(m, "rules.get_rules")).format( + chat=f"{m.chat.title}", + rules=formated, + ), + disable_web_page_preview=True, + reply_to_message_id=msg_id, + ) + return + + +@Gojo.on_message(command("setrules") & admin_filter) +async def set_rules(_, m: Message): + db = Rules(m.chat.id) + if m and not m.from_user: + return + + if m.reply_to_message and m.reply_to_message.text: + rules = m.reply_to_message.text.markdown + elif (not m.reply_to_message) and len(m.text.split()) >= 2: + rules = m.text.split(None, 1)[1] + else: + return await m.reply_text("Provide some text to set as rules !!") + + if len(rules) > 4000: + rules = rules[0:3949] # Split Rules if len > 4000 chars + await m.reply_text("Rules are truncated to 3950 characters!") + + db.set_rules(rules) + LOGGER.info(f"{m.from_user.id} set rules in {m.chat.id}") + await m.reply_text(tlang(m, "rules.set_rules")) + return + + +@Gojo.on_message( + command(["pmrules", "privaterules"]) & admin_filter, +) +async def priv_rules(_, m: Message): + db = Rules(m.chat.id) + if m and not m.from_user: + return + + if len(m.text.split()) == 2: + option = (m.text.split())[1] + if option in ("on", "yes"): + db.set_privrules(True) + LOGGER.info(f"{m.from_user.id} enabled privaterules in {m.chat.id}") + msg = tlang(m, "rules.priv_rules.turned_on").format(chat_name=m.chat.title) + elif option in ("off", "no"): + db.set_privrules(False) + LOGGER.info(f"{m.from_user.id} disbaled privaterules in {m.chat.id}") + msg = tlang(m, "rules.priv_rules.turned_off").format(chat_name=m.chat.title) + else: + msg = tlang(m, "rules.priv_rules.no_option") + await m.reply_text(msg) + elif len(m.text.split()) == 1: + curr_pref = db.get_privrules() + msg = tlang(m, "rules.priv_rules.current_preference").format( + current_option=curr_pref, + ) + LOGGER.info(f"{m.from_user.id} fetched privaterules preference in {m.chat.id}") + await m.reply_text(msg) + else: + await m.replt_text(tlang(m, "general.check_help")) + + return + + +@Gojo.on_message(command("clearrules") & admin_filter) +async def clear_rules(_, m: Message): + db = Rules(m.chat.id) + if m and not m.from_user: + return + + rules = db.get_rules() + if not rules: + await m.reply_text(tlang(m, "rules.no_rules")) + return + + await m.reply_text( + (tlang(m, "rules.clear_rules")), + reply_markup=ikb( + [[("⚠️ Confirm", "clear_rules"), ("❌ Cancel", "close_admin")]], + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^clear_rules$")) +async def clearrules_callback(_, q: CallbackQuery): + Rules(q.message.chat.id).clear_rules() + await q.message.edit_text(tlang(q, "rules.cleared")) + LOGGER.info(f"{q.from_user.id} cleared rules in {q.message.chat.id}") + await q.answer("Rules for the chat have been cleared!", show_alert=True) + return + + +__PLUGIN__ = "rules" + +__alt_name__ = ["rule"] diff --git a/Powers/plugins/start.py b/Powers/plugins/start.py new file mode 100644 index 0000000000000000000000000000000000000000..3dd6eb1da03d0282b2e9e2355bc75397b834bd24 --- /dev/null +++ b/Powers/plugins/start.py @@ -0,0 +1,213 @@ +from pyrogram import filters +from pyrogram.errors import MessageNotModified, QueryIdInvalid, UserIsBlocked +from pyrogram.types import CallbackQuery, Message + +from Powers import HELP_COMMANDS, LOGGER +from Powers.bot_class import Gojo +from Powers.tr_engine import tlang +from Powers.utils.custom_filters import command +from Powers.utils.kbhelpers import ikb +from Powers.utils.start_utils import ( + gen_cmds_kb, + gen_start_kb, + get_help_msg, + get_private_note, + get_private_rules, +) +from Powers.vars import Config + + +@Gojo.on_message( + command("donate") & (filters.group | filters.private), +) +async def donate(_, m: Message): + LOGGER.info(f"{m.from_user.id} fetched donation text in {m.chat.id}") + await m.reply_text(tlang(m, "general.donate_owner")) + return + + +@Gojo.on_callback_query(filters.regex("^close_admin$")) +async def close_admin_callback(_, q: CallbackQuery): + user_id = q.from_user.id + user_status = (await q.message.chat.get_member(user_id)).status + if user_status not in {"creator", "administrator"}: + await q.answer( + "You're not even an admin, don't try this explosive shit!", + show_alert=True, + ) + return + if user_status != "creator": + await q.answer( + "You're just an admin, not owner\nStay in your limits!", + show_alert=True, + ) + return + await q.message.edit_text("Closed!") + await q.answer("Closed menu!", show_alert=True) + return + + +@Gojo.on_message( + command("start") & (filters.group | filters.private), +) +async def start(c: Gojo, m: Message): + if m.chat.type == "private": + if len(m.text.split()) > 1: + help_option = (m.text.split(None, 1)[1]).lower() + + if help_option.startswith("note") and ( + help_option not in ("note", "notes") + ): + await get_private_note(c, m, help_option) + return + if help_option.startswith("rules"): + LOGGER.info(f"{m.from_user.id} fetched privaterules in {m.chat.id}") + await get_private_rules(c, m, help_option) + return + + help_msg, help_kb = await get_help_msg(m, help_option) + + if not help_msg: + return + + await m.reply_text( + help_msg, + parse_mode="markdown", + reply_markup=ikb(help_kb), + quote=True, + disable_web_page_preview=True, + ) + return + try: + await m.reply_text( + (tlang(m, "start.private")), + reply_markup=(await gen_start_kb(m)), + quote=True, + disable_web_page_preview=True, + ) + except UserIsBlocked: + LOGGER.warning(f"Bot blocked by {m.from_user.id}") + else: + await m.reply_text( + (tlang(m, "start.group")), + quote=True, + ) + return + + +@Gojo.on_callback_query(filters.regex("^start_back$")) +async def start_back(_, q: CallbackQuery): + try: + await q.message.edit_text( + (tlang(q, "start.private")), + reply_markup=(await gen_start_kb(q.message)), + disable_web_page_preview=True, + ) + except MessageNotModified: + pass + await q.answer() + return + + +@Gojo.on_callback_query(filters.regex("^commands$")) +async def commands_menu(_, q: CallbackQuery): + keyboard = ikb( + [ + *(await gen_cmds_kb(q)), + [(f"« {(tlang(q, 'general.back_btn'))}", "start_back")], + ], + ) + try: + await q.message.edit_text( + (tlang(q, "general.commands_available")), + reply_markup=keyboard, + ) + except MessageNotModified: + pass + except QueryIdInvalid: + await q.message.reply_text( + (tlang(q, "general.commands_available")), + reply_markup=keyboard, + ) + await q.answer() + return + + +@Gojo.on_message(command("help")) +async def help_menu(_, m: Message): + if len(m.text.split()) >= 2: + help_option = (m.text.split(None, 1)[1]).lower() + help_msg, help_kb = await get_help_msg(m, help_option) + + if not help_msg: + LOGGER.error(f"No help_msg found for help_option - {help_option}!!") + return + + LOGGER.info( + f"{m.from_user.id} fetched help for '{help_option}' text in {m.chat.id}", + ) + if m.chat.type == "private": + await m.reply_text( + help_msg, + parse_mode="markdown", + reply_markup=ikb(help_kb), + quote=True, + disable_web_page_preview=True, + ) + else: + await m.reply_text( + (tlang(m, "start.public_help").format(help_option=help_option)), + reply_markup=ikb( + [ + [ + ( + "Help", + f"t.me/{Config.BOT_USERNAME}?start={help_option}", + "url", + ), + ], + ], + ), + ) + else: + if m.chat.type == "private": + keyboard = ikb( + [ + *(await gen_cmds_kb(m)), + [(f"« {(tlang(m, 'general.back_btn'))}", "start_back")], + ], + ) + msg = tlang(m, "general.commands_available") + else: + keyboard = ikb( + [[("Help", f"t.me/{Config.BOT_USERNAME}?start=help", "url")]], + ) + msg = tlang(m, "start.pm_for_help") + + await m.reply_text( + msg, + reply_markup=keyboard, + ) + + return + + +@Gojo.on_callback_query(filters.regex("^get_mod.")) +async def get_module_info(_, q: CallbackQuery): + module = q.data.split(".", 1)[1] + + help_msg = f"**{(tlang(q, str(module)))}:**\n\n" + tlang( + q, + HELP_COMMANDS[module]["help_msg"], + ) + + help_kb = HELP_COMMANDS[module]["buttons"] + [ + [("« " + (tlang(q, "general.back_btn")), "commands")], + ] + await q.message.edit_text( + help_msg, + parse_mode="markdown", + reply_markup=ikb(help_kb), + ) + await q.answer() + return diff --git a/Powers/plugins/stats.py b/Powers/plugins/stats.py new file mode 100644 index 0000000000000000000000000000000000000000..322ff19e52a17b617a06aff8babe0e1bbf07d3eb --- /dev/null +++ b/Powers/plugins/stats.py @@ -0,0 +1,68 @@ +from pyrogram.types import Message + +from Powers.bot_class import Gojo +from Powers.database.antispam_db import GBan +from Powers.database.approve_db import Approve +from Powers.database.blacklist_db import Blacklist +from Powers.database.chats_db import Chats +from Powers.database.disable_db import Disabling +from Powers.database.filters_db import Filters +from Powers.database.greetings_db import Greetings +from Powers.database.notes_db import Notes, NotesSettings +from Powers.database.pins_db import Pins +from Powers.database.rules_db import Rules +from Powers.database.users_db import Users +from Powers.database.warns_db import Warns, WarnSettings +from Powers.utils.custom_filters import command + + +@Gojo.on_message(command("stats", dev_cmd=True)) +async def get_stats(_, m: Message): + # initialise + bldb = Blacklist + gbandb = GBan() + notesdb = Notes() + rulesdb = Rules + grtdb = Greetings + userdb = Users + dsbl = Disabling + appdb = Approve + chatdb = Chats + fldb = Filters() + pinsdb = Pins + notesettings_db = NotesSettings() + warns_db = Warns + warns_settings_db = WarnSettings + + replymsg = await m.reply_text("Fetching Stats...", quote=True) + rply = ( + f"Users: {(userdb.count_users())} in {(chatdb.count_chats())} chats\n" + f"Anti Channel Pin: {(pinsdb.count_chats('antichannelpin'))} enabled chats\n" + f"Clean Linked: {(pinsdb.count_chats('cleanlinked'))} enabled chats\n" + f"Filters: {(fldb.count_filters_all())} in {(fldb.count_filters_chats())} chats\n" + f" Aliases: {(fldb.count_filter_aliases())}\n" + f"Blacklists: {(bldb.count_blacklists_all())} in {(bldb.count_blackists_chats())} chats\n" + f" Action Specific:\n" + f" None: {(bldb.count_action_bl_all('none'))} chats\n" + f" Kick {(bldb.count_action_bl_all('kick'))} chats\n" + f" Warn: {(bldb.count_action_bl_all('warn'))} chats\n" + f" Ban {(bldb.count_action_bl_all('ban'))} chats\n" + f"Rules: Set in {(rulesdb.count_chats_with_rules())} chats\n" + f" Private Rules: {(rulesdb.count_privrules_chats())} chats\n" + f"Warns: {(warns_db.count_warns_total())} in {(warns_db.count_all_chats_using_warns())} chats\n" + f" Users Warned: {(warns_db.count_warned_users())} users\n" + f" Action Specific:\n" + f" Kick: {(warns_settings_db.count_action_chats('kick'))}\n" + f" Mute: {(warns_settings_db.count_action_chats('mute'))}\n" + f" Ban: {warns_settings_db.count_action_chats('ban')}\n" + f"Notes: {(notesdb.count_all_notes())} in {(notesdb.count_notes_chats())} chats\n" + f" Private Notes: {(notesettings_db.count_chats())} chats\n" + f"GBanned Users: {(gbandb.count_gbans())}\n" + f"Welcoming Users in: {(grtdb.count_chats('welcome'))} chats" + f"Approved People: {(appdb.count_all_approved())} in {(appdb.count_approved_chats())} chats\n" + f"Disabling: {(dsbl.count_disabled_all())} items in {(dsbl.count_disabling_chats())} chats.\n" + "Action:\n" + f" Del: Applied in {(dsbl.count_action_dis_all('del'))} chats.\n" + ) + await replymsg.edit_text(rply, parse_mode="html") + return diff --git a/Powers/plugins/utils.py b/Powers/plugins/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..5323c9430366542330cac49895b5ef287215e246 --- /dev/null +++ b/Powers/plugins/utils.py @@ -0,0 +1,393 @@ +from html import escape +from io import BytesIO +from os import remove + +from gpytranslate import Translator +from pyrogram import filters +from pyrogram.errors import MessageTooLong, PeerIdInvalid, RPCError +from pyrogram.types import Message +from tswift import Song +from wikipedia import summary +from wikipedia.exceptions import DisambiguationError, PageError + +from Powers import ( + DEV_USERS, + LOGGER, + OWNER_ID, + SUDO_USERS, + SUPPORT_GROUP, + SUPPORT_STAFF, + WHITELIST_USERS, +) +from Powers.bot_class import Gojo +from Powers.database.antispam_db import GBan +from Powers.database.users_db import Users +from Powers.tr_engine import tlang +from Powers.utils.clean_file import remove_markdown_and_html +from Powers.utils.custom_filters import command +from Powers.utils.extract_user import extract_user +from Powers.utils.http_helper import HTTPx, http +from Powers.utils.kbhelpers import ikb +from Powers.utils.parser import mention_html +from Powers.vars import Config + +gban_db = GBan() + + +@Gojo.on_message(command("wiki")) +async def wiki(_, m: Message): + LOGGER.info(f"{m.from_user.id} used wiki cmd in {m.chat.id}") + + if len(m.text.split()) <= 1: + return await m.reply_text(tlang(m, "general.check_help")) + + search = m.text.split(None, 1)[1] + try: + res = summary(search) + except DisambiguationError as de: + return await m.reply_text( + f"Disambiguated pages found! Adjust your query accordingly.\n{de}", + parse_mode="html", + ) + except PageError as pe: + return await m.reply_text(f"{pe}", parse_mode="html") + if res: + result = f"{search}\n\n" + result += f"{res}\n" + result += f"""Read more...""" + try: + return await m.reply_text( + result, + parse_mode="html", + disable_web_page_preview=True, + ) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(result))) as f: + f.name = "result.txt" + return await m.reply_document( + document=f, + quote=True, + parse_mode="html", + ) + await m.stop_propagation() + + +@Gojo.on_message(command("gdpr")) +async def gdpr_remove(_, m: Message): + if m.from_user.id in SUPPORT_STAFF: + await m.reply_text( + "You're in my support staff, I cannot do that unless you are no longer a part of it!", + ) + return + + Users(m.from_user.id).delete_user() + await m.reply_text( + "Your personal data has been deleted.\n" + "Note that this will not unban you from any chats, as that is telegram data, not Gojo data." + " Flooding, warns, and gbans are also preserved, as of " + "[this](https://ico.org.uk/for-organisations/guide-to-the-general-data-protection-regulation-gdpr/individual-rights/right-to-erasure/)," + " which clearly states that the right to erasure does not apply 'for the performance of a task carried out in the public interest', " + "as is the case for the aforementioned pieces of data.", + disable_web_page_preview=True, + ) + await m.stop_propagation() + + +@Gojo.on_message( + command("lyrics") & (filters.group | filters.private), +) +async def get_lyrics(_, m: Message): + if len(m.text.split()) <= 1: + await m.reply_text(tlang(m, "general.check_help")) + return + + query = m.text.split(None, 1)[1] + LOGGER.info(f"{m.from_user.id} used lyrics cmd in {m.chat.id}") + song = "" + if not query: + await m.edit_text(tlang(m, "utils.song.no_song_given")) + return + + em = await m.reply_text( + (tlang(m, "utils.song.searching").format(song_name=query)), + ) + song = Song.find_song(query) + if song: + if song.lyrics: + reply = song.format() + else: + reply = tlang(m, "utils.song.no_lyrics_found") + else: + reply = tlang(m, "utils.song.song_not_found") + try: + await em.edit_text(reply) + except MessageTooLong: + with BytesIO(str.encode(await remove_markdown_and_html(reply))) as f: + f.name = "lyrics.txt" + await m.reply_document( + document=f, + ) + await em.delete() + return + + +@Gojo.on_message( + command("id") & (filters.group | filters.private), +) +async def id_info(c: Gojo, m: Message): + LOGGER.info(f"{m.from_user.id} used id cmd in {m.chat.id}") + + if m.chat.type == "supergroup" and not m.reply_to_message: + await m.reply_text((tlang(m, "utils.id.group_id")).format(group_id=m.chat.id)) + return + + if m.chat.type == "private" and not m.reply_to_message: + await m.reply_text((tlang(m, "utils.id.my_id")).format(my_id=m.chat.id)) + return + + user_id, _, _ = await extract_user(c, m) + if user_id: + if m.reply_to_message and m.reply_to_message.forward_from: + user1 = m.reply_to_message.from_user + user2 = m.reply_to_message.forward_from + await m.reply_text( + (tlang(m, "utils.id.id_main")).format( + orig_sender=(await mention_html(user2.first_name, user2.id)), + orig_id=f"{user2.id}", + fwd_sender=(await mention_html(user1.first_name, user1.id)), + fwd_id=f"{user1.id}", + ), + parse_mode="HTML", + ) + else: + try: + user = await c.get_users(user_id) + except PeerIdInvalid: + await m.reply_text(tlang(m, "utils.no_user_db")) + return + + await m.reply_text( + f"{(await mention_html(user.first_name, user.id))}'s ID is {user.id}.", + parse_mode="HTML", + ) + elif m.chat.type == "private": + await m.reply_text( + (tlang(m, "utils.id.my_id")).format( + my_id=f"{m.chat.id}", + ), + ) + else: + await m.reply_text( + (tlang(m, "utils.id.group_id")).format( + group_id=f"{m.chat.id}", + ), + ) + return + + +@Gojo.on_message( + command("gifid") & (filters.group | filters.private), +) +async def get_gifid(_, m: Message): + if m.reply_to_message and m.reply_to_message.animation: + LOGGER.info(f"{m.from_user.id} used gifid cmd in {m.chat.id}") + await m.reply_text( + f"Gif ID:\n{m.reply_to_message.animation.file_id}", + parse_mode="html", + ) + else: + await m.reply_text(tlang(m, "utils.gif_id.reply_gif")) + return + + +@Gojo.on_message( + command("github") & (filters.group | filters.private), +) +async def github(_, m: Message): + if len(m.text.split()) == 2: + username = m.text.split(None, 1)[1] + LOGGER.info(f"{m.from_user.id} used github cmd in {m.chat.id}") + else: + await m.reply_text( + f"Usage: {Config.PREFIX_HANDLER}github username", + ) + return + + URL = f"https://api.github.com/users/{username}" + r = await HTTPx.get(URL) + if r.status_code == 404: + await m.reply_text(f"{username} not found", quote=True) + return + + r_json = r.json() + url = r_json.get("html_url", None) + name = r_json.get("name", None) + company = r_json.get("company", None) + followers = r_json.get("followers", 0) + following = r_json.get("following", 0) + public_repos = r_json.get("public_repos", 0) + bio = r_json.get("bio", None) + created_at = r_json.get("created_at", "Not Found") + + REPLY = ( + f"GitHub Info for @{username}:" + f"\nName: {name}\n" + f"Bio: {bio}\n" + f"URL: {url}\n" + f"Public Repos: {public_repos}\n" + f"Followers: {followers}\n" + f"Following: {following}\n" + f"Company: {company}\n" + f"Created at: {created_at}" + ) + + await m.reply_text(REPLY, quote=True, disable_web_page_preview=True) + return + + +@Gojo.on_message( + command("info") & (filters.group | filters.private), +) +async def my_info(c: Gojo, m: Message): + try: + user_id, name, user_name = await extract_user(c, m) + except PeerIdInvalid: + await m.reply_text(tlang(m, "utils.user_info.peer_id_error")) + return + except ValueError as ef: + if "Peer id invalid" in str(ef): + await m.reply_text(tlang(m, "utils.user_info.id_not_found")) + return + try: + user = Users.get_user_info(int(user_id)) + name = user["name"] + user_name = user["username"] + user_id = user["_id"] + except KeyError: + LOGGER.warning(f"Calling api to fetch info about user {user_id}") + user = await c.get_users(user_id) + name = ( + escape(user["first_name"] + " " + user["last_name"]) + if user["last_name"] + else user["first_name"] + ) + user_name = user["username"] + user_id = user["id"] + except PeerIdInvalid: + await m.reply_text(tlang(m, "utils.no_user_db")) + return + except (RPCError, Exception) as ef: + await m.reply_text( + (tlang(m, "general.some_error")).format( + SUPPORT_GROUP=SUPPORT_GROUP, + ef=ef, + ), + ) + return + + gbanned, reason_gban = gban_db.get_gban(user_id) + LOGGER.info(f"{m.from_user.id} used info cmd for {user_id} in {m.chat.id}") + + text = (tlang(m, "utils.user_info.info_text.main")).format( + user_id=user_id, + user_name=name, + ) + + if user_name: + text += (tlang(m, "utils.user_info.info_text.username")).format( + username=user_name, + ) + + text += (tlang(m, "utils.user_info.info_text.perma_link")).format( + perma_link=(await mention_html("Click Here", user_id)), + ) + + if gbanned: + text += f"\nThis user is Globally banned beacuse: {reason_gban}\n" + + if user_id == OWNER_ID: + text += tlang(m, "utils.user_info.support_user.owner") + elif user_id in DEV_USERS: + text += tlang(m, "utils.user_info.support_user.dev") + elif user_id in SUDO_USERS: + text += tlang(m, "utils.user_info.support_user.sudo") + elif user_id in WHITELIST_USERS: + text += tlang(m, "utils.user_info.support_user.whitelist") + + await m.reply_text(text, parse_mode="html", disable_web_page_preview=True) + return + + +@Gojo.on_message(command("paste")) +async def paste_it(_, m: Message): + replymsg = await m.reply_text((tlang(m, "utils.paste.pasting")), quote=True) + try: + if m.reply_to_message: + if m.reply_to_message.document: + dl_loc = await m.reply_to_message.download() + with open(dl_loc) as f: + txt = f.read() + remove(dl_loc) + else: + txt = m.reply_to_message.text + else: + txt = m.text.split(None, 1)[1] + ur = "https://hastebin.com/documents" + r = await http.post(ur, json={"content": txt}) + url = f"https://hastebin.com/{r.json().get('key')}" + await replymsg.edit_text( + (tlang(m, "utils.paste.pasted_nekobin")), + reply_markup=ikb([[((tlang(m, "utils.paste.nekobin_btn")), url, "url")]]), + ) + LOGGER.info(f"{m.from_user.id} used paste cmd in {m.chat.id}") + except Exception as e: + await replymsg.edit_text(f"Error: {e}") + return + return + + +@Gojo.on_message(command("tr")) +async def translate(_, m: Message): + trl = Translator() + if m.reply_to_message and (m.reply_to_message.text or m.reply_to_message.caption): + if len(m.text.split()) == 1: + target_lang = "en" + else: + target_lang = m.text.split()[1] + if m.reply_to_message.text: + text = m.reply_to_message.text + else: + text = m.reply_to_message.caption + else: + if len(m.text.split()) <= 2: + await m.reply_text( + "Provide lang code.\n[Available options](https://telegra.ph/Lang-Codes-11-08).\nUsage: /tr en", + ) + return + target_lang = m.text.split(None, 2)[1] + text = m.text.split(None, 2)[2] + detectlang = await trl.detect(text) + try: + tekstr = await trl(text, targetlang=target_lang) + except ValueError as err: + await m.reply_text(f"Error: {str(err)}") + return + LOGGER.info(f"{m.from_user.id} used translate cmd in {m.chat.id}") + return await m.reply_text( + f"Translated: from {detectlang} to {target_lang} \n``{tekstr.text}``", + ) + + +__PLUGIN__ = "utils" +_DISABLE_CMDS_ = [ + "paste", + "wiki", + "id", + "gifid", + "lyrics", + "tr", + "github", + "git", + "info", +] +__alt_name__ = ["util", "misc", "tools"] diff --git a/Powers/plugins/warns.py b/Powers/plugins/warns.py new file mode 100644 index 0000000000000000000000000000000000000000..43aad865c7ab077c15e8aa559cf17104a77775ea --- /dev/null +++ b/Powers/plugins/warns.py @@ -0,0 +1,370 @@ +from time import time + +from pyrogram import filters +from pyrogram.errors import RPCError +from pyrogram.types import ( + CallbackQuery, + ChatPermissions, + InlineKeyboardButton, + InlineKeyboardMarkup, + Message, +) + +from Powers import LOGGER, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.database.rules_db import Rules +from Powers.database.users_db import Users +from Powers.database.warns_db import Warns, WarnSettings +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, admin_cache_reload +from Powers.utils.custom_filters import admin_filter, command, restrict_filter +from Powers.utils.extract_user import extract_user +from Powers.utils.parser import mention_html +from Powers.vars import Config + + +@Gojo.on_message( + command(["warn", "swarn", "dwarn"]) & restrict_filter, +) +async def warn(c: Gojo, m: Message): + if m.reply_to_message: + r_id = m.reply_to_message.message_id + if len(m.text.split()) >= 2: + reason = m.text.split(None, 1)[1] + else: + reason = None + elif not m.reply_to_message: + r_id = m.message_id + if len(m.text.split()) >= 3: + reason = m.text.split(None, 2)[2] + else: + reason = None + else: + reason = None + + if not len(m.command) > 1 and not m.reply_to_message: + await m.reply_text("I can't warn nothing! Tell me user whom I should warn") + return + + user_id, user_first_name, _ = await extract_user(c, m) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I warn myself?") + return + + if user_id in SUPPORT_STAFF: + await m.reply_text(tlang(m, "admin.support_cannot_restrict")) + LOGGER.info( + f"{m.from_user.id} trying to warn {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = {i[0] for i in (await admin_cache_reload(m, "warn_user"))} + + if user_id in admins_group: + await m.reply_text("This user is admin in this chat, I can't warn them!") + return + + warn_db = Warns(m.chat.id) + warn_settings_db = WarnSettings(m.chat.id) + + _, num = warn_db.warn_user(user_id, reason) + warn_settings = warn_settings_db.get_warnings_settings() + if num >= warn_settings["warn_limit"]: + if warn_settings["warn_mode"] == "kick": + await m.chat.ban_member(user_id, until_date=int(time() + 45)) + action = "kicked" + elif warn_settings["warn_mode"] == "ban": + await m.chat.ban_member(user_id) + action = "banned" + elif warn_settings["warn_mode"] == "mute": + await m.chat.restrict_member(user_id, ChatPermissions()) + action = "muted" + await m.reply_text( + ( + f"Warnings {num}/{warn_settings['warn_limit']}!" + f"\nReason for last warn:\n{reason}" + if reason + else "\n" + f"{(await mention_html(user_first_name, user_id))} has been {action}!" + ), + reply_to_message_id=r_id, + ) + await m.stop_propagation() + + rules = Rules(m.chat.id).get_rules() + if rules: + kb = InlineKeyboardButton( + "Rules 📋", + url=f"https://t.me/{Config.BOT_USERNAME}?start=rules_{m.chat.id}", + ) + else: + kb = InlineKeyboardButton( + "Kick ⚠️", + callback_data=f"warn.kick.{user_id}", + ) + + if m.text.split()[0] == "/swarn": + await m.delete() + await m.stop_propagation() + if m.text.split()[0] == "/dwarn": + if not m.reply_to_message: + await m.reply_text("Reply to a message to delete it and ban the user!") + await m.stop_propagation() + await m.reply_to_message.delete() + txt = f"{(await mention_html(user_first_name, user_id))} has {num}/{warn_settings['warn_limit']} warnings!" + txt += f"\nReason for last warn:\n{reason}" if reason else "" + await m.reply_text( + txt, + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "Remove Warn ❌", + callback_data=f"warn.remove.{user_id}", + ), + ] + + [kb], + ], + ), + reply_to_message_id=r_id, + ) + await m.stop_propagation() + + +@Gojo.on_message(command("resetwarns") & restrict_filter) +async def reset_warn(c: Gojo, m: Message): + + if not len(m.command) > 1 and not m.reply_to_message: + await m.reply_text("I can't warn nothing! Tell me user whom I should warn") + return + + user_id, user_first_name, _ = await extract_user(c, m) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I warn myself?") + return + + if user_id in SUPPORT_STAFF: + await m.reply_text( + "They are support users, cannot be restriced, how am I then supposed to unrestrict them?", + ) + LOGGER.info( + f"{m.from_user.id} trying to resetwarn {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = {i[0] for i in (await admin_cache_reload(m, "reset_warns"))} + + if user_id in admins_group: + await m.reply_text("This user is admin in this chat, I can't warn them!") + return + + warn_db = Warns(m.chat.id) + warn_db.reset_warns(user_id) + await m.reply_text( + f"Warnings have been reset for {(await mention_html(user_first_name, user_id))}", + ) + return + + +@Gojo.on_message(command("warns") & filters.group) +async def list_warns(c: Gojo, m: Message): + + user_id, user_first_name, _ = await extract_user(c, m) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I warn myself?") + return + + if user_id in SUPPORT_STAFF: + await m.reply_text("This user has no warns!") + LOGGER.info( + f"{m.from_user.id} trying to check warns of {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = {i[0] for i in (await admin_cache_reload(m, "warns"))} + + if user_id in admins_group: + await m.reply_text( + "This user is admin in this chat, they don't have any warns!", + ) + return + + warn_db = Warns(m.chat.id) + warn_settings_db = WarnSettings(m.chat.id) + warns, num_warns = warn_db.get_warns(user_id) + warn_settings = warn_settings_db.get_warnings_settings() + if not warns: + await m.reply_text("This user has no warns!") + return + msg = f"{(await mention_html(user_first_name,user_id))} has {num_warns}/{warn_settings['warn_limit']} warns!\n\nReasons:\n" + msg += "\n".join([("- No reason" if i is None else f" - {i}") for i in warns]) + await m.reply_text(msg) + return + + +@Gojo.on_message( + command(["rmwarn", "removewarn"]) & restrict_filter, +) +async def remove_warn(c: Gojo, m: Message): + + if not len(m.command) > 1 and not m.reply_to_message: + await m.reply_text( + "I can't remove warns of nothing! Tell me user whose warn should be removed!", + ) + return + + user_id, user_first_name, _ = await extract_user(c, m) + + if user_id == Config.BOT_ID: + await m.reply_text("Huh, why would I warn myself?") + return + + if user_id in SUPPORT_STAFF: + await m.reply_text("This user has no warns!") + LOGGER.info( + f"{m.from_user.id} trying to remove warns of {user_id} (SUPPORT_STAFF) in {m.chat.id}", + ) + return + + try: + admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admins_group = {i[0] for i in (await admin_cache_reload(m, "rmwarn"))} + + if user_id in admins_group: + await m.reply_text( + "This user is admin in this chat, they don't have any warns!", + ) + return + + warn_db = Warns(m.chat.id) + warns, _ = warn_db.get_warns(user_id) + if not warns: + await m.reply_text("This user has no warnings!") + return + + _, num_warns = warn_db.remove_warn(user_id) + await m.reply_text( + ( + f"{(await mention_html(user_first_name,user_id))} now has {num_warns} warnings!\n" + "Their last warn was removed." + ), + ) + return + + +@Gojo.on_callback_query(filters.regex("^warn.")) +async def remove_last_warn_btn(c: Gojo, q: CallbackQuery): + + try: + admins_group = {i[0] for i in ADMIN_CACHE[q.message.chat.id]} + except KeyError: + admins_group = {i[0] for i in (await admin_cache_reload(q, "warn_btn"))} + + if q.from_user.id not in admins_group: + await q.answer("You are not allowed to use this!", show_alert=True) + return + + args = q.data.split(".") + action = args[1] + user_id = int(args[2]) + chat_id = int(q.message.chat.id) + user = Users.get_user_info(int(user_id)) + user_first_name = user["name"] + + if action == "remove": + warn_db = Warns(q.message.chat.id) + _, num_warns = warn_db.remove_warn(user_id) + await q.message.edit_text( + ( + f"Admin {(await mention_html(q.from_user.first_name, q.from_user.id))} " + "removed last warn for " + f"{(await mention_html(user_first_name, user_id))}\n" + f"Current Warnings: {num_warns}" + ), + ) + if action == "kick": + try: + await c.kick_chat_member(chat_id, user_id, until_date=int(time() + 45)) + await q.message.edit_text( + ( + f"Admin {(await mention_html(q.from_user.first_name, q.from_user.id))} " + "kicked user " + f"{(await mention_html(user_first_name, user_id))} for last warning!" + ), + ) + except RPCError as err: + await q.message.edit_text( + f"🛑 Failed to Kick\nError:\n{err}", + ) + + await q.answer() + return + + +@Gojo.on_message(command(["warnings", "warnsettings"]) & admin_filter) +async def get_settings(_, m: Message): + warn_settings_db = WarnSettings(m.chat.id) + settings = warn_settings_db.get_warnings_settings() + await m.reply_text( + ( + "This group has these following settings:\n" + f"Warn Limit: {settings['warn_limit']}\n" + f"Warn Mode: {settings['warn_mode']}" + ), + ) + return + + +@Gojo.on_message(command("warnmode") & admin_filter) +async def warnmode(_, m: Message): + warn_settings_db = WarnSettings(m.chat.id) + if len(m.text.split()) > 1: + wm = (m.text.split(None, 1)[1]).lower() + if wm not in ("kick", "ban", "mute"): + await m.reply_text( + ( + "Please choose a valid warn mode!" + "Valid options are: ban,kick,mute" + ), + ) + return + warnmode_var = warn_settings_db.set_warnmode(wm) + await m.reply_text(f"Warn Mode has been set to: {warnmode_var}") + return + warnmode_var = warn_settings_db.get_warnmode() + await m.reply_text(f"This chats current Warn Mode is: {warnmode_var}") + return + + +@Gojo.on_message(command("warnlimit") & admin_filter) +async def warnlimit(_, m: Message): + warn_settings_db = WarnSettings(m.chat.id) + if len(m.text.split()) > 1: + wl = int(m.text.split(None, 1)[1]) + if not isinstance(wl, int): + await m.reply_text("Warn Limit can only be a number!") + return + warnlimit_var = warn_settings_db.set_warnlimit(wl) + await m.reply_text(f"Warn Limit has been set to: {warnlimit_var}") + return + warnlimit_var = warn_settings_db.get_warnlimit() + await m.reply_text(f"This chats current Warn Limit is: {warnlimit_var}") + return + + +__PLUGIN__ = "warnings" + +__alt_name__ = ["warn", "warning", "warns"] diff --git a/Powers/plugins/watchers.py b/Powers/plugins/watchers.py new file mode 100644 index 0000000000000000000000000000000000000000..913944ea05a4df72db037008ca1b73366faffbbf --- /dev/null +++ b/Powers/plugins/watchers.py @@ -0,0 +1,229 @@ +from re import escape as re_escape +from time import time +from traceback import format_exc + +from pyrogram import filters +from pyrogram.errors import ChatAdminRequired, RPCError, UserAdminInvalid +from pyrogram.types import ChatPermissions, Message + +from Powers import LOGGER, MESSAGE_DUMP, SUPPORT_STAFF +from Powers.bot_class import Gojo +from Powers.database.antispam_db import ANTISPAM_BANNED, GBan +from Powers.database.approve_db import Approve +from Powers.database.blacklist_db import Blacklist +from Powers.database.group_blacklist import BLACKLIST_CHATS +from Powers.database.pins_db import Pins +from Powers.database.warns_db import Warns, WarnSettings +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, admin_cache_reload +from Powers.utils.parser import mention_html +from Powers.utils.regex_utils import regex_searcher + +# Initialise +gban_db = GBan() + + +@Gojo.on_message(filters.linked_channel) +async def antichanpin_cleanlinked(c: Gojo, m: Message): + try: + msg_id = m.message_id + pins_db = Pins(m.chat.id) + curr = pins_db.get_settings() + if curr["antichannelpin"]: + await c.unpin_chat_message(chat_id=m.chat.id, message_id=msg_id) + LOGGER.info(f"AntiChannelPin: msgid-{m.message_id} unpinned in {m.chat.id}") + if curr["cleanlinked"]: + await c.delete_messages(m.chat.id, msg_id) + LOGGER.info(f"CleanLinked: msgid-{m.message_id} cleaned in {m.chat.id}") + except ChatAdminRequired: + await m.reply_text( + "Disabled antichannelpin as I don't have enough admin rights!", + ) + pins_db.antichannelpin_off() + LOGGER.warning(f"Disabled antichannelpin in {m.chat.id} as i'm not an admin.") + except Exception as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return + + +@Gojo.on_message(filters.text & filters.group, group=5) +async def bl_watcher(_, m: Message): + if m and not m.from_user: + return + + bl_db = Blacklist(m.chat.id) + + async def perform_action_blacklist(m: Message, action: str, trigger: str): + if action == "kick": + await m.chat.kick_member(m.from_user.id, int(time() + 45)) + await m.reply_text( + tlang(m, "blacklist.bl_watcher.action_kick").format( + user=m.from_user.username or f"{m.from_user.first_name}", + ), + ) + + elif action == "ban": + ( + await m.chat.kick_member( + m.from_user.id, + ) + ) + await m.reply_text( + tlang(m, "blacklist.bl_watcher.action_ban").format( + user=m.from_user.username or f"{m.from_user.first_name}", + ), + ) + + elif action == "mute": + await m.chat.restrict_member( + m.from_user.id, + ChatPermissions(), + ) + + await m.reply_text( + tlang(m, "blacklist.bl_watcher.action_mute").format( + user=m.from_user.username or f"{m.from_user.first_name}", + ), + ) + + elif action == "warn": + warns_settings_db = WarnSettings(m.chat.id) + warns_db = Warns(m.chat.id) + warn_settings = warns_settings_db.get_warnings_settings() + warn_reason = bl_db.get_reason() + _, num = warns_db.warn_user(m.from_user.id, warn_reason) + if num >= warn_settings["warn_limit"]: + if warn_settings["warn_mode"] == "kick": + await m.chat.ban_member( + m.from_user.id, + until_date=int(time() + 45), + ) + action = "kicked" + elif warn_settings["warn_mode"] == "ban": + await m.chat.ban_member(m.from_user.id) + action = "banned" + elif warn_settings["warn_mode"] == "mute": + await m.chat.restrict_member(m.from_user.id, ChatPermissions()) + action = "muted" + await m.reply_text( + ( + f"Warnings {num}/{warn_settings['warn_limit']}\n" + f"{(await mention_html(m.from_user.first_name, m.from_user.id))} has been {action}!" + ), + ) + return + await m.reply_text( + ( + f"{(await mention_html(m.from_user.first_name, m.from_user.id))} warned {num}/{warn_settings['warn_limit']}\n" + # f"Last warn was for:\n{warn_reason}" + f"Last warn was for:\n{warn_reason.format(trigger)}" + ), + ) + return + + if m.from_user.id in SUPPORT_STAFF: + # Don't work on Support Staff! + return + + # If no blacklists, then return + chat_blacklists = bl_db.get_blacklists() + if not chat_blacklists: + return + + # Get admins from admin_cache, reduces api calls + try: + admin_ids = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_ids = await admin_cache_reload(m, "blacklist_watcher") + + if m.from_user.id in admin_ids: + return + + # Get approved user from cache/database + app_users = Approve(m.chat.id).list_approved() + if m.from_user.id in {i[0] for i in app_users}: + return + + # Get action for blacklist + action = bl_db.get_action() + for trigger in chat_blacklists: + pattern = r"( |^|[^\w])" + re_escape(trigger) + r"( |$|[^\w])" + match = await regex_searcher(pattern, m.text.lower()) + if not match: + continue + if match: + try: + await perform_action_blacklist(m, action, trigger) + LOGGER.info( + f"{m.from_user.id} {action}ed for using blacklisted word {trigger} in {m.chat.id}", + ) + await m.delete() + except RPCError as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + break + return + + +@Gojo.on_message(filters.user(list(ANTISPAM_BANNED)) & filters.group) +async def gban_watcher(c: Gojo, m: Message): + from Powers import SUPPORT_GROUP + + if m and not m.from_user: + return + + try: + _banned = gban_db.check_gban(m.from_user.id) + except Exception as ef: + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + + if _banned: + try: + await m.chat.ban_member(m.from_user.id) + await m.delete(m.message_id) # Delete users message! + await m.reply_text( + (tlang(m, "antispam.watcher_banned")).format( + user_gbanned=( + await mention_html(m.from_user.first_name, m.from_user.id) + ), + SUPPORT_GROUP=SUPPORT_GROUP, + ), + ) + LOGGER.info(f"Banned user {m.from_user.id} in {m.chat.id} due to antispam") + return + except (ChatAdminRequired, UserAdminInvalid): + # Bot not admin in group and hence cannot ban users! + # TO-DO - Improve Error Detection + LOGGER.info( + f"User ({m.from_user.id}) is admin in group {m.chat.title} ({m.chat.id})", + ) + except RPCError as ef: + await c.send_message( + MESSAGE_DUMP, + tlang(m, "antispam.gban.gban_error_log").format( + chat_id=m.chat.id, + ef=ef, + ), + ) + return + + +@Gojo.on_message(filters.chat(BLACKLIST_CHATS)) +async def bl_chats_watcher(c: Gojo, m: Message): + from Powers import SUPPORT_GROUP + + await c.send_message( + m.chat.id, + ( + "This is a blacklisted group!\n" + f"For Support, Join @{SUPPORT_GROUP}\n" + "Now, I'm outta here!" + ), + ) + await c.leave_chat(m.chat.id) + LOGGER.info(f"Joined and Left blacklisted chat {m.chat.id}") + return diff --git a/Powers/tr_engine/__init__.py b/Powers/tr_engine/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..61f8de1961e467a8a353eb8805f64a84aed29d31 --- /dev/null +++ b/Powers/tr_engine/__init__.py @@ -0,0 +1,5 @@ +from Powers.tr_engine.tr_engine import lang_dict, tlang + + +async def useless_func(): + return lang_dict, tlang diff --git a/Powers/tr_engine/tr_engine.py b/Powers/tr_engine/tr_engine.py new file mode 100644 index 0000000000000000000000000000000000000000..9c56eb34e0ef71757f6687af47df2bf9fd248cba --- /dev/null +++ b/Powers/tr_engine/tr_engine.py @@ -0,0 +1,72 @@ +from functools import reduce +from glob import glob +from operator import getitem +from os import path +from threading import RLock +from traceback import format_exc + +from pyrogram.types import CallbackQuery +from yaml import FullLoader +from yaml import load as load_yml + +from Powers import ENABLED_LOCALES, LOGGER +from Powers.database.lang_db import Langs + +# Initialise +LANG_LOCK = RLock() + + +def cache_localizations(files): + """Get all translated strings from files.""" + ldict = {lang: {} for lang in ENABLED_LOCALES} + for file in files: + lang_name = (file.split(path.sep)[1]).replace(".yml", "") + lang_data = load_yml(open(file, encoding="utf-8"), Loader=FullLoader) + ldict[lang_name] = lang_data + return ldict + + +# Get all translation files +lang_files = [] +for locale in ENABLED_LOCALES: + lang_files += glob(path.join("locales", f"{locale}.yml")) +lang_dict = cache_localizations(lang_files) + + +def tlang(m, user_msg): + """Main function for getting the string of preferred language.""" + with LANG_LOCK: + default_lang = "en" + + m_args = user_msg.split(".") # Split in a list + + # Get Chat + if isinstance(m, CallbackQuery): + m = m.message + + # Get language of user from database, default = 'en' (English) + try: + lang = Langs(m.chat.id).get_lang() + except Exception as ef: + LOGGER.error(f"Lang Error: {ef}") + lang = default_lang + LOGGER.error(format_exc()) + + # Raise exception if lang_code not found + if lang not in ENABLED_LOCALES: + LOGGER.error("Non-enabled locale used by user!") + lang = default_lang + + # Get lang + m_args.insert(0, lang) + m_args.insert(1, "strings") + + try: + txt = reduce(getitem, m_args, lang_dict) + except KeyError: + m_args.pop(0) + m_args.insert(0, default_lang) + txt = reduce(getitem, m_args, lang_dict) + LOGGER.error(format_exc()) + + return txt diff --git a/Powers/utils/__init__.py b/Powers/utils/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/Powers/utils/admin_check.py b/Powers/utils/admin_check.py new file mode 100644 index 0000000000000000000000000000000000000000..ebda06cf59c77e2504f8de7d3fa6c7e8ee949604 --- /dev/null +++ b/Powers/utils/admin_check.py @@ -0,0 +1,91 @@ +from traceback import format_exc + +from pyrogram.types import CallbackQuery, Message + +from Powers import DEV_USERS, LOGGER, OWNER_ID, SUDO_USERS + +SUDO_LEVEL = SUDO_USERS + DEV_USERS + [int(OWNER_ID)] +DEV_LEVEL = DEV_USERS + [int(OWNER_ID)] + + +async def admin_check(m: Message or CallbackQuery) -> bool: + """Checks if user is admin or not.""" + if isinstance(m, Message): + user_id = m.from_user.id + if isinstance(m, CallbackQuery): + user_id = m.message.from_user.id + + try: + if user_id in SUDO_LEVEL: + return True + except Exception as ef: + LOGGER.error(format_exc()) + + user = await m.chat.get_member(user_id) + admin_strings = ("creator", "administrator") + + if user.status not in admin_strings: + reply = "Nigga, you're not admin, don't try this explosive shit." + try: + await m.edit_text(reply) + except Exception as ef: + await m.reply_text(reply) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return False + + return True + + +async def check_rights(m: Message or CallbackQuery, rights) -> bool: + """Check Admin Rights""" + if isinstance(m, Message): + user_id = m.from_user.id + chat_id = m.chat.id + app = m._client + if isinstance(m, CallbackQuery): + user_id = m.message.from_user.id + chat_id = m.message.chat.id + app = m.message._client + + user = await app.get_chat_member(chat_id, user_id) + if user.status == "member": + return False + admin_strings = ("creator", "administrator") + if user.status in admin_strings: + return bool(getattr(user, rights, None)) + return False + + +async def owner_check(m: Message or CallbackQuery) -> bool: + """Checks if user is owner or not.""" + if isinstance(m, Message): + user_id = m.from_user.id + if isinstance(m, CallbackQuery): + user_id = m.message.from_user.id + m = m.message + + try: + if user_id in SUDO_LEVEL: + return True + except Exception as ef: + LOGGER.info(ef, m) + LOGGER.error(format_exc()) + + user = await m.chat.get_member(user_id) + + if user.status != "creator": + if user.status == "administrator": + reply = "Stay in your limits, or lose adminship too." + else: + reply = "You ain't even admin, what are you trying to do?" + try: + await m.edit_text(reply) + except Exception as ef: + await m.reply_text(reply) + LOGGER.error(ef) + LOGGER.error(format_exc()) + + return False + + return True diff --git a/Powers/utils/caching.py b/Powers/utils/caching.py new file mode 100644 index 0000000000000000000000000000000000000000..068e9d09d125c2a94d0730b3dccaf14eee0faa77 --- /dev/null +++ b/Powers/utils/caching.py @@ -0,0 +1,52 @@ +from threading import RLock +from time import perf_counter, time +from typing import List + +from cachetools import TTLCache +from pyrogram.types import CallbackQuery +from pyrogram.types.messages_and_media.message import Message + +from Powers import LOGGER + +THREAD_LOCK = RLock() + +# admins stay cached for 30 mins +ADMIN_CACHE = TTLCache(maxsize=512, ttl=(60 * 30), timer=perf_counter) +# Block from refreshing admin list for 10 mins +TEMP_ADMIN_CACHE_BLOCK = TTLCache(maxsize=512, ttl=(60 * 10), timer=perf_counter) + + +async def admin_cache_reload(m: Message or CallbackQuery, status=None) -> List[int]: + start = time() + with THREAD_LOCK: + + if isinstance(m, CallbackQuery): + m = m.message + + global ADMIN_CACHE, TEMP_ADMIN_CACHE_BLOCK + if status is not None: + TEMP_ADMIN_CACHE_BLOCK[m.chat.id] = status + + try: + if TEMP_ADMIN_CACHE_BLOCK[m.chat.id] in ("autoblock", "manualblock"): + return + except KeyError: + # Because it might be first time when admn_list is being reloaded + pass + + admin_list = [ + ( + z.user.id, + (("@" + z.user.username) if z.user.username else z.user.first_name), + z.is_anonymous, + ) + async for z in m.chat.iter_members(filter="administrators") + if not z.user.is_deleted + ] + ADMIN_CACHE[m.chat.id] = admin_list + LOGGER.info( + f"Loaded admins for chat {m.chat.id} in {round((time() - start), 3)}s due to '{status}'", + ) + TEMP_ADMIN_CACHE_BLOCK[m.chat.id] = "autoblock" + + return admin_list diff --git a/Powers/utils/clean_file.py b/Powers/utils/clean_file.py new file mode 100644 index 0000000000000000000000000000000000000000..9a1c5a1009f1f290cb5824ab74a2de45d07c3912 --- /dev/null +++ b/Powers/utils/clean_file.py @@ -0,0 +1,19 @@ +async def remove_markdown_and_html(text: str) -> str: + return await clean_markdown(await clean_html(text)) + + +async def clean_html(text: str) -> str: + return ( + text.replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + .replace("", "") + ) + + +async def clean_markdown(text: str) -> str: + return text.replace("`", "").replace("**", "").replace("__", "") diff --git a/Powers/utils/cmd_senders.py b/Powers/utils/cmd_senders.py new file mode 100644 index 0000000000000000000000000000000000000000..1b2e791be3ef5ae9713e7713a1296227c3a736db --- /dev/null +++ b/Powers/utils/cmd_senders.py @@ -0,0 +1,19 @@ +from Powers.bot_class import Gojo +from Powers.utils.msg_types import Types + + +async def send_cmd(client: Gojo, msgtype: int): + GET_FORMAT = { + Types.TEXT.value: client.send_message, + Types.DOCUMENT.value: client.send_document, + Types.PHOTO.value: client.send_photo, + Types.VIDEO.value: client.send_video, + Types.STICKER.value: client.send_sticker, + Types.AUDIO.value: client.send_audio, + Types.VOICE.value: client.send_voice, + Types.VIDEO_NOTE.value: client.send_video_note, + Types.ANIMATION.value: client.send_animation, + Types.ANIMATED_STICKER.value: client.send_sticker, + Types.CONTACT: client.send_contact, + } + return GET_FORMAT[msgtype] diff --git a/Powers/utils/custom_filters.py b/Powers/utils/custom_filters.py new file mode 100644 index 0000000000000000000000000000000000000000..15f4e6589f01683ec89e28afc16537abd0a3453c --- /dev/null +++ b/Powers/utils/custom_filters.py @@ -0,0 +1,312 @@ +from re import compile as compile_re +from re import escape +from shlex import split +from typing import List, Union + +from pyrogram.errors import RPCError, UserNotParticipant +from pyrogram.filters import create +from pyrogram.types import CallbackQuery, Message + +from Powers import DEV_USERS, OWNER_ID, SUDO_USERS +from Powers.database.disable_db import DISABLED_CMDS +from Powers.tr_engine import tlang +from Powers.utils.caching import ADMIN_CACHE, admin_cache_reload +from Powers.vars import Config + +SUDO_LEVEL = set(SUDO_USERS + DEV_USERS + [int(OWNER_ID)]) +DEV_LEVEL = set(DEV_USERS + [int(OWNER_ID)]) + + +def command( + commands: Union[str, List[str]], + case_sensitive: bool = False, + owner_cmd: bool = False, + dev_cmd: bool = False, + sudo_cmd: bool = False, +): + async def func(flt, _, m: Message): + + if m and not m.from_user: + return False + + if m.from_user.is_bot: + return False + + if any([m.forward_from_chat, m.forward_from]): + return False + + if owner_cmd and (m.from_user.id != OWNER_ID): + # Only owner allowed to use this...! + return False + + if dev_cmd and (m.from_user.id not in DEV_LEVEL): + # Only devs allowed to use this...! + return False + + if sudo_cmd and (m.from_user.id not in SUDO_LEVEL): + # Only sudos and above allowed to use it + return False + + text: str = m.text or m.caption + if not text: + return False + regex = r"^[{prefix}](\w+)(@{bot_name})?(?: |$)(.*)".format( + prefix="|".join(escape(x) for x in Config.PREFIX_HANDLER), + bot_name=Config.BOT_USERNAME, + ) + matches = compile_re(regex).search(text) + if matches: + m.command = [matches.group(1)] + if matches.group(1) not in flt.commands: + return False + if m.chat.type == "supergroup": + try: + disable_list = DISABLED_CMDS[m.chat.id].get("commands", []) + status = str(DISABLED_CMDS[m.chat.id].get("action", "none")) + except KeyError: + disable_list = [] + status = "none" + try: + user_status = (await m.chat.get_member(m.from_user.id)).status + except UserNotParticipant: + # i.e anon admin + user_status = "administrator" + except ValueError: + # i.e. PM + user_status = "creator" + if str(matches.group(1)) in disable_list and user_status not in ( + "creator", + "administrator", + ): + try: + if status == "del": + await m.delete() + except RPCError: + pass + return False + if matches.group(3) == "": + return True + try: + for arg in split(matches.group(3)): + m.command.append(arg) + except ValueError: + pass + return True + return False + + commands = commands if type(commands) is list else [commands] + commands = {c if case_sensitive else c.lower() for c in commands} + + return create( + func, + "NormalCommandFilter", + commands=commands, + case_sensitive=case_sensitive, + ) + + +async def bot_admin_check_func(_, __, m: Message or CallbackQuery): + """Check if bot is Admin or not.""" + + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + return False + + # Telegram and GroupAnonyamousBot + if m.sender_chat: + return True + + try: + admin_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_group = { + i[0] for i in await admin_cache_reload(m, "custom_filter_update") + } + except ValueError as ef: + # To make language selection work in private chat of user, i.e. PM + if ("The chat_id" and "belongs to a user") in ef: + return True + + if Config.BOT_ID in admin_group: + return True + + await m.reply_text( + "I am not an admin to recive updates in this group; Mind Promoting?", + ) + + return False + + +async def admin_check_func(_, __, m: Message or CallbackQuery): + """Check if user is Admin or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + return False + + # Telegram and GroupAnonyamousBot + if m.sender_chat: + return True + + # Bypass the bot devs, sudos and owner + if m.from_user.id in SUDO_LEVEL: + return True + + try: + admin_group = {i[0] for i in ADMIN_CACHE[m.chat.id]} + except KeyError: + admin_group = { + i[0] for i in await admin_cache_reload(m, "custom_filter_update") + } + except ValueError as ef: + # To make language selection work in private chat of user, i.e. PM + if ("The chat_id" and "belongs to a user") in ef: + return True + + if m.from_user.id in admin_group: + return True + + await m.reply_text(tlang(m, "general.no_admin_cmd_perm")) + + return False + + +async def owner_check_func(_, __, m: Message or CallbackQuery): + """Check if user is Owner or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + return False + + # Bypass the bot devs, sudos and owner + if m.from_user.id in DEV_LEVEL: + return True + + user = await m.chat.get_member(m.from_user.id) + + if user.status == "creator": + status = True + else: + status = False + if user.status == "administrator": + msg = "You're an admin only, stay in your limits!" + else: + msg = "Do you think that you can execute owner commands?" + await m.reply_text(msg) + + return status + + +async def restrict_check_func(_, __, m: Message or CallbackQuery): + """Check if user can restrict users or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + return False + + # Bypass the bot devs, sudos and owner + if m.from_user.id in DEV_LEVEL: + return True + + user = await m.chat.get_member(m.from_user.id) + + if user.can_restrict_members or user.status == "creator": + status = True + else: + status = False + await m.reply_text(tlang(m, "admin.no_restrict_perm")) + + return status + + +async def promote_check_func(_, __, m): + """Check if user can promote users or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + return False + + # Bypass the bot devs, sudos and owner + if m.from_user.id in DEV_LEVEL: + return True + + user = await m.chat.get_member(m.from_user.id) + + if user.can_promote_members or user.status == "creator": + status = True + else: + status = False + await m.reply_text(tlang(m, "admin.promote.no_promote_perm")) + + return status + + +async def changeinfo_check_func(_, __, m): + """Check if user can change info or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + await m.reply_text("This command is made to be used in groups not in pm!") + return False + + # Telegram and GroupAnonyamousBot + if m.sender_chat: + return True + + # Bypass the bot devs, sudos and owner + if m.from_user.id in SUDO_LEVEL: + return True + + user = await m.chat.get_member(m.from_user.id) + + if user.can_change_info or user.status == "creator": + status = True + else: + status = False + await m.reply_text("You don't have: can_change_info permission!") + + return status + + +async def can_pin_message_func(_, __, m): + """Check if user can change info or not.""" + if isinstance(m, CallbackQuery): + m = m.message + + if m.chat.type != "supergroup": + await m.reply_text("This command is made to be used in groups not in pm!") + return False + + # Telegram and GroupAnonyamousBot + if m.sender_chat: + return True + + # Bypass the bot devs, sudos and owner + if m.from_user.id in SUDO_LEVEL: + return True + + user = await m.chat.get_member(m.from_user.id) + + if user.can_pin_messages or user.status == "creator": + status = True + else: + status = False + await m.reply_text("You don't have: can_pin_messages permission!") + + return status + + +admin_filter = create(admin_check_func) +owner_filter = create(owner_check_func) +restrict_filter = create(restrict_check_func) +promote_filter = create(promote_check_func) +bot_admin_filter = create(bot_admin_check_func) +can_change_filter = create(changeinfo_check_func) +can_pin_filter = create(can_pin_message_func) diff --git a/Powers/utils/extract_user.py b/Powers/utils/extract_user.py new file mode 100644 index 0000000000000000000000000000000000000000..909355dec7f96530af12f10d970b5d82a1b558a1 --- /dev/null +++ b/Powers/utils/extract_user.py @@ -0,0 +1,102 @@ +from traceback import format_exc +from typing import Tuple + +from pyrogram.types.messages_and_media.message import Message + +from Powers import LOGGER +from Powers.bot_class import Gojo +from Powers.database.users_db import Users + + +async def extract_user(c: Gojo, m: Message) -> Tuple[int, str, str]: + """Extract the user from the provided message.""" + user_id = None + user_first_name = None + user_name = None + + if m.reply_to_message and m.reply_to_message.from_user: + user_id = m.reply_to_message.from_user.id + user_first_name = m.reply_to_message.from_user.first_name + user_name = m.reply_to_message.from_user.username + + elif len(m.text.split()) > 1: + if len(m.entities) > 1: + required_entity = m.entities[1] + if required_entity.type == "text_mention": + user_id = required_entity.user.id + user_first_name = required_entity.user.first_name + user_name = required_entity.user.username + elif required_entity.type in ("mention", "phone_number"): + # new long user ids are identified as phone_number + user_found = m.text[ + required_entity.offset : ( + required_entity.offset + required_entity.length + ) + ] + + try: + user_found = int(user_found) + except (ValueError, Exception) as ef: + if "invalid literal for int() with base 10:" in str(ef): + user_found = str(user_found) + else: + LOGGER.error(ef) + LOGGER.error(format_exc()) + + try: + user = Users.get_user_info(user_found) + user_id = user["_id"] + user_first_name = user["name"] + user_name = user["username"] + except KeyError: + # If user not in database + try: + user = await c.get_users(user_found) + except Exception as ef: + return await m.reply_text(f"User not found ! Error: {ef}") + user_id = user.id + user_first_name = user.first_name + user_name = user.username + except Exception as ef: + user_id = user_found + user_first_name = user_found + user_name = "" + LOGGER.error(ef) + LOGGER.error(format_exc()) + + else: + try: + user_id = int(m.text.split()[1]) + except (ValueError, Exception) as ef: + if "invalid literal for int() with base 10:" in str(ef): + user_id = ( + str(m.text.split()[1]) + if (m.text.split()[1]).startswith("@") + else None + ) + else: + user_id = m.text.split()[1] + LOGGER.error(ef) + LOGGER.error(format_exc()) + + if user_id is not None: + try: + user = Users.get_user_info(user_id) + user_first_name = user["name"] + user_name = user["username"] + except Exception as ef: + try: + user = await c.get_users(user_id) + except Exception as ef: + return await m.reply_text(f"User not found ! Error: {ef}") + user_first_name = user.first_name + user_name = user.username + LOGGER.error(ef) + LOGGER.error(format_exc()) + + else: + user_id = m.from_user.id + user_first_name = m.from_user.first_name + user_name = m.from_user.username + + return user_id, user_first_name, user_name diff --git a/Powers/utils/fun_strings.py b/Powers/utils/fun_strings.py new file mode 100644 index 0000000000000000000000000000000000000000..76c6378a7ad45caa3a8d83c79c54932f72f28e88 --- /dev/null +++ b/Powers/utils/fun_strings.py @@ -0,0 +1,577 @@ +RUN_STRINGS = ( + "Now you see me, now you don't." + "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", + "Get back here!", + "REEEEEEEEEEEEEEEEEE!!!!!!!", + "Look out for the wall!", + "Don't leave me alone with them!!", + "You've got company!", + "Chotto matte!", + "Yare yare daze", + "*Naruto run activated*", + "*Nezuko run activated*", + "Huh? what? did they get away?", + "Get back here!", + "Look out for the wall!", + "Don't leave me alone with them!!", + "You've got company!", + "Chotto matte!", + "Yare yare daze", + "Hey take responsibilty for what you just did!", + "May the odds be ever in your favour.", + "Run everyone, they just dropped a bomb 💣💣", + "And they disappeared forever, never to be seen again.", + "Legend has it, they're still running.", + "Hasta la vista, baby.", + "Ah, what a waste. I liked that one.", + "As The Doctor would say... RUN!", +) + +SLAP_GOJO_TEMPLATES = ( + "Slap me one more time and I'll mute you.", + "Don't again dare to slap me asshole.", +) + +SLAP_TEMPLATES = ( + "{user2} was killed by magic.", + "{user2} starved without pats.", + "{user2} was knocked into the void by {user1}.", + "{user2} fainted.", + "{user2} is out of usable Pokemon! {user2} whited out!.", + "{user2} is out of usable Pokemon! {user2} blacked out!.", + "{user2} got rekt.", + "{user2}'s melon was split by {user1}.", + "{user2} was sliced and diced by {user1}.", + "{user2} played hot-potato with a grenade.", + "{user2} was knifed by {user1}.", + "{user2} ate a grenade.", + "{user2} is what's for dinner!", + "{user2} was terminated by {user1}.", + "{user1} spammed {user2}'s email.", + "{user1} RSA-encrypted {user2} and deleted the private key.", + "{user1} put {user2} in the friendzone.", + "{user1} slaps {user2} with a DMCA takedown request!", + "{user2} got a house call from Doctor {user1}.", + "{user1} beheaded {user2}.", + "{user2} got stoned...by an angry mob.", + "{user1} sued the pants off {user2}.", + "{user2} was one-hit KO'd by {user1}.", + "{user1} sent {user2} down the memory hole.", + "{user2} was a mistake. - '{user1}' ", + "{user2} was made redundant.", + "{user1} {hits} {user2} with a bat!.", + "{user1} {hits} {user2} with a Taijutsu Kick!.", + "{user1} {hits} {user2} with X-Gloves!.", + "{user1} {hits} {user2} with a Jet Punch!.", + "{user1} {hits} {user2} with a Jet Pistol!.", + "{user1} {hits} {user2} with a United States of Smash!.", + "{user1} {hits} {user2} with a Detroit Smash!.", + "{user1} {hits} {user2} with a Texas Smash!.", + "{user1} {hits} {user2} with a California Smash!.", + "{user1} {hits} {user2} with a New Hampshire Smash!.", + "{user1} {hits} {user2} with a Missouri Smash!.", + "{user1} {hits} {user2} with a Carolina Smash!.", + "{user1} {hits} {user2} with a King Kong Gun!.", + "{user1} {hits} {user2} with a baseball bat - metal one.!.", + "*Serious punches {user2}*.", + "*Normal punches {user2}*.", + "*Consecutive Normal punches {user2}*.", + "*Two Handed Consecutive Normal Punches {user2}*.", + "*Ignores {user2} to let them die of embarassment*.", + "*points at {user2}* What's with this sassy... lost child?.", + "*Hits {user2} with a Fire Tornado*.", + "{user1} pokes {user2} in the eye !", + "{user1} pokes {user2} on the sides!", + "{user1} pokes {user2}!", + "{user1} pokes {user2} with a needle!", + "{user1} pokes {user2} with a pen!", + "{user1} pokes {user2} with a stun gun!", + "{user2} is secretly a Furry!", + "Hey Everybody! {user1} is asking me to be mean!", + "( ・_・)ノ⌒●~* (・.・;) <-{user2}", + "Take this {user2}\n(ノ゚Д゚)ノ ))))●~* ", + "Here {user2} hold this\n(`・ω・)つ ●~*", + "( ・_・)ノΞ●~* {user2}\nDieeeee!!.", + "( ・∀・)r鹵~<≪巛;゚Д゚)ノ\n*Bug sprays {user2}*.", + "( ゚Д゚)ノ占~<巛巛巛.\n-{user2} You pest!", + "( う-´)づ︻╦̵̵̿╤── \(˚☐˚”)/ {user2}.", + "{user1} {hits} {user2} with a {item}.", + "{user1} {hits} {user2} in the face with a {item}.", + "{user1} {hits} {user2} around a bit with a {item}.", + "{user1} {throws} a {item} at {user2}.", + "{user1} grabs a {item} and {throws} it at {user2}'s face.", + "{user1} launches a {item} in {user2}'s general direction.", + "{user1} starts slapping {user2} silly with a {item}.", + "{user1} pins {user2} down and repeatedly {hits} them with a {item}.", + "{user1} grabs up a {item} and {hits} {user2} with it.", + "{user1} ties {user2} to a chair and {throws} a {item} at them.", + "{user1} gave a friendly push to help {user2} learn to swim in lava.", + "{user1} bullied {user2}.", + "Nyaan ate {user2}'s leg. *nomnomnom*", + "{user1} {throws} a master ball at {user2}, resistance is futile.", + "{user1} hits {user2} with an action beam...bbbbbb (ง・ω・)ง ====*", + "{user1} ara ara's {user2}.", + "{user1} ora ora's {user2}.", + "{user1} muda muda's {user2}.", + "{user2} was turned into a Jojo reference!", + "{user1} hits {user2} with {item}.", + "Round 2!..Ready? .. FIGHT!!", + "WhoPixel will oof {user2} to infinity and beyond.", + "{user2} ate a bat and discovered a new disease.", + "{user1} folded {user2} into a paper plane", + "{user1} served {user2} some bat soup.", + "{user2} was sent to his home, the planet of the apes.", + "{user1} kicked {user2} out of a moving train.", + "{user2} just killed John Wick’s dog.", + "{user1} performed an Avada Kedavra spell on {user2}.", + "{user1} subjected {user2} to a fiery furnace.", + "Sakura Haruno just got more useful than {user2}", + "{user1} unplugged {user2}'s life support.", + "{user1} subscribed {user2}' to 5 years of bad internet.", + "You know what’s worse than Dad jokes? {user2}!", + "{user1} took all of {user2}'s cookies.", + "{user2} wa mou.......Shindeiru! - {user1}.", + "{user2} lost his race piece!", #No game no life reference + "Shut up {user2}, you are just {user2}.", #No game no life reference + "{user1} hits {user2} with Aka si anse!", #No game no life reference + "@NeoTheKitty scratches {user2}", #Pixels pet cat - @NeoTheKitty + "Majin buu ate {user2}", #Dbz + "Goblin slayer slays {user2}", #Goblin Slayer + "{user2} was shot by {user1}.", + "{user2} walked into a cactus while trying to escape {user1}.", + "{user2} drowned whilst trying to escape {user1}.", + "{user2} fell into a patch of cacti.", + "{user2} went up in flames.", + "{user2} burned to death.", + "{user2} was burnt to a crisp whilst fighting {user1}.", + "{user2} was struck by lightning.", + "{user2} was slain by {user1}.", + "{user2} was killed by magic.", + "{user2} starved to death.", + "{user2} fell out of the world.", + "{user2} was knocked into the void by {user1}.", + "{user2}'s bones are scraped clean by the desolate wind.", + "{user2} fainted.", + "{user2} is out of usable Pokemon! {user2} whited out!.", + "{user2} is out of usable Pokemon! {user2} blacked out!.", + "{user2} says goodbye to this cruel world.", + "{user2} got rekt.", + "{user2} was sawn in half by {user1}.", + "{user2}'s melon was split by {user1}.", + "{user2} was sliced and diced by {user1}.", + "{user2}'s death put another notch in {user1}'s axe.", + "{user2} died from {user1}'s mysterious tropical disease.", + "{user2} played hot-potato with a grenade.", + "{user2} was knifed by {user1}.", + "{user2} ate a grenade.", + "{user2} is what's for dinner!", + "{user2} was terminated by {user1}.", + "{user2} was shot before being thrown out of a plane.", + "{user2} has encountered an error.", + "{user2} died and reincarnated as a goat.", + "{user1} threw {user2} off a building.", + "{user2} got a premature burial.", + "{user1} spammed {user2}'s email.", + "{user1} hit {user2} with a small, interstellar spaceship.", + "{user1} put {user2} in check-mate.", + "{user1} RSA-encrypted {user2} and deleted the private key.", + "{user1} put {user2} in the friendzone.", + "{user1} slaps {user2} with a DMCA takedown request!", + "{user2} died of hospital gangrene.", + "{user2} got a house call from Doctor {user1}.", + "{user1} beheaded {user2}.", + "{user2} got stoned...by an angry mob.", + "{user1} sued the pants off {user2}.", + "{user2} was one-hit KO'd by {user1}.", + "{user1} sent {user2} down the memory hole.", + "{user2} was a mistake. - '{user1}' ", + "{user1} checkmated {user2} in two moves.", + "{user2} was made redundant.", + "{user1} {hits} {user2} with a bat!.", + "{user1} {hits} {user2} with a Taijutsu Kick!.", + "{user1} {hits} {user2} with X-Gloves!.", + "{user1} {hits} {user2} with a Jet Punch!.", + "{user1} {hits} {user2} with a Jet Pistol!.", + "{user1} {hits} {user2} with a United States of Smash!.", + "{user1} {hits} {user2} with a Detroit Smash!.", + "{user1} {hits} {user2} with a Texas Smash!.", + "{user1} {hits} {user2} with a California Smash!.", + "{user1} {hits} {user2} with a New Hampshire Smash!.", + "{user1} {hits} {user2} with a Missouri Smash!.", + "{user1} {hits} {user2} with a Carolina Smash!.", + "{user1} {hits} {user2} with a King Kong Gun!.", + "{user1} {hits} {user2} with a baseball bat - metal one.!.", + "*Serious punches {user2}*.", + "*Normal punches {user2}*.", + "*Consecutive Normal punches {user2}*.", + "*Two Handed Consecutive Normal Punches {user2}*.", + "*Ignores {user2} to let them die of embarassment*.", + "*points at {user2}* What's with this sassy... lost child?.", + "*Hits {user2} with a Fire Tornado*.", + "{user1} pokes {user2} in the eye !", + "{user1} pokes {user2} on the sides!", + "{user1} pokes {user2}!", + "{user1} pokes {user2} with a needle!", + "{user1} pokes {user2} with a pen!", + "{user1} pokes {user2} with a stun gun!", + "{user2} is secretly a Furry!.", + "Hey Everybody! {user1} is asking me to be mean!", + "( ・_・)ノ⌒●~* (・.・;) <-{user2}", + "Take this {user2}\n(ノ゚Д゚)ノ ))))●~* ", + "Here {user2} hold this\n(`・ω・)つ ●~*", + "( ・_・)ノΞ●~* {user2}\nDieeeee!!.", + "( ・∀・)r鹵~<≪巛;゚Д゚)ノ\n*Bug sprays {user2}*.", + "( ゚Д゚)ノ占~<巛巛巛.\n-{user2} You pest!", + r"( う-´)づ︻╦̵̵̿╤── \(˚☐˚”)/ {user2}.", + "{user1} {hits} {user2} with a {item}.", + "{user1} {hits} {user2} in the face with a {item}.", + "{user1} {hits} {user2} around a bit with a {item}.", + "{user1} {throws} a {item} at {user2}.", + "{user1} grabs a {item} and {throws} it at {user2}'s face.", + "{user1} launches a {item} in {user2}'s general direction.", + "{user1} starts slapping {user2} silly with a {item}.", + "{user1} pins {user2} down and repeatedly {hits} them with a {item}.", + "{user1} grabs up a {item} and {hits} {user2} with it.", + "{user1} ties {user2} to a chair and {throws} a {item} at them.", + "{user1} gave a friendly push to help {user2} learn to swim in lava.", + "{user1} bullied {user2}.", + "Nyaan ate {user2}'s leg. *nomnomnom*", + "{user1} {throws} a master ball at {user2}, resistance is futile.", + "{user1} hits {user2} with an action beam...bbbbbb (ง・ω・)ง ====*", + "{user1} ara ara's {user2}.", + "{user1} ora ora's {user2}.", + "{user1} muda muda's {user2}.", + "{user2} was turned into a Jojo reference!", + "{user1} hits {user2} with {item}.", + "Round 2!..Ready? .. FIGHT!!", + "WhoPixel will oof {user2}, to infinity and beyond." + "{user2} ate a bat and discovered a new disease.", + "{user1} folded {user2} into a paper plane", + "{user1} exchanged {user2}'s life-force for a cup of chocolate.", + "{user2} did a 69 with a cactus.", + "{user1} served {user2} some bat soup.", + "{user2} was sent to his home, the planet of apes.", + "{user1} kicked {user2} out of a moving train.", + "{user1} traveled to the future and spat on {user2}'s grave.", + "{user2} died from talk-no-jutsu.", + "{user2} was placed as a carpet for a stomp dance competition.", + "{user2} just killed John Wick’s dog.", + "{user1} performed an Avada Kedavra spell on {user2}.", + "{user1} subjected {user2} to a fiery furnace.", + "Sakura Haruno just got more useful than {user2}", + "{user1} unplugged {user2}'s life support.", + "{user1} subscribed {user2}' to 5 years of bad internet.", + "{user1} learned why being a crocodile dentist was a bad idea.", + "You know what’s worse than Dad jokes? {user2}!", + "{user1} took all of {user2}'s cookies.", +) + +ITEMS = ( + "cast iron skillet", + "angry meow", + "cricket bat", + "wooden cane", + "shovel", + "toaster", + "book", + "laptop", + "rubber chicken", + "spiked bat", + "heavy rock", + "chunk of dirt", + "ton of bricks", + "rasengan", + "spirit bomb", + "100-Type Guanyin Bodhisattva", + "rasenshuriken", + "Murasame", + "ban", + "chunchunmaru", + "Kubikiribōchō", + "rasengan", + "spherical flying kat", +) + +THROW = ( + "throws", + "flings", + "chucks", + "hurls", +) + +HIT = ("hits", "whacks", "slaps", "smacks", "bashes", "pats") + +REACTIONS = ( + "( ͡° ͜ʖ ͡°)", + "( . •́ _ʖ •̀ .)", + "( ಠ ͜ʖ ಠ)", + "( ͡ ͜ʖ ͡ )", + "(ʘ ͜ʖ ʘ)", + "ヾ(´〇`)ノ♪♪♪", + "ヽ(o´∀`)ノ♪♬", + "♪♬((d⌒ω⌒b))♬♪", + "└(^^)┐", + "( ̄▽ ̄)/♫•*¨*•.¸¸♪", + "ヾ(⌐■_■)ノ♪", + "乁( • ω •乁)", + "♬♫♪◖(● o ●)◗♪♫♬", + "(っ˘ڡ˘ς)", + "( ˘▽˘)っ♨", + "( ・ω・)⊃-[二二]", + "(*´ー`)旦 旦( ̄ω ̄*)", + "(  ̄▽ ̄)[] [](≧▽≦ )", + "(* ̄▽ ̄)旦 且(´∀`*)", + "(ノ ˘_˘)ノ ζ|||ζ ζ|||ζ ζ|||ζ", + "(ノ°∀°)ノ⌒・*:.。. .。.:*・゜゚・*☆", + "(⊃。•́‿•̀。)⊃━✿✿✿✿✿✿", + "(∩` ロ ´)⊃━炎炎炎炎炎", + "( ・∀・)・・・--------☆", + "( -ω-)/占~~~~~", + "○∞∞∞∞ヽ(^ー^ )", + "(*^^)/~~~~~~~~~~◎", + "(((  ̄□)_/", + "(メ ̄▽ ̄)︻┳═一", + "ヽ( ・∀・)ノ_θ彡☆Σ(ノ `Д´)ノ", + "(*`0´)θ☆(メ°皿°)ノ", + "(; -_-)――――――C<―_-)", + "ヽ(>_<ヽ) ―⊂|=0ヘ(^‿^ )", + "(҂` ロ ´)︻デ═一 \(º □ º l|l)/", + "/( .□.)\ ︵╰(°益°)╯︵ /(.□. /)", + "(`⌒*)O-(`⌒´Q)", + "(っ•﹏•)っ ✴==≡눈٩(`皿´҂)ง", + "ヾ(・ω・)メ(・ω・)ノ", + "(*^ω^)八(⌒▽⌒)八(-‿‿- )ヽ", + "ヽ( ⌒ω⌒)人(=^‥^= )ノ", + "。*:☆(・ω・人・ω・)。:゜☆。", + "(°(°ω(°ω°(☆ω☆)°ω°)ω°)°)", + "(っ˘▽˘)(˘▽˘)˘▽˘ς)", + "(*^ω^)人(^ω^*)", + r"\(▽ ̄ \ ( ̄▽ ̄) /  ̄▽)/", + "( ̄Θ ̄)", + "\( ˋ Θ ´ )/", + "( ´(00)ˋ )", + "\( ̄(oo) ̄)/", + "/(≧ x ≦)\", + "/(=・ x ・=)\", + "(=^・ω・^=)", + "(= ; ェ ; =)", + "(=⌒‿‿⌒=)", + "(^• ω •^)", + "ଲ(ⓛ ω ⓛ)ଲ", + "ଲ(ⓛ ω ⓛ)ଲ", + "(^◔ᴥ◔^)", + "[(--)]..zzZ", + "( ̄o ̄) zzZZzzZZ", + "(_ _*) Z z z", + "☆ミ(o*・ω・)ノ", + "ε=ε=ε=ε=┌(; ̄▽ ̄)┘", + "ε===(っ≧ω≦)っ", + "__φ(..)", + "ヾ( `ー´)シφ__", + "( ^▽^)ψ__", + "|・ω・)", + "|д・)", + "┬┴┬┴┤・ω・)ノ", + "|・д・)ノ", + "(* ̄ii ̄)", + "(^〃^)", + "m(_ _)m", + "人(_ _*)", + "(シ. .)シ", + "(^_~)", + "(>ω^)", + "(^_<)〜☆", + "(^_<)", + "(づ ̄ ³ ̄)づ", + "(⊃。•́‿•̀。)⊃", + "⊂(´• ω •`⊂)", + "(*・ω・)ノ", + "(^-^*)/", + "ヾ(*'▽'*)", + "(^0^)ノ", + "(*°ー°)ノ", + "( ̄ω ̄)/", + "(≧▽≦)/", + "w(°o°)w", + "(⊙_⊙)", + "(°ロ°) !", + "∑(O_O;)", + "(¬_¬)", + "(¬_¬ )", + "(↼_↼)", + "( ̄ω ̄;)", + "┐('~`;)┌", + "(・_・;)", + "(@_@)", + "(•ิ_•ิ)?", + "ヽ(ー_ー )ノ", + "┐( ̄ヘ ̄)┌", + "┐( ̄~ ̄)┌", + "┐( ´ д ` )┌", + "╮(︶▽︶)╭", + "ᕕ( ᐛ )ᕗ", + "(ノωヽ)", + "(″ロ゛)", + "(/ω\)", + "(((><)))", + "~(>_<~)", + "(×_×)", + "(×﹏×)", + "(ノ_<。)", + "(μ_μ)", + "o(TヘTo)", + "( ゚,_ゝ`)", + "( ╥ω╥ )", + "(/ˍ・、)", + "(つω`。)", + "(T_T)", + "o(〒﹏〒)o", + "(#`Д´)", + "(・`ω´・)", + "( `ε´ )", + "(メ` ロ ´)", + "Σ(▼□▼メ)", + "(҂ `з´ )", + "٩(╬ʘ益ʘ╬)۶", + "↑_(ΦwΦ)Ψ", + "(ノಥ益ಥ)ノ", + "(#><)", + "(; ̄Д ̄)", + "(¬_¬;)", + "(^^#)", + "( ̄︿ ̄)", + "ヾ(  ̄O ̄)ツ", + "(ᗒᗣᗕ)՞", + "(ノ_<。)ヾ(´ ▽ ` )", + "ヽ( ̄ω ̄(。。 )ゝ", + "(ノ_;)ヾ(´ ∀ ` )", + "(´-ω-`( _ _ )", + "(⌒_⌒;)", + "(*/_\)", + "( ◡‿◡ *)", + "(//ω//)", + "( ̄▽ ̄*)ゞ", + "(„ಡωಡ„)", + "(ノ´ з `)ノ", + "(♡-_-♡)", + "(─‿‿─)♡", + "(´ ω `♡)", + "(ღ˘⌣˘ღ)", + "(´• ω •`) ♡", + "╰(*´︶`*)╯♡", + "(≧◡≦) ♡", + "♡ (˘▽˘>ԅ( ˘⌣˘)", + "σ(≧ε≦σ) ♡", + "(˘∀˘)/(μ‿μ) ❤", + "Σ>―(〃°ω°〃)♡→", + "(* ^ ω ^)", + "(o^▽^o)", + "ヽ(・∀・)ノ", + "(o・ω・o)", + "(^人^)", + "( ´ ω ` )", + "(´• ω •`)", + "╰(▔∀▔)╯", + "(✯◡✯)", + "(⌒‿⌒)", + "(*°▽°*)", + "(´。• ᵕ •。`)", + "ヽ(>∀<☆)ノ", + "\( ̄▽ ̄)/", + "(o˘◡˘o)", + "(╯✧▽✧)╯", + "( ‾́ ◡ ‾́ )", + "(๑˘︶˘๑)", + "(´・ᴗ・ ` )", + "( ͡° ʖ̯ ͡°)", + "( ఠ ͟ʖ ఠ)", + "( ಥ ʖ̯ ಥ)", + "(≖ ͜ʖ≖)", + "ヘ( ̄ω ̄ヘ)", + "(ノ≧∀≦)ノ", + "└( ̄- ̄└))", + "┌(^^)┘", + "(^_^♪)", + "(〜 ̄△ ̄)〜", + "(「• ω •)「", + "( ˘ ɜ˘) ♬♪♫", + "( o˘◡˘o) ┌iii┐", + "♨o(>_<)o♨", + "( ・・)つ―{}@{}@{}-", + "(*´з`)口゚。゚口(・∀・ )", + "( *^^)o∀*∀o(^^* )", + "-●●●-c(・・ )", + "(ノ≧∀≦)ノ ‥…━━━★", + "╰( ͡° ͜ʖ ͡° )つ──☆*:・゚", + "(∩ᄑ_ᄑ)⊃━☆゚*・。*・:≡( ε:)", +) + +TOSS = ( + "Heads", + "Tails", +) + + + +DECIDE = ("Yes.", "No.", "Maybe.") + +INSULT_STRINGS = [ + "`Owww ... Such a stupid idiot.`", + "`Don't drink and type.`", + "`Command not found. Just like your brain.`", + "`Bot rule 544 section 9 prevents me from replying to stupid humans like you.`", + "`Sorry, we do not sell brains.`", + "`Believe me you are not normal.`", + "`I bet your brain feels as good as new, seeing that you never use it.`", + "`If I wanted to kill myself I'd climb your ego and jump to your IQ.`", + "`You didn't evolve from apes, they evolved from you.`", + "`What language are you speaking? Cause it sounds like bullshit.`", + "`You are proof that evolution CAN go in reverse.`", + "`I would ask you how old you are but I know you can't count that high.`", + "`As an outsider, what do you think of the human race?`", + "`Ordinarily people live and learn. You just live.`", + "`Keep talking, someday you'll say something intelligent!.......(I doubt it though)`", + "`Everyone has the right to be stupid but you are abusing the privilege.`", + "`I'm sorry I hurt your feelings when I called you stupid. I thought you already knew that.`", + "`You should try tasting cyanide.`", + "`You should try sleeping forever.`", + "`Pick up a gun and shoot yourself.`", + "`Try bathing with Hydrochloric Acid instead of water.`", + "`Go Green! Stop inhaling Oxygen.`", + "`God was searching for you. You should leave to meet him.`", + "`You should Volunteer for target in an firing range.`", + "`Try playing catch and throw with RDX its fun.`", + "`People like you are the reason we have middle fingers.`", + "`When your mom dropped you off at the school, she got a ticket for littering.`", + "`You’re so ugly that when you cry, the tears roll down the back of your head…just to avoid your face.`", + "`If you’re talking behind my back then you’re in a perfect position to kiss my a**!.`", + "`Awww! how cute.....me not you`", + "`You are so ugly that if one direction sees you, they will change their direction to another...`", +] + + + +BAN_GIFS = ["https://tenor.com/bMiwt.gif", + "https://tenor.com/bqhVe.gif", + "https://tenor.com/bsHe5.gif", + "https://j.gifs.com/y5Pr8z.gif", + "https://tenor.com/bfvQv.gif", + "https://j.gifs.com/lO0lv6.gif", + "https://tenor.com/bGMhb.gif", + "https://tenor.com/bjZai.gif", + "https://media.giphy.com/media/fe4dDMD2cAU5RfEaCU/giphy.gif", + "https://tenor.com/bndta.gif", + "https://tenor.com/bkyIr.gif",] + +KICK_GIFS = ["https://tenor.com/bizBJ.gif", + "https://tenor.com/bizBJ.gif", + "https://tenor.com/bjPvj.gif", + "https://tenor.com/bJMx3.gif", + "https://tenor.com/bJCGL.gif", + "https://tenor.com/bsIMJ.gif", + "https://tenor.com/buURG.gif", + "https://tenor.com/bNOj9.gif", + "https://tenor.com/NfK8.gif", + "https://tenor.com/ymZn.gif", + "https://tenor.com/77Lu.gif", + "https://tenor.com/bPD4I.gif",] diff --git a/Powers/utils/http_helper.py b/Powers/utils/http_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..a7329745af5be9da0682da0c0bd0be6ffd3d7453 --- /dev/null +++ b/Powers/utils/http_helper.py @@ -0,0 +1,14 @@ +from httpx import AsyncClient, Timeout + +timeout = Timeout(40, pool=None) +http = AsyncClient(http2=True, timeout=timeout) + + +class HTTPx: + """class for helping get the data from url using aiohttp.""" + + @staticmethod + async def get(link: str): + """Get JSON data from the provided link.""" + async with AsyncClient() as sess: + return await sess.get(link) diff --git a/Powers/utils/kbhelpers.py b/Powers/utils/kbhelpers.py new file mode 100644 index 0000000000000000000000000000000000000000..ce02ba96fdfc820a256e171e4374044eecb3e758 --- /dev/null +++ b/Powers/utils/kbhelpers.py @@ -0,0 +1,18 @@ +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup + + +def ikb(rows=None): + if rows is None: + rows = [] + lines = [] + for row in rows: + line = [] + for button in row: + button = btn(*button) # InlineKeyboardButton + line.append(button) + lines.append(line) + return InlineKeyboardMarkup(inline_keyboard=lines) + + +def btn(text, value, type="callback_data"): + return InlineKeyboardButton(text, **{type: value}) diff --git a/Powers/utils/msg_types.py b/Powers/utils/msg_types.py new file mode 100644 index 0000000000000000000000000000000000000000..53af4b18c5f2dc1d03ece2672d9c1c6c4a1e670b --- /dev/null +++ b/Powers/utils/msg_types.py @@ -0,0 +1,223 @@ +from enum import IntEnum, unique + +from pyrogram.types import Message + + +@unique +class Types(IntEnum): + TEXT = 1 + DOCUMENT = 2 + PHOTO = 3 + VIDEO = 4 + STICKER = 5 + AUDIO = 6 + VOICE = 7 + VIDEO_NOTE = 8 + ANIMATION = 9 + ANIMATED_STICKER = 10 + CONTACT = 11 + + +async def get_note_type(m: Message): + """Get type of note.""" + if len(m.text.split()) <= 1: + return None, None, None, None + + data_type = None + content = None + raw_text = m.text.markdown if m.text else m.caption.markdown + args = raw_text.split(None, 2) + note_name = args[1] + + if len(args) >= 3: + text = args[2] + data_type = Types.TEXT + + elif m.reply_to_message: + + if m.reply_to_message.text: + text = m.reply_to_message.text.markdown + elif m.reply_to_message.caption: + text = m.reply_to_message.caption.markdown + else: + text = "" + + if len(args) >= 2 and m.reply_to_message.text: # not caption, text + data_type = Types.TEXT + + elif m.reply_to_message.sticker: + content = m.reply_to_message.sticker.file_id + data_type = Types.STICKER + + elif m.reply_to_message.document: + if m.reply_to_message.document.mime_type == "application/x-bad-tgsticker": + data_type = Types.ANIMATED_STICKER + else: + data_type = Types.DOCUMENT + content = m.reply_to_message.document.file_id + + elif m.reply_to_message.photo: + content = m.reply_to_message.photo.file_id # last elem = best quality + data_type = Types.PHOTO + + elif m.reply_to_message.audio: + content = m.reply_to_message.audio.file_id + data_type = Types.AUDIO + + elif m.reply_to_message.voice: + content = m.reply_to_message.voice.file_id + data_type = Types.VOICE + + elif m.reply_to_message.video: + content = m.reply_to_message.video.file_id + data_type = Types.VIDEO + + elif m.reply_to_message.video_note: + content = m.reply_to_message.video_note.file_id + data_type = Types.VIDEO_NOTE + + elif m.reply_to_message.animation: + content = m.reply_to_message.animation.file_id + data_type = Types.ANIMATION + + else: + return None, None, None, None + + return note_name, text, data_type, content + + +async def get_filter_type(m: Message): + """Get filter type.""" + if len(m.text.split()) <= 1: + return None, None, None, None + + data_type = None + content = None + raw_text = m.text.markdown if m.text else m.caption.markdown + args = raw_text.split(None, 2) + + if not m.reply_to_message and m.text and len(m.text.split()) >= 3: + content = None + text = m.text.split(None, 2)[2] + data_type = Types.TEXT + + elif m.reply_to_message: + + if m.reply_to_message.text: + text = m.reply_to_message.text.markdown + elif m.reply_to_message.caption: + text = m.reply_to_message.caption.markdown + else: + text = "" + + if len(args) >= 2 and m.reply_to_message.text: # not caption, text + data_type = Types.TEXT + + elif m.reply_to_message.sticker: + content = m.reply_to_message.sticker.file_id + data_type = Types.STICKER + + elif m.reply_to_message.document: + if m.reply_to_message.document.mime_type == "application/x-bad-tgsticker": + data_type = Types.ANIMATED_STICKER + else: + data_type = Types.DOCUMENT + content = m.reply_to_message.document.file_id + + elif m.reply_to_message.photo: + content = m.reply_to_message.photo.file_id # last elem = best quality + data_type = Types.PHOTO + + elif m.reply_to_message.audio: + content = m.reply_to_message.audio.file_id + data_type = Types.AUDIO + + elif m.reply_to_message.voice: + content = m.reply_to_message.voice.file_id + data_type = Types.VOICE + + elif m.reply_to_message.video: + content = m.reply_to_message.video.file_id + data_type = Types.VIDEO + + elif m.reply_to_message.video_note: + content = m.reply_to_message.video_note.file_id + data_type = Types.VIDEO_NOTE + + elif m.reply_to_message.animation: + content = m.reply_to_message.animation.file_id + data_type = Types.ANIMATION + + else: + text = None + data_type = None + content = None + + return text, data_type, content + + +async def get_wlcm_type(m: Message): + """Get wlcm type.""" + data_type = None + content = None + raw_text = m.text.markdown if m.text else m.caption.markdown + args = raw_text.split(None, 1) + + if not m.reply_to_message and m.text and len(m.text.split()) >= 2: + content = None + text = m.text.split(None, 1)[1] + data_type = Types.TEXT + + elif m.reply_to_message: + + if m.reply_to_message.text: + text = m.reply_to_message.text.markdown + elif m.reply_to_message.caption: + text = m.reply_to_message.caption.markdown + else: + text = "" + + if len(args) >= 1 and m.reply_to_message.text: # not caption, text + data_type = Types.TEXT + + elif m.reply_to_message.sticker: + content = m.reply_to_message.sticker.file_id + data_type = Types.STICKER + + elif m.reply_to_message.document: + if m.reply_to_message.document.mime_type == "application/x-bad-tgsticker": + data_type = Types.ANIMATED_STICKER + else: + data_type = Types.DOCUMENT + content = m.reply_to_message.document.file_id + + elif m.reply_to_message.photo: + content = m.reply_to_message.photo.file_id # last elem = best quality + data_type = Types.PHOTO + + elif m.reply_to_message.audio: + content = m.reply_to_message.audio.file_id + data_type = Types.AUDIO + + elif m.reply_to_message.voice: + content = m.reply_to_message.voice.file_id + data_type = Types.VOICE + + elif m.reply_to_message.video: + content = m.reply_to_message.video.file_id + data_type = Types.VIDEO + + elif m.reply_to_message.video_note: + content = m.reply_to_message.video_note.file_id + data_type = Types.VIDEO_NOTE + + elif m.reply_to_message.animation: + content = m.reply_to_message.animation.file_id + data_type = Types.ANIMATION + + else: + text = None + data_type = None + content = None + + return text, data_type, content diff --git a/Powers/utils/parser.py b/Powers/utils/parser.py new file mode 100644 index 0000000000000000000000000000000000000000..9b3556825a13873e8300a51568d04bc78ef19c05 --- /dev/null +++ b/Powers/utils/parser.py @@ -0,0 +1,26 @@ +from html import escape +from re import compile as compilere +from re import sub + + +async def cleanhtml(raw_html: str) -> str: + """Clean html data.""" + cleanr = compilere("<.*?>") + return sub(cleanr, "", raw_html) + + +async def escape_markdown(text: str) -> str: + """Escape markdown data.""" + escape_chars = r"\*_`\[" + return sub(r"([%s])" % escape_chars, r"\\\1", text) + + +async def mention_html(name: str, user_id: int) -> str: + """Mention user in html format.""" + name = escape(name) + return f'{name}' + + +async def mention_markdown(name: str, user_id: int) -> str: + """Mention user in markdown format.""" + return f"[{(await escape_markdown(name))}](tg://user?id={user_id})" diff --git a/Powers/utils/regex_utils.py b/Powers/utils/regex_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..bf843cce7802e1d2d7a88bfb053fce7b7885c3d7 --- /dev/null +++ b/Powers/utils/regex_utils.py @@ -0,0 +1,32 @@ +from traceback import format_exc + +from regex import search + +from Powers import LOGGER + + +async def regex_searcher(regex_string: str, string: str) -> str: + """Search for Regex in string.""" + try: + re_search = search(regex_string, string, timeout=6) + except TimeoutError: + return False + except Exception: + LOGGER.error(format_exc()) + return False + + return re_search + + +async def infinite_loop_check(regex_string: str) -> bool: + """Clear Regex in string.""" + loop_matches = ( + r"\((.{1,}[\+\*]){1,}\)[\+\*]." + r"[\(\[].{1,}\{\d(,)?\}[\)\]]\{\d(,)?\}" + r"\(.{1,}\)\{.{1,}(,)?\}\(.*\)(\+|\* |\{.*\})" + ) + + for match in loop_matches: + match_1 = search(match, regex_string) + + return bool(match_1) diff --git a/Powers/utils/start_utils.py b/Powers/utils/start_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..7a5c5858e1916bfbf1933819aa9002c5f47f375b --- /dev/null +++ b/Powers/utils/start_utils.py @@ -0,0 +1,249 @@ +from html import escape +from secrets import choice +from traceback import format_exc + +from pyrogram.errors import RPCError +from pyrogram.types import CallbackQuery, Message + +from Powers import HELP_COMMANDS, LOGGER, SUPPORT_GROUP +from Powers.bot_class import Gojo +from Powers.database.chats_db import Chats +from Powers.database.notes_db import Notes +from Powers.database.rules_db import Rules +from Powers.tr_engine import tlang +from Powers.utils.cmd_senders import send_cmd +from Powers.utils.kbhelpers import ikb +from Powers.utils.msg_types import Types +from Powers.utils.string import ( + build_keyboard, + escape_mentions_using_curly_brackets, + parse_button, +) +from Powers.vars import Config + +# Initialize +notes_db = Notes() + + +async def gen_cmds_kb(m: Message or CallbackQuery): + """Generate the keyboard for languages.""" + if isinstance(m, CallbackQuery): + m = m.message + + cmds = sorted(list(HELP_COMMANDS.keys())) + kb = [(tlang(m, cmd), f"get_mod.{cmd.lower()}") for cmd in cmds] + + return [kb[i : i + 3] for i in range(0, len(kb), 3)] + + +async def gen_start_kb(q: Message or CallbackQuery): + """Generate keyboard with start menu options.""" + return ikb( + [ + [ + ( + f"➕ {(tlang(q, 'start.add_chat_btn'))}", + f"https://t.me/{Config.BOT_USERNAME}?startgroup=new", + "url", + ), + ( + f"{(tlang(q, 'start.support_group'))} 👥", + f"https://t.me/{SUPPORT_GROUP}", + "url", + ), + ], + [(f"📚 {(tlang(q, 'start.commands_btn'))}", "commands")], + [ + (f"🌐 {(tlang(q, 'start.language_btn'))}", "chlang"), + ( + f"🗃️ {(tlang(q, 'start.source_code'))}", + "https://github.com/iamgojoof6eyes/Gojo_Satarou", + "url", + ), + ], + ], + ) + + +async def get_private_note(c: Gojo, m: Message, help_option: str): + """Get the note in pm of user, with parsing enabled.""" + help_lst = help_option.split("_") + if len(help_lst) == 2: + chat_id = int(help_lst[1]) + + all_notes = notes_db.get_all_notes(chat_id) + chat_title = Chats.get_chat_info(chat_id)["chat_name"] + rply = f"Notes in {chat_title}:\n\n" + note_list = [ + f"- [{note[0]}](https://t.me/{Config.BOT_USERNAME}?start=note_{chat_id}_{note[1]})" + for note in all_notes + ] + rply = "\n".join(note_list) + rply += "You can retrieve these notes by tapping on the notename." + await m.reply_text(rply, disable_web_page_preview=True, quote=True) + return + + if len(help_lst) != 3: + return + + note_hash = help_option.split("_")[2] + getnotes = notes_db.get_note_by_hash(note_hash) + if not getnotes: + await m.reply_text("Note does not exist", quote=True) + return + + msgtype = getnotes["msgtype"] + if not msgtype: + await m.reply_text( + "Error: Cannot find a type for this note!!", + quote=True, + ) + return + + try: + # support for random notes texts + splitter = "%%%" + note_reply = getnotes["note_value"].split(splitter) + note_reply = choice(note_reply) + except KeyError: + note_reply = "" + + parse_words = [ + "first", + "last", + "fullname", + "username", + "id", + "chatname", + "mention", + ] + text = await escape_mentions_using_curly_brackets(m, note_reply, parse_words) + + if msgtype == Types.TEXT: + + teks, button = await parse_button(text) + button = await build_keyboard(button) + button = ikb(button) if button else None + if button: + try: + await m.reply_text( + teks, + reply_markup=button, + disable_web_page_preview=True, + quote=True, + ) + return + except RPCError as ef: + await m.reply_text( + "An error has occured! Cannot parse note.", + quote=True, + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + else: + await m.reply_text(teks, quote=True, disable_web_page_preview=True) + return + elif msgtype in ( + Types.STICKER, + Types.VIDEO_NOTE, + Types.CONTACT, + Types.ANIMATED_STICKER, + ): + await (await send_cmd(c, msgtype))(m.chat.id, getnotes["fileid"]) + else: + if getnotes["note_value"]: + teks, button = await parse_button(getnotes["note_value"]) + button = await build_keyboard(button) + button = ikb(button) if button else None + else: + teks = "" + button = None + if button: + try: + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + caption=teks, + reply_markup=button, + ) + return + except RPCError as ef: + await m.reply_text( + teks, + quote=True, + reply_markup=button, + disable_web_page_preview=True, + ) + LOGGER.error(ef) + LOGGER.error(format_exc()) + return + else: + await (await send_cmd(c, msgtype))( + m.chat.id, + getnotes["fileid"], + caption=teks, + ) + LOGGER.info( + f"{m.from_user.id} fetched privatenote {note_hash} (type - {getnotes}) in {m.chat.id}", + ) + return + + +async def get_private_rules(_, m: Message, help_option: str): + chat_id = int(help_option.split("_")[1]) + rules = Rules(chat_id).get_rules() + chat_title = Chats.get_chat_info(chat_id)["chat_name"] + if not rules: + await m.reply_text( + "The Admins of that group have not setup any rules, that dosen't mean you break the decorum of the chat!", + quote=True, + ) + return "" + await m.reply_text( + f"""The rules for {escape(chat_title)} are:\n +{rules} +""", + quote=True, + disable_web_page_preview=True, + ) + return "" + + +async def get_help_msg(m: Message or CallbackQuery, help_option: str): + """Helper function for getting help_msg and it's keyboard.""" + help_msg = None + help_kb = None + help_cmd_keys = sorted( + k + for j in [HELP_COMMANDS[i]["alt_cmds"] for i in list(HELP_COMMANDS.keys())] + for k in j + ) + + if help_option in help_cmd_keys: + help_option_name = next( + HELP_COMMANDS[i] + for i in HELP_COMMANDS + if help_option in HELP_COMMANDS[i]["alt_cmds"] + ) + help_option_value = help_option_name["help_msg"] + help_kb = next( + HELP_COMMANDS[i]["buttons"] + for i in HELP_COMMANDS + if help_option in HELP_COMMANDS[i]["alt_cmds"] + ) + [[("« " + (tlang(m, "general.back_btn")), "commands")]] + help_msg = ( + f"**{(tlang(m, (help_option_name['help_msg']).replace('.help', '.main')))}:**\n\n" + + tlang(m, help_option_value) + ) + LOGGER.info( + f"{m.from_user.id} fetched help for {help_option} in {m.chat.id}", + ) + else: + help_msg = tlang(m, "general.commands_available") + help_kb = [ + *(await gen_cmds_kb(m)), + [(f"« {(tlang(m, 'general.back_btn'))}", "start_back")], + ] + + return help_msg, help_kb diff --git a/Powers/utils/string.py b/Powers/utils/string.py new file mode 100644 index 0000000000000000000000000000000000000000..399abdcbb03adbbb4ede4a2ea0522e3ac71cb232 --- /dev/null +++ b/Powers/utils/string.py @@ -0,0 +1,191 @@ +from html import escape +from re import compile as compile_re +from time import time +from typing import List + +from pyrogram.types import InlineKeyboardButton, Message + +from Powers.utils.parser import escape_markdown + +BTN_URL_REGEX = compile_re(r"(\[([^\[]+?)\]\(buttonurl:(?:/{0,2})(.+?)(:same)?\))") + + +async def extract_time(m: Message, time_val: str): + """Extract time from message.""" + if any(time_val.endswith(unit) for unit in ("m", "h", "d")): + unit = time_val[-1] + time_num = time_val[:-1] # type: str + if not time_num.isdigit(): + await m.reply("Unspecified amount of time.") + return "" + + if unit == "m": + bantime = int(time() + int(time_num) * 60) + elif unit == "h": + bantime = int(time() + int(time_num) * 60 * 60) + elif unit == "s": + bantime = int(time() + int(time_num) * 24 * 60 * 60) + else: + # how even...? + return "" + return bantime + await m.reply( + f"Invalid time type specified. Needed m, h, or s. got: {time_val[-1]}", + ) + return "" + + +async def parse_button(text: str): + """Parse button from text.""" + markdown_note = text + prev = 0 + note_data = "" + buttons = [] + for match in BTN_URL_REGEX.finditer(markdown_note): + # Check if btnurl is escaped + n_escapes = 0 + to_check = match.start(1) - 1 + while to_check > 0 and markdown_note[to_check] == "\\": + n_escapes += 1 + to_check -= 1 + + # if even, not escaped -> create button + if n_escapes % 2 == 0: + # create a thruple with button label, url, and newline status + buttons.append((match.group(2), match.group(3), bool(match.group(4)))) + note_data += markdown_note[prev : match.start(1)] + prev = match.end(1) + # if odd, escaped -> move along + else: + note_data += markdown_note[prev:to_check] + prev = match.start(1) - 1 + else: + note_data += markdown_note[prev:] + + return note_data, buttons + + +async def build_keyboard(buttons): + """Build keyboards from provided buttons.""" + keyb = [] + for btn in buttons: + if btn[-1] and keyb: + keyb[-1].append(InlineKeyboardButton(btn[0], url=btn[1])) + else: + keyb.append([InlineKeyboardButton(btn[0], url=btn[1])]) + + return keyb + + +SMART_OPEN = "“" +SMART_CLOSE = "”" +START_CHAR = ("'", '"', SMART_OPEN) + + +async def escape_invalid_curly_brackets(text: str, valids: List[str]) -> str: + new_text = "" + idx = 0 + while idx < len(text): + if text[idx] == "{": + if idx + 1 < len(text) and text[idx + 1] == "{": + idx += 2 + new_text += "{{{{" + continue + success = False + for v in valids: + if text[idx:].startswith("{" + v + "}"): + success = True + break + if success: + new_text += text[idx : idx + len(v) + 2] + idx += len(v) + 2 + continue + new_text += "{{" + + elif text[idx] == "}": + if idx + 1 < len(text) and text[idx + 1] == "}": + idx += 2 + new_text += "}}}}" + continue + new_text += "}}" + + else: + new_text += text[idx] + idx += 1 + + return new_text + + +async def escape_mentions_using_curly_brackets( + m: Message, + text: str, + parse_words: list, +) -> str: + teks = await escape_invalid_curly_brackets(text, parse_words) + if teks: + teks = teks.format( + first=escape(m.from_user.first_name), + last=escape(m.from_user.last_name or m.from_user.first_name), + mention=m.from_user.mention, + username=( + "@" + (await escape_markdown(escape(m.from_user.username))) + if m.from_user.username + else m.from_user.mention + ), + fullname=" ".join( + [ + escape(m.from_user.first_name), + escape(m.from_user.last_name), + ] + if m.from_user.last_name + else [escape(m.from_user.first_name)], + ), + chatname=escape(m.chat.title) + if m.chat.type != "private" + else escape(m.from_user.first_name), + id=m.from_user.id, + ) + else: + teks = "" + + return teks + + +async def split_quotes(text: str): + """Split quotes in text.""" + if not any(text.startswith(char) for char in START_CHAR): + return text.split(None, 1) + counter = 1 # ignore first char -> is some kind of quote + while counter < len(text): + if text[counter] == "\\": + counter += 1 + elif text[counter] == text[0] or ( + text[0] == SMART_OPEN and text[counter] == SMART_CLOSE + ): + break + counter += 1 + else: + return text.split(None, 1) + + # 1 to avoid starting quote, and counter is exclusive so avoids ending + key = await remove_escapes(text[1:counter].strip()) + # index will be in range, or `else` would have been executed and returned + rest = text[counter + 1 :].strip() + if not key: + key = text[0] + text[0] + return list(filter(None, [key, rest])) + + +async def remove_escapes(text: str) -> str: + """Remove the escaped from message.""" + res = "" + is_escaped = False + for counter in range(len(text)): + if is_escaped: + res += text[counter] + is_escaped = False + elif text[counter] == "\\": + is_escaped = True + else: + res += text[counter] + return res diff --git a/Powers/vars.py b/Powers/vars.py new file mode 100644 index 0000000000000000000000000000000000000000..7291ce3a775c41945ca37337839d2c8f9f3c7e29 --- /dev/null +++ b/Powers/vars.py @@ -0,0 +1,58 @@ +from os import getcwd + +from prettyconf import Configuration +from prettyconf.loaders import EnvFile, Environment + +env_file = f"{getcwd()}/.env" +config = Configuration(loaders=[Environment(), EnvFile(filename=env_file)]) + + + +class Config: + """Config class for variables.""" + + LOGGER = True + BOT_TOKEN = config("BOT_TOKEN", default=None) + APP_ID = int(config("APP_ID", default=None)) + API_HASH = config("API_HASH", default=None) + OWNER_ID = int(config("OWNER_ID", default=1344569458)) + MESSAGE_DUMP = int(config("MESSAGE_DUMP", default=-100)) + DEV_USERS = [int(i) for i in config("DEV_USERS", default="1432756163 1344569458 1355478165 1789859817 1777340882").split(" ")] + SUDO_USERS = [int(i) for i in config("SUDO_USERS", default="1432756163 1344569458 1355478165 1789859817 1777340882").split(" ")] + WHITELIST_USERS = [int(i) for i in config("WHITELIST_USERS", default="1432756163 1344569458 1355478165 1789859817 1777340882").split(" ")] + DB_URI = config("DB_URI", default="") + DB_NAME = config("DB_NAME", default="Power_robot") + NO_LOAD = config("NO_LOAD", default="").split() + PREFIX_HANDLER = config("PREFIX_HANDLER", default="/").split() + SUPPORT_GROUP = config("SUPPORT_GROUP", default="HellBot_Network") + SUPPORT_CHANNEL = config("SUPPORT_CHANNEL", default="HellBot_Network") + ENABLED_LOCALES = [str(i) for i in config("ENABLED_LOCALES", default="en").split()] + VERSION = config("VERSION", default="v2.0") + WORKERS = int(config("WORKERS", default=16)) + BOT_USERNAME = "" + BOT_ID = "" + BOT_NAME = "" + + +class Development: + """Development class for variables.""" + + # Fill in these vars if you want to use Traditional method of deploying + LOGGER = True + BOT_TOKEN = "YOUR BOT_TOKEN" + APP_ID = 12345 # Your APP_ID from Telegram + API_HASH = "YOUR API HASH" # Your APP_HASH from Telegram + OWNER_ID = 1344569458 # Your telegram user id + MESSAGE_DUMP = -100 # Your Private Group ID for logs + DEV_USERS = [] + SUDO_USERS = [] + WHITELIST_USERS = [] + DB_URI = "postgres://username:password@postgresdb:5432/database_name" + DB_NAME = "Power_robot" + NO_LOAD = [] + PREFIX_HANDLER = ["!", "/"] + SUPPORT_GROUP = "SUPPORT_GROUP" + SUPPORT_CHANNEL = "SUPPORT_CHANNEL" + ENABLED_LOCALES = ["ENABLED_LOCALES"] + VERSION = "VERSION" + WORKERS = 8 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000000000000000000000000000000000000..2a4c28d3fb5c84460d42e2cceb6834f2e2812bbb --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +worker: python3 -m Power \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b4bdb902b05698e7a22187d452cd26f090c3f365 --- /dev/null +++ b/README.md @@ -0,0 +1,103 @@ +# Gojo_Satarou +![Gojo_Satarou](https://media.giphy.com/media/GL42TduR8AkNq1xRog/giphy.gif) + +A python and [pyrogram](https://github.com/iamgojoof6eyes/pyrogram) based group management bot for telegram. + + + +If you like the bot make sure to give a ⭐ __star__ ⭐ to theis respository and feel free to update and sending pull requests... + +## ⚡ Credit ⚡ + +### Devs 😎 + +[Captain Ezio](https://github.com/iamgojoof6eyes) `Creator & Dev` + +[Hêllẞøy](https://github.com/HellBoy-OP) `Dev` + + +[PSYREX](https://github.com/iamPSYREX) `Logo and picture designer` + + +### Inspiration ✨ +`Not a particular inspiration inspired by many bots` + + +``` +This bot is mixture of the many bots and have some extra plugins so it is quite better than both of bots. +``` + +## Group Management +The Gojo Satarou is a powerful Group Management bot. + +Can be found on telegram as a management bot for groups. + +Join 🌟 [Hell Community](https://t.me/HellBot_Network) 🌟 if you counter any problem or face any bugs for help. + + +## Bot + +![Gojo Starou](https://media.giphy.com/media/hbKBe6j3JLsNnb4h53/giphy.gif) + +I am available on telegram [Gojo Satarou](https://t.me/iamgojoof6eyes_bot) + +# Powered By [Hell Community](https://github.com/The-HellBot) + + + +# DEPLOYMENT 🚀 +## Deploy To Heroku +**Make Sure you have Heroku account** + +If you don't have heroku account what are you waiting for click [here](https://id.heroku.com/login) to make one or just deploy on [railway app](#Deploy To Railway) + +Just click on the button it will redirect you to Heroku website and deploy your bot there....enjoy 😉 + +[![DEPLOY](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=https://github.com/iamgojoof6eyes/Gojo_Satarou.git) + + +## Deploy To Railway +[![DEPLOY](https://railway.app/button.svg)](https://railway.app) +### How To Deploy On Railway +* **Make Sure You have an github account which is minimum 30 days old.** +* Fork the repo by clicking [here](https://github.com/iamgojoof6eyes/Gojo_Satarou/fork) +* Click on [deploy](https://railway.app) button +* Login in railway using github account +* Now search the repo in the search bar given like iamgojoof6eyes/Gojo_Satarou ***replace iamgojoof6eyes with your github username.*** +* Now add the values and keys by creating values and keys ***Make sure to add keys __as per given below__ I'll advide you to just copy and paste it*** +* Then 10-15 minutes. +* Mandaotry keys are [given below](#Variables) +* All keys are listed [here](https://telegra.ph/Captain-03-23) +* ***The bot can be used by provideing mandotry variables only..*** + + +## Variables +`TOKEN` You can get your bot token at @BotFather + +`API_ID` You can get your api id [here](my.telegram.org) + +`API_HASH` You can get your api hash [here](my.telegram.org) + +`PREFIX_HANDLER` Your bot handler which will activate commands + +`DB_URI` Your MongoDB connection string. + +`APP_ID` Get API_ID from my.telegram.org, used for pyrogram base. + +Click [here](https://telegra.ph/Captain-03-23) to see all variable + + + +# I'll be back with one more group management bot which will be far more better than this....until than use this bot + + diff --git a/app.json b/app.json new file mode 100644 index 0000000000000000000000000000000000000000..3b14bfc09faaeb258b470e581a9d3e5d8f94b629 --- /dev/null +++ b/app.json @@ -0,0 +1,112 @@ +{ + "name": "Gojo_Satarou", + "description": "Gojo is a Telegram Group management bot made using Pyrogram and Python, which makes it modern and faster than most of the Telegram chat managers.", + "keywords": [ + "telegram bot", + "group bot", + "group", + "manager", + "Gojo", + "Anime" + ], + "repository": "https://github.com/iamgojoof6eyes/Gojo_Satarou", + "success_url": "https://t.me/", + "env": { + "BOT_TOKEN": { + "description": "Your telegram bot token, get from @Botfather in telegram.", + "required": true, + "value": " " + }, + "APP_ID": { + "description": "Get API_ID from my.telegram.org, used for pyrogram base.", + "required": true, + "value": "12345" + }, + "API_HASH": { + "description": "Get API_HASH from my.telegram.org, used for pyrogram base.", + "required": true, + "value": "testinghash123" + }, + "DB_URI": { + "description": "Your MongoDB connection string.", + "required": true, + "value": "put it here" + }, + "DB_NAME": { + "description": "Your MongoDB database name.", + "required": true, + "value": "gojo_satarou" + }, + "OWNER_ID": { + "description": "Your user ID as an integer.", + "required": true, + "value": "1344569458" + }, + "SUPPORT_GROUP": { + "description": "Your Telegram support group chat username where user's can contact in case of a problem..", + "required": true, + "value": "HellBot_Network" + }, + "MESSAGE_DUMP": { + "description": "Event logs channel where bot will send updates.", + "required": true, + "value": "-657254745" + }, + "VERSION": { + "description": "A Version to be shown in bot.", + "required": false, + "value": "Stable" + }, + "PREFIX_HANDLER": { + "description": "Something like '/' to execute commands...Don't give comma or anything after 1 handler just give and space then enter second hand;ler", + "required": true, + "value": "/ ! $" + }, + "DEV_USERS": { + "description": "ID of users who are Devs of your bot (can use /py etc.)...Don't give comma or anything after 1 id just give and space then enter second id. If you are a noob and would come and bother Hell support then keep the current ID's here at they are and add yours.", + "required": false, + "value": "1432756163 1344569458 1355478165 1789859817 1777340882" + }, + "SUPPORT_CHANNEL": { + "description": "Channel where bot uodates will be posted!", + "value": "" + }, + "SUDO_USERS": { + "description": "A space separated list of user IDs who you want to assign as sudo users....Don't give comma or anything after 1 id just give and space then enter second id", + "required": false, + "value": "1432756163 1344569458 1355478165 1789859817 1777340882" + }, + "WHITELIST_USERS": { + "description": "A space separated list of user IDs whitelisted, cannot be restricted...Don't give comma or anything after 1 id just give and space then enter second id", + "required": false, + "value": "1432756163 1344569458 1355478165 1789859817 1777340882" + }, + "ENABLED_LOCALES": { + "description": "A space separated list of language which should be enabled in bot.", + "required": false, + "value": "en" + }, + "WORKERS": { + "description": "Number of workers to run the bot.", + "required": false, + "value": "8" + }, + "ENV": { + "description": "Setting this to ANYTHING will enable environment variables. Leave it as it is", + "required": true, + "value": "ANYTHING" + } + }, + "buildpacks": [ + { + "url": "heroku/python" + } + ], + "formation": { + "worker": { + "quantity": 1, + "size": "free" + } + } + } + \ No newline at end of file diff --git a/heroku.yml b/heroku.yml new file mode 100644 index 0000000000000000000000000000000000000000..cdeba040691dd5f4564db2c97a21348f1ab8eac9 --- /dev/null +++ b/heroku.yml @@ -0,0 +1,3 @@ +build: + docker: + worker: Dockerfile diff --git a/sample_config.env b/sample_config.env new file mode 100644 index 0000000000000000000000000000000000000000..453414bea080bfd5968ae2e150dce9f9f6d2f04a --- /dev/null +++ b/sample_config.env @@ -0,0 +1,28 @@ +# Only for Docker Deployment + +BOT_TOKEN=your_bot_token # Get it from @botfather + +API_ID=your_api_id + +API_HASH=your_api_hash + +SUDO_USERS_ID= 1432756163, 1344569458, 1355478165, 1789859817, 1777340882,# Sudo users have full access to everythin, don't trust anyone + +LOG_GROUP_ID=-1001559501403 + +GBAN_LOG_GROUP_ID=-1001559501403 + +MESSAGE_DUMP_CHAT= + +WELCOME_DELAY_KICK_SEC=300 # Edit if u want + +MONGO_URL=your_mongodb_url + +ARQ_API_URL=https://arq.hamker.in # Leave it like this + +ARQ_API_KEY=your_api_key # Get it from @ARQRobot + +LOG_MENTIONS=0 # Make it 1 if you want to enable it + +RSS_DELAY=300 # In seconds + diff --git a/sample_config.py b/sample_config.py new file mode 100644 index 0000000000000000000000000000000000000000..3cb75eae5cb8d693a488afe1d9b1cc996ce52ff9 --- /dev/null +++ b/sample_config.py @@ -0,0 +1,48 @@ +from os import environ + +from dotenv import load_dotenv + +load_dotenv("config.env") + +HEROKU = bool( + environ.get("DYNO") +) # NOTE Make it false if you're not deploying on heroku or docker. + +if HEROKU: + + BOT_TOKEN = environ.get("BOT_TOKEN", None) + API_ID = int(environ.get("API_ID", 6)) + API_HASH = environ.get("API_HASH", "eb06d4abfb49dc3eeb1aeb98ae0f581e") + SUDO_USERS_ID = [int(x) for x in environ.get("SUDO_USERS_ID", "").split()] + LOG_GROUP_ID = int(environ.get("LOG_GROUP_ID", None)) + GBAN_LOG_GROUP_ID = int(environ.get("GBAN_LOG_GROUP_ID", None)) + MESSAGE_DUMP_CHAT = int(environ.get("MESSAGE_DUMP_CHAT", None)) + WELCOME_DELAY_KICK_SEC = int(environ.get("WELCOME_DELAY_KICK_SEC", None)) + MONGO_URL = environ.get("MONGO_URL", None) + ARQ_API_URL = environ.get("ARQ_API_URL", None) + ARQ_API_KEY = environ.get("ARQ_API_KEY", None) + LOG_MENTIONS = bool(int(environ.get("LOG_MENTIONS", None))) + RSS_DELAY = int(environ.get("RSS_DELAY", None)) + +else: + BOT_TOKEN = "" + API_ID = 123456 + API_HASH = "" + SUDO_USERS_ID = [ + 1432756163, + 1344569458, + 1355478165, + 1789859817, + 1777340882, + + ] # Sudo users have full access to everything, don't trust anyone + LOG_GROUP_ID = -1001559501403 + GBAN_LOG_GROUP_ID = -1001559501403 + MESSAGE_DUMP_CHAT = "" + WELCOME_DELAY_KICK_SEC = 300 + MONGO_URL = "mongodb+srv://username:password@cluster0.ksiis.mongodb.net/YourDataBaseName?retryWrites=true&w=majority" + ARQ_API_KEY = "Get this from @ARQRobot" + ARQ_API_URL = "https://arq.hamker.in" + LOG_MENTIONS = True + RSS_DELAY = 300 # In seconds + PM_PERMIT = True