taslim19 commited on
Commit
98e44f0
·
1 Parent(s): a0d8280

feat: Add advanced antiflood plugin and fix admin commands

Browse files
DragMusic/core/mongo.py CHANGED
@@ -8,6 +8,8 @@ LOGGER(__name__).info("Connecting to your Mongo Database...")
8
  try:
9
  _mongo_async_ = AsyncIOMotorClient(MONGO_DB_URI)
10
  mongodb = _mongo_async_.Anon
 
 
11
  LOGGER(__name__).info("Connected to your Mongo Database.")
12
  except:
13
  LOGGER(__name__).error("Failed to connect to your Mongo Database.")
 
8
  try:
9
  _mongo_async_ = AsyncIOMotorClient(MONGO_DB_URI)
10
  mongodb = _mongo_async_.Anon
11
+ flooddb = _mongo_async_.Flood
12
+ sudoersdb = _mongo_async_.Sudoers
13
  LOGGER(__name__).info("Connected to your Mongo Database.")
14
  except:
15
  LOGGER(__name__).error("Failed to connect to your Mongo Database.")
DragMusic/plugins/management/admins.py CHANGED
@@ -1,9 +1,10 @@
1
  from pyrogram import filters
2
  from pyrogram.types import Message
 
3
  from pyrogram.errors import PeerIdInvalid
4
 
5
  from DragMusic import app
6
- from DragMusic.utils.decorators.admins import AdminRightsCheck
7
 
8
  # Helper function to extract user info from a message
9
  async def extract_user(message: Message, client):
@@ -25,7 +26,7 @@ async def extract_user(message: Message, client):
25
  return None
26
 
27
  @app.on_message(filters.command("promote") & filters.group)
28
- @AdminRightsCheck
29
  async def promote_user(client, message: Message, _):
30
  target_user = await extract_user(message, client)
31
  if not target_user:
@@ -38,7 +39,7 @@ async def promote_user(client, message: Message, _):
38
  await message.reply_text(f"Failed to promote user. Error: {e}")
39
 
40
  @app.on_message(filters.command("demote") & filters.group)
41
- @AdminRightsCheck
42
  async def demote_user(client, message: Message, _):
43
  target_user = await extract_user(message, client)
44
  if not target_user:
@@ -63,7 +64,7 @@ async def demote_user(client, message: Message, _):
63
  @app.on_message(filters.command("adminlist") & filters.group)
64
  async def admin_list(client, message: Message):
65
  admin_list = []
66
- async for admin in client.get_chat_members(message.chat.id, filter="administrators"):
67
  admin_list.append(f"- {admin.user.mention}")
68
 
69
  if admin_list:
@@ -72,7 +73,7 @@ async def admin_list(client, message: Message):
72
  await message.reply_text("No admins found in this chat.")
73
 
74
  @app.on_message(filters.command("admincache") & filters.group)
75
- @AdminRightsCheck
76
  async def admin_cache(client, message: Message, _):
77
  from DragMusic.utils.decorators.admins import adminlist
78
  if message.chat.id in adminlist:
 
1
  from pyrogram import filters
2
  from pyrogram.types import Message
3
+ from pyrogram.enums import ChatMembersFilter
4
  from pyrogram.errors import PeerIdInvalid
5
 
6
  from DragMusic import app
7
+ from DragMusic.utils.decorators.admins import AdminActual
8
 
9
  # Helper function to extract user info from a message
10
  async def extract_user(message: Message, client):
 
26
  return None
27
 
28
  @app.on_message(filters.command("promote") & filters.group)
29
+ @AdminActual
30
  async def promote_user(client, message: Message, _):
31
  target_user = await extract_user(message, client)
32
  if not target_user:
 
39
  await message.reply_text(f"Failed to promote user. Error: {e}")
40
 
41
  @app.on_message(filters.command("demote") & filters.group)
42
+ @AdminActual
43
  async def demote_user(client, message: Message, _):
44
  target_user = await extract_user(message, client)
45
  if not target_user:
 
64
  @app.on_message(filters.command("adminlist") & filters.group)
65
  async def admin_list(client, message: Message):
66
  admin_list = []
