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