dc-securex 2.15.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of dc-securex might be problematic. Click here for more details.

securex/client.py ADDED
@@ -0,0 +1,556 @@
1
+ """
2
+ SecureX SDK - Main Client
3
+ Backend-only anti-nuke protection system
4
+ """
5
+ import discord
6
+ import asyncio
7
+ from pathlib import Path
8
+ from typing import Dict, List, Callable, Optional
9
+ from .backup.manager import BackupManager
10
+ from .handlers.channel import ChannelHandler
11
+ from .handlers.role import RoleHandler
12
+ from .handlers.member import MemberHandler
13
+ from .utils.whitelist import WhitelistManager
14
+ from .utils.punishment import PunishmentExecutor
15
+ from .models import ThreatEvent, BackupInfo
16
+
17
+
18
+ class SecureX:
19
+ """
20
+ Main SecureX SDK class for anti-nuke protection.
21
+ Backend-only - no UI, no commands, just pure protection logic.
22
+ """
23
+
24
+ def __init__(
25
+ self,
26
+ bot: discord.Client,
27
+ backup_dir: str = "./data/backups",
28
+ punishments: Optional[Dict[str, str]] = None,
29
+ timeout_duration: int = 600,
30
+ notify_user: bool = True
31
+ ):
32
+ """
33
+ Initialize SecureX SDK.
34
+
35
+ Args:
36
+ bot: Your discord.Client or commands.Bot instance
37
+ backup_dir: Directory to store backups (default: ./data/backups)
38
+ punishments: Dict mapping violation types to punishment actions
39
+ timeout_duration: Duration in seconds for timeout punishment (default: 600)
40
+ notify_user: Whether to DM violators about punishment (default: True)
41
+ """
42
+ self.bot = bot
43
+ self.backup_dir = Path(backup_dir)
44
+ self.backup_dir.mkdir(parents=True, exist_ok=True)
45
+
46
+
47
+ self.backup_manager = BackupManager(self)
48
+ self.whitelist = WhitelistManager(self)
49
+
50
+
51
+ self._callbacks: Dict[str, List[Callable]] = {
52
+ 'threat_detected': [],
53
+ 'backup_completed': [],
54
+ 'restore_completed': [],
55
+ 'whitelist_changed': [],
56
+ }
57
+
58
+
59
+ self.punishments = {
60
+
61
+ "bot_add": "none",
62
+
63
+
64
+ "channel_create": "none",
65
+ "channel_delete": "none",
66
+ "channel_update": "none",
67
+
68
+
69
+ "role_create": "none",
70
+ "role_delete": "none",
71
+ "role_update": "none",
72
+
73
+
74
+ "member_ban": "none",
75
+ "member_kick": "none",
76
+ "member_unban": "none",
77
+ "member_update": "none",
78
+
79
+
80
+ "webhook_create": "none",
81
+ "webhook_delete": "none",
82
+ "webhook_update": "none"
83
+ }
84
+
85
+
86
+ self.punisher = PunishmentExecutor(bot)
87
+ self.timeout_duration = timeout_duration
88
+ self.notify_punished_user = notify_user
89
+
90
+
91
+ self.action_queue = asyncio.Queue(maxsize=1000)
92
+ self.log_queue = asyncio.Queue(maxsize=1000)
93
+ self.cleanup_queue = asyncio.Queue(maxsize=1000)
94
+
95
+
96
+ self.whitelist_cache = {}
97
+ self.punishment_cache = {}
98
+
99
+
100
+ self._worker_tasks = []
101
+
102
+
103
+ self.channel_handler = ChannelHandler(self)
104
+ self.role_handler = RoleHandler(self)
105
+ self.member_handler = MemberHandler(self)
106
+ self.webhook_handler = WebhookHandler(self)
107
+
108
+
109
+ self._register_audit_log_listener()
110
+
111
+
112
+ self._register_events()
113
+
114
+ def _register_audit_log_listener(self):
115
+ """Register INSTANT audit log event listener (v2.0 - 5-10ms response)"""
116
+ @self.bot.event
117
+ async def on_audit_log_entry_create(entry: discord.AuditLogEntry):
118
+
119
+ try:
120
+
121
+ await self.action_queue.put(entry)
122
+ await self.log_queue.put(entry)
123
+ await self.cleanup_queue.put(entry)
124
+ except asyncio.QueueFull as e:
125
+ print(f"⚠️ Queue full: {e}")
126
+
127
+ async def _action_worker(self):
128
+ """Worker 1: Process audit log entries and punish instantly"""
129
+ print("🔧 Action Worker started")
130
+ while True:
131
+ try:
132
+
133
+ entry = await self.action_queue.get()
134
+
135
+ guild = entry.guild
136
+ executor = entry.user
137
+
138
+
139
+ if executor.id == self.bot.user.id:
140
+ continue
141
+
142
+
143
+ if executor.id == guild.owner_id:
144
+ continue
145
+
146
+
147
+ whitelist_set = self.whitelist_cache.get(guild.id, set())
148
+ if executor.id in whitelist_set:
149
+ continue
150
+
151
+
152
+ guild_punishments = self.punishment_cache.get(guild.id, self.punishments)
153
+
154
+
155
+ action_map = {
156
+
157
+ discord.AuditLogAction.bot_add: "bot_add",
158
+
159
+
160
+ discord.AuditLogAction.channel_create: "channel_create",
161
+ discord.AuditLogAction.channel_delete: "channel_delete",
162
+ discord.AuditLogAction.channel_update: "channel_update",
163
+
164
+
165
+ discord.AuditLogAction.role_create: "role_create",
166
+ discord.AuditLogAction.role_delete: "role_delete",
167
+ discord.AuditLogAction.role_update: "role_update",
168
+
169
+
170
+ discord.AuditLogAction.kick: "member_kick",
171
+ discord.AuditLogAction.ban: "member_ban",
172
+ discord.AuditLogAction.unban: "member_unban",
173
+ discord.AuditLogAction.member_update: "member_update",
174
+
175
+
176
+ discord.AuditLogAction.webhook_create: "webhook_create",
177
+ discord.AuditLogAction.webhook_delete: "webhook_delete",
178
+ discord.AuditLogAction.webhook_update: "webhook_update",
179
+ }
180
+
181
+
182
+
183
+ violation_type = action_map.get(entry.action)
184
+ if not violation_type:
185
+ continue
186
+
187
+
188
+ punishment_action = guild_punishments.get(violation_type, "none")
189
+
190
+
191
+ punishment_result = "none"
192
+
193
+
194
+ if violation_type == "bot_add":
195
+ try:
196
+ bot_member = guild.get_member(entry.target.id)
197
+ if bot_member:
198
+ await bot_member.ban(reason="SecureX: Unauthorized bot addition")
199
+ punishment_result = "banned_bot"
200
+ print(f"🚀 INSTANT: Banned bot {entry.target.name} (added by {executor.name})")
201
+ except Exception as e:
202
+ print(f"Error banning bot: {e}")
203
+
204
+
205
+ if violation_type in ["member_kick", "member_ban", "member_unban","webhook_create"]:
206
+ try:
207
+ await guild.ban(executor, reason=f"SecureX: Unauthorized {violation_type}")
208
+ punishment_result = "banned_violator"
209
+ print(f"🔨 INSTANT: Banned {executor.name} for {violation_type}")
210
+ except Exception as e:
211
+ print(f"Error banning violator: {e}")
212
+
213
+ if punishment_action != "none":
214
+ try:
215
+ punishment_result = await self.punisher.punish(
216
+ guild=guild,
217
+ user=executor,
218
+ violation_type=violation_type,
219
+ details=f"{violation_type} on {getattr(entry.target, 'name', 'Unknown')}",
220
+ sdk=self
221
+ )
222
+ print(f"👢 {punishment_result.title()} {executor.name} for {violation_type}")
223
+ except Exception as e:
224
+ print(f"Cannot punish {executor.name}: {e}")
225
+
226
+
227
+
228
+ except Exception as e:
229
+ print(f"Error in action worker: {e}")
230
+
231
+ async def _cleanup_worker(self):
232
+ """Worker 1.5: Process audit log entries and delete unauthorized creations instantly"""
233
+ print("🧹 Cleanup Worker started")
234
+ while True:
235
+ try:
236
+
237
+ entry = await self.cleanup_queue.get()
238
+
239
+ guild = entry.guild
240
+ executor = entry.user
241
+
242
+
243
+ if executor.id == self.bot.user.id:
244
+ continue
245
+
246
+
247
+ if executor.id == guild.owner_id:
248
+ continue
249
+
250
+
251
+ whitelist_set = self.whitelist_cache.get(guild.id, set())
252
+ if executor.id in whitelist_set:
253
+ continue
254
+
255
+
256
+ if entry.action not in [
257
+ discord.AuditLogAction.channel_create,
258
+ discord.AuditLogAction.role_create,
259
+ discord.AuditLogAction.webhook_create
260
+ ]:
261
+ continue
262
+
263
+
264
+ try:
265
+ target = entry.target
266
+ if not target:
267
+ continue
268
+
269
+
270
+ if entry.action == discord.AuditLogAction.role_create:
271
+ role = guild.get_role(target.id)
272
+ if role:
273
+ await role.delete(reason="SecureX: Unauthorized role creation")
274
+ print(f"🗑️ [Worker] Deleted unauthorized role: {role.name}")
275
+
276
+ elif entry.action == discord.AuditLogAction.channel_create:
277
+ channel = guild.get_channel(target.id)
278
+ if channel:
279
+ await channel.delete(reason="SecureX: Unauthorized channel creation")
280
+ print(f"🗑️ [Worker] Deleted unauthorized channel: {channel.name}")
281
+
282
+ elif entry.action == discord.AuditLogAction.webhook_create:
283
+
284
+ webhooks = await guild.webhooks()
285
+ for webhook in webhooks:
286
+ if webhook.id == target.id:
287
+ await webhook.delete(reason="SecureX: Unauthorized webhook creation")
288
+ print(f"🗑️ [Worker] Deleted unauthorized webhook: {webhook.name}")
289
+ break
290
+
291
+
292
+ except (discord.Forbidden, discord.NotFound):
293
+ pass
294
+ except Exception as e:
295
+ print(f"Cleanup error: {e}")
296
+
297
+ except Exception as e:
298
+ print(f"Error in cleanup worker: {e}")
299
+
300
+ async def _log_worker(self):
301
+ """Worker 2: Write logs to DB/webhooks - completely independent"""
302
+ print("📝 Log Worker started")
303
+ while True:
304
+ try:
305
+
306
+ entry = await self.log_queue.get()
307
+
308
+ guild = entry.guild
309
+ executor = entry.user
310
+
311
+
312
+ if executor.id == self.bot.user.id or executor.id == guild.owner_id:
313
+ continue
314
+
315
+
316
+ action_map = {
317
+
318
+ discord.AuditLogAction.bot_add: "bot_add",
319
+
320
+
321
+ discord.AuditLogAction.channel_create: "channel_create",
322
+ discord.AuditLogAction.channel_delete: "channel_delete",
323
+ discord.AuditLogAction.channel_update: "channel_update",
324
+
325
+
326
+ discord.AuditLogAction.role_create: "role_create",
327
+ discord.AuditLogAction.role_delete: "role_delete",
328
+ discord.AuditLogAction.role_update: "role_update",
329
+
330
+
331
+ discord.AuditLogAction.kick: "member_kick",
332
+ discord.AuditLogAction.ban: "member_ban",
333
+ discord.AuditLogAction.unban: "member_unban",
334
+ discord.AuditLogAction.member_update: "member_update",
335
+
336
+
337
+ discord.AuditLogAction.webhook_create: "webhook_create",
338
+ discord.AuditLogAction.webhook_delete: "webhook_delete",
339
+ discord.AuditLogAction.webhook_update: "webhook_update",
340
+ }
341
+
342
+ violation_type = action_map.get(entry.action)
343
+ if not violation_type:
344
+ continue
345
+
346
+
347
+ threat = ThreatEvent(
348
+ type=violation_type,
349
+ guild_id=guild.id,
350
+ actor_id=executor.id,
351
+ target_id=entry.target.id if entry.target else None,
352
+ target_name=getattr(entry.target, 'name', 'Unknown'),
353
+ prevented=True,
354
+ restored=False,
355
+ punishment_action="logged"
356
+ )
357
+
358
+
359
+ await self._trigger_callbacks('threat_detected', threat)
360
+
361
+ except Exception as e:
362
+ print(f"Error in log worker: {e}")
363
+
364
+ def _register_events(self):
365
+ """Register Discord event listeners for restorations"""
366
+
367
+ @self.bot.event
368
+ async def on_guild_channel_delete(channel):
369
+ await self.channel_handler.on_channel_delete(channel)
370
+
371
+ @self.bot.event
372
+ async def on_guild_channel_update(before, after):
373
+ await self.channel_handler.on_channel_update(before, after)
374
+
375
+
376
+ @self.bot.event
377
+ async def on_guild_role_delete(role):
378
+ await self.role_handler.on_role_delete(role)
379
+
380
+ @self.bot.event
381
+ async def on_guild_role_update(before, after):
382
+ await self.role_handler.on_role_update(before, after)
383
+
384
+ @self.bot.event
385
+ async def on_member_ban(guild, user):
386
+ await self.member_handler.on_member_ban(guild, user)
387
+
388
+ @self.bot.event
389
+ async def on_member_update(before, after):
390
+ await self.member_handler.on_member_update(before, after)
391
+
392
+
393
+ def on(self, event_name: str):
394
+ """
395
+ Decorator to register callbacks for SDK events.
396
+
397
+ Usage:
398
+ @sx.on('threat_detected')
399
+ async def handle_threat(event: ThreatEvent):
400
+ print(f"Threat: {event.type}")
401
+ """
402
+ def decorator(func: Callable):
403
+ if event_name in self._callbacks:
404
+ self._callbacks[event_name].append(func)
405
+ return func
406
+ return decorator
407
+
408
+ @property
409
+ def on_threat_detected(self):
410
+ """Convenience decorator for threat_detected events"""
411
+ return self.on('threat_detected')
412
+
413
+ @property
414
+ def on_backup_completed(self):
415
+ """Convenience decorator for backup_completed events"""
416
+ return self.on('backup_completed')
417
+
418
+ @property
419
+ def on_restore_completed(self):
420
+ """Convenience decorator for restore_completed events"""
421
+ return self.on('restore_completed')
422
+
423
+ @property
424
+ def on_whitelist_changed(self):
425
+ """Convenience decorator for whitelist_changed events"""
426
+ return self.on('whitelist_changed')
427
+
428
+ async def _trigger_callbacks(self, event_name: str, *args, **kwargs):
429
+ """Trigger all registered callbacks for an event"""
430
+ if event_name in self._callbacks:
431
+ for callback in self._callbacks[event_name]:
432
+ try:
433
+ if asyncio.iscoroutinefunction(callback):
434
+ await callback(*args, **kwargs)
435
+ else:
436
+ callback(*args, **kwargs)
437
+ except Exception as e:
438
+ print(f"Error in callback for {event_name}: {e}")
439
+
440
+ async def enable(
441
+ self,
442
+ guild_id: Optional[int] = None,
443
+ whitelist: Optional[List[int]] = None,
444
+ auto_backup: bool = True,
445
+ punishments: Optional[Dict[str, str]] = None,
446
+ timeout_duration: int = 600,
447
+ notify_user: bool = True
448
+ ):
449
+ """
450
+ Enable SecureX protection.
451
+
452
+ Args:
453
+ guild_id: Guild ID to enable protection for (required if using whitelist)
454
+ whitelist: List of user IDs to whitelist (default: [])
455
+ auto_backup: Enable automatic backups every 30 minutes (default: True)
456
+ role_position_monitoring: Enable role position monitoring (default: True)
457
+ punishments: Dict mapping violation types to punishment actions
458
+ Example: {"channel_delete": "ban", "role_create": "kick"}
459
+ Available actions: "none", "warn", "timeout", "kick", "ban"
460
+ timeout_duration: Duration in seconds for timeout punishment (default: 600)
461
+ notify_user: Whether to DM violators about punishment (default: True)
462
+ """
463
+
464
+ await self.whitelist.preload_all()
465
+ await self.backup_manager.preload_all()
466
+
467
+
468
+ if guild_id:
469
+ whitelist_data = await self.whitelist.get_all(guild_id)
470
+ self.whitelist_cache[guild_id] = set(whitelist_data)
471
+ print(f"📋 Loaded {len(whitelist_data)} whitelisted users into cache")
472
+
473
+ if whitelist and guild_id:
474
+ for user_id in whitelist:
475
+ await self.whitelist.add(guild_id, user_id)
476
+
477
+ self.whitelist_cache[guild_id] = self.whitelist_cache.get(guild_id, set()) | set(whitelist)
478
+
479
+
480
+ if punishments:
481
+ self.punishments.update(punishments)
482
+
483
+ if guild_id:
484
+ self.punishment_cache[guild_id] = self.punishments.copy()
485
+ print(f"⚡ Punishment cache populated for guild {guild_id}")
486
+
487
+
488
+ self._worker_tasks.append(asyncio.create_task(self._action_worker()))
489
+ self._worker_tasks.append(asyncio.create_task(self._log_worker()))
490
+ self._worker_tasks.append(asyncio.create_task(self._cleanup_worker()))
491
+ print("🚀 Started 3 independent workers (Action, Log, Cleanup)")
492
+ print("⚡ Broadcast architecture - zero dependencies between workers")
493
+
494
+ if auto_backup:
495
+
496
+ self.backup_manager.start_auto_backup()
497
+
498
+ self.backup_manager.start_auto_refresh()
499
+
500
+ self.timeout_duration = timeout_duration
501
+ self.notify_punished_user = notify_user
502
+
503
+
504
+ if punishments:
505
+ enabled_punishments = {k: v for k, v in self.punishments.items() if v != "none"}
506
+ if enabled_punishments:
507
+ print(f"⚠️ Punishments configured for {len(enabled_punishments)} violation types")
508
+
509
+ print("✅ SecureX SDK v2.0 - Ultra-fast audit log system ACTIVE")
510
+ print("⚡ Response time: 5-10ms (100x faster than v1.x)")
511
+
512
+
513
+ if guild_id:
514
+ backup = await self.backup_manager.create_backup(guild_id)
515
+ if backup.success:
516
+ print(f"✅ Backup completed for guild {guild_id}: "
517
+ f"{backup.channel_count} channels, {backup.role_count} roles")
518
+
519
+
520
+ async def create_backup(self, guild_id: int) -> BackupInfo:
521
+ """
522
+ Create a backup for the specified guild.
523
+
524
+ Args:
525
+ guild_id: The guild ID to backup
526
+
527
+ Returns:
528
+ BackupInfo object with backup results
529
+ """
530
+ return await self.backup_manager.create_backup(guild_id)
531
+
532
+ async def restore_channel(self, guild: discord.Guild, channel: discord.abc.GuildChannel):
533
+ """
534
+ Restore a deleted channel from backup.
535
+
536
+ Args:
537
+ guild: The guild object
538
+ channel: The deleted channel object
539
+
540
+ Returns:
541
+ The newly created channel or None if restoration failed
542
+ """
543
+ return await self.backup_manager.restore_channel(guild, channel)
544
+
545
+ async def restore_role(self, guild: discord.Guild, role_id: int) -> bool:
546
+ """
547
+ Restore a deleted role from backup.
548
+
549
+ Args:
550
+ guild: The guild object
551
+ role_id: The ID of the deleted role
552
+
553
+ Returns:
554
+ True if successful, False otherwise
555
+ """
556
+ return await self.backup_manager.restore_role(guild, role_id)
@@ -0,0 +1,8 @@
1
+ """Handlers package - contains protection logic"""
2
+
3
+ from .channel import ChannelHandler
4
+ from .role import RoleHandler
5
+ from .member import MemberHandler
6
+ from .webhook import WebhookHandler
7
+
8
+ __all__ = ['ChannelHandler', 'RoleHandler', 'MemberHandler', 'WebhookHandler']
@@ -0,0 +1,97 @@
1
+ """
2
+ Channel protection handler for SecureX SDK.
3
+ """
4
+ import discord
5
+ import asyncio
6
+ from datetime import datetime, timedelta
7
+
8
+
9
+ class ChannelHandler:
10
+ """Handles channel protection logic"""
11
+
12
+ def __init__(self, sdk):
13
+ self.sdk = sdk
14
+ self.bot = sdk.bot
15
+ self.backup_manager = sdk.backup_manager
16
+ self.whitelist = sdk.whitelist
17
+
18
+ async def _is_authorized(self, guild: discord.Guild, user_id: int) -> bool:
19
+ """Check if user is authorized (owner or whitelisted)"""
20
+ if guild.owner_id == user_id:
21
+ return True
22
+ if user_id == self.bot.user.id:
23
+ return True
24
+ return await self.whitelist.is_whitelisted(guild.id, user_id)
25
+
26
+
27
+ async def on_channel_delete(self, channel: discord.abc.GuildChannel):
28
+ """Restore unauthorized channel deletions"""
29
+ try:
30
+ guild = channel.guild
31
+ print(f"🔍 [Debug] Channel deleted: {channel.name} ({channel.id})")
32
+ await asyncio.sleep(1)
33
+
34
+
35
+ from datetime import timezone
36
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
37
+
38
+ found_entry = False
39
+ async for entry in guild.audit_logs(limit=50, action=discord.AuditLogAction.channel_delete):
40
+ if entry.created_at < cutoff_time:
41
+ break
42
+
43
+ if entry.target.id == channel.id:
44
+ found_entry = True
45
+ authorized = await self._is_authorized(guild, entry.user.id)
46
+ print(f"👤 [Debug] Executor: {entry.user.name} ({entry.user.id}) | Authorized: {authorized}")
47
+
48
+ if not authorized:
49
+ print(f"🛠️ [Debug] Unauthorized deletion detected. Triggering restoration...")
50
+
51
+ new_channel = await self.backup_manager.restore_channel(guild, channel)
52
+ if new_channel:
53
+ print(f"🔄 Restored unauthorized channel deletion: {channel.name}")
54
+
55
+
56
+ if new_channel and isinstance(channel, discord.CategoryChannel):
57
+ await self.backup_manager.restore_category_children(
58
+ guild,
59
+ channel.id,
60
+ new_channel
61
+ )
62
+ else:
63
+ print(f"✅ [Debug] Deletion by authorized user. Skipping restoration.")
64
+ break
65
+
66
+ if not found_entry:
67
+ print(f"⚠️ [Debug] Could not find audit log entry for channel deletion of {channel.name}")
68
+
69
+ except Exception as e:
70
+ print(f"❌ [Debug] Error in on_channel_delete: {e}")
71
+
72
+ async def on_channel_update(self, before: discord.abc.GuildChannel, after: discord.abc.GuildChannel):
73
+ """Restore unauthorized channel permission changes"""
74
+ try:
75
+
76
+ if (before.overwrites == after.overwrites and
77
+ before.position == after.position):
78
+ return
79
+
80
+ guild = after.guild
81
+ await asyncio.sleep(0.5)
82
+
83
+
84
+ from datetime import timezone
85
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
86
+
87
+ async for entry in guild.audit_logs(limit=50, oldest_first=False):
88
+ if entry.created_at < cutoff_time:
89
+ break
90
+
91
+ if entry.action == discord.AuditLogAction.channel_update:
92
+ if entry.target and entry.target.id == after.id:
93
+ if not await self._is_authorized(guild, entry.user.id):
94
+ await self.backup_manager.restore_channel_permissions(guild, after.id)
95
+ break
96
+ except Exception:
97
+ pass