]> gitweb.pimeys.fr Git - bots/historien.git/blob - historien.py
import os
[bots/historien.git] / historien.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3
4 # Codé par 20-100 le 25/05/12
5
6 # Un bot IRC qui pose des questions d'histoire
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 cast_as_date import *
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 config_password="EtTaMère,ElleEstNéeQuand?"
24 config_pseudo="historien"
25 config_chanlist=["#bot","#flood"]
26 config_play_channels=["#flood"]
27 config_stay_channels=["#flood","#bot"]
28 config_overops=["[20-100]","[20-100]_"]
29 config_ops=["PEB","Petite-Peste"]
30
31 config_source_file="dates.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
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=[]
53
54 config_debug_stdout = True
55 config_logfile_template="historien.%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"%s : %s quitte le serveur IRC"]
61
62 config_leave_messages=[u"%s : %s quitte le channel"]
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 config_score_annee=1
92 config_score_mois=3
93 config_score_jour=4
94
95 config_noscore=["[20-100]","[20-100]_"] # parce que 20-100 est nul en histoire
96
97 class GoodCentury(Exception):
98 pass
99
100 class GoodDeceny(Exception):
101 pass
102
103 def reussi(message,answer,auteur):
104 if auteur in config_level3:
105 return answer in message
106 if auteur in config_level2:
107 return answer in message
108 else:
109 try:
110 date=cast_as_date(message.lower().strip())
111 except ThisIsNotADate:
112 return False
113 realdate=map(lambda x:int(x), answer.split('/'))
114 realdate.reverse()
115 score=0
116 if date[0]==realdate[0]:
117 score=config_score_annee
118 if date[1]==realdate[1]:
119 score+=config_score_mois
120 if date[2]==realdate[2]:
121 score+=config_score_jour
122 elif date[0]/10 == realdate[0]/10:
123 raise GoodDeceny
124 elif date[0]/100 == realdate[0]/100:
125 raise GoodCentury
126 return score
127
128 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
129 if case_sensitive:
130 chain=unicode(chain,"utf8")
131 else:
132 chain=unicode(chain,"utf8").lower()
133 allmatches="("+"|".join(matches)+")"
134 reg=(avant+allmatches+apres).lower()
135 o=re.match(reg,chain)
136 return o
137
138 def is_tag(chain):
139 return is_something(chain,config_tag_triggers)
140
141 class RefuseError(Exception):
142 pass
143
144 class Historien(ircbot.SingleServerIRCBot):
145 def __init__(self,serveur,debug=False):
146 temporary_pseudo=config_pseudo+str(random.randrange(10000,100000))
147 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
148 temporary_pseudo,"Un bot irc qui a au moins l'agreg d'histoire", 10)
149 self.debug=debug
150 self.serveur=serveur
151 self.overops=config_overops
152 self.ops=self.overops+config_ops
153 self.chanlist=config_chanlist
154 self.stay_channels=config_stay_channels
155 self.play_channels=config_play_channels
156 self.play_status={i:[0] for i in self.play_channels}
157 self.last_activity={}
158 self.quiet_channels=[]
159
160 def give_me_my_pseudo(self,serv):
161 serv.privmsg("NickServ","RECOVER %s %s"%(config_pseudo,config_password))
162 serv.privmsg("NickServ","RELEASE %s %s"%(config_pseudo,config_password))
163 time.sleep(0.3)
164 serv.nick(config_pseudo)
165
166 def on_welcome(self, serv, ev):
167 self.serv=serv # ça serv ira :)
168 self.give_me_my_pseudo(serv)
169 serv.privmsg("NickServ","identify %s"%(config_password))
170 log(self.serveur,"Connected")
171 if self.debug:
172 self.chanlist=["#bot"]
173 self.play_channels=["#bot"]
174 for c in self.chanlist:
175 log(self.serveur,"JOIN %s"%(c))
176 serv.join(c)
177 self.update_activity(c,"") # la chaîne vide ne sera jamais un nom de bot et donc marchera toujours
178 for c in self.play_channels:
179 token=time.time()-3600
180 self.play_status[c]=[0,token]
181 serv.execute_delayed(random.randrange(ttrig),self.start_enigme,(serv,c,token))
182
183 def start_enigme(self,serv,channel,token=None):
184 # On reste silencieux si lechan n'est pas actif
185 if not self.is_active(channel):
186 serv.execute_delayed(ttrig*5,self.start_enigme,(serv,channel,token))
187 return
188 if self.play_status[channel][0]==0 and channel in self.play_channels:
189 ok="skip"
190 if token==self.play_status[channel][-1]:
191 ok="do_it"
192 if token==None:
193 if time.time() > self.play_status[channel][-1]+config_time_incompressible:
194 ok="do_it"
195 else:
196 ok="refuse"
197 if ok=="do_it":
198 date,evenement=self.get_enigme()
199 log(self.serveur,channel,u"$Date$".encode("utf8"),("%s : %s"%(date, evenement)).encode("utf8"))
200 serv.privmsg(channel,evenement.encode("utf8"))
201 token=time.time()
202 # le 0 est le flag "bon siècle" n'a pas encore été dit
203 self.play_status[channel]=[1,date,evenement,0,token]
204 serv.execute_delayed(random.randrange(ttrig*3,ttrig*5),self.give_indice,(serv,channel,token))
205 elif ok=="refuse":
206 raise RefuseError
207 def give_indice(self,serv,channel,token):
208 if self.play_status[channel][0]==1:
209 if token==None:
210 # c'est donc que l'indice a été demandé
211 if self.play_status[channel][-1]+config_time_incompressible_clue<time.time():
212 token=self.play_status[channel][-1]
213 if self.play_status[channel][-1]==token:
214 date=self.play_status[channel][1]
215 indice=date[:5]
216 serv.privmsg(channel,"indice : %s"%(indice).encode("utf8"))
217 self.play_status[channel][0]=2
218 serv.execute_delayed(random.randrange(ttrig*1,ttrig*3),self.give_answer,(serv,channel,token))
219 def give_answer(self,serv,channel,token):
220 if self.play_status[channel][0]==2 and self.play_status[channel][-1]==token:
221 date=self.play_status[channel][1]
222 serv.privmsg(channel,"C'était le %s"%(date).encode("utf8"))
223 token=time.time()
224 self.play_status[channel]=[0,token]
225 serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,channel,token))
226
227 def get_enigme(self):
228 # on récupère les dates
229 f=open(config_source_file)
230 l=f.readlines()
231 f.close()
232 l=[i.split(" : ",2) for i in l]
233 dates={int(i[0]):i[1:] for i in l}
234 # on va chercher combien de fois elles ont été jouées
235 played_file=get_config_played_file(self.serveur)
236 f=open(played_file)
237 t=f.read()
238 f.close()
239 l=re.findall("(.*):(.*)",t)
240 played={int(i[0]):int(i[1]) for i in l}
241 # on récupère le nombre d'occurrences le plus faible
242 mini=min(played.values())
243 # on choisit un id dans ceux qui ont ce nombre d'occurences
244 id_choisi=random.choice([k for k,v in played.items() if v==mini])
245 date,evenement=dates[id_choisi]
246 evenement=evenement.replace("\n","")
247 # on incrémente la choisie
248 played[id_choisi]+=1
249 # on enregistre le played_file
250 f=open(played_file,"w")
251 f.write("\n".join(["%-4s : %s"%(k,v) for k,v in played.items()]))
252 f.close()
253 return map(lambda x:x.decode("utf8"), [date,evenement])
254
255 def pourmoi(self, serv, message):
256 pseudo=self.nick
257 size=len(pseudo)
258 if message[:size]==pseudo and len(message)>size and message[size]==":":
259 return (True,message[size+1:].strip(" "))
260 else:
261 return (False,message)
262
263 def on_privmsg(self, serv, ev):
264 message=ev.arguments()[0]
265 auteur = irclib.nm_to_n(ev.source())
266 try:
267 test=bot_unicode(message)
268 except UnicodeBotError:
269 serv.privmsg(auteur,
270 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
271 return
272 message=message.split()
273 cmd=message[0].lower()
274 notunderstood=False
275 if cmd=="help":
276 helpmsg_default="""Liste des commandes :
277 HELP Affiche ce message d'aide
278 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
279 SCORES Affiche les scores"""
280 helpmsg_ops="""
281 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
282 LEAVE Faire quitter un channel
283 PLAY Passe un channel en mode "jouer"
284 NOPLAY Passe un channel en mode "ne pas jouer"
285 QUIET Se taire sur un channel
286 NOQUIET Opposé de QUIET"""
287 helpmsg_overops="""
288 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
289 SAY Fais envoyer un message sur un chan ou à une personne
290 STAY Ignorera les prochains LEAVE pour un chan
291 NOSTAY Opposé de STAY
292 STATUS Montre l'état courant
293 DIE Mourir"""
294 helpmsg=helpmsg_default
295 if auteur in self.ops:
296 helpmsg+=helpmsg_ops
297 if auteur in self.overops:
298 helpmsg+=helpmsg_overops
299 for ligne in helpmsg.split("\n"):
300 serv.privmsg(auteur,ligne)
301 elif cmd=="join":
302 if auteur in self.ops:
303 if len(message)>1:
304 if message[1] in self.chanlist:
305 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
306 else:
307 serv.join(message[1])
308 self.chanlist.append(message[1])
309 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
310 log(self.serveur,"priv",auteur," ".join(message))
311 else:
312 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
313 else:
314 notunderstood=True
315 elif cmd=="leave":
316 if auteur in self.ops and len(message)>1:
317 if message[1] in self.chanlist:
318 if not (message[1] in self.stay_channels) or auteur in self.overops:
319 self.quitter(message[1]," ".join(message[2:]))
320 self.chanlist.remove(message[1])
321 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
322 else:
323 serv.privmsg(auteur,"Non, je reste !")
324 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
325 else:
326 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
327 else:
328 notunderstood=True
329 elif cmd=="stay":
330 if auteur in self.overops:
331 if len(message)>1:
332 if message[1] in self.stay_channels:
333 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
334 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
335 else:
336 self.stay_channels.append(message[1])
337 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
338 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
339 else:
340 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
341 else:
342 notunderstood=True
343 elif cmd=="nostay":
344 if auteur in self.overops:
345 if len(message)>1:
346 if message[1] in self.stay_channels:
347 self.stay_channels.remove(message[1])
348 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
349 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
350 else:
351 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
352 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
353 else:
354 notunderstood=True
355 elif cmd=="play":
356 if auteur in self.ops:
357 if len(message)>1:
358 if message[1] in self.play_channels:
359 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
360 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
361 else:
362 self.play_channels.append(message[1])
363 self.play_status[message[1]]=[0,time.time()-3600]
364 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
365 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
366 else:
367 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
368 else:
369 notunderstood=True
370 elif cmd=="noplay":
371 if auteur in self.ops:
372 if len(message)>1:
373 if message[1] in self.play_channels:
374 self.play_channels.remove(message[1])
375 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
376 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
377 else:
378 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
379 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
380 else:
381 notunderstood=True
382 elif cmd=="quiet":
383 if auteur in self.ops:
384 if len(message)>1:
385 if message[1] in self.quiet_channels:
386 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
387 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
388 else:
389 self.quiet_channels.append(message[1])
390 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
391 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
392 else:
393 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
394 else:
395 notunderstood=True
396 elif cmd=="noquiet":
397 if auteur in self.ops:
398 if len(message)>1:
399 if message[1] in self.quiet_channels:
400 self.quiet_channels.remove(message[1])
401 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
402 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
403 else:
404 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
405 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
406 else:
407 notunderstood=True
408 elif cmd in ["states","status"]:
409 if auteur in self.overops:
410 for k in self.play_status.keys():
411 serv.privmsg(auteur,(u"%s : %s"%(k," | ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
412 elif cmd=="say":
413 if auteur in self.overops and len(message)>2:
414 serv.privmsg(message[1]," ".join(message[2:]))
415 log(self.serveur,"priv",auteur," ".join(message))
416 elif len(message)<=2:
417 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
418 else:
419 notunderstood=True
420 elif cmd=="die":
421 if auteur in self.overops:
422 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
423 self.mourir()
424 elif cmd=="score":
425 if len(message)>1:
426 if len(message) in [3,4] and message[1].lower()=="transfert":
427 scores=self.get_scores()
428 de,to=auteur,message[2]
429 value=scores.get(de,0)
430 if len(message)==4:
431 try:
432 asked=int(message[3])
433 except ValueError:
434 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
435 return
436 else:
437 asked=value
438 if value==0:
439 serv.privmsg(auteur,"Vous n'avez pas de points")
440 return
441 elif asked<=0:
442 serv.privmsg(auteur,"Bien tenté…")
443 return
444 elif asked>value:
445 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
446 return
447 else:
448 self.add_score(de,-asked)
449 self.add_score(to,asked)
450 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
451 else:
452 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
453 else:
454 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
455 elif cmd=="scores":
456 if len(message)==1:
457 scores=self.get_scores().items()
458 # trie par score
459 scores.sort(lambda x,y:cmp(x[1],y[1]))
460 scores.reverse()
461 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
462 # trie par pseudo
463 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
464 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
465 elif auteur in self.overops:
466 souscmd=message[1].lower()
467 if souscmd=="del":
468 if len(message)==3:
469 todelete=message[2]
470 scores=self.get_scores()
471 if scores.has_key(todelete):
472 del scores[todelete]
473 self.save_scores(scores)
474 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
475 else:
476 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
477 else:
478 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
479 elif souscmd in ["add","sub"]:
480 if len(message)==4:
481 toadd,val=message[2],message[3]
482 try:
483 val=int(val)
484 except ValueError:
485 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
486 return
487 if souscmd=="sub":
488 val=-val
489 self.add_score(toadd,val)
490 serv.privmsg(auteur,"Done")
491 else:
492 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
493 else:
494 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
495 else:
496 notunderstood=True
497 else:
498 notunderstood=True
499 if notunderstood:
500 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
501
502 def on_pubmsg(self, serv, ev):
503 auteur = irclib.nm_to_n(ev.source())
504 canal = ev.target()
505 message = ev.arguments()[0]
506 self.update_activity(canal,auteur)
507 try:
508 test=bot_unicode(message)
509 except UnicodeBotError:
510 if not canal in self.quiet_channels:
511 serv.privmsg(canal,
512 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
513 return
514 tryother=False
515 pour_moi,message=self.pourmoi(serv,message)
516 if pour_moi and message.split()!=[]:
517 cmd=message.split()[0].lower()
518 try:
519 args=" ".join(message.split()[1:])
520 except:
521 args=""
522 if cmd in ["meurs","die","crève"]:
523 if auteur in self.overops:
524 self.mourir()
525 log(self.serveur,canal,auteur,message+"[successful]")
526 else:
527 serv.privmsg(canal,"%s: crève !"%(auteur))
528 log(self.serveur,canal,auteur,message+"[failed]")
529 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
530 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
531 if cmd in ["part","leave","dégage"]:
532 if auteur in self.ops and (not (canal in self.stay_channels)
533 or auteur in self.overops):
534 self.quitter(canal)
535 log(self.serveur,canal,auteur,message+"[successful]")
536 self.chanlist.remove(canal)
537 else:
538 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
539 log(self.serveur,canal,auteur,message+"[failed]")
540
541 if cmd in ["deviens","pseudo"]:
542 if auteur in self.ops:
543 become=args
544 serv.nick(become)
545 log(self.serveur,canal,auteur,message+"[successful]")
546 if cmd in ["coucou"] and not canal in self.quiet_channels:
547 serv.privmsg(canal,"%s: coucou"%(auteur))
548 if cmd in ["ping"] and not canal in self.quiet_channels:
549 serv.privmsg(canal,"%s: pong"%(auteur))
550 if cmd in ["date","dates","histoire","énigme","enigme","encore"]:
551 if canal in self.play_channels:
552 if self.play_status.get(canal,[-1])[0]==0:
553 try:
554 self.start_enigme(serv,canal)
555 except RefuseError:
556 serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
557 else:
558 serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][2])).encode("utf8") )
559 else:
560 serv.privmsg(canal,"%s: pas ici…"%(auteur))
561 if cmd in ["score","!score"]:
562 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
563 if cmd in ["scores","!scores"]:
564 scores=self.get_scores().items()
565 # trie par score
566 scores.sort(lambda x,y:cmp(x[1],y[1]))
567 scores.reverse()
568 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
569 # trie par pseudo
570 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
571 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
572 if cmd=="indice" and canal in self.play_channels:
573 self.give_indice(serv,canal,None)
574 if is_tag(message) and not canal in self.quiet_channels:
575 if auteur in self.ops:
576 action=random.choice(config_tag_actions)
577 serv.action(canal,action.encode("utf8"))
578 self.quiet_channels.append(canal)
579 else:
580 answer=random.choice(config_tag_answers)
581 for ligne in answer.split("\n"):
582 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
583 else:
584 tryother=True
585 else:
586 tryother=True
587 if tryother:
588 if self.play_status.get(canal,[-1])[0] in [1,2]:
589 answer=self.play_status[canal][1]
590 flag_century=self.play_status[canal][3]
591 try:
592 score_obtenu=reussi(message.decode("utf8"),answer,auteur)
593 except GoodCentury:
594 if not flag_century:
595 serv.privmsg(canal,"%s: C'est le bon siècle, mais pas la bonne année, cherche encore ;)"%(auteur))
596 self.play_status[canal][3]=1
597 return
598 except GoodDeceny:
599 if flag_century in [0,1]:
600 serv.privmsg(canal,"%s: C'est la bonne décennie, mais pas la bonne année, encore un effort ;)"%(auteur))
601 self.play_status[canal][3]=2
602 return
603 if score_obtenu:
604 if self.play_status[canal][0]==1:
605 bonusmsg=u" [+bonus_mois"*(score_obtenu>config_score_annee)+u"+bonus_jour"*(score_obtenu>config_score_annee+config_score_mois)
606 else:
607 bonusmsg=""
608 score_obtenu=1
609 if bonusmsg:
610 bonusmsg+=u"]"
611 serv.privmsg(canal,(u"%s: bravo ! (C'était le %s)%s"%(auteur,answer,bonusmsg)).encode("utf8"))
612 log(self.serveur,canal,auteur+"$win",message)
613 if auteur in config_noscore:
614 score_obtenu=0
615 self.add_score(auteur,score_obtenu)
616 token=time.time()
617 self.play_status[canal]=[0,token]
618 serv.execute_delayed(random.randrange(Ttrig*5,Ttrig*10),self.start_enigme,(serv,canal,token))
619
620 def on_kick(self,serv,ev):
621 auteur = irclib.nm_to_n(ev.source())
622 channel = ev.target()
623 victime = ev.arguments()[0]
624 raison = ev.arguments()[1]
625 if victime==self.nick:
626 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
627 time.sleep(5)
628 serv.join(channel)
629 self.update_activity(channel,"")
630 # on ne dit rien au rejoin
631 #l1,l2=config_kick_answers,config_kick_actions
632 #n1,n2=len(l1),len(l2)
633 #i=random.randrange(n1+n2)
634 #if i>=n1:
635 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
636 #else:
637 # serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
638
639 def quitter(self,chan,leave_message=None):
640 if leave_message==None:
641 leave_message=random.choice(config_leave_messages)
642 try:
643 leave_message=leave_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
644 except:
645 pass
646 self.serv.part(chan,message=leave_message.encode("utf8"))
647
648 def mourir(self):
649 quit_message=random.choice(config_quit_messages)
650 try:
651 quit_message=quit_message%(time.strftime("le %d/%m/%Y à %T").decode("utf8"),self.nick)
652 except:
653 pass
654 self.die(msg=quit_message.encode("utf8"))
655
656 def get_scores(self):
657 f=open(config_score_file)
658 scores=pickle.load(f)
659 f.close()
660 return scores
661
662 def add_score(self,pseudo,value):
663 scores=self.get_scores()
664 if scores.has_key(pseudo):
665 scores[pseudo]+=value
666 else:
667 scores[pseudo]=value
668 self.save_scores(scores)
669
670 def save_scores(self,scores):
671 f=open(config_score_file,"w")
672 pickle.dump(scores,f)
673 f.close()
674
675 def _getnick(self):
676 return self.serv.get_nickname()
677 nick = property(_getnick)
678
679 def update_activity(self,canal,pseudo):
680 if not pseudo in config_idle_bots:
681 self.last_activity[canal]=time.time()
682 def is_active(self,canal):
683 return time.time()-self.last_activity[canal]<config_idle_time
684
685 def start_as_daemon(self, outfile):
686 sys.stderr = Logger(outfile)
687 self.start()
688
689
690 class Logger(object):
691 """Pour écrire ailleurs que sur stdout"""
692 def __init__(self, filename="historien.full.log"):
693 self.filename = filename
694
695 def write(self, message):
696 f = open(self.filename, "a")
697 f.write(message)
698 f.close()
699
700
701 if __name__=="__main__":
702 import sys
703 if len(sys.argv)==1:
704 print "Usage : historien.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
705 print " --outfile sans --no-output ni --daemon n'a aucun effet"
706 exit(1)
707 serveur=sys.argv[1]
708 if "--daemon" in sys.argv:
709 thisfile = os.path.realpath(__file__)
710 thisdirectory = thisfile.rsplit("/", 1)[0]
711 os.chdir(thisdirectory)
712 daemon = True
713 else:
714 daemon = False
715 if "debug" in sys.argv or "--debug" in sys.argv:
716 debug=True
717 else:
718 debug=False
719 if "--no-output" in sys.argv or "--daemon" in sys.argv:
720 outfile = "/var/log/bots/historien.full.log"
721 for arg in sys.argv:
722 arg = arg.split("=")
723 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
724 outfile = arg[1]
725 sys.stdout = Logger(outfile)
726 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
727 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
728 try:
729 serveur=serveurs[serveur]
730 except KeyError:
731 print "Server Unknown : %s"%(serveur)
732 exit(404)
733 historien=Historien(serveur,debug)
734 if daemon:
735 child_pid = os.fork()
736 if child_pid == 0:
737 os.setsid()
738 historien.start_as_daemon(outfile)
739 else:
740 # on enregistre le pid de historien
741 pidfile = "/var/run/bots/historien.pid"
742 for arg in sys.argv:
743 arg = arg.split("=")
744 if arg[0].strip('-') in ["pidfile"]:
745 pidfile = arg[1]
746 f = open(pidfile, "w")
747 f.write("%s\n" % child_pid)
748 f.close()
749 else:
750 historien.start()
751