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