67
+ async for admin in client.get_chat_members(message.chat.id, filter=ChatMembersFilter.ADMINISTRATORS):
68
  admin_list.append(f"- {admin.user.mention}")
69
 
70
  if admin_list:
 
73
  await message.reply_text("No admins found in this chat.")
74
 
75
  @app.on_message(filters.command("admincache") & filters.group)
76
+ @AdminActual
77
  async def admin_cache(client, message: Message, _):
78
  from DragMusic.utils.decorators.admins import adminlist
79
  if message.chat.id in adminlist:
DragMusic/plugins/management/antiflood.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from collections import defaultdict
3
+ from datetime import datetime, timedelta
4
+
5
+ from pyrogram import filters
6
+ from pyrogram.types import Message, ChatPermissions
7
+
8
+ from DragMusic import app
9
+ from DragMusic.utils.database import get_flood_settings, update_flood_setting
10
+ from DragMusic.utils.decorators.admins import AdminActual
11
+ from DragMusic.utils.formatters import parse_time
12
+
13
+ # In-memory storage for flood tracking
14
+ user_flood_count = defaultdict(lambda: defaultdict(int))
15
+ user_timed_messages = defaultdict(lambda: defaultdict(list))
16
+ user_offenses = defaultdict(lambda: defaultdict(int))
17
+
18
+
19
+ @app.on_message(filters.group & ~filters.service, group=11)
20
+ async def flood_control(client, message: Message):
21
+ if not message.from_user:
22
+ return
23
+
24
+ chat_id = message.chat.id
25
+ user_id = message.from_user.id
26
+ now = datetime.now()
27
+
28
+ settings = await get_flood_settings(chat_id)
29
+
30
+ # --- Timed Flood Check ---
31
+ t_limit = settings.get("t_limit", 0)
32
+ t_duration = settings.get("t_duration", 30)
33
+
34
+ if t_limit > 0:
35
+ user_timed_messages[chat_id][user_id].append(now)
36
+ # Filter out old messages
37
+ user_timed_messages[chat_id][user_id] = [
38
+ t for t in user_timed_messages[chat_id][user_id] if now - t < timedelta(seconds=t_duration)
39
+ ]
40
+ if len(user_timed_messages[chat_id][user_id]) >= t_limit:
41
+ user_offenses[chat_id][user_id] += 1
42
+ return await take_action(client, message, settings, "timed flood")
43
+
44
+ # --- Consecutive Flood Check ---
45
+ limit = settings.get("limit", 0)
46
+ if limit > 0:
47
+ if user_id != list(user_flood_count[chat_id].keys())[0] if user_flood_count[chat_id] else None:
48
+ user_flood_count[chat_id].clear()
49
+
50
+ user_flood_count[chat_id][user_id] += 1
51
+
52
+ if user_flood_count[chat_id][user_id] >= limit:
53
+ user_offenses[chat_id][user_id] += 1
54
+ return await take_action(client, message, settings, "consecutive flood")
55
+
56
+ async def take_action(client, message: Message, settings: dict, reason: str):
57
+ chat_id = message.chat.id
58
+ user_id = message.from_user.id
59
+ action = settings.get("action", "ban")
60
+ duration_str = settings.get("action_duration", "1h")
61
+
62
+ # Clear flood messages if enabled
63
+ if settings.get("clear", False):
64
+ # This is a placeholder; deleting messages requires storing message_ids
65
+ pass
66
+
67
+ try:
68
+ if action == "ban":
69
+ await client.ban_chat_member(chat_id, user_id)
70
+ await message.reply_text(f"{message.from_user.mention} has been banned for {reason}.")
71
+ elif action == "kick":
72
+ await client.ban_chat_member(chat_id, user_id)
73
+ await client.unban_chat_member(chat_id, user_id)
74
+ await message.reply_text(f"{message.from_user.mention} has been kicked for {reason}.")
75
+ elif action == "mute":
76
+ await client.restrict_chat_member(chat_id, user_id, permissions=ChatPermissions())
77
+ await message.reply_text(f"{message.from_user.mention} has been muted for {reason}.")
78
+ elif action in ["tban", "tmute"]:
79
+ until_date = datetime.now() + parse_time(duration_str)
80
+ if action == "tban":
81
+ await client.ban_chat_member(chat_id, user_id, until_date=until_date)
82
+ await message.reply_text(f"{message.from_user.mention} has been banned for {duration_str} due to {reason}.")
83
+ else: # tmute
84
+ await client.restrict_chat_member(chat_id, user_id, permissions=ChatPermissions(), until_date=until_date)
85
+ await message.reply_text(f"{message.from_user.mention} has been muted for {duration_str} due to {reason}.")
86
+
87
+ # Reset counters
88
+ user_flood_count[chat_id].clear()
89
+ user_timed_messages[chat_id].clear()
90
+ except Exception as e:
91
+ await message.reply_text(f"Failed to take action. Error: {e}")
92
+
93
+ # --- Admin Commands ---
94
+
95
+ @app.on_message(filters.command("flood") & filters.group)
96
+ @AdminActual
97
+ async def get_flood_command(client, message: Message, _):
98
+ settings = await get_flood_settings(message.chat.id)
99
+ limit = settings.get('limit')
100
+ t_limit = settings.get('t_limit')
101
+ action = settings.get('action')
102
+ duration = settings.get('action_duration')
103
+
104
+ status_limit = f"{limit} consecutive messages" if limit > 0 else "Disabled"
105
+ status_t_limit = f"{t_limit} messages in {settings.get('t_duration')}s" if t_limit > 0 else "Disabled"
106
+
107
+ if action in ["tban", "tmute"]:
108
+ action_str = f"{action} for {duration}"
109
+ else:
110
+ action_str = action
111
+
112
+ await message.reply_text(
113
+ f"**Antiflood Settings:**\n"
114
+ f"- **Consecutive Flood:** {status_limit}\n"
115
+ f"- **Timed Flood:** {status_t_limit}\n"
116
+ f"- **Action:** {action_str}\n"
117
+ f"- **Clear messages:** {'On' if settings.get('clear') else 'Off'}"
118
+ )
119
+
120
+ @app.on_message(filters.command("setflood") & filters.group)
121
+ @AdminActual
122
+ async def set_flood_command(client, message: Message, _):
123
+ if len(message.command) < 2:
124
+ return await message.reply_text("Usage: /setflood <number/off>")
125
+
126
+ arg = message.command[1].lower()
127
+ if arg in ["off", "no", "0"]:
128
+ await update_flood_setting(message.chat.id, "limit", 0)
129
+ await message.reply_text("Consecutive antiflood has been disabled.")
130
+ elif arg.isdigit():
131
+ limit = int(arg)
132
+ if limit < 3:
133
+ return await message.reply_text("Flood limit must be at least 3.")
134
+ await update_flood_setting(message.chat.id, "limit", limit)
135
+ await message.reply_text(f"Consecutive flood limit set to {limit} messages.")
136
+ else:
137
+ await message.reply_text("Invalid argument. Use a number or 'off'.")
138
+
139
+ @app.on_message(filters.command("setfloodtimer") & filters.group)
140
+ @AdminActual
141
+ async def set_flood_timer_command(client, message: Message, _):
142
+ args = message.command[1:]
143
+ if not args or args[0].lower() in ["off", "no", "0"]:
144
+ await update_flood_setting(message.chat.id, "t_limit", 0)
145
+ return await message.reply_text("Timed antiflood has been disabled.")
146
+
147
+ if len(args) != 2 or not args[0].isdigit() or not re.match(r"\d+s", args[1].lower()):
148
+ return await message.reply_text("Usage: /setfloodtimer <count> <duration>s (e.g., 10 30s)")
149
+
150
+ await update_flood_setting(message.chat.id, "t_limit", int(args[0]))
151
+ await update_flood_setting(message.chat.id, "t_duration", int(args[1][:-1]))
152
+ await message.reply_text(f"Timed flood set to {args[0]} messages in {args[1]}.")
153
+
154
+ @app.on_message(filters.command("floodmode") & filters.group)
155
+ @AdminActual
156
+ async def set_flood_mode_command(client, message: Message, _):
157
+ args = message.command[1:]
158
+ if not args:
159
+ return await message.reply_text("Usage: /floodmode <ban|kick|mute|tban|tmute> [duration]")
160
+
161
+ action = args[0].lower()
162
+ if action not in ["ban", "kick", "mute", "tban", "tmute"]:
163
+ return await message.reply_text("Invalid action. Use: ban, kick, mute, tban, tmute.")
164
+
165
+ await update_flood_setting(message.chat.id, "action", action)
166
+
167
+ if action in ["tban", "tmute"]:
168
+ if len(args) < 2:
169
+ return await message.reply_text(f"Usage: /floodmode {action} <duration> (e.g., 1h, 3d)")
170
+ duration_str = args[1]
171
+ if not parse_time(duration_str):
172
+ return await message.reply_text("Invalid time format. Use d, h, m, s (e.g., 3d, 10m).")
173
+ await update_flood_setting(message.chat.id, "action_duration", duration_str)
174
+ await message.reply_text(f"Antiflood action set to {action} for {duration_str}.")
175
+ else:
176
+ await message.reply_text(f"Antiflood action set to {action}.")
177
+
178
+ @app.on_message(filters.command("clearflood") & filters.group)
179
+ @AdminActual
180
+ async def set_clear_flood_command(client, message: Message, _):
181
+ if len(message.command) < 2 or message.command[1].lower() not in ["on", "off", "yes", "no"]:
182
+ return await message.reply_text("Usage: /clearflood <on/off>")
183
+
184
+ status = message.command[1].lower() in ["on", "yes"]
185
+ await update_flood_setting(message.chat.id, "clear", status)
186
+ await message.reply_text(f"Deleting flood messages is now {'enabled' if status else 'disabled'}.")
DragMusic/utils/database.py CHANGED
@@ -644,3 +644,25 @@ async def remove_banned_user(user_id: int):
644
  if not is_gbanned:
