dc-securex 2.29.7__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.
securex/client.py ADDED
@@ -0,0 +1,329 @@
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
+ from .workers import ActionWorker, CleanupWorker, LogWorker, GuildWorker
17
+
18
+
19
+ class SecureX:
20
+ """
21
+ Main SecureX SDK class for anti-nuke protection.
22
+ Backend-only - no UI, no commands, just pure protection logic.
23
+ """
24
+
25
+ def __init__(
26
+ self,
27
+ bot: discord.Client,
28
+ backup_dir: str = "./data/backups",
29
+ punishments: Optional[Dict[str, str]] = None,
30
+ timeout_duration: int = 600,
31
+ notify_user: bool = True
32
+ ):
33
+ """
34
+ Initialize SecureX SDK.
35
+
36
+ Args:
37
+ bot: Your discord.Client or commands.Bot instance
38
+ backup_dir: Directory to store backups (default: ./data/backups)
39
+ punishments: Dict mapping violation types to punishment actions
40
+ timeout_duration: Duration in seconds for timeout punishment (default: 600)
41
+ notify_user: Whether to DM violators about punishment (default: True)
42
+ """
43
+ self.bot = bot
44
+ self.backup_dir = Path(backup_dir)
45
+ self.backup_dir.mkdir(parents=True, exist_ok=True)
46
+
47
+
48
+ self.backup_manager = BackupManager(self)
49
+ self.whitelist = WhitelistManager(self)
50
+
51
+
52
+ self._callbacks: Dict[str, List[Callable]] = {
53
+ 'threat_detected': [],
54
+ 'backup_completed': [],
55
+ 'restore_completed': [],
56
+ 'whitelist_changed': [],
57
+ }
58
+
59
+
60
+ self.punishments = {
61
+
62
+ "bot_add": "none",
63
+
64
+
65
+ "channel_create": "none",
66
+ "channel_delete": "none",
67
+ "channel_update": "none",
68
+
69
+
70
+ "role_create": "none",
71
+ "role_delete": "none",
72
+ "role_update": "none",
73
+
74
+
75
+ "member_ban": "none",
76
+ "member_kick": "none",
77
+ "member_unban": "none",
78
+ "member_update": "none",
79
+
80
+
81
+ "webhook_create": "none",
82
+ "webhook_delete": "none",
83
+ "webhook_update": "none",
84
+
85
+ "guild_update": "none"
86
+ }
87
+
88
+
89
+ self.punisher = PunishmentExecutor(bot)
90
+ self.timeout_duration = timeout_duration
91
+ self.notify_punished_user = notify_user
92
+
93
+
94
+ self.action_queue = asyncio.Queue(maxsize=1000)
95
+ self.log_queue = asyncio.Queue(maxsize=1000)
96
+ self.cleanup_queue = asyncio.Queue(maxsize=1000)
97
+ self.guild_queue = asyncio.Queue(maxsize=1000)
98
+
99
+
100
+ self.whitelist_cache = {}
101
+ self.punishment_cache = {}
102
+
103
+
104
+ self._worker_tasks = []
105
+
106
+
107
+ self.channel_handler = ChannelHandler(self)
108
+ self.role_handler = RoleHandler(self)
109
+ self.member_handler = MemberHandler(self)
110
+
111
+ # Initialize workers
112
+ self.action_worker = ActionWorker(self)
113
+ self.cleanup_worker = CleanupWorker(self)
114
+ self.log_worker = LogWorker(self)
115
+ self.guild_worker = GuildWorker(self)
116
+
117
+ self._register_audit_log_listener()
118
+
119
+
120
+ self._register_events()
121
+
122
+ def _register_audit_log_listener(self):
123
+ """Register INSTANT audit log event listener (v2.0 - 5-10ms response)"""
124
+ @self.bot.event
125
+ async def on_audit_log_entry_create(entry: discord.AuditLogEntry):
126
+
127
+ try:
128
+
129
+ await self.action_queue.put(entry)
130
+ await self.log_queue.put(entry)
131
+ await self.cleanup_queue.put(entry)
132
+ await self.guild_queue.put(entry)
133
+ except asyncio.QueueFull as e:
134
+ print(f"⚠️ Queue full: {e}")
135
+
136
+ def _register_events(self):
137
+ """Register Discord event listeners for restorations"""
138
+
139
+ @self.bot.event
140
+ async def on_guild_channel_delete(channel):
141
+ await self.channel_handler.on_channel_delete(channel)
142
+
143
+ @self.bot.event
144
+ async def on_guild_channel_update(before, after):
145
+ await self.channel_handler.on_channel_update(before, after)
146
+
147
+
148
+ @self.bot.event
149
+ async def on_guild_role_delete(role):
150
+ await self.role_handler.on_role_delete(role)
151
+
152
+ @self.bot.event
153
+ async def on_guild_role_update(before, after):
154
+ await self.role_handler.on_role_update(before, after)
155
+
156
+ @self.bot.event
157
+ async def on_member_ban(guild, user):
158
+ await self.member_handler.on_member_ban(guild, user)
159
+
160
+ @self.bot.event
161
+ async def on_member_update(before, after):
162
+ await self.member_handler.on_member_update(before, after)
163
+
164
+
165
+ def on(self, event_name: str):
166
+ """
167
+ Decorator to register callbacks for SDK events.
168
+
169
+ Usage:
170
+ @sx.on('threat_detected')
171
+ async def handle_threat(event: ThreatEvent):
172
+ print(f"Threat: {event.type}")
173
+ """
174
+ def decorator(func: Callable):
175
+ if event_name in self._callbacks:
176
+ self._callbacks[event_name].append(func)
177
+ return func
178
+ return decorator
179
+
180
+ @property
181
+ def on_threat_detected(self):
182
+ """Convenience decorator for threat_detected events"""
183
+ return self.on('threat_detected')
184
+
185
+ @property
186
+ def on_backup_completed(self):
187
+ """Convenience decorator for backup_completed events"""
188
+ return self.on('backup_completed')
189
+
190
+ @property
191
+ def on_restore_completed(self):
192
+ """Convenience decorator for restore_completed events"""
193
+ return self.on('restore_completed')
194
+
195
+ @property
196
+ def on_whitelist_changed(self):
197
+ """Convenience decorator for whitelist_changed events"""
198
+ return self.on('whitelist_changed')
199
+
200
+ async def _trigger_callbacks(self, event_name: str, *args, **kwargs):
201
+ """Trigger all registered callbacks for an event"""
202
+ if event_name in self._callbacks:
203
+ for callback in self._callbacks[event_name]:
204
+ try:
205
+ if asyncio.iscoroutinefunction(callback):
206
+ await callback(*args, **kwargs)
207
+ else:
208
+ callback(*args, **kwargs)
209
+ except Exception as e:
210
+ print(f"Error in callback for {event_name}: {e}")
211
+
212
+ async def enable(
213
+ self,
214
+ guild_id: Optional[int] = None,
215
+ whitelist: Optional[List[int]] = None,
216
+ auto_backup: bool = True,
217
+ punishments: Optional[Dict[str, str]] = None,
218
+ timeout_duration: int = 600,
219
+ notify_user: bool = True
220
+ ):
221
+ """
222
+ Enable SecureX protection.
223
+
224
+ Args:
225
+ guild_id: Guild ID to enable protection for (required if using whitelist)
226
+ whitelist: List of user IDs to whitelist (default: [])
227
+ auto_backup: Enable automatic backups every 30 minutes (default: True)
228
+ role_position_monitoring: Enable role position monitoring (default: True)
229
+ punishments: Dict mapping violation types to punishment actions
230
+ Example: {"channel_delete": "ban", "role_create": "kick"}
231
+ Available actions: "none", "warn", "timeout", "kick", "ban"
232
+ timeout_duration: Duration in seconds for timeout punishment (default: 600)
233
+ notify_user: Whether to DM violators about punishment (default: True)
234
+ """
235
+
236
+ await self.whitelist.preload_all()
237
+ await self.backup_manager.preload_all()
238
+
239
+
240
+ if guild_id:
241
+ whitelist_data = await self.whitelist.get_all(guild_id)
242
+ self.whitelist_cache[guild_id] = set(whitelist_data)
243
+ print(f"📋 Loaded {len(whitelist_data)} whitelisted users into cache")
244
+
245
+ if whitelist and guild_id:
246
+ for user_id in whitelist:
247
+ await self.whitelist.add(guild_id, user_id)
248
+
249
+ self.whitelist_cache[guild_id] = self.whitelist_cache.get(guild_id, set()) | set(whitelist)
250
+
251
+
252
+ if punishments:
253
+ self.punishments.update(punishments)
254
+
255
+ if guild_id:
256
+ self.punishment_cache[guild_id] = self.punishments.copy()
257
+ print(f"⚡ Punishment cache populated for guild {guild_id}")
258
+
259
+
260
+ await self.action_worker.start()
261
+ await self.cleanup_worker.start()
262
+ await self.log_worker.start()
263
+ await self.guild_worker.start()
264
+ print("🚀 Started 4 independent workers (Action, Cleanup, Log, Guild)")
265
+ print("⚡ Broadcast architecture - zero dependencies between workers")
266
+
267
+ if auto_backup:
268
+
269
+ self.backup_manager.start_auto_backup()
270
+
271
+ self.backup_manager.start_auto_refresh()
272
+
273
+ self.timeout_duration = timeout_duration
274
+ self.notify_punished_user = notify_user
275
+
276
+
277
+ if punishments:
278
+ enabled_punishments = {k: v for k, v in self.punishments.items() if v != "none"}
279
+ if enabled_punishments:
280
+ print(f"⚠️ Punishments configured for {len(enabled_punishments)} violation types")
281
+
282
+ print("✅ SecureX SDK v2.0 - Ultra-fast audit log system ACTIVE")
283
+ print("⚡ Response time: 5-10ms (100x faster than v1.x)")
284
+
285
+
286
+ if guild_id:
287
+ backup = await self.backup_manager.create_backup(guild_id)
288
+ if backup.success:
289
+ print(f"✅ Backup completed for guild {guild_id}: "
290
+ f"{backup.channel_count} channels, {backup.role_count} roles")
291
+
292
+
293
+ async def create_backup(self, guild_id: int) -> BackupInfo:
294
+ """
295
+ Create a backup for the specified guild.
296
+
297
+ Args:
298
+ guild_id: The guild ID to backup
299
+
300
+ Returns:
301
+ BackupInfo object with backup results
302
+ """
303
+ return await self.backup_manager.create_backup(guild_id)
304
+
305
+ async def restore_channel(self, guild: discord.Guild, channel: discord.abc.GuildChannel):
306
+ """
307
+ Restore a deleted channel from backup.
308
+
309
+ Args:
310
+ guild: The guild object
311
+ channel: The deleted channel object
312
+
313
+ Returns:
314
+ The newly created channel or None if restoration failed
315
+ """
316
+ return await self.backup_manager.restore_channel(guild, channel)
317
+
318
+ async def restore_role(self, guild: discord.Guild, role_id: int) -> bool:
319
+ """
320
+ Restore a deleted role from backup.
321
+
322
+ Args:
323
+ guild: The guild object
324
+ role_id: The ID of the deleted role
325
+
326
+ Returns:
327
+ True if successful, False otherwise
328
+ """
329
+ 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
+
7
+
8
+ __all__ = ['ChannelHandler', 'RoleHandler', 'MemberHandler']
@@ -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
@@ -0,0 +1,110 @@
1
+ """
2
+ Member protection handler (bot/ban/kick protection).
3
+ """
4
+ import discord
5
+ import asyncio
6
+
7
+
8
+ class MemberHandler:
9
+ """Handles member-related protection"""
10
+
11
+
12
+ DANGEROUS_PERMISSIONS = frozenset([
13
+ 'administrator',
14
+ 'kick_members',
15
+ 'ban_members',
16
+ 'manage_guild',
17
+ 'manage_roles',
18
+ 'manage_channels',
19
+ 'manage_webhooks',
20
+ 'manage_emojis',
21
+ 'mention_everyone',
22
+ 'manage_expressions'
23
+ ])
24
+
25
+ def __init__(self, sdk):
26
+ self.sdk = sdk
27
+ self.bot = sdk.bot
28
+ self.whitelist = sdk.whitelist
29
+
30
+ async def _is_authorized(self, guild: discord.Guild, user_id: int) -> bool:
31
+ """Check if user is authorized"""
32
+ if guild.owner_id == user_id:
33
+ return True
34
+ if user_id == self.bot.user.id:
35
+ return True
36
+ return await self.whitelist.is_whitelisted(guild.id, user_id)
37
+
38
+ async def on_member_ban(self, guild: discord.Guild, user: discord.User):
39
+ """Restore banned victims (punishment handled by action worker)"""
40
+ try:
41
+ await asyncio.sleep(0.5)
42
+
43
+
44
+ from datetime import datetime, timezone, timedelta
45
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
46
+
47
+ async for entry in guild.audit_logs(limit=50, action=discord.AuditLogAction.ban):
48
+ if entry.created_at < cutoff_time:
49
+ break
50
+
51
+ if entry.target.id == user.id:
52
+ if not await self._is_authorized(guild, entry.user.id):
53
+
54
+ try:
55
+ await guild.unban(user, reason="SecureX: Restoring banned member")
56
+ print(f"🔄 Unbanned unauthorized member ban: {user.name}")
57
+ except (discord.errors.Forbidden, discord.errors.HTTPException):
58
+ pass
59
+ break
60
+ except Exception as e:
61
+ print(f"Error in on_member_ban: {e}")
62
+
63
+ async def on_member_update(self, before: discord.Member, after: discord.Member):
64
+ """Check for dangerous permissions and remove unauthorized roles"""
65
+ try:
66
+
67
+ if before.roles == after.roles:
68
+ return
69
+
70
+ guild = after.guild
71
+ await asyncio.sleep(0.5)
72
+
73
+
74
+ from datetime import datetime, timezone, timedelta
75
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
76
+
77
+ async for entry in guild.audit_logs(limit=50, action=discord.AuditLogAction.member_role_update):
78
+ if entry.created_at < cutoff_time:
79
+ break
80
+ if entry.target.id == after.id:
81
+
82
+ is_authorized = await self._is_authorized(guild, entry.user.id)
83
+
84
+ if not is_authorized:
85
+
86
+ dangerous_roles = []
87
+
88
+ for role in after.roles:
89
+ if role.is_default():
90
+ continue
91
+
92
+
93
+ permissions = role.permissions
94
+ for perm_name in self.DANGEROUS_PERMISSIONS:
95
+ if getattr(permissions, perm_name, False):
96
+ dangerous_roles.append(role)
97
+ break
98
+
99
+
100
+ if dangerous_roles:
101
+ try:
102
+ await after.remove_roles(*dangerous_roles, reason="SecureX: Removed dangerous roles (unauthorized update)")
103
+ role_names = ", ".join([r.name for r in dangerous_roles])
104
+ print(f"🛡️ Bulk removed {len(dangerous_roles)} dangerous role(s) from {after.name}: {role_names}")
105
+ except (discord.errors.Forbidden, discord.errors.HTTPException) as e:
106
+ print(f"⚠️ Failed to remove roles: {e}")
107
+
108
+ break
109
+ except Exception as e:
110
+ print(f"Error in on_member_update: {e}")
@@ -0,0 +1,74 @@
1
+ """
2
+ Role protection handler for SecureX SDK.
3
+ """
4
+ import discord
5
+ import asyncio
6
+ from datetime import datetime, timedelta
7
+
8
+
9
+ class RoleHandler:
10
+ """Handles role 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"""
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_role_delete(self, role: discord.Role):
28
+ """Restore unauthorized role deletions"""
29
+ try:
30
+ guild = role.guild
31
+ await asyncio.sleep(1)
32
+
33
+
34
+ from datetime import timezone
35
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
36
+
37
+ async for entry in guild.audit_logs(limit=50, action=discord.AuditLogAction.role_delete):
38
+ if entry.created_at < cutoff_time:
39
+ break
40
+
41
+ if entry.target.id == role.id:
42
+ if not await self._is_authorized(guild, entry.user.id):
43
+ if await self.backup_manager.restore_role(guild, role.id):
44
+ print(f"🔄 Restored unauthorized role deletion: {role.name}")
45
+ break
46
+ except Exception:
47
+ pass
48
+
49
+ async def on_role_update(self, before: discord.Role, after: discord.Role):
50
+ """Restore unauthorized role permission changes"""
51
+ try:
52
+
53
+ if before.position == after.position and before.permissions == after.permissions:
54
+ return
55
+
56
+ guild = after.guild
57
+ await asyncio.sleep(0.3)
58
+
59
+
60
+ from datetime import timezone
61
+ cutoff_time = datetime.now(timezone.utc) - timedelta(seconds=30)
62
+
63
+ async for entry in guild.audit_logs(limit=50, oldest_first=False):
64
+ if entry.created_at < cutoff_time:
65
+ break
66
+
67
+ if entry.action == discord.AuditLogAction.role_update:
68
+ if entry.target.id == after.id:
69
+ if not await self._is_authorized(guild, entry.user.id):
70
+ if await self.backup_manager.restore_role_permissions(guild, after.id):
71
+ print(f"🔄 Restored unauthorized role update: {after.name}")
72
+ break
73
+ except Exception:
74
+ pass