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