645
  return
646
  return await blockeddb.delete_one({"user_id": user_id})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644
  if not is_gbanned:
645
  return
646
  return await blockeddb.delete_one({"user_id": user_id})
647
+
648
+
649
+ # Antiflood Settings
650
+ async def get_flood_settings(chat_id: int):
651
+ settings = await flooddb.find_one({"chat_id": chat_id})
652
+ if not settings:
653
+ return {
654
+ "limit": 0, # Consecutive messages, 0 to disable
655
+ "action": "ban",
656
+ "t_limit": 0, # Timed message count, 0 to disable
657
+ "t_duration": 30, # Timed duration in seconds
658
+ "action_duration": "1h", # Duration for tban/tmute
659
+ "clear": False, # Whether to delete flooding messages
660
+ }
661
+ return settings
662
+
663
+ async def update_flood_setting(chat_id: int, key: str, value):
664
+ await flooddb.update_one(
665
+ {"chat_id": chat_id},
666
+ {"$set": {key: value}},
667
+ upsert=True
668
+ )
DragMusic/utils/decorators/admins.py CHANGED
@@ -117,44 +117,20 @@ def AdminRightsCheck(mystic):
117
 
118
  def AdminActual(mystic):
119
  async def wrapper(client, message):
120
- if await is_maintenance() is False:
121
- if message.from_user.id not in SUDOERS:
122
- return await message.reply_text(
123
- text=f"{app.mention} ɪs ᴜɴᴅᴇʀ ᴍᴀɪɴᴛᴇɴᴀɴᴄᴇ, ᴠɪsɪᴛ <a href={SUPPORT_CHAT}>sᴜᴘᴘᴏʀᴛ ᴄʜᴀᴛ</a> ғᴏʀ ᴋɴᴏᴡɪɴɢ ᴛʜᴇ ʀᴇᴀsᴏɴ.",
124
- disable_web_page_preview=True,
125
- )
126
-
127
- try:
128
- await message.delete()
129
- except:
130
- pass
131
-
132
  try:
