]> gitweb.pimeys.fr Git - bots/saturnin.git/blob - saturnin.py
1af87945acfbbb9193598b371acaa57df1da1144
[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 else:
519 if is_pan(message):
520 self.shot(channel, auteur)
521
522 def on_action(self, serv, ev):
523 if ignore_event(serv, ev):
524 return
525 action = ev.arguments()[0]
526 auteur = irclib.nm_to_n(ev.source())
527 channel = ev.target()
528 #~ try:
529 #~ test=bot_unicode(action)
530 #~ except UnicodeBotError:
531 #~ if config.utf8_trigger and not channel in self.quiet_channels:
532 #~ serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
533 #~ return
534 #~ mypseudo=self.nick
535
536 def on_kick(self,serv,ev):
537 auteur = irclib.nm_to_n(ev.source())
538 channel = ev.target()
539 victime = ev.arguments()[0]
540 raison = ev.arguments()[1]
541 if victime==self.nick:
542 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
543 time.sleep(2)
544 serv.join(channel)
545 #~ l1,l2=config.kick_answers,config.kick_actions
546 #~ n1,n2=len(l1),len(l2)
547 #~ i=random.randrange(n1+n2)
548 #~ if i>=n1:
549 #~ serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
550 #~ else:
551 #~ serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
552
553 def spawn(self, channel, timestamp, delay=0):
554 if channel in self.play_channels:
555 if delay>0:
556 self.serv.execute_delayed(delay, self.spawn, (channel, timestamp))
557 self.status[channel] = [1, timestamp]
558 else:
559 # on teste le timestamp pour pas s'emmêler dans les spawn
560 infos = self.status.get(channel, [0,0])
561 if infos == [1, timestamp]:
562 spawn_sentence = random.choice(config.canards) + random.choice(config.spawn_sentences)
563 self.serv.privmsg(channel, spawn_sentence.encode("utf8"))
564 self.status[channel] = [2, timestamp]
565 times_up_delay = random.randrange(*config.times_up_delays)
566 self.serv.execute_delayed(times_up_delay, self.too_slow, (channel, timestamp))
567
568 def too_slow(self, channel, timestamp):
569 infos = self.status.get(channel, [0,0])
570 if infos == [2, timestamp]:
571 self.serv.privmsg(channel, random.choice(config.times_up_sentences).encode("utf8"))
572 respawn_delay = random.randrange(*config.spawn_delays)
573 self.spawn(channel, time.time(), respawn_delay)
574
575 def shot(self, channel, auteur):
576 if self.status.get(channel, [0, 0])[0] == 2:
577 succeed = random.randrange(0,101) > config.proba_miss
578 if succeed:
579 self.serv.privmsg(channel, random.choice(config.killed_templates).format(auteur).encode("utf8"))
580 self.add_score(auteur, 1)
581 if random.randrange(0, 101) < config.proba_killed_sentence:
582 self.serv.privmsg(channel, random.choice(config.killed_sentences).encode("utf8"))
583 respawn_delay = random.randrange(*config.spawn_delays)
584 self.spawn(channel, time.time(), respawn_delay)
585 else:
586 self.serv.privmsg(channel, random.choice(config.miss_templates).format(auteur).encode("utf8"))
587 if random.randrange(0,101) < config.proba_miss_sentence:
588 self.serv.privmsg(channel, random.choice(config.miss_sentences).encode("utf8"))
589
590 def quitter(self,chan,leave_message=None):
591 if leave_message==None:
592 leave_message=random.choice(config.leave_messages)
593 self.serv.part(chan,message=leave_message.encode("utf8"))
594
595 def mourir(self):
596 quit_message=random.choice(config.quit_messages)
597 self.die(msg=quit_message.encode("utf8"))
598
599 def get_scores(self):
600 f=open(config.score_file)
601 scores=pickle.load(f)
602 f.close()
603 return scores
604
605 def add_score(self, pseudo, value):
606 scores=self.get_scores()
607 if scores.has_key(pseudo):
608 scores[pseudo]+=value
609 else:
610 scores[pseudo]=value
611 self.save_scores(scores)
612
613 def save_scores(self,scores):
614 f=open(config.score_file,"w")
615 pickle.dump(scores,f)
616 f.close()
617
618 def _getnick(self):
619 return self.serv.get_nickname()
620 nick=property(_getnick)
621
622 def reload(self, auteur=None):
623 reload(config)
624 if auteur in [None, "SIGHUP"]:
625 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
626 for to in config.report_bugs_to:
627 self.serv.privmsg(to, towrite)
628 log(self.serveur, towrite)
629 else:
630 self.serv.privmsg(auteur,"Config reloaded")
631
632 def start_as_daemon(self, outfile):
633 sys.stderr = Logger(outfile)
634 self.start()
635
636
637 class Logger(object):
638 """Pour écrire ailleurs que sur stdout"""
639 def __init__(self, filename="saturnin.full.log"):
640 self.filename = filename
641
642 def write(self, message):
643 f = open(self.filename, "a")
644 f.write(message)
645 f.close()
646
647
648 if __name__=="__main__":
649 import sys
650 if len(sys.argv)==1:
651 print "Usage : saturnin.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
652 print " --outfile sans --no-output ni --daemon n'a aucun effet"
653 exit(1)
654 serveur=sys.argv[1]
655 if "--daemon" in sys.argv:
656 thisfile = os.path.realpath(__file__)
657 thisdirectory = thisfile.rsplit("/", 1)[0]
658 os.chdir(thisdirectory)
659 daemon = True
660 else:
661 daemon = False
662 if "debug" in sys.argv or "--debug" in sys.argv:
663 debug=True
664 else:
665 debug=False
666 if "--no-output" in sys.argv or "--daemon" in sys.argv:
667 outfile = "/var/log/bots/saturnin.full.log"
668 for arg in sys.argv:
669 arg = arg.split("=")
670 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
671 outfile = arg[1]
672 sys.stdout = Logger(outfile)
673 if "--quiet" in sys.argv:
674 config.debug_stdout=False
675 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
676 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org",
677 "local":"localhost"}
678 try:
679 serveur=serveurs[serveur]
680 except KeyError:
681 print "Server Unknown : %s"%(serveur)
682 exit(404)
683 saturnin = Saturnin(serveur,debug)
684 # Si on reçoit un SIGHUP, on reload la config
685 def sighup_handler(signum, frame):
686 saturnin.reload("SIGHUP")
687 signal.signal(signal.SIGHUP, sighup_handler)
688 if daemon:
689 child_pid = os.fork()
690 if child_pid == 0:
691 os.setsid()
692 saturnin.start_as_daemon(outfile)
693 else:
694 # on enregistre le pid de saturnin
695 pidfile = "/var/run/bots/saturnin.pid"
696 for arg in sys.argv:
697 arg = arg.split("=")
698 if arg[0].strip('-') in ["pidfile"]:
699 pidfile = arg[1]
700 f = open(pidfile, "w")
701 f.write("%s\n" % child_pid)
702 f.close()
703 else:
704 saturnin.start()
705