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