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