]> gitweb.pimeys.fr Git - bots/deconnaisseur.git/blob - deconnaisseur.py
Boulet
[bots/deconnaisseur.git] / deconnaisseur.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3
4 # Codé par 20-100 le 23/04/12
5
6 # Un bot IRC qui sort des déconnaissances
7
8 import threading
9 import random
10 import time
11 import pickle
12 import re
13 import signal
14 import sys
15 import os
16 from remplace_accents import remplace_accents
17
18 # Oui, j'ai recodé ma version d'irclib pour pouvoir rattrapper les SIGHUP
19 sys.path.insert(0, "/home/vincent/scripts/python-myirclib")
20 import irclib
21 import ircbot
22
23 # Fichier de conf
24 import config
25
26 def get_config_played_file(serveur):
27 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
28 return config.played_file_template%(serveurs[serveur])
29
30 def get_config_logfile(serveur):
31 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
32 return config.logfile_template%(serveurs[serveur])
33
34 class UnicodeBotError(Exception):
35 pass
36 def bot_unicode(chain):
37 try:
38 unicode(chain,"utf8")
39 except UnicodeDecodeError:
40 raise UnicodeBotError
41
42 def log(serveur,channel,auteur=None,message=None):
43 f=open(get_config_logfile(serveur),"a")
44 if auteur==message==None:
45 # alors c'est que c'est pas un channel mais juste une ligne de log
46 chain="%s %s"%(time.strftime("%F %T"),channel)
47 else:
48 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
49 f.write(chain+"\n")
50 if config.debug_stdout:
51 print chain
52 f.close()
53
54 def ignore_event(serv, ev):
55 """Retourne ``True`` si il faut ignorer cet évènement."""
56 for (blackmask, exceptmask) in config.blacklisted_masks:
57 usermask = ev.source()
58 if exceptmask is None:
59 exceptit = False
60 else:
61 exceptit = bool(irclib.mask_matches(usermask, exceptmask))
62 blackit = bool(irclib.mask_matches(usermask, blackmask))
63 if blackit and not exceptit:
64 return True
65
66 def reussi(message,answer,answer_regexp,auteur):
67 if auteur in config.level3:
68 return answer in message
69 if auteur in config.level2:
70 return remplace_accents(answer) in message
71 else:
72 if re.match(".*" + remplace_accents(answer_regexp).lower(),remplace_accents(message).lower()):
73 return True
74
75 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
76 if case_sensitive:
77 chain=unicode(chain,"utf8")
78 else:
79 chain=unicode(chain,"utf8").lower()
80 allmatches="("+"|".join(matches)+")"
81 reg=(avant+allmatches+apres).lower()
82 o=re.match(reg,chain)
83 return o
84
85 def is_tag(chain):
86 return is_something(chain,config.tag_triggers)
87
88 class RefuseError(Exception):
89 pass
90
91 class Deconnaisseur(ircbot.SingleServerIRCBot):
92 def __init__(self,serveur,debug=False):
93 temporary_pseudo=config.pseudo+str(random.randrange(10000,100000))
94 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
95 temporary_pseudo,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
96 self.debug=debug
97 self.serveur=serveur
98 self.overops=config.overops
99 self.ops=self.overops+config.ops
100 self.chanlist=config.chanlist
101 self.stay_channels=config.stay_channels
102 self.play_channels=config.play_channels
103 self.play_status={i:[0] for i in self.play_channels}
104 self.last_activity={}
105 self.quiet_channels=[]
106
107 def give_me_my_pseudo(self,serv):
108 serv.privmsg("NickServ","RECOVER %s %s"%(config.pseudo,config.password))
109 serv.privmsg("NickServ","RELEASE %s %s"%(config.pseudo,config.password))
110 time.sleep(0.3)
111 serv.nick(config.pseudo)
112
113 def on_welcome(self, serv, ev):
114 self.serv=serv # ça serv ira :)
115 self.give_me_my_pseudo(serv)
116 serv.privmsg("NickServ","identify %s"%(config.password))
117 log(self.serveur,"Connected")
118 if self.debug:
119 self.chanlist=["#bot"]
120 self.play_channels=["#bot"]
121 for c in self.chanlist:
122 log(self.serveur,"JOIN %s"%(c))
123 serv.join(c)
124 self.update_activity(c,force=True)
125 for c in self.play_channels:
126 token=time.time()-3600
127 self.play_status[c]=[0,token]
128 serv.execute_delayed(random.randrange(config.ttrig),self.start_enigme,(serv,c,token))
129
130 def start_enigme(self,serv,channel,token=None):
131 # On reste silencieux si lechan n'est pas actif
132 if not self.is_active(channel):
133 serv.execute_delayed(config.ttrig*5,self.start_enigme,(serv,channel,token))
134 return
135 if self.play_status[channel][0]==0 and channel in self.play_channels:
136 ok="skip"
137 if token==self.play_status[channel][-1]:
138 ok="do_it"
139 if token==None:
140 if time.time() > self.play_status[channel][-1]+config.time_incompressible:
141 ok="do_it"
142 else:
143 ok="refuse"
144 if ok=="do_it":
145 enigme,indice,answer_reg,answer=self.get_enigme()
146 log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)).encode("utf8"))
147 serv.privmsg(channel,enigme.encode("utf8"))
148 token=time.time()
149 self.play_status[channel]=[1,enigme,indice,answer_reg,answer,token]
150 serv.execute_delayed(random.randrange(config.ttrig*3,config.ttrig*5),self.give_indice,(serv,channel,token))
151 elif ok=="refuse":
152 raise RefuseError
153 def give_indice(self,serv,channel,token):
154 if self.play_status[channel][0]==1:
155 if token==None:
156 # c'est donc que l'indice a été demandé
157 if self.play_status[channel][-1]+config.time_incompressible_clue<time.time():
158 token=self.play_status[channel][-1]
159 if self.play_status[channel][-1]==token:
160 indice=self.play_status[channel][2]
161 serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
162 self.play_status[channel][0]=2
163 serv.execute_delayed(random.randrange(config.ttrig*1,config.ttrig*3),self.give_answer,(serv,channel,token))
164 def give_answer(self,serv,channel,token):
165 if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
166 answer=self.play_status[channel][4]
167 serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8"))
168 token=time.time()
169 self.play_status[channel]=[0,token]
170 serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,channel,token))
171
172 def get_enigme(self):
173 # on récupère les déconnaissances
174 f=open(config.source_file)
175 t=f.read()
176 f.close()
177 l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
178 dec={int(i[0]):list(i[1:]) for i in l if len(i)==5}
179 # on va chercher combien de fois elles ont été jouées
180 played_file=get_config_played_file(self.serveur)
181 f=open(played_file)
182 t=f.read()
183 f.close()
184 l=re.findall("(.*):(.*)",t)
185 played={int(i[0]):int(i[1]) for i in l}
186 # on récupère le nombre d'occurrences le plus faible
187 mini=min(played.values())
188 # on choisit un id dans ceux qui ont ce nombre d'occurences
189 id_choisi=random.choice([k for k,v in played.items() if v==mini])
190 enigme,indice,answer_reg,answer=dec[id_choisi]
191 # on incrémente la choisie
192 played[id_choisi]+=1
193 # on enregistre le played_file
194 f=open(played_file,"w")
195 f.write("\n".join(["%-3s : %s"%(k,v) for k,v in played.items()]))
196 f.close()
197 return enigme.decode("utf8"),indice.decode("utf8"),answer_reg.decode("utf8"),answer.decode("utf8")
198
199 def pourmoi(self, serv, message):
200 pseudo=self.nick
201 size=len(pseudo)
202 if message[:size]==pseudo and len(message)>size and message[size]==":":
203 return (True,message[size+1:].strip(" "))
204 else:
205 return (False,message)
206
207 def on_privmsg(self, serv, ev):
208 if ignore_event(serv, ev):
209 return
210 message=ev.arguments()[0]
211 auteur = irclib.nm_to_n(ev.source())
212 try:
213 test=bot_unicode(message)
214 except UnicodeBotError:
215 if config.utf8_trigger:
216 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
217 return
218 message=message.split()
219 cmd=message[0].lower()
220 notunderstood=False
221 if cmd=="help":
222 helpmsg_default="""Liste des commandes :
223 HELP Affiche ce message d'aide
224 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
225 SCORES Affiche les scores"""
226 helpmsg_ops="""
227 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
228 LEAVE Faire quitter un channel
229 PLAY Passe un channel en mode "jouer"
230 NOPLAY Passe un channel en mode "ne pas jouer"
231 QUIET Se taire sur un channel
232 NOQUIET Opposé de QUIET
233 RELOAD Recharge la config"""
234 helpmsg_overops="""
235 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
236 SAY Fais envoyer un message sur un chan ou à une personne
237 STAY Ignorera les prochains LEAVE pour un chan
238 NOSTAY Opposé de STAY
239 STATUS Montre l'état courant
240 DIE Mourir"""
241 helpmsg=helpmsg_default
242 if auteur in self.ops:
243 helpmsg+=helpmsg_ops
244 if auteur in self.overops:
245 helpmsg+=helpmsg_overops
246 for ligne in helpmsg.split("\n"):
247 serv.privmsg(auteur,ligne)
248 elif cmd=="join":
249 if auteur in self.ops:
250 if len(message)>1:
251 if message[1] in self.chanlist:
252 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
253 else:
254 serv.join(message[1])
255 self.chanlist.append(message[1])
256 self.update_activity(message[1],force=True)
257 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
258 log(self.serveur,"priv",auteur," ".join(message))
259 else:
260 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
261 else:
262 notunderstood=True
263 elif cmd=="leave":
264 if auteur in self.ops and len(message)>1:
265 if message[1] in self.chanlist:
266 if not (message[1] in self.stay_channels) or auteur in self.overops:
267 self.quitter(message[1]," ".join(message[2:]))
268 self.chanlist.remove(message[1])
269 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
270 else:
271 serv.privmsg(auteur,"Non, je reste !")
272 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
273 else:
274 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
275 else:
276 notunderstood=True
277 elif cmd=="stay":
278 if auteur in self.overops:
279 if len(message)>1:
280 if message[1] in self.stay_channels:
281 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
282 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
283 else:
284 self.stay_channels.append(message[1])
285 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
286 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
287 else:
288 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
289 else:
290 notunderstood=True
291 elif cmd=="nostay":
292 if auteur in self.overops:
293 if len(message)>1:
294 if message[1] in self.stay_channels:
295 self.stay_channels.remove(message[1])
296 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
297 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
298 else:
299 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
300 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
301 else:
302 notunderstood=True
303 elif cmd=="play":
304 if auteur in self.ops:
305 if len(message)>1:
306 if message[1] in self.play_channels:
307 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
308 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
309 else:
310 self.play_channels.append(message[1])
311 self.play_status[message[1]]=[0,time.time()-3600]
312 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
313 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
314 else:
315 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
316 else:
317 notunderstood=True
318 elif cmd=="noplay":
319 if auteur in self.ops:
320 if len(message)>1:
321 if message[1] in self.play_channels:
322 self.play_channels.remove(message[1])
323 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
324 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
325 else:
326 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
327 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
328 else:
329 notunderstood=True
330 elif cmd=="quiet":
331 if auteur in self.ops:
332 if len(message)>1:
333 if message[1] in self.quiet_channels:
334 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
335 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
336 else:
337 self.quiet_channels.append(message[1])
338 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
339 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
340 else:
341 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
342 else:
343 notunderstood=True
344 elif cmd=="noquiet":
345 if auteur in self.ops:
346 if len(message)>1:
347 if message[1] in self.quiet_channels:
348 self.quiet_channels.remove(message[1])
349 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
350 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
351 else:
352 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
353 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
354 else:
355 notunderstood=True
356 elif cmd=="reload":
357 if auteur in self.ops:
358 self.reload(auteur)
359 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
360 else:
361 notunderstood=True
362 elif cmd in ["states","status"]:
363 if auteur in self.overops:
364 for k in self.play_status.keys():
365 serv.privmsg(auteur,(u"%s : %s"%(k,"; ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
366 elif cmd=="say":
367 if auteur in self.overops and len(message)>2:
368 serv.privmsg(message[1]," ".join(message[2:]))
369 log(self.serveur,"priv",auteur," ".join(message))
370 elif len(message)<=2:
371 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
372 else:
373 notunderstood=True
374 elif cmd=="die":
375 if auteur in self.overops:
376 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
377 self.mourir()
378 elif cmd=="score":
379 if len(message)>1:
380 if len(message) in [3,4] and message[1].lower()=="transfert":
381 scores=self.get_scores()
382 de,to=auteur,message[2]
383 value=scores.get(de,0)
384 if len(message)==4:
385 try:
386 asked=int(message[3])
387 except ValueError:
388 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
389 return
390 else:
391 asked=value
392 if value==0:
393 serv.privmsg(auteur,"Vous n'avez pas de points")
394 return
395 elif asked<=0:
396 serv.privmsg(auteur,"Bien tenté…")
397 return
398 elif asked>value:
399 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
400 return
401 else:
402 self.add_score(de,-asked)
403 self.add_score(to,asked)
404 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
405 else:
406 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
407 else:
408 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
409 elif cmd=="scores":
410 if len(message)==1:
411 scores=self.get_scores().items()
412 # trie par score
413 scores.sort(lambda x,y:cmp(x[1],y[1]))
414 scores.reverse()
415 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
416 # trie par pseudo
417 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
418 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
419 elif auteur in self.overops:
420 souscmd=message[1].lower()
421 if souscmd=="del":
422 if len(message)==3:
423 todelete=message[2]
424 scores=self.get_scores()
425 if scores.has_key(todelete):
426 del scores[todelete]
427 self.save_scores(scores)
428 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
429 else:
430 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
431 else:
432 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
433 elif souscmd in ["add","sub"]:
434 if len(message)==4:
435 toadd,val=message[2],message[3]
436 try:
437 val=int(val)
438 except ValueError:
439 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
440 return
441 if souscmd=="sub":
442 val=-val
443 self.add_score(toadd,val)
444 serv.privmsg(auteur,"Done")
445 else:
446 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
447 else:
448 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
449 else:
450 notunderstood=True
451 else:
452 notunderstood=True
453 if notunderstood:
454 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
455
456 def on_pubmsg(self, serv, ev):
457 if ignore_event(serv, ev):
458 return
459 auteur = irclib.nm_to_n(ev.source())
460 canal = ev.target()
461 message = ev.arguments()[0]
462 self.update_activity(canal,auteur,message)
463 try:
464 test=bot_unicode(message)
465 except UnicodeBotError:
466 if config.utf8_trigger and not canal in self.quiet_channels:
467 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
468 return
469 tryother=False
470 pour_moi,message=self.pourmoi(serv,message)
471 if pour_moi and message.split()!=[]:
472 cmd=message.split()[0].lower()
473 try:
474 args=" ".join(message.split()[1:])
475 except:
476 args=""
477 if cmd in ["meurs","die","crève"]:
478 if auteur in self.overops:
479 self.mourir()
480 log(self.serveur,canal,auteur,message+"[successful]")
481 else:
482 serv.privmsg(canal,"%s: crève !"%(auteur))
483 log(self.serveur,canal,auteur,message+"[failed]")
484 elif cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
485 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
486 elif cmd == "reload":
487 if auteur in self.ops:
488 log(self.serveur, canal, auteur, message+"[successful]")
489 self.reload(canal)
490 elif cmd in ["part","leave","dégage"]:
491 if auteur in self.ops and (not (canal in self.stay_channels)
492 or auteur in self.overops):
493 self.quitter(canal)
494 log(self.serveur,canal,auteur,message+"[successful]")
495 self.chanlist.remove(canal)
496 else:
497 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
498 log(self.serveur,canal,auteur,message+"[failed]")
499
500 elif cmd in ["deviens","pseudo"]:
501 if auteur in self.ops:
502 become=args
503 serv.nick(become)
504 log(self.serveur,canal,auteur,message+"[successful]")
505 elif cmd in ["coucou"] and not canal in self.quiet_channels:
506 serv.privmsg(canal,"%s: coucou"%(auteur))
507 elif cmd in ["ping"] and not canal in self.quiet_channels:
508 serv.privmsg(canal,"%s: pong"%(auteur))
509 elif cmd in ["déconnaissance","deconnaissance","énigme","enigme","encore","aco","moo","moo !","moo !"]:
510 if canal in self.play_channels:
511 if self.play_status.get(canal,[-1])[0]==0:
512 try:
513 self.start_enigme(serv,canal)
514 except RefuseError:
515 serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
516 else:
517 serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][1])).encode("utf8") )
518 else:
519 serv.privmsg(canal,"%s: pas ici…"%(auteur))
520 elif cmd in ["score","!score"]:
521 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
522 elif cmd in ["scores","!scores"]:
523 scores=self.get_scores().items()
524 # trie par score
525 scores.sort(lambda x,y:cmp(x[1],y[1]))
526 scores.reverse()
527 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
528 # trie par pseudo
529 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
530 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
531 elif cmd=="indice" and canal in self.play_channels:
532 self.give_indice(serv,canal,None)
533 elif is_tag(message) and not canal in self.quiet_channels:
534 if auteur in self.ops:
535 action=random.choice(config.tag_actions)
536 serv.action(canal,action.encode("utf8"))
537 self.quiet_channels.append(canal)
538 else:
539 answer=random.choice(config.tag_answers)
540 for ligne in answer.split("\n"):
541 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
542 elif "Bâille, cru aile ou orld" in message:
543 self.mourir(u"Un de mes easters eggs (non en fait j'en ai qu'un) a été découvert par %s !"%auteur)
544 else:
545 tryother=True
546 else:
547 tryother=True
548 if tryother:
549 if self.play_status.get(canal,[-1])[0] in [1,2]:
550 answer_regexp=self.play_status[canal][3]
551 answer=self.play_status[canal][4]
552 if reussi(message.decode("utf8"),answer,answer_regexp,auteur):
553 serv.privmsg(canal,(u"%s: bravo ! (C'était %s)"%(auteur,answer)).encode("utf8"))
554 log(self.serveur,canal,auteur+"$win",message)
555 self.add_score(auteur,1)
556 token=time.time()
557 self.play_status[canal]=[0,token]
558 serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,canal,token))
559
560 def on_kick(self,serv,ev):
561 auteur = irclib.nm_to_n(ev.source())
562 channel = ev.target()
563 victime = ev.arguments()[0]
564 raison = ev.arguments()[1]
565 if victime==self.nick:
566 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
567 time.sleep(5)
568 serv.join(channel)
569 self.update_activity(channel,force=True)
570 # on ne dit rien au rejoin
571 #l1,l2=config.kick_answers,config.kick_actions
572 #n1,n2=len(l1),len(l2)
573 #i=random.randrange(n1+n2)
574 #if i>=n1:
575 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
576 #else:
577 # serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
578
579 def quitter(self,chan,leave_message=None):
580 if leave_message==None:
581 leave_message=random.choice(config.leave_messages)
582 self.serv.part(chan,message=leave_message.encode("utf8"))
583
584 def mourir(self,quit_message=None):
585 if quit_message==None:
586 quit_message=random.choice(config.quit_messages)
587 self.die(msg=quit_message.encode("utf8"))
588
589 def get_scores(self):
590 f=open(config.score_file)
591 scores=pickle.load(f)
592 f.close()
593 return scores
594
595 def add_score(self,pseudo,value):
596 scores=self.get_scores()
597 if scores.has_key(pseudo):
598 scores[pseudo]+=value
599 else:
600 scores[pseudo]=value
601 self.save_scores(scores)
602
603 def save_scores(self,scores):
604 f=open(config.score_file,"w")
605 pickle.dump(scores,f)
606 f.close()
607
608 def _getnick(self):
609 return self.serv.get_nickname()
610 nick = property(_getnick)
611
612 def update_activity(self,canal="",pseudo="",message="",force=False):
613 if force or (not pseudo in config.idle_bots and all([not re.match(ignore, message) for ignore in config.idle_messages])):
614 self.last_activity[canal]=time.time()
615 def is_active(self,canal):
616 return time.time()-self.last_activity[canal]<config.idle_time
617
618 def reload(self, auteur=None):
619 reload(config)
620 if auteur in [None, "SIGHUP"]:
621 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
622 for to in config.report_bugs_to:
623 self.serv.privmsg(to, towrite)
624 log(self.serveur, towrite)
625 else:
626 self.serv.privmsg(auteur,"Config reloaded")
627
628 def start_as_daemon(self, outfile):
629 sys.stderr = Logger(outfile)
630 self.start()
631
632
633 class Logger(object):
634 """Pour écrire ailleurs que sur stdout"""
635 def __init__(self, filename="deconnaisseur.full.log"):
636 self.filename = filename
637
638 def write(self, message):
639 f = open(self.filename, "a")
640 f.write(message)
641 f.close()
642
643
644 if __name__=="__main__":
645 import sys
646 if len(sys.argv)==1:
647 print "Usage : deconnaisseur.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
648 print " --outfile sans --no-output ni --daemon n'a aucun effet"
649 exit(1)
650 serveur=sys.argv[1]
651 if "--daemon" in sys.argv:
652 thisfile = os.path.realpath(__file__)
653 thisdirectory = thisfile.rsplit("/", 1)[0]
654 os.chdir(thisdirectory)
655 daemon = True
656 else:
657 daemon = False
658 if "debug" in sys.argv or "--debug" in sys.argv:
659 debug=True
660 else:
661 debug=False
662 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
663 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
664 if "--no-output" in sys.argv or "--daemon" in sys.argv:
665 outfile = "/var/log/bots/deconnaisseur.full.log"
666 for arg in sys.argv:
667 arg = arg.split("=")
668 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
669 outfile = arg[1]
670 sys.stdout = Logger(outfile)
671 try:
672 serveur=serveurs[serveur]
673 except KeyError:
674 print "Server Unknown : %s"%(serveur)
675 exit(404)
676 deconnaisseur=Deconnaisseur(serveur,debug)
677 # Si on reçoit un SIGHUP, on reload la config
678 def sighup_handler(signum, frame):
679 deconnaisseur.reload("SIGHUP")
680 signal.signal(signal.SIGHUP, sighup_handler)
681 if daemon:
682 child_pid = os.fork()
683 if child_pid == 0:
684 os.setsid()
685 deconnaisseur.start_as_daemon(outfile)
686 else:
687 # on enregistre le pid de deconnaisseur
688 pidfile = "/var/run/bots/deconnaisseur.pid"
689 for arg in sys.argv:
690 arg = arg.split("=")
691 if arg[0].strip('-') in ["pidfile"]:
692 pidfile = arg[1]
693 f = open(pidfile, "w")
694 f.write("%s\n" % child_pid)
695 f.close()
696 else:
697 deconnaisseur.start()