ManagerX-DevTools 1.0.0__tar.gz

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.
Files changed (25) hide show
  1. managerx_devtools-1.0.0/DevTools/__init__.py +1 -0
  2. managerx_devtools-1.0.0/DevTools/backend/__init__.py +1 -0
  3. managerx_devtools-1.0.0/DevTools/backend/database/__init__.py +12 -0
  4. managerx_devtools-1.0.0/DevTools/backend/database/antispam_db.py +494 -0
  5. managerx_devtools-1.0.0/DevTools/backend/database/autodelete_db.py +843 -0
  6. managerx_devtools-1.0.0/DevTools/backend/database/autorole_db.py +101 -0
  7. managerx_devtools-1.0.0/DevTools/backend/database/globalchat_db.py +955 -0
  8. managerx_devtools-1.0.0/DevTools/backend/database/levelsystem_db.py +758 -0
  9. managerx_devtools-1.0.0/DevTools/backend/database/logging_db.py +413 -0
  10. managerx_devtools-1.0.0/DevTools/backend/database/notes_db.py +55 -0
  11. managerx_devtools-1.0.0/DevTools/backend/database/settings_db.py +46 -0
  12. managerx_devtools-1.0.0/DevTools/backend/database/stats_db.py +476 -0
  13. managerx_devtools-1.0.0/DevTools/backend/database/vc_db.py +176 -0
  14. managerx_devtools-1.0.0/DevTools/backend/database/warn_db.py +121 -0
  15. managerx_devtools-1.0.0/DevTools/backend/database/welcome_db.py +552 -0
  16. managerx_devtools-1.0.0/LICENSE +675 -0
  17. managerx_devtools-1.0.0/ManagerX_DevTools.egg-info/PKG-INFO +32 -0
  18. managerx_devtools-1.0.0/ManagerX_DevTools.egg-info/SOURCES.txt +23 -0
  19. managerx_devtools-1.0.0/ManagerX_DevTools.egg-info/dependency_links.txt +1 -0
  20. managerx_devtools-1.0.0/ManagerX_DevTools.egg-info/requires.txt +6 -0
  21. managerx_devtools-1.0.0/ManagerX_DevTools.egg-info/top_level.txt +1 -0
  22. managerx_devtools-1.0.0/PKG-INFO +32 -0
  23. managerx_devtools-1.0.0/README.md +1 -0
  24. managerx_devtools-1.0.0/pyproject.toml +42 -0
  25. managerx_devtools-1.0.0/setup.cfg +4 -0
