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