diff --git a/Database/mongodb/afk_db.py b/Database/mongodb/afk_db.py new file mode 100644 index 0000000000000000000000000000000000000000..f6b844b6d29af274b04d66c8f65d13271d1bcfe0 --- /dev/null +++ b/Database/mongodb/afk_db.py @@ -0,0 +1,53 @@ +from Database.mongodb.db import dbname + +usersdb = dbname.users +cleandb = dbname.cleanmode +cleanmode = {} + + +async def is_cleanmode_on(chat_id: int) -> bool: + mode = cleanmode.get(chat_id) + if not mode: + user = await cleandb.find_one({"chat_id": chat_id}) + if not user: + cleanmode[chat_id] = True + return True + cleanmode[chat_id] = False + return False + return mode + + +async def cleanmode_on(chat_id: int): + cleanmode[chat_id] = True + user = await cleandb.find_one({"chat_id": chat_id}) + if user: + return await cleandb.delete_one({"chat_id": chat_id}) + + +async def cleanmode_off(chat_id: int): + cleanmode[chat_id] = False + user = await cleandb.find_one({"chat_id": chat_id}) + if not user: + return await cleandb.insert_one({"chat_id": chat_id}) + + +async def is_afk(user_id: int) -> bool: + user = await usersdb.find_one({"user_id": user_id}) + return (True, user["reason"]) if user else (False, {}) + + +async def add_afk(user_id: int, mode): + await usersdb.update_one( + {"user_id": user_id}, {"$set": {"reason": mode}}, upsert=True + ) + + +async def remove_afk(user_id: int): + user = await usersdb.find_one({"user_id": user_id}) + if user: + return await usersdb.delete_one({"user_id": user_id}) + + +async def get_afk_users() -> list: + users = usersdb.find({"user_id": {"$gt": 0}}) + return list(await users.to_list(length=1000000000)) if users else [] diff --git a/Database/mongodb/blacklistdb.py b/Database/mongodb/blacklistdb.py new file mode 100644 index 0000000000000000000000000000000000000000..f6a58beb2c3290b65d8d54000b4c2d2e0549ae11 --- /dev/null +++ b/Database/mongodb/blacklistdb.py @@ -0,0 +1,51 @@ +import codecs +import pickle +from typing import List + +from Database.mongodb.db import dbname + +blacklist_filtersdb = dbname.blacklistFilters + + +def obj_to_str(obj): + if not obj: + return False + string = codecs.encode(pickle.dumps(obj), "base64").decode() + return string + + +def str_to_obj(string: str): + obj = pickle.loads(codecs.decode(string.encode(), "base64")) + return obj + + +async def get_blacklisted_words(chat_id: int) -> List[str]: + _filters = await blacklist_filtersdb.find_one({"chat_id": chat_id}) + if not _filters: + return [] + return _filters["filters"] + + +async def save_blacklist_filter(chat_id: int, word: str): + word = word.lower().strip() + _filters = await get_blacklisted_words(chat_id) + _filters.append(word) + await blacklist_filtersdb.update_one( + {"chat_id": chat_id}, + {"$set": {"filters": _filters}}, + upsert=True, + ) + + +async def delete_blacklist_filter(chat_id: int, word: str) -> bool: + filtersd = await get_blacklisted_words(chat_id) + word = word.lower().strip() + if word in filtersd: + filtersd.remove(word) + await blacklist_filtersdb.update_one( + {"chat_id": chat_id}, + {"$set": {"filters": filtersd}}, + upsert=True, + ) + return True + return False diff --git a/Database/mongodb/locale_db.py b/Database/mongodb/locale_db.py new file mode 100644 index 0000000000000000000000000000000000000000..5f830e9eb2c341c781f78cd0b29f96ddac7a2b11 --- /dev/null +++ b/Database/mongodb/locale_db.py @@ -0,0 +1,22 @@ +from typing import Iterable + +from pyrogram.enums import ChatType + +from Database.mongodb.db import dbname + +localesdb = dbname.locale # DB for localization + +group_types: Iterable[ChatType] = (ChatType.GROUP, ChatType.SUPERGROUP) + + +async def set_db_lang(chat_id: int, chat_type: str, lang_code: str): + await localesdb.update_one( + {"chat_id": chat_id}, + {"$set": {"lang": lang_code, "chat_type": chat_type.value}}, + upsert=True, + ) + + +async def get_db_lang(chat_id: int) -> str: + ul = await localesdb.find_one({"chat_id": chat_id}) + return ul["lang"] if ul else {} diff --git a/Database/mongodb/sangmata_db.py b/Database/mongodb/sangmata_db.py new file mode 100644 index 0000000000000000000000000000000000000000..10229c335d1cb887adb0cc338ecac679cd44a162 --- /dev/null +++ b/Database/mongodb/sangmata_db.py @@ -0,0 +1,42 @@ +from Database.mongodb.db import dbname + +matadb = dbname["sangmata"] + + +# Get Data User +async def cek_userdata(user_id: int) -> bool: + user = await matadb.find_one({"user_id": user_id}) + return bool(user) + + +async def get_userdata(user_id: int) -> bool: + user = await matadb.find_one({"user_id": user_id}) + return user["username"], user["first_name"], user["last_name"] + + +async def add_userdata(user_id: int, username, first_name, last_name): + await matadb.update_one( + {"user_id": user_id}, + { + "$set": { + "username": username, + "first_name": first_name, + "last_name": last_name, + } + }, + upsert=True, + ) + + +# Enable Mata MissKaty in Selected Chat +async def is_sangmata_on(chat_id: int) -> bool: + chat = await matadb.find_one({"chat_id_toggle": chat_id}) + return bool(chat) + + +async def sangmata_on(chat_id: int) -> bool: + await matadb.insert_one({"chat_id_toggle": chat_id}) + + +async def sangmata_off(chat_id: int): + await matadb.delete_one({"chat_id_toggle": chat_id}) diff --git a/Database/mongodb/users_chats_db.py b/Database/mongodb/users_chats_db.py new file mode 100644 index 0000000000000000000000000000000000000000..987ce662c9e81cca490fc3f9ac6690b63ad5f8c7 --- /dev/null +++ b/Database/mongodb/users_chats_db.py @@ -0,0 +1,111 @@ +from async_pymongo import AsyncClient + +from Mikobot import DB_NAME, MONGO_DB_URI + + +class UsersData: + def __init__(self, uri, database_name): + self._client = AsyncClient(uri) + self.db = self._client[database_name] + self.col = self.db["userlist"] + self.grp = self.db["groups"] + + @staticmethod + def new_user(id, name): + return dict( + id=id, + name=name, + ban_status=dict( + is_banned=False, + ban_reason="", + ), + ) + + @staticmethod + def new_group(id, title): + return dict( + id=id, + title=title, + chat_status=dict( + is_disabled=False, + reason="", + ), + ) + + async def add_user(self, id, name): + user = self.new_user(id, name) + await self.col.insert_one(user) + + async def is_user_exist(self, id): + user = await self.col.find_one({"id": int(id)}) + return bool(user) + + async def total_users_count(self): + return await self.col.count_documents({}) + + async def remove_ban(self, id): + return await self.col.delete_one({"_id": id}) + + async def ban_user(self, user_id, ban_reason="No Reason"): + return await self.col.insert_one({"_id": user_id, "reason": ban_reason}) + + async def get_ban_status(self, id): + user = await self.col.find_one({"_id": int(id)}) + if user: + return True, user + return False, None + + async def get_all_users(self): + return self.col.find({}) + + async def delete_user(self, user_id): + await self.col.delete_many({"id": int(user_id)}) + + async def is_chat_exist(self, id): + user = await self.grp.find_one({"id": int(id)}) + return bool(user) + + async def get_banned(self): + users = self.col.find({"ban_status.is_banned": True}) + chats = self.grp.find({"chat_status.is_disabled": True}) + b_chats = [chat["id"] async for chat in chats] + b_users = [user["id"] async for user in users] + return b_users, b_chats + + async def add_chat(self, chat, title): + chat = self.new_group(chat, title) + await self.grp.insert_one(chat) + + async def get_chat(self, chat): + chat = await self.grp.find_one({"id": int(chat)}) + return chat.get("chat_status") if chat else False + + async def re_enable_chat(self, id): + chat_status = dict( + is_disabled=False, + reason="", + ) + await self.grp.update_one( + {"id": int(id)}, {"$set": {"chat_status": chat_status}} + ) + + async def disable_chat(self, chat, reason="No Reason"): + chat_status = dict( + is_disabled=True, + reason=reason, + ) + await self.grp.update_one( + {"id": int(chat)}, {"$set": {"chat_status": chat_status}} + ) + + async def total_chat_count(self): + return await self.grp.count_documents({}) + + async def get_all_chats(self): + return self.grp.find({}) + + async def get_db_size(self): + return (await self.db.command("dbstats"))["dataSize"] + + +db = UsersData(MONGO_DB_URI, DB_NAME) diff --git a/Database/sql/afk_sql.py b/Database/sql/afk_sql.py index 540c4142fd3f6a67932ef217398ae3c774db6e92..b925bb4603b7127f59fac4f1aa9106e55b793841 100644 --- a/Database/sql/afk_sql.py +++ b/Database/sql/afk_sql.py @@ -7,7 +7,7 @@ from Database.sql import BASE, SESSION class AFK(BASE): - __tablename__ = "afk_users" + __tablename__ = "afk_user" user_id = Column(BigInteger, primary_key=True) is_afk = Column(Boolean) diff --git a/Database/sql/antichannel_sql.py b/Database/sql/antichannel_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..a6042e5f8faec7a119571b709a54db2228e052e8 --- /dev/null +++ b/Database/sql/antichannel_sql.py @@ -0,0 +1,88 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading + +from sqlalchemy import Boolean, Column +from sqlalchemy.sql.sqltypes import String + +from Database.sql import BASE, SESSION + + +class AntiChannelSettings(BASE): + __tablename__ = "anti_channel_settings" + + chat_id = Column(String(14), primary_key=True) + setting = Column(Boolean, default=False, nullable=False) + + def __init__(self, chat_id: int, disabled: bool): + self.chat_id = str(chat_id) + self.setting = disabled + + def __repr__(self): + return "<ᴀɴᴛɪғʟᴏᴏᴅ sᴇᴛᴛɪɴɢ {} ({})>".format(self.chat_id, self.setting) + + +AntiChannelSettings.__table__.create(checkfirst=True) +ANTICHANNEL_SETTING_LOCK = threading.RLock() + + +def enable_antichannel(chat_id: int): + with ANTICHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiChannelSettings(str(chat_id), True) + + chat.setting = True + SESSION.add(chat) + SESSION.commit() + + +def disable_antichannel(chat_id: int): + with ANTICHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiChannelSettings(str(chat_id), False) + + chat.setting = False + SESSION.add(chat) + SESSION.commit() + + +def antichannel_status(chat_id: int) -> bool: + with ANTICHANNEL_SETTING_LOCK: + d = SESSION.query(AntiChannelSettings).get(str(chat_id)) + if not d: + return False + return d.setting + + +def migrate_chat(old_chat_id, new_chat_id): + with ANTICHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiChannelSettings).get(str(old_chat_id)) + if chat: + chat.chat_id = new_chat_id + SESSION.add(chat) + + SESSION.commit() diff --git a/Database/sql/antiflood_sql.py b/Database/sql/antiflood_sql.py index 5f3b71b6c3126277586cb64af61a38afe1154919..f8a5de981fadbefb7b365c975b9f02785542cc41 100644 --- a/Database/sql/antiflood_sql.py +++ b/Database/sql/antiflood_sql.py @@ -1,6 +1,30 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading -from sqlalchemy import BigInteger, Column, Integer, String, UnicodeText +from sqlalchemy import BigInteger, Column, String, UnicodeText from Database.sql import BASE, SESSION @@ -13,20 +37,20 @@ class FloodControl(BASE): __tablename__ = "antiflood" chat_id = Column(String(14), primary_key=True) user_id = Column(BigInteger) - count = Column(Integer, default=DEF_COUNT) - limit = Column(Integer, default=DEF_LIMIT) + count = Column(BigInteger, default=DEF_COUNT) + limit = Column(BigInteger, default=DEF_LIMIT) def __init__(self, chat_id): self.chat_id = str(chat_id) # ensure string def __repr__(self): - return "" % self.chat_id + return "<ғʟᴏᴏᴅ ᴄᴏɴᴛʀᴏʟ ғᴏʀ %s>" % self.chat_id class FloodSettings(BASE): __tablename__ = "antiflood_settings" chat_id = Column(String(14), primary_key=True) - flood_type = Column(Integer, default=1) + flood_type = Column(BigInteger, default=1) value = Column(UnicodeText, default="0") def __init__(self, chat_id, flood_type=1, value="0"): @@ -35,7 +59,7 @@ class FloodSettings(BASE): self.value = value def __repr__(self): - return "<{} will executing {} for flood.>".format(self.chat_id, self.flood_type) + return "<{} ᴡɪʟʟ ᴇxᴇᴄᴜᴛɪɴɢ {} ғᴏʀ ғʟᴏᴏᴅ.>".format(self.chat_id, self.flood_type) FloodControl.__table__.create(checkfirst=True) @@ -63,37 +87,40 @@ def set_flood(chat_id, amount): def update_flood(chat_id: str, user_id) -> bool: - if str(chat_id) in CHAT_FLOOD: - curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) - - if limit == 0: # no antiflood - return False + if str(chat_id) not in CHAT_FLOOD: + return - if user_id != curr_user_id or user_id is None: # other user - CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT, limit) - return False + curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ) - count += 1 - if count > limit: # too many msgs, kick - CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) - return True + if limit == 0: # no antiflood + return False - # default -> update - CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) + if user_id != curr_user_id or user_id is None: # other user + CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT, limit) return False + count += 1 + if count > limit: # too many msgs, kick + CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit) + return True + + # default -> update + CHAT_FLOOD[str(chat_id)] = (user_id, count, limit) + return False + def get_flood_limit(chat_id): return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2] def set_flood_strength(chat_id, flood_type, value): - # for flood_type + # ғᴏʀ ғʟᴏᴏᴅ_ᴛʏᴘᴇ # 1 = ban # 2 = kick # 3 = mute # 4 = tban # 5 = tmute + # 6 = ᴅᴍᴜᴛᴇ sᴏᴏɴ with INSERTION_FLOOD_SETTINGS_LOCK: curr_setting = SESSION.query(FloodSettings).get(str(chat_id)) if not curr_setting: @@ -115,8 +142,7 @@ def get_flood_setting(chat_id): setting = SESSION.query(FloodSettings).get(str(chat_id)) if setting: return setting.flood_type, setting.value - else: - return 1, "0" + return 1, "0" finally: SESSION.close() diff --git a/Database/sql/antilinkedchannel_sql.py b/Database/sql/antilinkedchannel_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..0b7bce3cba6cc77e729ec19f76f39623b4adfa5a --- /dev/null +++ b/Database/sql/antilinkedchannel_sql.py @@ -0,0 +1,142 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading + +from sqlalchemy import Boolean, Column +from sqlalchemy.sql.sqltypes import String + +from Database.sql import BASE, SESSION + + +class AntiLinkedChannelSettings(BASE): + __tablename__ = "anti_linked_channel_settings" + + chat_id = Column(String(14), primary_key=True) + setting = Column(Boolean, default=False, nullable=False) + + def __init__(self, chat_id: int, disabled: bool): + self.chat_id = str(chat_id) + self.setting = disabled + + def __repr__(self): + return "".format(self.chat_id, self.setting) + + +class AntiPinChannelSettings(BASE): + __tablename__ = "anti_pin_channel_settings" + + chat_id = Column(String(14), primary_key=True) + setting = Column(Boolean, default=False, nullable=False) + + def __init__(self, chat_id: int, disabled: bool): + self.chat_id = str(chat_id) + self.setting = disabled + + def __repr__(self): + return "<ᴀɴᴛɪᴘɪɴ sᴇᴛᴛɪɴɢ {} ({})>".format(self.chat_id, self.setting) + + +AntiLinkedChannelSettings.__table__.create(checkfirst=True) +ANTI_LINKED_CHANNEL_SETTING_LOCK = threading.RLock() + +AntiPinChannelSettings.__table__.create(checkfirst=True) +ANTI_PIN_CHANNEL_SETTING_LOCK = threading.RLock() + + +def enable_linked(chat_id: int): + with ANTI_LINKED_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiLinkedChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiLinkedChannelSettings(chat_id, True) + + chat.setting = True + SESSION.add(chat) + SESSION.commit() + + +def enable_pin(chat_id: int): + with ANTI_PIN_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiPinChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiPinChannelSettings(chat_id, True) + + chat.setting = True + SESSION.add(chat) + SESSION.commit() + + +def disable_linked(chat_id: int): + with ANTI_LINKED_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiLinkedChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiLinkedChannelSettings(chat_id, False) + + chat.setting = False + SESSION.add(chat) + SESSION.commit() + + +def disable_pin(chat_id: int): + with ANTI_PIN_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiPinChannelSettings).get(str(chat_id)) + if not chat: + chat = AntiPinChannelSettings(chat_id, False) + + chat.setting = False + SESSION.add(chat) + SESSION.commit() + + +def status_linked(chat_id: int) -> bool: + with ANTI_LINKED_CHANNEL_SETTING_LOCK: + d = SESSION.query(AntiLinkedChannelSettings).get(str(chat_id)) + if not d: + return False + return d.setting + + +def status_pin(chat_id: int) -> bool: + with ANTI_PIN_CHANNEL_SETTING_LOCK: + d = SESSION.query(AntiPinChannelSettings).get(str(chat_id)) + if not d: + return False + return d.setting + + +def migrate_chat(old_chat_id, new_chat_id): + with ANTI_LINKED_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiLinkedChannelSettings).get(str(old_chat_id)) + if chat: + chat.chat_id = new_chat_id + SESSION.add(chat) + + SESSION.commit() + with ANTI_PIN_CHANNEL_SETTING_LOCK: + chat = SESSION.query(AntiPinChannelSettings).get(str(old_chat_id)) + if chat: + chat.chat_id = new_chat_id + SESSION.add(chat) + + SESSION.commit() diff --git a/Database/sql/approve_sql.py b/Database/sql/approve_sql.py index 8f5e05a31d8d808d2cf8a89d319fbd87868d2ff5..45caa328e734454d01aba9b4245b3fb6bafd16ec 100644 --- a/Database/sql/approve_sql.py +++ b/Database/sql/approve_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading from sqlalchemy import BigInteger, Column, String @@ -15,7 +39,7 @@ class Approvals(BASE): self.user_id = user_id def __repr__(self): - return "" % self.user_id + return "<ᴀᴘᴘʀᴏᴠᴇ %s>" % self.user_id Approvals.__table__.create(checkfirst=True) diff --git a/Database/sql/blacklist_sql.py b/Database/sql/blacklist_sql.py index 7ef2957b54e28086acf030f5733685f5905691aa..d33038496b7b136a014a1e82780d3e3d701e99d3 100644 --- a/Database/sql/blacklist_sql.py +++ b/Database/sql/blacklist_sql.py @@ -1,6 +1,30 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading -from sqlalchemy import Column, Integer, String, UnicodeText, distinct, func +from sqlalchemy import BigInteger, Column, String, UnicodeText, distinct, func from Database.sql import BASE, SESSION @@ -15,7 +39,7 @@ class BlackListFilters(BASE): self.trigger = trigger def __repr__(self): - return "" % (self.trigger, self.chat_id) + return "<ʙʟᴀᴄᴋʟɪsᴛ ғɪʟᴛᴇʀ '%s' ғᴏʀ %s>" % (self.trigger, self.chat_id) def __eq__(self, other): return bool( @@ -28,7 +52,7 @@ class BlackListFilters(BASE): class BlacklistSettings(BASE): __tablename__ = "blacklist_settings" chat_id = Column(String(14), primary_key=True) - blacklist_type = Column(Integer, default=1) + blacklist_type = Column(BigInteger, default=1) value = Column(UnicodeText, default="0") def __init__(self, chat_id, blacklist_type=1, value="0"): @@ -37,7 +61,7 @@ class BlacklistSettings(BASE): self.value = value def __repr__(self): - return "<{} will executing {} for blacklist trigger.>".format( + return "<{} ᴡɪʟʟ ᴇxᴇᴄᴜᴛɪɴɢ {} ғᴏʀ ʙʟᴀᴄᴋʟɪsᴛ ᴛʀɪɢɢᴇʀ.>".format( self.chat_id, self.blacklist_type, ) @@ -146,8 +170,7 @@ def get_blacklist_setting(chat_id): setting = CHAT_SETTINGS_BLACKLISTS.get(str(chat_id)) if setting: return setting["blacklist_type"], setting["value"] - else: - return 1, "0" + return 1, "0" finally: SESSION.close() diff --git a/Database/sql/blacklistusers_sql.py b/Database/sql/blacklistusers_sql.py index c1c7d8c828eccc28f3f027b31d9fbbc632e923d4..7b0115f583b7353538367b693c1a03d69dcd7603 100644 --- a/Database/sql/blacklistusers_sql.py +++ b/Database/sql/blacklistusers_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading from sqlalchemy import Column, String, UnicodeText @@ -46,10 +70,7 @@ def unblacklist_user(user_id): def get_reason(user_id): user = SESSION.query(BlacklistUsers).get(str(user_id)) - rep = "" - if user: - rep = user.reason - + rep = user.reason if user else "" SESSION.close() return rep diff --git a/Database/sql/blsticker_sql.py b/Database/sql/blsticker_sql.py index 5cacb114ada7dd498d63371194347a2cab05753e..897ca46b34c5736d44cd176ac981b188d8c17344 100644 --- a/Database/sql/blsticker_sql.py +++ b/Database/sql/blsticker_sql.py @@ -1,6 +1,30 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading -from sqlalchemy import Column, Integer, String, UnicodeText, distinct, func +from sqlalchemy import BigInteger, Column, String, UnicodeText, distinct, func from Database.sql import BASE, SESSION @@ -15,7 +39,7 @@ class StickersFilters(BASE): self.trigger = trigger def __repr__(self): - return "" % (self.trigger, self.chat_id) + return "" % (self.trigger, self.chat_id) def __eq__(self, other): return bool( @@ -28,7 +52,7 @@ class StickersFilters(BASE): class StickerSettings(BASE): __tablename__ = "blsticker_settings" chat_id = Column(String(14), primary_key=True) - blacklist_type = Column(Integer, default=1) + blacklist_type = Column(BigInteger, default=1) value = Column(UnicodeText, default="0") def __init__(self, chat_id, blacklist_type=1, value="0"): @@ -37,7 +61,7 @@ class StickerSettings(BASE): self.value = value def __repr__(self): - return "<{} will executing {} for blacklist trigger.>".format( + return "<{} ᴡɪʟʟ ᴇxᴇᴄᴜᴛɪɴɢ {} ғᴏʀ ʙʟᴀᴄᴋʟɪsᴛ ᴛʀɪɢɢᴇʀ.>".format( self.chat_id, self.blacklist_type, ) @@ -146,8 +170,7 @@ def get_blacklist_setting(chat_id): setting = CHAT_BLSTICK_BLACKLISTS.get(str(chat_id)) if setting: return setting["blacklist_type"], setting["value"] - else: - return 1, "0" + return 1, "0" finally: SESSION.close() diff --git a/Database/sql/cleaner_sql.py b/Database/sql/cleaner_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..40744a3d86a1ff407aec75c8b8c76314d2305adc --- /dev/null +++ b/Database/sql/cleaner_sql.py @@ -0,0 +1,225 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading + +from sqlalchemy import Boolean, Column, UnicodeText + +from Database.sql import BASE, SESSION + + +class CleanerBlueTextChatSettings(BASE): + __tablename__ = "cleaner_bluetext_chat_setting" + chat_id = Column(UnicodeText, primary_key=True) + is_enable = Column(Boolean, default=False) + + def __init__(self, chat_id, is_enable): + self.chat_id = chat_id + self.is_enable = is_enable + + def __repr__(self): + return "ᴄʟᴇᴀɴ ʙʟᴜᴇ ᴛᴇxᴛ ғᴏʀ {}".format(self.chat_id) + + +class CleanerBlueTextChat(BASE): + __tablename__ = "cleaner_bluetext_chat_ignore_commands" + chat_id = Column(UnicodeText, primary_key=True) + command = Column(UnicodeText, primary_key=True) + + def __init__(self, chat_id, command): + self.chat_id = chat_id + self.command = command + + +class CleanerBlueTextGlobal(BASE): + __tablename__ = "cleaner_bluetext_global_ignore_commands" + command = Column(UnicodeText, primary_key=True) + + def __init__(self, command): + self.command = command + + +CleanerBlueTextChatSettings.__table__.create(checkfirst=True) +CleanerBlueTextChat.__table__.create(checkfirst=True) +CleanerBlueTextGlobal.__table__.create(checkfirst=True) + +CLEANER_CHAT_SETTINGS = threading.RLock() +CLEANER_CHAT_LOCK = threading.RLock() +CLEANER_GLOBAL_LOCK = threading.RLock() + +CLEANER_CHATS = {} +GLOBAL_IGNORE_COMMANDS = set() + + +def set_cleanbt(chat_id, is_enable): + with CLEANER_CHAT_SETTINGS: + curr = SESSION.query(CleanerBlueTextChatSettings).get(str(chat_id)) + + if not curr: + curr = CleanerBlueTextChatSettings(str(chat_id), is_enable) + else: + curr.is_enabled = is_enable + + if str(chat_id) not in CLEANER_CHATS: + CLEANER_CHATS.setdefault( + str(chat_id), {"setting": False, "commands": set()} + ) + + CLEANER_CHATS[str(chat_id)]["setting"] = is_enable + + SESSION.add(curr) + SESSION.commit() + + +def chat_ignore_command(chat_id, ignore): + ignore = ignore.lower() + with CLEANER_CHAT_LOCK: + ignored = SESSION.query(CleanerBlueTextChat).get((str(chat_id), ignore)) + + if not ignored: + if str(chat_id) not in CLEANER_CHATS: + CLEANER_CHATS.setdefault( + str(chat_id), {"setting": False, "commands": set()} + ) + + CLEANER_CHATS[str(chat_id)]["commands"].add(ignore) + + ignored = CleanerBlueTextChat(str(chat_id), ignore) + SESSION.add(ignored) + SESSION.commit() + return True + SESSION.close() + return False + + +def chat_unignore_command(chat_id, unignore): + unignore = unignore.lower() + with CLEANER_CHAT_LOCK: + unignored = SESSION.query(CleanerBlueTextChat).get((str(chat_id), unignore)) + + if unignored: + if str(chat_id) not in CLEANER_CHATS: + CLEANER_CHATS.setdefault( + str(chat_id), {"setting": False, "commands": set()} + ) + if unignore in CLEANER_CHATS.get(str(chat_id)).get("commands"): + CLEANER_CHATS[str(chat_id)]["commands"].remove(unignore) + + SESSION.delete(unignored) + SESSION.commit() + return True + + SESSION.close() + return False + + +def global_ignore_command(command): + command = command.lower() + with CLEANER_GLOBAL_LOCK: + ignored = SESSION.query(CleanerBlueTextGlobal).get(str(command)) + + if not ignored: + GLOBAL_IGNORE_COMMANDS.add(command) + + ignored = CleanerBlueTextGlobal(str(command)) + SESSION.add(ignored) + SESSION.commit() + return True + + SESSION.close() + return False + + +def global_unignore_command(command): + command = command.lower() + with CLEANER_GLOBAL_LOCK: + unignored = SESSION.query(CleanerBlueTextGlobal).get(str(command)) + + if unignored: + if command in GLOBAL_IGNORE_COMMANDS: + GLOBAL_IGNORE_COMMANDS.remove(command) + + SESSION.delete(command) + SESSION.commit() + return True + + SESSION.close() + return False + + +def is_command_ignored(chat_id, command): + if command.lower() in GLOBAL_IGNORE_COMMANDS: + return True + + if str(chat_id) in CLEANER_CHATS and command.lower() in CLEANER_CHATS.get( + str(chat_id) + ).get("commands"): + return True + + return False + + +def is_enabled(chat_id): + if str(chat_id) in CLEANER_CHATS: + return CLEANER_CHATS.get(str(chat_id)).get("setting") + + return False + + +def get_all_ignored(chat_id): + if str(chat_id) in CLEANER_CHATS: + LOCAL_IGNORE_COMMANDS = CLEANER_CHATS.get(str(chat_id)).get("commands") + else: + LOCAL_IGNORE_COMMANDS = set() + + return GLOBAL_IGNORE_COMMANDS, LOCAL_IGNORE_COMMANDS + + +def __load_cleaner_list(): + global GLOBAL_IGNORE_COMMANDS + global CLEANER_CHATS + + try: + GLOBAL_IGNORE_COMMANDS = { + x.command for x in SESSION.query(CleanerBlueTextGlobal).all() + } + finally: + SESSION.close() + + try: + for x in SESSION.query(CleanerBlueTextChatSettings).all(): + CLEANER_CHATS.setdefault(x.chat_id, {"setting": False, "commands": set()}) + CLEANER_CHATS[x.chat_id]["setting"] = x.is_enable + finally: + SESSION.close() + + try: + for x in SESSION.query(CleanerBlueTextChat).all(): + CLEANER_CHATS.setdefault(x.chat_id, {"setting": False, "commands": set()}) + CLEANER_CHATS[x.chat_id]["commands"].add(x.command) + finally: + SESSION.close() + + +__load_cleaner_list() diff --git a/Database/sql/connection_sql.py b/Database/sql/connection_sql.py index 48c17bc4eded5300e5d4ac5b595c9b2e9a819c84..71f6ea97c1110a8b8a64fb716c2a5c3b59046a84 100644 --- a/Database/sql/connection_sql.py +++ b/Database/sql/connection_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading import time from typing import Union @@ -17,8 +41,9 @@ class ChatAccessConnectionSettings(BASE): self.allow_connect_to_chat = str(allow_connect_to_chat) def __repr__(self): - return "".format( - self.chat_id, self.allow_connect_to_chat + return "<ᴄʜᴀᴛ ᴀᴄᴄᴇss sᴇᴛᴛɪɴɢs ({}) is {}>".format( + self.chat_id, + self.allow_connect_to_chat, ) @@ -46,7 +71,7 @@ class ConnectionHistory(BASE): self.conn_time = int(conn_time) def __repr__(self): - return "".format(self.user_id, self.chat_id) + return "<ᴄᴏɴɴᴇᴄᴛɪᴏɴ ᴜsᴇʀ {} ʜɪsᴛᴏʀʏ {}>".format(self.user_id, self.chat_id) ChatAccessConnectionSettings.__table__.create(checkfirst=True) @@ -113,9 +138,8 @@ def disconnect(user_id): SESSION.delete(disconnect) SESSION.commit() return True - else: - SESSION.close() - return False + SESSION.close() + return False def add_history_conn(user_id, chat_id, chat_name): @@ -128,13 +152,15 @@ def add_history_conn(user_id, chat_id, chat_name): .filter(ConnectionHistory.user_id == str(user_id)) .count() ) - getchat_id = {} - for x in HISTORY_CONNECT[int(user_id)]: - getchat_id[HISTORY_CONNECT[int(user_id)][x]["chat_id"]] = x + getchat_id = { + HISTORY_CONNECT[int(user_id)][x]["chat_id"]: x + for x in HISTORY_CONNECT[int(user_id)] + } + if chat_id in getchat_id: todeltime = getchat_id[str(chat_id)] delold = SESSION.query(ConnectionHistory).get( - (int(user_id), str(chat_id)) + (int(user_id), str(chat_id)), ) if delold: SESSION.delete(delold) @@ -146,7 +172,7 @@ def add_history_conn(user_id, chat_id, chat_name): for x in todel: chat_old = HISTORY_CONNECT[int(user_id)][x]["chat_id"] delold = SESSION.query(ConnectionHistory).get( - (int(user_id), str(chat_old)) + (int(user_id), str(chat_old)), ) if delold: SESSION.delete(delold) diff --git a/Database/sql/cust_filters_sql.py b/Database/sql/cust_filters_sql.py index d19dd25a7952ab0c8ae37913d890f6a9fca2f9bd..65b1b2a6f04deaf411a0dbcacae6a9dca5be96d4 100644 --- a/Database/sql/cust_filters_sql.py +++ b/Database/sql/cust_filters_sql.py @@ -11,30 +11,80 @@ class CustomFilters(BASE): chat_id = Column(String(14), primary_key=True) keyword = Column(UnicodeText, primary_key=True, nullable=False) reply = Column(UnicodeText, nullable=False) + is_sticker = Column(Boolean, nullable=False, default=False) + is_document = Column(Boolean, nullable=False, default=False) + is_image = Column(Boolean, nullable=False, default=False) + is_audio = Column(Boolean, nullable=False, default=False) + is_voice = Column(Boolean, nullable=False, default=False) + is_video = Column(Boolean, nullable=False, default=False) + + has_buttons = Column(Boolean, nullable=False, default=False) + # NOTE: Here for legacy purposes, to ensure older filters don't mess up. + has_markdown = Column(Boolean, nullable=False, default=False) + + # NEW FILTER + # alter table cust_filters add column reply_text text; + # alter table cust_filters add column file_type integer default 1; + # alter table cust_filters add column file_id text; reply_text = Column(UnicodeText) file_type = Column(Integer, nullable=False, default=1) file_id = Column(UnicodeText, default=None) - has_buttons = Column(Boolean, nullable=False, default=False) - # NOTE: Here for legacy purposes, to ensure older filters don't mess up. - has_media_spoiler = Column(Boolean, nullable=False, default=False) def __init__( self, - chat_id: int | str, - keyword: str, - reply: str, - reply_text: str, - has_buttons: bool, - has_media_spoiler: bool, - file_type: int, - file_id: str, + chat_id, + keyword, + reply, + is_sticker=False, + is_document=False, + is_image=False, + is_audio=False, + is_voice=False, + is_video=False, + has_buttons=False, + reply_text=None, + file_type=1, + file_id=None, ): self.chat_id = str(chat_id) # ensure string self.keyword = keyword self.reply = reply - self.reply_text = reply_text + self.is_sticker = is_sticker + self.is_document = is_document + self.is_image = is_image + self.is_audio = is_audio + self.is_voice = is_voice + self.is_video = is_video self.has_buttons = has_buttons - self.has_media_spoiler = has_media_spoiler + self.has_markdown = True + + self.reply_text = reply_text + self.file_type = file_type + self.file_id = file_id + + def __repr__(self): + return "" % self.chat_id + + def __eq__(self, other): + return bool( + isinstance(other, CustomFilters) + and self.chat_id == other.chat_id + and self.keyword == other.keyword + ) + + +class NewCustomFilters(BASE): + __tablename__ = "cust_filters_new" + chat_id = Column(String(14), primary_key=True) + keyword = Column(UnicodeText, primary_key=True, nullable=False) + text = Column(UnicodeText) + file_type = Column(Integer, nullable=False, default=1) + file_id = Column(UnicodeText, default=None) + + def __init__(self, chat_id, keyword, text, file_type, file_id): + self.chat_id = str(chat_id) # ensure string + self.keyword = keyword + self.text = text self.file_type = file_type self.file_id = file_id @@ -45,7 +95,7 @@ class CustomFilters(BASE): return bool( isinstance(other, CustomFilters) and self.chat_id == other.chat_id - and self.keyword == other.keyword, + and self.keyword == other.keyword ) @@ -81,6 +131,62 @@ def get_all_filters(): SESSION.close() +def add_filter( + chat_id, + keyword, + reply, + is_sticker=False, + is_document=False, + is_image=False, + is_audio=False, + is_voice=False, + is_video=False, + buttons=None, +): + global CHAT_FILTERS + + if buttons is None: + buttons = [] + + with CUST_FILT_LOCK: + prev = SESSION.query(CustomFilters).get((str(chat_id), keyword)) + if prev: + with BUTTON_LOCK: + prev_buttons = ( + SESSION.query(Buttons) + .filter(Buttons.chat_id == str(chat_id), Buttons.keyword == keyword) + .all() + ) + for btn in prev_buttons: + SESSION.delete(btn) + SESSION.delete(prev) + + filt = CustomFilters( + str(chat_id), + keyword, + reply, + is_sticker, + is_document, + is_image, + is_audio, + is_voice, + is_video, + bool(buttons), + ) + + if keyword not in CHAT_FILTERS.get(str(chat_id), []): + CHAT_FILTERS[str(chat_id)] = sorted( + CHAT_FILTERS.get(str(chat_id), []) + [keyword], + key=lambda x: (-len(x), x), + ) + + SESSION.add(filt) + SESSION.commit() + + for b_name, url, same_line in buttons: + add_note_button_to_db(chat_id, keyword, b_name, url, same_line) + + def new_add_filter( chat_id, keyword, reply_text, file_type, file_id, buttons, media_spoiler ): @@ -106,8 +212,13 @@ def new_add_filter( str(chat_id), keyword, reply="there is should be a new reply", + is_sticker=False, + is_document=False, + is_image=False, + is_audio=False, + is_voice=False, + is_video=False, has_buttons=bool(buttons), - has_media_spoiler=media_spoiler, reply_text=reply_text, file_type=file_type.value, file_id=file_id, @@ -168,7 +279,7 @@ def get_chat_filters(chat_id): SESSION.close() -def get_filter(chat_id, keyword) -> CustomFilters: +def get_filter(chat_id, keyword): try: return SESSION.query(CustomFilters).get((str(chat_id), keyword)) finally: @@ -248,21 +359,14 @@ def __migrate_filters(): else: file_type = Types.TEXT + print(str(x.chat_id), x.keyword, x.reply, file_type.value) if file_type == Types.TEXT: filt = CustomFilters( - str(x.chat_id), - x.keyword, - x.reply, - file_type.value, - None, + str(x.chat_id), x.keyword, x.reply, file_type.value, None ) else: filt = CustomFilters( - str(x.chat_id), - x.keyword, - None, - file_type.value, - x.reply, + str(x.chat_id), x.keyword, None, file_type.value, x.reply ) SESSION.add(filt) @@ -282,10 +386,11 @@ def migrate_chat(old_chat_id, new_chat_id): for filt in chat_filters: filt.chat_id = str(new_chat_id) SESSION.commit() - old_filt = CHAT_FILTERS.get(str(old_chat_id)) - if old_filt: - CHAT_FILTERS[str(new_chat_id)] = old_filt - del CHAT_FILTERS[str(old_chat_id)] + try: + CHAT_FILTERS[str(new_chat_id)] = CHAT_FILTERS[str(old_chat_id)] + except KeyError: + pass + del CHAT_FILTERS[str(old_chat_id)] with BUTTON_LOCK: chat_buttons = ( diff --git a/Database/sql/disable_sql.py b/Database/sql/disable_sql.py index 9958683e889c2495764b6ff1af358d1149967b62..f67aba0b15ff623d6b3901725f9332b230398ae8 100644 --- a/Database/sql/disable_sql.py +++ b/Database/sql/disable_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading from sqlalchemy import Column, String, UnicodeText, distinct, func @@ -15,7 +39,7 @@ class Disable(BASE): self.command = command def __repr__(self): - return "Disabled cmd {} in {}".format(self.command, self.chat_id) + return "ᴅɪsᴀʙʟᴇᴅ ᴄᴍᴅ {} in {}".format(self.command, self.chat_id) Disable.__table__.create(checkfirst=True) diff --git a/Database/sql/fontsql.py b/Database/sql/fontsql.py new file mode 100644 index 0000000000000000000000000000000000000000..c4e177466540d1b6dd21bb179e31f471c407d9f5 --- /dev/null +++ b/Database/sql/fontsql.py @@ -0,0 +1,2361 @@ +class Fonts: + def typewriter(text): + style = { + "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": "𝚣", + "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": "𝚉", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def outline(text): + style = { + "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": "𝕫", + "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": "ℤ", + "0": "𝟘", + "1": "𝟙", + "2": "𝟚", + "3": "𝟛", + "4": "𝟜", + "5": "𝟝", + "6": "𝟞", + "7": "𝟟", + "8": "𝟠", + "9": "𝟡", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def serief(text): + style = { + "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": "𝐳", + "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": "𝐙", + "0": "𝟎", + "1": "𝟏", + "2": "𝟐", + "3": "𝟑", + "4": "𝟒", + "5": "𝟓", + "6": "𝟔", + "7": "𝟕", + "8": "𝟖", + "9": "𝟗", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def bold_cool(text): + style = { + "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": "𝒛", + "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": "𝒁", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def cool(text): + style = { + "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": "𝑧", + "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": "𝑍", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def smallcap(text): + style = { + "a": "ᴀ", + "b": "ʙ", + "c": "ᴄ", + "d": "ᴅ", + "e": "ᴇ", + "f": "ғ", + "g": "ɢ", + "h": "ʜ", + "i": "ɪ", + "j": "J", + "k": "ᴋ", + "l": "ʟ", + "m": "ᴍ", + "n": "ɴ", + "o": "ᴏ", + "p": "ᴘ", + "q": "ǫ", + "r": "ʀ", + "s": "s", + "t": "ᴛ", + "u": "ᴜ", + "v": "ᴠ", + "w": "ᴡ", + "x": "x", + "y": "ʏ", + "z": "ᴢ", + "A": "A", + "B": "B", + "C": "C", + "D": "D", + "E": "E", + "F": "F", + "G": "G", + "H": "H", + "I": "I", + "J": "J", + "K": "K", + "L": "L", + "M": "M", + "N": "N", + "O": "O", + "P": "P", + "Q": "Q", + "R": "R", + "S": "S", + "T": "T", + "U": "U", + "V": "V", + "W": "W", + "X": "X", + "Y": "Y", + "Z": "Z", + "0": "𝟶", + "1": "𝟷", + "2": "𝟸", + "3": "𝟹", + "4": "𝟺", + "5": "𝟻", + "6": "𝟼", + "7": "𝟽", + "8": "𝟾", + "9": "𝟿", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def script(text): + style = { + "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": "𝓏", + "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": "𝒵", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def bold_script(text): + style = { + "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": "𝔃", + "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": "𝓩", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def tiny(text): + style = { + "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": "ᶻ", + "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": "ᶻ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def comic(text): + style = { + "a": "ᗩ", + "b": "ᗷ", + "c": "ᑕ", + "d": "ᗪ", + "e": "ᗴ", + "f": "ᖴ", + "g": "ᘜ", + "h": "ᕼ", + "i": "I", + "j": "ᒍ", + "k": "K", + "l": "ᒪ", + "m": "ᗰ", + "n": "ᑎ", + "o": "O", + "p": "ᑭ", + "q": "ᑫ", + "r": "ᖇ", + "s": "Տ", + "t": "T", + "u": "ᑌ", + "v": "ᐯ", + "w": "ᗯ", + "x": "᙭", + "y": "Y", + "z": "ᘔ", + "A": "ᗩ", + "B": "ᗷ", + "C": "ᑕ", + "D": "ᗪ", + "E": "ᗴ", + "F": "ᖴ", + "G": "ᘜ", + "H": "ᕼ", + "I": "I", + "J": "ᒍ", + "K": "K", + "L": "ᒪ", + "M": "ᗰ", + "N": "ᑎ", + "O": "O", + "P": "ᑭ", + "Q": "ᑫ", + "R": "ᖇ", + "S": "Տ", + "T": "T", + "U": "ᑌ", + "V": "ᐯ", + "W": "ᗯ", + "X": "᙭", + "Y": "Y", + "Z": "ᘔ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def san(text): + style = { + "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": "𝘇", + "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": "𝗭", + "0": "𝟬", + "1": "𝟭", + "2": "𝟮", + "3": "𝟯", + "4": "𝟰", + "5": "𝟱", + "6": "𝟲", + "7": "𝟳", + "8": "𝟴", + "9": "𝟵", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def slant_san(text): + style = { + "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": "𝙯", + "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": "𝙕", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def slant(text): + style = { + "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": "𝘻", + "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": "𝘡", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def sim(text): + style = { + "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": "𝗓", + "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": "𝖹", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def circles(text): + style = { + "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": "Ⓩ︎", + "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": "Ⓩ︎", + "0": "⓪", + "1": "①", + "2": "②", + "3": "③", + "4": "④", + "5": "⑤", + "6": "⑥", + "7": "⑦", + "8": "⑧", + "9": "⑨", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def dark_circle(text): + style = { + "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": "🅩︎", + "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": "🅩", + "0": "⓿", + "1": "➊", + "2": "➋", + "3": "➌", + "4": "➍", + "5": "➎", + "6": "➏", + "7": "➐", + "8": "➑", + "9": "➒", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def gothic(text): + style = { + "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": "𝔷", + "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": "ℨ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def bold_gothic(text): + style = { + "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": "𝖟", + "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": "𝖅", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def cloud(text): + style = { + "a": "a͜͡", + "b": "b͜͡", + "c": "c͜͡", + "d": "d͜͡", + "e": "e͜͡", + "f": "f͜͡", + "g": "g͜͡", + "h": "h͜͡", + "i": "i͜͡", + "j": "j͜͡", + "k": "k͜͡", + "l": "l͜͡", + "m": "m͜͡", + "n": "n͜͡", + "o": "o͜͡", + "p": "p͜͡", + "q": "q͜͡", + "r": "r͜͡", + "s": "s͜͡", + "t": "t͜͡", + "u": "u͜͡", + "v": "v͜͡", + "w": "w͜͡", + "x": "x͜͡", + "y": "y͜͡", + "z": "z͜͡", + "A": "A͜͡", + "B": "B͜͡", + "C": "C͜͡", + "D": "D͜͡", + "E": "E͜͡", + "F": "F͜͡", + "G": "G͜͡", + "H": "H͜͡", + "I": "I͜͡", + "J": "J͜͡", + "K": "K͜͡", + "L": "L͜͡", + "M": "M͜͡", + "N": "N͜͡", + "O": "O͜͡", + "P": "P͜͡", + "Q": "Q͜͡", + "R": "R͜͡", + "S": "S͜͡", + "T": "T͜͡", + "U": "U͜͡", + "V": "V͜͡", + "W": "W͜͡", + "X": "X͜͡", + "Y": "Y͜͡", + "Z": "Z͜͡", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def happy(text): + style = { + "a": "ă̈", + "b": "b̆̈", + "c": "c̆̈", + "d": "d̆̈", + "e": "ĕ̈", + "f": "f̆̈", + "g": "ğ̈", + "h": "h̆̈", + "i": "ĭ̈", + "j": "j̆̈", + "k": "k̆̈", + "l": "l̆̈", + "m": "m̆̈", + "n": "n̆̈", + "o": "ŏ̈", + "p": "p̆̈", + "q": "q̆̈", + "r": "r̆̈", + "s": "s̆̈", + "t": "t̆̈", + "u": "ŭ̈", + "v": "v̆̈", + "w": "w̆̈", + "x": "x̆̈", + "y": "y̆̈", + "z": "z̆̈", + "A": "Ă̈", + "B": "B̆̈", + "C": "C̆̈", + "D": "D̆̈", + "E": "Ĕ̈", + "F": "F̆̈", + "G": "Ğ̈", + "H": "H̆̈", + "I": "Ĭ̈", + "J": "J̆̈", + "K": "K̆̈", + "L": "L̆̈", + "M": "M̆̈", + "N": "N̆̈", + "O": "Ŏ̈", + "P": "P̆̈", + "Q": "Q̆̈", + "R": "R̆̈", + "S": "S̆̈", + "T": "T̆̈", + "U": "Ŭ̈", + "V": "V̆̈", + "W": "W̆̈", + "X": "X̆̈", + "Y": "Y̆̈", + "Z": "Z̆̈", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def sad(text): + style = { + "a": "ȃ̈", + "b": "b̑̈", + "c": "c̑̈", + "d": "d̑̈", + "e": "ȇ̈", + "f": "f̑̈", + "g": "g̑̈", + "h": "h̑̈", + "i": "ȋ̈", + "j": "j̑̈", + "k": "k̑̈", + "l": "l̑̈", + "m": "m̑̈", + "n": "n̑̈", + "o": "ȏ̈", + "p": "p̑̈", + "q": "q̑̈", + "r": "ȓ̈", + "s": "s̑̈", + "t": "t̑̈", + "u": "ȗ̈", + "v": "v̑̈", + "w": "w̑̈", + "x": "x̑̈", + "y": "y̑̈", + "z": "z̑̈", + "A": "Ȃ̈", + "B": "B̑̈", + "C": "C̑̈", + "D": "D̑̈", + "E": "Ȇ̈", + "F": "F̑̈", + "G": "G̑̈", + "H": "H̑̈", + "I": "Ȋ̈", + "J": "J̑̈", + "K": "K̑̈", + "L": "L̑̈", + "M": "M̑̈", + "N": "N̑̈", + "O": "Ȏ̈", + "P": "P̑̈", + "Q": "Q̑̈", + "R": "Ȓ̈", + "S": "S̑̈", + "T": "T̑̈", + "U": "Ȗ̈", + "V": "V̑̈", + "W": "W̑̈", + "X": "X̑̈", + "Y": "Y̑̈", + "Z": "Z̑̈", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def special(text): + style = { + "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": "🇿 ", + "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": "🇿 ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def square(text): + style = { + "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": "🅉", + "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": "🅉", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def dark_square(text): + style = { + "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": "🆉︎", + "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": "🆉︎", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def andalucia(text): + style = { + "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": "ɀ", + "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": "ɀ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def manga(text): + style = { + "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": "乙", + "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": "乙", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def stinky(text): + style = { + "a": "a̾", + "b": "b̾", + "c": "c̾", + "d": "d̾", + "e": "e̾", + "f": "f̾", + "g": "g̾", + "h": "h̾", + "i": "i̾", + "j": "j̾", + "k": "k̾", + "l": "l̾", + "m": "m̾", + "n": "n̾", + "o": "o̾", + "p": "p̾", + "q": "q̾", + "r": "r̾", + "s": "s̾", + "t": "t̾", + "u": "u̾", + "v": "v̾", + "w": "w̾", + "x": "x̾", + "y": "y̾", + "z": "z̾", + "A": "A̾", + "B": "B̾", + "C": "C̾", + "D": "D̾", + "E": "E̾", + "F": "F̾", + "G": "G̾", + "H": "H̾", + "I": "I̾", + "J": "J̾", + "K": "K̾", + "L": "L̾", + "M": "M̾", + "N": "N̾", + "O": "O̾", + "P": "P̾", + "Q": "Q̾", + "R": "R̾", + "S": "S̾", + "T": "T̾", + "U": "U̾", + "V": "V̾", + "W": "W̾", + "X": "X̾", + "Y": "Y̾", + "Z": "Z̾", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def bubbles(text): + style = { + "a": "ḁͦ", + "b": "b̥ͦ", + "c": "c̥ͦ", + "d": "d̥ͦ", + "e": "e̥ͦ", + "f": "f̥ͦ", + "g": "g̥ͦ", + "h": "h̥ͦ", + "i": "i̥ͦ", + "j": "j̥ͦ", + "k": "k̥ͦ", + "l": "l̥ͦ", + "m": "m̥ͦ", + "n": "n̥ͦ", + "o": "o̥ͦ", + "p": "p̥ͦ", + "q": "q̥ͦ", + "r": "r̥ͦ", + "s": "s̥ͦ", + "t": "t̥ͦ", + "u": "u̥ͦ", + "v": "v̥ͦ", + "w": "w̥ͦ", + "x": "x̥ͦ", + "y": "y̥ͦ", + "z": "z̥ͦ", + "A": "Ḁͦ", + "B": "B̥ͦ", + "C": "C̥ͦ", + "D": "D̥ͦ", + "E": "E̥ͦ", + "F": "F̥ͦ", + "G": "G̥ͦ", + "H": "H̥ͦ", + "I": "I̥ͦ", + "J": "J̥ͦ", + "K": "K̥ͦ", + "L": "L̥ͦ", + "M": "M̥ͦ", + "N": "N̥ͦ", + "O": "O̥ͦ", + "P": "P̥ͦ", + "Q": "Q̥ͦ", + "R": "R̥ͦ", + "S": "S̥ͦ", + "T": "T̥ͦ", + "U": "U̥ͦ", + "V": "V̥ͦ", + "W": "W̥ͦ", + "X": "X̥ͦ", + "Y": "Y̥ͦ", + "Z": "Z̥ͦ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def underline(text): + style = { + "a": "a͟", + "b": "b͟", + "c": "c͟", + "d": "d͟", + "e": "e͟", + "f": "f͟", + "g": "g͟", + "h": "h͟", + "i": "i͟", + "j": "j͟", + "k": "k͟", + "l": "l͟", + "m": "m͟", + "n": "n͟", + "o": "o͟", + "p": "p͟", + "q": "q͟", + "r": "r͟", + "s": "s͟", + "t": "t͟", + "u": "u͟", + "v": "v͟", + "w": "w͟", + "x": "x͟", + "y": "y͟", + "z": "z͟", + "A": "A͟", + "B": "B͟", + "C": "C͟", + "D": "D͟", + "E": "E͟", + "F": "F͟", + "G": "G͟", + "H": "H͟", + "I": "I͟", + "J": "J͟", + "K": "K͟", + "L": "L͟", + "M": "M͟", + "N": "N͟", + "O": "O͟", + "P": "P͟", + "Q": "Q͟", + "R": "R͟", + "S": "S͟", + "T": "T͟", + "U": "U͟", + "V": "V͟", + "W": "W͟", + "X": "X͟", + "Y": "Y͟", + "Z": "Z͟", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def ladybug(text): + style = { + "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": "ꁴ", + "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": "ꁴ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def rays(text): + style = { + "a": "a҉", + "b": "b҉", + "c": "c҉", + "d": "d҉", + "e": "e҉", + "f": "f҉", + "g": "g҉", + "h": "h҉", + "i": "i҉", + "j": "j҉", + "k": "k҉", + "l": "l҉", + "m": "m҉", + "n": "n҉", + "o": "o҉", + "p": "p҉", + "q": "q҉", + "r": "r҉", + "s": "s҉", + "t": "t҉", + "u": "u҉", + "v": "v҉", + "w": "w҉", + "x": "x҉", + "y": "y҉", + "z": "z҉", + "A": "A҉", + "B": "B҉", + "C": "C҉", + "D": "D҉", + "E": "E҉", + "F": "F҉", + "G": "G҉", + "H": "H҉", + "I": "I҉", + "J": "J҉", + "K": "K҉", + "L": "L҉", + "M": "M҉", + "N": "N҉", + "O": "O҉", + "P": "P҉", + "Q": "Q҉", + "R": "R҉", + "S": "S҉", + "T": "T҉", + "U": "U҉", + "V": "V҉", + "W": "W҉", + "X": "X҉", + "Y": "Y҉", + "Z": "Z҉", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def birds(text): + style = { + "a": "a҈", + "b": "b҈", + "c": "c҈", + "d": "d҈", + "e": "e҈", + "f": "f҈", + "g": "g҈", + "h": "h҈", + "i": "i҈", + "j": "j҈", + "k": "k҈", + "l": "l҈", + "m": "m҈", + "n": "n҈", + "o": "o҈", + "p": "p҈", + "q": "q҈", + "r": "r҈", + "s": "s҈", + "t": "t҈", + "u": "u҈", + "v": "v҈", + "w": "w҈", + "x": "x҈", + "y": "y҈", + "z": "z҈", + "A": "A҈", + "B": "B҈", + "C": "C҈", + "D": "D҈", + "E": "E҈", + "F": "F҈", + "G": "G҈", + "H": "H҈", + "I": "I҈", + "J": "J҈", + "K": "K҈", + "L": "L҈", + "M": "M҈", + "N": "N҈", + "O": "O҈", + "P": "P҈", + "Q": "Q҈", + "R": "R҈", + "S": "S҈", + "T": "T҈", + "U": "U҈", + "V": "V҈", + "W": "W҈", + "X": "X҈", + "Y": "Y҈", + "Z": "Z҈", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def slash(text): + style = { + "a": "a̸", + "b": "b̸", + "c": "c̸", + "d": "d̸", + "e": "e̸", + "f": "f̸", + "g": "g̸", + "h": "h̸", + "i": "i̸", + "j": "j̸", + "k": "k̸", + "l": "l̸", + "m": "m̸", + "n": "n̸", + "o": "o̸", + "p": "p̸", + "q": "q̸", + "r": "r̸", + "s": "s̸", + "t": "t̸", + "u": "u̸", + "v": "v̸", + "w": "w̸", + "x": "x̸", + "y": "y̸", + "z": "z̸", + "A": "A̸", + "B": "B̸", + "C": "C̸", + "D": "D̸", + "E": "E̸", + "F": "F̸", + "G": "G̸", + "H": "H̸", + "I": "I̸", + "J": "J̸", + "K": "K̸", + "L": "L̸", + "M": "M̸", + "N": "N̸", + "O": "O̸", + "P": "P̸", + "Q": "Q̸", + "R": "R̸", + "S": "S̸", + "T": "T̸", + "U": "U̸", + "V": "V̸", + "W": "W̸", + "X": "X̸", + "Y": "Y̸", + "Z": "Z̸", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def stop(text): + style = { + "a": "a⃠", + "b": "b⃠", + "c": "c⃠", + "d": "d⃠", + "e": "e⃠", + "f": "f⃠", + "g": "g⃠", + "h": "h⃠", + "i": "i⃠", + "j": "j⃠", + "k": "k⃠", + "l": "l⃠", + "m": "m⃠", + "n": "n⃠", + "o": "o⃠", + "p": "p⃠", + "q": "q⃠", + "r": "r⃠", + "s": "s⃠", + "t": "t⃠", + "u": "u⃠", + "v": "v⃠", + "w": "w⃠", + "x": "x⃠", + "y": "y⃠", + "z": "z⃠", + "A": "A⃠", + "B": "B⃠", + "C": "C⃠", + "D": "D⃠", + "E": "E⃠", + "F": "F⃠", + "G": "G⃠", + "H": "H⃠", + "I": "I⃠", + "J": "J⃠", + "K": "K⃠", + "L": "L⃠", + "M": "M⃠", + "N": "N⃠", + "O": "O⃠", + "P": "P⃠", + "Q": "Q⃠", + "R": "R⃠", + "S": "S⃠", + "T": "T⃠", + "U": "U⃠", + "V": "V⃠", + "W": "W⃠", + "X": "X⃠", + "Y": "Y⃠", + "Z": "Z⃠", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def skyline(text): + style = { + "a": "a̺͆", + "b": "b̺͆", + "c": "c̺͆", + "d": "d̺͆", + "e": "e̺͆", + "f": "f̺͆", + "g": "g̺͆", + "h": "h̺͆", + "i": "i̺͆", + "j": "j̺͆", + "k": "k̺͆", + "l": "l̺͆", + "m": "m̺͆", + "n": "n̺͆", + "o": "o̺͆", + "p": "p̺͆", + "q": "q̺͆", + "r": "r̺͆", + "s": "s̺͆", + "t": "t̺͆", + "u": "u̺͆", + "v": "v̺͆", + "w": "w̺͆", + "x": "x̺͆", + "y": "y̺͆", + "z": "z̺͆", + "A": "A̺͆", + "B": "B̺͆", + "C": "C̺͆", + "D": "D̺͆", + "E": "E̺͆", + "F": "F̺͆", + "G": "G̺͆", + "H": "H̺͆", + "I": "I̺͆", + "J": "J̺͆", + "K": "K̺͆", + "L": "L̺͆", + "M": "M̺͆", + "N": "N̺͆", + "O": "O̺͆", + "P": "P̺͆", + "Q": "Q̺͆", + "R": "R̺͆", + "S": "S̺͆", + "T": "T̺͆", + "U": "U̺͆", + "V": "V̺͆", + "W": "W̺͆", + "X": "X̺͆", + "Y": "Y̺͆", + "Z": "Z̺͆", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def arrows(text): + style = { + "a": "a͎", + "b": "b͎", + "c": "c͎", + "d": "d͎", + "e": "e͎", + "f": "f͎", + "g": "g͎", + "h": "h͎", + "i": "i͎", + "j": "j͎", + "k": "k͎", + "l": "l͎", + "m": "m͎", + "n": "n͎", + "o": "o͎", + "p": "p͎", + "q": "q͎", + "r": "r͎", + "s": "s͎", + "t": "t͎", + "u": "u͎", + "v": "v͎", + "w": "w͎", + "x": "x͎", + "y": "y͎", + "z": "z͎", + "A": "A͎", + "B": "B͎", + "C": "C͎", + "D": "D͎", + "E": "E͎", + "F": "F͎", + "G": "G͎", + "H": "H͎", + "I": "I͎", + "J": "J͎", + "K": "K͎", + "L": "L͎", + "M": "M͎", + "N": "N͎", + "O": "O͎", + "P": "P͎", + "Q": "Q͎", + "R": "R͎", + "S": "S͎", + "T": "T͎", + "U": "U͎", + "V": "V͎", + "W": "W͎", + "X": "X͎", + "Y": "Y͎", + "Z": "Z͎", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def rvnes(text): + style = { + "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": "ጊ", + "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": "ጊ", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def strike(text): + style = { + "a": "a̶", + "b": "b̶", + "c": "c̶", + "d": "d̶", + "e": "e̶", + "f": "f̶", + "g": "g̶", + "h": "h̶", + "i": "i̶", + "j": "j̶", + "k": "k̶", + "l": "l̶", + "m": "m̶", + "n": "n̶", + "o": "o̶", + "p": "p̶", + "q": "q̶", + "r": "r̶", + "s": "s̶", + "t": "t̶", + "u": "u̶", + "v": "v̶", + "w": "w̶", + "x": "x̶", + "y": "y̶", + "z": "z̶", + "A": "A̶", + "B": "B̶", + "C": "C̶", + "D": "D̶", + "E": "E̶", + "F": "F̶", + "G": "G̶", + "H": "H̶", + "I": "I̶", + "J": "J̶", + "K": "K̶", + "L": "L̶", + "M": "M̶", + "N": "N̶", + "O": "O̶", + "P": "P̶", + "Q": "Q̶", + "R": "R̶", + "S": "S̶", + "T": "T̶", + "U": "U̶", + "V": "V̶", + "W": "W̶", + "X": "X̶", + "Y": "Y̶", + "Z": "Z̶", + } + for i, j in style.items(): + text = text.replace(i, j) + return text + + def frozen(text): + style = { + "a": "a༙", + "b": "b༙", + "c": "c༙", + "d": "d༙", + "e": "e༙", + "f": "f༙", + "g": "g༙", + "h": "h༙", + "i": "i༙", + "j": "j༙", + "k": "k༙", + "l": "l༙", + "m": "m༙", + "n": "n༙", + "o": "o༙", + "p": "p༙", + "q": "q༙", + "r": "r༙", + "s": "s༙", + "t": "t༙", + "u": "u༙", + "v": "v༙", + "w": "w༙", + "x": "x༙", + "y": "y༙", + "z": "z༙", + "A": "A༙", + "B": "B༙", + "C": "C༙", + "D": "D༙", + "E": "E༙", + "F": "F༙", + "G": "G༙", + "H": "H༙", + "I": "I༙", + "J": "J༙", + "K": "K༙", + "L": "L༙", + "M": "M༙", + "N": "N༙", + "O": "O༙", + "P": "P༙", + "Q": "Q༙", + "R": "R༙", + "S": "S༙", + "T": "T༙", + "U": "U༙", + "V": "V༙", + "W": "W༙", + "X": "X༙", + "Y": "Y༙", + "Z": "Z༙", + } + for i, j in style.items(): + text = text.replace(i, j) + return text diff --git a/Database/sql/forceSubscribe_sql.py b/Database/sql/forceSubscribe_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..9c36b93e3608afc721d627583e7412225d099106 --- /dev/null +++ b/Database/sql/forceSubscribe_sql.py @@ -0,0 +1,46 @@ +from sqlalchemy import Column, Numeric, String + +from Database.sql import BASE, SESSION + + +class forceSubscribe(BASE): + __tablename__ = "forceSubscribe" + chat_id = Column(Numeric, primary_key=True) + channel = Column(String) + + def __init__(self, chat_id, channel): + self.chat_id = chat_id + self.channel = channel + + +forceSubscribe.__table__.create(checkfirst=True) + + +def fs_settings(chat_id): + try: + return ( + SESSION.query(forceSubscribe) + .filter(forceSubscribe.chat_id == chat_id) + .one() + ) + except: + return None + finally: + SESSION.close() + + +def add_channel(chat_id, channel): + adder = SESSION.query(forceSubscribe).get(chat_id) + if adder: + adder.channel = channel + else: + adder = forceSubscribe(chat_id, channel) + SESSION.add(adder) + SESSION.commit() + + +def disapprove(chat_id): + rem = SESSION.query(forceSubscribe).get(chat_id) + if rem: + SESSION.delete(rem) + SESSION.commit() diff --git a/Database/sql/locks_sql.py b/Database/sql/locks_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..3fd71b09d0befeca0e05259e6259ecd22a014a9c --- /dev/null +++ b/Database/sql/locks_sql.py @@ -0,0 +1,292 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +# New chat added -> setup permissions +import threading + +from sqlalchemy import Boolean, Column, String + +from Database.sql import BASE, SESSION + + +class Permissions(BASE): + __tablename__ = "permissions" + chat_id = Column(String(14), primary_key=True) + # Booleans are for "is this locked", _NOT_ "is this allowed" + audio = Column(Boolean, default=False) + voice = Column(Boolean, default=False) + contact = Column(Boolean, default=False) + video = Column(Boolean, default=False) + document = Column(Boolean, default=False) + photo = Column(Boolean, default=False) + sticker = Column(Boolean, default=False) + gif = Column(Boolean, default=False) + url = Column(Boolean, default=False) + bots = Column(Boolean, default=False) + forward = Column(Boolean, default=False) + game = Column(Boolean, default=False) + location = Column(Boolean, default=False) + rtl = Column(Boolean, default=False) + button = Column(Boolean, default=False) + egame = Column(Boolean, default=False) + inline = Column(Boolean, default=False) + + def __init__(self, chat_id): + self.chat_id = str(chat_id) # ensure string + self.audio = False + self.voice = False + self.contact = False + self.video = False + self.document = False + self.photo = False + self.sticker = False + self.gif = False + self.url = False + self.bots = False + self.forward = False + self.game = False + self.location = False + self.rtl = False + self.button = False + self.egame = False + self.inline = False + + def __repr__(self): + return "<ᴘᴇʀᴍɪssɪᴏɴs ғᴏʀ %s>" % self.chat_id + + +class Restrictions(BASE): + __tablename__ = "restrictions" + chat_id = Column(String(14), primary_key=True) + # Booleans are for "is this restricted", _NOT_ "is this allowed" + messages = Column(Boolean, default=False) + media = Column(Boolean, default=False) + other = Column(Boolean, default=False) + preview = Column(Boolean, default=False) + + def __init__(self, chat_id): + self.chat_id = str(chat_id) # ensure string + self.messages = False + self.media = False + self.other = False + self.preview = False + + def __repr__(self): + return "<ʀᴇsᴛʀɪᴄᴛɪᴏɴs ғᴏʀ %s>" % self.chat_id + + +# For those who faced database error, Just uncomment the +# line below and run bot for 1 time & remove that line! + +Permissions.__table__.create(checkfirst=True) +# Permissions.__table__.drop() +Restrictions.__table__.create(checkfirst=True) + +PERM_LOCK = threading.RLock() +RESTR_LOCK = threading.RLock() + + +def init_permissions(chat_id, reset=False): + curr_perm = SESSION.query(Permissions).get(str(chat_id)) + if reset: + SESSION.delete(curr_perm) + SESSION.flush() + perm = Permissions(str(chat_id)) + SESSION.add(perm) + SESSION.commit() + return perm + + +def init_restrictions(chat_id, reset=False): + curr_restr = SESSION.query(Restrictions).get(str(chat_id)) + if reset: + SESSION.delete(curr_restr) + SESSION.flush() + restr = Restrictions(str(chat_id)) + SESSION.add(restr) + SESSION.commit() + return restr + + +def update_lock(chat_id, lock_type, locked): + with PERM_LOCK: + curr_perm = SESSION.query(Permissions).get(str(chat_id)) + if not curr_perm: + curr_perm = init_permissions(chat_id) + + if lock_type == "audio": + curr_perm.audio = locked + elif lock_type == "voice": + curr_perm.voice = locked + elif lock_type == "contact": + curr_perm.contact = locked + elif lock_type == "video": + curr_perm.video = locked + elif lock_type == "document": + curr_perm.document = locked + elif lock_type == "photo": + curr_perm.photo = locked + elif lock_type == "sticker": + curr_perm.sticker = locked + elif lock_type == "gif": + curr_perm.gif = locked + elif lock_type == "url": + curr_perm.url = locked + elif lock_type == "bots": + curr_perm.bots = locked + elif lock_type == "forward": + curr_perm.forward = locked + elif lock_type == "game": + curr_perm.game = locked + elif lock_type == "location": + curr_perm.location = locked + elif lock_type == "rtl": + curr_perm.rtl = locked + elif lock_type == "button": + curr_perm.button = locked + elif lock_type == "egame": + curr_perm.egame = locked + elif lock_type == "inline": + curr_perm.inline = locked + + SESSION.add(curr_perm) + SESSION.commit() + + +def update_restriction(chat_id, restr_type, locked): + with RESTR_LOCK: + curr_restr = SESSION.query(Restrictions).get(str(chat_id)) + if not curr_restr: + curr_restr = init_restrictions(chat_id) + + if restr_type == "messages": + curr_restr.messages = locked + elif restr_type == "media": + curr_restr.media = locked + elif restr_type == "other": + curr_restr.other = locked + elif restr_type == "previews": + curr_restr.preview = locked + elif restr_type == "all": + curr_restr.messages = locked + curr_restr.media = locked + curr_restr.other = locked + curr_restr.preview = locked + SESSION.add(curr_restr) + SESSION.commit() + + +def is_locked(chat_id, lock_type): + curr_perm = SESSION.query(Permissions).get(str(chat_id)) + SESSION.close() + + if not curr_perm: + return False + + if lock_type == "sticker": + return curr_perm.sticker + if lock_type == "photo": + return curr_perm.photo + if lock_type == "audio": + return curr_perm.audio + if lock_type == "voice": + return curr_perm.voice + if lock_type == "contact": + return curr_perm.contact + if lock_type == "video": + return curr_perm.video + if lock_type == "document": + return curr_perm.document + if lock_type == "gif": + return curr_perm.gif + if lock_type == "url": + return curr_perm.url + if lock_type == "bots": + return curr_perm.bots + if lock_type == "forward": + return curr_perm.forward + if lock_type == "game": + return curr_perm.game + if lock_type == "location": + return curr_perm.location + if lock_type == "rtl": + return curr_perm.rtl + if lock_type == "button": + return curr_perm.button + if lock_type == "egame": + return curr_perm.egame + if lock_type == "inline": + return curr_perm.inline + + +def is_restr_locked(chat_id, lock_type): + curr_restr = SESSION.query(Restrictions).get(str(chat_id)) + SESSION.close() + + if not curr_restr: + return False + + if lock_type == "messages": + return curr_restr.messages + if lock_type == "media": + return curr_restr.media + if lock_type == "other": + return curr_restr.other + if lock_type == "previews": + return curr_restr.preview + if lock_type == "all": + return ( + curr_restr.messages + and curr_restr.media + and curr_restr.other + and curr_restr.preview + ) + + +def get_locks(chat_id): + try: + return SESSION.query(Permissions).get(str(chat_id)) + finally: + SESSION.close() + + +def get_restr(chat_id): + try: + return SESSION.query(Restrictions).get(str(chat_id)) + finally: + SESSION.close() + + +def migrate_chat(old_chat_id, new_chat_id): + with PERM_LOCK: + perms = SESSION.query(Permissions).get(str(old_chat_id)) + if perms: + perms.chat_id = str(new_chat_id) + SESSION.commit() + + with RESTR_LOCK: + rest = SESSION.query(Restrictions).get(str(old_chat_id)) + if rest: + rest.chat_id = str(new_chat_id) + SESSION.commit() diff --git a/Database/sql/log_channel_sql.py b/Database/sql/log_channel_sql.py index 472b693ff0f037eb73f0328369db8e676b56b788..52b58601f10357b3ebe0b54de4cb126dca12df47 100644 --- a/Database/sql/log_channel_sql.py +++ b/Database/sql/log_channel_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading import typing diff --git a/Database/sql/nightmode_sql.py b/Database/sql/nightmode_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..c21fc474f33b5fd6757d9a4817e7dc15cbe14829 --- /dev/null +++ b/Database/sql/nightmode_sql.py @@ -0,0 +1,42 @@ +from sqlalchemy import Column, String + +from Database.sql import BASE, SESSION + + +class Nightmode(BASE): + __tablename__ = "nightmode" + chat_id = Column(String(14), primary_key=True) + + def __init__(self, chat_id): + self.chat_id = chat_id + + +Nightmode.__table__.create(checkfirst=True) + + +def add_nightmode(chat_id: str): + nightmoddy = Nightmode(str(chat_id)) + SESSION.add(nightmoddy) + SESSION.commit() + + +def rmnightmode(chat_id: str): + rmnightmoddy = SESSION.query(Nightmode).get(str(chat_id)) + if rmnightmoddy: + SESSION.delete(rmnightmoddy) + SESSION.commit() + + +def get_all_chat_id(): + stark = SESSION.query(Nightmode).all() + SESSION.close() + return stark + + +def is_nightmode_indb(chat_id: str): + try: + s__ = SESSION.query(Nightmode).get(str(chat_id)) + if s__: + return str(s__.chat_id) + finally: + SESSION.close() diff --git a/Database/sql/notes_sql.py b/Database/sql/notes_sql.py index c1ce13ddd12ea510872bbbc296b41012ff3105a5..629243f7cfa8b1c0e4de384d596712d76f69309d 100644 --- a/Database/sql/notes_sql.py +++ b/Database/sql/notes_sql.py @@ -1,6 +1,31 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +# Note: chat_id's are stored as strings because the int is too large to be stored in a PSQL database. import threading -from sqlalchemy import Boolean, Column, Integer, String, UnicodeText, distinct, func +from sqlalchemy import BigInteger, Boolean, Column, String, UnicodeText, distinct, func from Database.sql import BASE, SESSION from Mikobot.plugins.helper_funcs.msg_types import Types @@ -14,7 +39,7 @@ class Notes(BASE): file = Column(UnicodeText) is_reply = Column(Boolean, default=False) has_buttons = Column(Boolean, default=False) - msgtype = Column(Integer, default=Types.BUTTON_TEXT.value) + msgtype = Column(BigInteger, default=Types.BUTTON_TEXT.value) def __init__(self, chat_id, name, value, msgtype, file=None): self.chat_id = str(chat_id) # ensure string @@ -24,12 +49,12 @@ class Notes(BASE): self.file = file def __repr__(self): - return "" % self.name + return "<ɴᴏᴛᴇ %s>" % self.name class Buttons(BASE): __tablename__ = "note_urls" - id = Column(Integer, primary_key=True, autoincrement=True) + id = Column(BigInteger, primary_key=True, autoincrement=True) chat_id = Column(String(14), primary_key=True) note_name = Column(UnicodeText, primary_key=True) name = Column(UnicodeText, nullable=False) @@ -118,10 +143,8 @@ def rm_note(chat_id, note_name): SESSION.delete(note) SESSION.commit() return True - - else: - SESSION.close() - return False + SESSION.close() + return False def get_all_chat_notes(chat_id): diff --git a/Database/sql/raid_sql.py b/Database/sql/raid_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..4688248045f6f526465d0d28b03984e2c4fff877 --- /dev/null +++ b/Database/sql/raid_sql.py @@ -0,0 +1,73 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading + +from sqlalchemy import Column, String + +from Database.sql import BASE, SESSION + + +class RaidChats(BASE): + __tablename__ = "raid_chats" + chat_id = Column(String(14), primary_key=True) + + def __init__(self, chat_id): + self.chat_id = chat_id + + +RaidChats.__table__.create(checkfirst=True) +INSERTION_LOCK = threading.RLock() + + +def is_raid(chat_id): + try: + chat = SESSION.query(RaidChats).get(str(chat_id)) + return bool(chat) + finally: + SESSION.close() + + +def set_raid(chat_id): + with INSERTION_LOCK: + raidchat = SESSION.query(RaidChats).get(str(chat_id)) + if not raidchat: + raidchat = RaidChats(str(chat_id)) + SESSION.add(raidchat) + SESSION.commit() + + +def rem_raid(chat_id): + with INSERTION_LOCK: + raidchat = SESSION.query(RaidChats).get(str(chat_id)) + if raidchat: + SESSION.delete(raidchat) + SESSION.commit() + + +def get_all_raid_chats(): + try: + return SESSION.query(RaidChats.chat_id).all() + finally: + SESSION.close() diff --git a/Database/sql/remind_sql.py b/Database/sql/remind_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..e77baf153f9cf9f747019f6a8ae6113f6f16cf8f --- /dev/null +++ b/Database/sql/remind_sql.py @@ -0,0 +1,140 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading +import time + +from sqlalchemy import Column, Integer, String, UnicodeText +from sqlalchemy.sql.sqltypes import BigInteger + +from Database.sql import BASE, SESSION + + +class Reminds(BASE): + __tablename__ = "reminds" + chat_id = Column(String(14), primary_key=True) + time_seconds = Column(Integer, primary_key=True) + remind_message = Column(UnicodeText, default="") + user_id = Column(BigInteger, default=0) + + def __init__(self, chat_id, time_seconds): + self.chat_id = str(chat_id) + self.time_seconds = int(time_seconds) + + def __repr__(self): + return "<ʀᴇᴍɪɴᴅ ɪɴ {} ғᴏʀ ᴛɪᴍᴇ {}>".format( + self.chat_id, + self.time_seconds, + ) + + +# Reminds.__table__.drop() +Reminds.__table__.create(checkfirst=True) + +INSERTION_LOCK = threading.RLock() + +REMINDERS = {} + + +def set_remind(chat_id, time_sec, remind_message, user_id): + with INSERTION_LOCK: + reminds = SESSION.query(Reminds).get((str(chat_id), time_sec)) + if not reminds: + reminds = Reminds(chat_id, time_sec) + reminds.remind_message = remind_message + reminds.user_id = user_id + SESSION.add(reminds) + SESSION.commit() + if not time_sec in REMINDERS: + REMINDERS[time_sec] = [] + REMINDERS[time_sec].append( + {"chat_id": str(chat_id), "message": remind_message, "user_id": user_id} + ) + + +def rem_remind(chat_id, time_sec, remind_message, user_id): + with INSERTION_LOCK: + reminds = SESSION.query(Reminds).get((str(chat_id), time_sec)) + if reminds: + SESSION.delete(reminds) + SESSION.commit() + REMINDERS[time_sec].remove( + {"chat_id": str(chat_id), "message": remind_message, "user_id": user_id} + ) + return True + SESSION.close() + return False + + +def get_remind_in_chat(chat_id, timestamp): + return ( + SESSION.query(Reminds) + .filter(Reminds.chat_id == str(chat_id), Reminds.time_seconds == timestamp) + .first() + ) + + +def num_reminds_in_chat(chat_id): + return SESSION.query(Reminds).filter(Reminds.chat_id == str(chat_id)).count() + + +def get_reminds_in_chat(chat_id): + try: + return ( + SESSION.query(Reminds) + .filter(Reminds.chat_id == str(chat_id)) + .order_by(Reminds.time_seconds.asc()) + .all() + ) + finally: + SESSION.close() + + +def __get_all_reminds(): + try: + chats = SESSION.query(Reminds).all() + for chat in chats: + if (chat.time_seconds <= round(time.time())) or chat.user_id == 0: + try: + rem_remind( + chat.chat_id, + chat.time_seconds, + chat.remind_message, + chat.user_id, + ) + except: + pass + continue + REMINDERS[chat.time_seconds] = [ + { + "chat_id": chat.chat_id, + "message": chat.remind_message, + "user_id": chat.user_id, + } + ] + finally: + SESSION.close() + + +__get_all_reminds() diff --git a/Database/sql/reporting_sql.py b/Database/sql/reporting_sql.py new file mode 100644 index 0000000000000000000000000000000000000000..48c42d4ee70002af67ade04c95ede1bd04af4f0c --- /dev/null +++ b/Database/sql/reporting_sql.py @@ -0,0 +1,115 @@ +""" +MIT License + +Copyright (c) 2022 Arsh + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +import threading +from typing import Union + +from sqlalchemy import BigInteger, Boolean, Column, String + +from Database.sql import BASE, SESSION + + +class ReportingUserSettings(BASE): + __tablename__ = "user_report_settings" + user_id = Column(BigInteger, primary_key=True) + should_report = Column(Boolean, default=True) + + def __init__(self, user_id): + self.user_id = user_id + + def __repr__(self): + return "<ᴜsᴇʀ ʀᴇᴘᴏʀᴛ sᴇᴛᴛɪɴɢs ({})>".format(self.user_id) + + +class ReportingChatSettings(BASE): + __tablename__ = "chat_report_settings" + chat_id = Column(String(14), primary_key=True) + should_report = Column(Boolean, default=True) + + def __init__(self, chat_id): + self.chat_id = str(chat_id) + + def __repr__(self): + return "<ᴄʜᴀᴛ ʀᴇᴘᴏʀᴛ sᴇᴛᴛɪɴɢs ({})>".format(self.chat_id) + + +ReportingUserSettings.__table__.create(checkfirst=True) +ReportingChatSettings.__table__.create(checkfirst=True) + +CHAT_LOCK = threading.RLock() +USER_LOCK = threading.RLock() + + +def chat_should_report(chat_id: Union[str, int]) -> bool: + try: + chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) + if chat_setting: + return chat_setting.should_report + return False + finally: + SESSION.close() + + +def user_should_report(user_id: int) -> bool: + try: + user_setting = SESSION.query(ReportingUserSettings).get(user_id) + if user_setting: + return user_setting.should_report + return True + finally: + SESSION.close() + + +def set_chat_setting(chat_id: Union[int, str], setting: bool): + with CHAT_LOCK: + chat_setting = SESSION.query(ReportingChatSettings).get(str(chat_id)) + if not chat_setting: + chat_setting = ReportingChatSettings(chat_id) + + chat_setting.should_report = setting + SESSION.add(chat_setting) + SESSION.commit() + + +def set_user_setting(user_id: int, setting: bool): + with USER_LOCK: + user_setting = SESSION.query(ReportingUserSettings).get(user_id) + if not user_setting: + user_setting = ReportingUserSettings(user_id) + + user_setting.should_report = setting + SESSION.add(user_setting) + SESSION.commit() + + +def migrate_chat(old_chat_id, new_chat_id): + with CHAT_LOCK: + chat_notes = ( + SESSION.query(ReportingChatSettings) + .filter(ReportingChatSettings.chat_id == str(old_chat_id)) + .all() + ) + for note in chat_notes: + note.chat_id = str(new_chat_id) + SESSION.commit() diff --git a/Database/sql/userinfo_sql.py b/Database/sql/userinfo_sql.py index a4b62507f2c98312862a6fd59bbc3868448bd27d..d5151e95ffab55a171c7535387faecbbd9dc93b0 100644 --- a/Database/sql/userinfo_sql.py +++ b/Database/sql/userinfo_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏᴏ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading from sqlalchemy import BigInteger, Column, UnicodeText @@ -15,7 +39,7 @@ class UserInfo(BASE): self.info = info def __repr__(self): - return "" % self.user_id + return "<ᴜsᴇʀ ɪɴғᴏ %d>" % self.user_id class UserBio(BASE): @@ -28,7 +52,7 @@ class UserBio(BASE): self.bio = bio def __repr__(self): - return "" % self.user_id + return "<ᴜsᴇʀ ɪɴғᴏ %d>" % self.user_id UserInfo.__table__.create(checkfirst=True) diff --git a/Database/sql/users_sql.py b/Database/sql/users_sql.py index e037eaae47bf658a929d1bc5515c75ecc4259f5a..6307ab94be58117aa65cd22a2a5e485b807153ce 100644 --- a/Database/sql/users_sql.py +++ b/Database/sql/users_sql.py @@ -1,10 +1,33 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading from sqlalchemy import ( BigInteger, Column, ForeignKey, - Integer, String, UnicodeText, UniqueConstraint, @@ -25,7 +48,7 @@ class Users(BASE): self.username = username def __repr__(self): - return "".format(self.username, self.user_id) + return "<ᴜsᴇʀ {} ({})>".format(self.username, self.user_id) class Chats(BASE): @@ -38,12 +61,12 @@ class Chats(BASE): self.chat_name = chat_name def __repr__(self): - return "".format(self.chat_name, self.chat_id) + return "<ᴄʜᴀᴛ {} ({})>".format(self.chat_name, self.chat_id) class ChatMembers(BASE): __tablename__ = "chat_members" - priv_chat_id = Column(Integer, primary_key=True) + priv_chat_id = Column(BigInteger, primary_key=True) # NOTE: Use dual primary key instead of private primary key? chat = Column( String(14), @@ -62,7 +85,7 @@ class ChatMembers(BASE): self.user = user def __repr__(self): - return "".format( + return "<ᴄʜᴀᴛ ᴜsᴇʀ {} ({}) ɪɴ ᴄʜᴀᴛ {} ({})>".format( self.user.username, self.user.user_id, self.chat.chat_name, diff --git a/Database/sql/warns_sql.py b/Database/sql/warns_sql.py index fac4e5303a21dd10fa2e84ffbbc02bd14a96d605..610cdfc3791cdc49ab7aacdd105552f5857ed566 100644 --- a/Database/sql/warns_sql.py +++ b/Database/sql/warns_sql.py @@ -1,15 +1,30 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import threading -from sqlalchemy import ( - BigInteger, - Boolean, - Column, - Integer, - String, - UnicodeText, - distinct, - func, -) +from sqlalchemy import BigInteger, Boolean, Column, String, UnicodeText, distinct, func from sqlalchemy.dialects import postgresql from Database.sql import BASE, SESSION @@ -20,7 +35,7 @@ class Warns(BASE): user_id = Column(BigInteger, primary_key=True) chat_id = Column(String(14), primary_key=True) - num_warns = Column(Integer, default=0) + num_warns = Column(BigInteger, default=0) reasons = Column(postgresql.ARRAY(UnicodeText)) def __init__(self, user_id, chat_id): @@ -30,7 +45,7 @@ class Warns(BASE): self.reasons = [] def __repr__(self): - return "<{} warns for {} in {} for reasons {}>".format( + return "<{} ᴡᴀʀɴs ғᴏʀ {} ɪɴ {} ғᴏʀ ʀᴇᴀsᴏɴs {}>".format( self.num_warns, self.user_id, self.chat_id, @@ -50,7 +65,7 @@ class WarnFilters(BASE): self.reply = reply def __repr__(self): - return "" % self.chat_id + return "<ᴘᴇʀᴍɪssɪᴏɴs ғᴏʀ %s>" % self.chat_id def __eq__(self, other): return bool( @@ -63,7 +78,7 @@ class WarnFilters(BASE): class WarnSettings(BASE): __tablename__ = "warn_settings" chat_id = Column(String(14), primary_key=True) - warn_limit = Column(Integer, default=3) + warn_limit = Column(BigInteger, default=3) soft_warn = Column(Boolean, default=False) def __init__(self, chat_id, warn_limit=3, soft_warn=False): @@ -72,7 +87,7 @@ class WarnSettings(BASE): self.soft_warn = soft_warn def __repr__(self): - return "<{} has {} possible warns.>".format(self.chat_id, self.warn_limit) + return "<{} ʜᴀs {} ᴘᴏssɪʙʟᴇ ᴡᴀʀɴs.>".format(self.chat_id, self.warn_limit) Warns.__table__.create(checkfirst=True) @@ -224,8 +239,7 @@ def get_warn_setting(chat_id): setting = SESSION.query(WarnSettings).get(str(chat_id)) if setting: return setting.warn_limit, setting.soft_warn - else: - return 3, False + return 3, False finally: SESSION.close() diff --git a/Database/sql/welcome_sql.py b/Database/sql/welcome_sql.py index 45e1ce966b01383bfba1e7a6894b583b6cbe186c..1b73f046a9e81a40de4662f90aac0e7ef9d259bd 100644 --- a/Database/sql/welcome_sql.py +++ b/Database/sql/welcome_sql.py @@ -1,3 +1,27 @@ +""" +MIT License + +Copyright (c) 2022 Aʙɪsʜɴᴏɪ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + import random import threading from typing import Union @@ -7,20 +31,19 @@ from sqlalchemy import BigInteger, Boolean, Column, Integer, String, UnicodeText from Database.sql import BASE, SESSION from Mikobot.plugins.helper_funcs.msg_types import Types -DEFAULT_WELCOME = "Hey {first}, how are you?" -DEFAULT_GOODBYE = "Nice knowing you!" +DEFAULT_WELCOME = "ʜᴇʏ {first}, ʜᴏᴡ ᴀʀᴇ ʏᴏᴜ?" +DEFAULT_GOODBYE = "ɴɪᴄᴇ ᴋɴᴏᴡɪɴɢ ʏᴀ!" DEFAULT_WELCOME_MESSAGES = [ - "{first} is here!", # Discord welcome messages copied - "Ready player {first}", - "Welcome bro {first}", + "{first} ɪs ʜᴇʀᴇ!", # Discord welcome messages copied + "ʀᴇᴀᴅʏ ᴘʟᴀʏᴇʀ {first}", + "ᴡᴇʟᴄᴏᴍᴇ ʙʀᴏ {first}", ] DEFAULT_GOODBYE_MESSAGES = [ - "{first} will be missed.", - "{first} when back ?", + "{first} ᴡɪʟʟ ʙᴇ ᴍɪssᴇᴅ.", + "{first} ᴡʜᴇɴ ʙᴀᴄᴋ ?.", ] -# Line 111 to 152 are references from https://bindingofisaac.fandom.com/wiki/Fortune_Telling_Machine class Welcome(BASE): @@ -31,8 +54,7 @@ class Welcome(BASE): custom_content = Column(UnicodeText, default=None) custom_welcome = Column( - UnicodeText, - default=random.choice(DEFAULT_WELCOME_MESSAGES), + UnicodeText, default=random.choice(DEFAULT_WELCOME_MESSAGES) ) welcome_type = Column(Integer, default=Types.TEXT.value) @@ -47,9 +69,8 @@ class Welcome(BASE): self.should_goodbye = should_goodbye def __repr__(self): - return "".format( - self.chat_id, - self.should_welcome, + return "<ᴄʜᴀᴛ {} sʜᴏᴜʟᴅ sʜᴏᴜʟᴅ ɴᴇᴡ ᴜsᴇʀs: {}>".format( + self.chat_id, self.should_welcome ) @@ -114,7 +135,23 @@ class CleanServiceSetting(BASE): self.chat_id = str(chat_id) def __repr__(self): - return "".format(self.chat_id) + return "<ᴄʜᴀᴛ ᴜsᴇᴅ ᴄʟᴇᴀɴ sᴇʀᴠɪᴄᴇ ({})>".format(self.chat_id) + + +class RaidMode(BASE): + __tablename__ = "raid_mode" + chat_id = Column(String(14), primary_key=True) + status = Column(Boolean, default=False) + time = Column(Integer, default=21600) + acttime = Column(Integer, default=3600) + # permanent = Column(Boolean, default=False) + + def __init__(self, chat_id, status, time, acttime): + self.chat_id = str(chat_id) + self.status = status + self.time = time + self.acttime = acttime + # self.permanent = permanent Welcome.__table__.create(checkfirst=True) @@ -123,12 +160,14 @@ GoodbyeButtons.__table__.create(checkfirst=True) WelcomeMute.__table__.create(checkfirst=True) WelcomeMuteUsers.__table__.create(checkfirst=True) CleanServiceSetting.__table__.create(checkfirst=True) +RaidMode.__table__.create(checkfirst=True) INSERTION_LOCK = threading.RLock() WELC_BTN_LOCK = threading.RLock() LEAVE_BTN_LOCK = threading.RLock() WM_LOCK = threading.RLock() CS_LOCK = threading.RLock() +RAID_LOCK = threading.RLock() def welcome_mutes(chat_id): @@ -260,11 +299,7 @@ def set_gdbye_preference(chat_id, should_goodbye): def set_custom_welcome( - chat_id, - custom_content, - custom_welcome, - welcome_type, - buttons=None, + chat_id, custom_content, custom_welcome, welcome_type, buttons=None ): if buttons is None: buttons = [] @@ -426,3 +461,47 @@ def migrate_chat(old_chat_id, new_chat_id): btn.chat_id = str(new_chat_id) SESSION.commit() + + +def getRaidStatus(chat_id): + try: + if stat := SESSION.query(RaidMode).get(str(chat_id)): + return stat.status, stat.time, stat.acttime + return False, 21600, 3600 # default + finally: + SESSION.close() + + +def setRaidStatus(chat_id, status, time=21600, acttime=3600): + with RAID_LOCK: + if prevObj := SESSION.query(RaidMode).get(str(chat_id)): + SESSION.delete(prevObj) + newObj = RaidMode(str(chat_id), status, time, acttime) + SESSION.add(newObj) + SESSION.commit() + + +def toggleRaidStatus(chat_id): + newObj = True + with RAID_LOCK: + prevObj = SESSION.query(RaidMode).get(str(chat_id)) + if prevObj: + newObj = not prevObj.status + stat = RaidMode( + str(chat_id), newObj, prevObj.time or 21600, prevObj.acttime or 3600 + ) + SESSION.add(stat) + SESSION.commit() + return newObj + + +def _ResetRaidOnRestart(): + with RAID_LOCK: + raid = SESSION.query(RaidMode).all() + for r in raid: + r.status = False + SESSION.commit() + + +# it uses a cron job to turn off so if the bot restarts and there is a pending raid disable job then raid will stay on +_ResetRaidOnRestart() diff --git a/Dockerfile b/Dockerfile index 65300611ee04e1159b8081b24932060d2c19d703..1c9254678688bfc57ecf11a2a6659babb950929d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,4 +9,4 @@ RUN pip3 install --upgrade pip setuptools RUN pip install -U -r requirements.txt -CMD python3 -m Mikobot +CMD python3 -m Mikobot \ No newline at end of file diff --git a/Infamous/karma.py b/Infamous/karma.py index 5bfb1f4487ce478645957c0dabe07dd4744af95b..eb4392c09e965875444afa21da04459c5b9e0790 100644 --- a/Infamous/karma.py +++ b/Infamous/karma.py @@ -44,11 +44,10 @@ START_BTN = [ ), ], [ - InlineKeyboardButton(text="HELP", callback_data="help_back"), + InlineKeyboardButton(text="HELP", callback_data="extra_command_handler"), ], [ InlineKeyboardButton(text="DETAILS", callback_data="Miko_"), - InlineKeyboardButton(text="AI", callback_data="ai_handler"), InlineKeyboardButton(text="SOURCE", callback_data="git_source"), ], [ @@ -83,7 +82,7 @@ ALIVE_BTN = [ ] HELP_STRINGS = """ -🫧 *Yae-Miko* 🫧 +🫧 *Yae-Miko* 🫧 [ㅤ](https://telegra.ph/file/b05535884267a19ee5c93.jpg) ☉ *Here, you will find a list of all the available commands.* diff --git a/Mikobot/__init__.py b/Mikobot/__init__.py index 1ac3c97ae585cb2c50f6436554d7df410b9e0532..f3ce2824413bced97640d7533ceeaddccf4ba55b 100644 --- a/Mikobot/__init__.py +++ b/Mikobot/__init__.py @@ -17,7 +17,7 @@ from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.constants import ParseMode from telegram.ext import Application, ApplicationBuilder from telethon import TelegramClient, events -from telethon.sessions import MemorySession +from telethon.sessions import MemorySession, StringSession # <=======================================================================================================> @@ -120,12 +120,12 @@ else: ALLOW_EXCL = Config.ALLOW_EXCL DB_NAME = Config.DB_NAME DB_URI = Config.DATABASE_URL + BAN_STICKER = Config.BAN_STICKER MESSAGE_DUMP = Config.MESSAGE_DUMP SUPPORT_ID = Config.SUPPORT_ID DEL_CMDS = Config.DEL_CMDS EVENT_LOGS = Config.EVENT_LOGS INFOPIC = Config.INFOPIC - BAN_STICKER = Config.BAN_STICKER LOAD = Config.LOAD MONGO_DB_URI = Config.MONGO_DB_URI NO_LOAD = Config.NO_LOAD @@ -230,7 +230,7 @@ loop.run_until_complete( # <=============================================== CLIENT SETUP ========================================================> # Create the Mikobot and TelegramClient instances app = Client("Mikobot", api_id=API_ID, api_hash=API_HASH, bot_token=TOKEN) -tbot = TelegramClient(MemorySession(), API_ID, API_HASH) +tbot = TelegramClient("Yaebot", API_ID, API_HASH) # <=======================================================================================================> # <=============================================== GETTING BOT INFO ========================================================> diff --git a/Mikobot/__main__.py b/Mikobot/__main__.py index c530af04c496bdfe01cd026f600d18d1e7e26ae7..d519f529f08e5fa141fa78d55c5d4a2f5461eb2b 100644 --- a/Mikobot/__main__.py +++ b/Mikobot/__main__.py @@ -36,7 +36,6 @@ from telegram.ext import ( ) from telegram.helpers import escape_markdown -import Database.sql.users_sql as sql from Infamous.karma import * from Mikobot import ( BOT_NAME, @@ -64,65 +63,6 @@ TELETHON_VERSION = telethon.__version__ # <============================================== FUNCTIONS =========================================================> -async def ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "ai_handler": - await query.answer() - await query.message.edit_text( - "🧠 *Artificial Intelligence Functions*:\n\n" - "All Commands:\n" - "➽ /askgpt : A chatbot using GPT for responding to user queries.\n\n" - "➽ /palm : Performs a Palm search using a chatbot.\n\n" - "➽ /upscale : Upscales your image quality.", - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton( - "𝙈𝙊𝙍𝙀 𝙄𝙈𝘼𝙂𝙀 𝙂𝙀𝙉 ➪", callback_data="more_ai_handler" - ), - ], - [ - InlineKeyboardButton("» 𝙃𝙊𝙈𝙀 «", callback_data="Miko_back"), - ], - ], - ), - ) - - -async def more_ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - if query.data == "more_ai_handler": - await query.answer() - await query.message.edit_text( - "*Here's more image gen-related commands*:\n\n" - "Command: /meinamix\n" - " • Description: Generates an image using the meinamix model.\n\n" - "Command: /darksushi\n" - " • Description: Generates an image using the darksushi model.\n\n" - "Command: /meinahentai\n" - " • Description: Generates an image using the meinahentai model.\n\n" - "Command: /darksushimix\n" - " • Description: Generates an image using the darksushimix model.\n\n" - "Command: /anylora\n" - " • Description: Generates an image using the anylora model.\n\n" - "Command: /cetsumix\n" - " • Description: Generates an image using the cetus-mix model.\n\n" - "Command: /darkv2\n" - " • Description: Generates an image using the darkv2 model.\n\n" - "Command: /creative\n" - " • Description: Generates an image using the creative model.", - parse_mode=ParseMode.MARKDOWN, - reply_markup=InlineKeyboardMarkup( - [ - [ - InlineKeyboardButton("⇦ 𝘽𝘼𝘾𝙆", callback_data="ai_handler"), - ], - ], - ), - ) - - def get_readable_time(seconds: int) -> str: count = 0 ping_time = "" @@ -269,6 +209,270 @@ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): ) +async def extra_command_handlered(update: Update, context: ContextTypes.DEFAULT_TYPE): + + keyboard = [ + [ + InlineKeyboardButton("MANAGEMENT", callback_data="help_back"), + InlineKeyboardButton("AI", callback_data="ai_command_handler"), + ], + [ + InlineKeyboardButton("ANIME", callback_data="anime_command_handler"), + InlineKeyboardButton("GENSHIN", callback_data="genshin_command_handler"), + ], + [ + InlineKeyboardButton("HOME", callback_data="Miko_back"), + ], + ] + + reply_markup = InlineKeyboardMarkup(keyboard) + + await update.message.reply_text( + "𝙎𝙚𝙡𝙚𝙘𝙩 𝙩𝙝𝙚 [𝙨𝙚𝙘𝙩𝙞𝙤𝙣](https://telegra.ph/file/8c092f4e9d303f9497c83.jpg) 𝙩𝙝𝙖𝙩 𝙮𝙤𝙪 𝙬𝙖𝙣𝙩 𝙩𝙤 𝙤𝙥𝙚𝙣", + reply_markup=reply_markup, + parse_mode="Markdown", + ) + + +async def extra_command_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "extra_command_handler": + await query.answer() # Use 'await' for asynchronous calls + await query.message.edit_text( + "𝙎𝙚𝙡𝙚𝙘𝙩 𝙩𝙝𝙚 [𝙨𝙚𝙘𝙩𝙞𝙤𝙣](https://telegra.ph/file/8c092f4e9d303f9497c83.jpg) 𝙩𝙝𝙖𝙩 𝙮𝙤𝙪 𝙬𝙖𝙣𝙩 𝙩𝙤 𝙤𝙥𝙚𝙣", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("MANAGEMENT", callback_data="help_back"), + InlineKeyboardButton("AI", callback_data="ai_command_handler"), + ], + [ + InlineKeyboardButton( + "ANIME", callback_data="anime_command_handler" + ), + InlineKeyboardButton( + "GENSHIN", callback_data="genshin_command_handler" + ), + ], + [ + InlineKeyboardButton("HOME", callback_data="Miko_back"), + ], + ] + ), + parse_mode="Markdown", # Added this line to explicitly specify Markdown parsing + ) + + +async def ai_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + keyboard = [ + [ + InlineKeyboardButton("AI", callback_data="ai_handler"), + InlineKeyboardButton("IMAGEGEN", callback_data="more_aihandlered"), + ], + ] + reply_markup = InlineKeyboardMarkup(keyboard) + + await update.message.reply_text( + "🧠 *Here are the options for* [𝗬𝗔𝗘 𝗠𝗜𝗞𝗢](https://telegra.ph/file/ed2d9c3693cacc9b0464e.jpg):", + reply_markup=reply_markup, + parse_mode="Markdown", + ) + + +async def ai_command_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "ai_command_handler": + await query.answer() + await query.message.edit_text( + "🧠 *Here are the options for* [𝗬𝗔𝗘 𝗠𝗜𝗞𝗢](https://telegra.ph/file/ed2d9c3693cacc9b0464e.jpg):", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("AI", callback_data="ai_handler"), + InlineKeyboardButton( + "IMAGEGEN", callback_data="more_aihandlered" + ), + ], + [ + InlineKeyboardButton( + "» 𝘽𝘼𝘾𝙆 «", callback_data="extra_command_handler" + ), + ], + ] + ), + parse_mode="Markdown", + ) + + +async def ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "ai_handler": + await query.answer() + await query.message.edit_text( + "[𝗔𝗿𝘁𝗶𝗳𝗶𝗰𝗶𝗮𝗹 𝗜𝗻𝘁𝗲𝗹𝗹𝗶𝗴𝗲𝗻𝘁 𝗙𝘂𝗻𝗰𝘁𝗶𝗼𝗻𝘀](https://telegra.ph/file/01a2e0cd1b9d03808c546.jpg):\n\n" + "All Commands:\n" + "➽ /askgpt : A chatbot using GPT for responding to user queries.\n\n" + "➽ /palm : Performs a Palm search using a chatbot.\n\n" + "➽ /upscale : Upscales your image quality.", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "More Image Gen ➪", callback_data="more_ai_handler" + ), + ], + [ + InlineKeyboardButton( + "⇦ BACK", callback_data="ai_command_handler" + ), + ], + ], + ), + parse_mode="Markdown", + ) + + +async def more_ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "more_ai_handler": + await query.answer() + await query.message.edit_text( + "*Here's more image gen related commands*:\n\n" + "Command: /meinamix\n" + " • Description: Generates an image using the meinamix model.\n\n" + "Command: /darksushi\n" + " • Description: Generates an image using the darksushi model.\n\n" + "Command: /meinahentai\n" + " • Description: Generates an image using the meinahentai model.\n\n" + "Command: /darksushimix\n" + " • Description: Generates an image using the darksushimix model.\n\n" + "Command: /anylora\n" + " • Description: Generates an image using the anylora model.\n\n" + "Command: /cetsumix\n" + " • Description: Generates an image using the cetsumix model.\n\n" + "Command: /anything\n" + " • Description: Generates an image using the anything model.\n\n" + "Command: /absolute\n" + " • Description: Generates an image using the absolute model.\n\n" + "Command: /darkv2\n" + " • Description: Generates an image using the darkv2 model.\n\n" + "Command: /creative\n" + " • Description: Generates an image using the creative model.", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("⇦ BACK", callback_data="ai_handler"), + ], + ], + ), + ) + + +async def more_aihandlered_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "more_aihandlered": + await query.answer() + await query.message.edit_text( + "*Here's more image gen related commands*:\n\n" + "*Command*: /meinamix\n" + " • Description: Generates an image using the meinamix model.\n\n" + "*Command*: /darksushi\n" + " • Description: Generates an image using the darksushi model.\n\n" + "*Command*: /meinahentai\n" + " • Description: Generates an image using the meinahentai model.\n\n" + "*Command*: /darksushimix\n" + " • Description: Generates an image using the darksushimix model.\n\n" + "*Command*: /anylora\n" + " • Description: Generates an image using the anylora model.\n\n" + "*Command*: /cetsumix\n" + " • Description: Generates an image using the cetsumix model.\n\n" + "*Command*: /anything\n" + " • Description: Generates an image using the anything model.\n\n" + "*Command*: /absolute\n" + " • Description: Generates an image using the absolute model.\n\n" + "*Command*: /darkv2\n" + " • Description: Generates an image using the darkv2 model.\n\n" + "*Command*: /creative\n" + " • Description: Generates an image using the creative model.", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "⇦ BACK", callback_data="ai_command_handler" + ), + ], + ], + ), + ) + + +async def anime_command_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "anime_command_handler": + await query.answer() + await query.message.edit_text( + "⛩[𝗔𝗻𝗶𝗺𝗲 𝗨𝗽𝗱𝗮𝘁𝗲𝘀](https://telegra.ph//file/59d93fede8bf12fec1a51.jpg) :\n\n" + "**╔ /anime: **fetches info on single anime (includes buttons to look up for prequels and sequels)\n" + "**╠ /character: **fetches info on multiple possible characters related to query\n" + "**╠ /manga: **fetches info on multiple possible mangas related to query\n" + "**╠ /airing: **fetches info on airing data for anime\n" + "**╠ /studio: **fetches info on multiple possible studios related to query\n" + "**╠ /schedule: **fetches scheduled animes\n" + "**╠ /browse: **get popular, trending or upcoming animes\n" + "**╠ /top: **to retrieve top animes for a genre or tag\n" + "**╠ /watch: **fetches watch order for anime series\n" + "**╠ /fillers: **to get a list of anime fillers\n" + "**╠ /gettags: **get a list of available tags\n" + "**╠ /animequotes: **get random anime quotes\n" + "**╚ /getgenres: **Get list of available Genres\n\n" + "**⚙️ Group Settings:**\n" + "**╔**\n" + "**╠ /anisettings: **to toggle NSFW lock and airing notifications and other settings in groups (anime news)\n" + "**╚**", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton("More Info", url="https://anilist.co/"), + InlineKeyboardButton( + "㊋Infamous•Hydra", url="https://t.me/Infamous_Hydra" + ), + ], + [ + InlineKeyboardButton( + "» 𝘽𝘼𝘾𝙆 «", callback_data="extra_command_handler" + ), + ], + ] + ), + parse_mode="Markdown", # Added this line to explicitly specify Markdown parsing + ) + + +async def genshin_command_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): + query = update.callback_query + if query.data == "genshin_command_handler": + await query.answer() + await query.message.edit_text( + "⛩ [𝗚𝗲𝗻𝘀𝗵𝗶𝗻 𝗜𝗺𝗽𝗮𝗰𝘁](https://telegra.ph/file/cd03348a4a357624e70db.jpg) ⛩\n\n" + "*UNDER DEVELOPMENT*", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "More Info", url="https://genshin.mihoyo.com/" + ), + ], + [ + InlineKeyboardButton( + "» 𝘽𝘼𝘾𝙆 «", callback_data="extra_command_handler" + ), + ], + ] + ), + parse_mode="Markdown", # Added this line to explicitly specify Markdown parsing + ) + + async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE): """Log the error and send a telegram message to notify the developer.""" # Log the error before we do anything else, so we can see it even if something breaks. @@ -455,8 +659,6 @@ async def Miko_about_callback(update: Update, context: ContextTypes.DEFAULT_TYPE f"➲ Ai integration." f"\n➲ Advance management capability." f"\n➲ Anime bot functionality." - f"\n\nUSERS » {sql.num_users()}" - f"\nCHATS » {sql.num_chats()}" f"\n\nClick on the buttons below for getting help and info about {BOT_NAME}." ) await query.message.edit_text( @@ -540,7 +742,7 @@ async def get_help(update: Update, context: ContextTypes.DEFAULT_TYPE): ) return await update.effective_message.reply_text( - "» Choose an option for getting help.", + "» *Choose an option for getting* [𝗵𝗲𝗹𝗽](https://telegra.ph/file/cce9038f6a9b88eb409b5.jpg)", reply_markup=InlineKeyboardMarkup( [ [ @@ -554,11 +756,12 @@ async def get_help(update: Update, context: ContextTypes.DEFAULT_TYPE): [ InlineKeyboardButton( text="OPEN HERE", - callback_data="help_back", + callback_data="extra_command_handler", ) ], ] ), + parse_mode="Markdown", # Added this line to explicitly specify Markdown parsing ) return @@ -763,7 +966,7 @@ async def migrate_chats(update: Update, context: ContextTypes.DEFAULT_TYPE): def main(): function(CommandHandler("start", start)) - function(CommandHandler("help", get_help)) + function(CommandHandler("help", extra_command_handlered)) function(CallbackQueryHandler(help_button, pattern=r"help_.*")) function(CommandHandler("settings", get_settings)) @@ -773,9 +976,26 @@ def main(): function(CallbackQueryHandler(Miko_about_callback, pattern=r"Miko_")) function(CallbackQueryHandler(gitsource_callback, pattern=r"git_source")) function(CallbackQueryHandler(stats_back, pattern=r"insider_")) + function(MessageHandler(filters.StatusUpdate.MIGRATE, migrate_chats)) function(CallbackQueryHandler(ai_handler_callback, pattern=r"ai_handler")) function(CallbackQueryHandler(more_ai_handler_callback, pattern=r"more_ai_handler")) - function(MessageHandler(filters.StatusUpdate.MIGRATE, migrate_chats)) + function(CallbackQueryHandler(ai_command_callback, pattern="ai_command_handler")) + function( + CallbackQueryHandler(anime_command_callback, pattern="anime_command_handler") + ) + function( + CallbackQueryHandler(more_aihandlered_callback, pattern="more_aihandlered") + ) + function( + CallbackQueryHandler(extra_command_callback, pattern="extra_command_handler") + ) + + function(CommandHandler("ai", ai_command)) + function( + CallbackQueryHandler( + genshin_command_callback, pattern="genshin_command_handler" + ) + ) dispatcher.add_error_handler(error_callback) diff --git a/Mikobot/elevated_users.json b/Mikobot/elevated_users.json new file mode 100644 index 0000000000000000000000000000000000000000..50d2d5e34dac58b1f43e94d7f82c02593d0c4d94 --- /dev/null +++ b/Mikobot/elevated_users.json @@ -0,0 +1,8 @@ +{ + "devs": [5907205317, 5978107653, 2093473332], + "supports": [], + "whitelists": [], + "sudos": [6864672519], + "tigers": [], + "spammers": [] +} diff --git a/Mikobot/plugins/afk.py b/Mikobot/plugins/afk.py index fe015c3a1d6116b655dd05d8f628081bbc61c41e..18dfd9416271b6948e1b37f5cd91167bc74b36ac 100644 --- a/Mikobot/plugins/afk.py +++ b/Mikobot/plugins/afk.py @@ -184,6 +184,12 @@ __help__ = """ » brb , !afk *:* same as the afk command - but not a command. +» /bye [Reason > Optional] - Tell others that you are AFK (Away From Keyboard). + +» /bye [reply to media] - AFK with media. + +» /byedel - Enable auto delete AFK message in group (Only for group admin). Default is **Enable**. + ➠ *When marked as AFK, any mentions will be replied to with a message to say you're not available!* """ diff --git a/Mikobot/plugins/ai.py b/Mikobot/plugins/ai.py index 0a1b5d6d37d2f652f1ed4f7998ef5cc545b7bc9e..f0fa1e763ce034941d63ca2ca2645d69ffb4b00e 100644 --- a/Mikobot/plugins/ai.py +++ b/Mikobot/plugins/ai.py @@ -16,7 +16,7 @@ from Mikobot.state import state # <=======================================================================================================> # <================================================ CONSTANTS =====================================================> -API_URL = "https://lexica.qewertyy.me/models" +API_URL = "https://lexica.qewertyy.dev/models" PALM_MODEL_ID = 0 GPT_MODEL_ID = 5 @@ -103,7 +103,7 @@ async def upscale_image(update: Update, context: ContextTypes.DEFAULT_TYPE): b = base64.b64encode(f).decode("utf-8") response = await state.post( - "https://lexica.qewertyy.me/upscale", + "https://lexica.qewertyy.dev/upscale", data={"image_data": b}, ) diff --git a/Mikobot/plugins/alive.py b/Mikobot/plugins/alive.py index 340a5223d3b3a5914a1dfe25a19994b986d59557..e6266dee0a8d84edcb48741eec7b14696bef3f20 100644 --- a/Mikobot/plugins/alive.py +++ b/Mikobot/plugins/alive.py @@ -34,12 +34,12 @@ async def alive(_, message: Message): caption = f"""**HEY, I AM {BOT_NAME}** ━━━━━━ 🌟✿🌟 ━━━━━━ -✪ **CREATOR:** [🄺🄰🅁🄼🄰](https://t.me/anime_Freakz) +✪ **CREATOR:** [🄺🄰🅁🄼🄰](https://t.me/O_oKarma) {library_versions_text} ➲ **PYTHON:** `{version_info[0]}.{version_info[1]}.{version_info[2]}` -➲ **BOT VERSION:** `2.0` +➲ **BOT VERSION:** `2.1 Rx` ━━━━━━ 🌟✿🌟 ━━━━━━""" await message.reply_animation( diff --git a/Mikobot/plugins/anime.py b/Mikobot/plugins/anime.py index acf7777423eaae67627fa2e44e606ebfefc1a96a..db6a05c006bdae7682b73380c141331b4b54b66a 100644 --- a/Mikobot/plugins/anime.py +++ b/Mikobot/plugins/anime.py @@ -2253,11 +2253,11 @@ async def get_top_animes(gnr: str, page, user): async def get_user_favourites(id_, user, req, page, sighs, duser=None): vars_ = {"id": int(id_), "page": int(page)} result = await return_json_senpai( - FAV_ANI_QUERY - if req == "ANIME" - else FAV_CHAR_QUERY - if req == "CHAR" - else FAV_MANGA_QUERY, + ( + FAV_ANI_QUERY + if req == "ANIME" + else FAV_CHAR_QUERY if req == "CHAR" else FAV_MANGA_QUERY + ), vars_, auth=True, user=int(user), @@ -2268,9 +2268,7 @@ async def get_user_favourites(id_, user, req, page, sighs, duser=None): msg = ( "Favourite Animes:\n\n" if req == "ANIME" - else "Favourite Characters:\n\n" - if req == "CHAR" - else "Favourite Manga:\n\n" + else "Favourite Characters:\n\n" if req == "CHAR" else "Favourite Manga:\n\n" ) for i in data["edges"]: node_name = ( @@ -2373,9 +2371,7 @@ async def get_additional_info( ( DES_INFO_QUERY if req == "desc" - else CHA_INFO_QUERY - if req == "char" - else REL_INFO_QUERY + else CHA_INFO_QUERY if req == "char" else REL_INFO_QUERY ) if ctgry == "ANI" else DESC_INFO_QUERY @@ -2817,11 +2813,11 @@ async def toggle_favourites(id_: int, media: str, user: int): query = ( ANIME_MUTATION if media == "ANIME" or media == "AIRING" - else CHAR_MUTATION - if media == "CHARACTER" - else MANGA_MUTATION - if media == "MANGA" - else STUDIO_MUTATION + else ( + CHAR_MUTATION + if media == "CHARACTER" + else MANGA_MUTATION if media == "MANGA" else STUDIO_MUTATION + ) ) k = await return_json_senpai(query=query, vars_=vars_, auth=True, user=int(user)) try: @@ -2890,9 +2886,7 @@ async def update_anilist(id_, req, user, eid: int = None, status: str = None): query=( ANILIST_MUTATION if req == "lsas" - else ANILIST_MUTATION_UP - if req == "lsus" - else ANILIST_MUTATION_DEL + else ANILIST_MUTATION_UP if req == "lsus" else ANILIST_MUTATION_DEL ), vars_=vars_, auth=True, @@ -2902,9 +2896,11 @@ async def update_anilist(id_, req, user, eid: int = None, status: str = None): ( k["data"]["SaveMediaListEntry"] if req == "lsas" - else k["data"]["UpdateMediaListEntries"] - if req == "lsus" - else k["data"]["DeleteMediaListEntry"] + else ( + k["data"]["UpdateMediaListEntries"] + if req == "lsus" + else k["data"]["DeleteMediaListEntry"] + ) ) return "ok" except KeyError: @@ -4007,32 +4003,52 @@ async def toggle_favourites_btn(client: Client, cq: CallbackQuery, cdata: dict): ) if query[1] == "ANIME" and len(query) == 3 else ( - await get_anilist( - query[3], - page=int(query[4]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[1] == "ANIME" and len(query) != 3 - else ( - await get_manga( - query[3], - page=int(query[4]), - auth=True, - user=auser, - cid=gid if gid != user else None, + ( + await get_anilist( + query[3], + page=int(query[4]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) ) - ) - if query[1] == "MANGA" - else (await get_airing(query[3], auth=True, user=auser, ind=int(query[4]))) - if query[1] == "AIRING" - else (await get_character(query[3], int(query[4]), auth=True, user=auser)) - if query[1] == "CHARACTER" - else ( - await get_studios( - query[3], int(query[4]), user=auser, auth=True, duser=user + if query[1] == "ANIME" and len(query) != 3 + else ( + ( + await get_manga( + query[3], + page=int(query[4]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) + ) + if query[1] == "MANGA" + else ( + ( + await get_airing( + query[3], auth=True, user=auser, ind=int(query[4]) + ) + ) + if query[1] == "AIRING" + else ( + ( + await get_character( + query[3], int(query[4]), auth=True, user=auser + ) + ) + if query[1] == "CHARACTER" + else ( + await get_studios( + query[3], + int(query[4]), + user=auser, + auth=True, + duser=user, + ) + ) + ) + ) ) ) ) @@ -4041,9 +4057,7 @@ async def toggle_favourites_btn(client: Client, cq: CallbackQuery, cdata: dict): pic, msg = ( (result[0], result[1]) if query[1] == "ANIME" and len(query) == 3 - else (result[0]) - if query[1] == "AIRING" - else (result[0], result[1][0]) + else (result[0]) if query[1] == "AIRING" else (result[0], result[1][0]) ) btns = get_btns( query[1], @@ -4075,9 +4089,11 @@ async def list_update_anilist_btn(client: Client, cq: CallbackQuery, cdata: dict row.append( InlineKeyboardButton( text=i, - callback_data=cq.data.replace("lsadd", f"lsas_{i}") - if query[0] == "lsadd" - else cq.data.replace("lsupdt", f"lsus_{i}"), + callback_data=( + cq.data.replace("lsadd", f"lsas_{i}") + if query[0] == "lsadd" + else cq.data.replace("lsupdt", f"lsus_{i}") + ), ) ) if len(row) == 3: @@ -4179,60 +4195,66 @@ async def update_anilist_btn(client: Client, cq: CallbackQuery, cdata: dict): ) if query[2] == "ANIME" and (len(query) == 4 or len(query) == 5) else ( - await get_anilist( - query[4], - page=int(query[5]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "ANIME" and len(query) == 6 - else ( - await get_anilist( - query[5], - page=int(query[6]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "ANIME" and len(query) == 7 - else ( - await get_manga( - query[4], - page=int(query[5]), - auth=True, - user=auser, - cid=gid if gid != user else None, - ) - ) - if query[2] == "MANGA" and len(query) == 6 - else ( - await get_manga( - query[5], - page=int(query[6]), - auth=True, - user=auser, - cid=gid if gid != user else None, + ( + await get_anilist( + query[4], + page=int(query[5]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) ) - ) - if query[2] == "MANGA" and len(query) == 7 - else ( - await get_airing( - query[4] if eid is None else query[5], - auth=True, - user=auser, - ind=int(query[5] if eid is None else query[6]), + if query[2] == "ANIME" and len(query) == 6 + else ( + ( + await get_anilist( + query[5], + page=int(query[6]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) + ) + if query[2] == "ANIME" and len(query) == 7 + else ( + ( + await get_manga( + query[4], + page=int(query[5]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) + ) + if query[2] == "MANGA" and len(query) == 6 + else ( + ( + await get_manga( + query[5], + page=int(query[6]), + auth=True, + user=auser, + cid=gid if gid != user else None, + ) + ) + if query[2] == "MANGA" and len(query) == 7 + else ( + await get_airing( + query[4] if eid is None else query[5], + auth=True, + user=auser, + ind=int(query[5] if eid is None else query[6]), + ) + ) + ) + ) ) ) ) pic, msg = ( (result[0], result[1]) if query[2] == "ANIME" and (len(query) == 4 or len(query) == 5) - else (result[0]) - if query[2] == "AIRING" - else (result[0], result[1][0]) + else (result[0]) if query[2] == "AIRING" else (result[0], result[1][0]) ) btns = get_btns( query[2], @@ -4240,11 +4262,11 @@ async def update_anilist_btn(client: Client, cq: CallbackQuery, cdata: dict): user=user, auth=True, lsqry=query[4] if len(query) == 6 else query[5] if len(query) == 7 else None, - lspage=int(query[5]) - if len(query) == 6 - else int(query[6]) - if len(query) == 7 - else None, + lspage=( + int(query[5]) + if len(query) == 6 + else int(query[6]) if len(query) == 7 else None + ), ) try: await cq.edit_message_media( @@ -4265,9 +4287,7 @@ async def additional_info_btn(client: Client, cq: CallbackQuery, cdata: dict): info = ( "Description" if kek == "desc" - else "Series List" - if kek == "ls" - else "Characters List" + else "Series List" if kek == "ls" else "Characters List" ) page = 0 lsqry = f"_{q[3]}" if len(q) > 6 else "" @@ -4329,9 +4349,11 @@ async def additional_info_btn(client: Client, cq: CallbackQuery, cdata: dict): cbd = ( f"btn_{qry}_{q[3]}_{user}" if len(q) <= 5 - else f"page_ANIME{lsqry}{lspg}_{q[5]}_{user}" - if ctgry == "ANI" - else f"page_CHARACTER{lsqry}{lspg}_{q[5]}_{user}" + else ( + f"page_ANIME{lsqry}{lspg}_{q[5]}_{user}" + if ctgry == "ANI" + else f"page_CHARACTER{lsqry}{lspg}_{q[5]}_{user}" + ) ) button.append([InlineKeyboardButton(text="BACK", callback_data=cbd)]) try: @@ -5112,7 +5134,7 @@ QUOTES_IMG = [ __help__ = """ ⛩ *Anime:* -➠ *Mikobot provides you the best anime-based commands including anime news and much more!* +➠ *Dazai provides you the best anime-based commands including anime news and much more!* ➠ *Commands:* diff --git a/Mikobot/plugins/approve.py b/Mikobot/plugins/approve.py index b3fba10e27aa0cb74d265777071f86f18c487c49..870025d4c534a44e63054a7d603090a69a399347 100644 --- a/Mikobot/plugins/approve.py +++ b/Mikobot/plugins/approve.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update @@ -14,10 +13,7 @@ from Mikobot.plugins.helper_funcs.chat_status import check_admin from Mikobot.plugins.helper_funcs.extraction import extract_user from Mikobot.plugins.log_channel import loggable -# <=======================================================================================================> - -# <================================================ FUNCTION =======================================================> @loggable @check_admin(is_user=True) async def approve(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -214,9 +210,6 @@ async def unapproveall_btn(update: Update, context: ContextTypes.DEFAULT_TYPE): await query.answer("You need to be admin to do this.") -# <=================================================== HELP ====================================================> - - __help__ = """ ➠ Sometimes, you might trust a user not to send unwanted content. Maybe not enough to make them admin, but you might be ok with locks, blacklists, and antiflood not applying to them. @@ -236,7 +229,6 @@ Maybe not enough to make them admin, but you might be ok with locks, blacklists, » /unapproveall: Unapprove *ALL* users in a chat. This cannot be undone. """ -# <================================================ HANDLER =======================================================> APPROVE = DisableAbleCommandHandler("approve", approve, block=False) DISAPPROVE = DisableAbleCommandHandler("unapprove", disapprove, block=False) APPROVED = DisableAbleCommandHandler("approved", approved, block=False) @@ -256,4 +248,3 @@ dispatcher.add_handler(UNAPPROVEALL_BTN) __mod_name__ = "APPROVALS" __command_list__ = ["approve", "unapprove", "approved", "approval"] __handlers__ = [APPROVE, DISAPPROVE, APPROVED, APPROVAL] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/ban.py b/Mikobot/plugins/ban.py index ada4008feeb26c7ac6cc9174d0f3efb5bdb71857..52659e82e846792c7c2f0156ec16962caaabd5c4 100644 --- a/Mikobot/plugins/ban.py +++ b/Mikobot/plugins/ban.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html from telegram import ( @@ -13,7 +12,6 @@ from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes, fil from telegram.helpers import mention_html from Mikobot import DEV_USERS, DRAGONS, LOGGER, OWNER_ID, function -from Mikobot.utils.can_restrict import BAN_STICKER from Mikobot.plugins.disable import DisableAbleCommandHandler from Mikobot.plugins.helper_funcs.chat_status import ( can_delete, @@ -28,10 +26,9 @@ from Mikobot.plugins.helper_funcs.misc import mention_username from Mikobot.plugins.helper_funcs.string_handling import extract_time from Mikobot.plugins.log_channel import gloggable, loggable -# <=======================================================================================================> +BAN_STICKER = "CAACAgUAAxkBAAEGWC5lloYv1tiI3-KPguoH5YX-RveWugACoQ4AAi4b2FQGdUhawbi91DQE" -# <================================================ FUNCTION =======================================================> @connection_status @loggable @check_admin(permission="can_restrict_members", is_both=True) @@ -693,9 +690,6 @@ async def bans_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): return log -# <================================================ HELP =======================================================> - - __help__ = """ » /kickme: kicks the user who issued the command @@ -714,7 +708,6 @@ __help__ = """ Banning or UnBanning channels only work if you reply to their message, so don't use their username to ban/unban. """ -# <================================================ HANDLER =======================================================> BAN_HANDLER = CommandHandler(["ban", "sban"], ban, block=False) TEMPBAN_HANDLER = CommandHandler(["tban"], temp_ban, block=False) KICK_HANDLER = CommandHandler("kick", kick, block=False) @@ -745,4 +738,3 @@ __handlers__ = [ KICKME_HANDLER, BAN_CALLBACK_HANDLER, ] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/blacklist.py b/Mikobot/plugins/blacklist.py index 7157f334b3bdd84bae7e9fe135c44a680c04b022..ab51fdd659e6656308bc3b7cd9f78a4a008365b9 100644 --- a/Mikobot/plugins/blacklist.py +++ b/Mikobot/plugins/blacklist.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html import re @@ -21,12 +20,9 @@ from Mikobot.plugins.helper_funcs.string_handling import extract_time from Mikobot.plugins.log_channel import loggable from Mikobot.plugins.warns import warn -# <=======================================================================================================> - BLACKLIST_GROUP = 11 -# <================================================ FUNCTION =======================================================> @check_admin(is_user=True) @typing_action async def blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -401,9 +397,9 @@ async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): await bot.sendMessage( chat.id, f"Muted {user.first_name} for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 4: @@ -413,9 +409,9 @@ async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): await bot.sendMessage( chat.id, f"Kicked {user.first_name} for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 5: @@ -424,9 +420,9 @@ async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): await bot.sendMessage( chat.id, f"Banned {user.first_name} for using Blacklisted word: {trigger}", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 6: @@ -436,9 +432,9 @@ async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): await bot.sendMessage( chat.id, f"Banned {user.first_name} until '{value}' for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 7: @@ -453,9 +449,9 @@ async def del_blacklist(update: Update, context: ContextTypes.DEFAULT_TYPE): await bot.sendMessage( chat.id, f"Muted {user.first_name} until '{value}' for using Blacklisted word: {trigger}!", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return except BadRequest as excp: @@ -487,9 +483,6 @@ def __stats__(): ) -# <=================================================== HELP ====================================================> - - __mod_name__ = "BLACKLIST" __help__ = """ @@ -517,8 +510,6 @@ Note: » can be https://t.me/addstickers/ or just or reply to the sticker message """ - -# <================================================ HANDLER =======================================================> BLACKLIST_HANDLER = DisableAbleCommandHandler( "blacklist", blacklist, admin_ok=True, block=False ) @@ -545,4 +536,3 @@ __handlers__ = [ BLACKLISTMODE_HANDLER, (BLACKLIST_DEL_HANDLER, BLACKLIST_GROUP), ] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/blacklist_stickers.py b/Mikobot/plugins/blacklist_stickers.py index 94971178579c6f6c39a51c1ac7130dcd6079d6fb..7cc937132e84734abf6e107ffb3c365919e0facc 100644 --- a/Mikobot/plugins/blacklist_stickers.py +++ b/Mikobot/plugins/blacklist_stickers.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html from telegram import ChatPermissions, Update @@ -18,10 +17,7 @@ from Mikobot.plugins.helper_funcs.string_handling import extract_time from Mikobot.plugins.log_channel import loggable from Mikobot.plugins.warns import warn -# <=======================================================================================================> - -# <================================================ FUNCTION =======================================================> async def blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYPE): msg = update.effective_message # type: Optional[Message] chat = update.effective_chat # type: Optional[Chat] @@ -452,9 +448,9 @@ async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYP trigger, ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 4: @@ -468,9 +464,9 @@ async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYP trigger, ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 5: @@ -483,9 +479,9 @@ async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYP trigger, ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 6: @@ -500,9 +496,9 @@ async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYP trigger, ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return elif getmode == 7: @@ -522,9 +518,9 @@ async def del_blackliststicker(update: Update, context: ContextTypes.DEFAULT_TYP trigger, ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return except BadRequest as excp: @@ -558,7 +554,6 @@ def __stats__(): __mod_name__ = "Stickers Blacklist" -# <================================================ HANDLER =======================================================> BLACKLIST_STICKER_HANDLER = DisableAbleCommandHandler( "blsticker", blackliststicker, admin_ok=True, block=False ) @@ -578,4 +573,3 @@ dispatcher.add_handler(ADDBLACKLIST_STICKER_HANDLER) dispatcher.add_handler(UNBLACKLIST_STICKER_HANDLER) dispatcher.add_handler(BLACKLISTMODE_HANDLER) dispatcher.add_handler(BLACKLIST_STICKER_DEL_HANDLER) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/botadmins.py b/Mikobot/plugins/botadmins.py index 50ebb85326e2e672b0f92ca5fb08032863ba61b0..f3e7078057a22c3ec74b7fdd4cc2ffcfeaed5c80 100644 --- a/Mikobot/plugins/botadmins.py +++ b/Mikobot/plugins/botadmins.py @@ -46,7 +46,7 @@ async def botstaff(update: Update, context: ContextTypes.DEFAULT_TYPE): try: owner = await get_chat_member(context, OWNER_ID) owner_info = await mention_html(owner.user.first_name, owner.user.id) - reply = f"✪ CREATOR : {owner_info} ({OWNER_ID})\n" + reply = f"✪ OWNER : {owner_info} ({OWNER_ID})\n" except TelegramError as e: LOGGER.error(f"Error getting owner information: {e}") reply = "" diff --git a/Mikobot/plugins/bug.py b/Mikobot/plugins/bug.py new file mode 100644 index 0000000000000000000000000000000000000000..2fba79909ac20dad75af2a3784659c55a85c89c3 --- /dev/null +++ b/Mikobot/plugins/bug.py @@ -0,0 +1,45 @@ +from pyrogram import filters +from pyrogram.enums import ParseMode + +# Chat IDs where bug reports should be sent +BUG_REPORT_CHAT_IDS = [-1001989832800, -1001823656633] + +from Mikobot import app + + +# Define a handler for the /bug command +@app.on_message(filters.command("bug", prefixes="/")) +def bug_command_handler(client, message): + # Check if the command is a reply + if message.reply_to_message: + # Get the message being replied to + replied_message = message.reply_to_message + + # Extract message content + content = replied_message.text or replied_message.caption + + # Check if there is media content + media_type = ( + "**Media content included**" + if replied_message.photo + or replied_message.document + or replied_message.video + or replied_message.audio + or replied_message.animation + else "**No media content included**" + ) + + # Prepare the report message with message link and media content info + report_message = f"**Bug reported by @{message.from_user.username}:**\n\n{content}\n\n{media_type}\n\n**Message Link:** {replied_message.link}" + + # Send the report message + client.send_message( + BUG_REPORT_CHAT_IDS[0], report_message, parse_mode=ParseMode.MARKDOWN + ) + else: + # If not a reply, send a message to reply with /bug command to report a bug + client.send_message( + message.chat.id, + "To report a bug, please reply to the message with **/ bug** cmd.", + parse_mode=ParseMode.MARKDOWN, + ) diff --git a/Mikobot/plugins/chatbot.py b/Mikobot/plugins/chatbot.py.txt similarity index 100% rename from Mikobot/plugins/chatbot.py rename to Mikobot/plugins/chatbot.py.txt diff --git a/Mikobot/plugins/connection.py b/Mikobot/plugins/connection.py index f3f0d938619330603c2241b95a91d231133716aa..fe127944e3366cf1413e433b10e547cfd7ae3a41 100644 --- a/Mikobot/plugins/connection.py +++ b/Mikobot/plugins/connection.py @@ -205,9 +205,11 @@ async def connect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE): ], ) text += "╘══「 Total {} Chats 」".format( - str(len(gethistory)) + " (max)" - if len(gethistory) == 5 - else str(len(gethistory)), + ( + str(len(gethistory)) + " (max)" + if len(gethistory) == 5 + else str(len(gethistory)) + ), ) conn_hist = InlineKeyboardMarkup(buttons) elif buttons: diff --git a/Mikobot/plugins/couple.py b/Mikobot/plugins/couple.py index 93eafcd2ff15f50c42eadf421887c2f37fb8f763..501df9320d2fbf23afe4d8019522c507b294dec9 100644 --- a/Mikobot/plugins/couple.py +++ b/Mikobot/plugins/couple.py @@ -9,6 +9,14 @@ from Mikobot import app # <=======================================================================================================> +# List of additional images +ADDITIONAL_IMAGES = [ + "https://telegra.ph/file/7ef6006ed6e452a6fd871.jpg", + "https://telegra.ph/file/16ede7c046f35e699ed3c.jpg", + "https://telegra.ph/file/f16b555b2a66853cc594e.jpg", + "https://telegra.ph/file/7ef6006ed6e452a6fd871.jpg", +] + # <================================================ FUNCTION =======================================================> def dt(): @@ -32,33 +40,42 @@ def dt_tom(): tomorrow = str(dt_tom()) today = str(dt()[0]) -COUPLES_PIC = "https://telegra.ph/file/c6d0c884f559b9ed8a54e.jpg" C = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} + ( PGM🎀😶 (https://t.me/Chalnayaaaaaarr) + 花火 (https://t.me/zd_sr07) + ゼロツー (https://t.me/wewewe_x) ) = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} +•➵💞࿐ 𝐇𝐚𝐩𝐩𝐲 𝐜𝐨𝐮𝐩𝐥𝐞 𝐨𝐟 𝐭𝐡𝐞 𝐝𝐚𝐲 +╭────────────── +┊•➢ {} + ( PGM🎀😶 (https://t.me/Chalnayaaaaaarr) + 花火 (https://t.me/zd_sr07) + ゼロツー (https://t.me/wewewe_x) ) = 💞 +╰───•➢♡ +╭────────────── +┊•➢ 𝗡𝗲𝘄 𝗰𝗼𝘂𝗽𝗹𝗲 𝗼𝗳 𝘁𝗵𝗲 𝗱𝗮𝘆 𝗺𝗮𝘆𝗯𝗲 +┊ 𝗰𝗵𝗼𝘀𝗲𝗻 𝗮𝘁 12AM {} +╰───•➢♡ """ CAP = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} + {} = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} +•➵💞࿐ 𝐇𝐚𝐩𝐩𝐲 𝐜𝐨𝐮𝐩𝐥𝐞 𝐨𝐟 𝐭𝐡𝐞 𝐝𝐚𝐲 +╭────────────── +┊•➢ {} + {} = 💞 +╰───•➢♡ +╭────────────── +┊•➢ 𝗡𝗲𝘄 𝗰𝗼𝘂𝗽𝗹𝗲 𝗼𝗳 𝘁𝗵𝗲 𝗱𝗮𝘆 𝗺𝗮𝘆𝗯𝗲 +┊ 𝗰𝗵𝗼𝘀𝗲𝗻 𝗮𝘁 12AM {} +╰───•➢♡ """ CAP2 = """ -✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -{} (tg://openmessage?user_id={}) + {} (tg://openmessage?user_id={}) = 💞\n -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {} +•➵💞࿐ 𝐇𝐚𝐩𝐩𝐲 𝐜𝐨𝐮𝐩𝐥𝐞 𝐨𝐟 𝐭𝐡𝐞 𝐝𝐚𝐲 +╭────────────── +┊{} (tg://openmessage?user_id={}) + {} (tg://openmessage?user_id={}) = 💞\n +╰───•➢♡ +╭────────────── +┊•➢ 𝗡𝗲𝘄 𝗰𝗼𝘂𝗽𝗹𝗲 𝗼𝗳 𝘁𝗵𝗲 𝗱𝗮𝘆 𝗺𝗮𝘆𝗯𝗲 +┊ 𝗰𝗵𝗼𝘀𝗲𝗻 𝗮𝘁 12AM {} +╰───•➢♡ """ @app.on_message(filters.command(["couple", "couples", "shipping"]) & ~filters.private) async def nibba_nibbi(_, message): + COUPLES_PIC = random.choice(ADDITIONAL_IMAGES) # Move inside the command function if message.from_user.id == 5540249238: my_ = await _.get_users("rfxtuv") me = await _.get_users(5540249238) @@ -75,7 +92,7 @@ async def nibba_nibbi(_, message): if not i.user.is_bot: list_of_users.append(i.user.id) if len(list_of_users) < 2: - return await message.reply_text("ɴᴏᴛ ᴇɴᴏᴜɢʜ ᴜsᴇʀs ɪɴ ᴛʜɪs ɢʀᴏᴜᴘ.") + return await message.reply_text("Not enough users in the group.") c1_id = random.choice(list_of_users) c2_id = random.choice(list_of_users) while c1_id == c2_id: @@ -98,11 +115,14 @@ async def nibba_nibbi(_, message): c1_name = (await _.get_users(c1_id)).first_name c2_name = (await _.get_users(c2_id)).first_name print(c1_id, c2_id, c1_name, c2_name) - couple_selection_message = f"""✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧ -➖➖➖➖➖➖➖➖➖➖➖➖ -[{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = 💞 -➖➖➖➖➖➖➖➖➖➖➖➖ -𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {tomorrow}""" + couple_selection_message = f"""•➵💞࿐ 𝐇𝐚𝐩𝐩𝐲 𝐜𝐨𝐮𝐩𝐥𝐞 𝐨𝐟 𝐭𝐡𝐞 𝐝𝐚𝐲 +╭────────────── +┊•➢ [{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = 💞 +╰───•➢♡ +╭────────────── +┊•➢ 𝗡𝗲𝘄 𝗰𝗼𝘂𝗽𝗹𝗲 𝗼𝗳 𝘁𝗵𝗲 𝗱𝗮𝘆 𝗺𝗮𝘆𝗯𝗲 +┊ 𝗰𝗵𝗼𝘀𝗲𝗻 𝗮𝘁 12AM {tomorrow} +╰───•➢♡""" await _.send_photo( message.chat.id, photo=COUPLES_PIC, caption=couple_selection_message ) @@ -112,12 +132,10 @@ async def nibba_nibbi(_, message): # <=================================================== HELP ====================================================> - - __help__ = """ 💘 *Choose couples in your chat* -» /couple, /couples, /shipping *:* Choose 2 users and send their names as couples in your chat. +➦ /couple, /couples, /shipping *:* Choose 2 users and send their names as couples in your chat. """ __mod_name__ = "COUPLE" diff --git a/Mikobot/plugins/cust_filters.py b/Mikobot/plugins/cust_filters.py index 6082e54c89c34605d3ac312e4a93c9989a353c5c..1d443b8929e96f261941dbe73ca197d227db250a 100644 --- a/Mikobot/plugins/cust_filters.py +++ b/Mikobot/plugins/cust_filters.py @@ -14,6 +14,7 @@ from telegram.ext import ( ContextTypes, MessageHandler, ) +from telegram.ext import filters from telegram.ext import filters as filters_module from telegram.helpers import escape_markdown, mention_html @@ -379,9 +380,9 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): chat.id, sticker_id, reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) return except BadRequest as excp: @@ -392,9 +393,11 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message( chat.id, "Message couldn't be sent, Is the sticker id valid?", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id + if chat.is_forum + else None + ), ) return else: @@ -412,26 +415,32 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): or message.from_user.first_name, ), fullname=" ".join( - [ - escape(message.from_user.first_name), - escape(message.from_user.last_name), - ] - if message.from_user.last_name - else [escape(message.from_user.first_name)], + ( + [ + escape(message.from_user.first_name), + escape(message.from_user.last_name), + ] + if message.from_user.last_name + else [escape(message.from_user.first_name)] + ), ), - username="@" + escape(message.from_user.username) - if message.from_user.username - else mention_html( - message.from_user.id, - message.from_user.first_name, + username=( + "@" + escape(message.from_user.username) + if message.from_user.username + else mention_html( + message.from_user.id, + message.from_user.first_name, + ) ), mention=mention_html( message.from_user.id, message.from_user.first_name, ), - chatname=escape(message.chat.title) - if message.chat.type != "private" - else escape(message.from_user.first_name), + chatname=( + escape(message.chat.title) + if message.chat.type != "private" + else escape(message.from_user.first_name) + ), id=message.from_user.id, ) else: @@ -469,9 +478,9 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): filt.file_id, reply_markup=keyboard, reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) else: await ENUM_FUNC_MAP[filt.file_type]( @@ -480,10 +489,10 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): reply_markup=keyboard, caption=filt.reply_text, reply_to_message_id=message.message_id, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, - has_spoiler=filt.has_media_spoiler, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), + has_spoiler=filters.HAS_MEDIA_SPOILER, ) except BadRequest: await send_message( @@ -520,9 +529,9 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): parse_mode=ParseMode.HTML, disable_web_page_preview=True, reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) except BadRequest as excp: if excp.message == "Unsupported url protocol": @@ -559,9 +568,9 @@ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE): await context.bot.send_message( chat.id, filt.reply, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) except BadRequest as excp: LOGGER.exception("Error in filters: " + excp.message) diff --git a/Mikobot/plugins/disasters.py b/Mikobot/plugins/disasters.py new file mode 100644 index 0000000000000000000000000000000000000000..226df39ec5f286a6874d0682591cfb948152a01c --- /dev/null +++ b/Mikobot/plugins/disasters.py @@ -0,0 +1,129 @@ +import html +import json +import os +from typing import Optional + +from telegram import Update +from telegram.ext import CommandHandler + +from Mikobot import dispatcher +from Mikobot.plugins.helper_funcs.chat_status import dev_plus, sudo_plus +from Mikobot.plugins.helper_funcs.extraction import extract_user +from Mikobot.plugins.log_channel import gloggable + +ELEVATED_USERS_FILE = os.path.join(os.getcwd(), "Mikobot/elevated_users.json") + +DISASTER_LEVELS = { + "Dragon": "DRAGONS", + "Demon": "DEMONS", + "Wolf": "WOLVES", + "Tiger": "TIGERS", +} + + +async def check_user_id(user_id: int) -> Optional[str]: + if not user_id: + return "That...is a chat! baka ka omae?" + return None + + +async def update_elevated_users(data): + with open(ELEVATED_USERS_FILE, "w") as outfile: + json.dump(data, outfile, indent=4) + + +async def add_disaster_level(update: Update, level: str, context) -> str: + message = update.effective_message + user = update.effective_user + chat = update.effective_chat + bot, args = context.bot, context.args + user_id = await extract_user(message, context, args) + user_member = await bot.get_chat(user_id) + rt = "" + + reply = await check_user_id(user_id) + if reply: + await message.reply_text(reply) + return "" + + with open(ELEVATED_USERS_FILE, "r") as infile: + data = json.load(infile) + + disaster_list = getattr(DISASTER_LEVELS, level) + if user_id in disaster_list: + await message.reply_text(f"This user is already a {level} Disaster.") + return "" + + for disaster_level, disaster_users in DISASTER_LEVELS.items(): + if user_id in disaster_users: + rt += f"Requested HA to promote this {disaster_level} to {level}." + data[disaster_users].remove(user_id) + setattr(DISASTER_LEVELS, disaster_level, disaster_users) + + data[DISASTER_LEVELS[level]].append(user_id) + setattr(DISASTER_LEVELS, level, user_id) + + await update_effective_message.reply_text( + rt + + f"\nSuccessfully set Disaster level of {user_member.first_name} to {level}!" + ) + + log_message = ( + f"#{level.upper()}\n" + f"Admin: {mention_html(user.id, html.escape(user.first_name))}\n" + f"User: {mention_html(user_member.id, html.escape(user_member.first_name))}" + ) + + if chat.type != "private": + log_message = f"{html.escape(chat.title)}:\n" + log_message + + await update.effective_message.reply_text(log_message) + + await update_elevated_users(data) + + +@dev_plus +@gloggable +async def addsudo(update: Update, context) -> str: + await add_disaster_level(update, "Dragon", context) + + +@sudo_plus +@gloggable +async def addsupport(update: Update, context) -> str: + await add_disaster_level(update, "Demon", context) + + +@sudo_plus +@gloggable +async def addwhitelist(update: Update, context) -> str: + await add_disaster_level(update, "Wolf", context) + + +@sudo_plus +@gloggable +async def addtiger(update: Update, context) -> str: + await add_disaster_level(update, "Tiger", context) + + +# Other functions can be refactored similarly... + +SUDO_HANDLER = CommandHandler("addsudo", addsudo, block=False) +SUPPORT_HANDLER = CommandHandler(("addsupport", "adddemon"), addsupport, block=False) +TIGER_HANDLER = CommandHandler("addtiger", addtiger, block=False) +WHITELIST_HANDLER = CommandHandler( + ("addwhitelist", "addwolf"), addwhitelist, block=False +) + +dispatcher.add_handler(SUDO_HANDLER) +dispatcher.add_handler(SUPPORT_HANDLER) +dispatcher.add_handler(TIGER_HANDLER) +dispatcher.add_handler(WHITELIST_HANDLER) + +__mod_name__ = "Devs" +__handlers__ = [ + SUDO_HANDLER, + SUPPORT_HANDLER, + TIGER_HANDLER, + WHITELIST_HANDLER, +] diff --git a/Mikobot/plugins/extra.py b/Mikobot/plugins/extra.py index 90da99b62f990f79655d7e0cfd9934f5e331d544..9ed2f368faac15c1a6dd3de4196712b3339d3cdb 100644 --- a/Mikobot/plugins/extra.py +++ b/Mikobot/plugins/extra.py @@ -15,49 +15,55 @@ UPTIME = time() # Check bot uptime # <================================================ FUNCTION =======================================================> -async def getid(update: Update, context: ContextTypes.DEFAULT_TYPE): - chat = update.effective_chat - your_id = update.message.from_user.id - message_id = update.message.message_id - reply = update.message.reply_to_message +@app.on_message(filters.command("id")) +async def _id(client, message): + chat = message.chat + your_id = message.from_user.id + mention_user = message.from_user.mention + message_id = message.id + reply = message.reply_to_message - text = f"[Message ID:](https://t.me/{chat.username}/{message_id}) `{message_id}`\n" - text += f"[Your ID:](tg://user?id={your_id}) `{your_id}`\n" + text = f"**๏ [ᴍᴇssᴀɢᴇ ɪᴅ]({message.link})** » `{message_id}`\n" + text += f"**๏ [{mention_user}](tg://user?id={your_id})** » `{your_id}`\n" - if context.args: + if not message.command: + message.command = message.text.split() + + if not message.command: + message.command = message.text.split() + + if len(message.command) == 2: try: - user_id = context.args[0] - text += f"[User ID:](tg://user?id={user_id}) `{user_id}`\n" + split = message.text.split(None, 1)[1].strip() + user_id = (await client.get_users(split)).id + user_mention = (await client.get_users(split)).mention + text += f"**๏ [{user_mention}](tg://user?id={user_id})** » `{user_id}`\n" + except Exception: - await update.message.reply_text( - "This user doesn't exist.", parse_mode="Markdown" - ) - return + return await message.reply_text("**🪄 ᴛʜɪs ᴜsᴇʀ ᴅᴏᴇsɴ'ᴛ ᴇxɪsᴛ.**") - text += f"[Chat ID:](https://t.me/{chat.username}) `{chat.id}`\n\n" + text += f"**๏ [ᴄʜᴀᴛ ɪᴅ ](https://t.me/{chat.username})** » `{chat.id}`\n\n" - if reply: - text += f"[Replied Message ID:](https://t.me/{chat.username}/{reply.message_id}) `{reply.message_id}`\n" - text += f"[Replied User ID:](tg://user?id={reply.from_user.id}) `{reply.from_user.id}`\n\n" + if ( + not getattr(reply, "empty", True) + and not message.forward_from_chat + and not reply.sender_chat + ): + text += f"**๏ [ʀᴇᴘʟɪᴇᴅ ᴍᴇssᴀɢᴇ ɪᴅ]({reply.link})** » `{message.reply_to_message.id}`\n" + text += f"**๏ [ʀᴇᴘʟɪᴇᴅ ᴜsᴇʀ ɪᴅ](tg://user?id={reply.from_user.id})** » `{reply.from_user.id}`\n\n" if reply and reply.forward_from_chat: - text += f"The forwarded channel, {reply.forward_from_chat.title}, has an id of `{reply.forward_from_chat.id}`\n\n" + text += f"๏ ᴛʜᴇ ғᴏʀᴡᴀʀᴅᴇᴅ ᴄʜᴀɴɴᴇʟ, {reply.forward_from_chat.title}, ʜᴀs ᴀɴ ɪᴅ ᴏғ `{reply.forward_from_chat.id}`\n\n" if reply and reply.sender_chat: - text += f"ID of the replied chat/channel, is `{reply.sender_chat.id}`" + text += f"๏ ID ᴏғ ᴛʜᴇ ʀᴇᴘʟɪᴇᴅ ᴄʜᴀᴛ/ᴄʜᴀɴɴᴇʟ, ɪs `{reply.sender_chat.id}`" - # Sticker ID to be sent + # Send sticker and text as a reply sticker_id = ( - "CAACAgIAAx0CanzPTAABASPCZQdU9NbQIol5TW1GU2zV4KfjDMEAAnccAALIWZhJPyYLf3FzPHswBA" - ) - - # Send the sticker - await update.message.reply_sticker(sticker=sticker_id) - - # Send the text message as a caption - await update.message.reply_text( - text, parse_mode="Markdown", disable_web_page_preview=True + "CAACAgIAAx0EdppwYAABAgotZg5rBL4P05Xjmy80p7DdNdneDmUAAnccAALIWZhJPyYLf3FzPHs0BA" ) + await message.reply_sticker(sticker=sticker_id) + await message.reply_text(text, disable_web_page_preview=True) # Function to handle the "logs" command @@ -100,7 +106,7 @@ async def ping(_, m: Message): delta_ping = time() - start up = strftime("%Hh %Mm %Ss", gmtime(time() - UPTIME)) - image_url = "https://telegra.ph/file/f215a1c4adf25a5bad81b.jpg" + image_url = "https://telegra.ph/file/e1049f371bbec3f006f3a.jpg" # Send the image as a reply await replymsg.reply_photo( @@ -115,7 +121,6 @@ async def ping(_, m: Message): # <================================================ HANDLER =======================================================> function(CommandHandler("logs", logs, block=False)) -function(CommandHandler("id", getid, block=False)) function(CallbackQueryHandler(close_callback, pattern="^close$", block=False)) # <================================================= HELP ======================================================> diff --git a/Mikobot/plugins/feds.py b/Mikobot/plugins/feds.py index 3930f432f6b2cd17c5a2e80c9af87858ab94a4fc..fdf6d6c3aaa10073a2eb480dcb0d54d6646aea72 100644 --- a/Mikobot/plugins/feds.py +++ b/Mikobot/plugins/feds.py @@ -276,9 +276,9 @@ async def join_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): getfed["fname"], ), parse_mode="markdown", - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) await message.reply_text( @@ -315,9 +315,11 @@ async def leave_fed(update: Update, context: ContextTypes.DEFAULT_TYPE): fed_info["fname"], ), parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) await send_message( update.effective_message, @@ -1246,9 +1248,11 @@ async def set_frules(update: Update, context: ContextTypes.DEFAULT_TYPE): getfed["fname"], ), parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) await update.effective_message.reply_text( f"Rules have been changed to :\n{rules}!" @@ -1777,9 +1781,11 @@ async def fed_import_bans(update: Update, context: ContextTypes.DEFAULT_TYPE): get_fedlog, teks, parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) elif fileformat == "csv": multi_fed_id = [] @@ -1859,9 +1865,11 @@ async def fed_import_bans(update: Update, context: ContextTypes.DEFAULT_TYPE): get_fedlog, teks, parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) else: await send_message(update.effective_message, "This file is not supported.") @@ -2150,9 +2158,11 @@ async def subs_feds(update: Update, context: ContextTypes.DEFAULT_TYPE): getfed["fname"], ), parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) else: await send_message( @@ -2224,9 +2234,11 @@ async def unsubs_feds(update: Update, context: ContextTypes.DEFAULT_TYPE): getfed["fname"], ), parse_mode="markdown", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) else: await send_message( diff --git a/Mikobot/plugins/getreaction.py.txt b/Mikobot/plugins/getreaction.py.txt new file mode 100644 index 0000000000000000000000000000000000000000..e61de6fa9695ae86bd9fbaac6b6fe6c6ff596a27 --- /dev/null +++ b/Mikobot/plugins/getreaction.py.txt @@ -0,0 +1,35 @@ +from pyrogram import filters +from pyrogram.types import Message + +from Mikobot import app + + +def has_reactions(_, __, m: Message): + return bool(m.reply_to_message.reactions) + + +@app.on_message(filters.command("getreaction") & filters.reply) +def get_reaction_list(client, message): + reaction_list = {} + reply_to_message = message.reply_to_message + for reaction in reply_to_message.reactions: + users = [] + for user_id in reaction.user_ids: + user = client.get_users(user_id) + users.append(user.username or user.first_name) + reaction_list[reaction.name] = users + + if reaction_list: + result_text = "\n".join( + f"{reaction}: {', '.join(users)}" + for reaction, users in reaction_list.items() + ) + message.reply_text(result_text) + else: + message.reply_text("No reactions found in the replied message.") + + +@app.on_message(filters.text & filters.reply & filters.create(has_reactions)) +def reply_get_reaction_list(client, message): + if message.text.lower() == "/getreaction": + get_reaction_list(client, message) diff --git a/Mikobot/plugins/helper_funcs/misc.py b/Mikobot/plugins/helper_funcs/misc.py index 287b4792667fc5900f41c3ccdaf2f705c7a89452..aa57bbf3f1e417fc3628f04f75d57a3e94ae8872 100644 --- a/Mikobot/plugins/helper_funcs/misc.py +++ b/Mikobot/plugins/helper_funcs/misc.py @@ -95,7 +95,9 @@ def paginate_modules(page_n: int, module_dict: Dict, prefix, chat=None) -> List: EqInlineKeyboardButton( "◁", callback_data="{}_prev({})".format(prefix, modulo_page) ), - EqInlineKeyboardButton("» 𝙃𝙊𝙈𝙀 «", callback_data="Miko_back"), + EqInlineKeyboardButton( + "» 𝘽𝘼𝘾𝙆 «", callback_data="extra_command_handler" + ), EqInlineKeyboardButton( "▷", callback_data="{}_next({})".format(prefix, modulo_page) ), diff --git a/Mikobot/plugins/imagegen.py b/Mikobot/plugins/imagegen.py index 454d40ee52bf6aafd9978da8f952f073b81ae072..7e07b673d99bf7233813dae1517b7056ad1b3a2d 100644 --- a/Mikobot/plugins/imagegen.py +++ b/Mikobot/plugins/imagegen.py @@ -12,8 +12,8 @@ from Mikobot import tbot as client # <=======================================================================================================> -BASE_URL = "https://lexica.qewertyy.me" -SESSION_HEADERS = {"Host": "lexica.qewertyy.me"} +BASE_URL = "https://lexica.qewertyy.dev" +SESSION_HEADERS = {"Host": "lexica.qewertyy.dev"} # <=============================================== CLASS + FUNCTION ========================================================> diff --git a/Mikobot/plugins/info.py b/Mikobot/plugins/info.py index 96b54be79a4c584d57818cfdf79978d2568da657..f316073edf0a040c8ec1233a7d8928e0331311d8 100644 --- a/Mikobot/plugins/info.py +++ b/Mikobot/plugins/info.py @@ -4,7 +4,12 @@ import re from html import escape from random import choice -from telegram import ChatMemberAdministrator, Update +from telegram import ( + ChatMemberAdministrator, + InlineKeyboardButton, + InlineKeyboardMarkup, + Update, +) from telegram.constants import ChatID, ChatType, ParseMode from telegram.error import BadRequest from telegram.ext import CommandHandler, ContextTypes @@ -14,7 +19,7 @@ from Database.sql.approve_sql import is_approved from Infamous.karma import START_IMG from Mikobot import DEV_USERS, DRAGONS, INFOPIC, OWNER_ID, function from Mikobot.__main__ import STATS, USER_INFO -from Mikobot.plugins.helper_funcs.chat_status import support_plus, dev_plus +from Mikobot.plugins.helper_funcs.chat_status import support_plus from Mikobot.plugins.users import get_user_id # <=======================================================================================================> @@ -187,15 +192,28 @@ async def info(update: Update, context: ContextTypes.DEFAULT_TYPE): await reply_with_text(escape(head)) -@dev_plus +@support_plus async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE): - stats = "📊 Miko-Bot's Statistics:\n\n" + "\n".join( + stats = "📊 Yae Miko Bot's Statistics:\n\n" + "\n".join( [mod.__stats__() for mod in STATS] ) result = re.sub(r"(\d+)", r"\1", stats) + keyboard = [ + [ + InlineKeyboardButton( + "㊋ Infamous • Hydra", url="https://t.me/Infamous_Hydra" + ), + ] + ] + + reply_markup = InlineKeyboardMarkup(keyboard) + await update.effective_message.reply_photo( - photo=str(choice(START_IMG)), caption=result, parse_mode=ParseMode.HTML + photo=str(choice(START_IMG)), + caption=result, + parse_mode=ParseMode.HTML, + reply_markup=reply_markup, ) @@ -205,7 +223,9 @@ async def stats(update: Update, context: ContextTypes.DEFAULT_TYPE): __help__ = """ *Overall information about user:* -» /info : Fetch information. +» /info : Fetch user information. + +» /uinfo : Fetch user information in banner. """ # <================================================ HANDLER =======================================================> diff --git a/Mikobot/plugins/locks.py b/Mikobot/plugins/locks.py new file mode 100644 index 0000000000000000000000000000000000000000..2b1158a72379c0119b97e133e6ed45399b532aa1 --- /dev/null +++ b/Mikobot/plugins/locks.py @@ -0,0 +1,720 @@ +import html + +from alphabet_detector import AlphabetDetector +from telegram import ( + Chat, + ChatMemberAdministrator, + ChatPermissions, + MessageEntity, + Update, +) +from telegram.constants import ParseMode +from telegram.error import BadRequest, TelegramError +from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters +from telegram.helpers import mention_html + +import Database.sql.locks_sql as sql +from Database.sql.approve_sql import is_approved +from Mikobot import DRAGONS, LOGGER, dispatcher, function +from Mikobot.plugins.connection import connected +from Mikobot.plugins.disable import DisableAbleCommandHandler +from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action +from Mikobot.plugins.helper_funcs.chat_status import ( + check_admin, + is_bot_admin, + user_not_admin, +) +from Mikobot.plugins.log_channel import loggable + +ad = AlphabetDetector() + +LOCK_TYPES = { + "audio": filters.AUDIO, + "voice": filters.VOICE, + "document": filters.Document.ALL, + "video": filters.VIDEO, + "contact": filters.CONTACT, + "photo": filters.PHOTO, + "url": filters.Entity(MessageEntity.URL) | filters.CaptionEntity(MessageEntity.URL), + "bots": filters.StatusUpdate.NEW_CHAT_MEMBERS, + "forward": filters.FORWARDED, + "game": filters.GAME, + "location": filters.LOCATION, + "egame": filters.Dice.ALL, + "rtl": "rtl", + "button": "button", + "inline": "inline", + "phone": filters.Entity(MessageEntity.PHONE_NUMBER) + | filters.CaptionEntity(MessageEntity.PHONE_NUMBER), + "command": filters.COMMAND, + "email": filters.Entity(MessageEntity.EMAIL) + | filters.CaptionEntity(MessageEntity.EMAIL), + "anonchannel": "anonchannel", + "forwardchannel": "forwardchannel", + "forwardbot": "forwardbot", + # "invitelink": , + "videonote": filters.VIDEO_NOTE, + "emojicustom": filters.Entity(MessageEntity.CUSTOM_EMOJI) + | filters.CaptionEntity(MessageEntity.CUSTOM_EMOJI), + "stickerpremium": filters.Sticker.PREMIUM, + "stickeranimated": filters.Sticker.ANIMATED, +} + +LOCK_CHAT_RESTRICTION = { + "all": { + "can_send_messages": False, + "can_send_media_messages": False, + "can_send_polls": False, + "can_send_other_messages": False, + "can_add_web_page_previews": False, + "can_change_info": False, + "can_invite_users": False, + "can_pin_messages": False, + "can_manage_topics": False, + }, + "messages": {"can_send_messages": False}, + "media": {"can_send_media_messages": False}, + "sticker": {"can_send_other_messages": False}, + "gif": {"can_send_other_messages": False}, + "poll": {"can_send_polls": False}, + "other": {"can_send_other_messages": False}, + "previews": {"can_add_web_page_previews": False}, + "info": {"can_change_info": False}, + "invite": {"can_invite_users": False}, + "pin": {"can_pin_messages": False}, + "topics": {"can_manage_topics": False}, +} + +UNLOCK_CHAT_RESTRICTION = { + "all": { + "can_send_messages": True, + "can_send_media_messages": True, + "can_send_polls": True, + "can_send_other_messages": True, + "can_add_web_page_previews": True, + "can_invite_users": True, + "can_manage_topics": True, + }, + "messages": {"can_send_messages": True}, + "media": {"can_send_media_messages": True}, + "sticker": {"can_send_other_messages": True}, + "gif": {"can_send_other_messages": True}, + "poll": {"can_send_polls": True}, + "other": {"can_send_other_messages": True}, + "previews": {"can_add_web_page_previews": True}, + "info": {"can_change_info": True}, + "invite": {"can_invite_users": True}, + "pin": {"can_pin_messages": True}, + "topics": {"can_manage_topics": True}, +} + +PERM_GROUP = 1 +REST_GROUP = 2 + + +# NOT ASYNC +async def restr_members( + bot, + chat_id, + members, + messages=False, + media=False, + other=False, + previews=False, +): + for mem in members: + if mem.user in DRAGONS: + pass + elif mem.user == 777000 or mem.user == 1087968824: + pass + try: + await bot.restrict_chat_member( + chat_id, + mem.user, + permissions=ChatPermissions( + can_send_messages=messages, + can_send_media_messages=media, + can_send_other_messages=other, + can_add_web_page_previews=previews, + ), + ) + except TelegramError: + pass + + +# NOT ASYNC +async def unrestr_members( + bot, + chat_id, + members, + messages=True, + media=True, + other=True, + previews=True, +): + for mem in members: + try: + await bot.restrict_chat_member( + chat_id, + mem.user, + permissions=ChatPermissions( + can_send_messages=messages, + can_send_media_messages=media, + can_send_other_messages=other, + can_add_web_page_previews=previews, + ), + ) + except TelegramError: + pass + + +async def locktypes(update: Update, context: ContextTypes.DEFAULT_TYPE): + await update.effective_message.reply_text( + "\n • ".join( + ["Locks available: "] + + sorted(list(LOCK_TYPES) + list(LOCK_CHAT_RESTRICTION)), + ), + ) + + +@check_admin(permission="can_delete_messages", is_both=True) +@loggable +@typing_action +async def lock(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: + args = context.args + chat = update.effective_chat + user = update.effective_user + + if len(args) >= 1: + ltype = args[0].lower() + if ltype in LOCK_TYPES: + # Connection check + conn = await connected(context.bot, update, chat, user.id, need_admin=True) + if conn: + chat = await dispatcher.bot.getChat(conn) + chat_id = conn + chat_name = chat.title + text = "Locked {} for non-admins in {}!".format(ltype, chat_name) + else: + if update.effective_message.chat.type == "private": + await send_message( + update.effective_message, + "This command is meant to use in group not in PM", + ) + return "" + chat = update.effective_chat + chat_id = update.effective_chat.id + chat_name = update.effective_message.chat.title + text = "Locked {} for non-admins!".format(ltype) + sql.update_lock(chat.id, ltype, locked=True) + await send_message(update.effective_message, text, parse_mode="markdown") + + return ( + "{}:" + "\n#LOCK" + "\nAdmin: {}" + "\nLocked {}.".format( + html.escape(chat.title), + mention_html(user.id, user.first_name), + ltype, + ) + ) + + elif ltype in LOCK_CHAT_RESTRICTION: + # Connection check + conn = await connected(context.bot, update, chat, user.id, need_admin=True) + if conn: + chat = await dispatcher.bot.getChat(conn) + chat_id = conn + chat_name = chat.title + text = "Locked {} for all non-admins in {}!".format( + ltype, + chat_name, + ) + else: + if update.effective_message.chat.type == "private": + await send_message( + update.effective_message, + "This command is meant to use in group not in PM", + ) + return "" + chat = update.effective_chat + chat_id = update.effective_chat.id + chat_name = update.effective_message.chat.title + text = "Locked {} for all non-admins!".format(ltype) + + chat_obj = await context.bot.getChat(chat_id) + current_permission = chat_obj.permissions + await context.bot.set_chat_permissions( + chat_id=chat_id, + permissions=get_permission_list( + current_permission.to_dict(), + LOCK_CHAT_RESTRICTION[ltype.lower()], + ), + ) + + await context.bot.restrict_chat_member( + chat.id, + int(777000), + permissions=ChatPermissions( + can_send_messages=True, + can_send_media_messages=True, + can_send_other_messages=True, + can_add_web_page_previews=True, + ), + ) + + await context.bot.restrict_chat_member( + chat.id, + int(1087968824), + permissions=ChatPermissions( + can_send_messages=True, + can_send_media_messages=True, + can_send_other_messages=True, + can_add_web_page_previews=True, + ), + ) + + await send_message(update.effective_message, text, parse_mode="markdown") + return ( + "{}:" + "\n#Permission_LOCK" + "\nAdmin: {}" + "\nLocked {}.".format( + html.escape(chat.title), + mention_html(user.id, user.first_name), + ltype, + ) + ) + + else: + await send_message( + update.effective_message, + "What are you trying to lock...? Try /locktypes for the list of lockables", + ) + else: + await send_message(update.effective_message, "What are you trying to lock...?") + + return "" + + +@check_admin(permission="can_delete_messages", is_both=True) +@loggable +@typing_action +async def unlock(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: + args = context.args + chat = update.effective_chat + user = update.effective_user + message = update.effective_message + + if len(args) >= 1: + ltype = args[0].lower() + if ltype in LOCK_TYPES: + # Connection check + conn = await connected(context.bot, update, chat, user.id, need_admin=True) + if conn: + chat = await dispatcher.bot.getChat(conn) + chat_id = conn + chat_name = chat.title + text = "Unlocked {} for everyone in {}!".format(ltype, chat_name) + else: + if update.effective_message.chat.type == "private": + await send_message( + update.effective_message, + "This command is meant to use in group not in PM", + ) + return "" + chat = update.effective_chat + chat_id = update.effective_chat.id + chat_name = update.effective_message.chat.title + text = "Unlocked {} for everyone!".format(ltype) + sql.update_lock(chat.id, ltype, locked=False) + await send_message(update.effective_message, text, parse_mode="markdown") + return ( + "{}:" + "\n#UNLOCK" + "\nAdmin: {}" + "\nUnlocked {}.".format( + html.escape(chat.title), + mention_html(user.id, user.first_name), + ltype, + ) + ) + + elif ltype in UNLOCK_CHAT_RESTRICTION: + # Connection check + conn = await connected(context.bot, update, chat, user.id, need_admin=True) + if conn: + chat = await dispatcher.bot.getChat(conn) + chat_id = conn + chat_name = chat.title + text = "Unlocked {} for everyone in {}!".format(ltype, chat_name) + else: + if update.effective_message.chat.type == "private": + await send_message( + update.effective_message, + "This command is meant to use in group not in PM", + ) + return "" + chat = update.effective_chat + chat_id = update.effective_chat.id + chat_name = update.effective_message.chat.title + text = "Unlocked {} for everyone!".format(ltype) + + member = await chat.get_member(context.bot.id) + + if isinstance(member, ChatMemberAdministrator): + can_change_info = member.can_change_info + else: + can_change_info = True + + if not can_change_info: + await send_message( + update.effective_message, + "I don't have permission to change group info.", + parse_mode="markdown", + ) + return + + chat_obj = await context.bot.getChat(chat_id) + current_permission = chat_obj.permissions + await context.bot.set_chat_permissions( + chat_id=chat_id, + permissions=get_permission_list( + current_permission.to_dict(), + UNLOCK_CHAT_RESTRICTION[ltype.lower()], + ), + ) + + await send_message(update.effective_message, text, parse_mode="markdown") + + return ( + "{}:" + "\n#UNLOCK" + "\nAdmin: {}" + "\nUnlocked {}.".format( + html.escape(chat.title), + mention_html(user.id, user.first_name), + ltype, + ) + ) + else: + await send_message( + update.effective_message, + "What are you trying to unlock...? Try /locktypes for the list of lockables.", + ) + + else: + await send_message( + update.effective_message, "What are you trying to unlock...?" + ) + + +@user_not_admin +@check_admin(permission="can_delete_messages", is_bot=True, no_reply=True) +async def del_lockables(update: Update, context: ContextTypes.DEFAULT_TYPE): + chat = update.effective_chat # type: Optional[Chat] + message = update.effective_message # type: Optional[Message] + user = update.effective_user + if is_approved(chat.id, user.id): + return + for lockable, filter in LOCK_TYPES.items(): + if lockable == "rtl": + if sql.is_locked(chat.id, lockable): + if message.caption: + check = ad.detect_alphabet("{}".format(message.caption)) + if "ARABIC" in check: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - rtl:caption") + break + if message.text: + check = ad.detect_alphabet("{}".format(message.text)) + if "ARABIC" in check: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - rtl:text") + break + continue + if lockable == "button": + if sql.is_locked(chat.id, lockable): + if message.reply_markup and message.reply_markup.inline_keyboard: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - button") + break + continue + if lockable == "inline": + if sql.is_locked(chat.id, lockable): + if message and message.via_bot: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - inline") + break + continue + if lockable == "forwardchannel": + if sql.is_locked(chat.id, lockable): + if message.forward_from_chat: + if message.forward_from_chat.type == "channel": + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - forwardchannel") + break + continue + continue + if lockable == "forwardbot": + if sql.is_locked(chat.id, lockable): + if message.forward_from: + if message.forward_from.is_bot: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - forwardchannel") + break + continue + continue + if lockable == "anonchannel": + if sql.is_locked(chat.id, lockable): + if message.from_user: + if message.from_user.id == 136817688: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables - anonchannel") + break + continue + continue + if filter.check_update(update) and sql.is_locked(chat.id, lockable): + if lockable == "bots": + new_members = update.effective_message.new_chat_members + for new_mem in new_members: + if new_mem.is_bot: + if not await is_bot_admin(chat, context.bot.id): + await send_message( + update.effective_message, + "I see a bot and I've been told to stop them from joining..." + "but I'm not admin!", + ) + return + + await chat.ban_member(new_mem.id) + await send_message( + update.effective_message, + "Only admins are allowed to add bots in this chat! Get outta here.", + ) + break + else: + try: + await message.delete() + except BadRequest as excp: + if excp.message == "Message to delete not found": + pass + else: + LOGGER.exception("ERROR in lockables") + + break + + +async def build_lock_message(chat_id): + locks = sql.get_locks(chat_id) + res = "" + locklist = [] + permslist = [] + if locks: + res += "*" + "These are the current locks in this Chat:" + "*" + if locks: + locklist.append("sticker = `{}`".format(locks.sticker)) + locklist.append("audio = `{}`".format(locks.audio)) + locklist.append("voice = `{}`".format(locks.voice)) + locklist.append("document = `{}`".format(locks.document)) + locklist.append("video = `{}`".format(locks.video)) + locklist.append("contact = `{}`".format(locks.contact)) + locklist.append("photo = `{}`".format(locks.photo)) + locklist.append("gif = `{}`".format(locks.gif)) + locklist.append("url = `{}`".format(locks.url)) + locklist.append("bots = `{}`".format(locks.bots)) + locklist.append("forward = `{}`".format(locks.forward)) + locklist.append("game = `{}`".format(locks.game)) + locklist.append("location = `{}`".format(locks.location)) + locklist.append("rtl = `{}`".format(locks.rtl)) + locklist.append("button = `{}`".format(locks.button)) + locklist.append("egame = `{}`".format(locks.egame)) + locklist.append("phone = `{}`".format(locks.phone)) + locklist.append("command = `{}`".format(locks.command)) + locklist.append("email = `{}`".format(locks.email)) + locklist.append("anonchannel = `{}`".format(locks.anonchannel)) + locklist.append("forwardchannel = `{}`".format(locks.forwardchannel)) + locklist.append("forwardbot = `{}`".format(locks.forwardbot)) + locklist.append("videonote = `{}`".format(locks.videonote)) + locklist.append("emojicustom = `{}`".format(locks.emojicustom)) + locklist.append("stickerpremium = `{}`".format(locks.stickerpremium)) + locklist.append("stickeranimated = `{}`".format(locks.stickeranimated)) + + permissions = await dispatcher.bot.get_chat(chat_id) + if isinstance(permissions, Chat): + permissions = permissions.permissions + permslist.append("messages = `{}`".format(permissions.can_send_messages)) + permslist.append("media = `{}`".format(permissions.can_send_media_messages)) + permslist.append("poll = `{}`".format(permissions.can_send_polls)) + permslist.append("other = `{}`".format(permissions.can_send_other_messages)) + permslist.append( + "previews = `{}`".format(permissions.can_add_web_page_previews) + ) + permslist.append("info = `{}`".format(permissions.can_change_info)) + permslist.append("invite = `{}`".format(permissions.can_invite_users)) + permslist.append("pin = `{}`".format(permissions.can_pin_messages)) + permslist.append("topics = `{}`".format(permissions.can_manage_topics)) + + if locklist: + # Ordering lock list + locklist.sort() + # Building lock list string + for x in locklist: + res += "\n • {}".format(x) + res += "\n\n*" + "These are the current chat permissions:" + "*" + for x in permslist: + res += "\n • {}".format(x) + return res + + +@typing_action +@check_admin(is_user=True) +async def list_locks(update: Update, context: ContextTypes.DEFAULT_TYPE): + chat = update.effective_chat # type: Optional[Chat] + user = update.effective_user + + # Connection check + conn = await connected(context.bot, update, chat, user.id, need_admin=True) + if conn: + chat = await dispatcher.bot.getChat(conn) + chat_name = chat.title + else: + if update.effective_message.chat.type == "private": + await send_message( + update.effective_message, + "This command is meant to use in group not in PM", + ) + return "" + chat = update.effective_chat + chat_name = update.effective_message.chat.title + + res = await build_lock_message(chat.id) + if conn: + res = res.replace("Locks in", "*{}*".format(chat_name)) + + await send_message(update.effective_message, res, parse_mode=ParseMode.MARKDOWN) + + +def get_permission_list(current, new): + permissions = { + "can_send_messages": None, + "can_send_media_messages": None, + "can_send_polls": None, + "can_send_other_messages": None, + "can_add_web_page_previews": None, + "can_change_info": None, + "can_invite_users": None, + "can_pin_messages": None, + "can_manage_topics": None, + } + permissions.update(current) + permissions.update(new) + new_permissions = ChatPermissions(**permissions) + return new_permissions + + +async def __import_data__(chat_id, data, message): + # set chat locks + locks = data.get("locks", {}) + for itemlock in locks: + if itemlock in LOCK_TYPES: + sql.update_lock(chat_id, itemlock, locked=True) + elif itemlock in LOCK_CHAT_RESTRICTION: + sql.update_restriction(chat_id, itemlock, locked=True) + else: + pass + + +def __migrate__(old_chat_id, new_chat_id): + sql.migrate_chat(old_chat_id, new_chat_id) + + +async def __chat_settings__(chat_id, user_id): + return await build_lock_message(chat_id) + + +__help__ = """ +➠ Do stickers annoy you? or want to avoid people sharing links? or pictures? \ +You're in the right place! +The locks module allows you to lock away some common items in the \ +telegram world; our bot will automatically delete them! + +» /locktypes: Lists all possible locktypes + +➠ *Admins only:* +» /lock : Lock items of a certain type (not available in private) +» /unlock : Unlock items of a certain type (not available in private) +» /locks: The current list of locks in this chat. + +➠ Locks can be used to restrict a group's users. +eg: +Locking urls will auto-delete all messages with urls, locking stickers will restrict all \ +non-admin users from sending stickers, etc. +Locking bots will stop non-admins from adding bots to the chat. +Locking anonchannel will stop anonymous channel from messaging in your group. + +➠ *Note:* + +» Unlocking permission *info* will allow members (non-admins) to change the group information, such as the description or the group name + +» Unlocking permission *pin* will allow members (non-admins) to pin a message in a group +""" + +__mod_name__ = "LOCKS" + +LOCKTYPES_HANDLER = DisableAbleCommandHandler("locktypes", locktypes, block=False) +LOCK_HANDLER = CommandHandler( + "lock", lock, block=False +) # , filters=filters.ChatType.GROUPS) +UNLOCK_HANDLER = CommandHandler( + "unlock", unlock, block=False +) # , filters=filters.ChatType.GROUPS) +LOCKED_HANDLER = CommandHandler( + "locks", list_locks, block=False +) # , filters=filters.ChatType.GROUPS) + +function(LOCK_HANDLER) +function(UNLOCK_HANDLER) +function(LOCKTYPES_HANDLER) +function(LOCKED_HANDLER) + +function( + MessageHandler(filters.ALL & filters.ChatType.GROUPS, del_lockables, block=False), + PERM_GROUP, +) diff --git a/Mikobot/plugins/mute.py b/Mikobot/plugins/mute.py index 1166aef79cd22b4d1e4a32cd71c5ed928e08c0cc..ce97421fecf82655085444f78431e5bfdb07c43c 100644 --- a/Mikobot/plugins/mute.py +++ b/Mikobot/plugins/mute.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html from typing import Union @@ -18,10 +17,7 @@ from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_a from Mikobot.plugins.helper_funcs.string_handling import extract_time from Mikobot.plugins.log_channel import loggable -# <=======================================================================================================> - -# <================================================ FUNCTION =======================================================> async def check_user(user_id: int, bot: Bot, chat: Chat) -> Union[str, None]: if not user_id: reply = "You don't seem to be referring to a user or the ID specified is incorrect.." @@ -234,9 +230,6 @@ async def temp_mute(update: Update, context: ContextTypes.DEFAULT_TYPE) -> str: return "" -# <=================================================== HELP ====================================================> - - __help__ = """ ➠ *Admins only:* @@ -247,7 +240,6 @@ __help__ = """ » /unmute : unmutes a user. Can also be used as a reply, muting the replied to user. """ -# <================================================ HANDLER =======================================================> MUTE_HANDLER = CommandHandler("mute", mute, block=False) UNMUTE_HANDLER = CommandHandler("unmute", unmute, block=False) TEMPMUTE_HANDLER = CommandHandler(["tmute", "tempmute"], temp_mute, block=False) @@ -258,4 +250,3 @@ function(TEMPMUTE_HANDLER) __mod_name__ = "MUTE" __handlers__ = [MUTE_HANDLER, UNMUTE_HANDLER, TEMPMUTE_HANDLER] -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/newuserinfo.py b/Mikobot/plugins/newuserinfo.py new file mode 100644 index 0000000000000000000000000000000000000000..a5de55a019a2e7ae87c3681736a6694af4b2fb8b --- /dev/null +++ b/Mikobot/plugins/newuserinfo.py @@ -0,0 +1,161 @@ +import os + +import unidecode +from PIL import Image, ImageChops, ImageDraw, ImageFont +from pyrogram import filters +from pyrogram.enums import ParseMode + +from Mikobot import DEMONS, DEV_USERS, DRAGONS, OWNER_ID, TIGERS, WOLVES, app + + +async def circle(pfp, size=(900, 900)): + pfp = pfp.resize(size, Image.ANTIALIAS).convert("RGBA") + bigsize = (pfp.size[0] * 3, pfp.size[1] * 3) + mask = Image.new("L", bigsize, 0) + draw = ImageDraw.Draw(mask) + draw.ellipse((0, 0) + bigsize, fill=255) + mask = mask.resize(pfp.size, Image.ANTIALIAS) + mask = ImageChops.darker(mask, pfp.split()[-1]) + pfp.putalpha(mask) + return pfp + + +async def download_and_process_pfp(user): + try: + pic = await app.download_media( + user.photo.big_file_id, file_name=f"pp{user.id}.png" + ) + if pic: + pfp = Image.open(pic).convert("RGBA") + return await circle(pfp, size=(900, 900)) + except Exception as e: + print(e) + finally: + if "pic" in locals() and pic: + os.remove(pic) + return None + + +async def userinfopic( + user, + user_x, + user_y, + user_id_x, + user_id_y, + pfp_x_offset=0, + pfp_y_offset=0, + pfp_size=(1218, 1385), +): + user_name = unidecode.unidecode(user.first_name) + + # Load the background image + background = Image.open("Extra/user.jpg") + background = background.resize( + (background.size[0], background.size[1]), Image.ANTIALIAS + ) + + draw = ImageDraw.Draw(background) + font = ImageFont.truetype("Extra/default.ttf", 100) + + try: + pfp = await download_and_process_pfp(user) + if pfp: + # Adjust pfp_x and pfp_y with the offsets + pfp_x = 927 + pfp_x_offset + pfp_y = (background.size[1] - pfp.size[1]) // 2 - 290 + pfp_y_offset + + # Increase the size of the pfp circle + pfp = await circle(pfp, size=pfp_size) + background.paste(pfp, (pfp_x, pfp_y), pfp) + + user_text_width, user_text_height = draw.textsize(user_name, font=font) + user_id_text_width, user_id_text_height = draw.textsize(str(user.id), font=font) + + draw.text((user_x, user_y), user_name, font=font, fill="white") + draw.text((user_id_x, user_id_y), str(user.id), font=font, fill="white") + + userinfo = f"downloads/userinfo_{user.id}.png" + background.save(userinfo) + + except Exception as e: + print(e) + userinfo = None + + return userinfo + + +# Command handler for /userinfo +@app.on_message(filters.command("uinfo")) +async def userinfo_command(client, message): + user = message.from_user + user_x, user_y = 1035, 2885 + user_id_x, user_id_y = 1035, 2755 + + try: + # Send a message indicating that user information is being processed + processing_message = await message.reply("Processing user information...") + + # Generate user info image + image_path = await userinfopic(user, user_x, user_y, user_id_x, user_id_y) + + # Delete the processing message + await processing_message.delete() + + if image_path: + # Initialize the caption with basic information + caption = ( + f"「 **According to the Mikos analogy, the userinfo is...** : 」\n\n" + f"❐ 𝗜𝗗: {user.id}\n" + f"❐ 𝗙𝗶𝗿𝘀𝘁 𝗡𝗮𝗺𝗲: {user.first_name}\n" + f"❐ 𝗟𝗮𝘀𝘁 𝗡𝗮𝗺𝗲: {user.last_name}\n" + f"❐ 𝗨𝘀𝗲𝗿𝗻𝗮𝗺𝗲: {user.username}\n" + f"❐ 𝗨𝘀𝗲𝗿𝗹𝗶𝗻𝗸: [link](https://t.me/{user.username})\n" + ) + + # Check if the user's ID matches one of the predefined ranks + if user.id == OWNER_ID: + caption += "\n\n〄 The disaster level of this user is **Owner**.\n" + elif user.id in DEV_USERS: + caption += "\n\n〄 This user is a member of **Developer**.\n" + elif user.id in DRAGONS: + caption += "\n\n〄 The disaster level of this user is **Sudo**.\n" + elif user.id in DEMONS: + caption += "\n\n〄 The disaster level of this user is **Demon**.\n" + elif user.id in TIGERS: + caption += "\n\n〄 The disaster level of this user is **Tiger**.\n" + elif user.id in WOLVES: + caption += "\n\n〄 The disaster level of this user is **Wolf**.\n" + + # Add the RANK line only if the user's ID matches one of the predefined ranks + if ( + user.id == OWNER_ID + or user.id in DEV_USERS + or user.id in DRAGONS + or user.id in DEMONS + or user.id in TIGERS + or user.id in WOLVES + ): + caption += "\n\n〄 𝗥𝗮𝗻𝗸: " + + if user.id == OWNER_ID: + caption += "**CREATOR**" + elif user.id in DEV_USERS: + caption += "**DEVELOPER**" + elif user.id in DRAGONS: + caption += "**DRAGON**" + elif user.id in DEMONS: + caption += "**DEMON**" + elif user.id in TIGERS: + caption += "**TIGER**" + elif user.id in WOLVES: + caption += "**WOLF**" + + caption += "\n" + + await message.reply_photo( + photo=image_path, caption=caption, parse_mode=ParseMode.MARKDOWN + ) + os.remove(image_path) + + except Exception as e: + print(e) diff --git a/Mikobot/plugins/notes.py b/Mikobot/plugins/notes.py index af0cb31bd66590c6b02b3d8ac9259b801e280ebc..c6b0edfb130d56ea78f6cec1fc193794646d08d3 100644 --- a/Mikobot/plugins/notes.py +++ b/Mikobot/plugins/notes.py @@ -147,25 +147,34 @@ async def get( ), fullname=escape_markdown( " ".join( - [message.from_user.first_name, message.from_user.last_name] - if message.from_user.last_name - else [message.from_user.first_name], + ( + [ + message.from_user.first_name, + message.from_user.last_name, + ] + if message.from_user.last_name + else [message.from_user.first_name] + ), ), ), - username="@" + message.from_user.username - if message.from_user.username - else mention_markdown( - message.from_user.id, - message.from_user.first_name, + username=( + "@" + message.from_user.username + if message.from_user.username + else mention_markdown( + message.from_user.id, + message.from_user.first_name, + ) ), mention=mention_markdown( message.from_user.id, message.from_user.first_name, ), chatname=escape_markdown( - message.chat.title - if message.chat.type != "private" - else message.from_user.first_name, + ( + message.chat.title + if message.chat.type != "private" + else message.from_user.first_name + ), ), id=message.from_user.id, ) @@ -192,9 +201,9 @@ async def get( parse_mode=parseMode, disable_web_page_preview=True, reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) else: await ENUM_FUNC_MAP[note.msgtype]( @@ -205,9 +214,9 @@ async def get( parse_mode=parseMode, disable_web_page_preview=True, reply_markup=keyboard, - message_thread_id=message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if chat.is_forum else None + ), ) except BadRequest as excp: @@ -560,9 +569,9 @@ async def __import_data__(chat_id, data, message: Message): caption="These files/photos failed to import due to originating " "from another bot. This is a telegram API restriction, and can't " "be avoided. Sorry for the inconvenience!", - message_thread_id=message.message_thread_id - if message.chat.is_forum - else None, + message_thread_id=( + message.message_thread_id if message.chat.is_forum else None + ), ) diff --git a/Mikobot/plugins/palmchat.py b/Mikobot/plugins/palmchat.py new file mode 100644 index 0000000000000000000000000000000000000000..185c5201e012b10ed27077cf9db6520405d785e7 --- /dev/null +++ b/Mikobot/plugins/palmchat.py @@ -0,0 +1,59 @@ +from pyrogram import filters + +from Mikobot import app +from Mikobot.state import state + +# Configuration - The PALM API URL +PALM_API_URL = "https://lexica.qewertyy.dev/models" +MODEL_ID = 1 # Modify this if you have a specific model ID to use + + +# Function to call the PALM API and get the response +async def get_palm_response(api_params): + try: + response = await state.post(PALM_API_URL, params=api_params) + if response.status_code == 200: + data = response.json() + return data.get( + "content", "Error: Empty response received from the PALM API." + ) + else: + return f"Error: Request failed with status code {response.status_code}." + except fetch.RequestError as e: + return f"Error: An error occurred while calling the PALM API. {e}" + + +# Command handler for /palm +@app.on_message(filters.text) +async def palm_chatbot(client, message): + if not message.text.startswith("Miko"): + return + # your code here + args = message.text.split(maxsplit=1) + if len(args) < 2: + await message.reply("Give me a query to search.") + return + + input_text = args[1] + + # Send the "giving results" message first + result_msg = await message.reply("🔍") + + # Call the PALM API to get the chatbot response asynchronously + api_params = {"model_id": MODEL_ID, "prompt": input_text} + api_response = await get_palm_response(api_params) + + # Delete the "giving results" message + await result_msg.delete() + + # Send the chatbot response to the user + await message.reply(api_response) + + +__help__ = """ +➦ *Write Miko with any sentence it will work as chatbot.* + +*Example*: Miko are you a bot? +""" + +__mod_name__ = "CHATBOT" diff --git a/Mikobot/plugins/pkang.py b/Mikobot/plugins/pkang.py new file mode 100644 index 0000000000000000000000000000000000000000..434d35c7da826223dcee4c4be5c8fba4289b0d50 --- /dev/null +++ b/Mikobot/plugins/pkang.py @@ -0,0 +1,86 @@ +# <============================================== IMPORTS =========================================================> +from uuid import uuid4 + +import pyrogram +from pyrogram import filters +from pyrogram.types import InlineKeyboardButton, InlineKeyboardMarkup + +from Mikobot import app + +# <=======================================================================================================> + + +# <================================================ FUNCTION =======================================================> +@app.on_message(filters.command("pkang")) +async def _packkang(app, message): + """ + @MaybeSuraj on telegram. who helped me in making this module. + """ + txt = await message.reply_text("Processing....") + if not message.reply_to_message: + await txt.edit("Reply to message") + return + if not message.reply_to_message.sticker: + await txt.edit("Reply to sticker") + return + if ( + message.reply_to_message.sticker.is_animated + or message.reply_to_message.sticker.is_video + ): + return await txt.edit("Reply to a non-animated sticker") + if len(message.command) < 2: + pack_name = f"{message.from_user.first_name}_sticker_pack_by_@app_Robot" + else: + pack_name = message.text.split(maxsplit=1)[1] + short_name = message.reply_to_message.sticker.set_name + stickers = await app.invoke( + pyrogram.raw.functions.messages.GetStickerSet( + stickerset=pyrogram.raw.types.InputStickerSetShortName( + short_name=short_name + ), + hash=0, + ) + ) + shits = stickers.documents + sticks = [] + + for i in shits: + sex = pyrogram.raw.types.InputDocument( + id=i.id, access_hash=i.access_hash, file_reference=i.thumbs[0].bytes + ) + + sticks.append( + pyrogram.raw.types.InputStickerSetItem( + document=sex, emoji=i.attributes[1].alt + ) + ) + + try: + short_name = f'stikcer_pack_{str(uuid4()).replace("-","")}_by_{app.me.username}' + user_id = await app.resolve_peer(message.from_user.id) + await app.invoke( + pyrogram.raw.functions.stickers.CreateStickerSet( + user_id=user_id, + title=pack_name, + short_name=short_name, + stickers=sticks, + ) + ) + await txt.edit( + f"""Your sticker has been added! For fast update remove your pack & add again\n +🎖 𝗧𝗢𝗧𝗔𝗟 𝗦𝗧𝗜𝗖𝗞𝗘𝗥: {len(sticks)}""", + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + "PACK", url=f"http://t.me/addstickers/{short_name}" + ) + ] + ] + ), + ) + except Exception as e: + await message.reply(str(e)) + + +# <================================================ END =======================================================> diff --git a/Mikobot/plugins/pokedex.py b/Mikobot/plugins/pokedex.py index 49d856f13de0695eee14546b79992bb4673d1c43..396a9897be1a42fb32bcf8531c5a8f5cb79a690d 100644 --- a/Mikobot/plugins/pokedex.py +++ b/Mikobot/plugins/pokedex.py @@ -5,7 +5,7 @@ # <============================================== IMPORTS =========================================================> from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update -from telegram.ext import ContextTypes, CallbackQueryHandler, CommandHandler +from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes from Mikobot import function from Mikobot.state import state diff --git a/Mikobot/plugins/reverse.py b/Mikobot/plugins/reverse.py index c6635a76bb7e9fbb3a78257bbdf017df74261ade..2deb2c95139acfe6131342e8755c5c334a89a242 100644 --- a/Mikobot/plugins/reverse.py +++ b/Mikobot/plugins/reverse.py @@ -1,117 +1,117 @@ -# CREATED BY: @Qewertyy - -# <============================================== IMPORTS =========================================================> import os -import traceback - -from pyrogram import Client, filters -from pyrogram import types as t - -from Mikobot import app -from Mikobot.state import state - -from .telegraph import telegraph, upload_file - - -# <================================================ FUNCTIONS =====================================================> -@app.on_message(filters.command(["p", "pp", "reverse", "sauce"])) -async def reverseImageSearch(_: Client, m: t.Message): - try: - reply = await m.reply_text("`Downloading...`") - file = None - if not m.reply_to_message: - return await reply.edit("Reply to an image?") - if m.reply_to_message.document is False or m.reply_to_message.photo is False: - return await reply.edit("Reply to an image?") - if ( - m.reply_to_message.document - and m.reply_to_message.document.mime_type - in ["image/png", "image/jpg", "image/jpeg"] - or m.reply_to_message.photo - ): - if ( - m.reply_to_message.document - and m.reply_to_message.document.file_size > 5242880 - ): - return await reply.edit("Reply to an image?") - file = await m.reply_to_message.download() - else: - return await reply.edit("Reply to an image?") - await reply.edit("`Uploading to the server...`") - imgUrl = upload_file(file) - os.remove(file) - if imgUrl is None: - return await reply.edit("Ran into an error.") - output = await reverse_image_search("google", f"https://graph.org/{imgUrl[0]}") - if output is None: - return await reply.edit("Ran into an error.") - - names = output["content"]["bestResults"]["names"] - urls = output["content"]["bestResults"]["urls"] - btn = t.InlineKeyboardMarkup( - [[t.InlineKeyboardButton(text="IMAGE URL", url=urls[-1])]] - ) - - if len(names) > 10: - message = "\n".join( - [f"{index+1}. {name}" for index, name in enumerate(names[:10])] - ) - htmlMessage = f"
".join( - [f"{index+1}. {name}" for index, name in enumerate(names)] - ) - htmlMessage += "

