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
+
+
+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
+
+
+
+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 😉
+
+[](https://heroku.com/deploy?template=https://github.com/iamgojoof6eyes/Gojo_Satarou.git)
+
+
+## Deploy To Railway
+[](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