]> gitweb.pimeys.fr Git - bots/salesman.git/blob - salesman.py
Boulet
[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 self.update_activity(c,force=True)
126 for c in self.play_channels:
127 token=time.time()-3600
128 self.play_status[c]=[0,token]
129 serv.execute_delayed(random.randrange(config.ttrig),self.start_enigme,(serv,c,token))
130
131 def start_enigme(self,serv,channel,token=None):
132 # On reste silencieux si lechan n'est pas actif
133 if not self.is_active(channel):
134 serv.execute_delayed(config.ttrig*5,self.start_enigme,(serv,channel,token))
135 return
136 if self.play_status[channel][0]==0 and channel in self.play_channels:
137 ok="skip"
138 if token==self.play_status[channel][-1]:
139 ok="do_it"
140 if token==None:
141 if time.time() > self.play_status[channel][-1]+config.time_incompressible:
142 ok="do_it"
143 else:
144 ok="refuse"
145 if ok=="do_it":
146 enigme,answer=self.get_enigme()
147 log(self.serveur,channel,u"$Énigme$".encode("utf8"),("%s | %s"%(enigme, answer)).encode("utf8"))
148 serv.privmsg(channel,enigme.encode("utf8"))
149 token=time.time()
150 self.play_status[channel]=[1,enigme,answer,token]
151 # ce bot n'a pas d'indices
152 serv.execute_delayed(random.randrange(config.ttrig*7,config.ttrig*10),self.give_answer,(serv,channel,token))
153 elif ok=="refuse":
154 raise RefuseError
155 def give_answer(self,serv,channel,token):
156 if self.play_status[channel][0]==1 and self.play_status[channel][-1]==token:
157 answer=self.play_status[channel][2]
158 serv.privmsg(channel,"C'était : %s"%(answer).encode("utf8"))
159 token=time.time()
160 self.play_status[channel]=[0,token]
161 serv.execute_delayed(random.randrange(config.Ttrig*5,config.Ttrig*10),self.start_enigme,(serv,channel,token))
162
163 def get_enigme(self):
164 # on récupère les capitales
165 f=open(config.source_file)
166 l=[i.strip("\n") for i in f.readlines()]
167 f.close()
168 l=[i.split(" | ") for i in l]
169 dec={int(i[0]):list(i[1:]) for i in l}
170 # on va chercher combien de fois elles ont été jouées
171 played_file=get_config_played_file(self.serveur)
172 f=open(played_file)
173 t=f.read()
174 f.close()
175 l=re.findall("(.*):(.*)",t)
176 played={int(i[0]):int(i[1]) for i in l}
177 # on récupère le nombre d'occurrences le plus faible
178 mini=min(played.values())
179 # on choisit un id dans ceux qui ont ce nombre d'occurences
180 id_choisi=random.choice([k for k,v in played.items() if v==mini])
181 enigme,answer=dec[id_choisi]
182 # on incrémente la choisie
183 played[id_choisi]+=1
184 # on enregistre le played_file
185 f=open(played_file,"w")
186 f.write("\n".join(["%-3s : %s"%(k,v) for k,v in played.items()]))
187 f.close()
188 return map(lambda x:x.decode("utf8"), [enigme,answer])
189
190 def pourmoi(self, serv, message):
191 pseudo=self.nick
192 size=len(pseudo)
193 if message[:size]==pseudo and len(message)>size and message[size]==":":
194 return (True,message[size+1:].strip(" "))
195 else:
196 return (False,message)
197
198 def on_privmsg(self, serv, ev):
199 if ignore_event(serv, ev):
200 return
201 message=ev.arguments()[0]
202 auteur = irclib.nm_to_n(ev.source())
203 try:
204 test=bot_unicode(message)
205 except UnicodeBotError:
206 if config.utf8_trigger:
207 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
208 return
209 message=message.split()
210 cmd=message[0].lower()
211 notunderstood=False
212 if cmd=="help":
213 helpmsg_default="""Liste des commandes :
214 HELP Affiche ce message d'aide
215 SCORE Affiche ton score (SCORE TRANSFERT <pseudo> [<n>] pour transférer des points)
216 SCORES Affiche les scores"""
217 helpmsg_ops="""
218 JOIN Faire rejoindre un channel (sans paramètres, donne la liste des chans actuels)
219 LEAVE Faire quitter un channel
220 PLAY Passe un channel en mode "jouer"
221 NOPLAY Passe un channel en mode "ne pas jouer"
222 QUIET Se taire sur un channel
223 NOQUIET Opposé de QUIET
224 RELOAD Me fait recharger la conf"""
225 helpmsg_overops="""
226 SCORES {DEL|ADD|SUB} Tu veux un dessin ?
227 SAY Fais envoyer un message sur un chan ou à une personne
228 STAY Ignorera les prochains LEAVE pour un chan
229 NOSTAY Opposé de STAY
230 STATUS Montre l'état courant
231 DIE Mourir"""
232 helpmsg=helpmsg_default
233 if auteur in self.ops:
234 helpmsg+=helpmsg_ops
235 if auteur in self.overops:
236 helpmsg+=helpmsg_overops
237 for ligne in helpmsg.split("\n"):
238 serv.privmsg(auteur,ligne)
239 elif cmd=="join":
240 if auteur in self.ops:
241 if len(message)>1:
242 if message[1] in self.chanlist:
243 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
244 else:
245 serv.join(message[1])
246 self.chanlist.append(message[1])
247 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
248 log(self.serveur,"priv",auteur," ".join(message))
249 else:
250 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
251 else:
252 notunderstood=True
253 elif cmd=="leave":
254 if auteur in self.ops and len(message)>1:
255 if message[1] in self.chanlist:
256 if not (message[1] in self.stay_channels) or auteur in self.overops:
257 self.quitter(message[1]," ".join(message[2:]))
258 self.chanlist.remove(message[1])
259 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
260 else:
261 serv.privmsg(auteur,"Non, je reste !")
262 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
263 else:
264 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
265 else:
266 notunderstood=True
267 elif cmd=="stay":
268 if auteur in self.overops:
269 if len(message)>1:
270 if message[1] in self.stay_channels:
271 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
272 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
273 else:
274 self.stay_channels.append(message[1])
275 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
276 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
277 else:
278 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
279 else:
280 notunderstood=True
281 elif cmd=="nostay":
282 if auteur in self.overops:
283 if len(message)>1:
284 if message[1] in self.stay_channels:
285 self.stay_channels.remove(message[1])
286 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
287 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
288 else:
289 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
290 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
291 else:
292 notunderstood=True
293 elif cmd=="play":
294 if auteur in self.ops:
295 if len(message)>1:
296 if message[1] in self.play_channels:
297 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
298 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
299 else:
300 self.play_channels.append(message[1])
301 self.play_status[message[1]]=[0,time.time()-3600]
302 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
303 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
304 else:
305 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
306 else:
307 notunderstood=True
308 elif cmd=="noplay":
309 if auteur in self.ops:
310 if len(message)>1:
311 if message[1] in self.play_channels:
312 self.play_channels.remove(message[1])
313 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
314 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
315 else:
316 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
317 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
318 else:
319 notunderstood=True
320 elif cmd=="quiet":
321 if auteur in self.ops:
322 if len(message)>1:
323 if message[1] in self.quiet_channels:
324 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
325 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
326 else:
327 self.quiet_channels.append(message[1])
328 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
329 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
330 else:
331 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
332 else:
333 notunderstood=True
334 elif cmd=="noquiet":
335 if auteur in self.ops:
336 if len(message)>1:
337 if message[1] in self.quiet_channels:
338 self.quiet_channels.remove(message[1])
339 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
340 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
341 else:
342 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
343 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
344 else:
345 notunderstood=True
346 elif cmd=="reload":
347 if auteur in self.ops:
348 self.reload(auteur)
349 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
350 else:
351 notunderstood=True
352 elif cmd in ["states","status"]:
353 if auteur in self.overops:
354 for k in self.play_status.keys():
355 serv.privmsg(auteur,(u"%s : %s"%(k," | ".join([unicode(i) for i in self.play_status[k]]))).encode("utf8") )
356 elif cmd=="say":
357 if auteur in self.overops and len(message)>2:
358 serv.privmsg(message[1]," ".join(message[2:]))
359 log(self.serveur,"priv",auteur," ".join(message))
360 elif len(message)<=2:
361 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
362 else:
363 notunderstood=True
364 elif cmd=="die":
365 if auteur in self.overops:
366 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
367 self.mourir()
368 elif cmd=="score":
369 if len(message)>1:
370 if len(message) in [3,4] and message[1].lower()=="transfert":
371 scores=self.get_scores()
372 de,to=auteur,message[2]
373 value=scores.get(de,0)
374 if len(message)==4:
375 try:
376 asked=int(message[3])
377 except ValueError:
378 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
379 return
380 else:
381 asked=value
382 if value==0:
383 serv.privmsg(auteur,"Vous n'avez pas de points")
384 return
385 elif asked<=0:
386 serv.privmsg(auteur,"Bien tenté…")
387 return
388 elif asked>value:
389 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
390 return
391 else:
392 self.add_score(de,-asked)
393 self.add_score(to,asked)
394 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
395 else:
396 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
397 else:
398 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
399 elif cmd=="scores":
400 if len(message)==1:
401 scores=self.get_scores().items()
402 # trie par score
403 scores.sort(lambda x,y:cmp(x[1],y[1]))
404 scores.reverse()
405 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
406 # trie par pseudo
407 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
408 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
409 elif auteur in self.overops:
410 souscmd=message[1].lower()
411 if souscmd=="del":
412 if len(message)==3:
413 todelete=message[2]
414 scores=self.get_scores()
415 if scores.has_key(todelete):
416 del scores[todelete]
417 self.save_scores(scores)
418 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
419 else:
420 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
421 else:
422 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
423 elif souscmd in ["add","sub"]:
424 if len(message)==4:
425 toadd,val=message[2],message[3]
426 try:
427 val=int(val)
428 except ValueError:
429 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
430 return
431 if souscmd=="sub":
432 val=-val
433 self.add_score(toadd,val)
434 serv.privmsg(auteur,"Done")
435 else:
436 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
437 else:
438 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
439 else:
440 notunderstood=True
441 else:
442 notunderstood=True
443 if notunderstood:
444 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
445
446 def on_pubmsg(self, serv, ev):
447 if ignore_event(serv, ev):
448 return
449 auteur = irclib.nm_to_n(ev.source())
450 canal = ev.target()
451 message = ev.arguments()[0]
452 self.update_activity(canal,auteur,message)
453 try:
454 test=bot_unicode(message)
455 except UnicodeBotError:
456 if config.utf8_trigger and not canal in self.quiet_channels:
457 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
458 return
459 tryother=False
460 pour_moi,message=self.pourmoi(serv,message)
461 if pour_moi and message.split()!=[]:
462 cmd=message.split()[0].lower()
463 try:
464 args=" ".join(message.split()[1:])
465 except:
466 args=""
467 if cmd in ["meurs","die","crève"]:
468 if auteur in self.overops:
469 self.mourir()
470 log(self.serveur,canal,auteur,message+"[successful]")
471 else:
472 serv.privmsg(canal,"%s: crève !"%(auteur))
473 log(self.serveur,canal,auteur,message+"[failed]")
474 elif cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
475 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
476 elif cmd == "reload":
477 if auteur in self.ops:
478 log(self.serveur, canal, auteur, message+"[successful]")
479 self.reload(canal)
480 elif cmd in ["part","leave","dégage"]:
481 if auteur in self.ops and (not (canal in self.stay_channels)
482 or auteur in self.overops):
483 self.quitter(canal)
484 log(self.serveur,canal,auteur,message+"[successful]")
485 self.chanlist.remove(canal)
486 else:
487 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
488 log(self.serveur,canal,auteur,message+"[failed]")
489
490 elif cmd in ["deviens","pseudo"]:
491 if auteur in self.ops:
492 become=args
493 serv.nick(become)
494 log(self.serveur,canal,auteur,message+"[successful]")
495 elif cmd in ["coucou"] and not canal in self.quiet_channels:
496 serv.privmsg(canal,"%s: coucou"%(auteur))
497 elif cmd in ["ping"] and not canal in self.quiet_channels:
498 serv.privmsg(canal,"%s: pong"%(auteur))
499 elif cmd in ["ville","capitale","pays","énigme","enigme","encore"]:
500 if canal in self.play_channels:
501 if self.play_status.get(canal,[-1])[0]==0:
502 try:
503 self.start_enigme(serv,canal)
504 except RefuseError:
505 serv.privmsg(canal,"%s: Je peux souffler une minute ?"%(auteur))
506 else:
507 serv.privmsg(canal,("%s: Rappel : %s"%(auteur,self.play_status[canal][1])).encode("utf8") )
508 else:
509 serv.privmsg(canal,"%s: pas ici…"%(auteur))
510 elif cmd in ["score","!score"]:
511 serv.privmsg(auteur,"Votre score : %s"%(self.get_scores().get(auteur,0)) )
512 elif cmd in ["scores","!scores"]:
513 scores=self.get_scores().items()
514 # trie par score
515 scores.sort(lambda x,y:cmp(x[1],y[1]))
516 scores.reverse()
517 serv.privmsg(auteur,"Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
518 # trie par pseudo
519 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
520 serv.privmsg(auteur,"Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
521 elif is_tag(message) and not canal in self.quiet_channels:
522 if auteur in self.ops:
523 action=random.choice(config.tag_actions)
524 serv.action(canal,action.encode("utf8"))
525 self.quiet_channels.append(canal)
526 else:
527 answer=random.choice(config.tag_answers)
528 for ligne in answer.split("\n"):
529 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
530 else:
531 tryother=True
532 else:
533 tryother=True
534 if tryother:
535 if self.play_status.get(canal,[-1])[0]==1:
536 answer=self.play_status[canal][2]
537 if reussi(message.decode("utf8"),answer,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,force=True)
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):
570 quit_message=random.choice(config.quit_messages)
571 self.die(msg=quit_message.encode("utf8"))
572
573 def get_scores(self):
574 f=open(config.score_file)
575 scores=pickle.load(f)
576 f.close()
577 return scores
578
579 def add_score(self,pseudo,value):
580 scores=self.get_scores()
581 if scores.has_key(pseudo):
582 scores[pseudo]+=value
583 else:
584 scores[pseudo]=value
585 self.save_scores(scores)
586
587 def save_scores(self,scores):
588 f=open(config.score_file,"w")
589 pickle.dump(scores,f)
590 f.close()
591
592 def _getnick(self):
593 return self.serv.get_nickname()
594 nick = property(_getnick)
595
596 def update_activity(self,canal="",pseudo="",message="",force=False):
597 if force or (not pseudo in config.idle_bots and all([not re.match(ignore, message) for ignore in config.idle_messages])):
598 self.last_activity[canal]=time.time()
599 def is_active(self,canal):
600 return time.time()-self.last_activity[canal]<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