133
  language = await get_lang(message.chat.id)
134
  _ = get_string(language)
135
  except:
136
  _ = get_string("en")
137
- if message.sender_chat:
138
- upl = InlineKeyboardMarkup(
139
- [
140
- [
141
- InlineKeyboardButton(
142
- text="ʜᴏᴡ ᴛᴏ ғɪx ?",
143
- callback_data="DragmousAdmin",
144
- ),
145
- ]
146
- ]
147
- )
148
- return await message.reply_text(_["general_3"], reply_markup=upl)
149
  if message.from_user.id not in SUDOERS:
150
  try:
151
- member = (
152
- await app.get_chat_member(message.chat.id, message.from_user.id)
153
- ).privileges
154
  except:
155
- return
156
- if not member.can_manage_video_chats:
157
- return await message.reply(_["general_4"])
158
  return await mystic(client, message, _)
159
 
160
  return wrapper
 
117
 
118
  def AdminActual(mystic):
119
  async def wrapper(client, message):
 
 
 
 
 
 
 
 
 
 
 
 
120
  try:
121
  language = await get_lang(message.chat.id)
122
  _ = get_string(language)
123
  except:
124
  _ = get_string("en")
125
+
 
 
 
 
 
 
 
 
 
 
 
126
  if message.from_user.id not in SUDOERS:
