-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgline_sync.py
More file actions
297 lines (233 loc) · 8.81 KB
/
Copy pathgline_sync.py
File metadata and controls
297 lines (233 loc) · 8.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
"""
Sentinel IRC Bot - Module de synchronisation G-Line
Thread qui synchronise les G-Lines toutes les 5 minutes
"""
import threading
import time
import logging
from parser import parse_stats_g_response
logger = logging.getLogger(__name__)
class GLineSyncer(threading.Thread):
"""Thread de synchronisation des G-Lines"""
def __init__(self, irc_client, database, config):
"""
Initialise le syncer
Args:
irc_client: Client IRC pour envoyer les commandes
database: Instance DatabaseManager
config (dict): Configuration de synchronisation
"""
super().__init__(daemon=True)
self.irc_client = irc_client
self.database = database
self.config = config
self.running = False
self.interval = config.get('interval', 300) # 5 minutes par défaut
self.gline_buffer = []
self.collecting = False
self.lock = threading.Lock()
logger.info(f"GLineSyncer initialisé (intervalle: {self.interval}s)")
def run(self):
"""Boucle principale du thread"""
self.running = True
logger.info("Thread de synchronisation G-Line démarré")
# Attendre que le bot soit connecté avant la première sync
time.sleep(10)
while self.running:
try:
self.sync_glines()
except Exception as e:
logger.error(f"Erreur lors de la synchronisation G-Line: {e}")
# Attendre l'intervalle avant la prochaine sync
for _ in range(self.interval):
if not self.running:
break
time.sleep(1)
logger.info("Thread de synchronisation G-Line arrêté")
def stop(self):
"""Arrête le thread de synchronisation"""
logger.info("Arrêt du thread de synchronisation G-Line...")
self.running = False
def sync_glines(self):
"""
Effectue une synchronisation complète des G-Lines
1. Envoie /stats g
2. Collecte les réponses
3. Parse les G-Lines
4. Vide et remplit la table
"""
try:
logger.info("Début de la synchronisation G-Line")
# Réinitialiser le buffer
with self.lock:
self.gline_buffer = []
self.collecting = True
# Envoyer la commande /stats g
self.irc_client.connection.send_raw(f"STATS g")
logger.debug("Commande STATS g envoyée")
# Attendre que la réponse soit complète (max 10 secondes)
timeout = 10
elapsed = 0
while self.collecting and elapsed < timeout:
time.sleep(0.5)
elapsed += 0.5
# Si timeout, on arrête la collecte
if elapsed >= timeout:
logger.warning("Timeout lors de la collecte des G-Lines")
with self.lock:
self.collecting = False
# Parser les G-Lines collectées
with self.lock:
glines = parse_stats_g_response(self.gline_buffer)
# Synchroniser avec la base de données
if self.database.sync_glines(glines):
logger.info(f"Synchronisation G-Line réussie: {len(glines)} entrées")
else:
logger.error("Échec de la synchronisation G-Line")
except Exception as e:
logger.error(f"Erreur lors de sync_glines: {e}", exc_info=True)
def add_line(self, line):
"""
Ajoute une ligne au buffer de collecte
Args:
line (str): Ligne de réponse du serveur
"""
with self.lock:
if self.collecting:
# Logger TOUTES les lignes reçues pendant la collecte pour debug
logger.info(f"[STATS G Response] {line}")
# Vérifier si c'est une ligne de G-Line (code 223)
if ' 223 ' in line:
self.gline_buffer.append(line)
logger.info(f"Ligne G-Line collectée: {line}")
# Vérifier si c'est la fin de la liste (code 219)
elif ' 219 ' in line and 'g' in line.lower():
logger.info("Fin de la liste G-Line détectée")
self.collecting = False
def force_sync(self):
"""Force une synchronisation immédiate (appelé par commande admin)"""
logger.info("Synchronisation G-Line forcée")
sync_thread = threading.Thread(target=self.sync_glines, daemon=True)
sync_thread.start()
def is_collecting(self):
"""Retourne True si on est en train de collecter des G-Lines"""
with self.lock:
return self.collecting
class GLineMonitor:
"""Moniteur pour détecter les changements de G-Lines"""
def __init__(self, database):
"""
Initialise le moniteur
Args:
database: Instance DatabaseManager
"""
self.database = database
self.last_count = 0
self.last_check = None
def check_changes(self):
"""
Vérifie s'il y a eu des changements dans les G-Lines
Returns:
dict: Informations sur les changements
"""
try:
current_count = self.database.fetch_all("SELECT COUNT(*) as count FROM glines")[0]['count']
changes = {
'has_changes': False,
'previous_count': self.last_count,
'current_count': current_count,
'diff': 0
}
if self.last_count != current_count:
changes['has_changes'] = True
changes['diff'] = current_count - self.last_count
logger.info(f"Changement détecté dans les G-Lines: {self.last_count} -> {current_count} ({changes['diff']:+d})")
self.last_count = current_count
self.last_check = time.time()
return changes
except Exception as e:
logger.error(f"Erreur lors de la vérification des changements: {e}")
return {'has_changes': False, 'error': str(e)}
def get_recent_glines(self, limit=10):
"""
Récupère les G-Lines les plus récentes
Args:
limit (int): Nombre de G-Lines à retourner
Returns:
list: G-Lines récentes
"""
query = """
SELECT * FROM glines
ORDER BY last_sync DESC
LIMIT %s
"""
return self.database.fetch_all(query, (limit,))
def search_gline(self, mask):
"""
Recherche une G-Line par masque
Args:
mask (str): Masque à rechercher
Returns:
dict: G-Line trouvée ou None
"""
query = "SELECT * FROM glines WHERE mask LIKE %s"
result = self.database.fetch_all(query, (f"%{mask}%",))
return result[0] if result else None
def create_gline_report(glines):
"""
Crée un rapport formaté des G-Lines
Args:
glines (list): Liste des G-Lines
Returns:
str: Rapport formaté
"""
if not glines:
return "Aucune G-Line trouvée"
report = []
report.append(f"=== Rapport G-Lines ({len(glines)} entrées) ===")
report.append("")
for gline in glines:
report.append(f"Mask: {gline['mask']}")
report.append(f"Raison: {gline['raison']}")
report.append(f"Durée: {gline['duree']}s")
if gline.get('serveur_source'):
report.append(f"Serveur: {gline['serveur_source']}")
report.append(f"Dernière sync: {gline['last_sync']}")
report.append("-" * 50)
return "\n".join(report)
def estimate_next_sync(syncer):
"""
Estime le temps avant la prochaine synchronisation
Args:
syncer (GLineSyncer): Instance du syncer
Returns:
int: Secondes restantes
"""
# Cette fonction est simplifiée, on retourne juste l'intervalle
# Dans une vraie implémentation, on trackrait le timestamp de la dernière sync
return syncer.interval
def get_sync_stats(database):
"""
Récupère les statistiques de synchronisation
Args:
database: Instance DatabaseManager
Returns:
dict: Statistiques
"""
stats = {}
# Dernière synchronisation
query = "SELECT MAX(last_sync) as last_sync FROM glines"
result = database.fetch_all(query)
stats['last_sync'] = result[0]['last_sync'] if result and result[0]['last_sync'] else None
# Nombre total de G-Lines
query = "SELECT COUNT(*) as count FROM glines"
result = database.fetch_all(query)
stats['total_glines'] = result[0]['count'] if result else 0
# G-Lines par serveur
query = """
SELECT serveur_source, COUNT(*) as count
FROM glines
GROUP BY serveur_source
"""
stats['by_server'] = database.fetch_all(query)
return stats