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