]> gitweb.pimeys.fr Git - bots/deconnaisseur.git/blob - deconnaisseur.py
[deconnaissances] Manque une espace
[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 for c in self.play_channels:
125 token=time.time()-3600
126 self.play_status[c]=[0,token]
127 serv.execute_delayed(random.randrange(config.ttrig),self.start_enigme,(serv,c,token))
128
129 def start_enigme(self,serv,channel,token=None):
130 # On reste silencieux si lechan n'est pas actif
131 if not self.is_active(channel):
132 serv.execute_delayed(config.ttrig*5,self.start_enigme,(serv,channel,token))
133 return
134 if self.play_status[channel][0]==0 and channel in self.play_channels:
135 ok="skip"
136 if token==self.play_status[channel][-1]:
137 ok="do_it"
138 if token==None:
139 if time.time() > self.play_status[channel][-1]+config.time_incompressible:
140 ok="do_it"
141 else:
142 ok="refuse"
143 if ok=="do_it":
144 enigme,indice,answer_reg,answer=self.get_enigme()
145 log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s; %s; %s; %s"%(enigme, indice, answer_reg, answer)).encode("utf8"))
146 serv.privmsg(channel,enigme.encode("utf8"))
147 token=time.time()
148 self.play_status[channel]=[1,enigme,indice,answer_reg,answer,token]
149 serv.execute_delayed(random.randrange(config.ttrig*3,config.ttrig*5),self.give_indice,(serv,channel,token))
150 elif ok=="refuse":
151 raise RefuseError
152 def give_indice(self,serv,channel,token):
153 if self.play_status[channel][0]==1:
154 if token==None:
155 # c'est donc que l'indice a été demandé
156 if self.play_status[channel][-1]+config.time_incompressible_clue<time.time():
157 token=self.play_status[channel][-1]
158 if self.play_status[channel][-1]==token:
159 indice=self.play_status[channel][2]
160 serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
161 self.play_status[channel][0]=2
162 serv.execute_delayed(random.randrange(config.ttrig*1,config.ttrig*3),self.give_answer,(serv,channel,token))
163 def give_answer(self,serv,channel,token):
164 if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
165 answer=self.play_status[channel][4]
166 serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8"))
167 token=time.time()
168 self.play_status[channel]=[0,token]
169 serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,channel,token))
170
171 def get_enigme(self):
172 # on récupère les déconnaissances
173 f=open(config.source_file)
174 t=f.read()
175 f.close()
176 l=re.findall("%\n(.*)\n(.*)\n(.*)\n(.*)\n(.*)\n",t)
177 dec={int(i[0]):list(i[1:]) for i in l if len(i)==5}
178 # on va chercher combien de fois elles ont été jouées
179 played_file=get_config_played_file(self.serveur)
180 f=open(played_file)
181 t=f.read()
182 f.close()
183 l=re.findall("(.*):(.*)",t)
184 played={int(i[0]):int(i[1]) for i in l}
185 # on récupère le nombre d'occurrences le plus faible
186 mini=min(played.values())
187 # on choisit un id dans ceux qui ont ce nombre d'occurences
188 id_choisi=random.choice([k for k,v in played.items() if v==mini])
189 enigme,indice,answer_reg,answer=dec[id_choisi]
190 # on incrémente la choisie
191 played[id_choisi]+=1
192 # on enregistre le played_file
193 f=open(played_file,"w")
194 f.write("\n".join(["%-3s : %s"%(k,v) for k,v in played.items()]))
195 f.close()
196 return enigme.decode("utf8"),indice.decode("utf8"),answer_reg.decode("utf8"),answer.decode("utf8")
197
198 def pourmoi(self, serv, message):
199 pseudo=self.nick
200 size=len(pseudo)
201 if message[:size]==pseudo and len(message)>size and message[size]==":":
202 return (True,message[size+1:].strip(" "))
203 else:
204 return (False,message)
205
206 def on_privmsg(self, serv, ev):
207 if ignore_event(serv, ev):
208 return
209 message=ev.arguments()[0]
210 auteur = irclib.nm_to_n(ev.source())
211 try:
212 test=bot_unicode(message)
213 except UnicodeBotError:
214 if config.utf8_trigger:
215 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
216 return
217 message=message.split()
218 cmd=message[0].lower()
219 notunderstood=False
220 if cmd=="help":
221 helpmsg_default="""Liste des commandes :
222 HELP Affiche ce message d'aide
223 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
224 SCORES Affiche les scores"""
225 helpmsg_ops="""
226 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
227 LEAVE Faire quitter un channel
228 PLAY Passe un channel en mode "jouer"
229 NOPLAY Passe un channel en mode "ne pas jouer"
230 QUIET Se taire sur un channel
231 NOQUIET Opposé de QUIET
232 RELOAD Recharge la config"""
233 helpmsg_overops="""
234 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
235 SAY Fais envoyer un message sur un chan ou à une personne
236 STAY Ignorera les prochains LEAVE pour un chan
237 NOSTAY Opposé de STAY
238 STATUS Montre l'état courant
239 DIE Mourir"""
240 helpmsg=helpmsg_default
241 if auteur in self.ops:
242 helpmsg+=helpmsg_ops
243 if auteur in self.overops:
244 helpmsg+=helpmsg_overops
245 for ligne in helpmsg.split("\n"):
246 serv.privmsg(auteur,ligne)
247 elif cmd=="join":
248 if auteur in self.ops:
249 if len(message)>1:
250 if message[1] in self.chanlist:
251 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
252 else:
253 serv.join(message[1])
254 self.chanlist.append(message[1])
255 self.update_activity(message[1],force=True)
256 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
257 log(self.serveur,"priv",auteur," ".join(message))
258 else:
259 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
260 else:
261 notunderstood=True
262 elif cmd=="leave":
263 if auteur in self.ops and len(message)>1:
264 if message[1] in self.chanlist:
265 if not (message[1] in self.stay_channels) or auteur in self.overops:
266 self.quitter(message[1]," ".join(message[2:]))
267 self.chanlist.remove(message[1])
268 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
269 else:
270 serv.privmsg(auteur,"Non, je reste !")
271 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
272 else:
273 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
274 else:
275 notunderstood=True
276 elif cmd=="stay":
277 if auteur in self.overops:
278 if len(message)>1:
279 if message[1] in self.stay_channels:
280 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
281 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
282 else:
283 self.stay_channels.append(message[1])
284 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
285 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
286 else:
287 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
288 else:
289 notunderstood=True
290 elif cmd=="nostay":
291 if auteur in self.overops:
292 if len(message)>1:
293 if message[1] in self.stay_channels:
294 self.stay_channels.remove(message[1])
295 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
296 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
297 else:
298 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
299 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
300 else:
301 notunderstood=True
302 elif cmd=="play":
303 if auteur in self.ops:
304 if len(message)>1:
305 if message[1] in self.play_channels:
306 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
307 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
308 else:
309 self.play_channels.append(message[1])
310 self.play_status[message[1]]=[0,time.time()-3600]
311 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
312 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
313 else:
314 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
315 else:
316 notunderstood=True
317 elif cmd=="noplay":
318 if auteur in self.ops:
319 if len(message)>1:
320 if message[1] in self.play_channels:
321 self.play_channels.remove(message[1])
322 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
323 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
324 else:
325 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
326 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
327 else:
328 notunderstood=True
329 elif cmd=="quiet":
330 if auteur in self.ops:
331 if len(message)>1:
332 if message[1] in self.quiet_channels:
333 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
334 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
335 else:
336 self.quiet_channels.append(message[1])
337 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
338 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
339 else:
340 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
341 else:
342 notunderstood=True
343 elif cmd=="noquiet":
344 if auteur in self.ops:
345 if len(message)>1:
346 if message[1] in self.quiet_channels:
347 self.quiet_channels.remove(message[1])
348 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
349 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
350 else:
351 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
352 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
353 else:
354 notunderstood=True
355 elif cmd=="reload":
356 if auteur in self.ops:
357 self.reload(auteur)
358 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
359 else:
360 notunderstood=True
361 elif cmd in ["states","status"]:
362 if auteur in self.overops:
363 for k in self.play_status.keys():
364 serv.privmsg(auteur,(u"%s : %s"%(k,"; ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
365 elif cmd=="say":
366 if auteur in self.overops and len(message)>2:
367 serv.privmsg(message[1]," ".join(message[2:]))
368 log(self.serveur,"priv",auteur," ".join(message))
369 elif len(message)<=2:
370 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
371 else:
372 notunderstood=True
373 elif cmd=="die":
374 if auteur in self.overops:
375 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
376 self.mourir()
377 elif cmd=="score":
378 if len(message)>1:
379 if len(message) in [3,4] and message[1].lower()=="transfert":
380 scores=self.get_scores()
381 de,to=auteur,message[2]
382 value=scores.get(de,0)
383 if len(message)==4:
384 try:
385 asked=int(message[3])
386 except ValueError:
387 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
388 return
389 else:
390 asked=value
391 if value==0:
392 serv.privmsg(auteur,"Vous n'avez pas de points")
393 return
394 elif asked<=0:
395 serv.privmsg(auteur,"Bien tenté…")
396 return
397 elif asked>value:
398 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
399 return
400 else:
401 self.add_score(de,-asked)
402 self.add_score(to,asked)
403 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
404 else:
405 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
406 else:
407 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
408 elif cmd=="scores":
409 if len(message)==1:
410 scores=self.get_scores().items()
411 # trie par score
412 scores.sort(lambda x,y:cmp(x[1],y[1]))
413 scores.reverse()
414 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
415 # trie par pseudo
416 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
417 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
418 elif auteur in self.overops:
419 souscmd=message[1].lower()
420 if souscmd=="del":
421 if len(message)==3:
422 todelete=message[2]
423 scores=self.get_scores()
424 if scores.has_key(todelete):
425 del scores[todelete]
426 self.save_scores(scores)
427 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
428 else:
429 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
430 else:
431 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
432 elif souscmd in ["add","sub"]:
433 if len(message)==4:
434 toadd,val=message[2],message[3]
435 try:
436 val=int(val)
437 except ValueError:
438 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
439 return
440 if souscmd=="sub":
441 val=-val
442 self.add_score(toadd,val)
443 serv.privmsg(auteur,"Done")
444 else:
445 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
446 else:
447 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
448 else:
449 notunderstood=True
450 else:
451 notunderstood=True
452 if notunderstood:
453 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
454
455 def on_pubmsg(self, serv, ev):
456 if ignore_event(serv, ev):
457 return
458 auteur = irclib.nm_to_n(ev.source())
459 canal = ev.target()
460 message = ev.arguments()[0]
461 self.update_activity(canal,auteur,message)
462 try:
463 test=bot_unicode(message)
464 except UnicodeBotError:
465 if config.utf8_trigger and not canal in self.quiet_channels:
466 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
467 return
468 tryother=False
469 pour_moi,message=self.pourmoi(serv,message)
470 if pour_moi and message.split()!=[]:
471 cmd=message.split()[0].lower()
472 try:
473 args=" ".join(message.split()[1:])
474 except:
475 args=""
476 if cmd in ["meurs","die","crève"]:
477 if auteur in self.overops:
478 self.mourir()
479 log(self.serveur,canal,auteur,message+"[successful]")
480 else:
481 serv.privmsg(canal,"%s: crève !"%(auteur))
482 log(self.serveur,canal,auteur,message+"[failed]")
483 elif cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
484 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
485 elif cmd == "reload":
486 if auteur in self.ops:
487 log(self.serveur, canal, auteur, message+"[successful]")
488 self.reload(canal)
489 elif cmd in ["part","leave","dégage"]:
490 if auteur in self.ops and (not (canal in self.stay_channels)
491 or auteur in self.overops):
492 self.quitter(canal)
493 log(self.serveur,canal,auteur,message+"[successful]")
494 self.chanlist.remove(canal)
495 else:
496 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
497 log(self.serveur,canal,auteur,message+"[failed]")
498
499 elif cmd in ["deviens","pseudo"]:
500 if auteur in self.ops:
501 become=args
502 serv.nick(become)
503 log(self.serveur,canal,auteur,message+"[successful]")
504 elif cmd in ["coucou"] and not canal in self.quiet_channels:
505 serv.privmsg(canal,"%s: coucou"%(auteur))
506 elif cmd in ["ping"] and not canal in self.quiet_channels:
507 serv.privmsg(canal,"%s: pong"%(auteur))
508 elif cmd in ["déconnaissance","deconnaissance","énigme","enigme","encore","aco","moo","moo !","moo !"]:
509 if canal in self.play_channels:
510 if self.play_status.get(canal,[-1])[0]==0:
511 try:
512 self.start_enigme(serv,canal)
513 except RefuseError:
514 serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
515 else:
516 serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][1])).encode("utf8") )
517 else:
518 serv.privmsg(canal,"%s: pas ici…"%(auteur))
519 elif cmd in ["score","!score"]:
520 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
521 elif cmd in ["scores","!scores"]:
522 scores=self.get_scores().items()
523 # trie par score
524 scores.sort(lambda x,y:cmp(x[1],y[1]))
525 scores.reverse()
526 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
527 # trie par pseudo
528 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
529 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
530 elif cmd=="indice" and canal in self.play_channels:
531 self.give_indice(serv,canal,None)
532 elif is_tag(message) and not canal in self.quiet_channels:
533 if auteur in self.ops:
534 action=random.choice(config.tag_actions)
535 serv.action(canal,action.encode("utf8"))
536 self.quiet_channels.append(canal)
537 else:
538 answer=random.choice(config.tag_answers)
539 for ligne in answer.split("\n"):
540 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
541 elif "Bâille, cru aile ou orld" in message:
542 self.mourir(u"Un de mes easters eggs (non en fait j'en ai qu'un) a été découvert par %s !"%auteur)
543 else:
544 tryother=True
545 else:
546 tryother=True
547 if tryother:
548 if self.play_status.get(canal,[-1])[0] in [1,2]:
549 answer_regexp=self.play_status[canal][3]
550 answer=self.play_status[canal][4]
551 if reussi(message.decode("utf8"),answer,answer_regexp,auteur):
552 serv.privmsg(canal,(u"%s: bravo ! (C'était %s)"%(auteur,answer)).encode("utf8"))
553 log(self.serveur,canal,auteur+"$win",message)
554 self.add_score(auteur,1)
555 token=time.time()
556 self.play_status[canal]=[0,token]
557 serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,canal,token))
558
559 def on_kick(self,serv,ev):
560 auteur = irclib.nm_to_n(ev.source())
561 channel = ev.target()
562 victime = ev.arguments()[0]
563 raison = ev.arguments()[1]
564 if victime==self.nick:
565 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
566 time.sleep(5)
567 serv.join(channel)
568 self.update_activity(channel,force=True)
569 # on ne dit rien au rejoin
570 #l1,l2=config.kick_answers,config.kick_actions
571 #n1,n2=len(l1),len(l2)
572 #i=random.randrange(n1+n2)
573 #if i>=n1:
574 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
575 #else:
576 # serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
577
578 def quitter(self,chan,leave_message=None):
579 if leave_message==None:
580 leave_message=random.choice(config.leave_messages)
581 self.serv.part(chan,message=leave_message.encode("utf8"))
582
583 def mourir(self,quit_message=None):
584 if quit_message==None:
585 quit_message=random.choice(config.quit_messages)
586 self.die(msg=quit_message.encode("utf8"))
587
588 def get_scores(self):
589 f=open(config.score_file)
590 scores=pickle.load(f)
591 f.close()
592 return scores
593
594 def add_score(self,pseudo,value):
595 scores=self.get_scores()
596 if scores.has_key(pseudo):
597 scores[pseudo]+=value
598 else:
599 scores[pseudo]=value
600 self.save_scores(scores)
601
602 def save_scores(self,scores):
603 f=open(config.score_file,"w")
604 pickle.dump(scores,f)
605 f.close()
606
607 def _getnick(self):
608 return self.serv.get_nickname()
609 nick = property(_getnick)
610
611 def update_activity(self,canal="",pseudo="",message="",force=False):
612 if force or (not pseudo in config.idle_bots and all([not re.match(ignore, message) for ignore in config.idle_messages])):
613 self.last_activity[canal]=time.time()
614 def is_active(self,canal):
615 # Si on n'a pas d'info sur le chan, il est inactif
616 return time.time()-self.last_activity.get(canal, config.idle_time)<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()