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