22from discord import app_commands
33from discord .ext import commands
44from datetime import timedelta , datetime
5+ from math import ceil
56import asyncio
67
78from database .mongo import add_hacked_user , get_hacked_users , remove_hacked_user
@@ -26,9 +27,7 @@ async def has_security_permission(self, source):
2627 return False
2728
2829 # --- CORE LOGIC: The shared hacked/purge process ---
29- async def _execute_hacked_action (
30- self , guild , target_user , moderator , days_to_clean = 7
31- ):
30+ async def _execute_hacked_action (self , guild , target_user , moderator ):
3231 """
3332 Shared logic that performs the timeout, DB update, and message purge.
3433 """
@@ -68,7 +67,7 @@ async def _execute_hacked_action(
6867 print (f"Failed to DM hacked user { target_user .id } : { e } " )
6968
7069 # 5. Global Message Purge
71- cutoff_date = datetime .utcnow () - timedelta (days = days_to_clean )
70+ cutoff_date = datetime .utcnow () - timedelta (hours = 12 )
7271 total_deleted = 0
7372 channels_checked = 0
7473
@@ -107,7 +106,7 @@ async def _execute_hacked_action(
107106 embed .add_field (name = "Status" , value = timeout_status , inline = False )
108107 embed .add_field (
109108 name = "Cleanup Stats" ,
110- value = f"🗑️ Deleted **{ total_deleted } messages** across **{ channels_checked } channels** (Past { days_to_clean } days )." ,
109+ value = f"🗑️ Deleted **{ total_deleted } messages** across **{ channels_checked } channels** (Past 12 hours )." ,
111110 inline = False ,
112111 )
113112 embed .add_field (
@@ -130,14 +129,11 @@ async def _send_security_logs(self, embed):
130129 name = "hacked" ,
131130 description = "MOD/ADMIN: Flag user as hacked, timeout them, and delete messages." ,
132131 )
133- @app_commands .describe (
134- user = "The hacked user" , days_to_clean = "Days of messages to delete (default 7)"
135- )
132+ @app_commands .describe (user = "The hacked user" )
136133 async def hacked_slash (
137134 self ,
138135 interaction : discord .Interaction ,
139136 user : discord .Member ,
140- days_to_clean : int = 7 ,
141137 ):
142138 if not await self .has_security_permission (interaction ):
143139 await interaction .response .send_message (
@@ -147,7 +143,7 @@ async def hacked_slash(
147143
148144 await interaction .response .defer ()
149145 result_embed = await self ._execute_hacked_action (
150- interaction .guild , user , interaction .user , days_to_clean
146+ interaction .guild , user , interaction .user
151147 )
152148 await interaction .followup .send (embed = result_embed )
153149
@@ -163,6 +159,9 @@ async def hacked_text(self, ctx):
163159 if not await self .has_security_permission (ctx ):
164160 return
165161
162+ if ctx .message .content .strip () != "!hacked" :
163+ return
164+
166165 if not ctx .message .reference :
167166 await ctx .send ("❌ Reply to a message with `!hacked` to flag that user." )
168167 return
@@ -229,24 +228,71 @@ async def hackedlist(self, interaction: discord.Interaction):
229228 )
230229 return
231230
232- embed = discord .Embed (
233- title = "🚨 Hacked Users List" , color = discord .Color .dark_red ()
231+ view = HackedListView (users , interaction .user )
232+ await interaction .followup .send (embed = view .create_embed (), view = view )
233+
234+
235+ class HackedListView (discord .ui .View ):
236+ def __init__ (self , users : list , author : discord .User ):
237+ super ().__init__ (timeout = 300 )
238+ self .users = sorted (
239+ users , key = lambda u : u .get ("timestamp" , datetime .min ), reverse = True
234240 )
235- description_lines = []
236- for u in users :
241+ self .author = author
242+ self .per_page = 10
243+ self .current_page = 0
244+ self .total_pages = ceil (len (users ) / self .per_page )
245+ self .update_buttons ()
246+
247+ def create_embed (self ) -> discord .Embed :
248+ start = self .current_page * self .per_page
249+ end = start + self .per_page
250+ page_users = self .users [start :end ]
251+
252+ entries = []
253+ for u in page_users :
237254 user_id = u ["_id" ]
238255 reason = u .get ("reason" , "No reason provided" )
239256 time_str = u .get ("timestamp" , datetime .utcnow ()).strftime ("%Y-%m-%d" )
240- description_lines .append (
241- f"• <@{ user_id } > (`{ user_id } `)\n Reason : *{ reason } * ({ time_str } )"
257+ entries .append (
258+ f"<@{ user_id } > (`{ user_id } `)\n Reason : *{ reason } * ({ time_str } )"
242259 )
243260
244- full_text = "\n " .join (description_lines )
245- if len (full_text ) > 4000 :
246- full_text = full_text [:3900 ] + "..."
261+ embed = discord .Embed (
262+ title = "🚨 Hacked Users List" ,
263+ description = "\n \n " .join (entries ),
264+ color = discord .Color .dark_red (),
265+ )
266+ embed .set_footer (text = f"Page { self .current_page + 1 } /{ self .total_pages } " )
267+ return embed
268+
269+ def update_buttons (self ):
270+ self .prev_button .disabled = self .current_page == 0
271+ self .next_button .disabled = self .current_page == self .total_pages - 1
247272
248- embed .description = full_text
249- await interaction .followup .send (embed = embed )
273+ @discord .ui .button (
274+ label = "◀ Previous" , style = discord .ButtonStyle .blurple , disabled = True
275+ )
276+ async def prev_button (
277+ self , interaction : discord .Interaction , button : discord .ui .Button
278+ ):
279+ if interaction .user .id != self .author .id :
280+ await interaction .response .defer ()
281+ return
282+ self .current_page -= 1
283+ self .update_buttons ()
284+ await interaction .response .edit_message (embed = self .create_embed (), view = self )
285+
286+ @discord .ui .button (label = "Next ▶" , style = discord .ButtonStyle .blurple )
287+ async def next_button (
288+ self , interaction : discord .Interaction , button : discord .ui .Button
289+ ):
290+ if interaction .user .id != self .author .id :
291+ await interaction .response .defer ()
292+ return
293+ self .current_page += 1
294+ self .update_buttons ()
295+ await interaction .response .edit_message (embed = self .create_embed (), view = self )
250296
251297
252298async def setup (bot ):
0 commit comments