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