@@ -0,0 +1 @@
1
+ from .backend import *
@@ -0,0 +1 @@
1
+ from .database import *
@@ -0,0 +1,12 @@
1
+ from .antispam_db import SpamDB as AntiSpamDatabase
2
+ from .autodelete_db import AutoDeleteDB
3
+ from .autorole_db import AutoRoleDatabase
4
+ from .globalchat_db import GlobalChatDatabase
5
+ from .settings_db import SettingsDB
6
+ from .levelsystem_db import LevelDatabase
7
+ from .logging_db import LoggingDatabase
8
+ from .notes_db import NotesDatabase
9
+ from .stats_db import StatsDB
10
+ from .vc_db import TempVCDatabase
11
+ from .warn_db import WarnDatabase
12
+ from .welcome_db import WelcomeDatabase
@@ -0,0 +1,494 @@
1
+ # Copyright (c) 2025 OPPRO.NET Network
2
+ import sqlite3
3
+ import os
4
+ import logging
5
+ from datetime import datetime, timedelta
6
+ from typing import Optional, Dict, List, Tuple
7
+ from contextlib import contextmanager
8
+
9
+ from colorama import Fore, Style
10
+
11
+
12
+ class SpamDBError(Exception):
13
+ """Custom exception for SpamDB errors"""
14
+ pass
15
+
16
+
17
+ class SpamDB:
18
+ def __init__(self, db_path='data/spam.db'):
19
+ """Initialize spam database with enhanced error handling and logging."""
20
+ self.db_path = db_path
21
+ self.logger = logging.getLogger(__name__)
22
+
23
+ try:
24
+ # Ensure data directory exists
25
+ os.makedirs(os.path.dirname(self.db_path), exist_ok=True)
26
+ self.conn = sqlite3.connect(self.db_path, check_same_thread=False)
27
+ self.conn.row_factory = sqlite3.Row # Enable dict-like access
28
+ self.create_tables()
29
+ self._migrate_database() # Add migration step
30
+ self._init_database()
31
+ except Exception as e:
32
+ self.logger.error(f"Failed to initialize database: {e}")
33
+ raise SpamDBError(f"Database initialization failed: {e}")
34
+
35
+ @contextmanager
36
+ def get_cursor(self):
37
+ """Context manager for database operations with proper error handling."""
38
+ cursor = self.conn.cursor()
39
+ try:
40
+ yield cursor
41
+ except sqlite3.Error as e:
42
+ self.conn.rollback()
43
+ self.logger.error(f"Database error: {e}")
44
+ raise SpamDBError(f"Database operation failed: {e}")
45
+ finally:
46
+ cursor.close()
47
+
48
+ def _get_table_columns(self, table_name: str) -> List[str]:
49
+ """Get list of columns for a table."""
50
+ with self.get_cursor() as cursor:
51
+ cursor.execute(f"PRAGMA table_info({table_name})")
52
+ return [row[1] for row in cursor.fetchall()]
53
+
54
+ def _migrate_database(self):
55
+ """Handle database migrations for schema changes."""
56
+ try:
57
+ tables = [table[0] for table in self._get_tables()]
58
+
59
+ # Migrate spam_settings table
60
+ if 'spam_settings' in tables:
61
+ spam_settings_columns = self._get_table_columns('spam_settings')
62
+ if 'created_at' not in spam_settings_columns:
63
+ with self.get_cursor() as cursor:
64
+ cursor.execute(
65
+ 'ALTER TABLE spam_settings ADD COLUMN created_at DATETIME DEFAULT CURRENT_TIMESTAMP')
66
+
67
+ if 'updated_at' not in spam_settings_columns:
68
+ with self.get_cursor() as cursor:
69
+ cursor.execute(
70
+ 'ALTER TABLE spam_settings ADD COLUMN updated_at DATETIME DEFAULT CURRENT_TIMESTAMP')
71
+
72
+ # Migrate spam_logs table
73
+ if 'spam_logs' in tables:
74
+ spam_logs_columns = self._get_table_columns('spam_logs')
75
+ if 'message_count' not in spam_logs_columns:
76
+ with self.get_cursor() as cursor:
77
+ cursor.execute('ALTER TABLE spam_logs ADD COLUMN message_count INTEGER DEFAULT 1')
78
+
79
+ # Migrate spam_whitelist table
80
+ if 'spam_whitelist' in tables:
81
+ whitelist_columns = self._get_table_columns('spam_whitelist')
82
+ if 'added_by' not in whitelist_columns:
83
+ with self.get_cursor() as cursor:
84
+ cursor.execute('ALTER TABLE spam_whitelist ADD COLUMN added_by INTEGER')
85
+
86
+ if 'added_at' not in whitelist_columns:
87
+ with self.get_cursor() as cursor:
88
+ cursor.execute(
89
+ 'ALTER TABLE spam_whitelist ADD COLUMN added_at DATETIME DEFAULT CURRENT_TIMESTAMP')
90
+
91
+ if 'reason' not in whitelist_columns:
92
+ with self.get_cursor() as cursor:
93
+ cursor.execute('ALTER TABLE spam_whitelist ADD COLUMN reason TEXT')
94
+
95
+ self.conn.commit()
96
+ self.logger.info("Database migration completed successfully")
97
+
98
+ except Exception as e:
99
+ self.logger.error(f"Database migration failed: {e}")
100
+ # Don't raise here - let the application continue with basic functionality
101
+
102
+ def _get_tables(self) -> List[Tuple]:
103
+ """Get list of all tables in the database."""
104
+ with self.get_cursor() as cursor:
105
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
106
+ return cursor.fetchall()
107
+
108
+ def create_tables(self):
109
+ """Create all necessary tables with improved schema."""
110
+ with self.get_cursor() as cursor:
111
+ # Spam settings table with better constraints
112
+ cursor.execute('''
113
+ CREATE TABLE IF NOT EXISTS spam_settings (
114
+ guild_id INTEGER PRIMARY KEY,
115
+ max_messages INTEGER DEFAULT 5 CHECK (max_messages > 0),
116
+ time_frame INTEGER DEFAULT 10 CHECK (time_frame > 0),
117
+ log_channel_id INTEGER
118
+ )
119
+ ''')
120
+
121
+ # Spam logs with better indexing
122
+ cursor.execute('''
123
+ CREATE TABLE IF NOT EXISTS spam_logs (
124
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
125
+ guild_id INTEGER NOT NULL,
126
+ user_id INTEGER NOT NULL,
127
+ message TEXT NOT NULL,
128
+ message_count INTEGER DEFAULT 1,
129
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
130
+ FOREIGN KEY (guild_id) REFERENCES spam_settings(guild_id)
131
+ )
132
+ ''')
133
+
134
+ # Whitelist with better constraints
135
+ cursor.execute('''
136
+ CREATE TABLE IF NOT EXISTS spam_whitelist (
137
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
138
+ guild_id INTEGER NOT NULL,
139
+ user_id INTEGER NOT NULL,
140
+ UNIQUE(guild_id, user_id)
141
+ )
142
+ ''')
143
+
144
+ # Create indexes for better performance
145
+ cursor.execute('''
146
+ CREATE INDEX IF NOT EXISTS idx_spam_logs_guild_timestamp
147
+ ON spam_logs(guild_id, timestamp)
148
+ ''')
149
+
150
+ cursor.execute('''
151
+ CREATE INDEX IF NOT EXISTS idx_spam_logs_user_timestamp
152
+ ON spam_logs(user_id, timestamp)
153
+ ''')
154
+
155
+ cursor.execute('''
156
+ CREATE INDEX IF NOT EXISTS idx_whitelist_guild_user
157
+ ON spam_whitelist(guild_id, user_id)
158
+ ''')
159
+
160
+ self.conn.commit()
161
+
162
+ def _init_database(self):
163
+ """Initialize database with any required default data."""
164
+ # Add any initialization logic here if needed
165
+ pass
166
+
167
+ def set_spam_settings(self, guild_id: int, max_messages: int = 5,
168
+ time_frame: int = 10, log_channel_id: Optional[int] = None) -> bool:
169
+ """Set spam detection settings for a guild with validation."""
170
+ if max_messages <= 0 or time_frame <= 0:
171
+ raise SpamDBError("max_messages and time_frame must be positive integers")
172
+
173
+ with self.get_cursor() as cursor:
174
+ # Check if updated_at column exists
175
+ columns = self._get_table_columns('spam_settings')
176
+
177
+ if 'updated_at' in columns:
178
+ cursor.execute('''
179
+ INSERT OR REPLACE INTO spam_settings
180
+ (guild_id, max_messages, time_frame, log_channel_id, updated_at)
181
+ VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
182
+ ''', (guild_id, max_messages, time_frame, log_channel_id))
183
+ else:
184
+ # Fallback for tables without updated_at column
185
+ cursor.execute('''
186
+ INSERT OR REPLACE INTO spam_settings
187
+ (guild_id, max_messages, time_frame, log_channel_id)
188
+ VALUES (?, ?, ?, ?)
189
+ ''', (guild_id, max_messages, time_frame, log_channel_id))
190
+
191
+ self.conn.commit()
192
+ return True
193
+
194
+ def set_log_channel(self, guild_id: int, channel_id: int) -> bool:
195
+ """Set the log channel for a guild."""
196
+ with self.get_cursor() as cursor:
197
+ # Get current settings or use defaults
198
+ cursor.execute('SELECT max_messages, time_frame FROM spam_settings WHERE guild_id = ?',
199
+ (guild_id,))
200
+ result = cursor.fetchone()
201
+
202
+ if result:
203
+ max_messages, time_frame = result['max_messages'], result['time_frame']
204
+ else:
205
+ max_messages, time_frame = 5, 10 # Default values
206
+
207
+ # Check if updated_at column exists
208
+ columns = self._get_table_columns('spam_settings')
209
+
210
+ if 'updated_at' in columns:
211
+ cursor.execute('''
212
+ INSERT OR REPLACE INTO spam_settings
213
+ (guild_id, max_messages, time_frame, log_channel_id, updated_at)
214
+ VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)
215
+ ''', (guild_id, max_messages, time_frame, channel_id))
216
+ else:
217
+ cursor.execute('''
218
+ INSERT OR REPLACE INTO spam_settings
219
+ (guild_id, max_messages, time_frame, log_channel_id)
220
+ VALUES (?, ?, ?, ?)
221
+ ''', (guild_id, max_messages, time_frame, channel_id))
222
+
223
+ self.conn.commit()
224
+ return True
225
+
226
+ def get_spam_settings(self, guild_id: int) -> Optional[Dict]:
227
+ """Get spam settings for a guild."""
228
+ with self.get_cursor() as cursor:
229
+ columns = self._get_table_columns('spam_settings')
230
+
231
+ # Build query based on available columns
232
+ select_columns = ['max_messages', 'time_frame', 'log_channel_id']
233
+ if 'created_at' in columns:
234
+ select_columns.append('created_at')
235
+ if 'updated_at' in columns:
236
+ select_columns.append('updated_at')
237
+
238
+ query = f"SELECT {', '.join(select_columns)} FROM spam_settings WHERE guild_id = ?"
239
+ cursor.execute(query, (guild_id,))
240
+ result = cursor.fetchone()
241
+
242
+ if result:
243
+ settings = {
244
+ 'max_messages': result['max_messages'],
245
+ 'time_frame': result['time_frame'],
246
+ 'log_channel_id': result['log_channel_id']
247
+ }
248
+
249
+ if 'created_at' in columns:
250
+ settings['created_at'] = result.get('created_at')
251
+ if 'updated_at' in columns:
252
+ settings['updated_at'] = result.get('updated_at')
253
+
254
+ return settings
255
+ return None
256
+
257
+ def get_log_channel(self, guild_id: int) -> Optional[int]:
258
+ """Get the log channel ID for a guild."""
259
+ with self.get_cursor() as cursor:
260
+ cursor.execute('''
261
+ SELECT log_channel_id FROM spam_settings WHERE guild_id = ?
262
+ ''', (guild_id,))
263
+ result = cursor.fetchone()
264
+ return result['log_channel_id'] if result and result['log_channel_id'] else None
265
+
266
+ def log_spam(self, guild_id: int, user_id: int, message: str, message_count: int = 1) -> bool:
267
+ """Log a spam incident with message count."""
268
+ with self.get_cursor() as cursor:
269
+ columns = self._get_table_columns('spam_logs')
270
+
271
+ if 'message_count' in columns:
272
+ cursor.execute('''
273
+ INSERT INTO spam_logs (guild_id, user_id, message, message_count)
274
+ VALUES (?, ?, ?, ?)
275
+ ''', (guild_id, user_id, message[:1000], message_count))
276
+ else:
277
+ # Fallback for tables without message_count column
278
+ cursor.execute('''
279
+ INSERT INTO spam_logs (guild_id, user_id, message)
280
+ VALUES (?, ?, ?)
281
+ ''', (guild_id, user_id, message[:1000]))
282
+
283
+ self.conn.commit()
284
+ return True
285
+
286
+ def get_spam_logs(self, guild_id: int, limit: int = 10) -> List[Dict]:
287
+ """Get recent spam logs for a guild."""
288
+ with self.get_cursor() as cursor:
289
+ cursor.execute('''
290
+ SELECT user_id, message, message_count, timestamp
291
+ FROM spam_logs WHERE guild_id = ?
292
+ ORDER BY timestamp DESC LIMIT ?
293
+ ''', (guild_id, limit))
294
+
295
+ return [
296
+ {
297
+ 'user_id': row['user_id'],
298
+ 'message': row['message'],
299
+ 'message_count': row['message_count'],
300
+ 'timestamp': row['timestamp']
301
+ }
302
+ for row in cursor.fetchall()
303
+ ]
304
+
305
+ def get_user_spam_history(self, guild_id: int, user_id: int,
306
+ hours: int = 24, limit: int = 50) -> List[Dict]:
307
+ """Get spam history for a specific user within a time frame."""
308
+ cutoff_time = datetime.now() - timedelta(hours=hours)
309
+
310
+ with self.get_cursor() as cursor:
311
+ cursor.execute('''
312
+ SELECT message, message_count, timestamp
313
+ FROM spam_logs
314
+ WHERE guild_id = ? AND user_id = ? AND timestamp > ?
315
+ ORDER BY timestamp DESC LIMIT ?
316
+ ''', (guild_id, user_id, cutoff_time, limit))
317
+
318
+ return [
319
+ {
320
+ 'message': row['message'],
321
+ 'message_count': row['message_count'],
322
+ 'timestamp': row['timestamp']
323
+ }
324
+ for row in cursor.fetchall()
325
+ ]
326
+
327
+ def clear_spam_logs(self, guild_id: int, older_than_days: Optional[int] = None) -> int:
328
+ """Clear spam logs for a guild, optionally only older entries."""
329
+ with self.get_cursor() as cursor:
330
+ if older_than_days:
331
+ cutoff_date = datetime.now() - timedelta(days=older_than_days)
332
+ cursor.execute('''
333
+ DELETE FROM spam_logs
334
+ WHERE guild_id = ? AND timestamp < ?
335
+ ''', (guild_id, cutoff_date))
336
+ else:
337
+ cursor.execute('DELETE FROM spam_logs WHERE guild_id = ?', (guild_id,))
338
+
339
+ deleted_count = cursor.rowcount
340
+ self.conn.commit()
341
+ return deleted_count
342
+
343
+ def add_to_whitelist(self, guild_id: int, user_id: int,
344
+ added_by: Optional[int] = None, reason: Optional[str] = None) -> bool:
345
+ """Add user to spam whitelist with additional metadata."""
346
+ with self.get_cursor() as cursor:
347
+ columns = self._get_table_columns('spam_whitelist')
348
+
349
+ # Build query based on available columns
350
+ if 'added_by' in columns and 'reason' in columns:
351
+ cursor.execute('''
352
+ INSERT OR IGNORE INTO spam_whitelist (guild_id, user_id, added_by, reason)
353
+ VALUES (?, ?, ?, ?)
354
+ ''', (guild_id, user_id, added_by, reason))
355
+ else:
356
+ cursor.execute('''
357
+ INSERT OR IGNORE INTO spam_whitelist (guild_id, user_id)
358
+ VALUES (?, ?)
359
+ ''', (guild_id, user_id))
360
+
361
+ success = cursor.rowcount > 0
362
+ self.conn.commit()
363
+ return success
364
+
365
+ def remove_from_whitelist(self, guild_id: int, user_id: int) -> bool:
366
+ """Remove user from spam whitelist."""
367
+ with self.get_cursor() as cursor:
368
+ cursor.execute('''
369
+ DELETE FROM spam_whitelist WHERE guild_id = ? AND user_id = ?
370
+ ''', (guild_id, user_id))
371
+ success = cursor.rowcount > 0
372
+ self.conn.commit()
373
+ return success
374
+
375
+ def is_whitelisted(self, guild_id: int, user_id: int) -> bool:
376
+ """Check if user is whitelisted."""
377
+ with self.get_cursor() as cursor:
378
+ cursor.execute('''
379
+ SELECT 1 FROM spam_whitelist WHERE guild_id = ? AND user_id = ?
380
+ ''', (guild_id, user_id))
381
+ return cursor.fetchone() is not None
382
+
383
+ def get_whitelist(self, guild_id: int) -> List[Dict]:
384
+ """Get all whitelisted users for a guild with metadata."""
385
+ with self.get_cursor() as cursor:
386
+ columns = self._get_table_columns('spam_whitelist')
387
+
388
+ # Build query based on available columns
389
+ select_columns = ['user_id']
390
+ if 'added_by' in columns:
391
+ select_columns.append('added_by')
392
+ if 'added_at' in columns:
393
+ select_columns.append('added_at')
394
+ if 'reason' in columns:
395
+ select_columns.append('reason')
396
+
397
+ query = f"SELECT {', '.join(select_columns)} FROM spam_whitelist WHERE guild_id = ? ORDER BY user_id"
398
+ cursor.execute(query, (guild_id,))
399
+
400
+ result = []
401
+ for row in cursor.fetchall():
402
+ entry = {'user_id': row['user_id']}
403
+ if 'added_by' in columns:
404
+ entry['added_by'] = row.get('added_by')
405
+ if 'added_at' in columns:
406
+ entry['added_at'] = row.get('added_at')
407
+ if 'reason' in columns:
408
+ entry['reason'] = row.get('reason')
409
+ result.append(entry)
410
+
411
+ return result
412
+
413
+ def get_guild_stats(self, guild_id: int) -> Dict:
414
+ """Get comprehensive statistics for a guild."""
415
+ with self.get_cursor() as cursor:
416
+ # Get spam logs count
417
+ cursor.execute('SELECT COUNT(*) as total FROM spam_logs WHERE guild_id = ?', (guild_id,))
418
+ total_logs = cursor.fetchone()['total']
419
+
420
+ # Get recent spam (last 24 hours)
421
+ yesterday = datetime.now() - timedelta(hours=24)
422
+ cursor.execute('''
423
+ SELECT COUNT(*) as recent FROM spam_logs
424
+ WHERE guild_id = ? AND timestamp > ?
425
+ ''', (guild_id, yesterday))
426
+ recent_logs = cursor.fetchone()['recent']
427
+
428
+ # Get whitelist count
429
+ cursor.execute('SELECT COUNT(*) as count FROM spam_whitelist WHERE guild_id = ?', (guild_id,))
430
+ whitelist_count = cursor.fetchone()['count']
431
+
432
+ # Get top spammers (last 7 days)
433
+ week_ago = datetime.now() - timedelta(days=7)
434
+ cursor.execute('''
435
+ SELECT user_id, COUNT(*) as spam_count, SUM(message_count) as total_messages
436
+ FROM spam_logs
437
+ WHERE guild_id = ? AND timestamp > ?
438
+ GROUP BY user_id
439
+ ORDER BY spam_count DESC
440
+ LIMIT 5
441
+ ''', (guild_id, week_ago))
442
+ top_spammers = cursor.fetchall()
443
+
444
+ return {
445
+ 'total_spam_logs': total_logs,
446
+ 'recent_spam_logs': recent_logs,
447
+ 'whitelist_count': whitelist_count,
448
+ 'top_spammers': [
449
+ {
450
+ 'user_id': row['user_id'],
451
+ 'spam_incidents': row['spam_count'],
452
+ 'total_messages': row['total_messages']
453
+ }
454
+ for row in top_spammers
455
+ ]
456
+ }
457
+
458
+ def cleanup_old_logs(self, days_to_keep: int = 30) -> int:
459
+ """Clean up old spam logs across all guilds."""
460
+ cutoff_date = datetime.now() - timedelta(days=days_to_keep)
461
+
462
+ with self.get_cursor() as cursor:
463
+ cursor.execute('DELETE FROM spam_logs WHERE timestamp < ?', (cutoff_date,))
464
+ deleted_count = cursor.rowcount
465
+ self.conn.commit()
466
+
467
+ if deleted_count > 0:
468
+ self.logger.info(f"Cleaned up {deleted_count} old spam logs")
469
+
470
+ return deleted_count
471
+
472
+ def backup_database(self, backup_path: str) -> bool:
473
+ """Create a backup of the database."""
474
+ try:
475
+ backup_conn = sqlite3.connect(backup_path)
476
+ self.conn.backup(backup_conn)
477
+ backup_conn.close()
478
+ return True
479
+ except Exception as e:
480
+ self.logger.error(f"Backup failed: {e}")
481
+ return False
482
+
483
+ def __enter__(self):
484
+ """Context manager entry."""
485
+ return self
486
+
487
+ def __exit__(self, exc_type, exc_val, exc_tb):
488
+ """Context manager exit with proper cleanup."""
489
+ self.close()
490
+
491
+ def close(self):
492
+ """Close database connection."""
493
+ if hasattr(self, 'conn') and self.conn:
494
+ self.conn.close()