127
  try:
128
+ member = await app.get_chat_member(message.chat.id, message.from_user.id)
129
+ if not member.privileges.can_promote_members:
130
+ return await message.reply_text(_["admin_18"])
131
  except:
132
+ return await message.reply_text("You are not an admin.")
133
+
 
134
  return await mystic(client, message, _)
135
 
136
  return wrapper
DragMusic/utils/formatters.py CHANGED
@@ -1,29 +1,26 @@
1
  import json
2
  import subprocess
 
 
3
 
4
 
5
  def get_readable_time(seconds: int) -> str:
6
- count = 0
7
- ping_time = ""
8
- time_list = []
9
- time_suffix_list = ["s", "ᴍ", "ʜ", "ᴅᴀʏs"]
10
- while count < 4:
11
- count += 1
12
- if count < 3:
13
- remainder, result = divmod(seconds, 60)
14
- else:
15
- remainder, result = divmod(seconds, 24)
16
- if seconds == 0 and remainder == 0:
17
- break
18
- time_list.append(int(result))
19
- seconds = int(remainder)
20
- for i in range(len(time_list)):
21
- time_list[i] = str(time_list[i]) + time_suffix_list[i]
22
- if len(time_list) == 4:
23
- ping_time += time_list.pop() + ", "
24
- time_list.reverse()
25
- ping_time += ":".join(time_list)
26
- return ping_time
27
 
28
 
29
  def convert_bytes(size: float) -> str:
@@ -183,3 +180,19 @@ formats = [
183
  "f4a",
184
  "f4b",
185
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import json
2
  import subprocess
3
+ import re
4
+ from datetime import datetime, timedelta
5
 
6
 
7
  def get_readable_time(seconds: int) -> str:
8
+ result = ""
9
+ (days, remainder) = divmod(seconds, 86400)
10
+ days = int(days)
11
+ if days != 0:
12
+ result += f"{days}d "
13
+ (hours, remainder) = divmod(remainder, 3600)
14
+ hours = int(hours)
15
+ if hours != 0:
16
+ result += f"{hours}h "
17
+ (minutes, seconds) = divmod(remainder, 60)
18
+ minutes = int(minutes)
19
+ if minutes != 0:
20
+ result += f"{minutes}m "
21
+ seconds = int(seconds)
22
+ result += f"{seconds}s "
23
+ return result.strip()
 
 
 
 
 
24
 
25
 
26
  def convert_bytes(size: float) -> str:
 
180
  "f4a",
181
  "f4b",
182
  ]
183
+
184
+
185
+ def parse_time(time_str: str) -> timedelta:
186
+ matches = re.match(r"(\d+)([d|h|m|s])", time_str)
187
+ if matches:
188
+ quantity, unit = matches.groups()
189
+ quantity = int(quantity)
190
+ if unit == 'd':
191
+ return timedelta(days=quantity)
192
+ elif unit == 'h':
193
+ return timedelta(hours=quantity)
194
+ elif unit == 'm':
195
+ return timedelta(minutes=quantity)
196
+ elif unit == 's':
197
+ return timedelta(seconds=quantity)
198
+ return timedelta(minutes=0) # Default or error case