URLS


" - htmlMessage += f"
".join([f"{url}" for url in urls]) - htmlMessage += ( - "

By LexicaAPI" - ) - telegraph_page = telegraph.create_page( - "More Results", html_content=htmlMessage - ) - message += f"\n\n[More Results](https://telegra.ph/{telegraph_page['path']})\n\nBy @LexicaAPI" - await reply.delete() - return await m.reply_text(message, reply_markup=btn) - - message = "\n".join([f"{index+1}. {name}" for index, name in enumerate(names)]) - await reply.delete() - await m.reply_text(f"{message}\n\nBy @LexicaAPI", reply_markup=btn) - except Exception as E: - traceback.print_exc() - return await m.reply_text("Ran into an error.") - - -async def reverse_image_search(search_engine, img_url) -> dict: - try: - response = await state.post( - f"https://lexica.qewertyy.me/image-reverse/{search_engine}?img_url={img_url}", - ) - if response.status_code != 200: - return None - output = response.json() - if output["code"] != 2: - return None - return output - except Exception as E: - raise Exception(f"API Error: {E}") - - -# <=================================================== HELP ====================================================> - - -__help__ = """ -🖼 *IMAGE REVERSE* - -» `/p`, `/pp`, `/reverse`, `/sauce`: Reverse image search using various search engines. - -➠ *Usage:* -Reply to an image with one of the above commands to perform a reverse image search. - -➠ *Example:* -» `/p` - Perform a reverse image search. - -➠ *Note:* -- Supported image formats: PNG, JPG, JPEG. -- Maximum file size: 5 MB. -""" - -__mod_name__ = "REVERSE" -# <================================================ END =======================================================> +import uuid +from html import escape + +import requests +from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update +from telegram.constants import ParseMode +from telegram.ext import CallbackContext, CommandHandler + +from Mikobot import dispatcher + +ENDPOINT = "https://sasta-api.vercel.app/googleImageSearch" + + +# Define strings +class STRINGS: + REPLY_TO_MEDIA = "ℹ️ Please reply to a message that contains one of the supported media types, such as a photo, sticker, or image file." + UNSUPPORTED_MEDIA_TYPE = "⚠️ Unsupported media type!\nℹ️ Please reply with a supported media type: image, sticker, or image file." + + REQUESTING_API_SERVER = "🫧" + + DOWNLOADING_MEDIA = "🔍" + UPLOADING_TO_API_SERVER = "📤" + PARSING_RESULT = "📥" + + EXCEPTION_OCCURRED = "❌Exception occurred!\n\nException: {}" + + RESULT = """ +Query: {query} +Google Page: Link + """ + OPEN_SEARCH_PAGE = "OPEN LINK" + + +# Define command handlers +async def reverse_image_search(update: Update, context: CallbackContext): + message = update.message + if len(message.text.split()) > 1: + image_url = message.text.split()[1] + params = {"image_url": image_url} + status_msg = await message.reply_text(STRINGS.REQUESTING_API_SERVER) + response = await requests.get(ENDPOINT, params=params) + + elif message.reply_to_message: + reply = message.reply_to_message + if reply.photo or reply.sticker or reply.document: + status_msg = await message.reply_text(STRINGS.DOWNLOADING_MEDIA) + file_path = f"temp/{uuid.uuid4()}" + try: + file_id = ( + reply.photo[-1].file_id + if reply.photo + else ( + reply.sticker.file_id + if reply.sticker + else reply.document.file_id + ) + ) + file = await context.bot.get_file(file_id) + os.makedirs( + os.path.dirname(file_path), exist_ok=True + ) # Ensure directory exists + await file.download_to_drive( + file_path + ) # Use download instead of download_to_drive + except Exception as exc: + text = STRINGS.EXCEPTION_OCCURRED.format(exc) + await message.reply_text(text) + return + + with open(file_path, "rb") as image_file: + files = {"file": image_file} + await status_msg.edit_text(STRINGS.UPLOADING_TO_API_SERVER) + response = requests.post(ENDPOINT, files=files) + + os.remove(file_path) # Remove the file after it's been used + + if response.status_code == 404: + text = STRINGS.EXCEPTION_OCCURRED.format(response.json()["error"]) + await message.reply_text(text) + await status_msg.delete() + return + elif response.status_code != 200: + text = STRINGS.EXCEPTION_OCCURRED.format(response.text) + await message.reply_text(text) + await status_msg.delete() + return + + await status_msg.edit_text(STRINGS.PARSING_RESULT) + response_json = response.json() + query = response_json["query"] + search_url = response_json["search_url"] + + # Escape HTML tags in query to prevent them from being interpreted as markup + escaped_query = escape(query) + + text = STRINGS.RESULT.format( + query=( + f"{escaped_query}" + if escaped_query + else "Name not found" + ), + search_url=search_url, + ) + buttons = [[InlineKeyboardButton(STRINGS.OPEN_SEARCH_PAGE, url=search_url)]] + await message.reply_text( + text, + disable_web_page_preview=True, + reply_markup=InlineKeyboardMarkup(buttons), + parse_mode=ParseMode.HTML, # Specify parse_mode as 'HTML' to interpret HTML tags + ) + await status_msg.delete() + + +dispatcher.add_handler( + CommandHandler(["reverse", "pp", "p", "grs", "sauce"], reverse_image_search) +) diff --git a/Mikobot/plugins/rules.py b/Mikobot/plugins/rules.py index 8dac9d7ef41183241e2b6691fbe4abe501cf6d0b..4738fec3cf8d7b699be22c754b188bcfcbda2763 100644 --- a/Mikobot/plugins/rules.py +++ b/Mikobot/plugins/rules.py @@ -34,9 +34,11 @@ async def send_rules(update, chat_id, from_pm=False): user.id, "The rules shortcut for this chat hasn't been set properly! Ask admins to " "fix this.\nMaybe they forgot the hyphen in ID", - message_thread_id=update.effective_message.message_thread_id - if chat.is_forum - else None, + message_thread_id=( + update.effective_message.message_thread_id + if chat.is_forum + else None + ), ) return else: diff --git a/Mikobot/plugins/sangmata.py b/Mikobot/plugins/sangmata.py new file mode 100644 index 0000000000000000000000000000000000000000..c366eeb793fd4269a046899b96f868c66d3b6b7c --- /dev/null +++ b/Mikobot/plugins/sangmata.py @@ -0,0 +1,123 @@ +from pyrogram import filters +from pyrogram.types import Message + +from Database.mongodb.sangmata_db import ( + add_userdata, + cek_userdata, + get_userdata, + is_sangmata_on, + sangmata_off, + sangmata_on, +) +from Mikobot import app +from Mikobot.utils.can_restrict import can_restrict +from Mikobot.utils.custom_filters import PREFIX_HANDLER +from Mikobot.utils.localization import use_chat_lang + + +# Check user that change first_name, last_name and usernaname +@app.on_message( + filters.group & ~filters.bot & ~filters.via_bot, + group=5, +) +@use_chat_lang() +async def cek_mataa(_, ctx: Message, strings): + if ctx.sender_chat or not await is_sangmata_on(ctx.chat.id): + return + if not await cek_userdata(ctx.from_user.id): + return await add_userdata( + ctx.from_user.id, + ctx.from_user.username, + ctx.from_user.first_name, + ctx.from_user.last_name, + ) + usernamebefore, first_name, lastname_before = await get_userdata(ctx.from_user.id) + msg = "" + if ( + usernamebefore != ctx.from_user.username + or first_name != ctx.from_user.first_name + or lastname_before != ctx.from_user.last_name + ): + msg += f"➼ 𝗠𝗜𝗞𝗢 𝗠𝗔𝗧𝗔\n\n🧑‍💼 User: {ctx.from_user.mention} [{ctx.from_user.id}]\n" + if usernamebefore != ctx.from_user.username: + usernamebefore = f"@{usernamebefore}" if usernamebefore else strings("no_uname") + usernameafter = ( + f"@{ctx.from_user.username}" + if ctx.from_user.username + else strings("no_uname") + ) + msg += strings("uname_change_msg").format(bef=usernamebefore, aft=usernameafter) + await add_userdata( + ctx.from_user.id, + ctx.from_user.username, + ctx.from_user.first_name, + ctx.from_user.last_name, + ) + if first_name != ctx.from_user.first_name: + msg += strings("firstname_change_msg").format( + bef=first_name, aft=ctx.from_user.first_name + ) + await add_userdata( + ctx.from_user.id, + ctx.from_user.username, + ctx.from_user.first_name, + ctx.from_user.last_name, + ) + if lastname_before != ctx.from_user.last_name: + lastname_before = lastname_before or strings("no_last_name") + lastname_after = ctx.from_user.last_name or strings("no_last_name") + msg += strings("lastname_change_msg").format( + bef=lastname_before, aft=lastname_after + ) + await add_userdata( + ctx.from_user.id, + ctx.from_user.username, + ctx.from_user.first_name, + ctx.from_user.last_name, + ) + if msg != "": + await ctx.reply(msg, quote=False) + + +@app.on_message( + filters.group + & filters.command("imposter", PREFIX_HANDLER) + & ~filters.bot + & ~filters.via_bot +) +@can_restrict +@use_chat_lang() +async def set_mataa(_, ctx: Message, strings): + if len(ctx.command) == 1: + return await ctx.reply(strings("set_sangmata_help").format(cmd=ctx.command[0])) + if ctx.command[1] == "on": + cekset = await is_sangmata_on(ctx.chat.id) + if cekset: + await ctx.reply(strings("sangmata_already_on")) + else: + await sangmata_on(ctx.chat.id) + await ctx.reply(strings("sangmata_enabled")) + elif ctx.command[1] == "off": + cekset = await is_sangmata_on(ctx.chat.id) + if not cekset: + await ctx.reply(strings("sangmata_already_off")) + else: + await sangmata_off(ctx.chat.id) + await ctx.reply(strings("sangmata_disabled")) + else: + await ctx.reply(strings("wrong_param")) + + +# <=================================================== HELP ====================================================> +__help__ = """ +🙅‍♂️ **Toji Mata**. + +» /imposter : Detects if some one change his/her name. + +» /imposter on : turns on toji mata. + +» /imposter off : turns off toji mata. +""" + +__mod_name__ = "IMPOSTER" +# <================================================ END =======================================================> diff --git a/Mikobot/plugins/stickers.py b/Mikobot/plugins/stickers.py new file mode 100644 index 0000000000000000000000000000000000000000..e9f995128ec9291755bc133d769d49a0d6bea218 --- /dev/null +++ b/Mikobot/plugins/stickers.py @@ -0,0 +1,645 @@ +import asyncio +import os +import re +import shutil +import tempfile +import textwrap + +from PIL import Image, ImageDraw, ImageFont +from pyrogram import Client, emoji, enums, filters +from pyrogram.errors import BadRequest, PeerIdInvalid, StickersetInvalid +from pyrogram.file_id import FileId +from pyrogram.raw.functions.messages import GetStickerSet, SendMedia +from pyrogram.raw.functions.stickers import ( + AddStickerToSet, + CreateStickerSet, + RemoveStickerFromSet, +) +from pyrogram.raw.types import ( + DocumentAttributeFilename, + InputDocument, + InputMediaUploadedDocument, + InputStickerSetItem, + InputStickerSetShortName, +) +from pyrogram.types import InlineKeyboardButton +from pyrogram.types import InlineKeyboardButton as IKB +from pyrogram.types import InlineKeyboardMarkup +from pyrogram.types import InlineKeyboardMarkup as IKM +from pyrogram.types import Message + +from Mikobot import MESSAGE_DUMP, app +from Mikobot.state import state +from Mikobot.utils.custom_filters import PREFIX_HANDLER +from Mikobot.utils.localization import use_chat_lang + + +def get_emoji_regex(): + e_list = [ + getattr(emoji, e).encode("unicode-escape").decode("ASCII") + for e in dir(emoji) + if not e.startswith("_") + ] + # to avoid re.error excluding char that start with '*' + e_sort = sorted([x for x in e_list if not x.startswith("*")], reverse=True) + # Sort emojis by length to make sure multi-character emojis are + # matched first + pattern_ = f"({'|'.join(e_sort)})" + return re.compile(pattern_) + + +EMOJI_PATTERN = get_emoji_regex() +SUPPORTED_TYPES = ["jpeg", "png", "webp"] + + +@app.on_message(filters.command(["getsticker"], PREFIX_HANDLER), group=111) +@use_chat_lang() +async def getsticker_(self: Client, ctx: Message, strings): + if not ctx.reply_to_message: + return await ctx.reply(strings("not_sticker")) + sticker = ctx.reply_to_message.sticker + if not sticker: + return await ctx.reply("Only support sticker..") + if sticker.is_animated: + return await ctx.reply(strings("no_anim_stick")) + with tempfile.TemporaryDirectory() as tempdir: + path = os.path.join(tempdir, "getsticker") + sticker_file = await self.download_media( + message=ctx.reply_to_message, + file_name=f"{path}/{sticker.set_name}.png", + ) + await ctx.reply_to_message.reply_document( + document=sticker_file, + caption=f"Emoji: {sticker.emoji}\n" + f"Sticker ID: {sticker.file_id}\n\n" + f"Send by: @{self.me.username}", + ) + shutil.rmtree(tempdir, ignore_errors=True) + + +@app.on_message(filters.command("getvidsticker"), group=222) +async def _vidstick(_, message): + chat_id = message.chat.id + replied = message.reply_to_message + if replied and replied.sticker: + if not replied.sticker.is_video: + return await message.reply_text("Use /getsticker if sticker is not video.") + file_id = replied.sticker.file_id + new_file = await _.download_media(file_id, file_name="sticker.mp4") + await _.send_animation(chat_id, new_file) + os.remove(new_file) + else: + await message.reply_text("Please reply to a video sticker to upload it's MP4.") + + +@app.on_message(filters.command("getvideo"), group=333) +async def _vidstick(_, message): + chat_id = message.chat.id + replied = message.reply_to_message + if replied and replied.animation: + file_id = replied.animation.file_id + new_file = await _.download_media(file_id, file_name="video.mp4") + print(new_file) + await _.send_video(chat_id, video=open(new_file, "rb")) + os.remove(new_file) + else: + await message.reply_text("Please reply to a gif for me to get it's video.") + + +@app.on_message(filters.command("stickerid", PREFIX_HANDLER) & filters.reply, group=444) +async def getstickerid(_, ctx: Message): + if ctx.reply_to_message.sticker: + await ctx.reply( + "The ID of this sticker is: {stickerid}".format( + stickerid=ctx.reply_to_message.sticker.file_id + ) + ) + + +@app.on_message(filters.command("unkang", PREFIX_HANDLER) & filters.reply, group=555) +@use_chat_lang() +async def unkangs(self: Client, ctx: Message, strings): + if not ctx.from_user: + return await ctx.reply("You're anon, unkang in my PM") + if sticker := ctx.reply_to_message.sticker: + if str(ctx.from_user.id) not in sticker.set_name: + return await ctx.reply("This sticker is not your pack, don't do it..") + pp = await ctx.reply(strings("unkang_msg")) + try: + decoded = FileId.decode(sticker.file_id) + sticker = InputDocument( + id=decoded.media_id, + access_hash=decoded.access_hash, + file_reference=decoded.file_reference, + ) + await app.invoke(RemoveStickerFromSet(sticker=sticker)) + await pp.edit(strings("unkang_success")) + except Exception as e: + await pp.edit(strings("unkang_error").format(e=e)) + else: + await ctx.reply(strings("unkang_help").format(c=self.me.username)) + + +@app.on_message(filters.command(["kang"], PREFIX_HANDLER), group=666) +@use_chat_lang() +async def kang_sticker(self: Client, ctx: Message, strings): + if not ctx.from_user: + return await ctx.reply(strings("anon_warn")) + prog_msg = await ctx.reply(strings("kang_msg")) + sticker_emoji = "✔️" + packnum = 0 + packname_found = False + resize = False + animated = False + videos = False + convert = False + reply = ctx.reply_to_message + user = await self.resolve_peer(ctx.from_user.username or ctx.from_user.id) + + if reply and reply.media: + if reply.photo: + resize = True + elif reply.animation: + videos = True + convert = True + elif reply.video: + convert = True + videos = True + elif reply.document: + if "image" in reply.document.mime_type: + # mime_type: image/webp + resize = True + elif reply.document.mime_type in ( + enums.MessageMediaType.VIDEO, + enums.MessageMediaType.ANIMATION, + ): + # mime_type: application/video + videos = True + convert = True + elif "tgsticker" in reply.document.mime_type: + # mime_type: application/x-tgsticker + animated = True + elif reply.sticker: + if not reply.sticker.file_name: + return await prog_msg.edit(strings("stick_no_name")) + if reply.sticker.emoji: + sticker_emoji = reply.sticker.emoji + animated = reply.sticker.is_animated + videos = reply.sticker.is_video + if videos: + convert = False + elif not reply.sticker.file_name.endswith(".tgs"): + resize = True + else: + return await prog_msg.edit("I cannot kang this type.") + + pack_prefix = "anim" if animated else "vid" if videos else "a" + packname = f"{pack_prefix}_{ctx.from_user.id}_by_{self.me.username}" + + if ( + len(ctx.command) > 1 + and ctx.command[1].isdigit() + and int(ctx.command[1]) > 0 + ): + # provide pack number to kang in desired pack + packnum = ctx.command.pop(1) + packname = ( + f"{pack_prefix}{packnum}_{ctx.from_user.id}_by_{self.me.username}" + ) + if len(ctx.command) > 1: + # matches all valid emojis in input + sticker_emoji = ( + "".join(set(EMOJI_PATTERN.findall("".join(ctx.command[1:])))) + or sticker_emoji + ) + filename = await self.download_media(ctx.reply_to_message) + if not filename: + # Failed to download + await prog_msg.delete() + return + elif ctx.entities and len(ctx.entities) > 1: + pack_prefix = "a" + filename = "sticker.png" + packname = f"c{ctx.from_user.id}_by_{self.me.username}" + img_url = next( + ( + ctx.text[y.offset : (y.offset + y.length)] + for y in ctx.entities + if y.type == "url" + ), + None, + ) + + if not img_url: + await prog_msg.delete() + return + try: + r = await state.get(img_url) + if r.status_code == 200: + with open(filename, mode="wb") as f: + f.write(r.read()) + except Exception as r_e: + return await prog_msg.edit(f"{r_e.__class__.__name__} : {r_e}") + if len(ctx.command) > 2: + # m.command[1] is image_url + if ctx.command[2].isdigit() and int(ctx.command[2]) > 0: + packnum = ctx.command.pop(2) + packname = f"a{packnum}_{ctx.from_user.id}_by_{self.me.username}" + if len(ctx.command) > 2: + sticker_emoji = ( + "".join(set(EMOJI_PATTERN.findall("".join(ctx.command[2:])))) + or sticker_emoji + ) + resize = True + else: + return await prog_msg.edit(strings("kang_help")) + try: + if resize: + filename = resize_image(filename) + elif convert: + filename = await convert_video(filename) + if filename is False: + return await prog_msg.edit("Error") + max_stickers = 50 if animated else 120 + while not packname_found: + try: + stickerset = await self.invoke( + GetStickerSet( + stickerset=InputStickerSetShortName(short_name=packname), + hash=0, + ) + ) + if stickerset.set.count >= max_stickers: + packnum += 1 + packname = f"{pack_prefix}_{packnum}_{ctx.from_user.id}_by_{self.me.username}" + else: + packname_found = True + except StickersetInvalid: + break + file = await self.save_file(filename) + media = await self.invoke( + SendMedia( + peer=(await self.resolve_peer(MESSAGE_DUMP)), + media=InputMediaUploadedDocument( + file=file, + mime_type=self.guess_mime_type(filename), + attributes=[DocumentAttributeFilename(file_name=filename)], + ), + message=f"#Sticker kang by UserID -> {ctx.from_user.id}", + random_id=self.rnd_id(), + ), + ) + msg_ = media.updates[-1].message + stkr_file = msg_.media.document + if packname_found: + await prog_msg.edit(strings("exist_pack")) + await self.invoke( + AddStickerToSet( + stickerset=InputStickerSetShortName(short_name=packname), + sticker=InputStickerSetItem( + document=InputDocument( + id=stkr_file.id, + access_hash=stkr_file.access_hash, + file_reference=stkr_file.file_reference, + ), + emoji=sticker_emoji, + ), + ) + ) + else: + await prog_msg.edit(strings("new_packs")) + stkr_title = f"{ctx.from_user.first_name}'s" + if animated: + stkr_title += "AnimPack" + elif videos: + stkr_title += "VidPack" + if packnum != 0: + stkr_title += f" v{packnum}" + try: + await self.invoke( + CreateStickerSet( + user_id=user, + title=stkr_title, + short_name=packname, + stickers=[ + InputStickerSetItem( + document=InputDocument( + id=stkr_file.id, + access_hash=stkr_file.access_hash, + file_reference=stkr_file.file_reference, + ), + emoji=sticker_emoji, + ) + ], + animated=animated, + videos=videos, + ) + ) + except PeerIdInvalid: + return await prog_msg.edit( + strings("please_start_msg"), + reply_markup=InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + strings("click_me"), + url=f"https://t.me/{self.me.username}?start", + ) + ] + ] + ), + ) + + except BadRequest: + return await prog_msg.edit(strings("pack_full")) + except Exception as all_e: + await prog_msg.edit(f"{all_e.__class__.__name__} : {all_e}") + else: + markup = InlineKeyboardMarkup( + [ + [ + InlineKeyboardButton( + text=strings("viewpack"), + url=f"https://t.me/addstickers/{packname}", + ) + ] + ] + ) + await prog_msg.edit( + strings("kang_success").format(emot=sticker_emoji), + reply_markup=markup, + ) + # Cleanup + await self.delete_messages( + chat_id=MESSAGE_DUMP, message_ids=msg_.id, revoke=True + ) + try: + os.remove(filename) + except OSError: + pass + + +def resize_image(filename: str) -> str: + im = Image.open(filename) + maxsize = 512 + scale = maxsize / max(im.width, im.height) + sizenew = (int(im.width * scale), int(im.height * scale)) + im = im.resize(sizenew, Image.NEAREST) + downpath, f_name = os.path.split(filename) + # not hardcoding png_image as "sticker.png" + png_image = os.path.join(downpath, f"{f_name.split('.', 1)[0]}.png") + im.save(png_image, "PNG") + if png_image != filename: + os.remove(filename) + return png_image + + +async def convert_video(filename: str) -> str: + downpath, f_name = os.path.split(filename) + webm_video = os.path.join(downpath, f"{f_name.split('.', 1)[0]}.webm") + cmd = [ + "ffmpeg", + "-loglevel", + "quiet", + "-i", + filename, + "-t", + "00:00:03", + "-vf", + "fps=30", + "-c:v", + "vp9", + "-b:v:", + "500k", + "-preset", + "ultrafast", + "-s", + "512x512", + "-y", + "-an", + webm_video, + ] + + proc = await asyncio.create_subprocess_exec(*cmd) + # Wait for the subprocess to finish + await proc.communicate() + + if webm_video != filename: + os.remove(filename) + return webm_video + + +@app.on_message(filters.command("mmf"), group=777) +async def handler(client, message): + if not message.reply_to_message: + await message.reply("Reply to an image or a sticker to memify it!") + return + + reply_message = message.reply_to_message + if not reply_message.media: + await message.reply("Provide some text please.") + return + + file = await client.download_media(reply_message) + msg = await message.reply("Memifying this image! Please wait.") + + text = message.text.split("/mmf ", maxsplit=1)[1].strip() + if len(text) < 1: + return await msg.edit("You might want to try `/mmf text`") + + meme = await draw_text(file, text) + await client.send_document(message.chat.id, document=meme) + await msg.delete() + os.remove(meme) + + +async def draw_text(image_path, text): + img = Image.open(image_path) + os.remove(image_path) + i_width, i_height = img.size + + if os.name == "nt": + fnt = "arial.ttf" + else: + fnt = "./Extra/default.ttf" + m_font = ImageFont.truetype(fnt, int((70 / 640) * i_width)) + + if ";" in text: + upper_text, lower_text = text.split(";") + else: + upper_text = text + lower_text = "" + + draw = ImageDraw.Draw(img) + current_h, pad = 10, 5 + + if upper_text: + for u_text in textwrap.wrap(upper_text, width=15): + u_width, u_height = draw.textsize(u_text, font=m_font) + + draw.text( + xy=(((i_width - u_width) / 2) - 2, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + + draw.text( + xy=(((i_width - u_width) / 2) + 2, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=((i_width - u_width) / 2, int(((current_h / 640) * i_width)) - 2), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + + draw.text( + xy=(((i_width - u_width) / 2), int(((current_h / 640) * i_width)) + 2), + text=u_text, + font=m_font, + fill=(0, 0, 0), + ) + + draw.text( + xy=((i_width - u_width) / 2, int((current_h / 640) * i_width)), + text=u_text, + font=m_font, + fill=(255, 255, 255), + ) + + current_h += u_height + pad + + if lower_text: + for l_text in textwrap.wrap(lower_text, width=15): + u_width, u_height = draw.textsize(l_text, font=m_font) + + draw.text( + xy=( + ((i_width - u_width) / 2) - 2, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + ((i_width - u_width) / 2) + 2, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((20 / 640) * i_width)) - 2, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + draw.text( + xy=( + (i_width - u_width) / 2, + (i_height - u_height - int((20 / 640) * i_width)) + 2, + ), + text=l_text, + font=m_font, + fill=(0, 0, 0), + ) + + draw.text( + xy=( + (i_width - u_width) / 2, + i_height - u_height - int((20 / 640) * i_width), + ), + text=l_text, + font=m_font, + fill=(255, 255, 255), + ) + + current_h += u_height + pad + + image_name = "memify.webp" + webp_file = os.path.join(image_name) + img.save(webp_file, "webp") + return webp_file + + +@app.on_message(filters.command(["stickerinfo", "stinfo"]), group=888) +async def give_st_info(c: app, m: Message): + if not m.reply_to_message: + await m.reply_text("Reply to a sticker") + return + elif not m.reply_to_message.sticker: + await m.reply_text("Reply to a sticker") + return + st_in = m.reply_to_message.sticker + st_type = "Normal" + if st_in.is_animated: + st_type = "Animated" + elif st_in.is_video: + st_type = "Video" + st_to_gib = f"""[Sticker]({m.reply_to_message.link}) info: +➼ 𝗙𝗜𝗟𝗘 𝗜𝗗 : `{st_in.file_id}` +➼ 𝗙𝗜𝗟𝗘 𝗡𝗔𝗠𝗘 : {st_in.file_name} +➼ 𝗙𝗜𝗟𝗘 𝗨𝗡𝗜𝗤𝗨𝗘 𝗜𝗗 : `{st_in.file_unique_id}` +➼ 𝗗𝗔𝗧𝗘 𝗔𝗡𝗗 𝗧𝗜𝗠𝗘 𝗢𝗙 𝗦𝗧𝗜𝗖𝗞𝗘𝗥 𝗖𝗥𝗘𝗔𝗧𝗘𝗗 : `{st_in.date}` +➼ 𝗦𝗧𝗜𝗖𝗞𝗘𝗥 𝗧𝗬𝗣𝗘 : `{st_type}` +➼ 𝗘𝗠𝗢𝗝𝗜 : {st_in.emoji} +➼ 𝗣𝗔𝗖𝗞 𝗡𝗔𝗠𝗘 : {st_in.set_name} +""" + kb = IKM( + [ + [ + IKB( + "➕ 𝗔𝗱𝗱 𝘀𝘁𝗶𝗰𝗸𝗲𝗿 𝗽𝗮𝗰𝗸", + url=f"https://t.me/addstickers/{st_in.set_name}", + ) + ] + ] + ) + await m.reply_text(st_to_gib, reply_markup=kb) + return + + +# <=================================================== HELP ====================================================> +__help__ = """ +➠ *👨‍🏫 User Commands:* + +» /kang (/steal) < emoji >: Reply to a sticker or any supported media. + +» /pkang: Reply to a image type sticker to get full pack. + +» /stickerinfo (/stinfo) : Reply to any sticker to get it's info. + +» /stickerid Reply to a sticker message to get the sticker ID and emoji. + +» /stickers < query >: Search for sticker packs based on the given query. + +» /getsticker: Reply to a sticker message to get the sticker as a document. + +» /getvidsticker: Reply to a video sticker message to get the video as an animation. + +» /getvideo: Reply to a GIF message to get the GIF as a video. + +» /unkang: Reply to a sticker created by the bot to remove it from your pack. + +» /mmf < your text >: Reply to a normal sticker or a photo or video file to memify it. If you want to right text at bottom use `;right your message` + ■ For e.g. + ○ /mmf Hello freinds : this will add text to the top + ○ /mmf Hello ; freinds : this will add Hello to the top and freinds at the bottom + ○ /mmf ; Hello friends : this will add text at the bottom + +➠ *Note* +➠ *mmf and getsticker only support photo and normal stickers for now*. +""" + +__mod_name__ = "STICKERS" +# <================================================ END =======================================================> diff --git a/Mikobot/plugins/unbanall.py b/Mikobot/plugins/unbanall.py new file mode 100644 index 0000000000000000000000000000000000000000..190aa81965c84367ae1e4db7bdc4f40aa43fd5dd --- /dev/null +++ b/Mikobot/plugins/unbanall.py @@ -0,0 +1,179 @@ +import os +from time import sleep + +from telethon import * +from telethon.errors import * +from telethon.errors import FloodWaitError, UserNotParticipantError +from telethon.tl import * +from telethon.tl import functions, types +from telethon.tl.functions.channels import GetParticipantRequest +from telethon.tl.types import * +from telethon.tl.types import ( + ChannelParticipantAdmin, + ChannelParticipantCreator, + ChatBannedRights, +) + +from Mikobot import LOGGER, tbot +from Mikobot.events import register + +sudo = 5978107653 +CMD_HELP = "/ !" + + +# ================================================ + + +async def is_register_admin(chat, user): + if isinstance(chat, (types.InputPeerChannel, types.InputChannel)): + return isinstance( + ( + await tbot(functions.channels.GetParticipantRequest(chat, user)) + ).participant, + (types.ChannelParticipantAdmin, types.ChannelParticipantCreator), + ) + if isinstance(chat, types.InputPeerUser): + return True + + +@register(pattern="^/unbanall$") +async def _(event): + chat = await event.get_chat() + admin = chat.admin_rights.ban_users + creator = chat.creator + if event.is_private: + return await event.respond( + "__This command can be use in groups and channels!__" + ) + + is_admin = False + try: + cutiepii = await tbot(GetParticipantRequest(event.chat_id, event.sender_id)) + except UserNotParticipantError: + is_admin = False + else: + if isinstance( + cutiepii.participant, + ( + ChannelParticipantAdmin, + ChannelParticipantCreator, + ), + ): + is_admin = True + if not is_admin: + return await event.respond("__Only admins can Unmuteall!__") + + if not admin and not creator: + await event.reply("`I don't have enough permissions!`") + return + + done = await event.reply("Searching Participant Lists.") + p = 0 + async for i in tbot.iter_participants( + event.chat_id, filter=ChannelParticipantsKicked, aggressive=True + ): + rights = ChatBannedRights(until_date=0, view_messages=False) + try: + await tbot(functions.channels.EditBannedRequest(event.chat_id, i, rights)) + except FloodWaitError as ex: + LOGGER.warn(f"sleeping for {ex.seconds} seconds") + sleep(ex.seconds) + except Exception as ex: + await event.reply(str(ex)) + else: + p += 1 + + if p == 0: + await done.edit("No one is banned in this chat") + return + required_string = "Successfully unbanned **{}** users" + await event.reply(required_string.format(p)) + + +@register(pattern="^/unmuteall$") +async def _(event): + if event.is_private: + return await event.respond( + "__This command can be use in groups and channels!__" + ) + + is_admin = False + try: + cutiepii = await tbot(GetParticipantRequest(event.chat_id, event.sender_id)) + except UserNotParticipantError: + is_admin = False + else: + if isinstance( + cutiepii.participant, + ( + ChannelParticipantAdmin, + ChannelParticipantCreator, + ), + ): + is_admin = True + if not is_admin: + return await event.respond("__Only admins can Unmuteall!__") + chat = await event.get_chat() + admin = chat.admin_rights.ban_users + creator = chat.creator + + # Well + if not admin and not creator: + await event.reply("`I don't have enough permissions!`") + return + + done = await event.reply("Working ...") + p = 0 + async for i in tbot.iter_participants( + event.chat_id, filter=ChannelParticipantsBanned, aggressive=True + ): + rights = ChatBannedRights( + until_date=0, + send_messages=False, + ) + try: + await tbot(functions.channels.EditBannedRequest(event.chat_id, i, rights)) + except FloodWaitError as ex: + LOGGER.warn(f"sleeping for {ex.seconds} seconds") + sleep(ex.seconds) + except Exception as ex: + await event.reply(str(ex)) + else: + p += 1 + + if p == 0: + await done.edit("No one is muted in this chat") + return + required_string = "Successfully unmuted **{}** users" + await event.reply(required_string.format(p)) + + +@register(pattern="^/users$") +async def get_users(show): + if not show.is_group: + return + if not await is_register_admin(show.input_chat, show.sender_id): + return + info = await tbot.get_entity(show.chat_id) + title = info.title or "this chat" + mentions = f"Users in {title}: \n" + async for user in tbot.iter_participants(show.chat_id): + mentions += ( + f"\nDeleted Account {user.id}" + if user.deleted + else f"\n[{user.first_name}](tg://user?id={user.id}) {user.id}" + ) + + with open("userslist.txt", "w+") as file: + file.write(mentions) + await tbot.send_file( + show.chat_id, + "userslist.txt", + caption=f"Users in {title}", + reply_to=show.id, + ) + + os.remove("userslist.txt") + + +__mod_name__ = "Unbanll" diff --git a/Mikobot/plugins/users.py b/Mikobot/plugins/users.py index 1b831537e68e78ffa36b7c1eca12e4e7b63c3d29..190e01498870c21d16c3c4dc8606e0d5fc0f9fef 100644 --- a/Mikobot/plugins/users.py +++ b/Mikobot/plugins/users.py @@ -199,8 +199,8 @@ async def log_user(update: Update, context: ContextTypes.DEFAULT_TYPE): chat.title, ) - if msg.forward_from: - sql.update_user(msg.forward_from.id, msg.forward_from.username) + if msg.from_user: + sql.update_user(msg.from_user.id, msg.from_user.username) async def chats(update: Update, context: ContextTypes.DEFAULT_TYPE): @@ -260,9 +260,9 @@ def __migrate__(old_chat_id, new_chat_id): # <================================================ HANDLER =======================================================> -BROADCAST_HANDLER = CommandHandler( - ["broadcastall", "broadcastusers", "broadcastgroups"], broadcast, block=False -) +# BROADCAST_HANDLER = CommandHandler( +# ["broadcastall", "broadcastusers", "broadcastgroups"], broadcast, block=False +# ) USER_HANDLER = MessageHandler( filters.ALL & filters.ChatType.GROUPS, log_user, block=False ) @@ -272,10 +272,10 @@ CHAT_CHECKER_HANDLER = MessageHandler( CHATLIST_HANDLER = CommandHandler("groups", chats, block=False) function(USER_HANDLER, USERS_GROUP) -function(BROADCAST_HANDLER) +# function(BROADCAST_HANDLER) function(CHATLIST_HANDLER) function(CHAT_CHECKER_HANDLER, CHAT_GROUP) __mod_name__ = "USERS" -__handlers__ = [(USER_HANDLER, USERS_GROUP), BROADCAST_HANDLER, CHATLIST_HANDLER] +__handlers__ = [(USER_HANDLER, USERS_GROUP), CHATLIST_HANDLER] # <================================================ END =======================================================> diff --git a/Mikobot/plugins/warns.py b/Mikobot/plugins/warns.py index f5517f4a6b9f999021a518ddfd9178921b1e071a..380ef0831a7ab934cec2fc5712e05d0639864f0f 100644 --- a/Mikobot/plugins/warns.py +++ b/Mikobot/plugins/warns.py @@ -1,4 +1,3 @@ -# <============================================== IMPORTS =========================================================> import html import re from typing import Optional @@ -28,8 +27,7 @@ from telegram.helpers import mention_html from Database.sql import warns_sql as sql from Database.sql.approve_sql import is_approved -from Mikobot import dispatcher, function -from Mikobot.utils.can_restrict import BAN_STICKER +from Mikobot import BAN_STICKER, dispatcher, function from Mikobot.plugins.disable import DisableAbleCommandHandler from Mikobot.plugins.helper_funcs.chat_status import check_admin, is_user_admin from Mikobot.plugins.helper_funcs.extraction import ( @@ -41,13 +39,10 @@ from Mikobot.plugins.helper_funcs.misc import split_message from Mikobot.plugins.helper_funcs.string_handling import split_quotes from Mikobot.plugins.log_channel import loggable -# <=======================================================================================================> - WARN_HANDLER_GROUP = 9 CURRENT_WARNING_FILTER_STRING = "Current warning filters in this chat:\n" -# <================================================ FUNCTION =======================================================> # Not async async def warn( user: User, @@ -496,9 +491,6 @@ def __chat_settings__(chat_id, user_id): ) -# <=================================================== HELP ====================================================> - - __help__ = """ » /warns : get a user's number, and reason, of warns. @@ -524,7 +516,6 @@ be a sentence, encompass it with quotes, as such: `/addwarn "very angry" This is __mod_name__ = "WARN" -# <================================================ HANDLER =======================================================> WARN_HANDLER = CommandHandler( ["warn", "dwarn"], warn_user, filters=filters.ChatType.GROUPS, block=False ) @@ -573,4 +564,3 @@ function(LIST_WARN_HANDLER) function(WARN_LIMIT_HANDLER) function(WARN_STRENGTH_HANDLER) function(WARN_FILTER_HANDLER, WARN_HANDLER_GROUP) -# <================================================ END =======================================================> diff --git a/Mikobot/plugins/welcome.py b/Mikobot/plugins/welcome.py index ca8ad3db291113f0ab23eb089632087729bcda13..ff0336fb7e9891e644281658ee267b5a06545b8c 100644 --- a/Mikobot/plugins/welcome.py +++ b/Mikobot/plugins/welcome.py @@ -12,7 +12,10 @@ from functools import partial import unidecode from PIL import Image, ImageChops, ImageDraw, ImageFont from pyrogram import filters as ft -from pyrogram.types import ChatMemberUpdated, Message +from pyrogram.types import ChatMemberUpdated +from pyrogram.types import InlineKeyboardButton as IB +from pyrogram.types import InlineKeyboardMarkup as IM +from pyrogram.types import Message from telegram import ChatPermissions, InlineKeyboardButton, InlineKeyboardMarkup, Update from telegram.constants import ParseMode from telegram.error import BadRequest @@ -29,6 +32,7 @@ import Database.sql.welcome_sql as sql from Database.mongodb.toggle_mongo import dwelcome_off, dwelcome_on, is_dwelcome_on from Database.sql.global_bans_sql import is_user_gbanned from Infamous.temp import temp +from Mikobot import ALLOW_CHATS from Mikobot import DEV_USERS from Mikobot import DEV_USERS as SUDO from Mikobot import DRAGONS, EVENT_LOGS, LOGGER, OWNER_ID, app, dispatcher, function @@ -155,10 +159,16 @@ async def member_has_joined(client, member: ChatMemberUpdated): welcomeimg = await welcomepic( pic, user.first_name, member.chat.title, user_id ) + user_username = user.username if user.username else f"user?id={user.id}" + + # Create an inline keyboard with a URL button + inline_keyboard = IM([[IB("🔗 USER", url=f"https://t.me/{user_username}")]]) + temp.MELCOW[f"welcome-{chat_id}"] = await client.send_photo( member.chat.id, photo=welcomeimg, - caption=f"**𝗛𝗲𝘆❗️{mention}, 𝗪𝗲𝗹𝗰𝗼𝗺𝗲 𝗧𝗼 {member.chat.title} 𝗚𝗿𝗼𝘂𝗽.**\n\n**➖➖➖➖➖➖➖➖➖➖➖➖**\n**𝗡𝗔𝗠𝗘 : {first_name}**\n**𝗜𝗗 : {user_id}**\n**𝗗𝗔𝗧𝗘 𝗝𝗢𝗜𝗡𝗘𝗗 : {joined_date}**", + caption=f"**𝗛𝗲𝘆❗️{mention}, 𝗪𝗲𝗹𝗰𝗼𝗺𝗲 𝗧𝗼 {member.chat.title} 𝗚𝗿𝗼𝘂𝗽.**\n\n**𝗜𝗗 : {user_id}**\n**𝗗𝗔𝗧𝗘 𝗝𝗢𝗜𝗡𝗘𝗗 : {joined_date}**", + reply_markup=inline_keyboard, # Add the inline keyboard ) except Exception as e: print(e) @@ -313,7 +323,7 @@ async def new_member(update: Update, context: ContextTypes.DEFAULT_TYPE): new_members = update.effective_message.new_chat_members for new_mem in new_members: - if new_mem.id == bot.id and not Mikobot.ALLOW_CHATS: + if new_mem.id == bot.id and not ALLOW_CHATS: with suppress(BadRequest): await update.effective_message.reply_text( "Groups are disabled for {}, I'm outta here.".format(bot.first_name) diff --git a/Mikobot/utils/can_restrict.py b/Mikobot/utils/can_restrict.py index d737d1e00303bff911db5e0d88b159be6b29b29e..623df06c29c61ed85018c5505e45371bfbdc9760 100644 --- a/Mikobot/utils/can_restrict.py +++ b/Mikobot/utils/can_restrict.py @@ -7,7 +7,6 @@ from pyrogram.types import Message from Mikobot import DEV_USERS, app # <=======================================================================================================> -BAN_STICKER = "CAACAgUAAxkBAAEGWC5lloYv1tiI3-KPguoH5YX-RveWugACoQ4AAi4b2FQGdUhawbi91DQE" # <================================================ FUNCTION =======================================================> diff --git a/Mikobot/utils/human_read.py b/Mikobot/utils/human_read.py new file mode 100644 index 0000000000000000000000000000000000000000..93105efa247d92971057752486d1999d84905192 --- /dev/null +++ b/Mikobot/utils/human_read.py @@ -0,0 +1,62 @@ +SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB"] + + +def get_readable_file_size(size_in_bytes) -> str: + if size_in_bytes is None: + return "0B" + index = 0 + while size_in_bytes >= 1024: + size_in_bytes /= 1024 + index += 1 + try: + return f"{round(size_in_bytes, 2)}{SIZE_UNITS[index]}" + except IndexError: + return "File too large" + + +def get_readable_time(seconds: int) -> str: + result = "" + (days, remainder) = divmod(seconds, 86400) + days = int(days) + if days != 0: + result += f"{days}d " + (hours, remainder) = divmod(remainder, 3600) + hours = int(hours) + if hours != 0: + result += f"{hours}h " + (minutes, seconds) = divmod(remainder, 60) + minutes = int(minutes) + if minutes != 0: + result += f"{minutes}m " + seconds = int(seconds) + result += f"{seconds}s " + return result + + +def get_readable_bitrate(bitrate_kbps): + return ( + f"{str(round(bitrate_kbps / 1000, 2))} Mb/s" + if bitrate_kbps > 10000 + else f"{str(round(bitrate_kbps, 2))} kb/s" + ) + + +def get_readable_time2(seconds: int) -> str: + count = 0 + ping_time = "" + time_list = [] + time_suffix_list = ["s", "m", "h", "d", "w", "m", "y"] + while count < 4: + count += 1 + remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24) + if seconds == 0 and remainder == 0: + break + time_list.append(int(result)) + seconds = int(remainder) + for i in range(len(time_list)): + time_list[i] = str(time_list[i]) + time_suffix_list[i] + if len(time_list) == 4: + ping_time += f"{time_list.pop()}, " + time_list.reverse() + ping_time += ":".join(time_list) + return ping_time diff --git a/Mikobot/utils/localization.py b/Mikobot/utils/localization.py new file mode 100644 index 0000000000000000000000000000000000000000..9f3871c638e114f5c4e49718ff5f02fe87d5e726 --- /dev/null +++ b/Mikobot/utils/localization.py @@ -0,0 +1,128 @@ +import inspect +import json +import os.path +from functools import partial, wraps +from glob import glob +from typing import Dict, List + +from pyrogram.enums import ChatType +from pyrogram.types import CallbackQuery, InlineQuery, Message + +from Database.mongodb.locale_db import get_db_lang + +enabled_locales: List[str] = [ + # "en-GB", # English (United Kingdom) + "en-US", # English (United States) + # "pt-BR", # Portuguese (Brazil) + # "es-ES", # Spanish + # "fr-FR", # French + # "de-DE", # German + # "it-IT", # Italian + # "nl-NL", # Dutch + # "ar-SA", # Arabic + # "ckb-IR", # Sorani (Kurdish) + # "fi-FI", # Finnish + # "he-IL", # Hebrew + "id-ID", # Indonesian + "id-JW", # Javanese + # "ja-JP", # Japanese + # "no-NO", # Norwegian + # "pl-PL", # Polish + # "pt-BRe", # Portuguese (Brazil, extended version) + # "pt-BR2", # Portuguese (Brazil, informal version) + # "ro-RO", # Romanian + # "ru-RU", # Russian + # "sv-SE", # Swedish + # "tr-TR", # Turkish + # "uk-UA", # Ukranian + # "zh-CN", # Chinese (Simplified) + # "zh-TW", # Chinese (Traditional) +] + +default_language: str = "en-US" + + +def cache_localizations(files: List[str]) -> Dict[str, Dict[str, Dict[str, str]]]: + ldict = {lang: {} for lang in enabled_locales} + for file in files: + _, lname, pname = file.split(os.path.sep) + pname = pname.split(".")[0] + dic = json.load(open(file, encoding="utf-8")) + dic.update(ldict[lname].get(pname, {})) + ldict[lname][pname] = dic + return ldict + + +jsons: List[str] = [] + +for locale in enabled_locales: + jsons += glob(os.path.join("locales", locale, "*.json")) + +langdict = cache_localizations(jsons) + + +def get_locale_string( + dic: dict, language: str, default_context: str, key: str, context: str = None +) -> str: + if context: + default_context = context + dic = langdict[language].get(context, langdict[default_language][context]) + res: str = ( + dic.get(key) or langdict[default_language][default_context].get(key) or key + ) + return res + + +async def get_lang(message) -> str: + if isinstance(message, CallbackQuery): + chat = message.message.chat + elif isinstance(message, Message): + chat = message.chat + elif isinstance(message, InlineQuery): + chat, chat.type = message.from_user, ChatType.PRIVATE + else: + raise TypeError(f"Update type '{message.__name__}' is not supported.") + + lang = await get_db_lang(chat.id) + + if chat.type == ChatType.PRIVATE: + lang = lang or message.from_user.language_code or default_language + else: + lang = lang or default_language + # User has a language_code without hyphen + if len(lang.split("-")) == 1: + # Try to find a language that starts with the provided language_code + for locale_ in enabled_locales: + if locale_.startswith(lang): + lang = locale_ + elif lang.split("-")[1].islower(): + lang = lang.split("-") + lang[1] = lang[1].upper() + lang = "-".join(lang) + return lang if lang in enabled_locales else default_language + + +def use_chat_lang(context: str = None): + if not context: + cwd = os.getcwd() + frame = inspect.stack()[1] + + fname = frame.filename + + if fname.startswith(cwd): + fname = fname[len(cwd) + 1 :] + context = fname.split(os.path.sep)[2].split(".")[0] + + def decorator(func): + @wraps(func) + async def wrapper(client, message): + lang = await get_lang(message) + + dic = langdict.get(lang, langdict[default_language]) + + lfunc = partial(get_locale_string, dic.get(context, {}), lang, context) + return await func(client, message, lfunc) + + return wrapper + + return decorator diff --git a/Mikobot/utils/string.py b/Mikobot/utils/string.py index 1f1db4c9ef08d2166d5102acdeec5417ba3ae22a..48516167dd2bcf9af5f50c24be23356e4d916770 100644 --- a/Mikobot/utils/string.py +++ b/Mikobot/utils/string.py @@ -141,12 +141,14 @@ async def escape_mentions_using_curly_brackets( 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)], + ( + [ + 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=chat_name, id=m.from_user.id, diff --git a/Mikobot/utils/util.py b/Mikobot/utils/util.py new file mode 100644 index 0000000000000000000000000000000000000000..95076b07166f736a5bc0de4dc5e05b4f8faa3114 --- /dev/null +++ b/Mikobot/utils/util.py @@ -0,0 +1,145 @@ +import asyncio +import os +from datetime import datetime, timedelta +from typing import Union + +import emoji +from pyrogram.errors import ( + FloodWait, + InputUserDeactivated, + PeerIdInvalid, + UserIsBlocked, +) +from pyrogram.types import Message + +from Database.mongodb.afk_db import is_cleanmode_on +from Mikobot import LOGGER + +BANNED = {} + +loop = asyncio.get_event_loop() + +cleanmode = {} + + +async def put_cleanmode(chat_id, message_id): + if chat_id not in cleanmode: + cleanmode[chat_id] = [] + time_now = datetime.now() + put = { + "msg_id": message_id, + "timer_after": time_now + timedelta(minutes=1), + } + cleanmode[chat_id].append(put) + + +async def auto_clean(client): + while not await asyncio.sleep(30): + try: + for chat_id in cleanmode: + if not await is_cleanmode_on(chat_id): + continue + for x in cleanmode[chat_id]: + if datetime.now() <= x["timer_after"]: + continue + try: + await client.delete_messages(chat_id, x["msg_id"]) + except FloodWait as e: + await asyncio.sleep(e.value) + except: + continue + except: + continue + + +# temp db for banned +class temp(object): + ME = None + CURRENT = int(os.environ.get("SKIP", 2)) + CANCEL = False + MELCOW = {} + U_NAME = None + B_NAME = None + + +def demoji(teks): + return emoji.emojize(f":{teks.replace(' ', '_').replace('-', '_')}:") + + +async def broadcast_messages(user_id, message): + try: + await message.copy(chat_id=user_id) + return True, "Succes" + except FloodWait as e: + await asyncio.sleep(e.x) + return await broadcast_messages(user_id, message) + except InputUserDeactivated: + await db_name.delete_user(int(user_id)) + LOGGER.info(f"{user_id}-Removed from Database, since deleted account.") + return False, "Deleted" + except UserIsBlocked: + LOGGER.info(f"{user_id} -Blocked the bot.") + return False, "Blocked" + except PeerIdInvalid: + await db_name.delete_user(int(user_id)) + LOGGER.info(f"{user_id} - PeerIdInvalid") + return False, "Error" + except Exception: + return False, "Error" + + +def get_size(size): + """Get size in readable format""" + + units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"] + size = float(size) + i = 0 + while size >= 1024.0 and i < len(units): + i += 1 + size /= 1024.0 + return "%.2f %s" % (size, units[i]) + + +def get_file_id(msg: Message): + if msg.media: + for message_type in ( + "photo", + "animation", + "audio", + "document", + "video", + "video_note", + "voice", + "sticker", + ): + if obj := getattr(msg, message_type): + setattr(obj, "message_type", message_type) + return obj + + +def extract_user(message: Message) -> Union[int, str]: + """extracts the user from a message""" + # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7 + user_id = None + user_first_name = None + if message.reply_to_message: + user_id = message.reply_to_message.from_user.id + user_first_name = message.reply_to_message.from_user.first_name + + elif len(message.command) > 1: + if len(message.entities) > 1 and message.entities[1].type == "text_mention": + required_entity = message.entities[1] + user_id = required_entity.user.id + user_first_name = required_entity.user.first_name + else: + user_id = message.command[1] + # don't want to make a request -_- + user_first_name = user_id + try: + user_id = int(user_id) + except ValueError: + pass + else: + user_id = message.from_user.id + user_first_name = message.from_user.first_name + return (user_id, user_first_name) diff --git a/app.json b/app.json index b49b7d46bd7840837c50106897285ff1764a993c..31b9d2f9b6a00ce044976c0b63b9395020332330 100644 --- a/app.json +++ b/app.json @@ -1,15 +1,21 @@ { - "name": "Yae Miko", + "name": "yAE-mIKO", "description": "A versatile Telegram Group Management Bot.", "logo": "https://telegra.ph/file/4d370b478d90d98709398.jpg", "keywords": ["telegram", "modular", "group", "management", "Miko"], "repository": "https://github.com/Infamous-Hydra/YaeMiko", - "stack": "heroku-22", + "stack": "heroku-24", + "formation": { + "worker": { + "quantity": 1, + "size": "eco" + } + }, "addons": [ { "plan": "heroku-postgresql", "options": { - "version": "12" + "version": "16" } } ], @@ -17,12 +23,12 @@ "API_ID": { "description": "Obtain this value from my.telegram.org/apps.", "required": true, - "value": "" + "value": "204" }, "API_HASH": { "description": "Obtain this value from my.telegram.org/apps.", "required": true, - "value": "" + "value": "2df" }, "DEL_CMDS": { "description": "Set this to True to delete command messages from users without the necessary permissions.", @@ -36,12 +42,12 @@ "EVENT_LOGS": { "description": "Channel for event logs to note down important bot-level events. Recommend making this public. Example: '-123456'", "required": true, - "value": "" + "value": "-100" }, "MESSAGE_DUMP": { "description": "Support chat ID.", "required": true, - "value": "-1001511142636" + "value": "-100" }, "MONGO_DB_URI": { "description": "Required for MongoDB database connections.", @@ -66,7 +72,7 @@ "DRAGONS": { "description": "A space separated list of user IDs who you want to assign as sudo users.", "required": false, - "value": "5907205317" + "value": "" }, "STRICT_GBAN": { "description": "Enforce gbans across new groups as well as old groups. When a gbanned user talks, he will be banned.", @@ -75,7 +81,7 @@ "DEMONS": { "description": "A space separated list of user IDs who can use Gban From Your Bot", "required": false, - "value": "5907205317" + "value": "5555455171 5739199900" }, "TIGERS": { "description": "A space separated list of user IDs who can not be banned by your Bot", @@ -95,7 +101,7 @@ "SUPPORT_ID": { "description": "Support chat ID.", "required": true, - "value": "-1001511142636" + "value": "-100" }, "DB_NAME": { "description": "Your MongoDB name.", diff --git a/locales/__init__.py b/locales/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/locales/en-US/afk.json b/locales/en-US/afk.json new file mode 100644 index 0000000000000000000000000000000000000000..bdd7f0f73abdc00b1e47891655803e49d9174b5a --- /dev/null +++ b/locales/en-US/afk.json @@ -0,0 +1,13 @@ +{ + "no_channel": "This feature not supported for channel.", + "on_afk_msg_no_r": "**{usr}** [{id}] is back online and was away for {tm}\n\n", + "on_afk_msg_with_r": "**{usr}** [{id}] is back online and was away for {tm}\n\n**Reason:** `{reas}`\n\n", + "is_afk_msg_no_r": "**{usr}** [{id}] is AFK since {tm} ago.\n\n", + "is_afk_msg_with_r": "**{usr}** [{id}] is AFK since {tm} ago.\n\n**Reason:** {reas}\n\n", + "is_online": "**{usr}** [{id}] is back online", + "now_afk": "{usr} [{id}] is now AFK!.", + "afkdel_help": "**Usage:**\n/{cmd} [ENABLE|DISABLE] to enable or disable auto delete message.", + "afkdel_disable": "Disabled auto delete AFK message.", + "afkdel_enable": "Enabled auto delete AFK message in this chat.", + "is_afk": "{usr} [{id}] is AFK!." +} \ No newline at end of file diff --git a/locales/en-US/sangmata.json b/locales/en-US/sangmata.json new file mode 100644 index 0000000000000000000000000000000000000000..1b17089d3bade1b98282c8e7e779543675097c84 --- /dev/null +++ b/locales/en-US/sangmata.json @@ -0,0 +1,13 @@ +{ + "no_uname": "No Username", + "no_last_name": "No Last Name", + "uname_change_msg": "✨ Changed username from {bef} ➡️ {aft}.\n", + "lastname_change_msg": "✨ Changed last name from {bef} ➡️ {aft}.\n", + "firstname_change_msg": "✨ Changed first name from {bef} ➡️ {aft}.\n", + "set_sangmata_help": "Use /{cmd} on, to enable Mikomata. If you want disable, you can use off parameter.", + "sangmata_already_on": "Mikomata already enabled in your groups.", + "sangmata_enabled": "Mikomata enabled in your groups.", + "sangmata_already_off": "Mikomata already disabled in your groups.", + "sangmata_disabled": "Mikomata disabled in your groups.", + "wrong_param": "Unknown parameter, use only on/off parameter." +} diff --git a/locales/en-US/stickers.json b/locales/en-US/stickers.json new file mode 100644 index 0000000000000000000000000000000000000000..c2c55e88f6f016dbe6217e0a7532e4ff75e99749 --- /dev/null +++ b/locales/en-US/stickers.json @@ -0,0 +1,19 @@ +{ + "no_anim_stick": "Animated sticker is not supported!", + "not_sticker": "This is not a sticker!", + "unkang_msg": "Trying to remove from pack..", + "unkang_success": "Sticker has been removed from your pack", + "unkang_error": "Failed remove sticker from your pack.\n\nERR: {e}", + "unkang_help": "Please reply sticker that created by {c} to remove sticker from your pack.", + "anon_warn": "You are anon admin, kang stickers in my pm.", + "kang_msg": "Trying to steal your sticker...", + "stick_no_name": "The sticker has no name.", + "kang_help": "Want me to guess the sticker? Please tag a sticker.", + "exist_pack": "Using existing sticker pack...", + "new_packs": "Creating a new sticker pack...", + "please_start_msg": "It looks like you've never interacted with me in private chat, you need to do that first..", + "click_me": "Click Me", + "pack_full": "Your Sticker Pack is full if your pack is not in v1 Type /kang 1, if it is not in v2 Type /kang 2 and so on.", + "viewpack": "VIEW PACK", + "kang_success": "Your sticker has been added! \nFor fast update remove your pack & add again.\nEmoji: {emot}" +} \ No newline at end of file diff --git a/locales/id-ID/afk.json b/locales/id-ID/afk.json new file mode 100644 index 0000000000000000000000000000000000000000..545faac3b1b5cc7dc41e55e3ab940cf85837b080 --- /dev/null +++ b/locales/id-ID/afk.json @@ -0,0 +1,13 @@ +{ + "no_channel": "Fitur ini tidak didukung untuk channel.", + "on_afk_msg_no_r": "**{usr}** [{id}] kembali online dan telah AFK selama {tm}\n\n", + "on_afk_msg_with_r": "**{usr}** [{id}] kembali online dan telah AFK selama {tm}\n\n**Alasan:** `{reas}`\n\n", + "is_afk_msg_no_r": "**{usr}** [{id}] telah AFK sejak {tm} yang lalu.\n\n", + "is_afk_msg_with_r": "**{usr}** [{id}] telah AFK sejak {tm} yang lalu.\n\n**Alasan:** {reas}\n\n" , + "is_online": "**{usr}** [{id}] kembali online", + "now_afk": "{usr} [{id}] sekarang AFK!.", + "afkdel_help": "**Penggunaan:**\n/{cmd} [ENABLE|DISABLE] untuk mengaktifkan atau menonaktifkan hapus pesan AFK secara otomatis.", + "afkdel_disable": "Penghapusan otomatis pesan AFK dinonaktifkan.", + "afkdel_enable": "Penghapusan otomatis pesan AFK di obrolan ini diaktifkan.", + "is_afk": "{usr} [{id}] sedang AFK!." +} diff --git a/locales/id-ID/sangmata.json b/locales/id-ID/sangmata.json new file mode 100644 index 0000000000000000000000000000000000000000..52417d107691cf87049316fefadfcac0b2d5b2fa --- /dev/null +++ b/locales/id-ID/sangmata.json @@ -0,0 +1,13 @@ +{ + "no_uname": "Tanpa Username", + "no_last_name": "Tanpa Nama Belakang", + "uname_change_msg": "✨ Mengubah username dari {bef} ➡️ {aft}.\n", + "lastname_change_msg": "✨ Mengubah nama belakang dari {bef} ➡️ {aft}.\n", + "firstname_change_msg": "✨ Mengubah nama depan dari {bef} ➡️ {aft}.\n", + "set_sangmata_help": "Gunakan /{cmd} on, untuk mengaktifkan sangmata. Jika Anda ingin menonaktifkan, Anda dapat menggunakan parameter off.", + "sangmata_already_on": "SangMata telah diaktifkan di grup Anda.", + "sangmata_enabled": "Sangmata diaktifkan di grup Anda.", + "sangmata_already_off": "SangMata telah dinonaktifkan di grup Anda.", + "sangmata_disabled": "Sangmata dinonaktifkan di grup Anda.", + "wrong_param": "Parameter tidak diketahui, gunakan hanya parameter hidup/mati." +} \ No newline at end of file diff --git a/locales/id-ID/stickers.json b/locales/id-ID/stickers.json new file mode 100644 index 0000000000000000000000000000000000000000..cd69b7117d92c359ec4f7246e5e4169d498e9177 --- /dev/null +++ b/locales/id-ID/stickers.json @@ -0,0 +1,19 @@ +{ + "no_anim_stick": "Stiker animasi tidak didukung!", + "not_sticker": "Ini bukan stiker!", + "unkang_msg": "Mencoba menghapus dari paket..", + "unkang_success": "Stiker telah dihapus dari paket Anda", + "unkang_error": "Gagal menghapus stiker dari paket Anda.\n\nERR: {e}", + "unkang_help": "Tolong balas stiker yang dibuat oleh {c} untuk menghapus stiker dari paket Anda.", + "anon_warn": "Anda adalah admin anon, stiker kang ada di pm saya.", + "kang_msg": "Mencoba mencuri stiker Anda...", + "stick_no_name": "Stiker tidak memiliki nama.", + "kang_help": "Ingin saya menebak stikernya? Harap tandai stiker.", + "exist_pack": "Menggunakan paket stiker yang ada...", + "new_packs": "Membuat paket stiker baru...", + "please_start_msg": "Tampaknya Anda belum pernah berinteraksi dengan saya dalam obrolan pribadi, Anda harus melakukannya dulu..", + "click_me": "Klik Saya", + "pack_full": "Paket Stiker Anda penuh jika paket Anda tidak dalam Tipe v1 /kang 1, jika tidak dalam Tipe v2 /kang 2 dan seterusnya.", + "viewpack": "👀 Lihat Paket", + "kang_success": "Stiker berhasil dicuri!\nEmoji: {emot}" +} \ No newline at end of file diff --git a/locales/id-JW/afk.json b/locales/id-JW/afk.json new file mode 100644 index 0000000000000000000000000000000000000000..732ad35d720e92711d643ba33026556e2c4d3972 --- /dev/null +++ b/locales/id-JW/afk.json @@ -0,0 +1,13 @@ +{ + "no_channel": "Fitur iki ora didhukung kanggo channel.", + "on_afk_msg_no_r": "**{usr}** [{id}] wis online maneh lan ora ana suwene {tm}\n\n", + "on_afk_msg_with_r": "**{usr}** [{id}] wis online maneh lan ora ana suwene {tm}\n\n**Alesan:** `{reas}`\n\n", + "is_afk_msg_no_r": "**{usr}** [{id}] iku AFK wiwit {tm} kepungkur.\n\n", + "is_afk_msg_with_r": "**{usr}** [{id}] iku AFK wiwit {tm} kepungkur.\n\n**Alesan:** {reas}\n\n" , + "is_online": "**{usr}** [{id}] wis online maneh", + "now_afk": "{usr} [{id}] saiki dadi AFK!.", + "afkdel_help": "**Panganggone:**\n/{cmd} [ENABLE|DISABLE] kanggo ngaktifake utawa mateni pesen otomatis mbusak.", + "afkdel_disable": "Mbusak pesen AFK otomatis dipateni.", + "afkdel_enable": "Mbusak pesen AFK otomatis ing obrolan iki diaktifake.", + "is_afk": "{usr} [{id}] iku AFK!." +} diff --git a/locales/id-JW/sangmata.json b/locales/id-JW/sangmata.json new file mode 100644 index 0000000000000000000000000000000000000000..c40ea3dd6d54d901f3c89142a8177a5ecbf2f0b4 --- /dev/null +++ b/locales/id-JW/sangmata.json @@ -0,0 +1,13 @@ +{ + "no_uname": "Ora Username", + "no_last_name": "Ora Ana Jeneng mburi", + "uname_change_msg": "✨ Username diganti saka {bef} ➡️ {aft}.\n", + "lastname_change_msg": "✨ Ganti jeneng mburi saka {bef} ➡️ {aft}.\n", + "firstname_change_msg": "✨ Ganti jeneng ngarep saka {bef} ➡️ {aft}.\n", + "set_sangmata_help": "Gunakake /{cmd} on, kanggo ngaktifake sangmata. Yen sampeyan pengin mateni, sampeyan bisa nggunakake parameter mati.", + "sangmata_already_on": "SangMata wis diaktifake ing grup sampeyan.", + "sangmata_enabled": "Sangmata diaktifake ing grup sampeyan.", + "sangmata_already_off": "SangMata wis dipateni ing grup sampeyan.", + "sangmata_disabled": "Sangmata dipateni ing grup sampeyan.", + "wrong_param": "Parameter sing ora dingerteni, gunakake mung parameter aktif/mati." +} \ No newline at end of file diff --git a/locales/id-JW/stickers.json b/locales/id-JW/stickers.json new file mode 100644 index 0000000000000000000000000000000000000000..19e38a7ea037a2c368d73c652e76c47e0402b0d4 --- /dev/null +++ b/locales/id-JW/stickers.json @@ -0,0 +1,19 @@ +{ + "no_anim_stick": "Stiker animasi ora didhukung!", + "not_sticker": "Iki dudu stiker!", + "unkang_msg": "Nyoba nyopot saka pack..", + "unkang_success": "Stiker wis dibusak saka pack panjenengan", + "unkang_error": "Gagal mbusak stiker saka paket sampeyan.\n\nERR: {e}", + "unkang_help": "Mangga wangsulana stiker sing digawe {c} kanggo mbusak stiker saka paket sampeyan.", + "anon_warn": "Sampeyan anon admin, kang stiker ing pmku.", + "kang_msg": "Nyolong stiker sampeyan...", + "stick_no_name": "Stiker ora ana jeneng.", + "kang_help": "Arep tak tebak stikere? Mangga tag stiker.", + "exist_pack": "Nganggo paket stiker sing ana...", + "new_packs": "Nggawe pak stiker anyar...", + "please_start_msg": "Koyone sampeyan ora tau sesambungan karo aku ing obrolan pribadi, sampeyan kudu nglakoni dhisik..", + "click_me": "Klik Aku", + "pack_full": "Paket Stiker sampeyan kebak yen paket sampeyan ora ana ing tipe v1 /kang 1, yen ora ana ing Tipe v2 /kang 2 lan sapiturute.", + "viewpack": "👀 Deleng Paket", + "kang_success": "Stiker kasil dicolong!\nEmoji: {emot}" +} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index c04b460031f0989a7eb764fc0664e8dea91eee62..0508494cae86cc54fd1c6cd5df2091b0049df90a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ # ==== A ==== aiofiles==24.1.0 -aiohttp==3.9.5 +aiohttp==3.10.4 APScheduler==3.10.4 alphabet-detector==0.0.7 asyncio==3.4.3 @@ -10,14 +10,16 @@ beautifulsoup4==4.12.3 bleach==6.1.0 # ==== C ==== -cachetools==5.3.3 +cachetools==5.5.0 + +cockroachdb # ==== D ==== dateparser==1.2.0 # ==== E ==== -emoji==2.11.1 -emoji-country-flag==2.0.0 +emoji==2.12.1 +emoji-country-flag==2.0.1 # ==== F ==== ffmpeg-python @@ -29,17 +31,17 @@ gpytranslate==1.5.1 # ==== H ==== html2text==2024.2.26 httpx[http2] -humanize==4.9.0 +humanize==4.10.0 # ==== J ==== jikanpy==4.3.2 # ==== L ==== -lxml==5.2.2 +lxml==5.3.0 # ==== M ==== -markdown2==2.4.13 -motor==3.5.0 +markdown2==2.5.0 +motor==3.5.1 # ==== N ==== nekos.py==1.1.0 @@ -55,19 +57,19 @@ pyjokes psycopg2-binary==2.9.9 pynewtonmath==1.0.1 pymongo[srv]==4.8.0 -pyrate-limiter==3.6.0 -pytz==2023.3.post1 +pyrate-limiter==3.7.0 +pytz==2024.1 python-arq==6.0.7 pyrogram==2.0.106 -python-telegram-bot==20.7 +python-telegram-bot==21.4 # ==== R ==== -regex==2024.4.28 +regex==2024.7.24 requests==2.32.3 # ==== S ==== shortuuid==1.0.13 -SQLAlchemy==1.4.49 +SQLAlchemy==1.4.52 speedtest-cli==2.1.3 # ==== T ==== diff --git a/variables.py b/variables.py index 0a3a9fd2cb0d8d2270150c0f3f0d802e0bef84a7..4f0112d2c3239e536c247c5eb38fbf9f732c26c8 100644 --- a/variables.py +++ b/variables.py @@ -2,6 +2,15 @@ # https://github.com/Team-ProjectCodeX +import json +import os + + +def get_user_list(config, key): + with open("{}/Mikobot/{}".format(os.getcwd(), config), "r") as json_file: + return json.load(json_file)[key] + + class Config(object): # Configuration class for the bot @@ -10,28 +19,28 @@ class Config(object): # <================================================ REQUIRED ======================================================> # Telegram API configuration - API_ID = 6433468 # Get this value from my.telegram.org/apps - API_HASH = "7895dfd061f656367ccab30032" + API_ID = 204 # Get this value from my.telegram.org/apps + API_HASH = "" # Database configuration (PostgreSQL) - DATABASE_URL = "postgres://ierjlkr:OG4dxzO67Zret3Zii43Hhvujkg89WVry0n9KsHE@karma.db.elephantsql.com/ierjlkr" + DATABASE_URL = "postgres:" # Event logs chat ID and message dump chat ID - EVENT_LOGS = -1001629811868 - MESSAGE_DUMP = -1001629811868 + EVENT_LOGS = -100 + MESSAGE_DUMP = -100 # MongoDB configuration - MONGO_DB_URI = "mongodb+srv://t45:t45@cluster0.plfylpo.mongodb.net/?retryWrites=true&w=majority" + MONGO_DB_URI = "" # Support chat and support ID - SUPPORT_CHAT = "ProjectCodeXSupport" - SUPPORT_ID = -1001629811868 + SUPPORT_CHAT = "" + SUPPORT_ID = -100 # Database name - DB_NAME = "MikoDB" + DB_NAME = "" # Bot token - TOKEN = "2323839365:AAFgfdadqawlfdsM7slOa33eM_ghop" # Get bot token from @BotFather on Telegram + TOKEN = "" # Get bot token from @BotFather on Telegram # Owner's Telegram user ID (Must be an integer) OWNER_ID = 5907205317 @@ -44,11 +53,11 @@ class Config(object): BL_CHATS = [] # User IDs of sudo users, dev users, support users, tiger users, and whitelist users - DRAGONS = [] # Sudo users - DEV_USERS = [] # Dev users - DEMONS = [] # Support users - TIGERS = [] # Tiger users - WOLVES = [] # Whitelist users + DRAGONS = get_user_list("elevated_users.json", "sudos") + DEV_USERS = get_user_list("elevated_users.json", "devs") + DEMONS = get_user_list("elevated_users.json", "supports") + TIGERS = get_user_list("elevated_users.json", "tigers") + WOLVES = get_user_list("elevated_users.json", "whitelists") # Toggle features ALLOW_CHATS = True @@ -62,6 +71,9 @@ class Config(object): # Global ban settings STRICT_GBAN = True + BAN_STICKER = ( + "CAACAgUAAxkBAAEGWC5lloYv1tiI3-KPguoH5YX-RveWugACoQ4AAi4b2FQGdUhawbi91DQE" + ) # Temporary download directory TEMP_DOWNLOAD_DIRECTORY = "./"