Karma commited on
Commit
c7dfe8b
·
1 Parent(s): bd415c3

Add files via upload

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Database/mongodb/db.py +6 -0
  2. Database/mongodb/karma_mongo.py +122 -0
  3. Database/mongodb/mongodb.py +75 -0
  4. Database/mongodb/toggle_mongo.py +50 -0
  5. Database/mongodb/users_db.py +102 -0
  6. Database/mongodb/whispers.py +19 -0
  7. Database/sql/__init__.py +27 -0
  8. Database/sql/antiflood_sql.py +145 -0
  9. Database/sql/approve_sql.py +61 -0
  10. Database/sql/blacklistusers_sql.py +69 -0
  11. Database/sql/connection_sql.py +204 -0
  12. Database/sql/cust_filters_sql.py +299 -0
  13. Database/sql/disable_sql.py +105 -0
  14. Database/sql/global_bans_sql.py +167 -0
  15. Database/sql/kuki_sql.py +42 -0
  16. Database/sql/log_channel_sql.py +156 -0
  17. Database/sql/rules_sql.py +58 -0
  18. Database/sql/userinfo_sql.py +76 -0
  19. Database/sql/users_sql.py +235 -0
  20. Database/sql/welcome_sql.py +428 -0
  21. Dockerfile +12 -0
  22. Extra/Calistoga-Regular.ttf +0 -0
  23. Extra/MutantAcademyStyle.ttf +0 -0
  24. Extra/a.jpg +0 -0
  25. Extra/bgg.jpg +0 -0
  26. Extra/default.ttf +0 -0
  27. Extra/profilepic.png +0 -0
  28. Infamous/karma.py +105 -0
  29. Infamous/temp.py +122 -0
  30. LICENSE +21 -0
  31. Mikobot/__init__.py +254 -0
  32. Mikobot/__main__.py +793 -0
  33. Mikobot/events.py +70 -0
  34. Mikobot/plugins/__init__.py +43 -0
  35. Mikobot/plugins/admin.py +1148 -0
  36. Mikobot/plugins/ai.py +142 -0
  37. Mikobot/plugins/alive.py +57 -0
  38. Mikobot/plugins/anime.py +0 -0
  39. Mikobot/plugins/antinsfw.py +189 -0
  40. Mikobot/plugins/ban.py +1045 -0
  41. Mikobot/plugins/botadmins.py +106 -0
  42. Mikobot/plugins/chatbot.py +188 -0
  43. Mikobot/plugins/connection.py +441 -0
  44. Mikobot/plugins/cosplay.py +37 -0
  45. Mikobot/plugins/couple.py +124 -0
  46. Mikobot/plugins/cust_filters.py +756 -0
  47. Mikobot/plugins/disable.py +376 -0
  48. Mikobot/plugins/extra.py +131 -0
  49. Mikobot/plugins/flood.py +457 -0
  50. Mikobot/plugins/gban.py +543 -0
Database/mongodb/db.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ from motor.motor_asyncio import AsyncIOMotorClient as MongoClient
2
+
3
+ from Mikobot import DB_NAME, MONGO_DB_URI
4
+
5
+ mongo = MongoClient(MONGO_DB_URI)
6
+ dbname = mongo[DB_NAME]
Database/mongodb/karma_mongo.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, Union
2
+
3
+ from pymongo import MongoClient
4
+
5
+ from Mikobot import DB_NAME, MONGO_DB_URI
6
+
7
+ client = MongoClient(MONGO_DB_URI)
8
+ db = client[DB_NAME]
9
+
10
+ coupledb = db.couple
11
+ karmadb = db.karma
12
+
13
+
14
+ async def _get_lovers(chat_id: int):
15
+ lovers = coupledb.find_one({"chat_id": chat_id})
16
+ if lovers:
17
+ lovers = lovers["couple"]
18
+ else:
19
+ lovers = {}
20
+ return lovers
21
+
22
+
23
+ async def get_couple(chat_id: int, date: str):
24
+ lovers = await _get_lovers(chat_id)
25
+ if date in lovers:
26
+ return lovers[date]
27
+ else:
28
+ return False
29
+
30
+
31
+ async def save_couple(chat_id: int, date: str, couple: dict):
32
+ lovers = await _get_lovers(chat_id)
33
+ lovers[date] = couple
34
+ coupledb.update_one({"chat_id": chat_id}, {"$set": {"couple": lovers}}, upsert=True)
35
+
36
+
37
+ async def get_karmas_count() -> dict:
38
+ chats = karmadb.find({"chat_id": {"$lt": 0}})
39
+ if not chats:
40
+ return {}
41
+ chats_count = 0
42
+ karmas_count = 0
43
+ for chat in await chats.to_list(length=1000000):
44
+ for i in chat["karma"]:
45
+ karma_ = chat["karma"][i]["karma"]
46
+ if karma_ > 0:
47
+ karmas_count += karma_
48
+ chats_count += 1
49
+ return {"chats_count": chats_count, "karmas_count": karmas_count}
50
+
51
+
52
+ async def user_global_karma(user_id) -> int:
53
+ chats = karmadb.find({"chat_id": {"$lt": 0}})
54
+ if not chats:
55
+ return 0
56
+ total_karma = 0
57
+ for chat in await chats.to_list(length=1000000):
58
+ karma = await get_karma(chat["chat_id"], await int_to_alpha(user_id))
59
+ if karma and (int(karma["karma"]) > 0):
60
+ total_karma += int(karma["karma"])
61
+ return total_karma
62
+
63
+
64
+ async def get_karmas(chat_id: int) -> Dict[str, int]:
65
+ karma = karmadb.find_one({"chat_id": chat_id})
66
+ if not karma:
67
+ return {}
68
+ return karma["karma"]
69
+
70
+
71
+ async def get_karma(chat_id: int, name: str) -> Union[bool, dict]:
72
+ name = name.lower().strip()
73
+ karmas = await get_karmas(chat_id)
74
+ if name in karmas:
75
+ return karmas[name]
76
+
77
+
78
+ async def update_karma(chat_id: int, name: str, karma: dict):
79
+ name = name.lower().strip()
80
+ karmas = await get_karmas(chat_id)
81
+ karmas[name] = karma
82
+ karmadb.update_one({"chat_id": chat_id}, {"$set": {"karma": karmas}}, upsert=True)
83
+
84
+
85
+ async def is_karma_on(chat_id: int) -> bool:
86
+ chat = karmadb.find_one({"chat_id_toggle": chat_id})
87
+ if not chat:
88
+ return True
89
+ return False
90
+
91
+
92
+ async def karma_on(chat_id: int):
93
+ is_karma = await is_karma_on(chat_id)
94
+ if is_karma:
95
+ return
96
+ return karmadb.delete_one({"chat_id_toggle": chat_id})
97
+
98
+
99
+ async def karma_off(chat_id: int):
100
+ is_karma = await is_karma_on(chat_id)
101
+ if not is_karma:
102
+ return
103
+ return karmadb.insert_one({"chat_id_toggle": chat_id})
104
+
105
+
106
+ async def int_to_alpha(user_id: int) -> str:
107
+ alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
108
+ text = ""
109
+ user_id = str(user_id)
110
+ for i in user_id:
111
+ text += alphabet[int(i)]
112
+ return text
113
+
114
+
115
+ async def alpha_to_int(user_id_alphabet: str) -> int:
116
+ alphabet = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"]
117
+ user_id = ""
118
+ for i in user_id_alphabet:
119
+ index = alphabet.index(i)
120
+ user_id += str(index)
121
+ user_id = int(user_id)
122
+ return user_id
Database/mongodb/mongodb.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sys import exit as exiter
2
+
3
+ from pymongo import MongoClient
4
+ from pymongo.errors import PyMongoError
5
+
6
+ from Mikobot import DB_NAME, LOGGER, MONGO_DB_URI
7
+
8
+ try:
9
+ Mikobot_db_client = MongoClient(MONGO_DB_URI)
10
+ except PyMongoError as f:
11
+ LOGGER.error(f"Error in Mongodb: {f}")
12
+ exiter(1)
13
+ Mikobot_main_db = Mikobot_db_client[DB_NAME]
14
+
15
+
16
+ class MongoDB:
17
+ """Class for interacting with Bot database."""
18
+
19
+ def __init__(self, collection) -> None:
20
+ self.collection = Mikobot_main_db[collection]
21
+
22
+ # Insert one entry into collection
23
+ def insert_one(self, document):
24
+ result = self.collection.insert_one(document)
25
+ return repr(result.inserted_id)
26
+
27
+ # Find one entry from collection
28
+ def find_one(self, query):
29
+ result = self.collection.find_one(query)
30
+ if result:
31
+ return result
32
+ return False
33
+
34
+ # Find entries from collection
35
+ def find_all(self, query=None):
36
+ if query is None:
37
+ query = {}
38
+ return list(self.collection.find(query))
39
+
40
+ # Count entries from collection
41
+ def count(self, query=None):
42
+ if query is None:
43
+ query = {}
44
+ return self.collection.count_documents(query)
45
+
46
+ # Delete entry/entries from collection
47
+ def delete_one(self, query):
48
+ self.collection.delete_many(query)
49
+ return self.collection.count_documents({})
50
+
51
+ # Replace one entry in collection
52
+ def replace(self, query, new_data):
53
+ old = self.collection.find_one(query)
54
+ _id = old["_id"]
55
+ self.collection.replace_one({"_id": _id}, new_data)
56
+ new = self.collection.find_one({"_id": _id})
57
+ return old, new
58
+
59
+ # Update one entry from collection
60
+ def update(self, query, update):
61
+ result = self.collection.update_one(query, {"$set": update})
62
+ new_document = self.collection.find_one(query)
63
+ return result.modified_count, new_document
64
+
65
+ @staticmethod
66
+ def close():
67
+ return Mikobot_db_client.close()
68
+
69
+
70
+ def __connect_first():
71
+ _ = MongoDB("test")
72
+ LOGGER.info("Initialized Mongo Database!\n")
73
+
74
+
75
+ __connect_first()
Database/mongodb/toggle_mongo.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from Database.mongodb.db import *
2
+
3
+ dwelcomedb = dbname.dwelcome
4
+ nsfwdb = dbname.nsfw
5
+ nekomodedb = dbname.nekomode
6
+
7
+
8
+ async def is_dwelcome_on(chat_id: int) -> bool:
9
+ chat = await dwelcomedb.find_one({"chat_id_toggle": chat_id})
10
+ return not bool(chat)
11
+
12
+
13
+ async def dwelcome_on(chat_id: int):
14
+ await dwelcomedb.delete_one({"chat_id_toggle": chat_id})
15
+
16
+
17
+ async def dwelcome_off(chat_id: int):
18
+ await dwelcomedb.insert_one({"chat_id_toggle": chat_id})
19
+
20
+
21
+ async def is_nsfw_on(chat_id: int) -> bool:
22
+ chat = await nsfwdb.find_one({"chat_id": chat_id})
23
+ return chat
24
+
25
+
26
+ async def nsfw_on(chat_id: int):
27
+ is_nsfw = await is_nsfw_on(chat_id)
28
+ if is_nsfw:
29
+ return
30
+ return await nsfwdb.insert_one({"chat_id": chat_id})
31
+
32
+
33
+ async def nsfw_off(chat_id: int):
34
+ is_nsfw = await is_nsfw_on(chat_id)
35
+ if not is_nsfw:
36
+ return
37
+ return await nsfwdb.delete_one({"chat_id": chat_id})
38
+
39
+
40
+ async def is_nekomode_on(chat_id: int) -> bool:
41
+ chat = await nekomodedb.find_one({"chat_id_toggle": chat_id})
42
+ return not bool(chat)
43
+
44
+
45
+ async def nekomode_on(chat_id: int):
46
+ await nekomodedb.delete_one({"chat_id_toggle": chat_id})
47
+
48
+
49
+ async def nekomode_off(chat_id: int):
50
+ await nekomodedb.insert_one({"chat_id_toggle": chat_id})
Database/mongodb/users_db.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from threading import RLock
2
+ from time import time
3
+
4
+ from Database.mongodb.mongodb import MongoDB
5
+ from Mikobot import LOGGER
6
+
7
+ INSERTION_LOCK = RLock()
8
+
9
+
10
+ class Users(MongoDB):
11
+ """Class to manage users for bot."""
12
+
13
+ db_name = "users"
14
+
15
+ def __init__(self, user_id: int) -> None:
16
+ super().__init__(self.db_name)
17
+ self.user_id = user_id
18
+ self.user_info = self.__ensure_in_db()
19
+
20
+ def update_user(self, name: str, username: str = None):
21
+ with INSERTION_LOCK:
22
+ if name != self.user_info["name"] or username != self.user_info["username"]:
23
+ return self.update(
24
+ {"_id": self.user_id},
25
+ {"username": username, "name": name},
26
+ )
27
+ return True
28
+
29
+ def delete_user(self):
30
+ with INSERTION_LOCK:
31
+ return self.delete_one({"_id": self.user_id})
32
+
33
+ @staticmethod
34
+ def count_users():
35
+ with INSERTION_LOCK:
36
+ collection = MongoDB(Users.db_name)
37
+ return collection.count()
38
+
39
+ def get_my_info(self):
40
+ with INSERTION_LOCK:
41
+ return self.user_info
42
+
43
+ @staticmethod
44
+ def list_users():
45
+ with INSERTION_LOCK:
46
+ collection = MongoDB(Users.db_name)
47
+ return collection.find_all()
48
+
49
+ @staticmethod
50
+ def get_user_info(user_id: int or str):
51
+ with INSERTION_LOCK:
52
+ collection = MongoDB(Users.db_name)
53
+ if isinstance(user_id, int):
54
+ curr = collection.find_one({"_id": user_id})
55
+ elif isinstance(user_id, str):
56
+ # user_id[1:] because we don't want the '@' in the username
57
+ # search!
58
+ curr = collection.find_one({"username": user_id[1:]})
59
+ else:
60
+ curr = None
61
+
62
+ if curr:
63
+ return curr
64
+
65
+ return {}
66
+
67
+ def __ensure_in_db(self):
68
+ chat_data = self.find_one({"_id": self.user_id})
69
+ if not chat_data:
70
+ new_data = {"_id": self.user_id, "username": "", "name": "unknown_till_now"}
71
+ self.insert_one(new_data)
72
+ LOGGER.info(f"Initialized User Document for {self.user_id}")
73
+ return new_data
74
+ return chat_data
75
+
76
+ @staticmethod
77
+ def load_from_db():
78
+ with INSERTION_LOCK:
79
+ collection = MongoDB(Users.db_name)
80
+ return collection.find_all()
81
+
82
+ @staticmethod
83
+ def repair_db(collection):
84
+ all_data = collection.find_all()
85
+ keys = {"username": "", "name": "unknown_till_now"}
86
+ for data in all_data:
87
+ for key, val in keys.items():
88
+ try:
89
+ _ = data[key]
90
+ except KeyError:
91
+ LOGGER.warning(
92
+ f"Repairing Users Database - setting '{key}:{val}' for {data['_id']}",
93
+ )
94
+ collection.update({"_id": data["_id"]}, {key: val})
95
+
96
+
97
+ def __pre_req_users():
98
+ start = time()
99
+ LOGGER.info("Starting Users Database Repair...")
100
+ collection = MongoDB(Users.db_name)
101
+ Users.repair_db(collection)
102
+ LOGGER.info(f"Done in {round((time() - start), 3)}s!")
Database/mongodb/whispers.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from Database.mongodb.db import dbname
2
+
3
+ collection = dbname["whisper"]
4
+
5
+
6
+ class Whispers:
7
+ @staticmethod
8
+ async def add_whisper(WhisperId, WhisperData):
9
+ whisper = {"WhisperId": WhisperId, "whisperData": WhisperData}
10
+ await collection.insert_one(whisper)
11
+
12
+ @staticmethod
13
+ async def del_whisper(WhisperId):
14
+ await collection.delete_one({"WhisperId": WhisperId})
15
+
16
+ @staticmethod
17
+ async def get_whisper(WhisperId):
18
+ whisper = await collection.find_one({"WhisperId": WhisperId})
19
+ return whisper["whisperData"] if whisper else None
Database/sql/__init__.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sqlalchemy import create_engine
2
+ from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.orm import scoped_session, sessionmaker
4
+
5
+ from Mikobot import DB_URI
6
+ from Mikobot import LOGGER as log
7
+
8
+ if DB_URI and DB_URI.startswith("postgres://"):
9
+ DB_URI = DB_URI.replace("postgres://", "postgresql://", 1)
10
+
11
+
12
+ def start() -> scoped_session:
13
+ engine = create_engine(DB_URI, client_encoding="utf8")
14
+ log.info("[PostgreSQL] Connecting to database......")
15
+ BASE.metadata.bind = engine
16
+ BASE.metadata.create_all(engine)
17
+ return scoped_session(sessionmaker(bind=engine, autoflush=False))
18
+
19
+
20
+ BASE = declarative_base()
21
+ try:
22
+ SESSION = start()
23
+ except Exception as e:
24
+ log.exception(f"[PostgreSQL] Failed to connect due to {e}")
25
+ exit()
26
+
27
+ log.info("[PostgreSQL] Connection successful, session started.")
Database/sql/antiflood_sql.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import BigInteger, Column, Integer, String, UnicodeText
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+ DEF_COUNT = 1
8
+ DEF_LIMIT = 0
9
+ DEF_OBJ = (None, DEF_COUNT, DEF_LIMIT)
10
+
11
+
12
+ class FloodControl(BASE):
13
+ __tablename__ = "antiflood"
14
+ chat_id = Column(String(14), primary_key=True)
15
+ user_id = Column(BigInteger)
16
+ count = Column(Integer, default=DEF_COUNT)
17
+ limit = Column(Integer, default=DEF_LIMIT)
18
+
19
+ def __init__(self, chat_id):
20
+ self.chat_id = str(chat_id) # ensure string
21
+
22
+ def __repr__(self):
23
+ return "<flood control for %s>" % self.chat_id
24
+
25
+
26
+ class FloodSettings(BASE):
27
+ __tablename__ = "antiflood_settings"
28
+ chat_id = Column(String(14), primary_key=True)
29
+ flood_type = Column(Integer, default=1)
30
+ value = Column(UnicodeText, default="0")
31
+
32
+ def __init__(self, chat_id, flood_type=1, value="0"):
33
+ self.chat_id = str(chat_id)
34
+ self.flood_type = flood_type
35
+ self.value = value
36
+
37
+ def __repr__(self):
38
+ return "<{} will executing {} for flood.>".format(self.chat_id, self.flood_type)
39
+
40
+
41
+ FloodControl.__table__.create(checkfirst=True)
42
+ FloodSettings.__table__.create(checkfirst=True)
43
+
44
+ INSERTION_FLOOD_LOCK = threading.RLock()
45
+ INSERTION_FLOOD_SETTINGS_LOCK = threading.RLock()
46
+
47
+ CHAT_FLOOD = {}
48
+
49
+
50
+ def set_flood(chat_id, amount):
51
+ with INSERTION_FLOOD_LOCK:
52
+ flood = SESSION.query(FloodControl).get(str(chat_id))
53
+ if not flood:
54
+ flood = FloodControl(str(chat_id))
55
+
56
+ flood.user_id = None
57
+ flood.limit = amount
58
+
59
+ CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, amount)
60
+
61
+ SESSION.add(flood)
62
+ SESSION.commit()
63
+
64
+
65
+ def update_flood(chat_id: str, user_id) -> bool:
66
+ if str(chat_id) in CHAT_FLOOD:
67
+ curr_user_id, count, limit = CHAT_FLOOD.get(str(chat_id), DEF_OBJ)
68
+
69
+ if limit == 0: # no antiflood
70
+ return False
71
+
72
+ if user_id != curr_user_id or user_id is None: # other user
73
+ CHAT_FLOOD[str(chat_id)] = (user_id, DEF_COUNT, limit)
74
+ return False
75
+
76
+ count += 1
77
+ if count > limit: # too many msgs, kick
78
+ CHAT_FLOOD[str(chat_id)] = (None, DEF_COUNT, limit)
79
+ return True
80
+
81
+ # default -> update
82
+ CHAT_FLOOD[str(chat_id)] = (user_id, count, limit)
83
+ return False
84
+
85
+
86
+ def get_flood_limit(chat_id):
87
+ return CHAT_FLOOD.get(str(chat_id), DEF_OBJ)[2]
88
+
89
+
90
+ def set_flood_strength(chat_id, flood_type, value):
91
+ # for flood_type
92
+ # 1 = ban
93
+ # 2 = kick
94
+ # 3 = mute
95
+ # 4 = tban
96
+ # 5 = tmute
97
+ with INSERTION_FLOOD_SETTINGS_LOCK:
98
+ curr_setting = SESSION.query(FloodSettings).get(str(chat_id))
99
+ if not curr_setting:
100
+ curr_setting = FloodSettings(
101
+ chat_id,
102
+ flood_type=int(flood_type),
103
+ value=value,
104
+ )
105
+
106
+ curr_setting.flood_type = int(flood_type)
107
+ curr_setting.value = str(value)
108
+
109
+ SESSION.add(curr_setting)
110
+ SESSION.commit()
111
+
112
+
113
+ def get_flood_setting(chat_id):
114
+ try:
115
+ setting = SESSION.query(FloodSettings).get(str(chat_id))
116
+ if setting:
117
+ return setting.flood_type, setting.value
118
+ else:
119
+ return 1, "0"
120
+
121
+ finally:
122
+ SESSION.close()
123
+
124
+
125
+ def migrate_chat(old_chat_id, new_chat_id):
126
+ with INSERTION_FLOOD_LOCK:
127
+ flood = SESSION.query(FloodControl).get(str(old_chat_id))
128
+ if flood:
129
+ CHAT_FLOOD[str(new_chat_id)] = CHAT_FLOOD.get(str(old_chat_id), DEF_OBJ)
130
+ flood.chat_id = str(new_chat_id)
131
+ SESSION.commit()
132
+
133
+ SESSION.close()
134
+
135
+
136
+ def __load_flood_settings():
137
+ global CHAT_FLOOD
138
+ try:
139
+ all_chats = SESSION.query(FloodControl).all()
140
+ CHAT_FLOOD = {chat.chat_id: (None, DEF_COUNT, chat.limit) for chat in all_chats}
141
+ finally:
142
+ SESSION.close()
143
+
144
+
145
+ __load_flood_settings()
Database/sql/approve_sql.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import BigInteger, Column, String
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class Approvals(BASE):
9
+ __tablename__ = "approval"
10
+ chat_id = Column(String(14), primary_key=True)
11
+ user_id = Column(BigInteger, primary_key=True)
12
+
13
+ def __init__(self, chat_id, user_id):
14
+ self.chat_id = str(chat_id) # ensure string
15
+ self.user_id = user_id
16
+
17
+ def __repr__(self):
18
+ return "<Approve %s>" % self.user_id
19
+
20
+
21
+ Approvals.__table__.create(checkfirst=True)
22
+
23
+ APPROVE_INSERTION_LOCK = threading.RLock()
24
+
25
+
26
+ def approve(chat_id, user_id):
27
+ with APPROVE_INSERTION_LOCK:
28
+ approve_user = Approvals(str(chat_id), user_id)
29
+ SESSION.add(approve_user)
30
+ SESSION.commit()
31
+
32
+
33
+ def is_approved(chat_id, user_id):
34
+ try:
35
+ return SESSION.query(Approvals).get((str(chat_id), user_id))
36
+ finally:
37
+ SESSION.close()
38
+
39
+
40
+ def disapprove(chat_id, user_id):
41
+ with APPROVE_INSERTION_LOCK:
42
+ disapprove_user = SESSION.query(Approvals).get((str(chat_id), user_id))
43
+ if disapprove_user:
44
+ SESSION.delete(disapprove_user)
45
+ SESSION.commit()
46
+ return True
47
+ else:
48
+ SESSION.close()
49
+ return False
50
+
51
+
52
+ def list_approved(chat_id):
53
+ try:
54
+ return (
55
+ SESSION.query(Approvals)
56
+ .filter(Approvals.chat_id == str(chat_id))
57
+ .order_by(Approvals.user_id.asc())
58
+ .all()
59
+ )
60
+ finally:
61
+ SESSION.close()
Database/sql/blacklistusers_sql.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import Column, String, UnicodeText
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class BlacklistUsers(BASE):
9
+ __tablename__ = "blacklistusers"
10
+ user_id = Column(String(14), primary_key=True)
11
+ reason = Column(UnicodeText)
12
+
13
+ def __init__(self, user_id, reason=None):
14
+ self.user_id = user_id
15
+ self.reason = reason
16
+
17
+
18
+ BlacklistUsers.__table__.create(checkfirst=True)
19
+
20
+ BLACKLIST_LOCK = threading.RLock()
21
+ BLACKLIST_USERS = set()
22
+
23
+
24
+ def blacklist_user(user_id, reason=None):
25
+ with BLACKLIST_LOCK:
26
+ user = SESSION.query(BlacklistUsers).get(str(user_id))
27
+ if not user:
28
+ user = BlacklistUsers(str(user_id), reason)
29
+ else:
30
+ user.reason = reason
31
+
32
+ SESSION.add(user)
33
+ SESSION.commit()
34
+ __load_blacklist_userid_list()
35
+
36
+
37
+ def unblacklist_user(user_id):
38
+ with BLACKLIST_LOCK:
39
+ user = SESSION.query(BlacklistUsers).get(str(user_id))
40
+ if user:
41
+ SESSION.delete(user)
42
+
43
+ SESSION.commit()
44
+ __load_blacklist_userid_list()
45
+
46
+
47
+ def get_reason(user_id):
48
+ user = SESSION.query(BlacklistUsers).get(str(user_id))
49
+ rep = ""
50
+ if user:
51
+ rep = user.reason
52
+
53
+ SESSION.close()
54
+ return rep
55
+
56
+
57
+ def is_user_blacklisted(user_id):
58
+ return user_id in BLACKLIST_USERS
59
+
60
+
61
+ def __load_blacklist_userid_list():
62
+ global BLACKLIST_USERS
63
+ try:
64
+ BLACKLIST_USERS = {int(x.user_id) for x in SESSION.query(BlacklistUsers).all()}
65
+ finally:
66
+ SESSION.close()
67
+
68
+
69
+ __load_blacklist_userid_list()
Database/sql/connection_sql.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+ import time
3
+ from typing import Union
4
+
5
+ from sqlalchemy import BigInteger, Boolean, Column, String, UnicodeText
6
+
7
+ from Database.sql import BASE, SESSION
8
+
9
+
10
+ class ChatAccessConnectionSettings(BASE):
11
+ __tablename__ = "access_connection"
12
+ chat_id = Column(String(14), primary_key=True)
13
+ allow_connect_to_chat = Column(Boolean, default=True)
14
+
15
+ def __init__(self, chat_id, allow_connect_to_chat):
16
+ self.chat_id = str(chat_id)
17
+ self.allow_connect_to_chat = str(allow_connect_to_chat)
18
+
19
+ def __repr__(self):
20
+ return "<Chat access settings ({}) is {}>".format(
21
+ self.chat_id, self.allow_connect_to_chat
22
+ )
23
+
24
+
25
+ class Connection(BASE):
26
+ __tablename__ = "connection"
27
+ user_id = Column(BigInteger, primary_key=True)
28
+ chat_id = Column(String(14))
29
+
30
+ def __init__(self, user_id, chat_id):
31
+ self.user_id = user_id
32
+ self.chat_id = str(chat_id) # Ensure String
33
+
34
+
35
+ class ConnectionHistory(BASE):
36
+ __tablename__ = "connection_history"
37
+ user_id = Column(BigInteger, primary_key=True)
38
+ chat_id = Column(String(14), primary_key=True)
39
+ chat_name = Column(UnicodeText)
40
+ conn_time = Column(BigInteger)
41
+
42
+ def __init__(self, user_id, chat_id, chat_name, conn_time):
43
+ self.user_id = user_id
44
+ self.chat_id = str(chat_id)
45
+ self.chat_name = str(chat_name)
46
+ self.conn_time = int(conn_time)
47
+
48
+ def __repr__(self):
49
+ return "<connection user {} history {}>".format(self.user_id, self.chat_id)
50
+
51
+
52
+ ChatAccessConnectionSettings.__table__.create(checkfirst=True)
53
+ Connection.__table__.create(checkfirst=True)
54
+ ConnectionHistory.__table__.create(checkfirst=True)
55
+
56
+ CHAT_ACCESS_LOCK = threading.RLock()
57
+ CONNECTION_INSERTION_LOCK = threading.RLock()
58
+ CONNECTION_HISTORY_LOCK = threading.RLock()
59
+
60
+ HISTORY_CONNECT = {}
61
+
62
+
63
+ def allow_connect_to_chat(chat_id: Union[str, int]) -> bool:
64
+ try:
65
+ chat_setting = SESSION.query(ChatAccessConnectionSettings).get(str(chat_id))
66
+ if chat_setting:
67
+ return chat_setting.allow_connect_to_chat
68
+ return False
69
+ finally:
70
+ SESSION.close()
71
+
72
+
73
+ def set_allow_connect_to_chat(chat_id: Union[int, str], setting: bool):
74
+ with CHAT_ACCESS_LOCK:
75
+ chat_setting = SESSION.query(ChatAccessConnectionSettings).get(str(chat_id))
76
+ if not chat_setting:
77
+ chat_setting = ChatAccessConnectionSettings(chat_id, setting)
78
+
79
+ chat_setting.allow_connect_to_chat = setting
80
+ SESSION.add(chat_setting)
81
+ SESSION.commit()
82
+
83
+
84
+ def connect(user_id, chat_id):
85
+ with CONNECTION_INSERTION_LOCK:
86
+ prev = SESSION.query(Connection).get((int(user_id)))
87
+ if prev:
88
+ SESSION.delete(prev)
89
+ connect_to_chat = Connection(int(user_id), chat_id)
90
+ SESSION.add(connect_to_chat)
91
+ SESSION.commit()
92
+ return True
93
+
94
+
95
+ def get_connected_chat(user_id):
96
+ try:
97
+ return SESSION.query(Connection).get((int(user_id)))
98
+ finally:
99
+ SESSION.close()
100
+
101
+
102
+ def curr_connection(chat_id):
103
+ try:
104
+ return SESSION.query(Connection).get((str(chat_id)))
105
+ finally:
106
+ SESSION.close()
107
+
108
+
109
+ def disconnect(user_id):
110
+ with CONNECTION_INSERTION_LOCK:
111
+ disconnect = SESSION.query(Connection).get((int(user_id)))
112
+ if disconnect:
113
+ SESSION.delete(disconnect)
114
+ SESSION.commit()
115
+ return True
116
+ else:
117
+ SESSION.close()
118
+ return False
119
+
120
+
121
+ def add_history_conn(user_id, chat_id, chat_name):
122
+ global HISTORY_CONNECT
123
+ with CONNECTION_HISTORY_LOCK:
124
+ conn_time = int(time.time())
125
+ if HISTORY_CONNECT.get(int(user_id)):
126
+ counting = (
127
+ SESSION.query(ConnectionHistory.user_id)
128
+ .filter(ConnectionHistory.user_id == str(user_id))
129
+ .count()
130
+ )
131
+ getchat_id = {}
132
+ for x in HISTORY_CONNECT[int(user_id)]:
133
+ getchat_id[HISTORY_CONNECT[int(user_id)][x]["chat_id"]] = x
134
+ if chat_id in getchat_id:
135
+ todeltime = getchat_id[str(chat_id)]
136
+ delold = SESSION.query(ConnectionHistory).get(
137
+ (int(user_id), str(chat_id))
138
+ )
139
+ if delold:
140
+ SESSION.delete(delold)
141
+ HISTORY_CONNECT[int(user_id)].pop(todeltime)
142
+ elif counting >= 5:
143
+ todel = list(HISTORY_CONNECT[int(user_id)])
144
+ todel.reverse()
145
+ todel = todel[4:]
146
+ for x in todel:
147
+ chat_old = HISTORY_CONNECT[int(user_id)][x]["chat_id"]
148
+ delold = SESSION.query(ConnectionHistory).get(
149
+ (int(user_id), str(chat_old))
150
+ )
151
+ if delold:
152
+ SESSION.delete(delold)
153
+ HISTORY_CONNECT[int(user_id)].pop(x)
154
+ else:
155
+ HISTORY_CONNECT[int(user_id)] = {}
156
+ delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_id)))
157
+ if delold:
158
+ SESSION.delete(delold)
159
+ history = ConnectionHistory(int(user_id), str(chat_id), chat_name, conn_time)
160
+ SESSION.add(history)
161
+ SESSION.commit()
162
+ HISTORY_CONNECT[int(user_id)][conn_time] = {
163
+ "chat_name": chat_name,
164
+ "chat_id": str(chat_id),
165
+ }
166
+
167
+
168
+ def get_history_conn(user_id):
169
+ if not HISTORY_CONNECT.get(int(user_id)):
170
+ HISTORY_CONNECT[int(user_id)] = {}
171
+ return HISTORY_CONNECT[int(user_id)]
172
+
173
+
174
+ def clear_history_conn(user_id):
175
+ global HISTORY_CONNECT
176
+ todel = list(HISTORY_CONNECT[int(user_id)])
177
+ for x in todel:
178
+ chat_old = HISTORY_CONNECT[int(user_id)][x]["chat_id"]
179
+ delold = SESSION.query(ConnectionHistory).get((int(user_id), str(chat_old)))
180
+ if delold:
181
+ SESSION.delete(delold)
182
+ HISTORY_CONNECT[int(user_id)].pop(x)
183
+ SESSION.commit()
184
+ return True
185
+
186
+
187
+ def __load_user_history():
188
+ global HISTORY_CONNECT
189
+ try:
190
+ qall = SESSION.query(ConnectionHistory).all()
191
+ HISTORY_CONNECT = {}
192
+ for x in qall:
193
+ check = HISTORY_CONNECT.get(x.user_id)
194
+ if check is None:
195
+ HISTORY_CONNECT[x.user_id] = {}
196
+ HISTORY_CONNECT[x.user_id][x.conn_time] = {
197
+ "chat_name": x.chat_name,
198
+ "chat_id": x.chat_id,
199
+ }
200
+ finally:
201
+ SESSION.close()
202
+
203
+
204
+ __load_user_history()
Database/sql/cust_filters_sql.py ADDED
@@ -0,0 +1,299 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import Boolean, Column, Integer, String, UnicodeText, distinct, func
4
+
5
+ from Database.sql import BASE, SESSION
6
+ from Mikobot.plugins.helper_funcs.msg_types import Types
7
+
8
+
9
+ class CustomFilters(BASE):
10
+ __tablename__ = "cust_filters"
11
+ chat_id = Column(String(14), primary_key=True)
12
+ keyword = Column(UnicodeText, primary_key=True, nullable=False)
13
+ reply = Column(UnicodeText, nullable=False)
14
+ reply_text = Column(UnicodeText)
15
+ file_type = Column(Integer, nullable=False, default=1)
16
+ file_id = Column(UnicodeText, default=None)
17
+ has_buttons = Column(Boolean, nullable=False, default=False)
18
+ # NOTE: Here for legacy purposes, to ensure older filters don't mess up.
19
+ has_media_spoiler = Column(Boolean, nullable=False, default=False)
20
+
21
+ def __init__(
22
+ self,
23
+ chat_id: int | str,
24
+ keyword: str,
25
+ reply: str,
26
+ reply_text: str,
27
+ has_buttons: bool,
28
+ has_media_spoiler: bool,
29
+ file_type: int,
30
+ file_id: str,
31
+ ):
32
+ self.chat_id = str(chat_id) # ensure string
33
+ self.keyword = keyword
34
+ self.reply = reply
35
+ self.reply_text = reply_text
36
+ self.has_buttons = has_buttons
37
+ self.has_media_spoiler = has_media_spoiler
38
+ self.file_type = file_type
39
+ self.file_id = file_id
40
+
41
+ def __repr__(self):
42
+ return "<Filter for %s>" % self.chat_id
43
+
44
+ def __eq__(self, other):
45
+ return bool(
46
+ isinstance(other, CustomFilters)
47
+ and self.chat_id == other.chat_id
48
+ and self.keyword == other.keyword,
49
+ )
50
+
51
+
52
+ class Buttons(BASE):
53
+ __tablename__ = "cust_filter_urls"
54
+ id = Column(Integer, primary_key=True, autoincrement=True)
55
+ chat_id = Column(String(14), primary_key=True)
56
+ keyword = Column(UnicodeText, primary_key=True)
57
+ name = Column(UnicodeText, nullable=False)
58
+ url = Column(UnicodeText, nullable=False)
59
+ same_line = Column(Boolean, default=False)
60
+
61
+ def __init__(self, chat_id, keyword, name, url, same_line=False):
62
+ self.chat_id = str(chat_id)
63
+ self.keyword = keyword
64
+ self.name = name
65
+ self.url = url
66
+ self.same_line = same_line
67
+
68
+
69
+ CustomFilters.__table__.create(checkfirst=True)
70
+ Buttons.__table__.create(checkfirst=True)
71
+
72
+ CUST_FILT_LOCK = threading.RLock()
73
+ BUTTON_LOCK = threading.RLock()
74
+ CHAT_FILTERS = {}
75
+
76
+
77
+ def get_all_filters():
78
+ try:
79
+ return SESSION.query(CustomFilters).all()
80
+ finally:
81
+ SESSION.close()
82
+
83
+
84
+ def new_add_filter(
85
+ chat_id, keyword, reply_text, file_type, file_id, buttons, media_spoiler
86
+ ):
87
+ global CHAT_FILTERS
88
+
89
+ if buttons is None:
90
+ buttons = []
91
+
92
+ with CUST_FILT_LOCK:
93
+ prev = SESSION.query(CustomFilters).get((str(chat_id), keyword))
94
+ if prev:
95
+ with BUTTON_LOCK:
96
+ prev_buttons = (
97
+ SESSION.query(Buttons)
98
+ .filter(Buttons.chat_id == str(chat_id), Buttons.keyword == keyword)
99
+ .all()
100
+ )
101
+ for btn in prev_buttons:
102
+ SESSION.delete(btn)
103
+ SESSION.delete(prev)
104
+
105
+ filt = CustomFilters(
106
+ str(chat_id),
107
+ keyword,
108
+ reply="there is should be a new reply",
109
+ has_buttons=bool(buttons),
110
+ has_media_spoiler=media_spoiler,
111
+ reply_text=reply_text,
112
+ file_type=file_type.value,
113
+ file_id=file_id,
114
+ )
115
+
116
+ if keyword not in CHAT_FILTERS.get(str(chat_id), []):
117
+ CHAT_FILTERS[str(chat_id)] = sorted(
118
+ CHAT_FILTERS.get(str(chat_id), []) + [keyword],
119
+ key=lambda x: (-len(x), x),
120
+ )
121
+
122
+ SESSION.add(filt)
123
+ SESSION.commit()
124
+
125
+ for b_name, url, same_line in buttons:
126
+ add_note_button_to_db(chat_id, keyword, b_name, url, same_line)
127
+
128
+
129
+ def remove_filter(chat_id, keyword):
130
+ global CHAT_FILTERS
131
+ with CUST_FILT_LOCK:
132
+ filt = SESSION.query(CustomFilters).get((str(chat_id), keyword))
133
+ if filt:
134
+ if keyword in CHAT_FILTERS.get(str(chat_id), []): # Sanity check
135
+ CHAT_FILTERS.get(str(chat_id), []).remove(keyword)
136
+
137
+ with BUTTON_LOCK:
138
+ prev_buttons = (
139
+ SESSION.query(Buttons)
140
+ .filter(Buttons.chat_id == str(chat_id), Buttons.keyword == keyword)
141
+ .all()
142
+ )
143
+ for btn in prev_buttons:
144
+ SESSION.delete(btn)
145
+
146
+ SESSION.delete(filt)
147
+ SESSION.commit()
148
+ return True
149
+
150
+ SESSION.close()
151
+ return False
152
+
153
+
154
+ def get_chat_triggers(chat_id):
155
+ return CHAT_FILTERS.get(str(chat_id), set())
156
+
157
+
158
+ def get_chat_filters(chat_id):
159
+ try:
160
+ return (
161
+ SESSION.query(CustomFilters)
162
+ .filter(CustomFilters.chat_id == str(chat_id))
163
+ .order_by(func.length(CustomFilters.keyword).desc())
164
+ .order_by(CustomFilters.keyword.asc())
165
+ .all()
166
+ )
167
+ finally:
168
+ SESSION.close()
169
+
170
+
171
+ def get_filter(chat_id, keyword) -> CustomFilters:
172
+ try:
173
+ return SESSION.query(CustomFilters).get((str(chat_id), keyword))
174
+ finally:
175
+ SESSION.close()
176
+
177
+
178
+ def add_note_button_to_db(chat_id, keyword, b_name, url, same_line):
179
+ with BUTTON_LOCK:
180
+ button = Buttons(chat_id, keyword, b_name, url, same_line)
181
+ SESSION.add(button)
182
+ SESSION.commit()
183
+
184
+
185
+ def get_buttons(chat_id, keyword):
186
+ try:
187
+ return (
188
+ SESSION.query(Buttons)
189
+ .filter(Buttons.chat_id == str(chat_id), Buttons.keyword == keyword)
190
+ .order_by(Buttons.id)
191
+ .all()
192
+ )
193
+ finally:
194
+ SESSION.close()
195
+
196
+
197
+ def num_filters():
198
+ try:
199
+ return SESSION.query(CustomFilters).count()
200
+ finally:
201
+ SESSION.close()
202
+
203
+
204
+ def num_chats():
205
+ try:
206
+ return SESSION.query(func.count(distinct(CustomFilters.chat_id))).scalar()
207
+ finally:
208
+ SESSION.close()
209
+
210
+
211
+ def __load_chat_filters():
212
+ global CHAT_FILTERS
213
+ try:
214
+ chats = SESSION.query(CustomFilters.chat_id).distinct().all()
215
+ for (chat_id,) in chats: # remove tuple by ( ,)
216
+ CHAT_FILTERS[chat_id] = []
217
+
218
+ all_filters = SESSION.query(CustomFilters).all()
219
+ for x in all_filters:
220
+ CHAT_FILTERS[x.chat_id] += [x.keyword]
221
+
222
+ CHAT_FILTERS = {
223
+ x: sorted(set(y), key=lambda i: (-len(i), i))
224
+ for x, y in CHAT_FILTERS.items()
225
+ }
226
+
227
+ finally:
228
+ SESSION.close()
229
+
230
+
231
+ # ONLY USE FOR MIGRATE OLD FILTERS TO NEW FILTERS
232
+ def __migrate_filters():
233
+ try:
234
+ all_filters = SESSION.query(CustomFilters).distinct().all()
235
+ for x in all_filters:
236
+ if x.is_document:
237
+ file_type = Types.DOCUMENT
238
+ elif x.is_image:
239
+ file_type = Types.PHOTO
240
+ elif x.is_video:
241
+ file_type = Types.VIDEO
242
+ elif x.is_sticker:
243
+ file_type = Types.STICKER
244
+ elif x.is_audio:
245
+ file_type = Types.AUDIO
246
+ elif x.is_voice:
247
+ file_type = Types.VOICE
248
+ else:
249
+ file_type = Types.TEXT
250
+
251
+ if file_type == Types.TEXT:
252
+ filt = CustomFilters(
253
+ str(x.chat_id),
254
+ x.keyword,
255
+ x.reply,
256
+ file_type.value,
257
+ None,
258
+ )
259
+ else:
260
+ filt = CustomFilters(
261
+ str(x.chat_id),
262
+ x.keyword,
263
+ None,
264
+ file_type.value,
265
+ x.reply,
266
+ )
267
+
268
+ SESSION.add(filt)
269
+ SESSION.commit()
270
+
271
+ finally:
272
+ SESSION.close()
273
+
274
+
275
+ def migrate_chat(old_chat_id, new_chat_id):
276
+ with CUST_FILT_LOCK:
277
+ chat_filters = (
278
+ SESSION.query(CustomFilters)
279
+ .filter(CustomFilters.chat_id == str(old_chat_id))
280
+ .all()
281
+ )
282
+ for filt in chat_filters:
283
+ filt.chat_id = str(new_chat_id)
284
+ SESSION.commit()
285
+ old_filt = CHAT_FILTERS.get(str(old_chat_id))
286
+ if old_filt:
287
+ CHAT_FILTERS[str(new_chat_id)] = old_filt
288
+ del CHAT_FILTERS[str(old_chat_id)]
289
+
290
+ with BUTTON_LOCK:
291
+ chat_buttons = (
292
+ SESSION.query(Buttons).filter(Buttons.chat_id == str(old_chat_id)).all()
293
+ )
294
+ for btn in chat_buttons:
295
+ btn.chat_id = str(new_chat_id)
296
+ SESSION.commit()
297
+
298
+
299
+ __load_chat_filters()
Database/sql/disable_sql.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import Column, String, UnicodeText, distinct, func
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class Disable(BASE):
9
+ __tablename__ = "disabled_commands"
10
+ chat_id = Column(String(14), primary_key=True)
11
+ command = Column(UnicodeText, primary_key=True)
12
+
13
+ def __init__(self, chat_id, command):
14
+ self.chat_id = chat_id
15
+ self.command = command
16
+
17
+ def __repr__(self):
18
+ return "Disabled cmd {} in {}".format(self.command, self.chat_id)
19
+
20
+
21
+ Disable.__table__.create(checkfirst=True)
22
+ DISABLE_INSERTION_LOCK = threading.RLock()
23
+
24
+ DISABLED = {}
25
+
26
+
27
+ def disable_command(chat_id, disable):
28
+ with DISABLE_INSERTION_LOCK:
29
+ disabled = SESSION.query(Disable).get((str(chat_id), disable))
30
+
31
+ if not disabled:
32
+ DISABLED.setdefault(str(chat_id), set()).add(disable)
33
+
34
+ disabled = Disable(str(chat_id), disable)
35
+ SESSION.add(disabled)
36
+ SESSION.commit()
37
+ return True
38
+
39
+ SESSION.close()
40
+ return False
41
+
42
+
43
+ def enable_command(chat_id, enable):
44
+ with DISABLE_INSERTION_LOCK:
45
+ disabled = SESSION.query(Disable).get((str(chat_id), enable))
46
+
47
+ if disabled:
48
+ if enable in DISABLED.get(str(chat_id)): # sanity check
49
+ DISABLED.setdefault(str(chat_id), set()).remove(enable)
50
+
51
+ SESSION.delete(disabled)
52
+ SESSION.commit()
53
+ return True
54
+
55
+ SESSION.close()
56
+ return False
57
+
58
+
59
+ def is_command_disabled(chat_id, cmd):
60
+ return str(cmd).lower() in DISABLED.get(str(chat_id), set())
61
+
62
+
63
+ def get_all_disabled(chat_id):
64
+ return DISABLED.get(str(chat_id), set())
65
+
66
+
67
+ def num_chats():
68
+ try:
69
+ return SESSION.query(func.count(distinct(Disable.chat_id))).scalar()
70
+ finally:
71
+ SESSION.close()
72
+
73
+
74
+ def num_disabled():
75
+ try:
76
+ return SESSION.query(Disable).count()
77
+ finally:
78
+ SESSION.close()
79
+
80
+
81
+ def migrate_chat(old_chat_id, new_chat_id):
82
+ with DISABLE_INSERTION_LOCK:
83
+ chats = SESSION.query(Disable).filter(Disable.chat_id == str(old_chat_id)).all()
84
+ for chat in chats:
85
+ chat.chat_id = str(new_chat_id)
86
+ SESSION.add(chat)
87
+
88
+ if str(old_chat_id) in DISABLED:
89
+ DISABLED[str(new_chat_id)] = DISABLED.get(str(old_chat_id), set())
90
+
91
+ SESSION.commit()
92
+
93
+
94
+ def __load_disabled_commands():
95
+ global DISABLED
96
+ try:
97
+ all_chats = SESSION.query(Disable).all()
98
+ for chat in all_chats:
99
+ DISABLED.setdefault(chat.chat_id, set()).add(chat.command)
100
+
101
+ finally:
102
+ SESSION.close()
103
+
104
+
105
+ __load_disabled_commands()
Database/sql/global_bans_sql.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import BigInteger, Boolean, Column, String, UnicodeText
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class GloballyBannedUsers(BASE):
9
+ __tablename__ = "gbans"
10
+ user_id = Column(BigInteger, primary_key=True)
11
+ name = Column(UnicodeText, nullable=False)
12
+ reason = Column(UnicodeText)
13
+
14
+ def __init__(self, user_id, name, reason=None):
15
+ self.user_id = user_id
16
+ self.name = name
17
+ self.reason = reason
18
+
19
+ def __repr__(self):
20
+ return "<GBanned User {} ({})>".format(self.name, self.user_id)
21
+
22
+ def to_dict(self):
23
+ return {"user_id": self.user_id, "name": self.name, "reason": self.reason}
24
+
25
+
26
+ class GbanSettings(BASE):
27
+ __tablename__ = "gban_settings"
28
+ chat_id = Column(String(14), primary_key=True)
29
+ setting = Column(Boolean, default=True, nullable=False)
30
+
31
+ def __init__(self, chat_id, enabled):
32
+ self.chat_id = str(chat_id)
33
+ self.setting = enabled
34
+
35
+ def __repr__(self):
36
+ return "<Gban setting {} ({})>".format(self.chat_id, self.setting)
37
+
38
+
39
+ GloballyBannedUsers.__table__.create(checkfirst=True)
40
+ GbanSettings.__table__.create(checkfirst=True)
41
+
42
+ GBANNED_USERS_LOCK = threading.RLock()
43
+ GBAN_SETTING_LOCK = threading.RLock()
44
+ GBANNED_LIST = set()
45
+ GBANSTAT_LIST = set()
46
+
47
+
48
+ def gban_user(user_id, name, reason=None):
49
+ with GBANNED_USERS_LOCK:
50
+ user = SESSION.query(GloballyBannedUsers).get(user_id)
51
+ if not user:
52
+ user = GloballyBannedUsers(user_id, name, reason)
53
+ else:
54
+ user.name = name
55
+ user.reason = reason
56
+
57
+ SESSION.merge(user)
58
+ SESSION.commit()
59
+ __load_gbanned_userid_list()
60
+
61
+
62
+ def update_gban_reason(user_id, name, reason=None):
63
+ with GBANNED_USERS_LOCK:
64
+ user = SESSION.query(GloballyBannedUsers).get(user_id)
65
+ if not user:
66
+ return None
67
+ old_reason = user.reason
68
+ user.name = name
69
+ user.reason = reason
70
+
71
+ SESSION.merge(user)
72
+ SESSION.commit()
73
+ return old_reason
74
+
75
+
76
+ def ungban_user(user_id):
77
+ with GBANNED_USERS_LOCK:
78
+ user = SESSION.query(GloballyBannedUsers).get(user_id)
79
+ if user:
80
+ SESSION.delete(user)
81
+
82
+ SESSION.commit()
83
+ __load_gbanned_userid_list()
84
+
85
+
86
+ def is_user_gbanned(user_id):
87
+ return user_id in GBANNED_LIST
88
+
89
+
90
+ def get_gbanned_user(user_id):
91
+ try:
92
+ return SESSION.query(GloballyBannedUsers).get(user_id)
93
+ finally:
94
+ SESSION.close()
95
+
96
+
97
+ def get_gban_list():
98
+ try:
99
+ return [x.to_dict() for x in SESSION.query(GloballyBannedUsers).all()]
100
+ finally:
101
+ SESSION.close()
102
+
103
+
104
+ def enable_gbans(chat_id):
105
+ with GBAN_SETTING_LOCK:
106
+ chat = SESSION.query(GbanSettings).get(str(chat_id))
107
+ if not chat:
108
+ chat = GbanSettings(chat_id, True)
109
+
110
+ chat.setting = True
111
+ SESSION.add(chat)
112
+ SESSION.commit()
113
+ if str(chat_id) in GBANSTAT_LIST:
114
+ GBANSTAT_LIST.remove(str(chat_id))
115
+
116
+
117
+ def disable_gbans(chat_id):
118
+ with GBAN_SETTING_LOCK:
119
+ chat = SESSION.query(GbanSettings).get(str(chat_id))
120
+ if not chat:
121
+ chat = GbanSettings(chat_id, False)
122
+
123
+ chat.setting = False
124
+ SESSION.add(chat)
125
+ SESSION.commit()
126
+ GBANSTAT_LIST.add(str(chat_id))
127
+
128
+
129
+ def does_chat_gban(chat_id):
130
+ return str(chat_id) not in GBANSTAT_LIST
131
+
132
+
133
+ def num_gbanned_users():
134
+ return len(GBANNED_LIST)
135
+
136
+
137
+ def __load_gbanned_userid_list():
138
+ global GBANNED_LIST
139
+ try:
140
+ GBANNED_LIST = {x.user_id for x in SESSION.query(GloballyBannedUsers).all()}
141
+ finally:
142
+ SESSION.close()
143
+
144
+
145
+ def __load_gban_stat_list():
146
+ global GBANSTAT_LIST
147
+ try:
148
+ GBANSTAT_LIST = {
149
+ x.chat_id for x in SESSION.query(GbanSettings).all() if not x.setting
150
+ }
151
+ finally:
152
+ SESSION.close()
153
+
154
+
155
+ def migrate_chat(old_chat_id, new_chat_id):
156
+ with GBAN_SETTING_LOCK:
157
+ chat = SESSION.query(GbanSettings).get(str(old_chat_id))
158
+ if chat:
159
+ chat.chat_id = new_chat_id
160
+ SESSION.add(chat)
161
+
162
+ SESSION.commit()
163
+
164
+
165
+ # Create in memory userid to avoid disk access
166
+ __load_gbanned_userid_list()
167
+ __load_gban_stat_list()
Database/sql/kuki_sql.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import Column, String
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class KukiChats(BASE):
9
+ __tablename__ = "kuki_chats"
10
+ chat_id = Column(String(14), primary_key=True)
11
+
12
+ def __init__(self, chat_id):
13
+ self.chat_id = chat_id
14
+
15
+
16
+ KukiChats.__table__.create(checkfirst=True)
17
+ INSERTION_LOCK = threading.RLock()
18
+
19
+
20
+ def is_kuki(chat_id):
21
+ try:
22
+ chat = SESSION.query(KukiChats).get(str(chat_id))
23
+ return bool(chat)
24
+ finally:
25
+ SESSION.close()
26
+
27
+
28
+ def set_kuki(chat_id):
29
+ with INSERTION_LOCK:
30
+ kukichat = SESSION.query(KukiChats).get(str(chat_id))
31
+ if not kukichat:
32
+ kukichat = KukiChats(str(chat_id))
33
+ SESSION.add(kukichat)
34
+ SESSION.commit()
35
+
36
+
37
+ def rem_kuki(chat_id):
38
+ with INSERTION_LOCK:
39
+ kukichat = SESSION.query(KukiChats).get(str(chat_id))
40
+ if kukichat:
41
+ SESSION.delete(kukichat)
42
+ SESSION.commit()
Database/sql/log_channel_sql.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+ import typing
3
+
4
+ from sqlalchemy import BigInteger, Boolean, Column, String, distinct, func
5
+
6
+ from Database.sql import BASE, SESSION
7
+
8
+
9
+ class GroupLogs(BASE):
10
+ __tablename__ = "log_channels"
11
+ chat_id = Column(String(14), primary_key=True)
12
+ log_channel = Column(String(14), nullable=False)
13
+
14
+ def __init__(self, chat_id, log_channel):
15
+ self.chat_id = str(chat_id)
16
+ self.log_channel = str(log_channel)
17
+
18
+
19
+ class LogChannelSettings(BASE):
20
+ __tablename__ = "log_channel_setting"
21
+ chat_id = Column(BigInteger, primary_key=True)
22
+ log_joins = Column(Boolean, default=True)
23
+ log_leave = Column(Boolean, default=True)
24
+ log_warn = Column(Boolean, default=True)
25
+ log_action = Column(Boolean, default=True)
26
+ # log_media = Column(Boolean)
27
+ log_report = Column(Boolean, default=True)
28
+
29
+ def __init__(
30
+ self,
31
+ chat_id: int,
32
+ log_join: bool,
33
+ log_leave: bool,
34
+ log_warn: bool,
35
+ log_action: bool,
36
+ log_report: bool,
37
+ ):
38
+ self.chat_id = chat_id
39
+ self.log_warn = log_warn
40
+ self.log_joins = log_join
41
+ self.log_leave = log_leave
42
+ self.log_report = log_report
43
+ self.log_action = log_action
44
+
45
+ def toggle_warn(self) -> bool:
46
+ self.log_warn = not self.log_warn
47
+ SESSION.commit()
48
+ return self.log_warn
49
+
50
+ def toggle_joins(self) -> bool:
51
+ self.log_joins = not self.log_joins
52
+ SESSION.commit()
53
+ return self.log_joins
54
+
55
+ def toggle_leave(self) -> bool:
56
+ self.log_leave = not self.log_leave
57
+ SESSION.commit()
58
+ return self.log_leave
59
+
60
+ def toggle_report(self) -> bool:
61
+ self.log_report = not self.log_report
62
+ SESSION.commit()
63
+ return self.log_report
64
+
65
+ def toggle_action(self) -> bool:
66
+ self.log_action = not self.log_action
67
+ SESSION.commit()
68
+ return self.log_action
69
+
70
+
71
+ GroupLogs.__table__.create(checkfirst=True)
72
+ LogChannelSettings.__table__.create(checkfirst=True)
73
+
74
+ LOGS_INSERTION_LOCK = threading.RLock()
75
+ LOG_SETTING_LOCK = threading.RLock()
76
+ CHANNELS = {}
77
+
78
+
79
+ def get_chat_setting(chat_id: int) -> typing.Optional[LogChannelSettings]:
80
+ with LOG_SETTING_LOCK:
81
+ return SESSION.query(LogChannelSettings).get(chat_id)
82
+
83
+
84
+ def set_chat_setting(setting: LogChannelSettings):
85
+ with LOGS_INSERTION_LOCK:
86
+ res: LogChannelSettings = SESSION.query(LogChannelSettings).get(setting.chat_id)
87
+ if res:
88
+ res.log_warn = setting.log_warn
89
+ res.log_action = setting.log_action
90
+ res.log_report = setting.log_report
91
+ res.log_joins = setting.log_joins
92
+ res.log_leave = setting.log_leave
93
+ else:
94
+ SESSION.add(setting)
95
+ SESSION.commit()
96
+
97
+
98
+ def set_chat_log_channel(chat_id, log_channel):
99
+ with LOGS_INSERTION_LOCK:
100
+ res = SESSION.query(GroupLogs).get(str(chat_id))
101
+ if res:
102
+ res.log_channel = log_channel
103
+ else:
104
+ res = GroupLogs(chat_id, log_channel)
105
+ SESSION.add(res)
106
+
107
+ CHANNELS[str(chat_id)] = log_channel
108
+ SESSION.commit()
109
+
110
+
111
+ def get_chat_log_channel(chat_id):
112
+ return CHANNELS.get(str(chat_id))
113
+
114
+
115
+ def stop_chat_logging(chat_id):
116
+ with LOGS_INSERTION_LOCK:
117
+ res = SESSION.query(GroupLogs).get(str(chat_id))
118
+ if res:
119
+ if str(chat_id) in CHANNELS:
120
+ del CHANNELS[str(chat_id)]
121
+
122
+ log_channel = res.log_channel
123
+ SESSION.delete(res)
124
+ SESSION.commit()
125
+ return log_channel
126
+
127
+
128
+ def num_logchannels():
129
+ try:
130
+ return SESSION.query(func.count(distinct(GroupLogs.chat_id))).scalar()
131
+ finally:
132
+ SESSION.close()
133
+
134
+
135
+ def migrate_chat(old_chat_id, new_chat_id):
136
+ with LOGS_INSERTION_LOCK:
137
+ chat = SESSION.query(GroupLogs).get(str(old_chat_id))
138
+ if chat:
139
+ chat.chat_id = str(new_chat_id)
140
+ SESSION.add(chat)
141
+ if str(old_chat_id) in CHANNELS:
142
+ CHANNELS[str(new_chat_id)] = CHANNELS.get(str(old_chat_id))
143
+
144
+ SESSION.commit()
145
+
146
+
147
+ def __load_log_channels():
148
+ global CHANNELS
149
+ try:
150
+ all_chats = SESSION.query(GroupLogs).all()
151
+ CHANNELS = {chat.chat_id: chat.log_channel for chat in all_chats}
152
+ finally:
153
+ SESSION.close()
154
+
155
+
156
+ __load_log_channels()
Database/sql/rules_sql.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import Column, String, UnicodeText, distinct, func
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class Rules(BASE):
9
+ __tablename__ = "rules"
10
+ chat_id = Column(String(14), primary_key=True)
11
+ rules = Column(UnicodeText, default="")
12
+
13
+ def __init__(self, chat_id):
14
+ self.chat_id = chat_id
15
+
16
+ def __repr__(self):
17
+ return "<Chat {} rules: {}>".format(self.chat_id, self.rules)
18
+
19
+
20
+ Rules.__table__.create(checkfirst=True)
21
+
22
+ INSERTION_LOCK = threading.RLock()
23
+
24
+
25
+ def set_rules(chat_id, rules_text):
26
+ with INSERTION_LOCK:
27
+ rules = SESSION.query(Rules).get(str(chat_id))
28
+ if not rules:
29
+ rules = Rules(str(chat_id))
30
+ rules.rules = rules_text
31
+
32
+ SESSION.add(rules)
33
+ SESSION.commit()
34
+
35
+
36
+ def get_rules(chat_id):
37
+ rules = SESSION.query(Rules).get(str(chat_id))
38
+ ret = ""
39
+ if rules:
40
+ ret = rules.rules
41
+
42
+ SESSION.close()
43
+ return ret
44
+
45
+
46
+ def num_chats():
47
+ try:
48
+ return SESSION.query(func.count(distinct(Rules.chat_id))).scalar()
49
+ finally:
50
+ SESSION.close()
51
+
52
+
53
+ def migrate_chat(old_chat_id, new_chat_id):
54
+ with INSERTION_LOCK:
55
+ chat = SESSION.query(Rules).get(str(old_chat_id))
56
+ if chat:
57
+ chat.chat_id = str(new_chat_id)
58
+ SESSION.commit()
Database/sql/userinfo_sql.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import BigInteger, Column, UnicodeText
4
+
5
+ from Database.sql import BASE, SESSION
6
+
7
+
8
+ class UserInfo(BASE):
9
+ __tablename__ = "userinfo"
10
+ user_id = Column(BigInteger, primary_key=True)
11
+ info = Column(UnicodeText)
12
+
13
+ def __init__(self, user_id, info):
14
+ self.user_id = user_id
15
+ self.info = info
16
+
17
+ def __repr__(self):
18
+ return "<User info %d>" % self.user_id
19
+
20
+
21
+ class UserBio(BASE):
22
+ __tablename__ = "userbio"
23
+ user_id = Column(BigInteger, primary_key=True)
24
+ bio = Column(UnicodeText)
25
+
26
+ def __init__(self, user_id, bio):
27
+ self.user_id = user_id
28
+ self.bio = bio
29
+
30
+ def __repr__(self):
31
+ return "<User info %d>" % self.user_id
32
+
33
+
34
+ UserInfo.__table__.create(checkfirst=True)
35
+ UserBio.__table__.create(checkfirst=True)
36
+
37
+ INSERTION_LOCK = threading.RLock()
38
+
39
+
40
+ def get_user_me_info(user_id):
41
+ userinfo = SESSION.query(UserInfo).get(user_id)
42
+ SESSION.close()
43
+ if userinfo:
44
+ return userinfo.info
45
+ return None
46
+
47
+
48
+ def set_user_me_info(user_id, info):
49
+ with INSERTION_LOCK:
50
+ userinfo = SESSION.query(UserInfo).get(user_id)
51
+ if userinfo:
52
+ userinfo.info = info
53
+ else:
54
+ userinfo = UserInfo(user_id, info)
55
+ SESSION.add(userinfo)
56
+ SESSION.commit()
57
+
58
+
59
+ def get_user_bio(user_id):
60
+ userbio = SESSION.query(UserBio).get(user_id)
61
+ SESSION.close()
62
+ if userbio:
63
+ return userbio.bio
64
+ return None
65
+
66
+
67
+ def set_user_bio(user_id, bio):
68
+ with INSERTION_LOCK:
69
+ userbio = SESSION.query(UserBio).get(user_id)
70
+ if userbio:
71
+ userbio.bio = bio
72
+ else:
73
+ userbio = UserBio(user_id, bio)
74
+
75
+ SESSION.add(userbio)
76
+ SESSION.commit()
Database/sql/users_sql.py ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import threading
2
+
3
+ from sqlalchemy import (
4
+ BigInteger,
5
+ Column,
6
+ ForeignKey,
7
+ Integer,
8
+ String,
9
+ UnicodeText,
10
+ UniqueConstraint,
11
+ func,
12
+ )
13
+
14
+ from Database.sql import BASE, SESSION
15
+ from Mikobot import dispatcher
16
+
17
+
18
+ class Users(BASE):
19
+ __tablename__ = "users"
20
+ user_id = Column(BigInteger, primary_key=True)
21
+ username = Column(UnicodeText)
22
+
23
+ def __init__(self, user_id, username=None):
24
+ self.user_id = user_id
25
+ self.username = username
26
+
27
+ def __repr__(self):
28
+ return "<User {} ({})>".format(self.username, self.user_id)
29
+
30
+
31
+ class Chats(BASE):
32
+ __tablename__ = "chats"
33
+ chat_id = Column(String(14), primary_key=True)
34
+ chat_name = Column(UnicodeText, nullable=False)
35
+
36
+ def __init__(self, chat_id, chat_name):
37
+ self.chat_id = str(chat_id)
38
+ self.chat_name = chat_name
39
+
40
+ def __repr__(self):
41
+ return "<Chat {} ({})>".format(self.chat_name, self.chat_id)
42
+
43
+
44
+ class ChatMembers(BASE):
45
+ __tablename__ = "chat_members"
46
+ priv_chat_id = Column(Integer, primary_key=True)
47
+ # NOTE: Use dual primary key instead of private primary key?
48
+ chat = Column(
49
+ String(14),
50
+ ForeignKey("chats.chat_id", onupdate="CASCADE", ondelete="CASCADE"),
51
+ nullable=False,
52
+ )
53
+ user = Column(
54
+ BigInteger,
55
+ ForeignKey("users.user_id", onupdate="CASCADE", ondelete="CASCADE"),
56
+ nullable=False,
57
+ )
58
+ __table_args__ = (UniqueConstraint("chat", "user", name="_chat_members_uc"),)
59
+
60
+ def __init__(self, chat, user):
61
+ self.chat = chat
62
+ self.user = user
63
+
64
+ def __repr__(self):
65
+ return "<Chat user {} ({}) in chat {} ({})>".format(
66
+ self.user.username,
67
+ self.user.user_id,
68
+ self.chat.chat_name,
69
+ self.chat.chat_id,
70
+ )
71
+
72
+
73
+ Users.__table__.create(checkfirst=True)
74
+ Chats.__table__.create(checkfirst=True)
75
+ ChatMembers.__table__.create(checkfirst=True)
76
+
77
+ INSERTION_LOCK = threading.RLock()
78
+
79
+
80
+ def ensure_bot_in_db():
81
+ with INSERTION_LOCK:
82
+ bot = Users(dispatcher.bot.id, dispatcher.bot.username)
83
+ SESSION.merge(bot)
84
+ SESSION.commit()
85
+
86
+
87
+ def update_user(user_id, username, chat_id=None, chat_name=None):
88
+ with INSERTION_LOCK:
89
+ user = SESSION.query(Users).get(user_id)
90
+ if not user:
91
+ user = Users(user_id, username)
92
+ SESSION.add(user)
93
+ SESSION.flush()
94
+ else:
95
+ user.username = username
96
+
97
+ if not chat_id or not chat_name:
98
+ SESSION.commit()
99
+ return
100
+
101
+ chat = SESSION.query(Chats).get(str(chat_id))
102
+ if not chat:
103
+ chat = Chats(str(chat_id), chat_name)
104
+ SESSION.add(chat)
105
+ SESSION.flush()
106
+
107
+ else:
108
+ chat.chat_name = chat_name
109
+
110
+ member = (
111
+ SESSION.query(ChatMembers)
112
+ .filter(ChatMembers.chat == chat.chat_id, ChatMembers.user == user.user_id)
113
+ .first()
114
+ )
115
+ if not member:
116
+ chat_member = ChatMembers(chat.chat_id, user.user_id)
117
+ SESSION.add(chat_member)
118
+
119
+ SESSION.commit()
120
+
121
+
122
+ def get_userid_by_name(username):
123
+ try:
124
+ return (
125
+ SESSION.query(Users)
126
+ .filter(func.lower(Users.username) == username.lower())
127
+ .all()
128
+ )
129
+ finally:
130
+ SESSION.close()
131
+
132
+
133
+ def get_name_by_userid(user_id):
134
+ try:
135
+ return SESSION.query(Users).get(Users.user_id == int(user_id)).first()
136
+ finally:
137
+ SESSION.close()
138
+
139
+
140
+ def get_chat_members(chat_id):
141
+ try:
142
+ return SESSION.query(ChatMembers).filter(ChatMembers.chat == str(chat_id)).all()
143
+ finally:
144
+ SESSION.close()
145
+
146
+
147
+ def get_all_chats():
148
+ try:
149
+ return SESSION.query(Chats).all()
150
+ finally:
151
+ SESSION.close()
152
+
153
+
154
+ def get_all_users():
155
+ try:
156
+ return SESSION.query(Users).all()
157
+ finally:
158
+ SESSION.close()
159
+
160
+
161
+ def get_user_num_chats(user_id):
162
+ try:
163
+ return (
164
+ SESSION.query(ChatMembers).filter(ChatMembers.user == int(user_id)).count()
165
+ )
166
+ finally:
167
+ SESSION.close()
168
+
169
+
170
+ def get_user_com_chats(user_id):
171
+ try:
172
+ chat_members = (
173
+ SESSION.query(ChatMembers).filter(ChatMembers.user == int(user_id)).all()
174
+ )
175
+ return [i.chat for i in chat_members]
176
+ finally:
177
+ SESSION.close()
178
+
179
+
180
+ def num_chats():
181
+ try:
182
+ return SESSION.query(Chats).count()
183
+ finally:
184
+ SESSION.close()
185
+
186
+
187
+ def num_users():
188
+ try:
189
+ return SESSION.query(Users).count()
190
+ finally:
191
+ SESSION.close()
192
+
193
+
194
+ def migrate_chat(old_chat_id, new_chat_id):
195
+ with INSERTION_LOCK:
196
+ chat = SESSION.query(Chats).get(str(old_chat_id))
197
+ if chat:
198
+ chat.chat_id = str(new_chat_id)
199
+ SESSION.commit()
200
+
201
+ chat_members = (
202
+ SESSION.query(ChatMembers)
203
+ .filter(ChatMembers.chat == str(old_chat_id))
204
+ .all()
205
+ )
206
+ for member in chat_members:
207
+ member.chat = str(new_chat_id)
208
+ SESSION.commit()
209
+
210
+
211
+ ensure_bot_in_db()
212
+
213
+
214
+ def del_user(user_id):
215
+ with INSERTION_LOCK:
216
+ curr = SESSION.query(Users).get(user_id)
217
+ if curr:
218
+ SESSION.delete(curr)
219
+ SESSION.commit()
220
+ return True
221
+
222
+ ChatMembers.query.filter(ChatMembers.user == user_id).delete()
223
+ SESSION.commit()
224
+ SESSION.close()
225
+ return False
226
+
227
+
228
+ def rem_chat(chat_id):
229
+ with INSERTION_LOCK:
230
+ chat = SESSION.query(Chats).get(str(chat_id))
231
+ if chat:
232
+ SESSION.delete(chat)
233
+ SESSION.commit()
234
+ else:
235
+ SESSION.close()
Database/sql/welcome_sql.py ADDED
@@ -0,0 +1,428 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import random
2
+ import threading
3
+ from typing import Union
4
+
5
+ from sqlalchemy import BigInteger, Boolean, Column, Integer, String, UnicodeText
6
+
7
+ from Database.sql import BASE, SESSION
8
+ from Mikobot.plugins.helper_funcs.msg_types import Types
9
+
10
+ DEFAULT_WELCOME = "Hey {first}, how are you?"
11
+ DEFAULT_GOODBYE = "Nice knowing you!"
12
+
13
+ DEFAULT_WELCOME_MESSAGES = [
14
+ "{first} is here!", # Discord welcome messages copied
15
+ "Ready player {first}",
16
+ "Welcome bro {first}",
17
+ ]
18
+
19
+ DEFAULT_GOODBYE_MESSAGES = [
20
+ "{first} will be missed.",
21
+ "{first} when back ?",
22
+ ]
23
+ # Line 111 to 152 are references from https://bindingofisaac.fandom.com/wiki/Fortune_Telling_Machine
24
+
25
+
26
+ class Welcome(BASE):
27
+ __tablename__ = "welcome_pref"
28
+ chat_id = Column(String(14), primary_key=True)
29
+ should_welcome = Column(Boolean, default=True)
30
+ should_goodbye = Column(Boolean, default=True)
31
+ custom_content = Column(UnicodeText, default=None)
32
+
33
+ custom_welcome = Column(
34
+ UnicodeText,
35
+ default=random.choice(DEFAULT_WELCOME_MESSAGES),
36
+ )
37
+ welcome_type = Column(Integer, default=Types.TEXT.value)
38
+
39
+ custom_leave = Column(UnicodeText, default=random.choice(DEFAULT_GOODBYE_MESSAGES))
40
+ leave_type = Column(Integer, default=Types.TEXT.value)
41
+
42
+ clean_welcome = Column(BigInteger)
43
+
44
+ def __init__(self, chat_id, should_welcome=True, should_goodbye=True):
45
+ self.chat_id = chat_id
46
+ self.should_welcome = should_welcome
47
+ self.should_goodbye = should_goodbye
48
+
49
+ def __repr__(self):
50
+ return "<Chat {} should Welcome new users: {}>".format(
51
+ self.chat_id,
52
+ self.should_welcome,
53
+ )
54
+
55
+
56
+ class WelcomeButtons(BASE):
57
+ __tablename__ = "welcome_urls"
58
+ id = Column(Integer, primary_key=True, autoincrement=True)
59
+ chat_id = Column(String(14), primary_key=True)
60
+ name = Column(UnicodeText, nullable=False)
61
+ url = Column(UnicodeText, nullable=False)
62
+ same_line = Column(Boolean, default=False)
63
+
64
+ def __init__(self, chat_id, name, url, same_line=False):
65
+ self.chat_id = str(chat_id)
66
+ self.name = name
67
+ self.url = url
68
+ self.same_line = same_line
69
+
70
+
71
+ class GoodbyeButtons(BASE):
72
+ __tablename__ = "leave_urls"
73
+ id = Column(Integer, primary_key=True, autoincrement=True)
74
+ chat_id = Column(String(14), primary_key=True)
75
+ name = Column(UnicodeText, nullable=False)
76
+ url = Column(UnicodeText, nullable=False)
77
+ same_line = Column(Boolean, default=False)
78
+
79
+ def __init__(self, chat_id, name, url, same_line=False):
80
+ self.chat_id = str(chat_id)
81
+ self.name = name
82
+ self.url = url
83
+ self.same_line = same_line
84
+
85
+
86
+ class WelcomeMute(BASE):
87
+ __tablename__ = "welcome_mutes"
88
+ chat_id = Column(String(14), primary_key=True)
89
+ welcomemutes = Column(UnicodeText, default=False)
90
+
91
+ def __init__(self, chat_id, welcomemutes):
92
+ self.chat_id = str(chat_id) # ensure string
93
+ self.welcomemutes = welcomemutes
94
+
95
+
96
+ class WelcomeMuteUsers(BASE):
97
+ __tablename__ = "human_checks"
98
+ user_id = Column(BigInteger, primary_key=True)
99
+ chat_id = Column(String(14), primary_key=True)
100
+ human_check = Column(Boolean)
101
+
102
+ def __init__(self, user_id, chat_id, human_check):
103
+ self.user_id = user_id # ensure string
104
+ self.chat_id = str(chat_id)
105
+ self.human_check = human_check
106
+
107
+
108
+ class CleanServiceSetting(BASE):
109
+ __tablename__ = "clean_service"
110
+ chat_id = Column(String(14), primary_key=True)
111
+ clean_service = Column(Boolean, default=True)
112
+
113
+ def __init__(self, chat_id):
114
+ self.chat_id = str(chat_id)
115
+
116
+ def __repr__(self):
117
+ return "<Chat used clean service ({})>".format(self.chat_id)
118
+
119
+
120
+ Welcome.__table__.create(checkfirst=True)
121
+ WelcomeButtons.__table__.create(checkfirst=True)
122
+ GoodbyeButtons.__table__.create(checkfirst=True)
123
+ WelcomeMute.__table__.create(checkfirst=True)
124
+ WelcomeMuteUsers.__table__.create(checkfirst=True)
125
+ CleanServiceSetting.__table__.create(checkfirst=True)
126
+
127
+ INSERTION_LOCK = threading.RLock()
128
+ WELC_BTN_LOCK = threading.RLock()
129
+ LEAVE_BTN_LOCK = threading.RLock()
130
+ WM_LOCK = threading.RLock()
131
+ CS_LOCK = threading.RLock()
132
+
133
+
134
+ def welcome_mutes(chat_id):
135
+ try:
136
+ welcomemutes = SESSION.query(WelcomeMute).get(str(chat_id))
137
+ if welcomemutes:
138
+ return welcomemutes.welcomemutes
139
+ return False
140
+ finally:
141
+ SESSION.close()
142
+
143
+
144
+ def set_welcome_mutes(chat_id, welcomemutes):
145
+ with WM_LOCK:
146
+ prev = SESSION.query(WelcomeMute).get((str(chat_id)))
147
+ if prev:
148
+ SESSION.delete(prev)
149
+ welcome_m = WelcomeMute(str(chat_id), welcomemutes)
150
+ SESSION.add(welcome_m)
151
+ SESSION.commit()
152
+
153
+
154
+ def set_human_checks(user_id, chat_id):
155
+ with INSERTION_LOCK:
156
+ human_check = SESSION.query(WelcomeMuteUsers).get((user_id, str(chat_id)))
157
+ if not human_check:
158
+ human_check = WelcomeMuteUsers(user_id, str(chat_id), True)
159
+
160
+ else:
161
+ human_check.human_check = True
162
+
163
+ SESSION.add(human_check)
164
+ SESSION.commit()
165
+
166
+ return human_check
167
+
168
+
169
+ def get_human_checks(user_id, chat_id):
170
+ try:
171
+ human_check = SESSION.query(WelcomeMuteUsers).get((user_id, str(chat_id)))
172
+ if not human_check:
173
+ return None
174
+ human_check = human_check.human_check
175
+ return human_check
176
+ finally:
177
+ SESSION.close()
178
+
179
+
180
+ def get_welc_mutes_pref(chat_id):
181
+ welcomemutes = SESSION.query(WelcomeMute).get(str(chat_id))
182
+ SESSION.close()
183
+
184
+ if welcomemutes:
185
+ return welcomemutes.welcomemutes
186
+
187
+ return False
188
+
189
+
190
+ def get_welc_pref(chat_id):
191
+ welc = SESSION.query(Welcome).get(str(chat_id))
192
+ SESSION.close()
193
+ if welc:
194
+ return (
195
+ welc.should_welcome,
196
+ welc.custom_welcome,
197
+ welc.custom_content,
198
+ welc.welcome_type,
199
+ )
200
+
201
+ else:
202
+ # Welcome by default.
203
+ return True, DEFAULT_WELCOME, None, Types.TEXT
204
+
205
+
206
+ def get_gdbye_pref(chat_id):
207
+ welc = SESSION.query(Welcome).get(str(chat_id))
208
+ SESSION.close()
209
+ if welc:
210
+ return welc.should_goodbye, welc.custom_leave, welc.leave_type
211
+ else:
212
+ # Welcome by default.
213
+ return True, DEFAULT_GOODBYE, Types.TEXT
214
+
215
+
216
+ def set_clean_welcome(chat_id, clean_welcome):
217
+ with INSERTION_LOCK:
218
+ curr = SESSION.query(Welcome).get(str(chat_id))
219
+ if not curr:
220
+ curr = Welcome(str(chat_id))
221
+
222
+ curr.clean_welcome = int(clean_welcome)
223
+
224
+ SESSION.add(curr)
225
+ SESSION.commit()
226
+
227
+
228
+ def get_clean_pref(chat_id):
229
+ welc = SESSION.query(Welcome).get(str(chat_id))
230
+ SESSION.close()
231
+
232
+ if welc:
233
+ return welc.clean_welcome
234
+
235
+ return False
236
+
237
+
238
+ def set_welc_preference(chat_id, should_welcome):
239
+ with INSERTION_LOCK:
240
+ curr = SESSION.query(Welcome).get(str(chat_id))
241
+ if not curr:
242
+ curr = Welcome(str(chat_id), should_welcome=should_welcome)
243
+ else:
244
+ curr.should_welcome = should_welcome
245
+
246
+ SESSION.add(curr)
247
+ SESSION.commit()
248
+
249
+
250
+ def set_gdbye_preference(chat_id, should_goodbye):
251
+ with INSERTION_LOCK:
252
+ curr = SESSION.query(Welcome).get(str(chat_id))
253
+ if not curr:
254
+ curr = Welcome(str(chat_id), should_goodbye=should_goodbye)
255
+ else:
256
+ curr.should_goodbye = should_goodbye
257
+
258
+ SESSION.add(curr)
259
+ SESSION.commit()
260
+
261
+
262
+ def set_custom_welcome(
263
+ chat_id,
264
+ custom_content,
265
+ custom_welcome,
266
+ welcome_type,
267
+ buttons=None,
268
+ ):
269
+ if buttons is None:
270
+ buttons = []
271
+
272
+ with INSERTION_LOCK:
273
+ welcome_settings = SESSION.query(Welcome).get(str(chat_id))
274
+ if not welcome_settings:
275
+ welcome_settings = Welcome(str(chat_id), True)
276
+
277
+ if custom_welcome or custom_content:
278
+ welcome_settings.custom_content = custom_content
279
+ welcome_settings.custom_welcome = custom_welcome
280
+ welcome_settings.welcome_type = welcome_type.value
281
+
282
+ else:
283
+ welcome_settings.custom_welcome = DEFAULT_WELCOME
284
+ welcome_settings.welcome_type = Types.TEXT.value
285
+
286
+ SESSION.add(welcome_settings)
287
+
288
+ with WELC_BTN_LOCK:
289
+ prev_buttons = (
290
+ SESSION.query(WelcomeButtons)
291
+ .filter(WelcomeButtons.chat_id == str(chat_id))
292
+ .all()
293
+ )
294
+ for btn in prev_buttons:
295
+ SESSION.delete(btn)
296
+
297
+ for b_name, url, same_line in buttons:
298
+ button = WelcomeButtons(chat_id, b_name, url, same_line)
299
+ SESSION.add(button)
300
+
301
+ SESSION.commit()
302
+
303
+
304
+ def get_custom_welcome(chat_id):
305
+ welcome_settings = SESSION.query(Welcome).get(str(chat_id))
306
+ ret = DEFAULT_WELCOME
307
+ if welcome_settings and welcome_settings.custom_welcome:
308
+ ret = welcome_settings.custom_welcome
309
+
310
+ SESSION.close()
311
+ return ret
312
+
313
+
314
+ def set_custom_gdbye(chat_id, custom_goodbye, goodbye_type, buttons=None):
315
+ if buttons is None:
316
+ buttons = []
317
+
318
+ with INSERTION_LOCK:
319
+ welcome_settings = SESSION.query(Welcome).get(str(chat_id))
320
+ if not welcome_settings:
321
+ welcome_settings = Welcome(str(chat_id), True)
322
+
323
+ if custom_goodbye:
324
+ welcome_settings.custom_leave = custom_goodbye
325
+ welcome_settings.leave_type = goodbye_type.value
326
+
327
+ else:
328
+ welcome_settings.custom_leave = DEFAULT_GOODBYE
329
+ welcome_settings.leave_type = Types.TEXT.value
330
+
331
+ SESSION.add(welcome_settings)
332
+
333
+ with LEAVE_BTN_LOCK:
334
+ prev_buttons = (
335
+ SESSION.query(GoodbyeButtons)
336
+ .filter(GoodbyeButtons.chat_id == str(chat_id))
337
+ .all()
338
+ )
339
+ for btn in prev_buttons:
340
+ SESSION.delete(btn)
341
+
342
+ for b_name, url, same_line in buttons:
343
+ button = GoodbyeButtons(chat_id, b_name, url, same_line)
344
+ SESSION.add(button)
345
+
346
+ SESSION.commit()
347
+
348
+
349
+ def get_custom_gdbye(chat_id):
350
+ welcome_settings = SESSION.query(Welcome).get(str(chat_id))
351
+ ret = DEFAULT_GOODBYE
352
+ if welcome_settings and welcome_settings.custom_leave:
353
+ ret = welcome_settings.custom_leave
354
+
355
+ SESSION.close()
356
+ return ret
357
+
358
+
359
+ def get_welc_buttons(chat_id):
360
+ try:
361
+ return (
362
+ SESSION.query(WelcomeButtons)
363
+ .filter(WelcomeButtons.chat_id == str(chat_id))
364
+ .order_by(WelcomeButtons.id)
365
+ .all()
366
+ )
367
+ finally:
368
+ SESSION.close()
369
+
370
+
371
+ def get_gdbye_buttons(chat_id):
372
+ try:
373
+ return (
374
+ SESSION.query(GoodbyeButtons)
375
+ .filter(GoodbyeButtons.chat_id == str(chat_id))
376
+ .order_by(GoodbyeButtons.id)
377
+ .all()
378
+ )
379
+ finally:
380
+ SESSION.close()
381
+
382
+
383
+ def clean_service(chat_id: Union[str, int]) -> bool:
384
+ try:
385
+ chat_setting = SESSION.query(CleanServiceSetting).get(str(chat_id))
386
+ if chat_setting:
387
+ return chat_setting.clean_service
388
+ return False
389
+ finally:
390
+ SESSION.close()
391
+
392
+
393
+ def set_clean_service(chat_id: Union[int, str], setting: bool):
394
+ with CS_LOCK:
395
+ chat_setting = SESSION.query(CleanServiceSetting).get(str(chat_id))
396
+ if not chat_setting:
397
+ chat_setting = CleanServiceSetting(chat_id)
398
+
399
+ chat_setting.clean_service = setting
400
+ SESSION.add(chat_setting)
401
+ SESSION.commit()
402
+
403
+
404
+ def migrate_chat(old_chat_id, new_chat_id):
405
+ with INSERTION_LOCK:
406
+ chat = SESSION.query(Welcome).get(str(old_chat_id))
407
+ if chat:
408
+ chat.chat_id = str(new_chat_id)
409
+
410
+ with WELC_BTN_LOCK:
411
+ chat_buttons = (
412
+ SESSION.query(WelcomeButtons)
413
+ .filter(WelcomeButtons.chat_id == str(old_chat_id))
414
+ .all()
415
+ )
416
+ for btn in chat_buttons:
417
+ btn.chat_id = str(new_chat_id)
418
+
419
+ with LEAVE_BTN_LOCK:
420
+ chat_buttons = (
421
+ SESSION.query(GoodbyeButtons)
422
+ .filter(GoodbyeButtons.chat_id == str(old_chat_id))
423
+ .all()
424
+ )
425
+ for btn in chat_buttons:
426
+ btn.chat_id = str(new_chat_id)
427
+
428
+ SESSION.commit()
Dockerfile ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11.6
2
+
3
+ WORKDIR /root/Mikobot
4
+
5
+ COPY . .
6
+
7
+ RUN apt-get install -y ffmpeg python3-pip curl
8
+ RUN pip3 install --upgrade pip setuptools
9
+
10
+ RUN pip install -U -r requirements.txt
11
+
12
+ CMD python3 -m Mikobot
Extra/Calistoga-Regular.ttf ADDED
Binary file (129 kB). View file
 
Extra/MutantAcademyStyle.ttf ADDED
Binary file (56.4 kB). View file
 
Extra/a.jpg ADDED
Extra/bgg.jpg ADDED
Extra/default.ttf ADDED
Binary file (136 kB). View file
 
Extra/profilepic.png ADDED
Infamous/karma.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://github.com/Infamous-Hydra/YaeMiko
2
+ # https://github.com/Team-ProjectCodeX
3
+ # https://t.me/O_okarma
4
+
5
+ # <============================================== IMPORTS =========================================================>
6
+ from pyrogram.types import InlineKeyboardButton as ib
7
+ from telegram import InlineKeyboardButton
8
+
9
+ from Mikobot import BOT_USERNAME, OWNER_ID, SUPPORT_CHAT
10
+
11
+ # <============================================== CONSTANTS =========================================================>
12
+ START_IMG = [
13
+ "https://telegra.ph/file/40b93b46642124605e678.jpg",
14
+ "https://telegra.ph/file/01a2e0cd1b9d03808c546.jpg",
15
+ "https://telegra.ph/file/ed4385c26dcf6de70543f.jpg",
16
+ "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg",
17
+ "https://telegra.ph/file/cce9038f6a9b88eb409b5.jpg",
18
+ "https://telegra.ph/file/262c86393730a609cdade.jpg",
19
+ "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg",
20
+ ]
21
+
22
+ HEY_IMG = "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg"
23
+
24
+ ALIVE_ANIMATION = [
25
+ "https://telegra.ph//file/f9e2b9cdd9324fc39970a.mp4",
26
+ "https://telegra.ph//file/8d4d7d06efebe2f8becd0.mp4",
27
+ "https://telegra.ph//file/c4c2759c5fc04cefd207a.mp4",
28
+ "https://telegra.ph//file/b1fa6609b1c4807255927.mp4",
29
+ "https://telegra.ph//file/f3c7147da6511fbe27c25.mp4",
30
+ "https://telegra.ph//file/39071b73c02e3ff5945ca.mp4",
31
+ "https://telegra.ph//file/8d4d7d06efebe2f8becd0.mp4",
32
+ "https://telegra.ph//file/6efdd8e28756bc2f6e53e.mp4",
33
+ ]
34
+
35
+ BAN_GIFS = [
36
+ "https://telegra.ph//file/85ac1ab12c833afa1a5dd.mp4",
37
+ ]
38
+
39
+
40
+ KICK_GIFS = [
41
+ "https://telegra.ph//file/79a6c527e6e6d530bcdc8.mp4",
42
+ ]
43
+
44
+
45
+ MUTE_GIFS = [
46
+ "https://telegra.ph//file/b4faf6e390d72d286abdf.mp4",
47
+ ]
48
+
49
+ FIRST_PART_TEXT = "✨ *ʜᴇʟʟᴏ* `{}` . . ."
50
+
51
+ PM_START_TEXT = "✨ *ɪ ᴀᴍ ᴍɪᴋᴏ, ᴀ ɢᴇɴꜱʜɪɴ ɪᴍᴘᴀᴄᴛ ᴛʜᴇᴍᴇᴅ ʀᴏʙᴏᴛ ᴡʜɪᴄʜ ᴄᴀɴ ʜᴇʟᴘ ʏᴏᴜ ᴛᴏ ᴍᴀɴᴀɢᴇ ᴀɴᴅ ꜱᴇᴄᴜʀᴇ ʏᴏᴜʀ ɢʀᴏᴜᴘ ᴡɪᴛʜ ʜᴜɢᴇ ɢʀᴏᴜᴘ ᴍᴀɴᴀɢᴇᴍᴇɴᴛ*"
52
+
53
+ START_BTN = [
54
+ [
55
+ InlineKeyboardButton(
56
+ text="⇦ ADD ME ⇨",
57
+ url=f"https://t.me/{BOT_USERNAME}?startgroup=true",
58
+ ),
59
+ ],
60
+ [
61
+ InlineKeyboardButton(text="HELP", callback_data="help_back"),
62
+ ],
63
+ [
64
+ InlineKeyboardButton(text="DETAILS", callback_data="Miko_"),
65
+ InlineKeyboardButton(text="AI", callback_data="ai_handler"),
66
+ InlineKeyboardButton(text="SOURCE", callback_data="git_source"),
67
+ ],
68
+ [
69
+ InlineKeyboardButton(text="CREATOR", url=f"tg://user?id={OWNER_ID}"),
70
+ ],
71
+ ]
72
+
73
+ GROUP_START_BTN = [
74
+ [
75
+ InlineKeyboardButton(
76
+ text="⇦ ADD ME ⇨",
77
+ url=f"https://t.me/{BOT_USERNAME}?startgroup=true",
78
+ ),
79
+ ],
80
+ [
81
+ InlineKeyboardButton(text="SUPPORT", url=f"https://t.me/{SUPPORT_CHAT}"),
82
+ InlineKeyboardButton(text="CREATOR", url=f"tg://user?id={OWNER_ID}"),
83
+ ],
84
+ ]
85
+
86
+ ALIVE_BTN = [
87
+ [
88
+ ib(text="UPDATES", url="https://t.me/Hydra_Updates"),
89
+ ib(text="SUPPORT", url="https://t.me/hydraXsupport"),
90
+ ],
91
+ [
92
+ ib(
93
+ text="⇦ ADD ME ⇨",
94
+ url=f"https://t.me/{BOT_USERNAME}?startgroup=true",
95
+ ),
96
+ ],
97
+ ]
98
+
99
+ HELP_STRINGS = """
100
+ 🫧 *Yae-Miko* 🫧
101
+
102
+ ☉ *Here, you will find a list of all the available commands.*
103
+
104
+ ᴀʟʟ ᴄᴏᴍᴍᴀɴᴅs ᴄᴀɴ ʙᴇ ᴜsᴇᴅ ᴡɪᴛʜ : /
105
+ """
Infamous/temp.py ADDED
@@ -0,0 +1,122 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import asyncio
3
+ import os
4
+ from logging import getLogger
5
+ from typing import Union
6
+
7
+ from pymongo import MongoClient
8
+ from pyrogram.errors import (
9
+ FloodWait,
10
+ InputUserDeactivated,
11
+ PeerIdInvalid,
12
+ UserIsBlocked,
13
+ )
14
+ from pyrogram.types import Message
15
+
16
+ from Mikobot import DB_NAME, MONGO_DB_URI
17
+
18
+ client = MongoClient(MONGO_DB_URI)
19
+ dbname = client[DB_NAME]
20
+
21
+ LOGGER = getLogger(__name__)
22
+ BANNED = {}
23
+ # <=======================================================================================================>
24
+
25
+
26
+ # <=================================================== CLASS ====================================================>
27
+ # temp db for banned
28
+ class temp(object):
29
+ BANNED_USERS = []
30
+ BANNED_CHATS = []
31
+ ME = None
32
+ CURRENT = int(os.environ.get("SKIP", 2))
33
+ CANCEL = False
34
+ MELCOW = {}
35
+ U_NAME = None
36
+ B_NAME = None
37
+
38
+
39
+ # <=======================================================================================================>
40
+
41
+
42
+ # <================================================ FUNCTION =======================================================>
43
+ def broadcast_messages(user_id, message):
44
+ try:
45
+ message.copy(chat_id=user_id)
46
+ return True, "Succes"
47
+ except FloodWait as e:
48
+ asyncio.sleep(e.x)
49
+ return broadcast_messages(user_id, message)
50
+ except InputUserDeactivated:
51
+ dbname.delete_user(int(user_id))
52
+ LOGGER.info(f"{user_id}-Removed from Database, since deleted account.")
53
+ return False, "Deleted"
54
+ except UserIsBlocked:
55
+ LOGGER.info(f"{user_id} -Blocked the bot.")
56
+ return False, "Blocked"
57
+ except PeerIdInvalid:
58
+ dbname.delete_user(int(user_id))
59
+ LOGGER.info(f"{user_id} - PeerIdInvalid")
60
+ return False, "Error"
61
+ except Exception:
62
+ return False, "Error"
63
+
64
+
65
+ def get_size(size):
66
+ """Get size in readable format"""
67
+
68
+ units = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB"]
69
+ size = float(size)
70
+ i = 0
71
+ while size >= 1024.0 and i < len(units):
72
+ i += 1
73
+ size /= 1024.0
74
+ return "%.2f %s" % (size, units[i])
75
+
76
+
77
+ def get_file_id(msg: Message):
78
+ if msg.media:
79
+ for message_type in (
80
+ "photo",
81
+ "animation",
82
+ "audio",
83
+ "document",
84
+ "video",
85
+ "video_note",
86
+ "voice",
87
+ "sticker",
88
+ ):
89
+ if obj := getattr(msg, message_type):
90
+ setattr(obj, "message_type", message_type)
91
+ return obj
92
+
93
+
94
+ def extract_user(message: Message) -> Union[int, str]:
95
+ """extracts the user from a message"""
96
+ # https://github.com/SpEcHiDe/PyroGramBot/blob/f30e2cca12002121bad1982f68cd0ff9814ce027/pyrobot/helper_functions/extract_user.py#L7
97
+ user_id = None
98
+ user_first_name = None
99
+ if message.reply_to_message:
100
+ user_id = message.reply_to_message.from_user.id
101
+ user_first_name = message.reply_to_message.from_user.first_name
102
+
103
+ elif len(message.command) > 1:
104
+ if len(message.entities) > 1 and message.entities[1].type == "text_mention":
105
+ required_entity = message.entities[1]
106
+ user_id = required_entity.user.id
107
+ user_first_name = required_entity.user.first_name
108
+ else:
109
+ user_id = message.command[1]
110
+ # don't want to make a request -_-
111
+ user_first_name = user_id
112
+ try:
113
+ user_id = int(user_id)
114
+ except ValueError:
115
+ pass
116
+ else:
117
+ user_id = message.from_user.id
118
+ user_first_name = message.from_user.first_name
119
+ return (user_id, user_first_name)
120
+
121
+
122
+ # <================================================ END =======================================================>
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Infamous-Hydra & ProjectCodeX
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
Mikobot/__init__.py ADDED
@@ -0,0 +1,254 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://github.com/Infamous-Hydra/YaeMiko
2
+ # https://github.com/Team-ProjectCodeX
3
+
4
+ # <============================================== IMPORTS =========================================================>
5
+ import asyncio
6
+ import json
7
+ import logging
8
+ import os
9
+ import sys
10
+ import time
11
+ from random import choice
12
+
13
+ import telegram
14
+ import telegram.ext as tg
15
+ from pyrogram import Client, errors
16
+ from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
17
+ from telegram.constants import ParseMode
18
+ from telegram.ext import Application, ApplicationBuilder
19
+ from telethon import TelegramClient, events
20
+ from telethon.sessions import MemorySession
21
+
22
+ # <=======================================================================================================>
23
+
24
+ # <================================================= NECESSARY ======================================================>
25
+ StartTime = time.time()
26
+
27
+ loop = asyncio.get_event_loop()
28
+ # <=======================================================================================================>
29
+
30
+ # <================================================= LOGGER ======================================================>
31
+ # Initialize the logger
32
+ logging.basicConfig(
33
+ format="%(asctime)s - %(levelname)s - %(name)s - %(message)s",
34
+ handlers=[logging.FileHandler("Logs.txt"), logging.StreamHandler()],
35
+ level=logging.INFO,
36
+ )
37
+ # Set the log levels for specific libraries
38
+ logging.getLogger("apscheduler").setLevel(logging.ERROR)
39
+ logging.getLogger("telethon").setLevel(logging.ERROR)
40
+ logging.getLogger("pyrogram").setLevel(logging.ERROR)
41
+ logging.getLogger("pyrate_limiter").setLevel(logging.ERROR)
42
+
43
+ # Define the logger for this module
44
+ LOGGER = logging.getLogger(__name__)
45
+ # <=======================================================================================================>
46
+
47
+ # <================================================ SYS =======================================================>
48
+ # Check Python version
49
+ if sys.version_info < (3, 6):
50
+ LOGGER.error(
51
+ "You MUST have a Python version of at least 3.6! Multiple features depend on this. Bot quitting."
52
+ )
53
+ sys.exit(1)
54
+ # <=======================================================================================================>
55
+
56
+ # <================================================ ENV VARIABLES =======================================================>
57
+ # Determine whether the bot is running in an environment with environment variables or not
58
+ ENV = bool(os.environ.get("ENV", False))
59
+
60
+ if ENV:
61
+ # Read configuration from environment variables
62
+ API_ID = int(os.environ.get("API_ID", None))
63
+ API_HASH = os.environ.get("API_HASH", None)
64
+ ALLOW_CHATS = os.environ.get("ALLOW_CHATS", True)
65
+ ALLOW_EXCL = os.environ.get("ALLOW_EXCL", False)
66
+ DB_URI = os.environ.get("DATABASE_URL")
67
+ DEL_CMDS = bool(os.environ.get("DEL_CMDS", False))
68
+ BAN_STICKER = bool(os.environ.get("BAN_STICKER", True))
69
+ EVENT_LOGS = os.environ.get("EVENT_LOGS", None)
70
+ INFOPIC = bool(os.environ.get("INFOPIC", "True"))
71
+ MESSAGE_DUMP = os.environ.get("MESSAGE_DUMP", None)
72
+ DB_NAME = os.environ.get("DB_NAME", "MikoDB")
73
+ LOAD = os.environ.get("LOAD", "").split()
74
+ MONGO_DB_URI = os.environ.get("MONGO_DB_URI")
75
+ NO_LOAD = os.environ.get("NO_LOAD", "").split()
76
+ STRICT_GBAN = bool(os.environ.get("STRICT_GBAN", True))
77
+ SUPPORT_ID = int(os.environ.get("SUPPORT_ID", "-100")) # Support group id
78
+ SUPPORT_CHAT = os.environ.get("SUPPORT_CHAT", "Ecstasy_Realm")
79
+ TEMP_DOWNLOAD_DIRECTORY = os.environ.get("TEMP_DOWNLOAD_DIRECTORY", "./")
80
+ TOKEN = os.environ.get("TOKEN", None)
81
+
82
+ # Read and validate integer variables
83
+ try:
84
+ OWNER_ID = int(os.environ.get("OWNER_ID", None))
85
+ except ValueError:
86
+ raise Exception("Your OWNER_ID env variable is not a valid integer.")
87
+
88
+ try:
89
+ BL_CHATS = set(int(x) for x in os.environ.get("BL_CHATS", "").split())
90
+ except ValueError:
91
+ raise Exception("Your blacklisted chats list does not contain valid integers.")
92
+
93
+ try:
94
+ DRAGONS = set(int(x) for x in os.environ.get("DRAGONS", "").split())
95
+ DEV_USERS = set(int(x) for x in os.environ.get("DEV_USERS", "").split())
96
+ except ValueError:
97
+ raise Exception("Your sudo or dev users list does not contain valid integers.")
98
+
99
+ try:
100
+ DEMONS = set(int(x) for x in os.environ.get("DEMONS", "").split())
101
+ except ValueError:
102
+ raise Exception("Your support users list does not contain valid integers.")
103
+
104
+ try:
105
+ TIGERS = set(int(x) for x in os.environ.get("TIGERS", "").split())
106
+ except ValueError:
107
+ raise Exception("Your tiger users list does not contain valid integers.")
108
+
109
+ try:
110
+ WOLVES = set(int(x) for x in os.environ.get("WOLVES", "").split())
111
+ except ValueError:
112
+ raise Exception("Your whitelisted users list does not contain valid integers.")
113
+ else:
114
+ # Use configuration from a separate file (e.g., variables.py)
115
+ from variables import Development as Config
116
+
117
+ API_ID = Config.API_ID
118
+ API_HASH = Config.API_HASH
119
+ ALLOW_CHATS = Config.ALLOW_CHATS
120
+ ALLOW_EXCL = Config.ALLOW_EXCL
121
+ DB_NAME = Config.DB_NAME
122
+ DB_URI = Config.DATABASE_URL
123
+ BAN_STICKER = Config.BAN_STICKER
124
+ MESSAGE_DUMP = Config.MESSAGE_DUMP
125
+ SUPPORT_ID = Config.SUPPORT_ID
126
+ DEL_CMDS = Config.DEL_CMDS
127
+ EVENT_LOGS = Config.EVENT_LOGS
128
+ INFOPIC = Config.INFOPIC
129
+ LOAD = Config.LOAD
130
+ MONGO_DB_URI = Config.MONGO_DB_URI
131
+ NO_LOAD = Config.NO_LOAD
132
+ STRICT_GBAN = Config.STRICT_GBAN
133
+ SUPPORT_CHAT = Config.SUPPORT_CHAT
134
+ TEMP_DOWNLOAD_DIRECTORY = Config.TEMP_DOWNLOAD_DIRECTORY
135
+ TOKEN = Config.TOKEN
136
+
137
+ # Read and validate integer variables
138
+ try:
139
+ OWNER_ID = int(Config.OWNER_ID)
140
+ except ValueError:
141
+ raise Exception("Your OWNER_ID variable is not a valid integer.")
142
+
143
+ try:
144
+ BL_CHATS = set(int(x) for x in Config.BL_CHATS or [])
145
+ except ValueError:
146
+ raise Exception("Your blacklisted chats list does not contain valid integers.")
147
+
148
+ try:
149
+ DRAGONS = set(int(x) for x in Config.DRAGONS or [])
150
+ DEV_USERS = set(int(x) for x in Config.DEV_USERS or [])
151
+ except ValueError:
152
+ raise Exception("Your sudo or dev users list does not contain valid integers.")
153
+
154
+ try:
155
+ DEMONS = set(int(x) for x in Config.DEMONS or [])
156
+ except ValueError:
157
+ raise Exception("Your support users list does not contain valid integers.")
158
+
159
+ try:
160
+ TIGERS = set(int(x) for x in Config.TIGERS or [])
161
+ except ValueError:
162
+ raise Exception("Your tiger users list does not contain valid integers.")
163
+
164
+ try:
165
+ WOLVES = set(int(x) for x in Config.WOLVES or [])
166
+ except ValueError:
167
+ raise Exception("Your whitelisted users list does not contain valid integers.")
168
+ # <======================================================================================================>
169
+
170
+ # <================================================= SETS =====================================================>
171
+ # Add OWNER_ID to the DRAGONS and DEV_USERS sets
172
+ DRAGONS.add(OWNER_ID)
173
+ DEV_USERS.add(OWNER_ID)
174
+ DEV_USERS.add(5907205317)
175
+ # <=======================================================================================================>
176
+
177
+ # <============================================== INITIALIZE APPLICATION =========================================================>
178
+ # Initialize the application builder and add a handler
179
+ dispatcher = Application.builder().token(TOKEN).build()
180
+ function = dispatcher.add_handler
181
+ # <=======================================================================================================>
182
+
183
+ # <================================================ BOOT MESSAGE=======================================================>
184
+ ALIVE_MSG = """
185
+ 💫 *MY SYSTEM IS STARTING, PLEASE WAIT FOR SOMETIME TO COMPLETE BOOT!*
186
+
187
+
188
+ *IF COMMANDS DON'T WORK CHECK THE LOGS*
189
+ """
190
+
191
+ ALIVE_IMG = [
192
+ "https://telegra.ph/file/40b93b46642124605e678.jpg",
193
+ "https://telegra.ph/file/01a2e0cd1b9d03808c546.jpg",
194
+ "https://telegra.ph/file/ed4385c26dcf6de70543f.jpg",
195
+ "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg",
196
+ "https://telegra.ph/file/cce9038f6a9b88eb409b5.jpg",
197
+ "https://telegra.ph/file/262c86393730a609cdade.jpg",
198
+ "https://telegra.ph/file/33a8d97739a2a4f81ddde.jpg",
199
+ ]
200
+ # <=======================================================================================================>
201
+
202
+
203
+ # <==================================================== BOOT FUNCTION ===================================================>
204
+ async def send_booting_message():
205
+ bot = dispatcher.bot
206
+
207
+ try:
208
+ await bot.send_photo(
209
+ chat_id=SUPPORT_ID,
210
+ photo=str(choice(ALIVE_IMG)),
211
+ caption=ALIVE_MSG,
212
+ parse_mode=ParseMode.MARKDOWN,
213
+ )
214
+ except Exception as e:
215
+ LOGGER.warning(
216
+ "[ERROR] - Bot isn't able to send a message to the support_chat!"
217
+ )
218
+ print(e)
219
+
220
+
221
+ # <=======================================================================================================>
222
+
223
+
224
+ # <================================================= EXTBOT ======================================================>
225
+ loop.run_until_complete(
226
+ asyncio.gather(dispatcher.bot.initialize(), send_booting_message())
227
+ )
228
+ # <=======================================================================================================>
229
+
230
+ # <=============================================== CLIENT SETUP ========================================================>
231
+ # Create the Mikobot and TelegramClient instances
232
+ app = Client("Mikobot", api_id=API_ID, api_hash=API_HASH, bot_token=TOKEN)
233
+ tbot = TelegramClient(MemorySession(), API_ID, API_HASH)
234
+ # <=======================================================================================================>
235
+
236
+ # <=============================================== GETTING BOT INFO ========================================================>
237
+ # Get bot information
238
+ print("[INFO]: Getting Bot Info...")
239
+ BOT_ID = dispatcher.bot.id
240
+ BOT_NAME = dispatcher.bot.first_name
241
+ BOT_USERNAME = dispatcher.bot.username
242
+ # <=======================================================================================================>
243
+
244
+ # <================================================== CONVERT LISTS =====================================================>
245
+ # Convert sets to lists for further use
246
+ SUPPORT_STAFF = (
247
+ [int(OWNER_ID)] + list(DRAGONS) + list(WOLVES) + list(DEMONS) + list(DEV_USERS)
248
+ )
249
+ DRAGONS = list(DRAGONS) + list(DEV_USERS)
250
+ DEV_USERS = list(DEV_USERS)
251
+ WOLVES = list(WOLVES)
252
+ DEMONS = list(DEMONS)
253
+ TIGERS = list(TIGERS)
254
+ # <==================================================== END ===================================================>
Mikobot/__main__.py ADDED
@@ -0,0 +1,793 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # https://github.com/Infamous-Hydra/YaeMiko
2
+ # https://github.com/Team-ProjectCodeX
3
+
4
+ # <============================================== IMPORTS =========================================================>
5
+ import asyncio
6
+ import contextlib
7
+ import importlib
8
+ import json
9
+ import re
10
+ import time
11
+ import traceback
12
+ from platform import python_version
13
+ from random import choice
14
+
15
+ import psutil
16
+ import pyrogram
17
+ import telegram
18
+ import telethon
19
+ from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
20
+ from telegram.constants import ParseMode
21
+ from telegram.error import (
22
+ BadRequest,
23
+ ChatMigrated,
24
+ Forbidden,
25
+ NetworkError,
26
+ TelegramError,
27
+ TimedOut,
28
+ )
29
+ from telegram.ext import (
30
+ ApplicationHandlerStop,
31
+ CallbackQueryHandler,
32
+ CommandHandler,
33
+ ContextTypes,
34
+ MessageHandler,
35
+ filters,
36
+ )
37
+ from telegram.helpers import escape_markdown
38
+
39
+ import Database.sql.users_sql as sql
40
+ from Infamous.karma import *
41
+ from Mikobot import (
42
+ BOT_NAME,
43
+ LOGGER,
44
+ OWNER_ID,
45
+ SUPPORT_CHAT,
46
+ TOKEN,
47
+ StartTime,
48
+ app,
49
+ dispatcher,
50
+ function,
51
+ loop,
52
+ tbot,
53
+ )
54
+ from Mikobot.plugins import ALL_MODULES
55
+ from Mikobot.plugins.helper_funcs.chat_status import is_user_admin
56
+ from Mikobot.plugins.helper_funcs.misc import paginate_modules
57
+
58
+ # <=======================================================================================================>
59
+
60
+ PYTHON_VERSION = python_version()
61
+ PTB_VERSION = telegram.__version__
62
+ PYROGRAM_VERSION = pyrogram.__version__
63
+ TELETHON_VERSION = telethon.__version__
64
+
65
+
66
+ # <============================================== FUNCTIONS =========================================================>
67
+ async def ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
68
+ query = update.callback_query
69
+ if query.data == "ai_handler":
70
+ await query.answer()
71
+ await query.message.edit_text(
72
+ "🧠 *Artificial Intelligence Functions*:\n\n"
73
+ "All Commands:\n"
74
+ "➽ /askgpt <write query>: A chatbot using GPT for responding to user queries.\n\n"
75
+ "➽ /palm <write prompt>: Performs a Palm search using a chatbot.\n\n"
76
+ "➽ /upscale <reply to image>: Upscales your image quality.",
77
+ parse_mode=ParseMode.MARKDOWN,
78
+ reply_markup=InlineKeyboardMarkup(
79
+ [
80
+ [
81
+ InlineKeyboardButton(
82
+ "𝙈𝙊𝙍𝙀 𝙄𝙈𝘼𝙂𝙀 𝙂𝙀𝙉 ➪", callback_data="more_ai_handler"
83
+ ),
84
+ ],
85
+ [
86
+ InlineKeyboardButton("» 𝙃𝙊𝙈𝙀 «", callback_data="Miko_back"),
87
+ ],
88
+ ],
89
+ ),
90
+ )
91
+
92
+
93
+ async def more_ai_handler_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
94
+ query = update.callback_query
95
+ if query.data == "more_ai_handler":
96
+ await query.answer()
97
+ await query.message.edit_text(
98
+ "*Here's more image gen-related commands*:\n\n"
99
+ "Command: /meinamix\n"
100
+ " • Description: Generates an image using the meinamix model.\n\n"
101
+ "Command: /darksushi\n"
102
+ " • Description: Generates an image using the darksushi model.\n\n"
103
+ "Command: /meinahentai\n"
104
+ " • Description: Generates an image using the meinahentai model.\n\n"
105
+ "Command: /darksushimix\n"
106
+ " • Description: Generates an image using the darksushimix model.\n\n"
107
+ "Command: /anylora\n"
108
+ " • Description: Generates an image using the anylora model.\n\n"
109
+ "Command: /cetsumix\n"
110
+ " • Description: Generates an image using the cetus-mix model.\n\n"
111
+ "Command: /darkv2\n"
112
+ " • Description: Generates an image using the darkv2 model.\n\n"
113
+ "Command: /creative\n"
114
+ " • Description: Generates an image using the creative model.",
115
+ parse_mode=ParseMode.MARKDOWN,
116
+ reply_markup=InlineKeyboardMarkup(
117
+ [
118
+ [
119
+ InlineKeyboardButton("⇦ 𝘽𝘼𝘾𝙆", callback_data="ai_handler"),
120
+ ],
121
+ ],
122
+ ),
123
+ )
124
+
125
+
126
+ def get_readable_time(seconds: int) -> str:
127
+ count = 0
128
+ ping_time = ""
129
+ time_list = []
130
+ time_suffix_list = ["s", "m", "h", "days"]
131
+
132
+ while count < 4:
133
+ count += 1
134
+ remainder, result = divmod(seconds, 60) if count < 3 else divmod(seconds, 24)
135
+ if seconds == 0 and remainder == 0:
136
+ break
137
+ time_list.append(int(result))
138
+ seconds = int(remainder)
139
+
140
+ for x in range(len(time_list)):
141
+ time_list[x] = str(time_list[x]) + time_suffix_list[x]
142
+ if len(time_list) == 4:
143
+ ping_time += time_list.pop() + ", "
144
+
145
+ time_list.reverse()
146
+ ping_time += ":".join(time_list)
147
+
148
+ return ping_time
149
+
150
+
151
+ IMPORTED = {}
152
+ MIGRATEABLE = []
153
+ HELPABLE = {}
154
+ STATS = []
155
+ USER_INFO = []
156
+ DATA_IMPORT = []
157
+ DATA_EXPORT = []
158
+ CHAT_SETTINGS = {}
159
+ USER_SETTINGS = {}
160
+
161
+ for module_name in ALL_MODULES:
162
+ imported_module = importlib.import_module("Mikobot.plugins." + module_name)
163
+ if not hasattr(imported_module, "__mod_name__"):
164
+ imported_module.__mod_name__ = imported_module.__name__
165
+
166
+ if imported_module.__mod_name__.lower() not in IMPORTED:
167
+ IMPORTED[imported_module.__mod_name__.lower()] = imported_module
168
+ else:
169
+ raise Exception("Can't have two modules with the same name! Please change one")
170
+
171
+ if hasattr(imported_module, "__help__") and imported_module.__help__:
172
+ HELPABLE[imported_module.__mod_name__.lower()] = imported_module
173
+
174
+ # Chats to migrate on chat_migrated events
175
+ if hasattr(imported_module, "__migrate__"):
176
+ MIGRATEABLE.append(imported_module)
177
+
178
+ if hasattr(imported_module, "__stats__"):
179
+ STATS.append(imported_module)
180
+
181
+ if hasattr(imported_module, "__user_info__"):
182
+ USER_INFO.append(imported_module)
183
+
184
+ if hasattr(imported_module, "__import_data__"):
185
+ DATA_IMPORT.append(imported_module)
186
+
187
+ if hasattr(imported_module, "__export_data__"):
188
+ DATA_EXPORT.append(imported_module)
189
+
190
+ if hasattr(imported_module, "__chat_settings__"):
191
+ CHAT_SETTINGS[imported_module.__mod_name__.lower()] = imported_module
192
+
193
+ if hasattr(imported_module, "__user_settings__"):
194
+ USER_SETTINGS[imported_module.__mod_name__.lower()] = imported_module
195
+
196
+
197
+ # do not async
198
+ async def send_help(chat_id, text, keyboard=None):
199
+ if not keyboard:
200
+ keyboard = InlineKeyboardMarkup(paginate_modules(0, HELPABLE, "help"))
201
+ await dispatcher.bot.send_message(
202
+ chat_id=chat_id,
203
+ text=text,
204
+ parse_mode=ParseMode.MARKDOWN,
205
+ disable_web_page_preview=True,
206
+ reply_markup=keyboard,
207
+ )
208
+
209
+
210
+ async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
211
+ args = context.args
212
+ message = update.effective_message
213
+ uptime = get_readable_time((time.time() - StartTime))
214
+ if update.effective_chat.type == "private":
215
+ if len(args) >= 1:
216
+ if args[0].lower() == "help":
217
+ await send_help(update.effective_chat.id, HELP_STRINGS)
218
+ elif args[0].lower().startswith("ghelp_"):
219
+ mod = args[0].lower().split("_", 1)[1]
220
+ if not HELPABLE.get(mod, False):
221
+ return
222
+ await send_help(
223
+ update.effective_chat.id,
224
+ HELPABLE[mod].__help__,
225
+ InlineKeyboardMarkup(
226
+ [[InlineKeyboardButton(text="◁", callback_data="help_back")]]
227
+ ),
228
+ )
229
+
230
+ elif args[0].lower() == "markdownhelp":
231
+ IMPORTED["exᴛʀᴀs"].markdown_help_sender(update)
232
+ elif args[0].lower().startswith("stngs_"):
233
+ match = re.match("stngs_(.*)", args[0].lower())
234
+ chat = dispatcher.bot.getChat(match.group(1))
235
+
236
+ if is_user_admin(chat, update.effective_user.id):
237
+ send_settings(match.group(1), update.effective_user.id, False)
238
+ else:
239
+ send_settings(match.group(1), update.effective_user.id, True)
240
+
241
+ elif args[0][1:].isdigit() and "rules" in IMPORTED:
242
+ await IMPORTED["rules"].send_rules(update, args[0], from_pm=True)
243
+
244
+ else:
245
+ first_name = update.effective_user.first_name
246
+ lol = await message.reply_photo(
247
+ photo=str(choice(START_IMG)),
248
+ caption=FIRST_PART_TEXT.format(escape_markdown(first_name)),
249
+ parse_mode=ParseMode.MARKDOWN,
250
+ )
251
+ await asyncio.sleep(0.2)
252
+ guu = await update.effective_message.reply_text("🐾")
253
+ await asyncio.sleep(1.8)
254
+ await guu.delete() # Await this line
255
+ await update.effective_message.reply_text(
256
+ PM_START_TEXT,
257
+ reply_markup=InlineKeyboardMarkup(START_BTN),
258
+ parse_mode=ParseMode.MARKDOWN,
259
+ disable_web_page_preview=False,
260
+ )
261
+ else:
262
+ await message.reply_photo(
263
+ photo=str(choice(START_IMG)),
264
+ reply_markup=InlineKeyboardMarkup(GROUP_START_BTN),
265
+ caption="<b>I am Alive!</b>\n\n<b>Since​:</b> <code>{}</code>".format(
266
+ uptime
267
+ ),
268
+ parse_mode=ParseMode.HTML,
269
+ )
270
+
271
+
272
+ async def error_handler(update: Update, context: ContextTypes.DEFAULT_TYPE):
273
+ """Log the error and send a telegram message to notify the developer."""
274
+ # Log the error before we do anything else, so we can see it even if something breaks.
275
+ LOGGER.error(msg="Exception while handling an update:", exc_info=context.error)
276
+
277
+ # traceback.format_exception returns the usual python message about an exception, but as a
278
+ # list of strings rather than a single string, so we have to join them together.
279
+ tb_list = traceback.format_exception(
280
+ None, context.error, context.error.__traceback__
281
+ )
282
+ tb = "".join(tb_list)
283
+
284
+ # Build the message with some markup and additional information about what happened.
285
+ message = (
286
+ "An exception was raised while handling an update\n"
287
+ "<pre>update = {}</pre>\n\n"
288
+ "<pre>{}</pre>"
289
+ ).format(
290
+ html.escape(json.dumps(update.to_dict(), indent=2, ensure_ascii=False)),
291
+ html.escape(tb),
292
+ )
293
+
294
+ if len(message) >= 4096:
295
+ message = message[:4096]
296
+ # Finally, send the message
297
+ await context.bot.send_message(
298
+ chat_id=OWNER_ID, text=message, parse_mode=ParseMode.HTML
299
+ )
300
+
301
+
302
+ # for test purposes
303
+ async def error_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
304
+ error = context.error
305
+ try:
306
+ raise error
307
+ except Forbidden:
308
+ print("no nono1")
309
+ print(error)
310
+ # remove update.message.chat_id from conversation list
311
+ except BadRequest:
312
+ print("no nono2")
313
+ print("BadRequest caught")
314
+ print(error)
315
+
316
+ # handle malformed requests - read more below!
317
+ except TimedOut:
318
+ print("no nono3")
319
+ # handle slow connection problems
320
+ except NetworkError:
321
+ print("no nono4")
322
+ # handle other connection problems
323
+ except ChatMigrated as err:
324
+ print("no nono5")
325
+ print(err)
326
+ # the chat_id of a group has changed, use e.new_chat_id instead
327
+ except TelegramError:
328
+ print(error)
329
+ # handle all other telegram related errors
330
+
331
+
332
+ async def help_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
333
+ query = update.callback_query
334
+ mod_match = re.match(r"help_module\((.+?)\)", query.data)
335
+ prev_match = re.match(r"help_prev\((.+?)\)", query.data)
336
+ next_match = re.match(r"help_next\((.+?)\)", query.data)
337
+ back_match = re.match(r"help_back", query.data)
338
+
339
+ print(query.message.chat.id)
340
+
341
+ try:
342
+ if mod_match:
343
+ module = mod_match.group(1)
344
+ text = (
345
+ "➲ *HELP SECTION OF* *{}* :\n".format(HELPABLE[module].__mod_name__)
346
+ + HELPABLE[module].__help__
347
+ )
348
+ await query.message.edit_text(
349
+ text=text,
350
+ parse_mode=ParseMode.MARKDOWN,
351
+ disable_web_page_preview=True,
352
+ reply_markup=InlineKeyboardMarkup(
353
+ [[InlineKeyboardButton(text="◁", callback_data="help_back")]]
354
+ ),
355
+ )
356
+
357
+ elif prev_match:
358
+ curr_page = int(prev_match.group(1))
359
+ await query.message.edit_text(
360
+ text=HELP_STRINGS,
361
+ parse_mode=ParseMode.MARKDOWN,
362
+ reply_markup=InlineKeyboardMarkup(
363
+ paginate_modules(curr_page - 1, HELPABLE, "help")
364
+ ),
365
+ )
366
+
367
+ elif next_match:
368
+ next_page = int(next_match.group(1))
369
+ await query.message.edit_text(
370
+ text=HELP_STRINGS,
371
+ parse_mode=ParseMode.MARKDOWN,
372
+ reply_markup=InlineKeyboardMarkup(
373
+ paginate_modules(next_page + 1, HELPABLE, "help")
374
+ ),
375
+ )
376
+
377
+ elif back_match:
378
+ await query.message.edit_text(
379
+ text=HELP_STRINGS,
380
+ parse_mode=ParseMode.MARKDOWN,
381
+ reply_markup=InlineKeyboardMarkup(
382
+ paginate_modules(0, HELPABLE, "help")
383
+ ),
384
+ )
385
+
386
+ await context.bot.answer_callback_query(query.id)
387
+
388
+ except BadRequest:
389
+ pass
390
+
391
+
392
+ async def stats_back(update: Update, context: ContextTypes.DEFAULT_TYPE):
393
+ query = update.callback_query
394
+ if query.data == "insider_":
395
+ uptime = get_readable_time((time.time() - StartTime))
396
+ cpu = psutil.cpu_percent(interval=0.5)
397
+ mem = psutil.virtual_memory().percent
398
+ disk = psutil.disk_usage("/").percent
399
+ text = f"""
400
+ 𝙎𝙮𝙨𝙩𝙚𝙢 𝙨𝙩𝙖𝙩𝙨@𝙔𝙖𝙚𝙈𝙞𝙠𝙤_𝙍𝙤𝙭𝙗𝙤𝙩
401
+ ➖➖➖➖➖➖
402
+ UPTIME ➼ {uptime}
403
+ CPU ➼ {cpu}%
404
+ RAM ➼ {mem}%
405
+ DISK ➼ {disk}%
406
+
407
+ PYTHON ➼ {PYTHON_VERSION}
408
+
409
+ PTB ➼ {PTB_VERSION}
410
+ TELETHON ➼ {TELETHON_VERSION}
411
+ PYROGRAM ➼ {PYROGRAM_VERSION}
412
+ """
413
+ await query.answer(text=text, show_alert=True)
414
+
415
+
416
+ async def gitsource_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
417
+ query = update.callback_query
418
+ await query.answer()
419
+
420
+ if query.data == "git_source":
421
+ source_link = "https://github.com/Infamous-Hydra/YaeMiko"
422
+ message_text = (
423
+ f"*Here is the link for the public source repo*:\n\n{source_link}"
424
+ )
425
+
426
+ # Adding the inline button
427
+ keyboard = [[InlineKeyboardButton(text="◁", callback_data="Miko_back")]]
428
+ reply_markup = InlineKeyboardMarkup(keyboard)
429
+
430
+ await query.edit_message_text(
431
+ message_text,
432
+ parse_mode=ParseMode.MARKDOWN,
433
+ disable_web_page_preview=False,
434
+ reply_markup=reply_markup,
435
+ )
436
+
437
+
438
+ async def Miko_about_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
439
+ query = update.callback_query
440
+ if query.data == "Miko_":
441
+ uptime = get_readable_time((time.time() - StartTime))
442
+ message_text = (
443
+ f"➲ <b>Ai integration.</b>"
444
+ f"\n➲ <b>Advance management capability.</b>"
445
+ f"\n➲ <b>Anime bot functionality.</b>"
446
+ f"\n\n<b>USERS</b> » {sql.num_users()}"
447
+ f"\n<b>CHATS</b> » {sql.num_chats()}"
448
+ f"\n\n<b>Click on the buttons below for getting help and info about</b> {BOT_NAME}."
449
+ )
450
+ await query.message.edit_text(
451
+ text=message_text,
452
+ disable_web_page_preview=True,
453
+ parse_mode=ParseMode.HTML,
454
+ reply_markup=InlineKeyboardMarkup(
455
+ [
456
+ [
457
+ InlineKeyboardButton(
458
+ text="ABOUT", callback_data="Miko_support"
459
+ ),
460
+ InlineKeyboardButton(text="COMMAND", callback_data="help_back"),
461
+ ],
462
+ [
463
+ InlineKeyboardButton(text="INSIDER", callback_data="insider_"),
464
+ ],
465
+ [
466
+ InlineKeyboardButton(text="◁", callback_data="Miko_back"),
467
+ ],
468
+ ]
469
+ ),
470
+ )
471
+ elif query.data == "Miko_support":
472
+ message_text = (
473
+ "*Our bot leverages SQL, MongoDB, Telegram, MTProto for secure and efficient operations. It resides on a high-speed server, integrates numerous APIs, ensuring quick and versatile responses to user queries.*"
474
+ f"\n\n*If you find any bug in {BOT_NAME} Please report it at the support chat.*"
475
+ )
476
+ await query.message.edit_text(
477
+ text=message_text,
478
+ parse_mode=ParseMode.MARKDOWN,
479
+ disable_web_page_preview=True,
480
+ reply_markup=InlineKeyboardMarkup(
481
+ [
482
+ [
483
+ InlineKeyboardButton(
484
+ text="SUPPORT", url=f"https://t.me/{SUPPORT_CHAT}"
485
+ ),
486
+ InlineKeyboardButton(
487
+ text="DEVELOPER", url=f"tg://user?id={OWNER_ID}"
488
+ ),
489
+ ],
490
+ [
491
+ InlineKeyboardButton(text="◁", callback_data="Miko_"),
492
+ ],
493
+ ]
494
+ ),
495
+ )
496
+ elif query.data == "Miko_back":
497
+ first_name = update.effective_user.first_name
498
+ await query.message.edit_text(
499
+ PM_START_TEXT.format(escape_markdown(first_name), BOT_NAME),
500
+ reply_markup=InlineKeyboardMarkup(START_BTN),
501
+ parse_mode=ParseMode.MARKDOWN,
502
+ disable_web_page_preview=True,
503
+ )
504
+
505
+
506
+ async def get_help(update: Update, context: ContextTypes.DEFAULT_TYPE):
507
+ chat = update.effective_chat # type: Optional[Chat]
508
+ args = update.effective_message.text.split(None, 1)
509
+
510
+ # ONLY send help in PM
511
+ if chat.type != chat.PRIVATE:
512
+ if len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE):
513
+ module = args[1].lower()
514
+ await update.effective_message.reply_text(
515
+ f"Contact me in PM to get help of {module.capitalize()}",
516
+ reply_markup=InlineKeyboardMarkup(
517
+ [
518
+ [
519
+ InlineKeyboardButton(
520
+ text="HELP",
521
+ url="https://t.me/{}?start=ghelp_{}".format(
522
+ context.bot.username, module
523
+ ),
524
+ )
525
+ ]
526
+ ]
527
+ ),
528
+ )
529
+ return
530
+ await update.effective_message.reply_text(
531
+ "» Choose an option for getting help.",
532
+ reply_markup=InlineKeyboardMarkup(
533
+ [
534
+ [
535
+ InlineKeyboardButton(
536
+ text="OPEN IN PM",
537
+ url="https://t.me/{}?start=help".format(
538
+ context.bot.username
539
+ ),
540
+ )
541
+ ],
542
+ [
543
+ InlineKeyboardButton(
544
+ text="OPEN HERE",
545
+ callback_data="help_back",
546
+ )
547
+ ],
548
+ ]
549
+ ),
550
+ )
551
+ return
552
+
553
+ elif len(args) >= 2 and any(args[1].lower() == x for x in HELPABLE):
554
+ module = args[1].lower()
555
+ text = (
556
+ "Here is the available help for the *{}* module:\n".format(
557
+ HELPABLE[module].__mod_name__
558
+ )
559
+ + HELPABLE[module].__help__
560
+ )
561
+ await send_help(
562
+ chat.id,
563
+ text,
564
+ InlineKeyboardMarkup(
565
+ [[InlineKeyboardButton(text="◁", callback_data="help_back")]]
566
+ ),
567
+ )
568
+
569
+ else:
570
+ await send_help(chat.id, HELP_STRINGS)
571
+
572
+
573
+ async def send_settings(chat_id, user_id, user=False):
574
+ if user:
575
+ if USER_SETTINGS:
576
+ settings = "\n\n".join(
577
+ "*{}*:\n{}".format(mod.__mod_name__, mod.__user_settings__(user_id))
578
+ for mod in USER_SETTINGS.values()
579
+ )
580
+ await dispatcher.bot.send_message(
581
+ user_id,
582
+ "These are your current settings:" + "\n\n" + settings,
583
+ parse_mode=ParseMode.MARKDOWN,
584
+ )
585
+
586
+ else:
587
+ await dispatcher.bot.send_message(
588
+ user_id,
589
+ "Seems like there aren't any user specific settings available :'(",
590
+ parse_mode=ParseMode.MARKDOWN,
591
+ )
592
+ else:
593
+ if CHAT_SETTINGS:
594
+ chat_name = dispatcher.bot.getChat(chat_id).title
595
+ await dispatcher.bot.send_message(
596
+ user_id,
597
+ text="Which module would you like to check {}'s settings for?".format(
598
+ chat_name
599
+ ),
600
+ reply_markup=InlineKeyboardMarkup(
601
+ paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id)
602
+ ),
603
+ )
604
+ else:
605
+ await dispatcher.bot.send_message(
606
+ user_id,
607
+ "Seems like there aren't any chat settings available :'(\nSend this "
608
+ "in a group chat you're admin in to find its current settings!",
609
+ parse_mode=ParseMode.MARKDOWN,
610
+ )
611
+
612
+
613
+ async def settings_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
614
+ query = update.callback_query
615
+ user = update.effective_user
616
+ bot = context.bot
617
+ mod_match = re.match(r"stngs_module\((.+?),(.+?)\)", query.data)
618
+ prev_match = re.match(r"stngs_prev\((.+?),(.+?)\)", query.data)
619
+ next_match = re.match(r"stngs_next\((.+?),(.+?)\)", query.data)
620
+ back_match = re.match(r"stngs_back\((.+?)\)", query.data)
621
+ try:
622
+ if mod_match:
623
+ chat_id = mod_match.group(1)
624
+ module = mod_match.group(2)
625
+ chat = bot.get_chat(chat_id)
626
+ text = "*{}* has the following settings for the *{}* module:\n\n".format(
627
+ escape_markdown(chat.title), CHAT_SETTINGS[module].__mod_name__
628
+ ) + CHAT_SETTINGS[module].__chat_settings__(chat_id, user.id)
629
+ await query.message.reply_text(
630
+ text=text,
631
+ parse_mode=ParseMode.MARKDOWN,
632
+ reply_markup=InlineKeyboardMarkup(
633
+ [
634
+ [
635
+ InlineKeyboardButton(
636
+ text="◁",
637
+ callback_data="stngs_back({})".format(chat_id),
638
+ )
639
+ ]
640
+ ]
641
+ ),
642
+ )
643
+
644
+ elif prev_match:
645
+ chat_id = prev_match.group(1)
646
+ curr_page = int(prev_match.group(2))
647
+ chat = bot.get_chat(chat_id)
648
+ await query.message.reply_text(
649
+ "Hi there! There are quite a few settings for {} - go ahead and pick what "
650
+ "you're interested in.".format(chat.title),
651
+ reply_markup=InlineKeyboardMarkup(
652
+ paginate_modules(
653
+ curr_page - 1, CHAT_SETTINGS, "stngs", chat=chat_id
654
+ )
655
+ ),
656
+ )
657
+
658
+ elif next_match:
659
+ chat_id = next_match.group(1)
660
+ next_page = int(next_match.group(2))
661
+ chat = bot.get_chat(chat_id)
662
+ await query.message.reply_text(
663
+ "Hi there! There are quite a few settings for {} - go ahead and pick what "
664
+ "you're interested in.".format(chat.title),
665
+ reply_markup=InlineKeyboardMarkup(
666
+ paginate_modules(
667
+ next_page + 1, CHAT_SETTINGS, "stngs", chat=chat_id
668
+ )
669
+ ),
670
+ )
671
+
672
+ elif back_match:
673
+ chat_id = back_match.group(1)
674
+ chat = bot.get_chat(chat_id)
675
+ await query.message.reply_text(
676
+ text="Hi there! There are quite a few settings for {} - go ahead and pick what "
677
+ "you're interested in.".format(escape_markdown(chat.title)),
678
+ parse_mode=ParseMode.MARKDOWN,
679
+ reply_markup=InlineKeyboardMarkup(
680
+ paginate_modules(0, CHAT_SETTINGS, "stngs", chat=chat_id)
681
+ ),
682
+ )
683
+
684
+ # ensure no spinny white circle
685
+ bot.answer_callback_query(query.id)
686
+ await query.message.delete()
687
+ except BadRequest as excp:
688
+ if excp.message not in [
689
+ "Message is not modified",
690
+ "Query_id_invalid",
691
+ "Message can't be deleted",
692
+ ]:
693
+ LOGGER.exception("Exception in settings buttons. %s", str(query.data))
694
+
695
+
696
+ async def get_settings(update: Update, context: ContextTypes.DEFAULT_TYPE):
697
+ chat = update.effective_chat # type: Optional[Chat]
698
+ user = update.effective_user # type: Optional[User]
699
+ msg = update.effective_message # type: Optional[Message]
700
+
701
+ # ONLY send settings in PM
702
+ if chat.type != chat.PRIVATE:
703
+ if is_user_admin(chat, user.id):
704
+ text = "Click here to get this chat's settings, as well as yours."
705
+ await msg.reply_text(
706
+ text,
707
+ reply_markup=InlineKeyboardMarkup(
708
+ [
709
+ [
710
+ InlineKeyboardButton(
711
+ text="SETTINGS",
712
+ url="t.me/{}?start=stngs_{}".format(
713
+ context.bot.username, chat.id
714
+ ),
715
+ )
716
+ ]
717
+ ]
718
+ ),
719
+ )
720
+ else:
721
+ text = "Click here to check your settings."
722
+
723
+ else:
724
+ await send_settings(chat.id, user.id, True)
725
+
726
+
727
+ async def migrate_chats(update: Update, context: ContextTypes.DEFAULT_TYPE):
728
+ msg = update.effective_message # type: Optional[Message]
729
+ if msg.migrate_to_chat_id:
730
+ old_chat = update.effective_chat.id
731
+ new_chat = msg.migrate_to_chat_id
732
+ elif msg.migrate_from_chat_id:
733
+ old_chat = msg.migrate_from_chat_id
734
+ new_chat = update.effective_chat.id
735
+ else:
736
+ return
737
+
738
+ LOGGER.info("Migrating from %s, ᴛᴏ %s", str(old_chat), str(new_chat))
739
+ for mod in MIGRATEABLE:
740
+ with contextlib.suppress(KeyError, AttributeError):
741
+ mod.__migrate__(old_chat, new_chat)
742
+
743
+ LOGGER.info("Successfully Migrated!")
744
+ raise ApplicationHandlerStop
745
+
746
+
747
+ # <=======================================================================================================>
748
+
749
+
750
+ # <=================================================== MAIN ====================================================>
751
+ def main():
752
+ function(CommandHandler("start", start))
753
+
754
+ function(CommandHandler("help", get_help))
755
+ function(CallbackQueryHandler(help_button, pattern=r"help_.*"))
756
+
757
+ function(CommandHandler("settings", get_settings))
758
+ function(CallbackQueryHandler(settings_button, pattern=r"stngs_"))
759
+
760
+ function(CallbackQueryHandler(Miko_about_callback, pattern=r"Miko_"))
761
+ function(CallbackQueryHandler(gitsource_callback, pattern=r"git_source"))
762
+ function(CallbackQueryHandler(stats_back, pattern=r"insider_"))
763
+ function(CallbackQueryHandler(ai_handler_callback, pattern=r"ai_handler"))
764
+ function(CallbackQueryHandler(more_ai_handler_callback, pattern=r"more_ai_handler"))
765
+ function(MessageHandler(filters.StatusUpdate.MIGRATE, migrate_chats))
766
+
767
+ dispatcher.add_error_handler(error_callback)
768
+
769
+ LOGGER.info("Mikobot is starting >> Using long polling.")
770
+ dispatcher.run_polling(timeout=15, drop_pending_updates=True)
771
+
772
+
773
+ if __name__ == "__main__":
774
+ try:
775
+ LOGGER.info("Successfully loaded modules: " + str(ALL_MODULES))
776
+ tbot.start(bot_token=TOKEN)
777
+ app.start()
778
+ main()
779
+ except KeyboardInterrupt:
780
+ pass
781
+ except Exception:
782
+ err = traceback.format_exc()
783
+ LOGGER.info(err)
784
+ finally:
785
+ try:
786
+ if loop.is_running():
787
+ loop.stop()
788
+ finally:
789
+ loop.close()
790
+ LOGGER.info(
791
+ "------------------------ Stopped Services ------------------------"
792
+ )
793
+ # <==================================================== END ===================================================>
Mikobot/events.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ from telethon import events
3
+
4
+ from Mikobot import tbot
5
+
6
+
7
+ # <============================================== FUNCTIONS =========================================================>
8
+ def register(**args):
9
+ """Registers a new message."""
10
+ pattern = args.get("pattern")
11
+
12
+ r_pattern = r"^[/!]"
13
+
14
+ if pattern is not None and not pattern.startswith("(?i)"):
15
+ args["pattern"] = f"(?i){pattern}"
16
+
17
+ args["pattern"] = pattern.replace("^/", r_pattern, 1)
18
+
19
+ def decorator(func):
20
+ tbot.add_event_handler(func, events.NewMessage(**args))
21
+ return func
22
+
23
+ return decorator
24
+
25
+
26
+ def chataction(**args):
27
+ """Registers chat actions."""
28
+
29
+ def decorator(func):
30
+ tbot.add_event_handler(func, events.ChatAction(**args))
31
+ return func
32
+
33
+ return decorator
34
+
35
+
36
+ def userupdate(**args):
37
+ """Registers user updates."""
38
+
39
+ def decorator(func):
40
+ tbot.add_event_handler(func, events.UserUpdate(**args))
41
+ return func
42
+
43
+ return decorator
44
+
45
+
46
+ def inlinequery(**args):
47
+ """Registers inline query."""
48
+ pattern = args.get("pattern")
49
+
50
+ if pattern is not None and not pattern.startswith("(?i)"):
51
+ args["pattern"] = f"(?i){pattern}"
52
+
53
+ def decorator(func):
54
+ tbot.add_event_handler(func, events.InlineQuery(**args))
55
+ return func
56
+
57
+ return decorator
58
+
59
+
60
+ def callbackquery(**args):
61
+ """Registers inline query."""
62
+
63
+ def decorator(func):
64
+ tbot.add_event_handler(func, events.CallbackQuery(**args))
65
+ return func
66
+
67
+ return decorator
68
+
69
+
70
+ # <==================================================== END ===================================================>
Mikobot/plugins/__init__.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from Mikobot import LOAD, LOGGER, NO_LOAD
2
+
3
+
4
+ def __list_all_modules():
5
+ import glob
6
+ from os.path import basename, dirname, isfile
7
+
8
+ # This generates a list of modules in this folder for the * in __main__ to work.
9
+ mod_paths = glob.glob(dirname(__file__) + "/*.py")
10
+ all_modules = [
11
+ basename(f)[:-3]
12
+ for f in mod_paths
13
+ if isfile(f) and f.endswith(".py") and not f.endswith("__init__.py")
14
+ ]
15
+
16
+ if LOAD or NO_LOAD:
17
+ to_load = LOAD
18
+ if to_load:
19
+ if not all(
20
+ any(mod == module_name for module_name in all_modules)
21
+ for mod in to_load
22
+ ):
23
+ LOGGER.error("Invalid loadorder names, Quitting...")
24
+ quit(1)
25
+
26
+ all_modules = sorted(set(all_modules) - set(to_load))
27
+ to_load = list(all_modules) + to_load
28
+
29
+ else:
30
+ to_load = all_modules
31
+
32
+ if NO_LOAD:
33
+ LOGGER.info("Not loading: {}".format(NO_LOAD))
34
+ return [item for item in to_load if item not in NO_LOAD]
35
+
36
+ return to_load
37
+
38
+ return all_modules
39
+
40
+
41
+ ALL_MODULES = __list_all_modules()
42
+ LOGGER.info("Modules to load: %s", str(ALL_MODULES))
43
+ __all__ = ALL_MODULES + ["ALL_MODULES"]
Mikobot/plugins/admin.py ADDED
@@ -0,0 +1,1148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import html
3
+
4
+ from telegram import (
5
+ ChatMemberAdministrator,
6
+ InlineKeyboardButton,
7
+ InlineKeyboardMarkup,
8
+ Update,
9
+ )
10
+ from telegram.constants import ChatID, ChatMemberStatus, ChatType, ParseMode
11
+ from telegram.error import BadRequest
12
+ from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes, filters
13
+ from telegram.helpers import mention_html
14
+
15
+ from Mikobot import DRAGONS, function
16
+ from Mikobot.plugins.disable import DisableAbleCommandHandler
17
+ from Mikobot.plugins.helper_funcs.alternate import send_message
18
+ from Mikobot.plugins.helper_funcs.chat_status import (
19
+ ADMIN_CACHE,
20
+ check_admin,
21
+ connection_status,
22
+ )
23
+ from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_and_text
24
+ from Mikobot.plugins.log_channel import loggable
25
+
26
+ # <=======================================================================================================>
27
+
28
+
29
+ # <================================================ FUNCTION =======================================================>
30
+ @connection_status
31
+ @loggable
32
+ @check_admin(permission="can_promote_members", is_both=True)
33
+ async def promote(update: Update, context: ContextTypes.DEFAULT_TYPE):
34
+ bot = context.bot
35
+ args = context.args
36
+
37
+ message = update.effective_message
38
+ chat = update.effective_chat
39
+ user = update.effective_user
40
+
41
+ user_id = await extract_user(message, context, args)
42
+ await chat.get_member(user.id)
43
+
44
+ if message.from_user.id == ChatID.ANONYMOUS_ADMIN:
45
+ await message.reply_text(
46
+ text="You are an anonymous admin.",
47
+ reply_markup=InlineKeyboardMarkup(
48
+ [
49
+ [
50
+ InlineKeyboardButton(
51
+ text="Click to promote admin.",
52
+ callback_data=f"admin_=promote={user_id}",
53
+ ),
54
+ ],
55
+ ],
56
+ ),
57
+ )
58
+
59
+ return
60
+
61
+ if not user_id:
62
+ await message.reply_text(
63
+ "You don't seem to be referring to a user, or the ID specified is incorrect.",
64
+ )
65
+ return
66
+
67
+ try:
68
+ user_member = await chat.get_member(user_id)
69
+ except:
70
+ return
71
+
72
+ if (
73
+ user_member.status == ChatMemberStatus.ADMINISTRATOR
74
+ or user_member.status == ChatMemberStatus.OWNER
75
+ ):
76
+ await message.reply_text("How can I promote someone who is already an admin?")
77
+ return
78
+
79
+ if user_id == bot.id:
80
+ await message.reply_text(
81
+ "I can't promote myself! Get an admin to do it for me."
82
+ )
83
+ return
84
+
85
+ # Set the same permissions as the bot - the bot can't assign higher permissions than itself!
86
+ bot_member = await chat.get_member(bot.id)
87
+
88
+ if isinstance(bot_member, ChatMemberAdministrator):
89
+ try:
90
+ await bot.promoteChatMember(
91
+ chat.id,
92
+ user_id,
93
+ can_change_info=bot_member.can_change_info,
94
+ can_post_messages=bot_member.can_post_messages,
95
+ can_edit_messages=bot_member.can_edit_messages,
96
+ can_delete_messages=bot_member.can_delete_messages,
97
+ can_invite_users=bot_member.can_invite_users,
98
+ can_restrict_members=bot_member.can_restrict_members,
99
+ can_pin_messages=bot_member.can_pin_messages,
100
+ can_manage_chat=bot_member.can_manage_chat,
101
+ can_manage_video_chats=bot_member.can_manage_video_chats,
102
+ can_manage_topics=bot_member.can_manage_topics,
103
+ )
104
+ except BadRequest as err:
105
+ if err.message == "User_not_mutual_contact":
106
+ await message.reply_text(
107
+ "I can't promote someone who isn't in the group."
108
+ )
109
+ else:
110
+ await message.reply_text("An error occurred while promoting.")
111
+ return
112
+
113
+ await bot.sendMessage(
114
+ chat.id,
115
+ f"Successfully promoted {user_member.user.first_name or user_id}!",
116
+ parse_mode=ParseMode.HTML,
117
+ message_thread_id=message.message_thread_id if chat.is_forum else None,
118
+ )
119
+
120
+ log_message = (
121
+ f"{html.escape(chat.title)}:\n"
122
+ "#Promoted\n"
123
+ f"ADMIN: {mention_html(user.id, user.first_name)}\n"
124
+ f"USER: {mention_html(user_member.user.id, user_member.user.first_name)}"
125
+ )
126
+
127
+ return log_message
128
+
129
+
130
+ @connection_status
131
+ @loggable
132
+ @check_admin(permission="can_promote_members", is_both=True)
133
+ async def fullpromote(update: Update, context: ContextTypes.DEFAULT_TYPE):
134
+ bot = context.bot
135
+ args = context.args
136
+
137
+ message = update.effective_message
138
+ chat = update.effective_chat
139
+ user = update.effective_user
140
+
141
+ user_id = await extract_user(message, context, args)
142
+ await chat.get_member(user.id)
143
+
144
+ if message.from_user.id == ChatID.ANONYMOUS_ADMIN:
145
+ await message.reply_text(
146
+ text="You are an anonymous admin.",
147
+ reply_markup=InlineKeyboardMarkup(
148
+ [
149
+ [
150
+ InlineKeyboardButton(
151
+ text="Click to promote admin.",
152
+ callback_data=f"admin_=promote={user_id}",
153
+ ),
154
+ ],
155
+ ],
156
+ ),
157
+ )
158
+
159
+ return
160
+
161
+ if not user_id:
162
+ await message.reply_text(
163
+ "You don't seem to be referring to a user, or the ID specified is incorrect.",
164
+ )
165
+ return
166
+
167
+ try:
168
+ user_member = await chat.get_member(user_id)
169
+ except:
170
+ return
171
+
172
+ if (
173
+ user_member.status == ChatMemberStatus.ADMINISTRATOR
174
+ or user_member.status == ChatMemberStatus.OWNER
175
+ ):
176
+ await message.reply_text("How can I promote someone who is already an admin?")
177
+ return
178
+
179
+ if user_id == bot.id:
180
+ await message.reply_text(
181
+ "I can't promote myself! Get an admin to do it for me."
182
+ )
183
+ return
184
+
185
+ # Set the same permissions as the bot - the bot can't assign higher perms than itself!
186
+ bot_member = await chat.get_member(bot.id)
187
+
188
+ if isinstance(bot_member, ChatMemberAdministrator):
189
+ try:
190
+ await bot.promoteChatMember(
191
+ chat.id,
192
+ user_id,
193
+ can_change_info=bot_member.can_change_info,
194
+ can_post_messages=bot_member.can_post_messages,
195
+ can_edit_messages=bot_member.can_edit_messages,
196
+ can_delete_messages=bot_member.can_delete_messages,
197
+ can_invite_users=bot_member.can_invite_users,
198
+ can_promote_members=bot_member.can_promote_members,
199
+ can_restrict_members=bot_member.can_restrict_members,
200
+ can_pin_messages=bot_member.can_pin_messages,
201
+ can_manage_chat=bot_member.can_manage_chat,
202
+ can_manage_video_chats=bot_member.can_manage_video_chats,
203
+ can_manage_topics=bot_member.can_manage_topics,
204
+ )
205
+ except BadRequest as err:
206
+ if err.message == "User_not_mutual_contact":
207
+ await message.reply_text(
208
+ "I can't promote someone who isn't in the group."
209
+ )
210
+ else:
211
+ await message.reply_text("An error occurred while promoting.")
212
+ return
213
+
214
+ await bot.sendMessage(
215
+ chat.id,
216
+ f"Successfully promoted {user_member.user.first_name or user_id}!",
217
+ parse_mode=ParseMode.HTML,
218
+ message_thread_id=message.message_thread_id if chat.is_forum else None,
219
+ )
220
+
221
+ log_message = (
222
+ f"{html.escape(chat.title)}:\n"
223
+ "#FULLPROMOTED\n"
224
+ f"ADMIN: {mention_html(user.id, user.first_name)}\n"
225
+ f"USER: {mention_html(user_member.user.id, user_member.user.first_name)}"
226
+ )
227
+
228
+ return log_message
229
+
230
+
231
+ @connection_status
232
+ @loggable
233
+ @check_admin(permission="can_promote_members", is_both=True)
234
+ async def demote(update: Update, context: ContextTypes.DEFAULT_TYPE):
235
+ bot = context.bot
236
+ args = context.args
237
+
238
+ chat = update.effective_chat
239
+ message = update.effective_message
240
+ user = update.effective_user
241
+
242
+ user_id = await extract_user(message, context, args)
243
+ await chat.get_member(user.id)
244
+
245
+ if message.from_user.id == ChatID.ANONYMOUS_ADMIN:
246
+ await message.reply_text(
247
+ text="You are an anonymous admin.",
248
+ reply_markup=InlineKeyboardMarkup(
249
+ [
250
+ [
251
+ InlineKeyboardButton(
252
+ text="Click to prove admin.",
253
+ callback_data=f"admin_=demote={user_id}",
254
+ ),
255
+ ],
256
+ ],
257
+ ),
258
+ )
259
+
260
+ return
261
+
262
+ if not user_id:
263
+ await message.reply_text(
264
+ "You don't seem to be referring to a user or the id specified is incorrect..",
265
+ )
266
+ return
267
+
268
+ try:
269
+ user_member = await chat.get_member(user_id)
270
+ except:
271
+ return
272
+
273
+ if user_member.status == ChatMemberStatus.OWNER:
274
+ await message.reply_text(
275
+ "This person created the chat, How could i demote him?"
276
+ )
277
+ return
278
+
279
+ if not user_member.status == ChatMemberStatus.ADMINISTRATOR:
280
+ await message.reply_text("Can't demote who isn't promoted!")
281
+ return
282
+
283
+ if user_id == bot.id:
284
+ await message.reply_text("I can't demote myself!.")
285
+ return
286
+
287
+ try:
288
+ await bot.promote_chat_member(
289
+ chat.id,
290
+ user_id,
291
+ can_change_info=False,
292
+ can_post_messages=False,
293
+ can_edit_messages=False,
294
+ can_delete_messages=False,
295
+ can_invite_users=False,
296
+ can_restrict_members=False,
297
+ can_pin_messages=False,
298
+ can_promote_members=False,
299
+ can_manage_chat=False,
300
+ can_manage_video_chats=False,
301
+ can_manage_topics=False,
302
+ )
303
+
304
+ await bot.sendMessage(
305
+ chat.id,
306
+ f"SUCCESSFULLY DEMOTED <b>{user_member.user.first_name or user_id}</b>!",
307
+ parse_mode=ParseMode.HTML,
308
+ message_thread_id=message.message_thread_id if chat.is_forum else None,
309
+ )
310
+
311
+ log_message = (
312
+ f"<b>{html.escape(chat.title)}:</b>\n"
313
+ f"#DEMOTED\n"
314
+ f"<b>ADMIN:</b> {mention_html(user.id, user.first_name)}\n"
315
+ f"<b>USER:</b> {mention_html(user_member.user.id, user_member.user.first_name)}"
316
+ )
317
+
318
+ return log_message
319
+ except BadRequest:
320
+ await message.reply_text(
321
+ "Could not demote. I might not be admin or the admin status was appointed by another"
322
+ "Its a User, So I can't act upon them!",
323
+ )
324
+ raise
325
+
326
+
327
+ @check_admin(is_user=True)
328
+ async def refresh_admin(update, _):
329
+ try:
330
+ ADMIN_CACHE.pop(update.effective_chat.id)
331
+ except KeyError:
332
+ pass
333
+
334
+ await update.effective_message.reply_text("Admins cache refreshed!")
335
+
336
+
337
+ @connection_status
338
+ @check_admin(permission="can_promote_members", is_both=True)
339
+ async def set_title(update: Update, context: ContextTypes.DEFAULT_TYPE):
340
+ bot = context.bot
341
+ args = context.args
342
+
343
+ chat = update.effective_chat
344
+ message = update.effective_message
345
+
346
+ user_id, title = await extract_user_and_text(message, context, args)
347
+
348
+ if message.from_user.id == 1087968824:
349
+ await message.reply_text(
350
+ text="You are an anonymous admin.",
351
+ reply_markup=InlineKeyboardMarkup(
352
+ [
353
+ [
354
+ InlineKeyboardButton(
355
+ text="Click to prove admin.",
356
+ callback_data=f"admin_=title={user_id}={title}",
357
+ ),
358
+ ],
359
+ ],
360
+ ),
361
+ )
362
+
363
+ return
364
+
365
+ try:
366
+ user_member = await chat.get_member(user_id)
367
+ except:
368
+ return
369
+
370
+ if not user_id:
371
+ await message.reply_text(
372
+ "You don't seem to be referring to a user or the ID specified is incorrect..",
373
+ )
374
+ return
375
+
376
+ if user_member.status == ChatMemberStatus.OWNER:
377
+ await message.reply_text(
378
+ "This person CREATED the chat, how can I set custom title for him?",
379
+ )
380
+ return
381
+
382
+ if user_member.status != ChatMemberStatus.ADMINISTRATOR:
383
+ await message.reply_text(
384
+ "Can't set title for non-admins!\nPromote them first to set custom title!",
385
+ )
386
+ return
387
+
388
+ if user_id == bot.id:
389
+ await message.reply_text(
390
+ "I can't set my own title myself! Get the one who made me admin to do it for me.",
391
+ )
392
+ return
393
+
394
+ if not title:
395
+ await message.reply_text("Setting a blank title doesn't do anything!")
396
+ return
397
+
398
+ if len(title) > 16:
399
+ await message.reply_text(
400
+ "The title length is longer than 16 characters.\nTruncating it to 16 characters.",
401
+ )
402
+
403
+ try:
404
+ await bot.setChatAdministratorCustomTitle(chat.id, user_id, title)
405
+ except BadRequest:
406
+ await message.reply_text(
407
+ "Either they aren't promoted by me or you set a title text that is impossible to set."
408
+ )
409
+ raise
410
+
411
+ await bot.sendMessage(
412
+ chat.id,
413
+ f"Successfully set title for <code>{user_member.user.first_name or user_id}</code> "
414
+ f"to <code>{html.escape(title[:16])}</code>!",
415
+ parse_mode=ParseMode.HTML,
416
+ message_thread_id=message.message_thread_id if chat.is_forum else None,
417
+ )
418
+
419
+
420
+ @loggable
421
+ @check_admin(permission="can_pin_messages", is_both=True)
422
+ async def pin(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
423
+ bot = context.bot
424
+ args = context.args
425
+
426
+ user = update.effective_user
427
+ chat = update.effective_chat
428
+ message = update.effective_message
429
+
430
+ is_group = chat.type != "private" and chat.type != "channel"
431
+ prev_message = update.effective_message.reply_to_message
432
+
433
+ is_silent = True
434
+ if len(args) >= 1:
435
+ is_silent = not (
436
+ args[0].lower() == "notify"
437
+ or args[0].lower() == "loud"
438
+ or args[0].lower() == "violent"
439
+ )
440
+
441
+ if not prev_message:
442
+ await message.reply_text("Please reply to message which you want to pin.")
443
+ return
444
+
445
+ if message.from_user.id == 1087968824:
446
+ await message.reply_text(
447
+ text="You are an anonymous admin.",
448
+ reply_markup=InlineKeyboardMarkup(
449
+ [
450
+ [
451
+ InlineKeyboardButton(
452
+ text="Click to prove admin.",
453
+ callback_data=f"admin_=pin={prev_message.message_id}={is_silent}",
454
+ ),
455
+ ],
456
+ ],
457
+ ),
458
+ )
459
+
460
+ return
461
+
462
+ if prev_message and is_group:
463
+ try:
464
+ await bot.pinChatMessage(
465
+ chat.id,
466
+ prev_message.message_id,
467
+ disable_notification=is_silent,
468
+ )
469
+ except BadRequest as excp:
470
+ if excp.message == "Chat_not_modified":
471
+ pass
472
+ else:
473
+ raise
474
+ log_message = (
475
+ f"{chat.title}:\n"
476
+ "#PINNED\n"
477
+ f"Admin: {mention_html(user.id, user.first_name)}"
478
+ )
479
+
480
+ return log_message
481
+
482
+
483
+ @loggable
484
+ @check_admin(permission="can_pin_messages", is_both=True)
485
+ async def unpin(update: Update, context: ContextTypes.DEFAULT_TYPE):
486
+ bot = context.bot
487
+ chat = update.effective_chat
488
+ user = update.effective_user
489
+ message = update.effective_message
490
+
491
+ if message.from_user.id == 1087968824:
492
+ await message.reply_text(
493
+ text="You are an anonymous admin.",
494
+ reply_markup=InlineKeyboardMarkup(
495
+ [
496
+ [
497
+ InlineKeyboardButton(
498
+ text="Click to prove Admin.",
499
+ callback_data=f"admin_=unpin",
500
+ ),
501
+ ],
502
+ ],
503
+ ),
504
+ )
505
+
506
+ return
507
+
508
+ try:
509
+ await bot.unpinChatMessage(chat.id)
510
+ except BadRequest as excp:
511
+ if excp.message == "Chat_not_modified":
512
+ pass
513
+ elif excp.message == "Message to unpin not found":
514
+ await message.reply_text("No pinned message found")
515
+ return
516
+ else:
517
+ raise
518
+
519
+ log_message = (
520
+ f"{chat.title}:\n"
521
+ "#UNPINNED\n"
522
+ f"Admin: {mention_html(user.id, user.first_name)}"
523
+ )
524
+
525
+ return log_message
526
+
527
+
528
+ @loggable
529
+ @check_admin(permission="can_pin_messages", is_both=True)
530
+ async def unpinall(update: Update, context: ContextTypes.DEFAULT_TYPE):
531
+ bot = context.bot
532
+ chat = update.effective_chat
533
+ user = update.effective_user
534
+ message = update.effective_message
535
+ admin_member = await chat.get_member(user.id)
536
+
537
+ if message.from_user.id == 1087968824:
538
+ await message.reply_text(
539
+ text="You are an anonymous admin.",
540
+ reply_markup=InlineKeyboardMarkup(
541
+ [
542
+ [
543
+ InlineKeyboardButton(
544
+ text="Click to prove admin.",
545
+ callback_data=f"admin_=unpinall",
546
+ ),
547
+ ],
548
+ ],
549
+ ),
550
+ )
551
+
552
+ return
553
+ elif not admin_member.status == ChatMemberStatus.OWNER and user.id not in DRAGONS:
554
+ await message.reply_text("Only chat OWNER can unpin all messages.")
555
+ return
556
+
557
+ try:
558
+ if chat.is_forum:
559
+ await bot.unpin_all_forum_topic_messages(chat.id, message.message_thread_id)
560
+ else:
561
+ await bot.unpin_all_chat_messages(chat.id)
562
+ except BadRequest as excp:
563
+ if excp.message == "Chat_not_modified":
564
+ pass
565
+ else:
566
+ raise
567
+
568
+ log_message = (
569
+ f"{chat.title}:\n"
570
+ "#UNPINNED_ALL\n"
571
+ f"Admin: {mention_html(user.id, user.first_name)}"
572
+ )
573
+
574
+ return log_message
575
+
576
+
577
+ @connection_status
578
+ @check_admin(permission="can_invite_users", is_bot=True)
579
+ async def invite(update: Update, context: ContextTypes.DEFAULT_TYPE):
580
+ bot = context.bot
581
+ chat = update.effective_chat
582
+
583
+ if chat.username:
584
+ await update.effective_message.reply_text(f"https://t.me/{chat.username}")
585
+ elif chat.type in [ChatType.SUPERGROUP, ChatType.CHANNEL]:
586
+ bot_member = await chat.get_member(bot.id)
587
+ if (
588
+ bot_member.can_invite_users
589
+ if isinstance(bot_member, ChatMemberAdministrator)
590
+ else None
591
+ ):
592
+ invitelink = await bot.exportChatInviteLink(chat.id)
593
+ await update.effective_message.reply_text(invitelink)
594
+ else:
595
+ await update.effective_message.reply_text(
596
+ "I don't have access to the invite link, try changing my permissions!",
597
+ )
598
+ else:
599
+ await update.effective_message.reply_text(
600
+ "I can only give you invite links for supergroups and channels, sorry!",
601
+ )
602
+
603
+
604
+ @connection_status
605
+ async def adminlist(update: Update, context: ContextTypes.DEFAULT_TYPE):
606
+ chat = update.effective_chat # type: Optional[Chat]
607
+ user = update.effective_user # type: Optional[User]
608
+ args = context.args
609
+ bot = context.bot
610
+ if update.effective_message.chat.type == "private":
611
+ await send_message(
612
+ update.effective_message, "This command only works in Groups."
613
+ )
614
+ return
615
+ chat = update.effective_chat
616
+ chat_id = update.effective_chat.id
617
+ chat_name = update.effective_message.chat.title
618
+ try:
619
+ msg = await update.effective_message.reply_text(
620
+ "Fetching group admins...", parse_mode=ParseMode.HTML
621
+ )
622
+ except BadRequest:
623
+ msg = await update.effective_message.reply_text(
624
+ "Fetching group admins...", quote=False, parse_mode=ParseMode.HTML
625
+ )
626
+ administrators = await bot.get_chat_administrators(chat_id)
627
+ administrators_list = list(administrators) # Convert to a list
628
+ text = "「 𝗔𝗗𝗠𝗜𝗡𝗦 𝗜𝗡 <b>{}</b>:".format(html.escape(update.effective_chat.title))
629
+ bot_admin_list = []
630
+ for admin in administrators_list:
631
+ user = admin.user
632
+ status = admin.status
633
+ custom_title = admin.custom_title
634
+ if user.first_name == "":
635
+ name = "☠ Deleted Account"
636
+ else:
637
+ name = "{}".format(
638
+ mention_html(
639
+ user.id, html.escape(user.first_name + " " + (user.last_name or ""))
640
+ )
641
+ )
642
+ if user.is_bot:
643
+ bot_admin_list.append(name)
644
+ administrators_list.remove(admin)
645
+ continue
646
+ if status == "creator":
647
+ text += "\n\n 👑 <b>Creator:</b>"
648
+ text += "\n<code> ╰─➽ </code>{}\n".format(name)
649
+ if custom_title:
650
+ text += f"<code> ┗━ {html.escape(custom_title)}</code>\n"
651
+ text += "\n🚓 <b>Admins:</b>"
652
+ custom_admin_list = {}
653
+ normal_admin_list = []
654
+ for admin in administrators_list:
655
+ user = admin.user
656
+ status = admin.status
657
+ custom_title = admin.custom_title
658
+ if user.first_name == "":
659
+ name = "☠ Deleted Account"
660
+ else:
661
+ name = "{}".format(
662
+ mention_html(
663
+ user.id, html.escape(user.first_name + " " + (user.last_name or ""))
664
+ )
665
+ )
666
+ if status == "administrator":
667
+ if custom_title:
668
+ try:
669
+ custom_admin_list[custom_title].append(name)
670
+ except KeyError:
671
+ custom_admin_list.update({custom_title: [name]})
672
+ else:
673
+ normal_admin_list.append(name)
674
+ for admin in normal_admin_list:
675
+ text += "\n<code> ╰─➽ </code>{}".format(admin)
676
+ for admin_group in custom_admin_list.copy():
677
+ if len(custom_admin_list[admin_group]) == 1:
678
+ text += "\n<code> ╰─➽ </code>{} | <code>{}</code>".format(
679
+ custom_admin_list[admin_group][0], html.escape(admin_group)
680
+ )
681
+ custom_admin_list.pop(admin_group)
682
+ text += "\n"
683
+ for admin_group in custom_admin_list:
684
+ text += "\n🚨 <code>{}</code>".format(admin_group)
685
+ for admin in custom_admin_list[admin_group]:
686
+ text += "\n<code> ╰─➽ </code>{}".format(admin)
687
+ text += "\n"
688
+ text += "\n🤖 <b>Bots:</b>"
689
+ for each_bot in bot_admin_list:
690
+ text += "\n<code> ╰─➽ </code>{}".format(each_bot)
691
+ try:
692
+ await msg.edit_text(text, parse_mode=ParseMode.HTML)
693
+ except BadRequest: # if the original message is deleted
694
+ return
695
+
696
+
697
+ @loggable
698
+ async def admin_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
699
+ query = update.callback_query
700
+ bot = context.bot
701
+ message = update.effective_message
702
+ chat = update.effective_chat
703
+ admin_user = query.from_user
704
+
705
+ splitter = query.data.replace("admin_", "").split("=")
706
+
707
+ if splitter[1] == "promote":
708
+ promoter = await chat.get_member(admin_user.id)
709
+
710
+ if (
711
+ not (
712
+ promoter.can_promote_members
713
+ if isinstance(promoter, ChatMemberAdministrator)
714
+ else None or promoter.status == ChatMemberStatus.OWNER
715
+ )
716
+ and admin_user.id not in DRAGONS
717
+ ):
718
+ await query.answer(
719
+ "You don't have the necessary rights to do that!", show_alert=True
720
+ )
721
+ return
722
+
723
+ try:
724
+ user_id = int(splitter[2])
725
+ except ValueError:
726
+ user_id = splitter[2]
727
+ await message.edit_text(
728
+ "You don't seem to be referring to a user or the ID specified is incorrect..."
729
+ )
730
+ return
731
+
732
+ try:
733
+ user_member = await chat.get_member(user_id)
734
+ except:
735
+ return
736
+
737
+ if (
738
+ user_member.status == ChatMemberStatus.ADMINISTRATOR
739
+ or user_member.status == ChatMemberStatus.OWNER
740
+ ):
741
+ await message.edit_text(
742
+ "How am I meant to promote someone that's already an admin?"
743
+ )
744
+ return
745
+
746
+ bot_member = await chat.get_member(bot.id)
747
+
748
+ if isinstance(bot_member, ChatMemberAdministrator):
749
+ try:
750
+ await bot.promoteChatMember(
751
+ chat.id,
752
+ user_id,
753
+ can_change_info=bot_member.can_change_info,
754
+ can_post_messages=bot_member.can_post_messages,
755
+ can_edit_messages=bot_member.can_edit_messages,
756
+ can_delete_messages=bot_member.can_delete_messages,
757
+ can_invite_users=bot_member.can_invite_users,
758
+ can_restrict_members=bot_member.can_restrict_members,
759
+ can_pin_messages=bot_member.can_pin_messages,
760
+ can_manage_chat=bot_member.can_manage_chat,
761
+ can_manage_video_chats=bot_member.can_manage_video_chats,
762
+ )
763
+ except BadRequest as err:
764
+ if err.message == "User_not_mutual_contact":
765
+ await message.edit_text(
766
+ "I can't promote someone who isn't in the group"
767
+ )
768
+ else:
769
+ await message.edit_text("An error occurred while promoting.")
770
+ return
771
+
772
+ await message.edit_text(
773
+ f"Successfully promoted <b>{user_member.user.first_name or user_id}</b>!",
774
+ parse_mode=ParseMode.HTML,
775
+ )
776
+ await query.answer("Done")
777
+
778
+ log_message = (
779
+ f"<b>{html.escape(chat.title)}:</b>\n"
780
+ f"#PROMOTED\n"
781
+ f"<b>Admin:</b> {mention_html(admin_user.id, admin_user.first_name)}\n"
782
+ f"<b>User:</b> {mention_html(user_member.user.id, user_member.user.first_name)}"
783
+ )
784
+
785
+ return log_message
786
+
787
+ elif splitter[1] == "demote":
788
+ demoter = await chat.get_member(admin_user.id)
789
+
790
+ if not (
791
+ demoter.can_promote_members
792
+ if isinstance(demoter, ChatMemberAdministrator)
793
+ else None or demoter.status == ChatMemberStatus.OWNER
794
+ ):
795
+ await query.answer(
796
+ "You don't have the necessary rights to do that!", show_alert=True
797
+ )
798
+ return
799
+
800
+ try:
801
+ user_id = int(splitter[2])
802
+ except:
803
+ user_id = splitter[2]
804
+ await message.edit_text(
805
+ "You don't seem to be referring to a user or the ID specified is incorrect.."
806
+ )
807
+ return
808
+
809
+ try:
810
+ user_member = await chat.get_member(user_id)
811
+ except:
812
+ return
813
+
814
+ if user_member.status == ChatMemberStatus.OWNER:
815
+ await message.edit_text(
816
+ "This person CREATED the chat, how would I demote them?"
817
+ )
818
+ return
819
+
820
+ if not user_member.status == ChatMemberStatus.ADMINISTRATOR:
821
+ await message.edit_text("Can't demote what wasn't promoted!")
822
+ return
823
+
824
+ if user_id == bot.id:
825
+ await message.edit_text(
826
+ "I can't demote myself!, get an admin to do it for me."
827
+ )
828
+ return
829
+
830
+ try:
831
+ await bot.promoteChatMember(
832
+ chat.id,
833
+ user_id,
834
+ can_change_info=False,
835
+ can_post_messages=False,
836
+ can_edit_messages=False,
837
+ can_delete_messages=False,
838
+ can_invite_users=False,
839
+ can_restrict_members=False,
840
+ can_pin_messages=False,
841
+ can_promote_members=False,
842
+ can_manage_chat=False,
843
+ can_manage_video_chats=False,
844
+ )
845
+
846
+ await message.edit_text(
847
+ f"Successfully demoted <b>{user_member.user.first_name or user_id}</b>!",
848
+ parse_mode=ParseMode.HTML,
849
+ )
850
+ await query.answer("Done")
851
+
852
+ log_message = (
853
+ f"<b>{html.escape(chat.title)}:</b>\n"
854
+ f"#DEMOTE\n"
855
+ f"<b>Admin:</b> {mention_html(admin_user.id, admin_user.first_name)}\n"
856
+ f"<b>User:</b> {mention_html(user_member.user.id, user_member.user.first_name)}"
857
+ )
858
+
859
+ return log_message
860
+ except BadRequest:
861
+ await message.edit_text(
862
+ "Could not demote. I might not be admin, or the admin status was appointed by another"
863
+ " user, so I can't act upon them!"
864
+ )
865
+ return
866
+
867
+ elif splitter[1] == "title":
868
+ title = splitter[3]
869
+
870
+ admin_member = await chat.get_member(admin_user.id)
871
+
872
+ if (
873
+ not (
874
+ (
875
+ admin_member.can_promote_members
876
+ if isinstance(admin_member, ChatMemberAdministrator)
877
+ else None
878
+ )
879
+ or admin_member.status == ChatMemberStatus.OWNER
880
+ )
881
+ and admin_user.id not in DRAGONS
882
+ ):
883
+ await query.answer("You don't have the necessary rights to do that!")
884
+ return
885
+
886
+ try:
887
+ user_id = int(splitter[2])
888
+ except:
889
+ await message.edit_text(
890
+ "You don't seem to be referring to a user or the ID specified is incorrect...",
891
+ )
892
+ return
893
+
894
+ try:
895
+ user_member = await chat.get_member(user_id)
896
+ except:
897
+ return
898
+
899
+ if user_member.status == ChatMemberStatus.OWNER:
900
+ await message.edit_text(
901
+ "This person CREATED the chat, how can I set a custom title for him?",
902
+ )
903
+ return
904
+
905
+ if user_member.status != ChatMemberStatus.ADMINISTRATOR:
906
+ await message.edit_text(
907
+ "Can't set a title for non-admins! Promote them first to set a custom title!",
908
+ )
909
+ return
910
+
911
+ if user_id == bot.id:
912
+ await message.edit_text(
913
+ "I can't set my own title myself! Get the one who made me admin to do it for me.",
914
+ )
915
+ return
916
+
917
+ if not title:
918
+ await message.edit_text("Setting a blank title doesn't do anything!")
919
+ return
920
+
921
+ if len(title) > 16:
922
+ await message.edit_text(
923
+ "The title length is longer than 16 characters. Truncating it to 16 characters.",
924
+ )
925
+
926
+ try:
927
+ await bot.setChatAdministratorCustomTitle(chat.id, user_id, title)
928
+ except BadRequest:
929
+ await message.edit_text(
930
+ "Either they aren't promoted by me or you set a title text that is impossible to set."
931
+ )
932
+ return
933
+
934
+ await message.edit_text(
935
+ text=f"Successfully set title for <code>{user_member.user.first_name or user_id}</code> "
936
+ f"to <code>{html.escape(title[:16])}</code>!",
937
+ parse_mode=ParseMode.HTML,
938
+ )
939
+
940
+ elif splitter[1] == "pin":
941
+ admin_member = await chat.get_member(admin_user.id)
942
+
943
+ if (
944
+ not (
945
+ (
946
+ admin_member.can_pin_messages
947
+ if isinstance(admin_member, ChatMemberAdministrator)
948
+ else None
949
+ )
950
+ or admin_member.status == ChatMemberStatus.OWNER
951
+ )
952
+ and admin_user.id not in DRAGONS
953
+ ):
954
+ await query.answer(
955
+ "You don't have the necessary rights to do that!", show_alert=True
956
+ )
957
+ return
958
+
959
+ try:
960
+ message_id = int(splitter[2])
961
+ except:
962
+ return
963
+
964
+ is_silent = bool(splitter[3])
965
+ is_group = chat.type != "private" and chat.type != "channel"
966
+
967
+ if is_group:
968
+ try:
969
+ await bot.pinChatMessage(
970
+ chat.id,
971
+ message_id,
972
+ disable_notification=is_silent,
973
+ )
974
+ except BadRequest as excp:
975
+ if excp.message == "Chat_not_modified":
976
+ pass
977
+ else:
978
+ raise
979
+
980
+ await message.edit_text("Done Pinned.")
981
+
982
+ log_message = (
983
+ f"<b>{html.escape(chat.title)}</b>\n"
984
+ f"#PINNED\n"
985
+ f"<b>Admin:</b> {mention_html(admin_user.id, html.escape(admin_user.first_name))}"
986
+ )
987
+
988
+ return log_message
989
+
990
+ elif splitter[1] == "unpin":
991
+ admin_member = await chat.get_member(admin_user.id)
992
+
993
+ if (
994
+ not (
995
+ (
996
+ admin_member.can_pin_messages
997
+ if isinstance(admin_member, ChatMemberAdministrator)
998
+ else None
999
+ )
1000
+ or admin_member.status == ChatMemberStatus.OWNER
1001
+ )
1002
+ and admin_user.id not in DRAGONS
1003
+ ):
1004
+ await query.answer(
1005
+ "You don't have the necessary rights to do that!",
1006
+ show_alert=True,
1007
+ )
1008
+ return
1009
+
1010
+ try:
1011
+ await bot.unpinChatMessage(chat.id)
1012
+ except BadRequest as excp:
1013
+ if excp.message == "Chat_not_modified":
1014
+ pass
1015
+ elif excp.message == "Message_to_unpin_not_found":
1016
+ await message.edit_text("No pinned message found")
1017
+ return
1018
+ else:
1019
+ raise
1020
+
1021
+ log_message = (
1022
+ f"<b>{html.escape(chat.title)}:</b>\n"
1023
+ f"#UNPINNED\n"
1024
+ f"<b>Admin:</b> {mention_html(admin_user.id, html.escape(admin_user.first_name))}"
1025
+ )
1026
+
1027
+ return log_message
1028
+
1029
+ elif splitter[1] == "unpinall":
1030
+ admin_member = await chat.get_member(admin_user.id)
1031
+
1032
+ if (
1033
+ not admin_member.status == ChatMemberStatus.OWNER
1034
+ and admin_user.id not in DRAGONS
1035
+ ):
1036
+ await query.answer("Only chat OWNER can unpin all messages.")
1037
+ return
1038
+
1039
+ try:
1040
+ if chat.is_forum:
1041
+ await bot.unpin_all_forum_topic_messages(
1042
+ chat.id, message.message_thread_id
1043
+ )
1044
+ else:
1045
+ await bot.unpin_all_chat_messages(chat.id)
1046
+ except BadRequest as excp:
1047
+ if excp.message == "Chat_not_modified":
1048
+ pass
1049
+ else:
1050
+ raise
1051
+
1052
+ await message.edit_text("Done unpinning all messages.")
1053
+ log_message = (
1054
+ f"<b>{html.escape(chat.title)}:</b>\n"
1055
+ f"#UNPINNED-ALL\n"
1056
+ f"<b>ADMIN:</b> {mention_html(admin_user.id, html.escape(admin_user.first_name))}"
1057
+ )
1058
+
1059
+ return log_message
1060
+
1061
+
1062
+ # <=================================================== HELP ====================================================>
1063
+
1064
+
1065
+ __help__ = """
1066
+ » /adminlist: List of admins in the chat.
1067
+
1068
+ ➠ *Admins only:*
1069
+
1070
+ » /pin: Silently pins the message replied to. Add 'loud' or 'notify' to give notifications to users.
1071
+
1072
+ » /unpin: Unpins the currently pinned message.
1073
+
1074
+ » /unpinall: Unpins all the pinned messages. Works in topics too (only OWNER can do this).
1075
+
1076
+ » /invitelink: Get an invite link.
1077
+
1078
+ » /promote: Promotes the user replied to.
1079
+
1080
+ » /fullpromote: FullPromotes the user replied to.
1081
+
1082
+ » /demote: Demotes the user replied to.
1083
+
1084
+ » /title <Title here>: Sets a custom title for an admin that the bot promoted.
1085
+
1086
+ » /admincache: Force refresh the admins list.
1087
+ """
1088
+
1089
+ # <================================================ HANDLER =======================================================>
1090
+ ADMINLIST_HANDLER = DisableAbleCommandHandler("adminlist", adminlist, block=False)
1091
+
1092
+ PIN_HANDLER = CommandHandler("pin", pin, filters=filters.ChatType.GROUPS, block=False)
1093
+ UNPIN_HANDLER = CommandHandler(
1094
+ "unpin", unpin, filters=filters.ChatType.GROUPS, block=False
1095
+ )
1096
+ UNPINALL_HANDLER = CommandHandler(
1097
+ "unpinall", unpinall, filters=filters.ChatType.GROUPS, block=False
1098
+ )
1099
+
1100
+ INVITE_HANDLER = DisableAbleCommandHandler("invitelink", invite, block=False)
1101
+
1102
+ PROMOTE_HANDLER = DisableAbleCommandHandler("promote", promote, block=False)
1103
+ FULLPROMOTE_HANDLER = DisableAbleCommandHandler("fullpromote", fullpromote, block=False)
1104
+ DEMOTE_HANDLER = DisableAbleCommandHandler("demote", demote, block=False)
1105
+
1106
+ SET_TITLE_HANDLER = CommandHandler("title", set_title, block=False)
1107
+ ADMIN_REFRESH_HANDLER = CommandHandler(
1108
+ "admincache", refresh_admin, filters=filters.ChatType.GROUPS, block=False
1109
+ )
1110
+ ADMIN_CALLBACK_HANDLER = CallbackQueryHandler(
1111
+ admin_callback, block=False, pattern=r"admin_"
1112
+ )
1113
+
1114
+ function(ADMINLIST_HANDLER)
1115
+ function(PIN_HANDLER)
1116
+ function(UNPIN_HANDLER)
1117
+ function(UNPINALL_HANDLER)
1118
+ function(INVITE_HANDLER)
1119
+ function(PROMOTE_HANDLER)
1120
+ function(FULLPROMOTE_HANDLER)
1121
+ function(DEMOTE_HANDLER)
1122
+ function(SET_TITLE_HANDLER)
1123
+ function(ADMIN_REFRESH_HANDLER)
1124
+ function(ADMIN_CALLBACK_HANDLER)
1125
+
1126
+ __mod_name__ = "ADMIN"
1127
+ __command_list__ = [
1128
+ "adminlist",
1129
+ "admins",
1130
+ "invitelink",
1131
+ "promote",
1132
+ "demote",
1133
+ "admincache",
1134
+ "fullpromote",
1135
+ "setgpic",
1136
+ "delgpic",
1137
+ ]
1138
+ __handlers__ = [
1139
+ ADMINLIST_HANDLER,
1140
+ PIN_HANDLER,
1141
+ UNPIN_HANDLER,
1142
+ INVITE_HANDLER,
1143
+ PROMOTE_HANDLER,
1144
+ DEMOTE_HANDLER,
1145
+ SET_TITLE_HANDLER,
1146
+ ADMIN_REFRESH_HANDLER,
1147
+ ]
1148
+ # <================================================ END =======================================================>
Mikobot/plugins/ai.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CREATED BY: https://t.me/O_oKarma
2
+ # API CREDITS: @Qewertyy
3
+ # PROVIDED BY: https://github.com/Team-ProjectCodeX
4
+
5
+ # <============================================== IMPORTS =========================================================>
6
+ import base64
7
+
8
+ from telegram import Update
9
+ from telegram.constants import ParseMode
10
+ from telegram.ext import CommandHandler, ContextTypes
11
+
12
+ from Mikobot import LOGGER as logger
13
+ from Mikobot import function
14
+ from Mikobot.state import state
15
+
16
+ # <=======================================================================================================>
17
+
18
+ # <================================================ CONSTANTS =====================================================>
19
+ PALM_API_URL = "https://lexica.qewertyy.me/models"
20
+ GPT_API_URL = "https://lexica.qewertyy.me/models"
21
+ PALM_MODEL_ID = 0
22
+ GPT_MODEL_ID = 5
23
+
24
+ # <================================================ FUNCTIONS =====================================================>
25
+
26
+
27
+ async def get_api_response(model_id, api_params, api_url):
28
+ try:
29
+ response = await state.post(api_url, params=api_params)
30
+ if response.status_code == 200:
31
+ data = response.json()
32
+ return data.get(
33
+ "content", f"Error: Empty response received from the {model_id} API."
34
+ )
35
+ else:
36
+ return f"Error: Request failed with status code {response.status_code}."
37
+ except state.RequestError as e:
38
+ return f"Error: An error occurred while calling the {model_id} API. {e}"
39
+
40
+
41
+ async def palm_chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE):
42
+ args = context.args
43
+ if not args:
44
+ await context.bot.send_message(
45
+ chat_id=update.effective_chat.id,
46
+ text="Error: Missing input text after /palm command.",
47
+ )
48
+ return
49
+
50
+ input_text = " ".join(args)
51
+
52
+ result_msg = await context.bot.send_message(
53
+ chat_id=update.effective_chat.id, text="🌴"
54
+ )
55
+
56
+ api_params = {"model_id": PALM_MODEL_ID, "prompt": input_text}
57
+ api_response = await get_api_response("PALM", api_params, PALM_API_URL)
58
+
59
+ await result_msg.delete()
60
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=api_response)
61
+
62
+
63
+ async def gpt_chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE):
64
+ args = context.args
65
+ if not args:
66
+ await context.bot.send_message(
67
+ chat_id=update.effective_chat.id,
68
+ text="Error: Missing input text after /askgpt command.",
69
+ )
70
+ return
71
+
72
+ input_text = " ".join(args)
73
+
74
+ result_msg = await context.bot.send_message(
75
+ chat_id=update.effective_chat.id, text="💬"
76
+ )
77
+
78
+ api_params = {"model_id": GPT_MODEL_ID, "prompt": input_text}
79
+ api_response = await get_api_response("GPT", api_params, GPT_API_URL)
80
+
81
+ await result_msg.delete()
82
+ await context.bot.send_message(chat_id=update.effective_chat.id, text=api_response)
83
+
84
+
85
+ # Define the upscale_image function
86
+ async def upscale_image(update: Update, context: ContextTypes.DEFAULT_TYPE):
87
+ try:
88
+ # Check if the replied message contains a photo
89
+ if update.message.reply_to_message and update.message.reply_to_message.photo:
90
+ # Send a message indicating upscaling is in progress
91
+ progress_msg = await update.message.reply_text(
92
+ "Upscaling your image, please wait..."
93
+ )
94
+
95
+ # Access the image file_id from the replied message
96
+ image = await update.message.reply_to_message.photo[-1].get_file()
97
+
98
+ # Download the image and save it
99
+ image_path = await image.download_to_drive()
100
+
101
+ with open(image_path, "rb") as image_file:
102
+ f = image_file.read()
103
+
104
+ b = base64.b64encode(f).decode("utf-8")
105
+
106
+ response = await state.post(
107
+ "https://lexica.qewertyy.me/upscale",
108
+ data={"image_data": b},
109
+ )
110
+
111
+ # Save the upscaled image
112
+ upscaled_file_path = "upscaled_image.png"
113
+ with open(upscaled_file_path, "wb") as output_file:
114
+ output_file.write(response.content)
115
+
116
+ # Delete the progress message
117
+ await context.bot.delete_message(
118
+ chat_id=update.message.chat_id, message_id=progress_msg.message_id
119
+ )
120
+
121
+ # Send the upscaled image as a PNG file
122
+ await update.message.reply_document(
123
+ document=open(upscaled_file_path, "rb"),
124
+ caption=f"<b>Upscaled your image.</b>\n<b>Generated By:</b> @{context.bot.username}",
125
+ parse_mode=ParseMode.HTML,
126
+ )
127
+ else:
128
+ await update.message.reply_text("Please reply to an image to upscale it.")
129
+
130
+ except Exception as e:
131
+ logger.error(f"Failed to upscale the image: {e}")
132
+ await update.message.reply_text(
133
+ "Failed to upscale the image. Please try again later."
134
+ )
135
+
136
+
137
+ # <================================================ HANDLER =======================================================>
138
+ # Register the upscale_image command handler
139
+ function(CommandHandler("upscale", upscale_image, block=False))
140
+ function(CommandHandler("palm", palm_chatbot, block=False))
141
+ function(CommandHandler("askgpt", gpt_chatbot, block=False))
142
+ # <================================================ END =======================================================>
Mikobot/plugins/alive.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOURCE https://github.com/Team-ProjectCodeX
2
+ # CREATED BY https://t.me/O_okarma
3
+ # PROVIDED BY https://t.me/ProjectCodeX
4
+
5
+ # <============================================== IMPORTS =========================================================>
6
+ import random
7
+ from sys import version_info
8
+
9
+ import pyrogram
10
+ import telegram
11
+ import telethon
12
+ from pyrogram import filters
13
+ from pyrogram.types import InlineKeyboardMarkup, Message
14
+
15
+ from Infamous.karma import ALIVE_ANIMATION, ALIVE_BTN
16
+ from Mikobot import BOT_NAME, app
17
+
18
+ # <=======================================================================================================>
19
+
20
+
21
+ # <================================================ FUNCTION =======================================================>
22
+ @app.on_message(filters.command("alive"))
23
+ async def alive(_, message: Message):
24
+ library_versions = {
25
+ "PTB": telegram.__version__,
26
+ "TELETHON": telethon.__version__,
27
+ "PYROGRAM": pyrogram.__version__,
28
+ }
29
+
30
+ library_versions_text = "\n".join(
31
+ [f"➲ **{key}:** `{value}`" for key, value in library_versions.items()]
32
+ )
33
+
34
+ caption = f"""**HEY, I AM {BOT_NAME}**
35
+
36
+ ━━━━━━ 🌟✿🌟 ━━━━━━
37
+ ✪ **CREATOR:** [🄺🄰🅁🄼🄰](https://t.me/anime_Freakz)
38
+
39
+ {library_versions_text}
40
+
41
+ ➲ **PYTHON:** `{version_info[0]}.{version_info[1]}.{version_info[2]}`
42
+ ➲ **BOT VERSION:** `2.0`
43
+ ━━━━━━ 🌟✿🌟 ━━━━━━"""
44
+
45
+ await message.reply_animation(
46
+ random.choice(ALIVE_ANIMATION),
47
+ caption=caption,
48
+ reply_markup=InlineKeyboardMarkup(ALIVE_BTN),
49
+ )
50
+
51
+
52
+ # <=======================================================================================================>
53
+
54
+
55
+ # <================================================ NAME =======================================================>
56
+ __mod_name__ = "ALIVE"
57
+ # <================================================ END =======================================================>
Mikobot/plugins/anime.py ADDED
The diff for this file is too large to render. See raw diff
 
Mikobot/plugins/antinsfw.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ from os import remove
3
+
4
+ from pyrogram import filters
5
+
6
+ from Database.mongodb.toggle_mongo import is_nsfw_on, nsfw_off, nsfw_on
7
+ from Mikobot import BOT_USERNAME, DRAGONS, app
8
+ from Mikobot.state import arq
9
+ from Mikobot.utils.can_restrict import can_restrict
10
+ from Mikobot.utils.errors import capture_err
11
+
12
+ # <=======================================================================================================>
13
+
14
+
15
+ # <================================================ FUNCTION =======================================================>
16
+ async def get_file_id_from_message(message):
17
+ file_id = None
18
+ if message.document:
19
+ if int(message.document.file_size) > 3145728:
20
+ return
21
+ mime_type = message.document.mime_type
22
+ if mime_type not in ("image/png", "image/jpeg"):
23
+ return
24
+ file_id = message.document.file_id
25
+
26
+ if message.sticker:
27
+ if message.sticker.is_animated:
28
+ if not message.sticker.thumbs:
29
+ return
30
+ file_id = message.sticker.thumbs[0].file_id
31
+ else:
32
+ file_id = message.sticker.file_id
33
+
34
+ if message.photo:
35
+ file_id = message.photo.file_id
36
+
37
+ if message.animation:
38
+ if not message.animation.thumbs:
39
+ return
40
+ file_id = message.animation.thumbs[0].file_id
41
+
42
+ if message.video:
43
+ if not message.video.thumbs:
44
+ return
45
+ file_id = message.video.thumbs[0].file_id
46
+ return file_id
47
+
48
+
49
+ @app.on_message(
50
+ (
51
+ filters.document
52
+ | filters.photo
53
+ | filters.sticker
54
+ | filters.animation
55
+ | filters.video
56
+ )
57
+ & ~filters.private,
58
+ group=8,
59
+ )
60
+ @capture_err
61
+ async def detect_nsfw(_, message):
62
+ if not await is_nsfw_on(message.chat.id):
63
+ return
64
+ if not message.from_user:
65
+ return
66
+ file_id = await get_file_id_from_message(message)
67
+ if not file_id:
68
+ return
69
+ file = await _.download_media(file_id)
70
+ try:
71
+ results = await arq.nsfw_scan(file=file)
72
+ except Exception:
73
+ return
74
+ if not results.ok:
75
+ return
76
+ results = results.result
77
+ remove(file)
78
+ nsfw = results.is_nsfw
79
+ if message.from_user.id in DRAGONS:
80
+ return
81
+ if not nsfw:
82
+ return
83
+ try:
84
+ await message.delete()
85
+ except Exception:
86
+ return
87
+ await message.reply_text(
88
+ f"""
89
+ **🔞 NSFW Image Detected & Deleted Successfully!**
90
+
91
+ **✪ User:** {message.from_user.mention} [`{message.from_user.id}`]
92
+ **✪ Safe:** `{results.neutral} %`
93
+ **✪ Porn:** `{results.porn} %`
94
+ **✪ Adult:** `{results.sexy} %`
95
+ **✪ Hentai:** `{results.hentai} %`
96
+ **✪ Drawings:** `{results.drawings} %`
97
+ """
98
+ )
99
+
100
+
101
+ @app.on_message(filters.command(["nsfwscan", f"nsfwscan@{BOT_USERNAME}"]))
102
+ @capture_err
103
+ async def nsfw_scan_command(_, message):
104
+ if not message.reply_to_message:
105
+ await message.reply_text(
106
+ "Reply to an image/document/sticker/animation to scan it."
107
+ )
108
+ return
109
+ reply = message.reply_to_message
110
+ if (
111
+ not reply.document
112
+ and not reply.photo
113
+ and not reply.sticker
114
+ and not reply.animation
115
+ and not reply.video
116
+ ):
117
+ await message.reply_text(
118
+ "Reply to an image/document/sticker/animation to scan it."
119
+ )
120
+ return
121
+ m = await message.reply_text("Scanning")
122
+ file_id = await get_file_id_from_message(reply)
123
+ if not file_id:
124
+ return await m.edit("Something wrong happened.")
125
+ file = await _.download_media(file_id)
126
+ try:
127
+ results = await arq.nsfw_scan(file=file)
128
+ except Exception:
129
+ return
130
+ remove(file)
131
+ if not results.ok:
132
+ return await m.edit(results.result)
133
+ results = results.result
134
+ await m.edit(
135
+ f"""
136
+ **➢ Neutral:** `{results.neutral} %`
137
+ **➢ Porn:** `{results.porn} %`
138
+ **➢ Hentai:** `{results.hentai} %`
139
+ **➢ Sexy:** `{results.sexy} %`
140
+ **➢ Drawings:** `{results.drawings} %`
141
+ **➢ NSFW:** `{results.is_nsfw}`
142
+ """
143
+ )
144
+
145
+
146
+ @app.on_message(
147
+ filters.command(["antinsfw", f"antinsfw@{BOT_USERNAME}"]) & ~filters.private
148
+ )
149
+ @can_restrict
150
+ async def nsfw_enable_disable(_, message):
151
+ if len(message.command) != 2:
152
+ await message.reply_text("Usage: /antinsfw [on/off]")
153
+ return
154
+ status = message.text.split(None, 1)[1].strip()
155
+ status = status.lower()
156
+ chat_id = message.chat.id
157
+ if status in ("on", "yes"):
158
+ if await is_nsfw_on(chat_id):
159
+ await message.reply_text("Antinsfw is already enabled.")
160
+ return
161
+ await nsfw_on(chat_id)
162
+ await message.reply_text(
163
+ "Enabled AntiNSFW System. I will Delete Messages Containing Inappropriate Content."
164
+ )
165
+ elif status in ("off", "no"):
166
+ if not await is_nsfw_on(chat_id):
167
+ await message.reply_text("Antinsfw is already disabled.")
168
+ return
169
+ await nsfw_off(chat_id)
170
+ await message.reply_text("Disabled AntiNSFW System.")
171
+ else:
172
+ await message.reply_text("Unknown Suffix, Use /antinsfw [on/off]")
173
+
174
+
175
+ # <=================================================== HELP ====================================================>
176
+
177
+
178
+ __mod_name__ = "ANTI-NSFW"
179
+
180
+ __help__ = """
181
+ *🔞 Helps in detecting NSFW material and removing it*.
182
+
183
+ ➠ *Usage:*
184
+
185
+ » /antinsfw [on/off]: Enables Anti-NSFW system.
186
+
187
+ » /nsfwscan <reply to message>: Scans the file replied to.
188
+ """
189
+ # <================================================ END =======================================================>
Mikobot/plugins/ban.py ADDED
@@ -0,0 +1,1045 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ from random import choice
3
+ from traceback import format_exc
4
+
5
+ from pyrogram import enums
6
+ from pyrogram.errors import (
7
+ ChatAdminRequired,
8
+ PeerIdInvalid,
9
+ RightForbidden,
10
+ RPCError,
11
+ UserAdminInvalid,
12
+ )
13
+ from pyrogram.filters import regex
14
+ from pyrogram.types import (
15
+ CallbackQuery,
16
+ InlineKeyboardButton,
17
+ InlineKeyboardMarkup,
18
+ Message,
19
+ )
20
+
21
+ from Infamous.karma import BAN_GIFS, KICK_GIFS
22
+ from Mikobot import BOT_ID, LOGGER, MESSAGE_DUMP, OWNER_ID, SUPPORT_STAFF, app
23
+ from Mikobot.utils.caching import ADMIN_CACHE, admin_cache_reload
24
+ from Mikobot.utils.custom_filters import command, restrict_filter
25
+ from Mikobot.utils.extract_user import extract_user
26
+ from Mikobot.utils.parser import mention_html
27
+ from Mikobot.utils.string import extract_time
28
+
29
+ # <=======================================================================================================>
30
+
31
+
32
+ # <================================================ FUNCTION =======================================================>
33
+ @app.on_message(command("tban") & restrict_filter)
34
+ async def tban_usr(c: app, m: Message):
35
+ if len(m.text.split()) == 1 and not m.reply_to_message:
36
+ await m.reply_text(text="I can't ban nothing!")
37
+ await m.stop_propagation()
38
+
39
+ try:
40
+ user_id, user_first_name, _ = await extract_user(c, m)
41
+ except Exception:
42
+ return
43
+
44
+ if not user_id:
45
+ await m.reply_text("Cannot find user to ban")
46
+ return
47
+ if user_id == BOT_ID:
48
+ await m.reply_text("WTF?? Why would I ban myself?")
49
+ await m.stop_propagation()
50
+
51
+ if user_id in SUPPORT_STAFF:
52
+ await m.reply_text(
53
+ text="This user is in my support staff, cannot restrict them."
54
+ )
55
+ LOGGER.info(
56
+ f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
57
+ )
58
+ await m.stop_propagation()
59
+
60
+ r_id = m.reply_to_message.id if m.reply_to_message else m.id
61
+
62
+ if m.reply_to_message and len(m.text.split()) >= 2:
63
+ reason = m.text.split(None, 1)[1]
64
+ elif not m.reply_to_message and len(m.text.split()) >= 3:
65
+ reason = m.text.split(None, 2)[2]
66
+ else:
67
+ await m.reply_text("Read /help !!")
68
+ return
69
+
70
+ if not reason:
71
+ await m.reply_text("You haven't specified a time to ban this user for!")
72
+ return
73
+
74
+ split_reason = reason.split(None, 1)
75
+ time_val = split_reason[0].lower()
76
+ reason = split_reason[1] if len(split_reason) > 1 else ""
77
+
78
+ bantime = await extract_time(m, time_val)
79
+
80
+ if not bantime:
81
+ return
82
+
83
+ try:
84
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
85
+ except KeyError:
86
+ admins_group = await admin_cache_reload(m, "ban")
87
+
88
+ if user_id in admins_group:
89
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
90
+ await m.stop_propagation()
91
+
92
+ try:
93
+ admin = await mention_html(m.from_user.first_name, m.from_user.id)
94
+ banned = await mention_html(user_first_name, user_id)
95
+ chat_title = m.chat.title
96
+ LOGGER.info(f"{m.from_user.id} tbanned {user_id} in {m.chat.id}")
97
+ await m.chat.ban_member(user_id, until_date=bantime)
98
+ t_t = (f"{admin} banned {banned} in <b>{chat_title}</b>!",)
99
+ txt = t_t
100
+ if type(t_t) is tuple:
101
+ txt = t_t[
102
+ 0
103
+ ] # Done this bcuz idk why t_t is tuple type data. SO now if it is tuple this will get text from it
104
+ if reason:
105
+ txt += f"\n<b>Reason</b>: {reason}"
106
+ else:
107
+ txt += "\n<b>Reason</b>: Not Specified"
108
+ if time_val:
109
+ txt += f"\n<b>Banned for</b>:{time_val}"
110
+ keyboard = InlineKeyboardMarkup(
111
+ [
112
+ [
113
+ InlineKeyboardButton(
114
+ "UNBAN",
115
+ callback_data=f"unban_={user_id}",
116
+ ),
117
+ ],
118
+ ],
119
+ )
120
+ anim = choice(BAN_GIFS)
121
+ try:
122
+ await m.reply_animation(
123
+ reply_to_message_id=r_id,
124
+ animation=str(anim),
125
+ caption=txt,
126
+ reply_markup=keyboard,
127
+ parse_mode=enums.ParseMode.HTML,
128
+ )
129
+ except Exception:
130
+ await m.reply_text(
131
+ reply_to_message_id=r_id,
132
+ text=txt,
133
+ reply_markup=keyboard,
134
+ parse_mode=enums.ParseMode.HTML,
135
+ )
136
+ await c.send_message(MESSAGE_DUMP, f"#REMOVE from BAN_GFIS\n{anim}")
137
+ # await m.reply_text(txt, reply_markup=keyboard,
138
+ # reply_to_message_id=r_id)
139
+ except ChatAdminRequired:
140
+ await m.reply_text(text="I'm not admin or I don't have rights.")
141
+ except PeerIdInvalid:
142
+ await m.reply_text(
143
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
144
+ )
145
+ except UserAdminInvalid:
146
+ await m.reply_text(
147
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
148
+ )
149
+ except RightForbidden:
150
+ await m.reply_text(text="I don't have enough rights to ban this user.")
151
+ except RPCError as ef:
152
+ await m.reply_text(
153
+ (
154
+ f"""Some error occured, report it using `/bug`
155
+
156
+ <b>Error:</b> <code>{ef}</code>"""
157
+ )
158
+ )
159
+ LOGGER.error(ef)
160
+ LOGGER.error(format_exc())
161
+ return
162
+
163
+
164
+ @app.on_message(command("stban") & restrict_filter)
165
+ async def stban_usr(c: app, m: Message):
166
+ if len(m.text.split()) == 1 and not m.reply_to_message:
167
+ await m.reply_text(text="I can't ban nothing!")
168
+ await m.stop_propagation()
169
+
170
+ try:
171
+ user_id, _, _ = await extract_user(c, m)
172
+ except Exception:
173
+ return
174
+
175
+ if not user_id:
176
+ await m.reply_text("Cannot find user to ban")
177
+ return
178
+ if user_id == BOT_ID:
179
+ await m.reply_text("What the heck? Why would I ban myself?")
180
+ await m.stop_propagation()
181
+
182
+ if user_id in SUPPORT_STAFF:
183
+ await m.reply_text(
184
+ text="This user is in my support staff, cannot restrict them."
185
+ )
186
+ LOGGER.info(
187
+ f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
188
+ )
189
+ await m.stop_propagation()
190
+
191
+ if m.reply_to_message and len(m.text.split()) >= 2:
192
+ reason = m.text.split(None, 1)[1]
193
+ elif not m.reply_to_message and len(m.text.split()) >= 3:
194
+ reason = m.text.split(None, 2)[2]
195
+ else:
196
+ await m.reply_text("Read /help !!")
197
+ return
198
+
199
+ if not reason:
200
+ await m.reply_text("You haven't specified a time to ban this user for!")
201
+ return
202
+
203
+ split_reason = reason.split(None, 1)
204
+ time_val = split_reason[0].lower()
205
+ reason = split_reason[1] if len(split_reason) > 1 else ""
206
+
207
+ bantime = await extract_time(m, time_val)
208
+
209
+ if not bantime:
210
+ return
211
+
212
+ try:
213
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
214
+ except KeyError:
215
+ admins_group = await admin_cache_reload(m, "ban")
216
+
217
+ if user_id in admins_group:
218
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
219
+ await m.stop_propagation()
220
+
221
+ try:
222
+ LOGGER.info(f"{m.from_user.id} stbanned {user_id} in {m.chat.id}")
223
+ await m.chat.ban_member(user_id, until_date=bantime)
224
+ await m.delete()
225
+ if m.reply_to_message:
226
+ await m.reply_to_message.delete()
227
+ return
228
+ return
229
+ except ChatAdminRequired:
230
+ await m.reply_text(text="I'm not admin or I don't have rights.")
231
+ except PeerIdInvalid:
232
+ await m.reply_text(
233
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
234
+ )
235
+ except UserAdminInvalid:
236
+ await m.reply_text(
237
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
238
+ )
239
+ except RightForbidden:
240
+ await m.reply_text(text="I don't have enough rights to ban this user.")
241
+ except RPCError as ef:
242
+ await m.reply_text(
243
+ text=f"""Some error occured, report it using `/bug`
244
+
245
+ <b>Error:</b> <code>{ef}</code>"""
246
+ )
247
+ LOGGER.error(ef)
248
+ LOGGER.error(format_exc())
249
+ return
250
+
251
+
252
+ @app.on_message(command("dtban") & restrict_filter)
253
+ async def dtban_usr(c: app, m: Message):
254
+ if len(m.text.split()) == 1 and not m.reply_to_message:
255
+ await m.reply_text(text="I can't ban nothing!")
256
+ await m.stop_propagation()
257
+
258
+ if not m.reply_to_message:
259
+ await m.reply_text(
260
+ "Reply to a message with this command to temp ban and delete the message.",
261
+ )
262
+ await m.stop_propagation()
263
+
264
+ user_id = m.reply_to_message.from_user.id
265
+ user_first_name = m.reply_to_message.from_user.first_name
266
+
267
+ if not user_id:
268
+ await m.reply_text("Cannot find user to ban")
269
+ return
270
+ if user_id == BOT_ID:
271
+ await m.reply_text("Huh, why would I ban myself?")
272
+ await m.stop_propagation()
273
+
274
+ if user_id in SUPPORT_STAFF:
275
+ await m.reply_text(text="I am not going to ban one of my support staff")
276
+ LOGGER.info(
277
+ f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
278
+ )
279
+ await m.stop_propagation()
280
+
281
+ if m.reply_to_message and len(m.text.split()) >= 2:
282
+ reason = m.text.split(None, 1)[1]
283
+ elif not m.reply_to_message and len(m.text.split()) >= 3:
284
+ reason = m.text.split(None, 2)[2]
285
+ else:
286
+ await m.reply_text("Read /help !!")
287
+ return
288
+
289
+ if not reason:
290
+ await m.reply_text("You haven't specified a time to ban this user for!")
291
+ return
292
+
293
+ split_reason = reason.split(None, 1)
294
+ time_val = split_reason[0].lower()
295
+ reason = split_reason[1] if len(split_reason) > 1 else ""
296
+
297
+ bantime = await extract_time(m, time_val)
298
+
299
+ if not bantime:
300
+ return
301
+
302
+ try:
303
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
304
+ except KeyError:
305
+ admins_group = await admin_cache_reload(m, "ban")
306
+
307
+ if user_id in admins_group:
308
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
309
+ await m.stop_propagation()
310
+
311
+ try:
312
+ admin = await mention_html(m.from_user.first_name, m.from_user.id)
313
+ banned = await mention_html(user_first_name, user_id)
314
+ chat_title = m.chat.title
315
+ LOGGER.info(f"{m.from_user.id} dtbanned {user_id} in {m.chat.id}")
316
+ await m.chat.ban_member(user_id, until_date=bantime)
317
+ await m.reply_to_message.delete()
318
+ txt = f"{admin} banned {banned} in <b>{chat_title}</b>!"
319
+ if reason:
320
+ txt += f"\n<b>Reason</b>: {reason}"
321
+ else:
322
+ txt += "\n<b>Reason</b>: Not Specified"
323
+
324
+ if bantime:
325
+ txt += f"\n<b>Banned for</b>: {time_val}"
326
+ keyboard = InlineKeyboardMarkup(
327
+ [
328
+ [
329
+ InlineKeyboardButton(
330
+ "UNBAN",
331
+ callback_data=f"unban_={user_id}",
332
+ ),
333
+ ],
334
+ ],
335
+ )
336
+ anim = choice(BAN_GIFS)
337
+ try:
338
+ await m.reply_animation(
339
+ animation=str(anim),
340
+ caption=txt,
341
+ reply_markup=keyboard,
342
+ parse_mode=enums.ParseMode.HTML,
343
+ )
344
+ except Exception:
345
+ await m.reply_text(
346
+ txt,
347
+ reply_markup=keyboard,
348
+ parse_mode=enums.ParseMode.HTML,
349
+ )
350
+ await c.send_message(MESSAGE_DUMP, f"#REMOVE from BAN_GFIS\n{anim}")
351
+ # await c.send_message(m.chat.id, txt, reply_markup=keyboard)
352
+ except ChatAdminRequired:
353
+ await m.reply_text(text="I'm not admin or I don't have rights.")
354
+ except PeerIdInvalid:
355
+ await m.reply_text(
356
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
357
+ )
358
+ except UserAdminInvalid:
359
+ await m.reply_text(
360
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
361
+ )
362
+ except RightForbidden:
363
+ await m.reply_text(text="I don't have enough rights to ban this user.")
364
+ except RPCError as ef:
365
+ await m.reply_text(
366
+ text=f"""Some error occured, report it using `/bug`
367
+
368
+ <b>Error:</b> <code>{ef}</code>"""
369
+ )
370
+ LOGGER.error(ef)
371
+ LOGGER.error(format_exc())
372
+ return
373
+
374
+
375
+ @app.on_message(command("kick") & restrict_filter)
376
+ async def kick_usr(c: app, m: Message):
377
+ if len(m.text.split()) == 1 and not m.reply_to_message:
378
+ await m.reply_text(text="I can't kick nothing!")
379
+ return
380
+
381
+ reason = None
382
+
383
+ if m.reply_to_message:
384
+ r_id = m.reply_to_message.id
385
+ if len(m.text.split()) >= 2:
386
+ reason = m.text.split(None, 1)[1]
387
+ else:
388
+ r_id = m.id
389
+ if len(m.text.split()) >= 3:
390
+ reason = m.text.split(None, 2)[2]
391
+ try:
392
+ user_id, user_first_name, _ = await extract_user(c, m)
393
+ except Exception:
394
+ return
395
+
396
+ if not user_id:
397
+ await m.reply_text("Cannot find user to kick")
398
+ return
399
+
400
+ if user_id == BOT_ID:
401
+ await m.reply_text("Huh, why would I kick myself?")
402
+ await m.stop_propagation()
403
+
404
+ if user_id in SUPPORT_STAFF:
405
+ await m.reply_text(
406
+ text="This user is in my support staff, cannot restrict them."
407
+ )
408
+ LOGGER.info(
409
+ f"{m.from_user.id} trying to kick {user_id} (SUPPORT_STAFF) in {m.chat.id}",
410
+ )
411
+ await m.stop_propagation()
412
+
413
+ try:
414
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
415
+ except KeyError:
416
+ admins_group = await admin_cache_reload(m, "kick")
417
+
418
+ if user_id in admins_group:
419
+ await m.reply_text(text="This user is an admin, I cannot kick them!")
420
+ await m.stop_propagation()
421
+
422
+ try:
423
+ admin = await mention_html(m.from_user.first_name, m.from_user.id)
424
+ kicked = await mention_html(user_first_name, user_id)
425
+ chat_title = m.chat.title
426
+ LOGGER.info(f"{m.from_user.id} kicked {user_id} in {m.chat.id}")
427
+ await m.chat.ban_member(user_id)
428
+ txt = f"{admin} kicked {kicked} in <b>{chat_title}</b>!"
429
+ if reason:
430
+ txt += f"\n<b>Reason</b>: {reason}"
431
+ else:
432
+ txt += "\n<b>Reason</b>: Not Specified"
433
+ # await m.reply_text(txt, reply_to_message_id=r_id)
434
+ kickk = choice(KICK_GIFS)
435
+ try:
436
+ await m.reply_animation(
437
+ reply_to_message_id=r_id,
438
+ animation=str(kickk),
439
+ caption=txt,
440
+ parse_mode=enums.ParseMode.HTML,
441
+ )
442
+ except:
443
+ await m.reply_text(
444
+ reply_to_message_id=r_id,
445
+ text=txt,
446
+ parse_mode=enums.ParseMode.HTML,
447
+ )
448
+ await c.send_message(MESSAGE_DUMP, f"#REMOVE from KICK_GFIS\n{kickk}")
449
+ await m.chat.unban_member(user_id)
450
+ except ChatAdminRequired:
451
+ await m.reply_text(text="I'm not admin or I don't have rights.")
452
+ except PeerIdInvalid:
453
+ await m.reply_text(
454
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
455
+ )
456
+ except UserAdminInvalid:
457
+ await m.reply_text(
458
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
459
+ )
460
+ except RightForbidden:
461
+ await m.reply_text(text="I don't have enough rights to ban this user.")
462
+ except RPCError as ef:
463
+ await m.reply_text(
464
+ text=f"""Some error occured, report it using `/bug`
465
+
466
+ <b>Error:</b> <code>{ef}</code>"""
467
+ )
468
+ LOGGER.error(ef)
469
+ LOGGER.error(format_exc())
470
+
471
+ return
472
+
473
+
474
+ @app.on_message(command("skick") & restrict_filter)
475
+ async def skick_usr(c: app, m: Message):
476
+ if len(m.text.split()) == 1 and not m.reply_to_message:
477
+ await m.reply_text(text="I can't kick nothing!")
478
+ return
479
+
480
+ try:
481
+ user_id, _, _ = await extract_user(c, m)
482
+ except Exception:
483
+ return
484
+
485
+ if not user_id:
486
+ await m.reply_text("Cannot find user to kick")
487
+ return
488
+
489
+ if user_id == BOT_ID:
490
+ await m.reply_text("Huh, why would I kick myself?")
491
+ await m.stop_propagation()
492
+
493
+ if user_id in SUPPORT_STAFF:
494
+ await m.reply_text(
495
+ text="This user is in my support staff, cannot restrict them."
496
+ )
497
+ LOGGER.info(
498
+ f"{m.from_user.id} trying to skick {user_id} (SUPPORT_STAFF) in {m.chat.id}",
499
+ )
500
+ await m.stop_propagation()
501
+
502
+ try:
503
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
504
+ except KeyError:
505
+ admins_group = await admin_cache_reload(m, "kick")
506
+
507
+ if user_id in admins_group:
508
+ await m.reply_text(text="This user is an admin, I cannot kick them!")
509
+ await m.stop_propagation()
510
+
511
+ try:
512
+ LOGGER.info(f"{m.from_user.id} skicked {user_id} in {m.chat.id}")
513
+ await m.chat.ban_member(user_id)
514
+ await m.delete()
515
+ if m.reply_to_message:
516
+ await m.reply_to_message.delete()
517
+ await m.chat.unban_member(user_id)
518
+ except ChatAdminRequired:
519
+ await m.reply_text(text="I'm not admin or I don't have rights.")
520
+ except PeerIdInvalid:
521
+ await m.reply_text(
522
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
523
+ )
524
+ except UserAdminInvalid:
525
+ await m.reply_text(
526
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
527
+ )
528
+ except RightForbidden:
529
+ await m.reply_text(text="I don't have enough rights to kick this user.")
530
+ except RPCError as ef:
531
+ await m.reply_text(
532
+ text=f"""Some error occured, report it using `/bug`
533
+
534
+ <b>Error:</b> <code>{ef}</code>"""
535
+ )
536
+ LOGGER.error(ef)
537
+ LOGGER.error(format_exc())
538
+
539
+ return
540
+
541
+
542
+ @app.on_message(command("dkick") & restrict_filter)
543
+ async def dkick_usr(c: app, m: Message):
544
+ if len(m.text.split()) == 1 and not m.reply_to_message:
545
+ await m.reply_text(text="I can't ban nothing!")
546
+ return
547
+ if not m.reply_to_message:
548
+ return await m.reply_text("Reply to a message to delete it and kick the user!")
549
+
550
+ reason = None
551
+
552
+ user_id = m.reply_to_message.from_user.id
553
+ user_first_name = m.reply_to_message.from_user.first_name
554
+
555
+ if not user_id:
556
+ await m.reply_text("Cannot find user to kick")
557
+ return
558
+
559
+ if user_id == BOT_ID:
560
+ await m.reply_text("Huh, why would I kick myself?")
561
+ await m.stop_propagation()
562
+
563
+ if user_id in SUPPORT_STAFF:
564
+ await m.reply_text(
565
+ text="This user is in my support staff, cannot restrict them."
566
+ )
567
+ LOGGER.info(
568
+ f"{m.from_user.id} trying to dkick {user_id} (SUPPORT_STAFF) in {m.chat.id}",
569
+ )
570
+ await m.stop_propagation()
571
+
572
+ try:
573
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
574
+ except KeyError:
575
+ admins_group = await admin_cache_reload(m, "kick")
576
+
577
+ if user_id in admins_group:
578
+ await m.reply_text(text="This user is an admin, I cannot kick them!")
579
+ await m.stop_propagation()
580
+
581
+ try:
582
+ LOGGER.info(f"{m.from_user.id} dkicked {user_id} in {m.chat.id}")
583
+ await m.reply_to_message.delete()
584
+ await m.chat.ban_member(user_id)
585
+ admin = await mention_html(m.from_user.first_name, m.from_user.id)
586
+ kicked = await mention_html(user_first_name, user_id)
587
+ chat_title = m.chat.title
588
+ txt = f"{admin} kicked {kicked} in <b>{chat_title}</b>!"
589
+ if reason:
590
+ txt += f"\n<b>Reason</b>: {reason}"
591
+ else:
592
+ txt += "\n<b>Reason</b>: Not Specified"
593
+ kickk = choice(KICK_GIFS)
594
+ try:
595
+ await m.reply_animation(
596
+ animation=str(kickk),
597
+ caption=txt,
598
+ parse_mode=enums.ParseMode.HTML,
599
+ )
600
+ except:
601
+ await m.reply_text(
602
+ txt,
603
+ parse_mode=enums.ParseMode.HTML,
604
+ )
605
+ await c.send_message(MESSAGE_DUMP, f"#REMOVE from KICK_GFIS\n{kickk}")
606
+ await m.chat.unban_member(user_id)
607
+ except ChatAdminRequired:
608
+ await m.reply_text(text="I'm not admin or I don't have rights.")
609
+ except PeerIdInvalid:
610
+ await m.reply_text(
611
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
612
+ )
613
+ except UserAdminInvalid:
614
+ await m.reply_text(
615
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
616
+ )
617
+ except RightForbidden:
618
+ await m.reply_text(text="I don't have enough rights to kick this user.")
619
+ except RPCError as ef:
620
+ await m.reply_text(
621
+ text=f"""Some error occured, report it using `/bug`
622
+
623
+ <b>Error:</b> <code>{ef}</code>"""
624
+ )
625
+ LOGGER.error(ef)
626
+ LOGGER.error(format_exc())
627
+
628
+ return
629
+
630
+
631
+ @app.on_message(command("unban") & restrict_filter)
632
+ async def unban_usr(c: app, m: Message):
633
+ if len(m.text.split()) == 1 and not m.reply_to_message:
634
+ await m.reply_text(text="I can't unban nothing!")
635
+ await m.stop_propagation()
636
+
637
+ if m.reply_to_message and not m.reply_to_message.from_user:
638
+ user_id, user_first_name = (
639
+ m.reply_to_message.sender_chat.id,
640
+ m.reply_to_message.sender_chat.title,
641
+ )
642
+ else:
643
+ try:
644
+ user_id, user_first_name, _ = await extract_user(c, m)
645
+ except Exception:
646
+ return
647
+
648
+ if m.reply_to_message and len(m.text.split()) >= 2:
649
+ reason = m.text.split(None, 2)[1]
650
+ elif not m.reply_to_message and len(m.text.split()) >= 3:
651
+ reason = m.text.split(None, 2)[2]
652
+ else:
653
+ reason = None
654
+
655
+ try:
656
+ statu = (await m.chat.get_member(user_id)).status
657
+ if statu not in [
658
+ enums.ChatMemberStatus.BANNED,
659
+ enums.ChatMemberStatus.RESTRICTED,
660
+ ]:
661
+ await m.reply_text(
662
+ "User is not banned in this chat\nOr using this command as reply to his message"
663
+ )
664
+ return
665
+ except Exception as e:
666
+ LOGGER.error(e)
667
+ LOGGER.exception(format_exc())
668
+ try:
669
+ await m.chat.unban_member(user_id)
670
+ admin = m.from_user.mention
671
+ unbanned = await mention_html(user_first_name, user_id)
672
+ chat_title = (m.chat.title,)
673
+ txt = f"{admin} unbanned {unbanned} in chat <b>{chat_title}</b>!"
674
+ if reason:
675
+ txt += f"\n<b>Reason</b>: {reason}"
676
+ else:
677
+ txt += "\n<b>Reason</b>: Not Specified"
678
+ await m.reply_text(txt)
679
+ except ChatAdminRequired:
680
+ await m.reply_text(text="I'm not admin or I don't have rights.")
681
+ except RightForbidden:
682
+ await m.reply_text(text="I don't have enough rights to unban this user.")
683
+ except RPCError as ef:
684
+ await m.reply_text(
685
+ text=f"""Some error occured, report it using `/bug`
686
+
687
+ <b>Error:</b> <code>{ef}</code>"""
688
+ )
689
+ LOGGER.error(ef)
690
+ LOGGER.error(format_exc())
691
+
692
+ return
693
+
694
+
695
+ @app.on_message(command("sban") & restrict_filter)
696
+ async def sban_usr(c: app, m: Message):
697
+ if len(m.text.split()) == 1 and not m.reply_to_message:
698
+ await m.reply_text(text="I can't ban nothing!")
699
+ await m.stop_propagation()
700
+
701
+ if m.reply_to_message and not m.reply_to_message.from_user:
702
+ user_id = m.reply_to_message.sender_chat.id
703
+ else:
704
+ try:
705
+ user_id, _, _ = await extract_user(c, m)
706
+ except Exception:
707
+ return
708
+
709
+ if not user_id:
710
+ await m.reply_text("Cannot find user to ban")
711
+ return
712
+ if user_id == m.chat.id:
713
+ await m.reply_text("That's an admin!")
714
+ await m.stop_propagation()
715
+ if user_id == BOT_ID:
716
+ await m.reply_text("Huh, why would I ban myself?")
717
+ await m.stop_propagation()
718
+
719
+ if user_id in SUPPORT_STAFF:
720
+ await m.reply_text(
721
+ text="This user is in my support staff, cannot restrict them."
722
+ )
723
+ LOGGER.info(
724
+ f"{m.from_user.id} trying to sban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
725
+ )
726
+ await m.stop_propagation()
727
+
728
+ try:
729
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
730
+ except KeyError:
731
+ admins_group = await admin_cache_reload(m, "ban")
732
+
733
+ if user_id in admins_group:
734
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
735
+ await m.stop_propagation()
736
+
737
+ try:
738
+ LOGGER.info(f"{m.from_user.id} sbanned {user_id} in {m.chat.id}")
739
+ await m.chat.ban_member(user_id)
740
+ await m.delete()
741
+ if m.reply_to_message:
742
+ await m.reply_to_message.delete()
743
+ except ChatAdminRequired:
744
+ await m.reply_text(text="I'm not admin or I don't have rights.")
745
+ except PeerIdInvalid:
746
+ await m.reply_text(
747
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
748
+ )
749
+ except UserAdminInvalid:
750
+ await m.reply_text(
751
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
752
+ )
753
+ except RightForbidden:
754
+ await m.reply_text(text="I don't have enough rights to ban this user.")
755
+ except RPCError as ef:
756
+ await m.reply_text(
757
+ text=f"""Some error occured, report it using `/bug`
758
+
759
+ <b>Error:</b> <code>{ef}</code>"""
760
+ )
761
+ LOGGER.error(ef)
762
+ LOGGER.error(format_exc())
763
+ return
764
+
765
+
766
+ @app.on_message(command("dban") & restrict_filter)
767
+ async def dban_usr(c: app, m: Message):
768
+ if len(m.text.split()) == 1 and not m.reply_to_message:
769
+ await m.reply_text(text="I can't ban nothing!")
770
+ await m.stop_propagation()
771
+
772
+ if not m.reply_to_message:
773
+ return await m.reply_text("Reply to a message to delete it and ban the user!")
774
+
775
+ if m.reply_to_message and not m.reply_to_message.from_user:
776
+ user_id, user_first_name = (
777
+ m.reply_to_message.sender_chat.id,
778
+ m.reply_to_message.sender_chat.title,
779
+ )
780
+ else:
781
+ user_id, user_first_name = (
782
+ m.reply_to_message.from_user.id,
783
+ m.reply_to_message.from_user.first_name,
784
+ )
785
+
786
+ if not user_id:
787
+ await m.reply_text("Cannot find user to ban")
788
+ return
789
+ if user_id == m.chat.id:
790
+ await m.reply_text("That's an admin!")
791
+ await m.stop_propagation()
792
+ if user_id == BOT_ID:
793
+ await m.reply_text("Huh, why would I ban myself?")
794
+ await m.stop_propagation()
795
+
796
+ if user_id in SUPPORT_STAFF:
797
+ await m.reply_text(
798
+ text="This user is in my support staff, cannot restrict them."
799
+ )
800
+ LOGGER.info(
801
+ f"{m.from_user.id} trying to dban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
802
+ )
803
+ await m.stop_propagation()
804
+
805
+ try:
806
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
807
+ except KeyError:
808
+ admins_group = await admin_cache_reload(m, "ban")
809
+
810
+ if user_id in admins_group:
811
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
812
+ await m.stop_propagation()
813
+
814
+ reason = None
815
+ if len(m.text.split()) >= 2:
816
+ reason = m.text.split(None, 1)[1]
817
+
818
+ try:
819
+ LOGGER.info(f"{m.from_user.id} dbanned {user_id} in {m.chat.id}")
820
+ await m.reply_to_message.delete()
821
+ await m.chat.ban_member(user_id)
822
+ txt = f"{m.from_user.mention} banned {m.reply_to_message.from_user.mention} in <b>{m.chat.title}</b>!"
823
+ if reason:
824
+ txt += f"\n<b>Reason</b>: {reason}"
825
+ else:
826
+ txt += "\n<b>Reason</b>: Not Specified"
827
+ keyboard = InlineKeyboardMarkup(
828
+ [
829
+ [
830
+ InlineKeyboardButton(
831
+ "UNBAN",
832
+ callback_data=f"unban_={user_id}",
833
+ ),
834
+ ],
835
+ ],
836
+ )
837
+ animm = choice(BAN_GIFS)
838
+ try:
839
+ await c.send_animation(
840
+ m.chat.id, animation=str(animm), caption=txt, reply_markup=keyboard
841
+ )
842
+ except Exception:
843
+ await c.send_message(
844
+ m.chat.id, txt, enums.ParseMode.HTML, reply_markup=keyboard
845
+ )
846
+ await c.send_messagea(MESSAGE_DUMP, f"#REMOVE from BAN_GIFS\n{animm}")
847
+ except ChatAdminRequired:
848
+ await m.reply_text(text="I'm not admin or I don't have rights.")
849
+ except PeerIdInvalid:
850
+ await m.reply_text(
851
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
852
+ )
853
+ except UserAdminInvalid:
854
+ await m.reply_text(
855
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
856
+ )
857
+ except RightForbidden:
858
+ await m.reply_text(text="I don't have enough rights to ban this user.")
859
+ except RPCError as ef:
860
+ await m.reply_text(
861
+ text=f"""Some error occured, report it using `/bug`
862
+
863
+ <b>Error:</b> <code>{ef}</code>"""
864
+ )
865
+ LOGGER.error(ef)
866
+ LOGGER.error(format_exc())
867
+ return
868
+
869
+
870
+ @app.on_message(command("ban") & restrict_filter)
871
+ async def ban_usr(c: app, m: Message):
872
+ if len(m.text.split()) == 1 and not m.reply_to_message:
873
+ await m.reply_text(text="I can't ban nothing!")
874
+ await m.stop_propagation()
875
+
876
+ if m.reply_to_message and not m.reply_to_message.from_user:
877
+ user_id, user_first_name = (
878
+ m.reply_to_message.sender_chat.id,
879
+ m.reply_to_message.sender_chat.title,
880
+ )
881
+ else:
882
+ try:
883
+ user_id, user_first_name, _ = await extract_user(c, m)
884
+ except Exception:
885
+ return
886
+
887
+ if not user_id:
888
+ await m.reply_text("Cannot find user to ban")
889
+ await m.stop_propagation()
890
+ if user_id == m.chat.id:
891
+ await m.reply_text("That's an admin!")
892
+ await m.stop_propagation()
893
+ if user_id == BOT_ID:
894
+ await m.reply_text("Huh, why would I ban myself?")
895
+ await m.stop_propagation()
896
+
897
+ if user_id in SUPPORT_STAFF:
898
+ await m.reply_text(
899
+ text="This user is in my support staff, cannot restrict them."
900
+ )
901
+ LOGGER.info(
902
+ f"{m.from_user.id} trying to ban {user_id} (SUPPORT_STAFF) in {m.chat.id}",
903
+ )
904
+ await m.stop_propagation()
905
+
906
+ try:
907
+ admins_group = {i[0] for i in ADMIN_CACHE[m.chat.id]}
908
+ except KeyError:
909
+ admins_group = await admin_cache_reload(m, "ban")
910
+
911
+ if user_id in admins_group:
912
+ await m.reply_text(text="This user is an admin, I cannot ban them!")
913
+ await m.stop_propagation()
914
+
915
+ reason = None
916
+ if m.reply_to_message:
917
+ r_id = m.reply_to_message.id
918
+ if len(m.text.split()) >= 2:
919
+ reason = m.text.split(None, 1)[1]
920
+ else:
921
+ r_id = m.id
922
+ if len(m.text.split()) >= 3:
923
+ reason = m.text.split(None, 2)[2]
924
+
925
+ try:
926
+ LOGGER.info(f"{m.from_user.id} banned {user_id} in {m.chat.id}")
927
+ await m.chat.ban_member(user_id)
928
+ banned = await mention_html(user_first_name, user_id)
929
+ txt = f"{m.from_user.mention} banned {banned} in <b>{m.chat.title}</b>!"
930
+ if reason:
931
+ txt += f"\n<b>Reason</b>: {reason}"
932
+ else:
933
+ txt += "\n<b>Reason</b>: Not Specified"
934
+ keyboard = InlineKeyboardMarkup(
935
+ [
936
+ [
937
+ InlineKeyboardButton(
938
+ "UNBAN",
939
+ callback_data=f"unban_={user_id}",
940
+ ),
941
+ ],
942
+ ],
943
+ )
944
+ anim = choice(BAN_GIFS)
945
+ try:
946
+ await m.reply_animation(
947
+ reply_to_message_id=r_id,
948
+ animation=str(anim),
949
+ caption=txt,
950
+ reply_markup=keyboard,
951
+ parse_mode=enums.ParseMode.HTML,
952
+ )
953
+ except Exception:
954
+ await m.reply_text(
955
+ reply_to_message_id=r_id,
956
+ text=txt,
957
+ reply_markup=keyboard,
958
+ parse_mode=enums.ParseMode.HTML,
959
+ )
960
+ await c.send_message(MESSAGE_DUMP, f"#REMOVE from BAN_GFIS\n{anim}")
961
+ except ChatAdminRequired:
962
+ await m.reply_text(text="I'm not admin or I don't have rights.")
963
+ except PeerIdInvalid:
964
+ await m.reply_text(
965
+ "I have not seen this user yet...!\nMind forwarding one of their message so I can recognize them?",
966
+ )
967
+ except UserAdminInvalid:
968
+ await m.reply_text(
969
+ text="Cannot act on this user, maybe I wasn't the one who changed their permissions."
970
+ )
971
+ except RightForbidden:
972
+ await m.reply_text(text="I don't have enough rights to ban this user.")
973
+ except RPCError as ef:
974
+ await m.reply_text(
975
+ text=f"""Some error occured, report it using `/bug`
976
+
977
+ <b>Error:</b> <code>{ef}</code>"""
978
+ )
979
+ LOGGER.error(ef)
980
+ LOGGER.error(format_exc())
981
+ return
982
+
983
+
984
+ @app.on_callback_query(regex("^unban_"))
985
+ async def unbanbutton(c: app, q: CallbackQuery):
986
+ splitter = (str(q.data).replace("unban_", "")).split("=")
987
+ user_id = int(splitter[1])
988
+ user = await q.message.chat.get_member(q.from_user.id)
989
+
990
+ if not user:
991
+ await q.answer(
992
+ "You don't have enough permission to do this!\nStay in your limits!",
993
+ show_alert=True,
994
+ )
995
+ return
996
+
997
+ if not user.privileges.can_restrict_members and q.from_user.id != OWNER_ID:
998
+ await q.answer(
999
+ "You don't have enough permission to do this!\nStay in your limits!",
1000
+ show_alert=True,
1001
+ )
1002
+ return
1003
+ whoo = await c.get_chat(user_id)
1004
+ doneto = whoo.first_name if whoo.first_name else whoo.title
1005
+ try:
1006
+ await q.message.chat.unban_member(user_id)
1007
+ except RPCError as e:
1008
+ await q.message.edit_text(f"Error: {e}")
1009
+ return
1010
+ await q.message.edit_text(f"{q.from_user.mention} unbanned {doneto}!")
1011
+ return
1012
+
1013
+
1014
+ # <=================================================== HELP ====================================================>
1015
+
1016
+
1017
+ __help__ = """
1018
+ ➠ *Admin only:*
1019
+
1020
+ » /kick: Kick the user replied or tagged.
1021
+
1022
+ » /skick: Kick the user replied or tagged and delete your messsage.
1023
+
1024
+ » /dkick: Kick the user replied and delete their message.
1025
+
1026
+ » /ban: Bans the user replied to or tagged.
1027
+
1028
+ » /sban: Bans the user replied or tagged and delete your messsage.
1029
+
1030
+ » /dban: Bans the user replied and delete their message.
1031
+
1032
+ » /tban <userhandle> x(m/h/d): Bans a user for x time. (via handle, or reply). m = minutes, h = hours, d = days.
1033
+
1034
+ » /stban <userhandle> x(m/h/d): Silently bans a user for x time. (via handle, or reply). m = minutes, h = hours, d = days.
1035
+
1036
+ » /dtban <userhandle> x(m/h/d): Silently bans a user for x time and delete the replied message. (via reply). m = minutes, h = hours, d = days.
1037
+
1038
+ » /unban: Unbans the user replied to or tagged.
1039
+
1040
+
1041
+ ➠ **Example:**
1042
+ » `/ban @username`: this bans a user in the chat."""
1043
+
1044
+ __mod_name__ = "BANS"
1045
+ # <================================================ END =======================================================>
Mikobot/plugins/botadmins.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOURCE https://github.com/Team-ProjectCodeX
2
+ # CREATED BY https://t.me/O_okarma
3
+ # PROVIDED BY https://t.me/ProjectCodeX
4
+
5
+ # <============================================== IMPORTS =========================================================>
6
+
7
+ from telegram import Update
8
+ from telegram.constants import ParseMode
9
+ from telegram.error import TelegramError
10
+ from telegram.ext import CommandHandler, ContextTypes
11
+
12
+ from Mikobot import DEMONS, DEV_USERS, DRAGONS, LOGGER, OWNER_ID, WOLVES, function
13
+ from Mikobot.plugins.helper_funcs.chat_status import check_admin
14
+ from Mikobot.utils.parser import mention_html
15
+
16
+ # <=======================================================================================================>
17
+
18
+
19
+ # <================================================ FUNCTION =======================================================>
20
+ async def get_chat_member(context: ContextTypes.DEFAULT_TYPE, user_id):
21
+ try:
22
+ return await context.bot.get_chat_member(user_id, user_id)
23
+ except TelegramError as e:
24
+ LOGGER.error(f"Error getting chat member {user_id}: {e}")
25
+ return None
26
+
27
+
28
+ async def get_user_info(context: ContextTypes.DEFAULT_TYPE, user_id):
29
+ user_info = await get_chat_member(context, user_id)
30
+ return user_info.user.first_name if user_info else "Unknown User"
31
+
32
+
33
+ async def get_users_info(context: ContextTypes.DEFAULT_TYPE, user_ids):
34
+ return [(await get_user_info(context, user_id), user_id) for user_id in user_ids]
35
+
36
+
37
+ async def get_users_list(context: ContextTypes.DEFAULT_TYPE, user_ids):
38
+ return [
39
+ f"• {await mention_html(name, user_id)} (<code>{user_id}</code>)"
40
+ for name, user_id in await get_users_info(context, user_ids)
41
+ ]
42
+
43
+
44
+ @check_admin(only_dev=True)
45
+ async def botstaff(update: Update, context: ContextTypes.DEFAULT_TYPE):
46
+ try:
47
+ owner = await get_chat_member(context, OWNER_ID)
48
+ owner_info = await mention_html(owner.user.first_name, owner.user.id)
49
+ reply = f"✪ <b>CREATOR :</b> {owner_info} (<code>{OWNER_ID}</code>)\n"
50
+ except TelegramError as e:
51
+ LOGGER.error(f"Error getting owner information: {e}")
52
+ reply = ""
53
+
54
+ true_dev = list(set(DEV_USERS) - {OWNER_ID})
55
+ reply += "\n\n➪ <b>SPECIAL GRADE USERS :</b>\n"
56
+ reply += "\n".join(await get_users_list(context, true_dev)) or "No Dev Users"
57
+
58
+ true_sudo = list(set(DRAGONS) - set(DEV_USERS))
59
+ reply += "\n\n➪ <b>A GRADE USERS :</b>\n"
60
+ reply += "\n".join(await get_users_list(context, true_sudo)) or "No Sudo Users"
61
+
62
+ reply += "\n\n➪ <b>B GRADE USERS :</b>\n"
63
+ reply += "\n".join(await get_users_list(context, DEMONS)) or "No Demon Users"
64
+
65
+ reply += "\n\n➪ <b>NORMAL GRADE USERS :</b>\n"
66
+ reply += (
67
+ "\n".join(await get_users_list(context, WOLVES))
68
+ or "No additional whitelisted users"
69
+ )
70
+
71
+ await update.message.reply_text(reply, parse_mode=ParseMode.HTML)
72
+ LOGGER.info(
73
+ f"{update.message.from_user.id} fetched botstaff in {update.message.chat.id}"
74
+ )
75
+
76
+
77
+ # <================================================ HANDLER =======================================================>
78
+ function(CommandHandler("botadmins", botstaff, block=False))
79
+ # <================================================ END =======================================================>
80
+
81
+
82
+ # <=================================================== HELP ====================================================>
83
+ __help__ = """
84
+ ➠ *BOT ADMINS ONLY:*
85
+
86
+ » /stats: Shows bot stats.
87
+
88
+ » /gban: Global ban.
89
+
90
+ » /gbanlist: Shows gban list.
91
+
92
+ » /botadmins: Opens Bot admin lists.
93
+
94
+ » /gcast: Advance broadcast system. Just reply to any message.
95
+
96
+ ➠ *Write with text message*
97
+
98
+ » /broadcastall
99
+
100
+ » /broadcastusers
101
+
102
+ » /broadcastgroups
103
+ """
104
+
105
+ __mod_name__ = "BOT-ADMIN"
106
+ # <================================================ HANDLER =======================================================>
Mikobot/plugins/chatbot.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import asyncio
3
+ import html
4
+ import json
5
+ import re
6
+ from typing import Optional
7
+
8
+ import requests
9
+ from telegram import (
10
+ CallbackQuery,
11
+ Chat,
12
+ InlineKeyboardButton,
13
+ InlineKeyboardMarkup,
14
+ Update,
15
+ User,
16
+ )
17
+ from telegram.constants import ParseMode
18
+ from telegram.error import BadRequest, Forbidden, RetryAfter
19
+ from telegram.ext import (
20
+ CallbackQueryHandler,
21
+ CommandHandler,
22
+ ContextTypes,
23
+ MessageHandler,
24
+ filters,
25
+ )
26
+ from telegram.helpers import mention_html
27
+
28
+ import Database.sql.kuki_sql as sql
29
+ from Mikobot import function
30
+ from Mikobot.plugins.log_channel import gloggable
31
+
32
+ # <=======================================================================================================>
33
+
34
+
35
+ # <================================================ FUNCTION =======================================================>
36
+ @gloggable
37
+ async def kukirm(update: Update, context: ContextTypes.DEFAULT_TYPE):
38
+ query: Optional[CallbackQuery] = update.callback_query
39
+ user: Optional[User] = update.effective_user
40
+ if match := re.match(r"rm_chat\((.+?)\)", query.data):
41
+ user_id = match[1]
42
+ chat: Optional[Chat] = update.effective_chat
43
+ if is_kuki := sql.rem_kuki(chat.id):
44
+ sql.rem_kuki(user_id)
45
+ return (
46
+ f"<b>{html.escape(chat.title)}:</b>\n"
47
+ f"AI_DISABLED\n"
48
+ f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n"
49
+ )
50
+ else:
51
+ await update.effective_message.edit_text(
52
+ f"Chatbot disable by {mention_html(user.id, user.first_name)}.",
53
+ parse_mode=ParseMode.HTML,
54
+ )
55
+
56
+ return ""
57
+
58
+
59
+ @gloggable
60
+ async def kukiadd(update: Update, context: ContextTypes.DEFAULT_TYPE):
61
+ query: Optional[CallbackQuery] = update.callback_query
62
+ user: Optional[User] = update.effective_user
63
+ if match := re.match(r"add_chat\((.+?)\)", query.data):
64
+ user_id = match[1]
65
+ chat: Optional[Chat] = update.effective_chat
66
+ if is_kuki := sql.set_kuki(chat.id):
67
+ sql.set_kuki(user_id)
68
+ return (
69
+ f"<b>{html.escape(chat.title)}:</b>\n"
70
+ f"AI_ENABLE\n"
71
+ f"<b>Admin:</b> {mention_html(user.id, html.escape(user.first_name))}\n"
72
+ )
73
+ else:
74
+ await update.effective_message.edit_text(
75
+ f"Hey Darling Chatbot enable by {mention_html(user.id, user.first_name)}.",
76
+ parse_mode=ParseMode.HTML,
77
+ )
78
+
79
+ return ""
80
+
81
+
82
+ @gloggable
83
+ async def kuki(update: Update, context: ContextTypes.DEFAULT_TYPE):
84
+ update.effective_user
85
+ message = update.effective_message
86
+ msg = "Choose an option"
87
+ keyboard = InlineKeyboardMarkup(
88
+ [
89
+ [InlineKeyboardButton(text="Enable", callback_data="add_chat({})")],
90
+ [InlineKeyboardButton(text="Disable", callback_data="rm_chat({})")],
91
+ ]
92
+ )
93
+ await message.reply_text(
94
+ msg,
95
+ reply_markup=keyboard,
96
+ parse_mode=ParseMode.HTML,
97
+ )
98
+
99
+
100
+ async def kuki_message(context: ContextTypes.DEFAULT_TYPE, message):
101
+ reply_message = message.reply_to_message
102
+ if message.text.lower() == "kuki":
103
+ return True
104
+ if reply_message:
105
+ if reply_message.from_user.id == (await context.bot.get_me()).id:
106
+ return True
107
+ else:
108
+ return False
109
+
110
+
111
+ async def chatbot(update: Update, context: ContextTypes.DEFAULT_TYPE):
112
+ update.effective_user
113
+ message = update.effective_message
114
+ chat_id = update.effective_chat.id
115
+ bot = context.bot
116
+ is_kuki = sql.is_kuki(chat_id)
117
+ if not is_kuki:
118
+ return
119
+
120
+ if message.text and not message.document:
121
+ if not await kuki_message(context, message):
122
+ return
123
+ Message = message.text
124
+ await bot.send_chat_action(chat_id, action="typing")
125
+ kukiurl = requests.get(
126
+ f"http://api.brainshop.ai/get?bid=176809&key=lbMN8CXTGzhn1NKG&uid=[user]&msg={Message}"
127
+ )
128
+
129
+ Kuki = json.loads(kukiurl.text)
130
+ kuki = Kuki["cnt"]
131
+
132
+ await asyncio.sleep(0.3)
133
+ await message.reply_text(kuki)
134
+
135
+
136
+ async def list_all_chats(update: Update, context: ContextTypes.DEFAULT_TYPE):
137
+ chats = sql.get_all_kuki_chats()
138
+ text = "<b>Neko Enabled Chats</b>\n"
139
+ for chat in chats:
140
+ try:
141
+ x = await context.bot.get_chat(int(*chat))
142
+ name = x.title or x.first_name
143
+ text += f"• <code>{name}</code>\n"
144
+ except (BadRequest, Forbidden):
145
+ sql.rem_kuki(*chat)
146
+ except RetryAfter as e:
147
+ await asyncio.sleep(e.retry_after)
148
+ await update.effective_message.reply_text(text, parse_mode="HTML")
149
+
150
+
151
+ # <=================================================== HELP ====================================================>
152
+
153
+
154
+ __help__ = """
155
+ ➠ *Admins only command*:
156
+
157
+ » `/Chatbot`*:* shows chatbot panel.
158
+ """
159
+
160
+ __mod_name__ = "CHATBOT"
161
+
162
+
163
+ # <================================================ HANDLER =======================================================>
164
+ CHATBOTK_HANDLER = CommandHandler("chatbot", kuki, block=False)
165
+ ADD_CHAT_HANDLER = CallbackQueryHandler(kukiadd, pattern=r"add_chat", block=False)
166
+ RM_CHAT_HANDLER = CallbackQueryHandler(kukirm, pattern=r"rm_chat", block=False)
167
+ CHATBOT_HANDLER = MessageHandler(
168
+ filters.TEXT
169
+ & (~filters.Regex(r"^#[^\s]+") & ~filters.Regex(r"^!") & ~filters.Regex(r"^\/")),
170
+ chatbot,
171
+ block=False,
172
+ )
173
+ LIST_ALL_CHATS_HANDLER = CommandHandler("allchats", list_all_chats, block=False)
174
+
175
+ function(ADD_CHAT_HANDLER)
176
+ function(CHATBOTK_HANDLER)
177
+ function(RM_CHAT_HANDLER)
178
+ function(LIST_ALL_CHATS_HANDLER)
179
+ function(CHATBOT_HANDLER)
180
+
181
+ __handlers__ = [
182
+ ADD_CHAT_HANDLER,
183
+ CHATBOTK_HANDLER,
184
+ RM_CHAT_HANDLER,
185
+ LIST_ALL_CHATS_HANDLER,
186
+ CHATBOT_HANDLER,
187
+ ]
188
+ # <================================================ END =======================================================>
Mikobot/plugins/connection.py ADDED
@@ -0,0 +1,441 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import re
3
+ import time
4
+
5
+ from telegram import Bot, InlineKeyboardButton, InlineKeyboardMarkup, Update
6
+ from telegram.constants import ParseMode
7
+ from telegram.error import BadRequest, Forbidden
8
+ from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes
9
+
10
+ import Database.sql.connection_sql as sql
11
+ from Mikobot import DEV_USERS, DRAGONS, dispatcher, function
12
+ from Mikobot.plugins.helper_funcs import chat_status
13
+ from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action
14
+
15
+ # <=======================================================================================================>
16
+
17
+ check_admin = chat_status.check_admin
18
+
19
+
20
+ # <================================================ FUNCTION =======================================================>
21
+ @check_admin(is_user=True)
22
+ @typing_action
23
+ async def allow_connections(update: Update, context: ContextTypes.DEFAULT_TYPE):
24
+ chat = update.effective_chat
25
+ args = context.args
26
+
27
+ if chat.type != chat.PRIVATE:
28
+ if len(args) >= 1:
29
+ var = args[0]
30
+ if var == "no":
31
+ sql.set_allow_connect_to_chat(chat.id, False)
32
+ await send_message(
33
+ update.effective_message,
34
+ "Connection has been disabled for this chat.",
35
+ )
36
+ elif var == "yes":
37
+ sql.set_allow_connect_to_chat(chat.id, True)
38
+ await send_message(
39
+ update.effective_message,
40
+ "Connection has been enabled for this chat.",
41
+ )
42
+ else:
43
+ await send_message(
44
+ update.effective_message,
45
+ "Please enter 'yes' or 'no'!",
46
+ parse_mode=ParseMode.MARKDOWN,
47
+ )
48
+ else:
49
+ get_settings = sql.allow_connect_to_chat(chat.id)
50
+ if get_settings:
51
+ await send_message(
52
+ update.effective_message,
53
+ "Connections to this group are *allowed* for members!",
54
+ parse_mode=ParseMode.MARKDOWN,
55
+ )
56
+ else:
57
+ await send_message(
58
+ update.effective_message,
59
+ "Connection to this group is *not allowed* for members!",
60
+ parse_mode=ParseMode.MARKDOWN,
61
+ )
62
+ else:
63
+ await send_message(
64
+ update.effective_message,
65
+ "This command is for groups only, not in PM!",
66
+ )
67
+
68
+
69
+ @typing_action
70
+ async def connection_chat(update: Update, context: ContextTypes.DEFAULT_TYPE):
71
+ chat = update.effective_chat
72
+ user = update.effective_user
73
+
74
+ conn = await connected(context.bot, update, chat, user.id, need_admin=True)
75
+
76
+ if conn:
77
+ chat = await dispatcher.bot.getChat(conn)
78
+ chat_obj = await dispatcher.bot.getChat(conn)
79
+ chat_name = chat_obj.title
80
+ else:
81
+ if update.effective_message.chat.type != "private":
82
+ return
83
+ chat = update.effective_chat
84
+ chat_name = update.effective_message.chat.title
85
+
86
+ if conn:
87
+ message = "You are currently connected to {}.\n".format(chat_name)
88
+ else:
89
+ message = "You are currently not connected in any group.\n"
90
+ await send_message(update.effective_message, message, parse_mode="markdown")
91
+
92
+
93
+ @typing_action
94
+ async def connect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE):
95
+ chat = update.effective_chat
96
+ user = update.effective_user
97
+ args = context.args
98
+
99
+ if update.effective_chat.type == "private":
100
+ if args and len(args) >= 1:
101
+ try:
102
+ connect_chat = int(args[0])
103
+ getstatusadmin = await context.bot.get_chat_member(
104
+ connect_chat,
105
+ update.effective_message.from_user.id,
106
+ )
107
+ except ValueError:
108
+ try:
109
+ connect_chat = str(args[0])
110
+ get_chat = await context.bot.getChat(connect_chat)
111
+ connect_chat = get_chat.id
112
+ getstatusadmin = await context.bot.get_chat_member(
113
+ connect_chat,
114
+ update.effective_message.from_user.id,
115
+ )
116
+ except BadRequest:
117
+ await send_message(update.effective_message, "Invalid Chat ID!")
118
+ return
119
+ except BadRequest:
120
+ await send_message(update.effective_message, "Invalid Chat ID!")
121
+ return
122
+
123
+ isadmin = getstatusadmin.status in ("administrator", "creator")
124
+ ismember = getstatusadmin.status in ("member")
125
+ isallow = sql.allow_connect_to_chat(connect_chat)
126
+
127
+ if (isadmin) or (isallow and ismember) or (user.id in DRAGONS):
128
+ connection_status = sql.connect(
129
+ update.effective_message.from_user.id,
130
+ connect_chat,
131
+ )
132
+ if connection_status:
133
+ conn = await connected(
134
+ context.bot, update, chat, user.id, need_admin=False
135
+ )
136
+ conn_chat = await dispatcher.bot.getChat(conn)
137
+ chat_name = conn_chat.title
138
+ await send_message(
139
+ update.effective_message,
140
+ "Successfully connected to *{}*. Use /helpconnect to check available commands.".format(
141
+ chat_name,
142
+ ),
143
+ parse_mode=ParseMode.MARKDOWN,
144
+ )
145
+ sql.add_history_conn(user.id, str(conn_chat.id), chat_name)
146
+ else:
147
+ await send_message(update.effective_message, "Connection failed!")
148
+ else:
149
+ await send_message(
150
+ update.effective_message,
151
+ "Connection to this chat is not allowed!",
152
+ )
153
+ else:
154
+ gethistory = sql.get_history_conn(user.id)
155
+ if gethistory:
156
+ buttons = [
157
+ InlineKeyboardButton(
158
+ text="❎ Close Button",
159
+ callback_data="connect_close",
160
+ ),
161
+ InlineKeyboardButton(
162
+ text="🧹 Clear History",
163
+ callback_data="connect_clear",
164
+ ),
165
+ ]
166
+ else:
167
+ buttons = []
168
+ conn = await connected(context.bot, update, chat, user.id, need_admin=False)
169
+ if conn:
170
+ connectedchat = await dispatcher.bot.getChat(conn)
171
+ text = "You are currently connected to *{}* (`{}`)".format(
172
+ connectedchat.title,
173
+ conn,
174
+ )
175
+ buttons.append(
176
+ InlineKeyboardButton(
177
+ text="🔌 Disconnect",
178
+ callback_data="connect_disconnect",
179
+ ),
180
+ )
181
+ else:
182
+ text = "Write the chat ID or tag to connect!"
183
+ if gethistory:
184
+ text += "\n\n*Connection History:*\n"
185
+ text += "╒═══「 *Info* 」\n"
186
+ text += "│ Sorted: `Newest`\n"
187
+ text += "│\n"
188
+ buttons = [buttons]
189
+ for x in sorted(gethistory.keys(), reverse=True):
190
+ htime = time.strftime("%d/%m/%Y", time.localtime(x))
191
+ text += "╞═「 *{}* 」\n│ `{}`\n│ `{}`\n".format(
192
+ gethistory[x]["chat_name"],
193
+ gethistory[x]["chat_id"],
194
+ htime,
195
+ )
196
+ text += "│\n"
197
+ buttons.append(
198
+ [
199
+ InlineKeyboardButton(
200
+ text=gethistory[x]["chat_name"],
201
+ callback_data="connect({})".format(
202
+ gethistory[x]["chat_id"],
203
+ ),
204
+ ),
205
+ ],
206
+ )
207
+ text += "╘══「 Total {} Chats 」".format(
208
+ str(len(gethistory)) + " (max)"
209
+ if len(gethistory) == 5
210
+ else str(len(gethistory)),
211
+ )
212
+ conn_hist = InlineKeyboardMarkup(buttons)
213
+ elif buttons:
214
+ conn_hist = InlineKeyboardMarkup([buttons])
215
+ else:
216
+ conn_hist = None
217
+ await send_message(
218
+ update.effective_message,
219
+ text,
220
+ parse_mode="markdown",
221
+ reply_markup=conn_hist,
222
+ )
223
+
224
+ else:
225
+ getstatusadmin = await context.bot.get_chat_member(
226
+ chat.id,
227
+ update.effective_message.from_user.id,
228
+ )
229
+ isadmin = getstatusadmin.status in ("administrator", "creator")
230
+ ismember = getstatusadmin.status in ("member")
231
+ isallow = sql.allow_connect_to_chat(chat.id)
232
+ if (isadmin) or (isallow and ismember) or (user.id in DRAGONS):
233
+ connection_status = sql.connect(
234
+ update.effective_message.from_user.id,
235
+ chat.id,
236
+ )
237
+ if connection_status:
238
+ chat_obj = await dispatcher.bot.getChat(chat.id)
239
+ chat_name = chat_obj.title
240
+ await send_message(
241
+ update.effective_message,
242
+ "Successfully connected to *{}*.".format(chat_name),
243
+ parse_mode=ParseMode.MARKDOWN,
244
+ )
245
+ try:
246
+ sql.add_history_conn(user.id, str(chat.id), chat_name)
247
+ await context.bot.send_message(
248
+ update.effective_message.from_user.id,
249
+ "You are connected to *{}*. \nUse `/helpconnect` to check available commands.".format(
250
+ chat_name,
251
+ ),
252
+ parse_mode="markdown",
253
+ )
254
+ except BadRequest:
255
+ pass
256
+ except Forbidden:
257
+ pass
258
+ else:
259
+ await send_message(update.effective_message, "ᴄᴏɴɴᴇᴄᴛɪᴏɴ ғᴀɪʟᴇᴅ!")
260
+ else:
261
+ await send_message(
262
+ update.effective_message,
263
+ "Connection to this chat is not allowed!",
264
+ )
265
+
266
+
267
+ async def disconnect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE):
268
+ if update.effective_chat.type == "private":
269
+ disconnection_status = sql.disconnect(update.effective_message.from_user.id)
270
+ if disconnection_status:
271
+ sql.disconnected_chat = await send_message(
272
+ update.effective_message,
273
+ "Disconnected from chat!",
274
+ )
275
+ else:
276
+ await send_message(update.effective_message, "You're not connected!")
277
+ else:
278
+ await send_message(
279
+ update.effective_message, "This command is only available in PM."
280
+ )
281
+
282
+
283
+ async def connected(bot: Bot, update: Update, chat, user_id, need_admin=True):
284
+ user = update.effective_user
285
+
286
+ if chat.type == chat.PRIVATE and sql.get_connected_chat(user_id):
287
+ conn_id = sql.get_connected_chat(user_id).chat_id
288
+ getstatusadmin = await bot.get_chat_member(
289
+ conn_id,
290
+ update.effective_message.from_user.id,
291
+ )
292
+ isadmin = getstatusadmin.status in ("administrator", "creator")
293
+ ismember = getstatusadmin.status in ("member")
294
+ isallow = sql.allow_connect_to_chat(conn_id)
295
+
296
+ if (
297
+ (isadmin)
298
+ or (isallow and ismember)
299
+ or (user.id in DRAGONS)
300
+ or (user.id in DEV_USERS)
301
+ ):
302
+ if need_admin is True:
303
+ if (
304
+ getstatusadmin.status in ("administrator", "creator")
305
+ or user_id in DRAGONS
306
+ or user.id in DEV_USERS
307
+ ):
308
+ return conn_id
309
+ else:
310
+ await send_message(
311
+ update.effective_message,
312
+ "You must be an admin in the connected group!",
313
+ )
314
+ else:
315
+ return conn_id
316
+ else:
317
+ await send_message(
318
+ update.effective_message,
319
+ "The group changed the connection rights or you are no longer an admin.\nI've disconnected you.",
320
+ )
321
+ disconnect_chat(update, bot)
322
+ else:
323
+ return False
324
+
325
+
326
+ CONN_HELP = """
327
+ Actions are available with connected groups:
328
+ • View and edit Notes.
329
+ • View and edit Filters.
330
+ • Get invite link of chat.
331
+ • Set and control AntiFlood settings.
332
+ • Set and control Blacklist settings.
333
+ • Set Locks and Unlocks in chat.
334
+ • Enable and Disable commands in chat.
335
+ • Export and Imports of chat backup.
336
+ """
337
+
338
+
339
+ async def help_connect_chat(update: Update, context: ContextTypes.DEFAULT_TYPE):
340
+ context.args
341
+
342
+ if update.effective_message.chat.type != "private":
343
+ await send_message(
344
+ update.effective_message, "PM me with that command to get help."
345
+ )
346
+ return
347
+ else:
348
+ await send_message(update.effective_message, CONN_HELP, parse_mode="markdown")
349
+
350
+
351
+ async def connect_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
352
+ query = update.callback_query
353
+ chat = update.effective_chat
354
+ user = update.effective_user
355
+
356
+ connect_match = re.match(r"connect\((.+?)\)", query.data)
357
+ disconnect_match = query.data == "connect_disconnect"
358
+ clear_match = query.data == "connect_clear"
359
+ connect_close = query.data == "connect_close"
360
+
361
+ if connect_match:
362
+ target_chat = connect_match.group(1)
363
+ getstatusadmin = await context.bot.get_chat_member(
364
+ target_chat, query.from_user.id
365
+ )
366
+ isadmin = getstatusadmin.status in ("administrator", "creator")
367
+ ismember = getstatusadmin.status in ("member")
368
+ isallow = sql.allow_connect_to_chat(target_chat)
369
+
370
+ if (isadmin) or (isallow and ismember) or (user.id in DRAGONS):
371
+ connection_status = sql.connect(query.from_user.id, target_chat)
372
+
373
+ if connection_status:
374
+ conn = await connected(
375
+ context.bot, update, chat, user.id, need_admin=False
376
+ )
377
+ conn_chat = await dispatcher.bot.getChat(conn)
378
+ chat_name = conn_chat.title
379
+ await query.message.edit_text(
380
+ "Successfully connected to *{}*. Use `/helpconnect` to check available commands.".format(
381
+ chat_name,
382
+ ),
383
+ parse_mode=ParseMode.MARKDOWN,
384
+ )
385
+ sql.add_history_conn(user.id, str(conn_chat.id), chat_name)
386
+ else:
387
+ await query.message.edit_text("Connection failed!")
388
+ else:
389
+ await context.bot.answer_callback_query(
390
+ query.id,
391
+ "Connection to this chat is not allowed!",
392
+ show_alert=True,
393
+ )
394
+ elif disconnect_match:
395
+ disconnection_status = sql.disconnect(query.from_user.id)
396
+ if disconnection_status:
397
+ sql.disconnected_chat = await query.message.edit_text(
398
+ "Disconnected from chat!"
399
+ )
400
+ else:
401
+ await context.bot.answer_callback_query(
402
+ query.id,
403
+ "You're not connected!",
404
+ show_alert=True,
405
+ )
406
+ elif clear_match:
407
+ sql.clear_history_conn(query.from_user.id)
408
+ await query.message.edit_text("History connected has been cleared!")
409
+ elif connect_close:
410
+ await query.message.edit_text("Closed. To open again, type /connect")
411
+ else:
412
+ connect_chat(update, context)
413
+
414
+
415
+ # <=================================================== HELP ====================================================>
416
+ __mod_name__ = "CONNECT"
417
+
418
+ __help__ = """
419
+ ➠ *Sometimes, you just want to add some notes and filters to a group chat, but you don't want everyone to see; this is where connections come in. This allows you to connect to a chat's database and add things to it without the commands appearing in chat! For obvious reasons, you need to be an admin to add things, but any member in the group can view your data.*
420
+
421
+ » /connect: Connects to chat (can be done in a group by /connect or /connect <chat id> in PM)
422
+
423
+ » /connection: List connected chats
424
+
425
+ » /disconnect: Disconnect from a chat
426
+
427
+ » /helpconnect: List available commands that can be used remotely
428
+
429
+ ➠ *Admin Only:*
430
+
431
+ » /allowconnect <yes/no>: Allow a user to connect to a chat
432
+ """
433
+
434
+ # <================================================ HANDLER =======================================================>
435
+ function(CommandHandler("connect", connect_chat, block=False))
436
+ function(CommandHandler("connection", connection_chat, block=False))
437
+ function(CommandHandler("disconnect", disconnect_chat, block=False))
438
+ function(CommandHandler("allowconnect", allow_connections, block=False))
439
+ function(CommandHandler("helpconnect", help_connect_chat, block=False))
440
+ function(CallbackQueryHandler(connect_button, pattern=r"connect", block=False))
441
+ # <================================================ END =======================================================>
Mikobot/plugins/cosplay.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SOURCE https://github.com/Team-ProjectCodeX
2
+ # CREATED BY https://t.me/O_okarma
3
+ # API BY https://www.github.com/SOME-1HING
4
+ # PROVIDED BY https://t.me/ProjectCodeX
5
+
6
+ # <============================================== IMPORTS =========================================================>
7
+ from telegram import Update
8
+ from telegram.ext import CommandHandler, ContextTypes
9
+
10
+ from Mikobot import function
11
+ from Mikobot.state import state
12
+
13
+ # <=======================================================================================================>
14
+
15
+
16
+ # <================================================ FUNCTIONS =====================================================>
17
+ async def get_cosplay_data():
18
+ cosplay_url = "https://sugoi-api.vercel.app/cosplay"
19
+ response = await state.get(cosplay_url)
20
+ return response.json()
21
+
22
+
23
+ async def cosplay(update: Update, context: ContextTypes.DEFAULT_TYPE):
24
+ try:
25
+ data = await get_cosplay_data()
26
+ photo_url = data.get("url") # Corrected key: "url" instead of "cosplay_url"
27
+ if photo_url:
28
+ await update.message.reply_photo(photo=photo_url)
29
+ else:
30
+ await update.message.reply_text("Could not fetch photo URL.")
31
+ except state.FetchError:
32
+ await update.message.reply_text("Unable to fetch data.")
33
+
34
+
35
+ # <================================================ HANDLER =======================================================>
36
+ function(CommandHandler("cosplay", cosplay, block=False))
37
+ # <================================================ END =======================================================>
Mikobot/plugins/couple.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import random
3
+ from datetime import datetime
4
+
5
+ from pyrogram import filters
6
+
7
+ from Database.mongodb.karma_mongo import get_couple, save_couple
8
+ from Mikobot import app
9
+
10
+ # <=======================================================================================================>
11
+
12
+
13
+ # <================================================ FUNCTION =======================================================>
14
+ def dt():
15
+ now = datetime.now()
16
+ dt_string = now.strftime("%d/%m/%Y %H:%M")
17
+ dt_list = dt_string.split(" ")
18
+ return dt_list
19
+
20
+
21
+ def dt_tom():
22
+ a = (
23
+ str(int(dt()[0].split("/")[0]) + 1)
24
+ + "/"
25
+ + dt()[0].split("/")[1]
26
+ + "/"
27
+ + dt()[0].split("/")[2]
28
+ )
29
+ return a
30
+
31
+
32
+ tomorrow = str(dt_tom())
33
+ today = str(dt()[0])
34
+
35
+ COUPLES_PIC = "https://telegra.ph/file/c6d0c884f559b9ed8a54e.jpg"
36
+ C = """
37
+ ✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧
38
+ ➖➖➖➖➖➖➖➖➖➖➖➖
39
+ {} + ( PGM🎀😶 (https://t.me/Chalnayaaaaaarr) + 花火 (https://t.me/zd_sr07) + ゼロツー (https://t.me/wewewe_x) ) = 💞
40
+ ➖➖➖➖➖➖➖➖➖➖➖➖
41
+ 𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {}
42
+ """
43
+ CAP = """
44
+ ✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧
45
+ ➖➖➖➖➖➖➖➖➖➖➖➖
46
+ {} + {} = 💞
47
+ ➖➖➖➖➖➖➖➖➖➖➖➖
48
+ 𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {}
49
+ """
50
+
51
+ CAP2 = """
52
+ ✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧
53
+ ➖➖➖➖➖➖➖➖➖➖➖➖
54
+ {} (tg://openmessage?user_id={}) + {} (tg://openmessage?user_id={}) = 💞\n
55
+ ➖➖➖➖➖➖➖➖➖➖➖➖
56
+ 𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {}
57
+ """
58
+
59
+
60
+ @app.on_message(filters.command(["couple", "couples", "shipping"]) & ~filters.private)
61
+ async def nibba_nibbi(_, message):
62
+ if message.from_user.id == 5540249238:
63
+ my_ = await _.get_users("rfxtuv")
64
+ me = await _.get_users(5540249238)
65
+ await message.reply_photo(
66
+ photo=COUPLES_PIC, caption=C.format(me.mention, tomorrow)
67
+ )
68
+ else:
69
+ try:
70
+ chat_id = message.chat.id
71
+ is_selected = await get_couple(chat_id, today)
72
+ if not is_selected:
73
+ list_of_users = []
74
+ async for i in _.get_chat_members(message.chat.id, limit=50):
75
+ if not i.user.is_bot:
76
+ list_of_users.append(i.user.id)
77
+ if len(list_of_users) < 2:
78
+ return await message.reply_text("ɴᴏᴛ ᴇɴᴏᴜɢʜ ᴜsᴇʀs ɪɴ ᴛʜɪs ɢʀᴏᴜᴘ.")
79
+ c1_id = random.choice(list_of_users)
80
+ c2_id = random.choice(list_of_users)
81
+ while c1_id == c2_id:
82
+ c1_id = random.choice(list_of_users)
83
+ c1_mention = (await _.get_users(c1_id)).mention
84
+ c2_mention = (await _.get_users(c2_id)).mention
85
+ await _.send_photo(
86
+ message.chat.id,
87
+ photo=COUPLES_PIC,
88
+ caption=CAP.format(c1_mention, c2_mention, tomorrow),
89
+ )
90
+
91
+ couple = {"c1_id": c1_id, "c2_id": c2_id}
92
+ await save_couple(chat_id, today, couple)
93
+
94
+ elif is_selected:
95
+ c1_id = int(is_selected["c1_id"])
96
+ c2_id = int(is_selected["c2_id"])
97
+
98
+ c1_name = (await _.get_users(c1_id)).first_name
99
+ c2_name = (await _.get_users(c2_id)).first_name
100
+ print(c1_id, c2_id, c1_name, c2_name)
101
+ couple_selection_message = f"""✧ 𝗖𝗢𝗨𝗣𝗟𝗘𝗦 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 ✧
102
+ ➖➖➖➖➖➖➖➖➖➖➖➖
103
+ [{c1_name}](tg://openmessage?user_id={c1_id}) + [{c2_name}](tg://openmessage?user_id={c2_id}) = 💞
104
+ ➖➖➖➖➖➖➖➖➖➖➖➖
105
+ 𝗡𝗘𝗪 𝗖𝗢𝗨𝗣𝗟𝗘 𝗢𝗙 𝗧𝗛𝗘 𝗗𝗔𝗬 𝗖𝗔𝗡 𝗕𝗘 𝗖𝗛𝗢𝗦𝗘𝗡 𝗔𝗧 12AM {tomorrow}"""
106
+ await _.send_photo(
107
+ message.chat.id, photo=COUPLES_PIC, caption=couple_selection_message
108
+ )
109
+ except Exception as e:
110
+ print(e)
111
+ await message.reply_text(str(e))
112
+
113
+
114
+ # <=================================================== HELP ====================================================>
115
+
116
+
117
+ __help__ = """
118
+ 💘 *Choose couples in your chat*
119
+
120
+ » /couple, /couples, /shipping *:* Choose 2 users and send their names as couples in your chat.
121
+ """
122
+
123
+ __mod_name__ = "COUPLE"
124
+ # <================================================ END =======================================================>
Mikobot/plugins/cust_filters.py ADDED
@@ -0,0 +1,756 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import random
3
+ import re
4
+ from html import escape
5
+
6
+ from pyrate_limiter import BucketFullException, Duration, InMemoryBucket, Limiter, Rate
7
+ from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
8
+ from telegram.constants import ChatMemberStatus, MessageLimit, ParseMode
9
+ from telegram.error import BadRequest
10
+ from telegram.ext import (
11
+ ApplicationHandlerStop,
12
+ CallbackQueryHandler,
13
+ CommandHandler,
14
+ ContextTypes,
15
+ MessageHandler,
16
+ )
17
+ from telegram.ext import filters as filters_module
18
+ from telegram.helpers import escape_markdown, mention_html
19
+
20
+ from Database.sql import cust_filters_sql as sql
21
+ from Mikobot import DEV_USERS, DRAGONS, LOGGER, dispatcher, function
22
+ from Mikobot.plugins.connection import connected
23
+ from Mikobot.plugins.disable import DisableAbleCommandHandler
24
+ from Mikobot.plugins.helper_funcs.alternate import send_message, typing_action
25
+ from Mikobot.plugins.helper_funcs.chat_status import check_admin
26
+ from Mikobot.plugins.helper_funcs.extraction import extract_text
27
+ from Mikobot.plugins.helper_funcs.misc import build_keyboard_parser
28
+ from Mikobot.plugins.helper_funcs.msg_types import get_filter_type
29
+ from Mikobot.plugins.helper_funcs.string_handling import (
30
+ button_markdown_parser,
31
+ escape_invalid_curly_brackets,
32
+ markdown_to_html,
33
+ split_quotes,
34
+ )
35
+
36
+ # <=======================================================================================================>
37
+
38
+ HANDLER_GROUP = 10
39
+
40
+ ENUM_FUNC_MAP = {
41
+ sql.Types.TEXT.value: dispatcher.bot.send_message,
42
+ sql.Types.BUTTON_TEXT.value: dispatcher.bot.send_message,
43
+ sql.Types.STICKER.value: dispatcher.bot.send_sticker,
44
+ sql.Types.DOCUMENT.value: dispatcher.bot.send_document,
45
+ sql.Types.PHOTO.value: dispatcher.bot.send_photo,
46
+ sql.Types.AUDIO.value: dispatcher.bot.send_audio,
47
+ sql.Types.VOICE.value: dispatcher.bot.send_voice,
48
+ sql.Types.VIDEO.value: dispatcher.bot.send_video,
49
+ }
50
+
51
+
52
+ class AntiSpam:
53
+ def __init__(self):
54
+ self.whitelist = (DEV_USERS or []) + (DRAGONS or [])
55
+ # Values are HIGHLY experimental, its recommended you pay attention to our commits as we will be adjusting the values over time with what suits best.
56
+ Duration.CUSTOM = 15 # Custom duration, 15 seconds
57
+ self.sec_limit = Rate(6, Duration.CUSTOM) # 6 / Per 15 Seconds
58
+ self.min_limit = Rate(20, Duration.MINUTE) # 20 / Per minute
59
+ self.hour_limit = Rate(100, Duration.HOUR) # 100 / Per hour
60
+ self.daily_limit = Rate(1000, Duration.DAY) # 1000 / Per day
61
+ self.limiter = Limiter(
62
+ InMemoryBucket(
63
+ [self.sec_limit, self.min_limit, self.hour_limit, self.daily_limit]
64
+ )
65
+ )
66
+
67
+ def check_user(self, user):
68
+ """
69
+ Return True if user is to be ignored else False
70
+ """
71
+ if user in self.whitelist:
72
+ return False
73
+ try:
74
+ self.limiter.consume(user)
75
+ return False
76
+ except BucketFullException:
77
+ return True
78
+
79
+
80
+ MessageHandlerChecker = AntiSpam()
81
+
82
+
83
+ # <================================================ FUNCTION =======================================================>
84
+ @typing_action
85
+ async def list_handlers(update: Update, context: ContextTypes.DEFAULT_TYPE):
86
+ chat = update.effective_chat
87
+ user = update.effective_user
88
+
89
+ conn = await connected(context.bot, update, chat, user.id, need_admin=False)
90
+ if not conn is False:
91
+ chat_id = conn
92
+ chat_obj = await dispatcher.bot.getChat(conn)
93
+ chat_name = chat_obj.title
94
+ filter_list = "*Filter in {}:*\n"
95
+ else:
96
+ chat_id = update.effective_chat.id
97
+ if chat.type == "private":
98
+ chat_name = "Local filters"
99
+ filter_list = "*local filters:*\n"
100
+ else:
101
+ chat_name = chat.title
102
+ filter_list = "*Filters in {}*:\n"
103
+
104
+ all_handlers = sql.get_chat_triggers(chat_id)
105
+
106
+ if not all_handlers:
107
+ await send_message(
108
+ update.effective_message,
109
+ "No filters saved in {}!".format(chat_name),
110
+ )
111
+ return
112
+
113
+ for keyword in all_handlers:
114
+ entry = " • `{}`\n".format(escape_markdown(keyword))
115
+ if len(entry) + len(filter_list) > MessageLimit.MAX_TEXT_LENGTH:
116
+ await send_message(
117
+ update.effective_message,
118
+ filter_list.format(chat_name),
119
+ parse_mode=ParseMode.MARKDOWN,
120
+ )
121
+ filter_list = entry
122
+ else:
123
+ filter_list += entry
124
+
125
+ await send_message(
126
+ update.effective_message,
127
+ filter_list.format(chat_name),
128
+ parse_mode=ParseMode.MARKDOWN,
129
+ )
130
+
131
+
132
+ @typing_action
133
+ @check_admin(is_user=True)
134
+ async def filters(update: Update, context: ContextTypes.DEFAULT_TYPE):
135
+ chat = update.effective_chat
136
+ user = update.effective_user
137
+ msg = update.effective_message
138
+ args = msg.text.split(
139
+ None, 1
140
+ ) # use python's maxsplit to separate Cmd, keyword, and reply_text
141
+
142
+ buttons = None
143
+ conn = await connected(context.bot, update, chat, user.id)
144
+ if not conn is False:
145
+ chat_id = conn
146
+ chat_obj = await dispatcher.bot.getChat(conn)
147
+ chat_name = chat_obj.title
148
+ else:
149
+ chat_id = update.effective_chat.id
150
+ if chat.type == "private":
151
+ chat_name = "local filters"
152
+ else:
153
+ chat_name = chat.title
154
+
155
+ if not msg.reply_to_message and len(args) < 2:
156
+ await send_message(
157
+ update.effective_message,
158
+ "Please provide keyboard keyword for this filter to reply with!",
159
+ )
160
+ return
161
+
162
+ if msg.reply_to_message and not msg.reply_to_message.forum_topic_created:
163
+ if len(args) < 2:
164
+ await send_message(
165
+ update.effective_message,
166
+ "Please provide keyword for this filter to reply with!",
167
+ )
168
+ return
169
+ else:
170
+ keyword = args[1]
171
+ else:
172
+ extracted = split_quotes(args[1])
173
+ if len(extracted) < 1:
174
+ return
175
+ # set trigger -> lower, so as to avoid adding duplicate filters with different cases
176
+ keyword = extracted[0].lower()
177
+
178
+ # Add the filter
179
+ # Note: perhaps handlers can be removed somehow using sql.get_chat_filters
180
+ for handler in dispatcher.handlers.get(HANDLER_GROUP, []):
181
+ if handler.filters == (keyword, chat_id):
182
+ dispatcher.remove_handler(handler, HANDLER_GROUP)
183
+
184
+ text, file_type, file_id, media_spoiler = get_filter_type(msg)
185
+ if not msg.reply_to_message and len(extracted) >= 2:
186
+ offset = len(extracted[1]) - len(
187
+ msg.text,
188
+ ) # set correct offset relative to command + notename
189
+ text, buttons = button_markdown_parser(
190
+ extracted[1],
191
+ entities=msg.parse_entities(),
192
+ offset=offset,
193
+ )
194
+ text = text.strip()
195
+ if not text:
196
+ await send_message(
197
+ update.effective_message,
198
+ "There is no note message - You can't JUST have buttons, you need a message to go with it!",
199
+ )
200
+ return
201
+
202
+ if len(args) >= 2:
203
+ if msg.reply_to_message:
204
+ if msg.reply_to_message.forum_topic_created:
205
+ offset = len(extracted[1]) - len(msg.text)
206
+
207
+ text, buttons = button_markdown_parser(
208
+ extracted[1], entities=msg.parse_entities(), offset=offset
209
+ )
210
+
211
+ text = text.strip()
212
+ if not text:
213
+ await send_message(
214
+ update.effective_message,
215
+ "There is no note message - You can't JUST have buttons, you need a message to go with it!",
216
+ )
217
+ return
218
+ else:
219
+ pass
220
+
221
+ elif msg.reply_to_message and len(args) >= 1:
222
+ if msg.reply_to_message.text:
223
+ text_to_parsing = msg.reply_to_message.text
224
+ elif msg.reply_to_message.caption:
225
+ text_to_parsing = msg.reply_to_message.caption
226
+ else:
227
+ text_to_parsing = ""
228
+ offset = len(
229
+ text_to_parsing,
230
+ ) # set correct offset relative to command + notename
231
+ text, buttons = button_markdown_parser(
232
+ text_to_parsing,
233
+ entities=msg.parse_entities(),
234
+ offset=offset,
235
+ )
236
+ text = text.strip()
237
+
238
+ elif not text and not file_type:
239
+ await send_message(
240
+ update.effective_message,
241
+ "Please provide keyword for this filter reply with!",
242
+ )
243
+ return
244
+
245
+ elif msg.reply_to_message:
246
+ if msg.reply_to_message.forum_topic_created:
247
+ return
248
+
249
+ if msg.reply_to_message.text:
250
+ text_to_parsing = msg.reply_to_message.text
251
+ elif msg.reply_to_message.caption:
252
+ text_to_parsing = msg.reply_to_message.caption
253
+ else:
254
+ text_to_parsing = ""
255
+ offset = len(
256
+ text_to_parsing,
257
+ ) # set correct offset relative to command + notename
258
+ text, buttons = button_markdown_parser(
259
+ text_to_parsing,
260
+ entities=msg.parse_entities(),
261
+ offset=offset,
262
+ )
263
+ text = text.strip()
264
+ if (msg.reply_to_message.text or msg.reply_to_message.caption) and not text:
265
+ await send_message(
266
+ update.effective_message,
267
+ "There is no note message - You can't JUST have buttons, you need a message to go with it!",
268
+ )
269
+ return
270
+
271
+ else:
272
+ await send_message(update.effective_message, "Invalid filter!")
273
+ return
274
+
275
+ add = await addnew_filter(
276
+ update, chat_id, keyword, text, file_type, file_id, buttons, media_spoiler
277
+ )
278
+ # This is an old method
279
+ # sql.add_filter(chat_id, keyword, content, is_sticker, is_document, is_image, is_audio, is_voice, is_video, buttons)
280
+
281
+ if add is True:
282
+ await send_message(
283
+ update.effective_message,
284
+ "Saved filter '{}' in *{}*!".format(keyword, chat_name),
285
+ parse_mode=ParseMode.MARKDOWN,
286
+ )
287
+ raise ApplicationHandlerStop
288
+
289
+
290
+ @typing_action
291
+ @check_admin(is_user=True)
292
+ async def stop_filter(update: Update, context: ContextTypes.DEFAULT_TYPE):
293
+ chat = update.effective_chat
294
+ user = update.effective_user
295
+ args = update.effective_message.text.split(None, 1)
296
+
297
+ conn = await connected(context.bot, update, chat, user.id)
298
+ if not conn is False:
299
+ chat_id = conn
300
+ chat_obj = await dispatcher.bot.getChat(conn)
301
+ chat_name = chat_obj.title
302
+ else:
303
+ chat_id = update.effective_chat.id
304
+ if chat.type == "private":
305
+ chat_name = "Local filters"
306
+ else:
307
+ chat_name = chat.title
308
+
309
+ if len(args) < 2:
310
+ await send_message(update.effective_message, "What should i stop?")
311
+ return
312
+
313
+ chat_filters = sql.get_chat_triggers(chat_id)
314
+
315
+ if not chat_filters:
316
+ await send_message(update.effective_message, "No filters active here!")
317
+ return
318
+
319
+ for keyword in chat_filters:
320
+ if keyword == args[1]:
321
+ sql.remove_filter(chat_id, args[1])
322
+ await send_message(
323
+ update.effective_message,
324
+ "Okay, I'll stop replying to that filter in *{}*.".format(chat_name),
325
+ parse_mode=ParseMode.MARKDOWN,
326
+ )
327
+ raise ApplicationHandlerStop
328
+
329
+ await send_message(
330
+ update.effective_message,
331
+ "That's not a filter - Click: /filters to get currently active filters.",
332
+ )
333
+
334
+
335
+ async def reply_filter(update: Update, context: ContextTypes.DEFAULT_TYPE):
336
+ chat = update.effective_chat
337
+ message = update.effective_message
338
+
339
+ if not update.effective_user or update.effective_user.id == 777000:
340
+ return
341
+ to_match = await extract_text(message)
342
+ if not to_match:
343
+ return
344
+
345
+ chat_filters = sql.get_chat_triggers(chat.id)
346
+ for keyword in chat_filters:
347
+ pattern = r"( |^|[^\w])" + re.escape(keyword) + r"( |$|[^\w])"
348
+ if re.search(pattern, to_match, flags=re.IGNORECASE):
349
+ if MessageHandlerChecker.check_user(update.effective_user.id):
350
+ return
351
+ filt = sql.get_filter(chat.id, keyword)
352
+ if filt.reply == "there is should be a new reply":
353
+ buttons = sql.get_buttons(chat.id, filt.keyword)
354
+ keyb = build_keyboard_parser(context.bot, chat.id, buttons)
355
+ keyboard = InlineKeyboardMarkup(keyb)
356
+
357
+ VALID_WELCOME_FORMATTERS = [
358
+ "first",
359
+ "last",
360
+ "fullname",
361
+ "username",
362
+ "id",
363
+ "chatname",
364
+ "mention",
365
+ ]
366
+ if filt.reply_text:
367
+ if "%%%" in filt.reply_text:
368
+ split = filt.reply_text.split("%%%")
369
+ if all(split):
370
+ text = random.choice(split)
371
+ else:
372
+ text = filt.reply_text
373
+ else:
374
+ text = filt.reply_text
375
+ if text.startswith("~!") and text.endswith("!~"):
376
+ sticker_id = text.replace("~!", "").replace("!~", "")
377
+ try:
378
+ await context.bot.send_sticker(
379
+ chat.id,
380
+ sticker_id,
381
+ reply_to_message_id=message.message_id,
382
+ message_thread_id=message.message_thread_id
383
+ if chat.is_forum
384
+ else None,
385
+ )
386
+ return
387
+ except BadRequest as excp:
388
+ if (
389
+ excp.message
390
+ == "Wrong remote file identifier specified: wrong padding in the string"
391
+ ):
392
+ await context.bot.send_message(
393
+ chat.id,
394
+ "Message couldn't be sent, Is the sticker id valid?",
395
+ message_thread_id=message.message_thread_id
396
+ if chat.is_forum
397
+ else None,
398
+ )
399
+ return
400
+ else:
401
+ LOGGER.exception("Error in filters: " + excp.message)
402
+ return
403
+ valid_format = escape_invalid_curly_brackets(
404
+ text,
405
+ VALID_WELCOME_FORMATTERS,
406
+ )
407
+ if valid_format:
408
+ filtext = valid_format.format(
409
+ first=escape(message.from_user.first_name),
410
+ last=escape(
411
+ message.from_user.last_name
412
+ or message.from_user.first_name,
413
+ ),
414
+ fullname=" ".join(
415
+ [
416
+ escape(message.from_user.first_name),
417
+ escape(message.from_user.last_name),
418
+ ]
419
+ if message.from_user.last_name
420
+ else [escape(message.from_user.first_name)],
421
+ ),
422
+ username="@" + escape(message.from_user.username)
423
+ if message.from_user.username
424
+ else mention_html(
425
+ message.from_user.id,
426
+ message.from_user.first_name,
427
+ ),
428
+ mention=mention_html(
429
+ message.from_user.id,
430
+ message.from_user.first_name,
431
+ ),
432
+ chatname=escape(message.chat.title)
433
+ if message.chat.type != "private"
434
+ else escape(message.from_user.first_name),
435
+ id=message.from_user.id,
436
+ )
437
+ else:
438
+ filtext = ""
439
+ else:
440
+ filtext = ""
441
+
442
+ if filt.file_type in (sql.Types.BUTTON_TEXT, sql.Types.TEXT):
443
+ try:
444
+ await message.reply_text(
445
+ markdown_to_html(filtext),
446
+ parse_mode=ParseMode.HTML,
447
+ disable_web_page_preview=True,
448
+ reply_markup=keyboard,
449
+ )
450
+ except BadRequest as excp:
451
+ LOGGER.exception("Error in filters: " + excp.message)
452
+ try:
453
+ await send_message(
454
+ update.effective_message,
455
+ get_exception(excp, filt, chat),
456
+ )
457
+ except BadRequest as excp:
458
+ LOGGER.exception(
459
+ "Failed to send message: " + excp.message,
460
+ )
461
+ else:
462
+ try:
463
+ if filt.file_type not in [
464
+ sql.Types.PHOTO.value,
465
+ sql.Types.VIDEO,
466
+ ]:
467
+ await ENUM_FUNC_MAP[filt.file_type](
468
+ chat.id,
469
+ filt.file_id,
470
+ reply_markup=keyboard,
471
+ reply_to_message_id=message.message_id,
472
+ message_thread_id=message.message_thread_id
473
+ if chat.is_forum
474
+ else None,
475
+ )
476
+ else:
477
+ await ENUM_FUNC_MAP[filt.file_type](
478
+ chat.id,
479
+ filt.file_id,
480
+ reply_markup=keyboard,
481
+ caption=filt.reply_text,
482
+ reply_to_message_id=message.message_id,
483
+ message_thread_id=message.message_thread_id
484
+ if chat.is_forum
485
+ else None,
486
+ has_spoiler=filt.has_media_spoiler,
487
+ )
488
+ except BadRequest:
489
+ await send_message(
490
+ message,
491
+ "I don't have the permission to send the content of the filter.",
492
+ )
493
+ break
494
+ else:
495
+ if filt.is_sticker:
496
+ await message.reply_sticker(filt.reply)
497
+ elif filt.is_document:
498
+ await message.reply_document(filt.reply)
499
+ elif filt.is_image:
500
+ await message.reply_photo(
501
+ filt.reply, has_spoiler=filt.has_media_spoiler
502
+ )
503
+ elif filt.is_audio:
504
+ await message.reply_audio(filt.reply)
505
+ elif filt.is_voice:
506
+ await message.reply_voice(filt.reply)
507
+ elif filt.is_video:
508
+ await message.reply_video(
509
+ filt.reply, has_spoiler=filt.has_media_spoiler
510
+ )
511
+ elif filt.has_buttons:
512
+ buttons = sql.get_buttons(chat.id, filt.keyword)
513
+ keyb = build_keyboard_parser(context.bot, chat.id, buttons)
514
+ keyboard = InlineKeyboardMarkup(keyb)
515
+
516
+ try:
517
+ await context.bot.send_message(
518
+ chat.id,
519
+ markdown_to_html(filt.reply),
520
+ parse_mode=ParseMode.HTML,
521
+ disable_web_page_preview=True,
522
+ reply_markup=keyboard,
523
+ message_thread_id=message.message_thread_id
524
+ if chat.is_forum
525
+ else None,
526
+ )
527
+ except BadRequest as excp:
528
+ if excp.message == "Unsupported url protocol":
529
+ try:
530
+ await send_message(
531
+ update.effective_message,
532
+ "You seem to be trying to use an unsupported url protocol. "
533
+ "Telegram doesn't support buttons for some protocols, such as tg://. Please try "
534
+ "again...",
535
+ )
536
+ except BadRequest as excp:
537
+ LOGGER.exception("Error in filters: " + excp.message)
538
+ else:
539
+ try:
540
+ await send_message(
541
+ update.effective_message,
542
+ "This message couldn't be sent as it's incorrectly formatted.",
543
+ )
544
+ except BadRequest as excp:
545
+ LOGGER.exception("Error in filters: " + excp.message)
546
+ LOGGER.warning(
547
+ "Message %s could not be parsed",
548
+ str(filt.reply),
549
+ )
550
+ LOGGER.exception(
551
+ "Could not parse filter %s in chat %s",
552
+ str(filt.keyword),
553
+ str(chat.id),
554
+ )
555
+
556
+ else:
557
+ # LEGACY - all new filters will have has_markdown set to True.
558
+ try:
559
+ await context.bot.send_message(
560
+ chat.id,
561
+ filt.reply,
562
+ message_thread_id=message.message_thread_id
563
+ if chat.is_forum
564
+ else None,
565
+ )
566
+ except BadRequest as excp:
567
+ LOGGER.exception("Error in filters: " + excp.message)
568
+ break
569
+
570
+
571
+ async def rmall_filters(update: Update, context: ContextTypes.DEFAULT_TYPE):
572
+ chat = update.effective_chat
573
+ user = update.effective_user
574
+ member = await chat.get_member(user.id)
575
+ if member.status != ChatMemberStatus.OWNER and user.id not in DRAGONS:
576
+ await update.effective_message.reply_text(
577
+ "Only the chat owner can clear all notes at once.",
578
+ )
579
+ else:
580
+ buttons = InlineKeyboardMarkup(
581
+ [
582
+ [
583
+ InlineKeyboardButton(
584
+ text="STOP ALL FILTERS",
585
+ callback_data="filters_rmall",
586
+ ),
587
+ ],
588
+ [InlineKeyboardButton(text="CANCEL", callback_data="filters_cancel")],
589
+ ],
590
+ )
591
+ await update.effective_message.reply_text(
592
+ f"Are you sure you would like to stop ALL filters in {chat.title}? This action cannot be undone.",
593
+ reply_markup=buttons,
594
+ parse_mode=ParseMode.MARKDOWN,
595
+ )
596
+
597
+
598
+ async def rmall_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
599
+ query = update.callback_query
600
+ chat = update.effective_chat
601
+ msg = update.effective_message
602
+ member = await chat.get_member(query.from_user.id)
603
+ if query.data == "filters_rmall":
604
+ if member.status == "creator" or query.from_user.id in DRAGONS:
605
+ allfilters = sql.get_chat_triggers(chat.id)
606
+ if not allfilters:
607
+ await msg.edit_text("No filters in this chat, nothing to stop!")
608
+ return
609
+
610
+ count = 0
611
+ filterlist = []
612
+ for x in allfilters:
613
+ count += 1
614
+ filterlist.append(x)
615
+
616
+ for i in filterlist:
617
+ sql.remove_filter(chat.id, i)
618
+
619
+ await msg.edit_text(f"Cleaned {count} filters in {chat.title}")
620
+
621
+ if member.status == "administrator":
622
+ await query.answer("Only owner of the chat can do this.")
623
+
624
+ if member.status == "member":
625
+ await query.answer("You need to be admin to do this.")
626
+ elif query.data == "filters_cancel":
627
+ if member.status == "creator" or query.from_user.id in DRAGONS:
628
+ await msg.edit_text("Clearing of all filters has been cancelled.")
629
+ return await query.answer()
630
+ if member.status == "administrator":
631
+ await query.answer("Only owner of the chat can do this.")
632
+ if member.status == "member":
633
+ await query.answer("You need to be admin to do this.")
634
+
635
+
636
+ # NOT ASYNC NOT A HANDLER
637
+ def get_exception(excp, filt, chat):
638
+ if excp.message == "Unsupported url protocol":
639
+ return "You seem to be trying to use the URL protocol which is not supported. Telegram does not support key for multiple protocols, such as tg: //. Please try again!"
640
+ elif excp.message == "Reply message not found":
641
+ return "noreply"
642
+ else:
643
+ LOGGER.warning("Message %s could not be parsed", str(filt.reply))
644
+ LOGGER.exception(
645
+ "Could not parse filter %s in chat %s",
646
+ str(filt.keyword),
647
+ str(chat.id),
648
+ )
649
+ return "This data could not be sent because it is incorrectly formatted."
650
+
651
+
652
+ # NOT ASYNC NOT A HANDLER
653
+ async def addnew_filter(
654
+ update, chat_id, keyword, text, file_type, file_id, buttons, has_spoiler
655
+ ):
656
+ msg = update.effective_message
657
+ totalfilt = sql.get_chat_triggers(chat_id)
658
+ if len(totalfilt) >= 150: # Idk why i made this like function....
659
+ await msg.reply_text("This group has reached its max filters limit of 150.")
660
+ return False
661
+ else:
662
+ sql.new_add_filter(
663
+ chat_id, keyword, text, file_type, file_id, buttons, has_spoiler
664
+ )
665
+ return True
666
+
667
+
668
+ def __stats__():
669
+ return "• {} filters, across {} chats.".format(sql.num_filters(), sql.num_chats())
670
+
671
+
672
+ async def __import_data__(chat_id, data, message):
673
+ # set chat filters
674
+ filters = data.get("filters", {})
675
+ for trigger in filters:
676
+ sql.add_to_blacklist(chat_id, trigger)
677
+
678
+
679
+ def __migrate__(old_chat_id, new_chat_id):
680
+ sql.migrate_chat(old_chat_id, new_chat_id)
681
+
682
+
683
+ def __chat_settings__(chat_id, user_id):
684
+ cust_filters = sql.get_chat_triggers(chat_id)
685
+ return "There are `{}` custom filters here.".format(len(cust_filters))
686
+
687
+
688
+ # <=================================================== HELP ====================================================>
689
+
690
+
691
+ __help__ = """
692
+ » `/filters`*:* List all active filters saved in the chat.
693
+
694
+ ➠ *Admin only:*
695
+
696
+ » `/filter <keyword> <reply message>`*:* Add a filter to this chat. The bot will now reply that message whenever 'keyword'\
697
+ is mentioned. If you reply to a sticker with a keyword, the bot will reply with that sticker. NOTE: all filter \
698
+ keywords are in lowercase. If you want your keyword to be a sentence, use quotes. eg: /filter "hey there" How you \
699
+ doin?
700
+
701
+ ➠ Separate diff replies by `%%%` to get random replies
702
+ ➠ *Example:*
703
+
704
+ » `/filter "filtername"
705
+ Reply 1
706
+ %%%
707
+ Reply 2
708
+ %%%
709
+ Reply 3`
710
+
711
+ » `/stop <filter keyword>`*:* Stop that filter.
712
+
713
+ ➠ *Chat creator only:*
714
+ » `/removeallfilters`*:* Remove all chat filters at once.
715
+
716
+ ➠ *Note*: Filters also support markdown formatters like: {first}, {last} etc.. and buttons.
717
+ ➠ Now Supports media spoilers too, and media caption.
718
+ """
719
+
720
+ __mod_name__ = "FILTERS"
721
+
722
+ # <================================================ HANDLER =======================================================>
723
+ FILTER_HANDLER = CommandHandler("filter", filters, block=False)
724
+ STOP_HANDLER = CommandHandler("stop", stop_filter, block=False)
725
+ RMALLFILTER_HANDLER = CommandHandler(
726
+ "removeallfilters",
727
+ rmall_filters,
728
+ filters=filters_module.ChatType.GROUPS,
729
+ block=False,
730
+ )
731
+ RMALLFILTER_CALLBACK = CallbackQueryHandler(
732
+ rmall_callback, pattern=r"filters_.*", block=False
733
+ )
734
+ LIST_HANDLER = DisableAbleCommandHandler(
735
+ "filters", list_handlers, admin_ok=True, block=False
736
+ )
737
+ CUST_FILTER_HANDLER = MessageHandler(
738
+ filters_module.TEXT & ~filters_module.UpdateType.EDITED_MESSAGE,
739
+ reply_filter,
740
+ block=False,
741
+ )
742
+
743
+ function(FILTER_HANDLER)
744
+ function(STOP_HANDLER)
745
+ function(LIST_HANDLER)
746
+ function(CUST_FILTER_HANDLER, HANDLER_GROUP)
747
+ function(RMALLFILTER_HANDLER)
748
+ function(RMALLFILTER_CALLBACK)
749
+
750
+ __handlers__ = [
751
+ FILTER_HANDLER,
752
+ STOP_HANDLER,
753
+ LIST_HANDLER,
754
+ (CUST_FILTER_HANDLER, HANDLER_GROUP, RMALLFILTER_HANDLER),
755
+ ]
756
+ # <================================================ END =======================================================>
Mikobot/plugins/disable.py ADDED
@@ -0,0 +1,376 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import importlib
3
+ import re
4
+ from typing import Dict, List, Optional, Tuple, Union
5
+
6
+ from future.utils import string_types
7
+ from telegram import Update
8
+ from telegram.constants import ParseMode
9
+ from telegram.ext import CommandHandler, ContextTypes, MessageHandler
10
+ from telegram.ext import filters as filters_module
11
+ from telegram.helpers import escape_markdown
12
+
13
+ from Mikobot import function
14
+ from Mikobot.plugins.helper_funcs.misc import is_module_loaded
15
+ from Mikobot.utils.cmdprefix import CMD_STARTERS
16
+
17
+ # <=======================================================================================================>
18
+
19
+ FILENAME = __name__.rsplit(".", 1)[-1]
20
+
21
+ # If module is due to be loaded, then setup all the magical handlers
22
+ if is_module_loaded(FILENAME):
23
+ from Database.sql import disable_sql as sql
24
+ from Mikobot.plugins.helper_funcs.chat_status import (
25
+ check_admin,
26
+ connection_status,
27
+ is_user_admin,
28
+ )
29
+
30
+ DISABLE_CMDS = []
31
+ DISABLE_OTHER = []
32
+ ADMIN_CMDS = []
33
+
34
+ # <================================================ CLASS =======================================================>
35
+ class DisableAbleCommandHandler(CommandHandler):
36
+ def __init__(
37
+ self,
38
+ command,
39
+ callback,
40
+ block: bool,
41
+ filters: filters_module.BaseFilter = None,
42
+ admin_ok=False,
43
+ ):
44
+ super().__init__(command, callback, block=block)
45
+ self.admin_ok = admin_ok
46
+
47
+ if isinstance(command, string_types):
48
+ commands = frozenset({command.lower()})
49
+ DISABLE_CMDS.append(command)
50
+ if admin_ok:
51
+ ADMIN_CMDS.append(command)
52
+ else:
53
+ commands = frozenset(x.lower() for x in command)
54
+ DISABLE_CMDS.extend(command)
55
+ if admin_ok:
56
+ ADMIN_CMDS.extend(command)
57
+ for comm in commands:
58
+ if not re.match(r"^[\da-z_]{1,32}$", comm):
59
+ raise ValueError(f"Command `{comm}` is not a valid bot command")
60
+
61
+ self.commands = commands
62
+ self.filters = (
63
+ filters if filters is not None else filters_module.UpdateType.MESSAGES
64
+ )
65
+
66
+ def check_update(
67
+ self, update
68
+ ) -> Optional[Union[bool, Tuple[List[str], Optional[Union[bool, Dict]]]]]:
69
+ if isinstance(update, Update) and update.effective_message:
70
+ message = update.effective_message
71
+
72
+ if message.text and len(message.text) > 1:
73
+ fst_word = message.text.split(None, 1)[0]
74
+ if len(fst_word) > 1 and any(
75
+ fst_word.startswith(start) for start in CMD_STARTERS
76
+ ):
77
+ args = message.text.split()[1:]
78
+ command_parts = fst_word[1:].split("@")
79
+ command_parts.append(message.get_bot().username)
80
+
81
+ if not (
82
+ command_parts[0].lower() in self.commands
83
+ and command_parts[1].lower()
84
+ == message.get_bot().username.lower()
85
+ ):
86
+ return None
87
+
88
+ chat = update.effective_chat
89
+ user = update.effective_user
90
+
91
+ filter_result = self.filters.check_update(update)
92
+ if filter_result:
93
+ # disabled, admincmd, user admin
94
+ if sql.is_command_disabled(
95
+ chat.id, command_parts[0].lower()
96
+ ):
97
+ # check if command was disabled
98
+ is_disabled = command_parts[
99
+ 0
100
+ ] in ADMIN_CMDS and is_user_admin(chat, user.id)
101
+ if not is_disabled:
102
+ return None
103
+ else:
104
+ return args, filter_result
105
+
106
+ return args, filter_result
107
+ return False
108
+ return None
109
+
110
+ class DisableAbleMessageHandler(MessageHandler):
111
+ def __init__(self, filters, callback, block: bool, friendly, **kwargs):
112
+ super().__init__(filters, callback, block=block, **kwargs)
113
+ DISABLE_OTHER.append(friendly)
114
+ self.friendly = friendly
115
+ if filters:
116
+ self.filters = filters_module.UpdateType.MESSAGES & filters
117
+ else:
118
+ self.filters = filters_module.UpdateType.MESSAGES
119
+
120
+ def check_update(self, update):
121
+ chat = update.effective_chat
122
+ message = update.effective_message
123
+ filter_result = self.filters.check_update(update)
124
+
125
+ try:
126
+ args = message.text.split()[1:]
127
+ except:
128
+ args = []
129
+
130
+ if super().check_update(update):
131
+ if sql.is_command_disabled(chat.id, self.friendly):
132
+ return False
133
+ else:
134
+ return args, filter_result
135
+
136
+ # <=======================================================================================================>
137
+
138
+ # <================================================ FUNCTION =======================================================>
139
+ @connection_status
140
+ @check_admin(is_user=True)
141
+ async def disable(update: Update, context: ContextTypes.DEFAULT_TYPE):
142
+ args = context.args
143
+ chat = update.effective_chat
144
+ if len(args) >= 1:
145
+ disable_cmd = args[0]
146
+ if disable_cmd.startswith(CMD_STARTERS):
147
+ disable_cmd = disable_cmd[1:]
148
+
149
+ if disable_cmd in set(DISABLE_CMDS + DISABLE_OTHER):
150
+ sql.disable_command(chat.id, str(disable_cmd).lower())
151
+ await update.effective_message.reply_text(
152
+ f"Disabled the use of `{disable_cmd}`",
153
+ parse_mode=ParseMode.MARKDOWN,
154
+ )
155
+ else:
156
+ await update.effective_message.reply_text(
157
+ "That command can't be disabled"
158
+ )
159
+
160
+ else:
161
+ await update.effective_message.reply_text("What should I disable?")
162
+
163
+ @connection_status
164
+ @check_admin(is_user=True)
165
+ async def disable_module(
166
+ update: Update, context: ContextTypes.DEFAULT_TYPE
167
+ ) -> None:
168
+ args = context.args
169
+ chat = update.effective_chat
170
+ if len(args) >= 1:
171
+ disable_module = "Mikobot.plugins." + args[0].rsplit(".", 1)[0]
172
+
173
+ try:
174
+ module = importlib.import_module(disable_module)
175
+ except:
176
+ await update.effective_message.reply_text(
177
+ "Does that module even exsist?"
178
+ )
179
+ return
180
+
181
+ try:
182
+ command_list = module.__command_list__
183
+ except:
184
+ await update.effective_message.reply_text(
185
+ "Module does not contain command list!",
186
+ )
187
+ return
188
+
189
+ disabled_cmds = []
190
+ failed_disabled_cmds = []
191
+
192
+ for disable_cmd in command_list:
193
+ if disable_cmd.startswith(CMD_STARTERS):
194
+ disable_cmd = disable_cmd[1:]
195
+
196
+ if disable_cmd in set(DISABLE_CMDS + DISABLE_OTHER):
197
+ sql.disable_command(chat.id, str(disable_cmd).lower())
198
+ disabled_cmds.append(disable_cmd)
199
+ else:
200
+ failed_disabled_cmds.append(disable_cmd)
201
+
202
+ if disabled_cmds:
203
+ disabled_cmds_string = ", ".join(disabled_cmds)
204
+ await update.effective_message.reply_text(
205
+ f"Disabled the use of`{disabled_cmds_string}`",
206
+ parse_mode=ParseMode.MARKDOWN,
207
+ )
208
+
209
+ if failed_disabled_cmds:
210
+ failed_disabled_cmds_string = ", ".join(failed_disabled_cmds)
211
+ await update.effective_message.reply_text(
212
+ f"Commands `{failed_disabled_cmds_string}` can't be disabled",
213
+ parse_mode=ParseMode.MARKDOWN,
214
+ )
215
+
216
+ else:
217
+ await update.effective_message.reply_text("What should I disable?")
218
+
219
+ @connection_status
220
+ @check_admin(is_user=True)
221
+ async def enable(update: Update, context: ContextTypes.DEFAULT_TYPE):
222
+ args = context.args
223
+ chat = update.effective_chat
224
+ if len(args) >= 1:
225
+ enable_cmd = args[0]
226
+ if enable_cmd.startswith(CMD_STARTERS):
227
+ enable_cmd = enable_cmd[1:]
228
+
229
+ if sql.enable_command(chat.id, enable_cmd):
230
+ await update.effective_message.reply_text(
231
+ f"Enabled the use of`{enable_cmd}`",
232
+ parse_mode=ParseMode.MARKDOWN,
233
+ )
234
+ else:
235
+ await update.effective_message.reply_text("Is that even disabled?")
236
+
237
+ else:
238
+ await update.effective_message.reply_text("What sould I enable?")
239
+
240
+ @connection_status
241
+ @check_admin(is_user=True)
242
+ async def enable_module(update: Update, context: ContextTypes.DEFAULT_TYPE):
243
+ args = context.args
244
+ chat = update.effective_chat
245
+
246
+ if len(args) >= 1:
247
+ enable_module = "Mikobot.plugins." + args[0].rsplit(".", 1)[0]
248
+
249
+ try:
250
+ module = importlib.import_module(enable_module)
251
+ except:
252
+ await update.effective_message.reply_text(
253
+ "Does that module even exsist?"
254
+ )
255
+ return
256
+
257
+ try:
258
+ command_list = module.__command_list__
259
+ except:
260
+ await update.effective_message.reply_text(
261
+ "Module does not contain command list!",
262
+ )
263
+ return
264
+
265
+ enabled_cmds = []
266
+ failed_enabled_cmds = []
267
+
268
+ for enable_cmd in command_list:
269
+ if enable_cmd.startswith(CMD_STARTERS):
270
+ enable_cmd = enable_cmd[1:]
271
+
272
+ if sql.enable_command(chat.id, enable_cmd):
273
+ enabled_cmds.append(enable_cmd)
274
+ else:
275
+ failed_enabled_cmds.append(enable_cmd)
276
+
277
+ if enabled_cmds:
278
+ enabled_cmds_string = ", ".join(enabled_cmds)
279
+ await update.effective_message.reply_text(
280
+ f"Enabled the use of`{enabled_cmds_string}`",
281
+ parse_mode=ParseMode.MARKDOWN,
282
+ )
283
+
284
+ if failed_enabled_cmds:
285
+ failed_enabled_cmds_string = ", ".join(failed_enabled_cmds)
286
+ await update.effective_message.reply_text(
287
+ f"Are the commands `{failed_enabled_cmds_string}` even disabled?",
288
+ parse_mode=ParseMode.MARKDOWN,
289
+ )
290
+
291
+ else:
292
+ await update.effective_message.reply_text("ᴡʜᴀᴛ sʜᴏᴜʟᴅ I ᴇɴᴀʙʟᴇ?")
293
+
294
+ @connection_status
295
+ @check_admin(is_user=True)
296
+ async def list_cmds(update: Update, context: ContextTypes.DEFAULT_TYPE):
297
+ if DISABLE_CMDS + DISABLE_OTHER:
298
+ result = ""
299
+ for cmd in set(DISABLE_CMDS + DISABLE_OTHER):
300
+ result += f" - `{escape_markdown(cmd)}`\n"
301
+ await update.effective_message.reply_text(
302
+ f"The following commands are toggleable:\n{result}",
303
+ parse_mode=ParseMode.MARKDOWN,
304
+ )
305
+ else:
306
+ await update.effective_message.reply_text("No commands can be disabled.")
307
+
308
+ # do not async
309
+ def build_curr_disabled(chat_id: Union[str, int]) -> str:
310
+ disabled = sql.get_all_disabled(chat_id)
311
+ if not disabled:
312
+ return "No commands are disabled!"
313
+
314
+ result = ""
315
+ for cmd in disabled:
316
+ result += " - `{}`\n".format(escape_markdown(cmd))
317
+ return "The following commands are currently restricted:\n{}".format(result)
318
+
319
+ @connection_status
320
+ async def commands(update: Update, context: ContextTypes.DEFAULT_TYPE):
321
+ chat = update.effective_chat
322
+ await update.effective_message.reply_text(
323
+ build_curr_disabled(chat.id),
324
+ parse_mode=ParseMode.MARKDOWN,
325
+ )
326
+
327
+ def __stats__():
328
+ return f"• {sql.num_disabled()} disabled items, across {sql.num_chats()} chats."
329
+
330
+ def __migrate__(old_chat_id, new_chat_id):
331
+ sql.migrate_chat(old_chat_id, new_chat_id)
332
+
333
+ def __chat_settings__(chat_id, user_id):
334
+ return build_curr_disabled(chat_id)
335
+
336
+ # <=================================================== HANDLER ====================================================>
337
+
338
+ DISABLE_HANDLER = CommandHandler("disable", disable, block=False)
339
+ DISABLE_MODULE_HANDLER = CommandHandler(
340
+ "disablemodule", disable_module, block=False
341
+ )
342
+ ENABLE_HANDLER = CommandHandler("enable", enable, block=False)
343
+ ENABLE_MODULE_HANDLER = CommandHandler("enablemodule", enable_module, block=False)
344
+ COMMANDS_HANDLER = CommandHandler(["cmds", "disabled"], commands, block=False)
345
+ TOGGLE_HANDLER = CommandHandler("listcmds", list_cmds, block=False)
346
+
347
+ function(DISABLE_HANDLER)
348
+ function(DISABLE_MODULE_HANDLER)
349
+ function(ENABLE_HANDLER)
350
+ function(ENABLE_MODULE_HANDLER)
351
+ function(COMMANDS_HANDLER)
352
+ function(TOGGLE_HANDLER)
353
+
354
+ # <=================================================== HELP ====================================================>
355
+ __help__ = """
356
+ » /cmds: Check the current status of disabled commands
357
+
358
+ ➠ *Admins only*:
359
+
360
+ » /enable < cmd name >: Enable that command.
361
+
362
+ » /disable < cmd name >: Disable that command.
363
+
364
+ » /enablemodule < module name >: Enable all commands in that module.
365
+
366
+ » /disablemodule < module name >: Disable all commands in that module.
367
+
368
+ » /listcmds: List all possible toggleable commands.
369
+ """
370
+
371
+ __mod_name__ = "DISABLE"
372
+
373
+ else:
374
+ DisableAbleCommandHandler = CommandHandler
375
+ DisableAbleMessageHandler = MessageHandler
376
+ # <================================================ END =======================================================>
Mikobot/plugins/extra.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ from time import gmtime, strftime, time
3
+
4
+ from pyrogram import filters
5
+ from pyrogram.types import Message
6
+ from telegram import InlineKeyboardButton, InlineKeyboardMarkup, Update
7
+ from telegram.ext import CallbackQueryHandler, CommandHandler, ContextTypes
8
+
9
+ from Mikobot import LOGGER, app, function
10
+ from Mikobot.plugins.helper_funcs.chat_status import check_admin
11
+
12
+ # <=======================================================================================================>
13
+
14
+ UPTIME = time() # Check bot uptime
15
+
16
+
17
+ # <================================================ FUNCTION =======================================================>
18
+ async def getid(update: Update, context: ContextTypes.DEFAULT_TYPE):
19
+ chat = update.effective_chat
20
+ your_id = update.message.from_user.id
21
+ message_id = update.message.message_id
22
+ reply = update.message.reply_to_message
23
+
24
+ text = f"[Message ID:](https://t.me/{chat.username}/{message_id}) `{message_id}`\n"
25
+ text += f"[Your ID:](tg://user?id={your_id}) `{your_id}`\n"
26
+
27
+ if context.args:
28
+ try:
29
+ user_id = context.args[0]
30
+ text += f"[User ID:](tg://user?id={user_id}) `{user_id}`\n"
31
+ except Exception:
32
+ await update.message.reply_text(
33
+ "This user doesn't exist.", parse_mode="Markdown"
34
+ )
35
+ return
36
+
37
+ text += f"[Chat ID:](https://t.me/{chat.username}) `{chat.id}`\n\n"
38
+
39
+ if reply:
40
+ text += f"[Replied Message ID:](https://t.me/{chat.username}/{reply.message_id}) `{reply.message_id}`\n"
41
+ text += f"[Replied User ID:](tg://user?id={reply.from_user.id}) `{reply.from_user.id}`\n\n"
42
+
43
+ if reply and reply.forward_from_chat:
44
+ text += f"The forwarded channel, {reply.forward_from_chat.title}, has an id of `{reply.forward_from_chat.id}`\n\n"
45
+
46
+ if reply and reply.sender_chat:
47
+ text += f"ID of the replied chat/channel, is `{reply.sender_chat.id}`"
48
+
49
+ # Sticker ID to be sent
50
+ sticker_id = (
51
+ "CAACAgIAAx0CanzPTAABASPCZQdU9NbQIol5TW1GU2zV4KfjDMEAAnccAALIWZhJPyYLf3FzPHswBA"
52
+ )
53
+
54
+ # Send the sticker
55
+ await update.message.reply_sticker(sticker=sticker_id)
56
+
57
+ # Send the text message as a caption
58
+ await update.message.reply_text(
59
+ text, parse_mode="Markdown", disable_web_page_preview=True
60
+ )
61
+
62
+
63
+ # Function to handle the "logs" command
64
+ @check_admin(only_dev=True)
65
+ async def logs(update: Update, context: ContextTypes.DEFAULT_TYPE):
66
+ user = update.effective_user
67
+ with open("Logs.txt", "rb") as f:
68
+ caption = "Here is your log"
69
+ reply_markup = InlineKeyboardMarkup(
70
+ [[InlineKeyboardButton("Close", callback_data="close")]]
71
+ )
72
+ message = await context.bot.send_document(
73
+ document=f,
74
+ filename=f.name,
75
+ caption=caption,
76
+ reply_markup=reply_markup,
77
+ chat_id=user.id,
78
+ )
79
+
80
+ # Store the message ID for later reference
81
+ context.user_data["log_message_id"] = message.message_id
82
+
83
+
84
+ # Asynchronous callback query handler for the "close" button
85
+ @check_admin(only_dev=True)
86
+ async def close_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
87
+ query = update.callback_query
88
+ message_id = context.user_data.get("log_message_id")
89
+ if message_id:
90
+ await context.bot.delete_message(
91
+ chat_id=query.message.chat_id, message_id=message_id
92
+ )
93
+
94
+
95
+ @app.on_message(filters.command("pyroping"))
96
+ async def ping(_, m: Message):
97
+ LOGGER.info(f"{m.from_user.id} used ping cmd in {m.chat.id}")
98
+ start = time()
99
+ replymsg = await m.reply_text(text="Pinging...", quote=True)
100
+ delta_ping = time() - start
101
+
102
+ up = strftime("%Hh %Mm %Ss", gmtime(time() - UPTIME))
103
+ image_url = "https://telegra.ph/file/f215a1c4adf25a5bad81b.jpg"
104
+
105
+ # Send the image as a reply
106
+ await replymsg.reply_photo(
107
+ photo=image_url,
108
+ caption=f"<b>Pyro-Pong!</b>\n{delta_ping * 1000:.3f} ms\n\nUptime: <code>{up}</code>",
109
+ )
110
+ await replymsg.delete()
111
+
112
+
113
+ # <=======================================================================================================>
114
+
115
+
116
+ # <================================================ HANDLER =======================================================>
117
+ function(CommandHandler("logs", logs, block=False))
118
+ function(CommandHandler("id", getid, block=False))
119
+ function(CallbackQueryHandler(close_callback, pattern="^close$", block=False))
120
+
121
+ # <================================================= HELP ======================================================>
122
+ __help__ = """
123
+ ➠ *Commands*:
124
+
125
+ » /ping: see ping.
126
+
127
+ » /id: reply to get user id.
128
+ """
129
+
130
+ __mod_name__ = "EXTRA"
131
+ # <================================================ END =======================================================>
Mikobot/plugins/flood.py ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import html
3
+ import re
4
+
5
+ from telegram import ChatPermissions, Update
6
+ from telegram.error import BadRequest
7
+ from telegram.ext import (
8
+ CallbackQueryHandler,
9
+ CommandHandler,
10
+ ContextTypes,
11
+ MessageHandler,
12
+ filters,
13
+ )
14
+ from telegram.helpers import mention_html
15
+
16
+ from Database.sql import antiflood_sql as sql
17
+ from Database.sql.approve_sql import is_approved
18
+ from Mikobot import dispatcher, function
19
+ from Mikobot.plugins.connection import connected
20
+ from Mikobot.plugins.helper_funcs.alternate import send_message
21
+ from Mikobot.plugins.helper_funcs.chat_status import check_admin, is_user_admin
22
+ from Mikobot.plugins.helper_funcs.string_handling import extract_time
23
+ from Mikobot.plugins.log_channel import loggable
24
+
25
+ # <=======================================================================================================>
26
+
27
+ FLOOD_GROUP = 3
28
+
29
+
30
+ # <================================================ FUNCTION =======================================================>
31
+ @loggable
32
+ async def check_flood(update: Update, context: ContextTypes.DEFAULT_TYPE):
33
+ user = update.effective_user
34
+ chat = update.effective_chat
35
+ msg = update.effective_message
36
+ if not user:
37
+ return ""
38
+
39
+ if await is_user_admin(chat, user.id):
40
+ sql.update_flood(chat.id, None)
41
+ return ""
42
+
43
+ if is_approved(chat.id, user.id):
44
+ sql.update_flood(chat.id, None)
45
+ return
46
+
47
+ should_ban = sql.update_flood(chat.id, user.id)
48
+ if not should_ban:
49
+ return ""
50
+
51
+ try:
52
+ getmode, getvalue = sql.get_flood_setting(chat.id)
53
+ if getmode == 1:
54
+ await chat.ban_member(user.id)
55
+ execstrings = "BANNED"
56
+ tag = "BANNED"
57
+ elif getmode == 2:
58
+ await chat.ban_member(user.id)
59
+ await chat.unban_member(user.id)
60
+ execstrings = "KICKED"
61
+ tag = "KICKED"
62
+ elif getmode == 3:
63
+ await context.bot.restrict_chat_member(
64
+ chat.id,
65
+ user.id,
66
+ permissions=ChatPermissions(can_send_messages=False),
67
+ )
68
+ execstrings = "MUTED"
69
+ tag = "MUTED"
70
+ elif getmode == 4:
71
+ bantime = await extract_time(msg, getvalue)
72
+ await chat.ban_member(user.id, until_date=bantime)
73
+ execstrings = "BANNED for {}".format(getvalue)
74
+ tag = "TBAN"
75
+ elif getmode == 5:
76
+ mutetime = await extract_time(msg, getvalue)
77
+ await context.bot.restrict_chat_member(
78
+ chat.id,
79
+ user.id,
80
+ until_date=mutetime,
81
+ permissions=ChatPermissions(can_send_messages=False),
82
+ )
83
+ execstrings = "MUTED for {}".format(getvalue)
84
+ tag = "TMUTE"
85
+ await send_message(
86
+ update.effective_message,
87
+ "Beep boop! Boop beep!\n{}!".format(execstrings),
88
+ )
89
+
90
+ return (
91
+ "<b>{}:</b>"
92
+ "\n#{}"
93
+ "\n<b>user:</b> {}"
94
+ "\nFlooded the group.".format(
95
+ tag,
96
+ html.escape(chat.title),
97
+ mention_html(user.id, html.escape(user.first_name)),
98
+ )
99
+ )
100
+
101
+ except BadRequest:
102
+ await msg.reply_text(
103
+ "I can't restrict people here, give me permissions first! Until then, I'll disable anti-flood.",
104
+ )
105
+ sql.set_flood(chat.id, 0)
106
+ return (
107
+ "<b>{}:</b>"
108
+ "\n#INFO"
109
+ "\nDon't have enough permission to restrict users so automatically disabled anti-flood.".format(
110
+ chat.title,
111
+ )
112
+ )
113
+
114
+
115
+ @check_admin(permission="can_restrict_members", is_both=True, no_reply=True)
116
+ async def flood_button(update: Update, context: ContextTypes.DEFAULT_TYPE):
117
+ bot = context.bot
118
+ query = update.callback_query
119
+ user = update.effective_user
120
+ match = re.match(r"unmute_flooder\((.+?)\)", query.data)
121
+ if match:
122
+ user_id = match.group(1)
123
+ chat = update.effective_chat.id
124
+ try:
125
+ await bot.restrict_chat_member(
126
+ chat,
127
+ int(user_id),
128
+ permissions=ChatPermissions(
129
+ can_send_messages=True,
130
+ can_send_media_messages=True,
131
+ can_send_other_messages=True,
132
+ can_add_web_page_previews=True,
133
+ ),
134
+ )
135
+ await update.effective_message.edit_text(
136
+ f"Unmuted by {mention_html(user.id, html.escape(user.first_name))}.",
137
+ parse_mode="HTML",
138
+ )
139
+ except:
140
+ pass
141
+
142
+
143
+ @loggable
144
+ @check_admin(is_user=True)
145
+ async def set_flood(update: Update, context: ContextTypes.DEFAULT_TYPE):
146
+ chat = update.effective_chat
147
+ user = update.effective_user
148
+ message = update.effective_message
149
+ args = context.args
150
+
151
+ conn = await connected(context.bot, update, chat, user.id, need_admin=True)
152
+ if conn:
153
+ chat_id = conn
154
+ chat_obj = await dispatcher.bot.getChat(conn)
155
+ chat_name = chat_obj.title
156
+ else:
157
+ if update.effective_message.chat.type == "private":
158
+ await send_message(
159
+ update.effective_message,
160
+ "This command is meant to use in a group, not in PM.",
161
+ )
162
+ return ""
163
+ chat_id = update.effective_chat.id
164
+ chat_name = update.effective_message.chat.title
165
+
166
+ if len(args) >= 1:
167
+ val = args[0].lower()
168
+ if val in ["off", "no", "0"]:
169
+ sql.set_flood(chat_id, 0)
170
+ if conn:
171
+ text = await message.reply_text(
172
+ "Antiflood has been disabled in {}.".format(chat_name),
173
+ )
174
+ else:
175
+ text = await message.reply_text("Antiflood has been disabled.")
176
+
177
+ elif val.isdigit():
178
+ amount = int(val)
179
+ if amount <= 0:
180
+ sql.set_flood(chat_id, 0)
181
+ if conn:
182
+ text = await message.reply_text(
183
+ "Antiflood has been disabled in {}.".format(chat_name),
184
+ )
185
+ else:
186
+ text = await message.reply_text("Antiflood has been disabled.")
187
+ return (
188
+ "<b>{}:</b>"
189
+ "\n#SETFLOOD"
190
+ "\n<b>Admin:</b> {}"
191
+ "\nDisable Antiflood.".format(
192
+ html.escape(chat_name),
193
+ mention_html(user.id, html.escape(user.first_name)),
194
+ )
195
+ )
196
+
197
+ elif amount <= 3:
198
+ await send_message(
199
+ update.effective_message,
200
+ "Antiflood must be either 0 (disabled) or a number greater than 3!",
201
+ )
202
+ return ""
203
+
204
+ else:
205
+ sql.set_flood(chat_id, amount)
206
+ if conn:
207
+ text = await message.reply_text(
208
+ "Antiflood limit has been set to {} in chat: {}".format(
209
+ amount,
210
+ chat_name,
211
+ ),
212
+ )
213
+ else:
214
+ text = await message.reply_text(
215
+ "Successfully updated antiflood limit to {}!".format(amount),
216
+ )
217
+ return (
218
+ "<b>{}:</b>"
219
+ "\n#SETFLOOD"
220
+ "\n<b>Admin:</b> {}"
221
+ "\nSet Antiflood to <code>{}</code>.".format(
222
+ html.escape(chat_name),
223
+ mention_html(user.id, html.escape(user.first_name)),
224
+ amount,
225
+ )
226
+ )
227
+
228
+ else:
229
+ await message.reply_text(
230
+ "Invalid argument, please use a number, 'off', or 'no'."
231
+ )
232
+ else:
233
+ await message.reply_text(
234
+ (
235
+ "Use `/setflood number` to enable antiflood.\n"
236
+ "Or use `/setflood off` to disable antiflood."
237
+ ),
238
+ parse_mode="markdown",
239
+ )
240
+ return ""
241
+
242
+
243
+ async def flood(update: Update, context: ContextTypes.DEFAULT_TYPE):
244
+ chat = update.effective_chat
245
+ user = update.effective_user
246
+ msg = update.effective_message
247
+
248
+ conn = await connected(context.bot, update, chat, user.id, need_admin=False)
249
+ if conn:
250
+ chat_id = conn
251
+ chat_obj = await dispatcher.bot.getChat(conn)
252
+ chat_name = chat_obj.title
253
+ else:
254
+ if update.effective_message.chat.type == "private":
255
+ await send_message(
256
+ update.effective_message,
257
+ "This command is meant to use in a group, not in PM.",
258
+ )
259
+ return
260
+ chat_id = update.effective_chat.id
261
+ chat_name = update.effective_message.chat.title
262
+
263
+ limit = sql.get_flood_limit(chat_id)
264
+ if limit == 0:
265
+ if conn:
266
+ text = await msg.reply_text(
267
+ "I'm not enforcing any flood control in {}!".format(chat_name),
268
+ )
269
+ else:
270
+ text = await msg.reply_text("I'm not enforcing any flood control here!")
271
+ else:
272
+ if conn:
273
+ text = await msg.reply_text(
274
+ "I'm currently restricting members after {} consecutive messages in {}.".format(
275
+ limit,
276
+ chat_name,
277
+ ),
278
+ )
279
+ else:
280
+ text = await msg.reply_text(
281
+ "I'm currently restricting members after {} consecutive messages.".format(
282
+ limit,
283
+ ),
284
+ )
285
+
286
+
287
+ @check_admin(is_user=True)
288
+ async def set_flood_mode(update: Update, context: ContextTypes.DEFAULT_TYPE):
289
+ chat = update.effective_chat
290
+ user = update.effective_user
291
+ msg = update.effective_message
292
+ args = context.args
293
+
294
+ conn = await connected(context.bot, update, chat, user.id, need_admin=True)
295
+ if conn:
296
+ chat = await dispatcher.bot.getChat(conn)
297
+ chat_id = conn
298
+ chat_obj = await dispatcher.bot.getChat(conn)
299
+ chat_name = chat_obj.title
300
+ else:
301
+ if update.effective_message.chat.type == "private":
302
+ await send_message(
303
+ update.effective_message,
304
+ "This command is meant to use in a group, not in PM.",
305
+ )
306
+ return ""
307
+ chat = update.effective_chat
308
+ chat_id = update.effective_chat.id
309
+ chat_name = update.effective_message.chat.title
310
+
311
+ if args:
312
+ if args[0].lower() == "ban":
313
+ settypeflood = "ban"
314
+ sql.set_flood_strength(chat_id, 1, "0")
315
+ elif args[0].lower() == "kick":
316
+ settypeflood = "kick"
317
+ sql.set_flood_strength(chat_id, 2, "0")
318
+ elif args[0].lower() == "mute":
319
+ settypeflood = "mute"
320
+ sql.set_flood_strength(chat_id, 3, "0")
321
+ elif args[0].lower() == "tban":
322
+ if len(args) == 1:
323
+ teks = """It looks like you tried to set time value for antiflood but you didn't specified time; Try, `/setfloodmode tban <timevalue>`.
324
+ Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks."""
325
+ await send_message(
326
+ update.effective_message, teks, parse_mode="markdown"
327
+ )
328
+ return
329
+ settypeflood = "tban for {}".format(args[1])
330
+ sql.set_flood_strength(chat_id, 4, str(args[1]))
331
+ elif args[0].lower() == "tmute":
332
+ if len(args) == 1:
333
+ teks = """It looks like you tried to set time value for antiflood but you didn't specified time; Try, `/setfloodmode tmute <timevalue>`.
334
+ Examples of time value: 4m = 4 minutes, 3h = 3 hours, 6d = 6 days, 5w = 5 weeks."""
335
+ await send_message(
336
+ update.effective_message, teks, parse_mode="markdown"
337
+ )
338
+ return
339
+ settypeflood = "tmute for {}".format(args[1])
340
+ sql.set_flood_strength(chat_id, 5, str(args[1]))
341
+ else:
342
+ await send_message(
343
+ update.effective_message,
344
+ "I only understand ban/kick/mute/tban/tmute!",
345
+ )
346
+ return
347
+ if conn:
348
+ text = await msg.reply_text(
349
+ "Exceeding consecutive flood limit will result in {} in {}!".format(
350
+ settypeflood,
351
+ chat_name,
352
+ ),
353
+ )
354
+ else:
355
+ text = await msg.reply_text(
356
+ "Exceeding consecutive flood limit will result in {}!".format(
357
+ settypeflood,
358
+ ),
359
+ )
360
+ return (
361
+ "<b>{}:</b>\n"
362
+ "<b>Admin:</b> {}\n"
363
+ "Has changed antiflood mode. User will {}.".format(
364
+ settypeflood,
365
+ html.escape(chat.title),
366
+ mention_html(user.id, html.escape(user.first_name)),
367
+ )
368
+ )
369
+ else:
370
+ getmode, getvalue = sql.get_flood_setting(chat.id)
371
+ if getmode == 1:
372
+ settypeflood = "ban"
373
+ elif getmode == 2:
374
+ settypeflood = "kick"
375
+ elif getmode == 3:
376
+ settypeflood = "mute"
377
+ elif getmode == 4:
378
+ settypeflood = "tban for {}".format(getvalue)
379
+ elif getmode == 5:
380
+ settypeflood = "tmute for {}".format(getvalue)
381
+ if conn:
382
+ text = await msg.reply_text(
383
+ "Sending more messages than flood limit will result in {} in {}.".format(
384
+ settypeflood,
385
+ chat_name,
386
+ ),
387
+ )
388
+ else:
389
+ text = await msg.reply_text(
390
+ "Sending more messages than flood limit will result in {}.".format(
391
+ settypeflood,
392
+ ),
393
+ )
394
+ return ""
395
+
396
+
397
+ def __migrate__(old_chat_id, new_chat_id):
398
+ sql.migrate_chat(old_chat_id, new_chat_id)
399
+
400
+
401
+ def __chat_settings__(chat_id, user_id):
402
+ limit = sql.get_flood_limit(chat_id)
403
+ if limit == 0:
404
+ return "Not enforcing flood control."
405
+ else:
406
+ return "Antiflood has been set to `{}`.".format(limit)
407
+
408
+
409
+ # <=================================================== HELP ====================================================>
410
+
411
+
412
+ __help__ = """
413
+ ➠ *Antiflood allows you to take action on users that send more than x messages in a row. Exceeding the set flood will result in restricting that user.*
414
+
415
+ ➠ *Admin Only*
416
+
417
+ » /flood: Get the current antiflood settings.
418
+
419
+ » /setflood <number/off/no>: Set the number of messages after which to take action on a user. Set to '0', 'off', or 'no' to disable.
420
+
421
+ » /setfloodmode <action type>: Choose which action to take on a user who has been flooding. Options: ban/kick/mute/tban/tmute.
422
+ """
423
+
424
+ __mod_name__ = "ANTI-FLOOD"
425
+
426
+ # <================================================ HANDLER =======================================================>
427
+ FLOOD_BAN_HANDLER = MessageHandler(
428
+ filters.ALL & ~filters.StatusUpdate.ALL & filters.ChatType.GROUPS,
429
+ check_flood,
430
+ block=False,
431
+ )
432
+ SET_FLOOD_HANDLER = CommandHandler(
433
+ "setflood", set_flood, filters=filters.ChatType.GROUPS, block=False
434
+ )
435
+ SET_FLOOD_MODE_HANDLER = CommandHandler(
436
+ "setfloodmode", set_flood_mode, block=False
437
+ ) # , filters=filters.ChatType.GROUPS)
438
+ FLOOD_QUERY_HANDLER = CallbackQueryHandler(
439
+ flood_button, pattern=r"unmute_flooder", block=False
440
+ )
441
+ FLOOD_HANDLER = CommandHandler(
442
+ "flood", flood, filters=filters.ChatType.GROUPS, block=False
443
+ )
444
+
445
+ function(FLOOD_BAN_HANDLER, FLOOD_GROUP)
446
+ function(FLOOD_QUERY_HANDLER)
447
+ function(SET_FLOOD_HANDLER)
448
+ function(SET_FLOOD_MODE_HANDLER)
449
+ function(FLOOD_HANDLER)
450
+
451
+ __handlers__ = [
452
+ (FLOOD_BAN_HANDLER, FLOOD_GROUP),
453
+ SET_FLOOD_HANDLER,
454
+ FLOOD_HANDLER,
455
+ SET_FLOOD_MODE_HANDLER,
456
+ ]
457
+ # <================================================ END =======================================================>
Mikobot/plugins/gban.py ADDED
@@ -0,0 +1,543 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <============================================== IMPORTS =========================================================>
2
+ import html
3
+ import time
4
+ from datetime import datetime
5
+ from io import BytesIO
6
+
7
+ from telegram import ChatMemberAdministrator, Update
8
+ from telegram.constants import ParseMode
9
+ from telegram.error import BadRequest, Forbidden, TelegramError
10
+ from telegram.ext import CommandHandler, ContextTypes, MessageHandler, filters
11
+ from telegram.helpers import mention_html
12
+
13
+ import Database.sql.global_bans_sql as sql
14
+ from Database.sql.users_sql import get_user_com_chats
15
+ from Mikobot import (
16
+ DEV_USERS,
17
+ DRAGONS,
18
+ EVENT_LOGS,
19
+ OWNER_ID,
20
+ STRICT_GBAN,
21
+ SUPPORT_CHAT,
22
+ dispatcher,
23
+ function,
24
+ )
25
+ from Mikobot.plugins.helper_funcs.chat_status import (
26
+ check_admin,
27
+ is_user_admin,
28
+ support_plus,
29
+ )
30
+ from Mikobot.plugins.helper_funcs.extraction import extract_user, extract_user_and_text
31
+ from Mikobot.plugins.helper_funcs.misc import send_to_list
32
+
33
+ # <=======================================================================================================>
34
+
35
+ GBAN_ENFORCE_GROUP = 6
36
+
37
+ GBAN_ERRORS = {
38
+ "User is an administrator of the chat",
39
+ "Chat not found",
40
+ "Not enough rights to restrict/unrestrict chat member",
41
+ "User_not_participant",
42
+ "Peer_id_invalid",
43
+ "Group chat was deactivated",
44
+ "Need to be inviter of a user to kick it from a basic group",
45
+ "Chat_admin_required",
46
+ "Only the creator of a basic group can kick group administrators",
47
+ "Channel_private",
48
+ "Not in the chat",
49
+ "Can't remove chat owner",
50
+ }
51
+
52
+ UNGBAN_ERRORS = {
53
+ "User is an administrator of the chat",
54
+ "Chat not found",
55
+ "Not enough rights to restrict/unrestrict chat member",
56
+ "User_not_participant",
57
+ "Method is available for supergroup and channel chats only",
58
+ "Not in the chat",
59
+ "Channel_private",
60
+ "Chat_admin_required",
61
+ "Peer_id_invalid",
62
+ "User not found",
63
+ }
64
+
65
+
66
+ # <================================================ FUNCTION =======================================================>
67
+ @support_plus
68
+ async def gban(update: Update, context: ContextTypes.DEFAULT_TYPE):
69
+ bot, args = context.bot, context.args
70
+ message = update.effective_message
71
+ user = update.effective_user
72
+ chat = update.effective_chat
73
+ log_message = ""
74
+
75
+ user_id, reason = await extract_user_and_text(message, context, args)
76
+
77
+ if not user_id:
78
+ await message.reply_text(
79
+ "You don't seem to be referring to a user or the ID specified is incorrect..",
80
+ )
81
+ return
82
+
83
+ if int(user_id) in DEV_USERS:
84
+ await message.reply_text(
85
+ "That user is part of the Association\nI can't act against our own.",
86
+ )
87
+ return
88
+
89
+ if int(user_id) in DRAGONS:
90
+ await message.reply_text(
91
+ "I spy, with my little eye... a disaster! Why are you guys turning on each other?",
92
+ )
93
+ return
94
+
95
+ if user_id == bot.id:
96
+ await message.reply_text("You uhh...want me to kick myself?")
97
+ return
98
+
99
+ if user_id in [777000, 1087968824]:
100
+ await message.reply_text("Fool! You can't attack Telegram's native tech!")
101
+ return
102
+
103
+ try:
104
+ user_chat = await bot.get_chat(user_id)
105
+ except BadRequest as excp:
106
+ if excp.message == "User not found":
107
+ await message.reply_text("I can't seem to find this user.")
108
+ return ""
109
+ else:
110
+ return
111
+
112
+ if user_chat.type != "private":
113
+ await message.reply_text("That's not a user!")
114
+ return
115
+
116
+ if sql.is_user_gbanned(user_id):
117
+ if not reason:
118
+ await message.reply_text(
119
+ "This user is already gbanned; I'd change the reason, but you haven't given me one...",
120
+ )
121
+ return
122
+
123
+ old_reason = sql.update_gban_reason(
124
+ user_id,
125
+ user_chat.username or user_chat.first_name,
126
+ reason,
127
+ )
128
+ if old_reason:
129
+ await message.reply_text(
130
+ "This user is already gbanned, for the following reason:\n"
131
+ "<code>{}</code>\n"
132
+ "I've gone and updated it with your new reason!".format(
133
+ html.escape(old_reason),
134
+ ),
135
+ parse_mode=ParseMode.HTML,
136
+ )
137
+
138
+ else:
139
+ await message.reply_text(
140
+ "This user is already gbanned, but had no reason set; I've gone and updated it!",
141
+ )
142
+
143
+ return
144
+
145
+ await message.reply_text("On it!")
146
+
147
+ start_time = time.time()
148
+ datetime_fmt = "%Y-%m-%dT%H:%M"
149
+ current_time = datetime.utcnow().strftime(datetime_fmt)
150
+
151
+ if chat.type != "private":
152
+ chat_origin = "<b>{} ({})</b>\n".format(html.escape(chat.title), chat.id)
153
+ else:
154
+ chat_origin = "<b>{}</b>\n".format(chat.id)
155
+
156
+ log_message = (
157
+ f"#GBANNED\n"
158
+ f"<b>Originated from:</b> <code>{chat_origin}</code>\n"
159
+ f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n"
160
+ f"<b>Banned User:</b> {mention_html(user_chat.id, user_chat.first_name)}\n"
161
+ f"<b>Banned User ID:</b> <code>{user_chat.id}</code>\n"
162
+ f"<b>Event Stamp:</b> <code>{current_time}</code>"
163
+ )
164
+
165
+ if reason:
166
+ if chat.type == chat.SUPERGROUP and chat.username:
167
+ log_message += f'\n<b>Reason:</b> <a href="https://telegram.me/{chat.username}/{message.message_id}">{reason}</a>'
168
+ else:
169
+ log_message += f"\n<b>Reason:</b> <code>{reason}</code>"
170
+
171
+ if EVENT_LOGS:
172
+ try:
173
+ log = await bot.send_message(
174
+ EVENT_LOGS, log_message, parse_mode=ParseMode.HTML
175
+ )
176
+ except BadRequest as excp:
177
+ log = await bot.send_message(
178
+ EVENT_LOGS,
179
+ log_message
180
+ + "\n\nFormatting has been disabled due to an unexpected error.",
181
+ )
182
+
183
+ else:
184
+ send_to_list(bot, DRAGONS, log_message, html=True)
185
+
186
+ sql.gban_user(user_id, user_chat.username or user_chat.first_name, reason)
187
+
188
+ chats = get_user_com_chats(user_id)
189
+ gbanned_chats = 0
190
+
191
+ for chat in chats:
192
+ chat_id = int(chat)
193
+
194
+ # Check if this group has disabled gbans
195
+ if not sql.does_chat_gban(chat_id):
196
+ continue
197
+
198
+ try:
199
+ await bot.ban_chat_member(chat_id, user_id)
200
+ gbanned_chats += 1
201
+
202
+ except BadRequest as excp:
203
+ if excp.message in GBAN_ERRORS:
204
+ pass
205
+ else:
206
+ await message.reply_text(f"Could not gban due to: {excp.message}")
207
+ if EVENT_LOGS:
208
+ await bot.send_message(
209
+ EVENT_LOGS,
210
+ f"Could not gban due to {excp.message}",
211
+ parse_mode=ParseMode.HTML,
212
+ )
213
+ else:
214
+ send_to_list(
215
+ bot,
216
+ DRAGONS,
217
+ f"Could not gban due to: {excp.message}",
218
+ )
219
+ sql.ungban_user(user_id)
220
+ return
221
+ except TelegramError:
222
+ pass
223
+
224
+ if EVENT_LOGS:
225
+ await log.edit_text(
226
+ log_message + f"\n<b>Chats affected:</b> <code>{gbanned_chats}</code>",
227
+ parse_mode=ParseMode.HTML,
228
+ )
229
+ else:
230
+ send_to_list(
231
+ bot,
232
+ DRAGONS,
233
+ f"Gban complete! (User banned in <code>{gbanned_chats}</code> chats)",
234
+ html=True,
235
+ )
236
+
237
+ end_time = time.time()
238
+ gban_time = round((end_time - start_time), 2)
239
+
240
+ if gban_time > 60:
241
+ gban_time = round((gban_time / 60), 2)
242
+ await message.reply_text("Done! Gbanned.", parse_mode=ParseMode.HTML)
243
+ else:
244
+ await message.reply_text("Done! Gbanned.", parse_mode=ParseMode.HTML)
245
+
246
+ try:
247
+ await bot.send_message(
248
+ user_id,
249
+ "#EVENT"
250
+ "You have been marked as Malicious and as such have been banned from any future groups we manage."
251
+ f"\n<b>Reason:</b> <code>{html.escape(user.reason)}</code>"
252
+ f"</b>Appeal Chat:</b> @{SUPPORT_CHAT}",
253
+ parse_mode=ParseMode.HTML,
254
+ )
255
+ except:
256
+ pass # bot probably blocked by user
257
+
258
+
259
+ @support_plus
260
+ async def ungban(update: Update, context: ContextTypes.DEFAULT_TYPE):
261
+ bot, args = context.bot, context.args
262
+ message = update.effective_message
263
+ user = update.effective_user
264
+ chat = update.effective_chat
265
+ log_message = ""
266
+
267
+ user_id = await extract_user(message, context, args)
268
+
269
+ if not user_id:
270
+ await message.reply_text(
271
+ "You don't seem to be referring to a user or the ID specified is incorrect..",
272
+ )
273
+ return
274
+
275
+ user_chat = await bot.get_chat(user_id)
276
+ if user_chat.type != "private":
277
+ await message.reply_text("That's not a user!")
278
+ return
279
+
280
+ if not sql.is_user_gbanned(user_id):
281
+ await message.reply_text("This user is not gbanned!")
282
+ return
283
+
284
+ await message.reply_text(
285
+ f"I'll give {user_chat.first_name} a second chance, globally."
286
+ )
287
+
288
+ start_time = time.time()
289
+ datetime_fmt = "%Y-%m-%dT%H:%M"
290
+ current_time = datetime.utcnow().strftime(datetime_fmt)
291
+
292
+ if chat.type != "private":
293
+ chat_origin = f"<b>{html.escape(chat.title)} ({chat.id})</b>\n"
294
+ else:
295
+ chat_origin = f"<b>{chat.id}</b>\n"
296
+
297
+ log_message = (
298
+ f"#UNGBANNED\n"
299
+ f"<b>Originated from:</b> <code>{chat_origin}</code>\n"
300
+ f"<b>Admin:</b> {mention_html(user.id, user.first_name)}\n"
301
+ f"<b>Unbanned User:</b> {mention_html(user_chat.id, user_chat.first_name)}\n"
302
+ f"<b>Unbanned User ID:</b> <code>{user_chat.id}</code>\n"
303
+ f"<b>Event Stamp:</b> <code>{current_time}</code>"
304
+ )
305
+
306
+ if EVENT_LOGS:
307
+ try:
308
+ log = await bot.send_message(
309
+ EVENT_LOGS, log_message, parse_mode=ParseMode.HTML
310
+ )
311
+ except BadRequest as excp:
312
+ log = await bot.send_message(
313
+ EVENT_LOGS,
314
+ log_message
315
+ + "\n\nFormatting has been disabled due to an unexpected error.",
316
+ )
317
+ else:
318
+ send_to_list(bot, DRAGONS, log_message, html=True)
319
+
320
+ chats = get_user_com_chats(user_id)
321
+ ungbanned_chats = 0
322
+
323
+ for chat in chats:
324
+ chat_id = int(chat)
325
+
326
+ # Check if this group has disabled gbans
327
+ if not sql.does_chat_gban(chat_id):
328
+ continue
329
+
330
+ try:
331
+ member = await bot.get_chat_member(chat_id, user_id)
332
+ if member.status == "kicked":
333
+ await bot.unban_chat_member(chat_id, user_id)
334
+ ungbanned_chats += 1
335
+
336
+ except BadRequest as excp:
337
+ if excp.message in UNGBAN_ERRORS:
338
+ pass
339
+ else:
340
+ await message.reply_text(f"Could not un-gban due to: {excp.message}")
341
+ if EVENT_LOGS:
342
+ await bot.send_message(
343
+ EVENT_LOGS,
344
+ f"Could not un-gban due to: {excp.message}",
345
+ parse_mode=ParseMode.HTML,
346
+ )
347
+ else:
348
+ await bot.send_message(
349
+ OWNER_ID,
350
+ f"Could not un-gban due to: {excp.message}",
351
+ )
352
+ return
353
+ except TelegramError:
354
+ pass
355
+
356
+ sql.ungban_user(user_id)
357
+
358
+ if EVENT_LOGS:
359
+ await log.edit_text(
360
+ log_message + f"\n<b>Chats affected:</b> {ungbanned_chats}",
361
+ parse_mode=ParseMode.HTML,
362
+ )
363
+ else:
364
+ send_to_list(bot, DRAGONS, "un-gban complete!")
365
+
366
+ end_time = time.time()
367
+ ungban_time = round((end_time - start_time), 2)
368
+
369
+ if ungban_time > 60:
370
+ ungban_time = round((ungban_time / 60), 2)
371
+ await message.reply_text(f"Person has been un-gbanned. Took {ungban_time} min")
372
+ else:
373
+ await message.reply_text(f"Person has been un-gbanned. Took {ungban_time} sec")
374
+
375
+
376
+ @support_plus
377
+ async def gbanlist(update: Update, context: ContextTypes.DEFAULT_TYPE):
378
+ banned_users = sql.get_gban_list()
379
+
380
+ if not banned_users:
381
+ await update.effective_message.reply_text(
382
+ "There aren't any gbanned users! You're kinder than I expected...",
383
+ )
384
+ return
385
+
386
+ banfile = "Screw these guys.\n"
387
+ for user in banned_users:
388
+ banfile += f"[x] {user['name']} - {user['user_id']}\n"
389
+ if user["reason"]:
390
+ banfile += f"Reason: {user['reason']}\n"
391
+
392
+ with BytesIO(str.encode(banfile)) as output:
393
+ output.name = "gbanlist.txt"
394
+ await update.effective_message.reply_document(
395
+ document=output,
396
+ filename="gbanlist.txt",
397
+ caption="Here is the list of currently gbanned users.",
398
+ )
399
+
400
+
401
+ async def check_and_ban(update, user_id, should_message=True):
402
+ if sql.is_user_gbanned(user_id):
403
+ await update.effective_chat.ban_member(user_id)
404
+ if should_message:
405
+ text = (
406
+ f"<b>Alert</b>: this user is globally banned.\n"
407
+ f"<code>*bans them from here*</code>.\n"
408
+ f"<b>Appeal chat</b>: @{SUPPORT_CHAT}\n"
409
+ f"<b>User ID</b>: <code>{user_id}</code>"
410
+ )
411
+ user = sql.get_gbanned_user(user_id)
412
+ if user.reason:
413
+ text += f"\n<b>Ban Reason:</b> <code>{html.escape(user.reason)}</code>"
414
+ await update.effective_message.reply_text(text, parse_mode=ParseMode.HTML)
415
+
416
+
417
+ async def enforce_gban(update: Update, context: ContextTypes.DEFAULT_TYPE):
418
+ # Not using @restrict handler to avoid spamming - just ignore if cant gban.
419
+ bot = context.bot
420
+ try:
421
+ get_member = await update.effective_chat.get_member(
422
+ bot.id,
423
+ )
424
+ if isinstance(get_member, ChatMemberAdministrator):
425
+ restrict_permission = get_member.can_restrict_members
426
+ else:
427
+ return
428
+ except Forbidden:
429
+ return
430
+ if sql.does_chat_gban(update.effective_chat.id) and restrict_permission:
431
+ user = update.effective_user
432
+ chat = update.effective_chat
433
+ msg = update.effective_message
434
+
435
+ if user and not await is_user_admin(chat, user.id):
436
+ await check_and_ban(update, user.id)
437
+ return
438
+
439
+ if msg.new_chat_members:
440
+ new_members = update.effective_message.new_chat_members
441
+ for mem in new_members:
442
+ await check_and_ban(update, mem.id)
443
+
444
+ if msg.reply_to_message:
445
+ user = msg.reply_to_message.from_user
446
+ if user and not await is_user_admin(chat, user.id):
447
+ await check_and_ban(update, user.id, should_message=False)
448
+
449
+
450
+ @check_admin(is_user=True)
451
+ async def gbanstat(update: Update, context: ContextTypes.DEFAULT_TYPE):
452
+ args = context.args
453
+ if len(args) > 0:
454
+ if args[0].lower() in ["on", "yes"]:
455
+ sql.enable_gbans(update.effective_chat.id)
456
+ await update.effective_message.reply_text(
457
+ "Antispam is now enabled ✅ "
458
+ "I am now protecting your group from potential remote threats!",
459
+ )
460
+ elif args[0].lower() in ["off", "no"]:
461
+ sql.disable_gbans(update.effective_chat.id)
462
+ await update.effective_message.reply_text(
463
+ "I am not now protecting your group from potential remote threats!",
464
+ )
465
+ else:
466
+ await update.effective_message.reply_text(
467
+ "Give me some arguments to choose a setting! on/off, yes/no!\n\n"
468
+ "Your current setting is: {}\n"
469
+ "When True, any gbans that happen will also happen in your group. "
470
+ "When False, they won't, leaving you at the possible mercy of "
471
+ "spammers.".format(sql.does_chat_gban(update.effective_chat.id)),
472
+ )
473
+
474
+
475
+ def __stats__():
476
+ return f"• {sql.num_gbanned_users()} gbanned users."
477
+
478
+
479
+ def __user_info__(user_id):
480
+ is_gbanned = sql.is_user_gbanned(user_id)
481
+ text = "Malicious: <b>{}</b>"
482
+ if user_id in [777000, 1087968824]:
483
+ return ""
484
+ if user_id == dispatcher.bot.id:
485
+ return ""
486
+ if int(user_id) in DRAGONS:
487
+ return ""
488
+ if is_gbanned:
489
+ text = text.format("Yes")
490
+ user = sql.get_gbanned_user(user_id)
491
+ if user.reason:
492
+ text += f"\n<b>Reason:</b> <code>{html.escape(user.reason)}</code>"
493
+ text += f"\n<b>Appeal Chat:</b> @{SUPPORT_CHAT}"
494
+ else:
495
+ text = text.format("???")
496
+ return text
497
+
498
+
499
+ def __migrate__(old_chat_id, new_chat_id):
500
+ sql.migrate_chat(old_chat_id, new_chat_id)
501
+
502
+
503
+ def __chat_settings__(chat_id, user_id):
504
+ return f"This chat is enforcing *gbans*: `{sql.does_chat_gban(chat_id)}`."
505
+
506
+
507
+ # <=================================================== HELP ====================================================>
508
+
509
+
510
+ __help__ = f"""
511
+ ➠ *Admins only:*
512
+ » `/antispam <on/off/yes/no>`*:* Will toggle our antispam tech or return your current settings.
513
+
514
+ ➠ Anti-Spam, used by bot devs to ban spammers across all groups. This helps protect \
515
+ you and your groups by removing spam flooders as quickly as possible.
516
+ ➠ *Note:* Users can appeal gbans or report spammers at @hydraX2support
517
+ """
518
+
519
+ # <================================================ HANDLER =======================================================>
520
+ GBAN_HANDLER = CommandHandler("gban", gban, block=False)
521
+ UNGBAN_HANDLER = CommandHandler("ungban", ungban, block=False)
522
+ GBAN_LIST = CommandHandler("gbanlist", gbanlist, block=False)
523
+
524
+ GBAN_STATUS = CommandHandler(
525
+ "antispam", gbanstat, filters=filters.ChatType.GROUPS, block=False
526
+ )
527
+
528
+ GBAN_ENFORCER = MessageHandler(
529
+ filters.ALL & filters.ChatType.GROUPS, enforce_gban, block=False
530
+ )
531
+
532
+ function(GBAN_HANDLER)
533
+ function(UNGBAN_HANDLER)
534
+ function(GBAN_LIST)
535
+ function(GBAN_STATUS)
536
+
537
+ __mod_name__ = "ANTI-SPAM"
538
+ __handlers__ = [GBAN_HANDLER, UNGBAN_HANDLER, GBAN_LIST, GBAN_STATUS]
539
+
540
+ if STRICT_GBAN: # enforce GBANS if this is set
541
+ function(GBAN_ENFORCER, GBAN_ENFORCE_GROUP)
542
+ __handlers__.append((GBAN_ENFORCER, GBAN_ENFORCE_GROUP))
543
+ # <================================================ END =======================================================>