]> gitweb.pimeys.fr Git - bots/salesman.git/blob - salesman.py
Au revoir jben
[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 return blackit and not exceptit
65
66 def reussi(message,answer,auteur):
67 if auteur in config.level3:
68 return answer in message
69 if auteur in config.level2:
70 return remplace_accents(answer) in message
71 else:
72 if re.match(".*"+remplace_accents(answer).lower(),remplace_accents(message).lower()):
73 return True
74
75 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
76 if case_sensitive:
77 chain=unicode(chain,"utf8")
78 else:
79 chain=unicode(chain,"utf8").lower()
80 allmatches="("+"|".join(matches)+")"
81 reg=(avant+allmatches+apres).lower()
82 o=re.match(reg,chain)
83 return o
84
85 def is_tag(chain):
86 return is_something(chain,config.tag_triggers)
87
88 class RefuseError(Exception):
89 pass
90
91 class Salesman(ircbot.SingleServerIRCBot):
92 def __init__(self,serveur,debug=False):
93 temporary_pseudo=config.pseudo+str(random.randrange(10000,100000))
94 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
95 temporary_pseudo,"Un bot irc.[flagellez 20-100, il le mérite]", 10)
96 self.debug=debug
97 self.serveur=serveur
98 self.overops=config.overops
99 self.ops=self.overops+config.ops
100 self.chanlist=config.chanlist
101 self.stay_channels=config.stay_channels
102 self.play_channels=config.play_channels
103 self.play_status={i:[0] for i in self.play_channels}
104 self.last_activity={}
105 self.quiet_channels=[]
106
107 def give_me_my_pseudo(self,serv):
108 serv.privmsg("NickServ","RECOVER %s %s"%(config.pseudo,config.password))
109 serv.privmsg("NickServ","RELEASE %s %s"%(config.pseudo,config.password))
110 time.sleep(0.3)
111 serv.nick(config.pseudo)
112
113 def on_welcome(self, serv, ev):
114 self.serv=serv # ça serv ira :)
115 self.give_me_my_pseudo(serv)
116 serv.privmsg("NickServ","identify %s"%(config.password))
117 log(self.serveur,"Connected")
118 if self.debug:
119 self.chanlist=["#bot"]
120 self.play_channels=["#bot"]
121 for c in self.chanlist:
122 log(self.serveur,"JOIN %s"%(c))
123 serv.join(c)
124 self.update_activity(c,force=True)
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 ["ville","capitale","pays","é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 return time.time()-self.last_activity[canal]<config.idle_time
600
601 def reload(self, auteur=None):
602 reload(config)
603 if auteur in [None, "SIGHUP"]:
604 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
605 for to in config.report_bugs_to:
606 self.serv.privmsg(to, towrite)
607 log(self.serveur, towrite)
608 else:
609 self.serv.privmsg(auteur,"Config reloaded")
610
611 def start_as_daemon(self, outfile):
612 sys.stderr = Logger(outfile)
613 self.start()
614
615
616 class Logger(object):
617 """Pour écrire ailleurs que sur stdout"""
618 def __init__(self, filename="salesman.full.log"):
619 self.filename = filename
620
621 def write(self, message):
622 f = open(self.filename, "a")
623 f.write(message)
624 f.close()
625
626
627 if __name__=="__main__":
628 import sys
629 if len(sys.argv)==1:
630 print "Usage : salesman.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
631 print " --outfile sans --no-output ni --daemon n'a aucun effet"
632 exit(1)
633 serveur=sys.argv[1]
634 if "--daemon" in sys.argv:
635 thisfile = os.path.realpath(__file__)
636 thisdirectory = thisfile.rsplit("/", 1)[0]
637 os.chdir(thisdirectory)
638 daemon = True
639 else:
640 daemon = False
641 if "debug" in sys.argv or "--debug" in sys.argv:
642 debug=True
643 else:
644 debug=False
645 if "--no-output" in sys.argv or "--daemon" in sys.argv:
646 outfile = "/var/log/bots/salesman.full.log"
647 for arg in sys.argv:
648 arg = arg.split("=")
649 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
650 outfile = arg[1]
651 sys.stdout = Logger(outfile)
652 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
653 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
654 try:
655 serveur=serveurs[serveur]
656 except KeyError:
657 print "Server Unknown : %s"%(serveur)
658 exit(404)
659 salesman=Salesman(serveur,debug)
660 # Si on reçoit un SIGHUP, on reload la config
661 def sighup_handler(signum, frame):
662 salesman.reload("SIGHUP")
663 signal.signal(signal.SIGHUP, sighup_handler)
664 if daemon:
665 child_pid = os.fork()
666 if child_pid == 0:
667 os.setsid()
668 salesman.start_as_daemon(outfile)
669 else:
670 # on enregistre le pid de salesman
671 pidfile = "/var/run/bots/salesman.pid"
672 for arg in sys.argv:
673 arg = arg.split("=")
674 if arg[0].strip('-') in ["pidfile"]:
675 pidfile = arg[1]
676 f = open(pidfile, "w")
677 f.write("%s\n" % child_pid)
678 f.close()
679 else:
680 salesman.start()
681