]> gitweb.pimeys.fr Git - bots/saturnin.git/blob - saturnin.py
typos and misconfigurations
[bots/saturnin.git] / saturnin.py
1 #!/usr/bin/python
2 # -*- encoding: utf-8 -*-
3
4 # Codé par 20-100
5
6 # Un bot IRC pour remplacer le canard.
7 # parce que le canard, c'est le bien et que braice ne pong pas
8
9 import threading
10 import random
11 import time
12 import socket, ssl, json
13 import pickle
14 import re
15 import os
16 import signal
17 import sys
18 from commands import getstatusoutput as ex
19
20 # Oui, j'ai recodé ma version d'irclib pour pouvoir rattrapper les SIGHUP
21 sys.path.insert(0, "/home/vincent/scripts/python-myirclib")
22 import irclib
23 import ircbot
24
25 # on récupère la config
26 import config
27
28
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 def log(serveur,channel,auteur=None,message=None):
35 f=open(get_config_logfile(serveur),"a")
36 if auteur==message==None:
37 # alors c'est que c'est pas un channel mais juste une ligne de log
38 chain="%s %s"%(time.strftime("%F %T"),channel)
39 else:
40 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
41 f.write(chain+"\n")
42 if config.debug_stdout:
43 print chain
44 f.close()
45
46 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
47 if case_sensitive:
48 chain=unicode(chain,"utf8")
49 else:
50 chain=unicode(chain,"utf8").lower()
51 allmatches="("+"|".join(matches)+")"
52 reg=(avant+allmatches+apres).lower()
53 o=re.match(reg,chain)
54 return o
55
56 regexp_pan = re.compile(u".*(" + "|".join(config.killwords) + u").*")
57 def is_pan(chain):
58 return regexp_pan.match(unicode(chain,"utf8").lower())
59 def ignore_pan(serv, ev):
60 """Retourne ``True`` si il faut ignorer ce pan."""
61 for (blackmask, exceptmask) in config.ignored_pan_masks:
62 usermask = ev.source()
63 return bool(irclib.mask_matches(usermask, blackmask) and not irclib.mask_matches(usermask, exceptmask))
64
65 class UnicodeBotError(Exception):
66 pass
67 def bot_unicode(chain):
68 try:
69 unicode(chain,"utf8")
70 except UnicodeDecodeError as exc:
71 raise UnicodeBotError
72
73 class Saturnin(ircbot.SingleServerIRCBot):
74 def __init__(self,serveur,debug=False):
75 temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
76 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
77 temporary_pseudo,"Coin ? ©braice [mais 'faut frapper 20-100]", 10)
78 self.debug=debug
79 self.serveur=serveur
80 self.overops=config.overops
81 self.ops=self.overops+config.ops
82 self.chanlist=config.chanlist
83 self.stay_channels=config.stay_channels
84 self.quiet_channels=config.quiet_channels
85 self.play_channels=config.play_channels
86 self.status = { chan : [0, None] for chan in self.play_channels }
87 # 0 : pas de spawn prévu
88 # 1 : un spawn prévu,
89 # avec en deuxième paramètre le timestamp du moment où il a été déclenché (pas du moment où il se fera)
90 # 2 : alive
91 self.last_perdu=0
92
93 def give_me_my_pseudo(self,serv):
94 serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
95 serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
96 time.sleep(0.3)
97 serv.nick(config.irc_pseudo)
98
99 def on_welcome(self, serv, ev):
100 self.serv=serv # ça serv ira :)
101 self.give_me_my_pseudo(serv)
102 serv.privmsg("NickServ","IDENTIFY %s"%(config.irc_password))
103 log(self.serveur,"Connected")
104 if self.debug:
105 self.chanlist = self.play_channels = ["#bot"]
106 self.status = { chan : [0, 0] for chan in self.play_channels }
107 for c in self.chanlist:
108 log(self.serveur,"JOIN %s"%(c))
109 serv.join(c)
110 if c in self.play_channels:
111 spawn_delay = random.randrange(*config.spawn_delays)
112 self.spawn(c, time.time(), spawn_delay)
113
114 def pourmoi(self, serv, message):
115 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
116 pseudo=self.nick
117 size=len(pseudo)
118 if message[:size]==pseudo and len(message)>size and message[size]==":":
119 return (True,message[size+1:].lstrip(" "))
120 else:
121 return (False,message)
122
123 def on_privmsg(self, serv, ev):
124 message=ev.arguments()[0]
125 auteur = irclib.nm_to_n(ev.source())
126 try:
127 test=bot_unicode(message)
128 except UnicodeBotError:
129 if config.utf8_trigger:
130 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
131 return
132 message=message.split()
133 cmd=message[0].lower()
134 notunderstood=False
135 if cmd=="help":
136 helpdico={"help":["""HELP <commande>
137 Affiche de l'aide sur la commande""",None,None],
138 "score":["""SCORE
139 Affiche votre score""", None, None],
140 "scores":["""SCORES
141 Afficher tous les scores""", None, None],
142 "join": [None, """JOIN <channel>
143 Me fait rejoindre le channel""",None],
144 "leave": [None,"""LEAVE <channel>
145 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
146 "quiet": [None,"""QUIET <channel>
147 Me rend silencieux sur le channel.""",None],
148 "noquiet": [None,"""NOQUIET <channel>
149 Me rend la parole sur le channel.""",None],
150 "play": [None, """PLAY
151 Passe un channel en mode "jouer" """,None],
152 "noplay": [None, """NOPLAY
153 Passe un channel en mode "ne pas jouer" """,None],
154 "SPAWN": [None, """SPAWN <channel>
155 Me fait spawner sur le channel.""",None],
156 "reload": [None,"""RELOAD
157 Recharge la configuration.""",None],
158 "say": [None,None,"""SAY <channel> <message>
159 Me fait parler sur le channel."""],
160 "do": [None,None,"""DO <channel> <action>
161 Me fait faitre une action (/me) sur le channel."""],
162 "stay": [None,None,"""STAY <channel>
163 Ajoute le channel à ma stay_list."""],
164 "nostay": [None,None,"""NOSTAY <channel>
165 Retire le channel de ma stay_list."""],
166 "ops": [None,None,"""OPS
167 Affiche la liste des ops."""],
168 "overops": [None,None,"""OVEROPS
169 Affiche la liste des overops."""],
170 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
171 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
172 "die": [None,None,"""DIE
173 Me déconnecte du serveur IRC."""]
174 }
175 helpmsg_default="Liste des commandes disponibles :\nHELP SCORE SCORES"
176 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET PLAY NOPLAY SPAWN"
177 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
178 op,overop=auteur in self.ops, auteur in self.overops
179 if len(message)==1:
180 helpmsg=helpmsg_default
181 if op:
182 helpmsg+=helpmsg_ops
183 if overop:
184 helpmsg+=helpmsg_overops
185 else:
186 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
187 helpmsg=helpmsgs[0]
188 if op and helpmsgs[1]:
189 if helpmsg:
190 helpmsg+="\n"+helpmsgs[1]
191 else:
192 helpmsg=helpmsgs[1]
193 if overop and helpmsgs[2]:
194 if helpmsg:
195 helpmsg+="\n"+helpmsgs[2]
196 else:
197 helpmsg=helpmsgs[2]
198 for ligne in helpmsg.split("\n"):
199 serv.privmsg(auteur,ligne)
200 elif cmd=="join":
201 if auteur in self.ops:
202 if len(message)>1:
203 if message[1] in self.chanlist:
204 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
205 else:
206 serv.join(message[1])
207 self.chanlist.append(message[1])
208 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
209 log(self.serveur,"priv",auteur," ".join(message))
210 else:
211 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
212 else:
213 notunderstood=True
214 elif cmd=="leave":
215 if auteur in self.ops and len(message)>1:
216 if message[1] in self.chanlist:
217 if not (message[1] in self.stay_channels) or auteur in self.overops:
218 self.quitter(message[1]," ".join(message[2:]))
219 self.chanlist.remove(message[1])
220 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
221 else:
222 serv.privmsg(auteur,"Non, je reste !")
223 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
224 else:
225 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
226 else:
227 notunderstood=True
228 elif cmd=="stay":
229 if auteur in self.overops:
230 if len(message)>1:
231 if message[1] in self.stay_channels:
232 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
233 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
234 else:
235 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
236 self.stay_channels.append(message[1])
237 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
238 else:
239 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
240 else:
241 notunderstood=True
242 elif cmd=="nostay":
243 if auteur in self.overops:
244 if len(message)>1:
245 if message[1] in self.stay_channels:
246 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
247 self.stay_channels.remove(message[1])
248 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
249 else:
250 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
251 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
252
253 else:
254 notunderstood=True
255 elif cmd=="die":
256 if auteur in self.overops:
257 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
258 self.mourir()
259 else:
260 notunderstood=True
261 elif cmd=="quiet":
262 if auteur in self.ops:
263 if len(message)>1:
264 if message[1] in self.quiet_channels:
265 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
266 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
267 else:
268 self.quiet_channels.append(message[1])
269 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
270 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
271 else:
272 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
273 else:
274 notunderstood=True
275 elif cmd=="noquiet":
276 if auteur in self.ops:
277 if len(message)>1:
278 if message[1] in self.quiet_channels:
279 self.quiet_channels.remove(message[1])
280 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
281 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
282 else:
283 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
284 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
285 else:
286 notunderstood=True
287 elif cmd=="play":
288 if auteur in self.ops:
289 if len(message)>1:
290 if message[1] in self.play_channels:
291 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
292 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
293 else:
294 self.play_channels.append(message[1])
295 self.spawn(message[1], 1)
296 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
297 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
298 else:
299 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
300 else:
301 notunderstood=True
302 elif cmd=="noplay":
303 if auteur in self.ops:
304 if len(message)>1:
305 if message[1] in self.play_channels:
306 self.play_channels.remove(message[1])
307 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
308 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
309 else:
310 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
311 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
312 else:
313 notunderstood=True
314 elif cmd=="status":
315 if auteur in self.ops:
316 serv.privmsg(auteur,"Status : %s" % (self.status, ))
317 else:
318 notunderstood=True
319 elif cmd=="spawn":
320 if auteur in self.ops:
321 if len(message)>1:
322 if message[1] in self.play_channels:
323 # Le plus pratique pour pas s'embêter c'est de mettre un
324 # delay d'une seconde, comme ça .spawn() fait le boulot
325 self.spawn(message[1], time.time(), 1)
326 else:
327 serv.privmsg(auteur, "Je ne joue pas sur %s" % message[1])
328 else:
329 serv.privmsg(auteur, "Syntaxe : SPAWN <channel>")
330 else:
331 notunderstood=True
332 elif cmd=="say":
333 if auteur in self.overops and len(message)>2:
334 serv.privmsg(message[1]," ".join(message[2:]))
335 log(self.serveur,"priv",auteur," ".join(message))
336 elif len(message)<=2:
337 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
338 else:
339 notunderstood=True
340 elif cmd=="do":
341 if auteur in self.overops and len(message)>2:
342 serv.action(message[1]," ".join(message[2:]))
343 log(self.serveur,"priv",auteur," ".join(message))
344 elif len(message)<=2:
345 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
346 else:
347 notunderstood=True
348 elif cmd=="kick":
349 if auteur in self.overops and len(message)>2:
350 serv.kick(message[1],message[2]," ".join(message[3:]))
351 log(self.serveur,"priv",auteur," ".join(message))
352 elif len(message)<=2:
353 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
354 else:
355 notunderstood=True
356 elif cmd=="ops":
357 if auteur in self.overops:
358 serv.privmsg(auteur," ".join(self.ops))
359 else:
360 notunderstood=True
361 elif cmd=="overops":
362 if auteur in self.overops:
363 serv.privmsg(auteur," ".join(self.overops))
364 else:
365 notunderstood=True
366 elif cmd=="reload":
367 if auteur in self.ops:
368 self.reload(auteur)
369 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
370 else:
371 notunderstood=True
372 elif cmd=="score":
373 if len(message)>1:
374 if len(message) in [3,4] and message[1].lower()=="transfert":
375 scores=self.get_scores()
376 de,to=auteur,message[2]
377 value=scores.get(de,0)
378 if len(message)==4:
379 try:
380 asked=int(message[3])
381 except ValueError:
382 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
383 return
384 else:
385 asked=value
386 if value==0:
387 serv.privmsg(auteur,"Vous n'avez pas de points")
388 return
389 elif asked<=0:
390 serv.privmsg(auteur,"Bien tenté…")
391 return
392 elif asked>value:
393 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
394 return
395 else:
396 self.add_score(de,-asked)
397 self.add_score(to,asked)
398 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
399 else:
400 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
401 else:
402 self.sendscore(auteur)
403 elif cmd=="scores":
404 if len(message)==1:
405 self.sendscores(auteur)
406 elif auteur in self.overops:
407 souscmd=message[1].lower()
408 if souscmd=="del":
409 if len(message)==3:
410 todelete=message[2]
411 scores=self.get_scores()
412 if scores.has_key(todelete):
413 del scores[todelete]
414 self.save_scores(scores)
415 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
416 else:
417 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
418 else:
419 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
420 elif souscmd in ["add","sub"]:
421 if len(message)==4:
422 toadd,val=message[2],message[3]
423 try:
424 val=int(val)
425 except ValueError:
426 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
427 return
428 if souscmd=="sub":
429 val=-val
430 self.add_score(toadd,val)
431 serv.privmsg(auteur,"Done")
432 else:
433 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
434 else:
435 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
436 else:
437 notunderstood=True
438 else:
439 notunderstood=True
440 if notunderstood:
441 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
442
443 def sendscore(self, to):
444 self.serv.privmsg(to, "Votre score : %s"%(self.get_scores().get(to,0)) )
445
446 def sendscores(self, to):
447 scores=self.get_scores().items()
448 # trie par score
449 scores.sort(lambda x,y:cmp(x[1],y[1]))
450 scores.reverse()
451 self.serv.privmsg(to, "Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
452 # trie par pseudo
453 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
454 self.serv.privmsg(to, "Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
455
456 def on_pubmsg(self, serv, ev):
457 auteur = irclib.nm_to_n(ev.source())
458 if re.match(config.ignored_pseudos, auteur):
459 return
460 channel = ev.target()
461 message = ev.arguments()[0]
462 try:
463 test=bot_unicode(message)
464 except UnicodeBotError:
465 if config.utf8_trigger and not channel in self.quiet_channels:
466 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
467 return
468 pour_moi,message=self.pourmoi(serv,message)
469 if pour_moi and message.split()!=[]:
470 cmd=message.split()[0].lower()
471 try:
472 args=" ".join(message.split()[1:])
473 except:
474 args=""
475 if cmd in ["meurs","die","crève"]:
476 if auteur in self.overops:
477 log(self.serveur,channel,auteur,message+"[successful]")
478 self.mourir()
479 else:
480 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
481 log(self.serveur,channel,auteur,message+"[failed]")
482 elif cmd == "reload":
483 if auteur in self.ops:
484 log(self.serveur, channel, auteur, message+"[successful]")
485 self.reload(channel)
486 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
487 if auteur in self.ops and (not (channel in self.stay_channels)
488 or auteur in self.overops):
489 self.quitter(channel)
490 log(self.serveur,channel,auteur,message+"[successful]")
491 if channel in self.chanlist:
492 self.chanlist.remove(channel)
493 else:
494 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
495 log(self.serveur,channel,auteur,message+"[failed]")
496 elif cmd == "score":
497 self.sendscore(auteur)
498 elif cmd == "scores":
499 self.sendscores(auteur)
500 else:
501 if is_pan(message):
502 if ignore_pan(serv, ev):
503 return
504 self.shot(channel, auteur)
505
506 def on_action(self, serv, ev):
507 action = ev.arguments()[0]
508 auteur = irclib.nm_to_n(ev.source())
509 channel = ev.target()
510 #~ try:
511 #~ test=bot_unicode(action)
512 #~ except UnicodeBotError:
513 #~ if config.utf8_trigger and not channel in self.quiet_channels:
514 #~ serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
515 #~ return
516 #~ mypseudo=self.nick
517
518 def on_kick(self,serv,ev):
519 auteur = irclib.nm_to_n(ev.source())
520 channel = ev.target()
521 victime = ev.arguments()[0]
522 raison = ev.arguments()[1]
523 if victime==self.nick:
524 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
525 time.sleep(2)
526 serv.join(channel)
527 #~ l1,l2=config.kick_answers,config.kick_actions
528 #~ n1,n2=len(l1),len(l2)
529 #~ i=random.randrange(n1+n2)
530 #~ if i>=n1:
531 #~ serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
532 #~ else:
533 #~ serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
534
535 def spawn(self, channel, timestamp, delay=0):
536 if channel in self.play_channels:
537 if delay>0:
538 self.serv.execute_delayed(delay, self.spawn, (channel, timestamp))
539 self.status[channel] = [1, timestamp]
540 else:
541 # on teste le timestamp pour pas s'emmêler dans les spawn
542 infos = self.status.get(channel, [0,0])
543 if infos == [1, timestamp]:
544 spawn_sentence = random.choice(config.canards) + random.choice(config.spawn_sentences)
545 self.serv.privmsg(channel, spawn_sentence.encode("utf8"))
546 self.status[channel] = [2, timestamp]
547 times_up_delay = random.randrange(*config.times_up_delays)
548 self.serv.execute_delayed(times_up_delay, self.too_slow, (channel, timestamp))
549
550 def too_slow(self, channel, timestamp):
551 infos = self.status.get(channel, [0,0])
552 if infos == [2, timestamp]:
553 self.serv.privmsg(channel, random.choice(config.times_up_sentences).encode("utf8"))
554 respawn_delay = random.randrange(*config.spawn_delays)
555 self.spawn(channel, time.time(), respawn_delay)
556
557 def shot(self, channel, auteur):
558 if self.status.get(channel, [0, 0])[0] == 2:
559 succeed = random.randrange(0,101) > config.proba_miss
560 if succeed:
561 self.serv.privmsg(channel, random.choice(config.killed_templates).format(auteur).encode("utf8"))
562 self.add_score(auteur, 1)
563 if random.randrange(0, 101) < config.proba_killed_sentence:
564 self.serv.privmsg(channel, random.choice(config.killed_sentences).encode("utf8"))
565 respawn_delay = random.randrange(*config.spawn_delays)
566 self.spawn(channel, time.time(), respawn_delay)
567 else:
568 self.serv.privmsg(channel, random.choice(config.miss_templates).format(auteur).encode("utf8"))
569 if random.randrange(0,101) < config.proba_miss_sentence:
570 self.serv.privmsg(channel, random.choice(config.miss_sentences).encode("utf8"))
571
572 def quitter(self,chan,leave_message=None):
573 if leave_message==None:
574 leave_message=random.choice(config.leave_messages)
575 self.serv.part(chan,message=leave_message.encode("utf8"))
576
577 def mourir(self):
578 quit_message=random.choice(config.quit_messages)
579 self.die(msg=quit_message.encode("utf8"))
580
581 def get_scores(self):
582 f=open(config.score_file)
583 scores=pickle.load(f)
584 f.close()
585 return scores
586
587 def add_score(self, pseudo, value):
588 scores=self.get_scores()
589 if scores.has_key(pseudo):
590 scores[pseudo]+=value
591 else:
592 scores[pseudo]=value
593 self.save_scores(scores)
594
595 def save_scores(self,scores):
596 f=open(config.score_file,"w")
597 pickle.dump(scores,f)
598 f.close()
599
600 def _getnick(self):
601 return self.serv.get_nickname()
602 nick=property(_getnick)
603
604 def reload(self, auteur=None):
605 reload(config)
606 if auteur in [None, "SIGHUP"]:
607 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
608 for to in config.report_bugs_to:
609 self.serv.privmsg(to, towrite)
610 log(self.serveur, towrite)
611 else:
612 self.serv.privmsg(auteur,"Config reloaded")
613
614 def start_as_daemon(self, outfile):
615 sys.stderr = Logger(outfile)
616 self.start()
617
618
619 class Logger(object):
620 """Pour écrire ailleurs que sur stdout"""
621 def __init__(self, filename="saturnin.full.log"):
622 self.filename = filename
623
624 def write(self, message):
625 f = open(self.filename, "a")
626 f.write(message)
627 f.close()
628
629
630 if __name__=="__main__":
631 import sys
632 if len(sys.argv)==1:
633 print "Usage : saturnin.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
634 print " --outfile sans --no-output ni --daemon n'a aucun effet"
635 exit(1)
636 serveur=sys.argv[1]
637 if "--daemon" in sys.argv:
638 thisfile = os.path.realpath(__file__)
639 thisdirectory = thisfile.rsplit("/", 1)[0]
640 os.chdir(thisdirectory)
641 daemon = True
642 else:
643 daemon = False
644 if "debug" in sys.argv or "--debug" in sys.argv:
645 debug=True
646 else:
647 debug=False
648 if "--no-output" in sys.argv or "--daemon" in sys.argv:
649 outfile = "/var/log/bots/saturnin.full.log"
650 for arg in sys.argv:
651 arg = arg.split("=")
652 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
653 outfile = arg[1]
654 sys.stdout = Logger(outfile)
655 if "--quiet" in sys.argv:
656 config.debug_stdout=False
657 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
658 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org",
659 "local":"localhost"}
660 try:
661 serveur=serveurs[serveur]
662 except KeyError:
663 print "Server Unknown : %s"%(serveur)
664 exit(404)
665 saturnin = Saturnin(serveur,debug)
666 # Si on reçoit un SIGHUP, on reload la config
667 def sighup_handler(signum, frame):
668 saturnin.reload("SIGHUP")
669 signal.signal(signal.SIGHUP, sighup_handler)
670 if daemon:
671 child_pid = os.fork()
672 if child_pid == 0:
673 os.setsid()
674 saturnin.start_as_daemon(outfile)
675 else:
676 # on enregistre le pid de saturnin
677 pidfile = "/var/run/bots/saturnin.pid"
678 for arg in sys.argv:
679 arg = arg.split("=")
680 if arg[0].strip('-') in ["pidfile"]:
681 pidfile = arg[1]
682 f = open(pidfile, "w")
683 f.write("%s\n" % child_pid)
684 f.close()
685 else:
686 saturnin.start()
687