]> gitweb.pimeys.fr Git - bots/saturnin.git/blob - saturnin.py
On peut demander le score d'un autre joueur
[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 for ligne in helpmsg.split("\n"):
208 serv.privmsg(auteur,ligne)
209 elif cmd=="join":
210 if auteur in self.ops:
211 if len(message)>1:
212 if message[1] in self.chanlist:
213 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
214 else:
215 serv.join(message[1])
216 self.chanlist.append(message[1])
217 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
218 log(self.serveur,"priv",auteur," ".join(message))
219 else:
220 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
221 else:
222 notunderstood=True
223 elif cmd=="leave":
224 if auteur in self.ops and len(message)>1:
225 if message[1] in self.chanlist:
226 if not (message[1] in self.stay_channels) or auteur in self.overops:
227 self.quitter(message[1]," ".join(message[2:]))
228 self.chanlist.remove(message[1])
229 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
230 else:
231 serv.privmsg(auteur,"Non, je reste !")
232 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
233 else:
234 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
235 else:
236 notunderstood=True
237 elif cmd=="stay":
238 if auteur in self.overops:
239 if len(message)>1:
240 if message[1] in self.stay_channels:
241 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
242 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
243 else:
244 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
245 self.stay_channels.append(message[1])
246 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
247 else:
248 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
249 else:
250 notunderstood=True
251 elif cmd=="nostay":
252 if auteur in self.overops:
253 if len(message)>1:
254 if message[1] in self.stay_channels:
255 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
256 self.stay_channels.remove(message[1])
257 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
258 else:
259 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
260 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
261
262 else:
263 notunderstood=True
264 elif cmd=="die":
265 if auteur in self.overops:
266 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
267 self.mourir()
268 else:
269 notunderstood=True
270 elif cmd=="quiet":
271 if auteur in self.ops:
272 if len(message)>1:
273 if message[1] in self.quiet_channels:
274 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
275 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
276 else:
277 self.quiet_channels.append(message[1])
278 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
279 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
280 else:
281 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
282 else:
283 notunderstood=True
284 elif cmd=="noquiet":
285 if auteur in self.ops:
286 if len(message)>1:
287 if message[1] in self.quiet_channels:
288 self.quiet_channels.remove(message[1])
289 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
290 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
291 else:
292 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
293 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
294 else:
295 notunderstood=True
296 elif cmd=="play":
297 if auteur in self.ops:
298 if len(message)>1:
299 if message[1] in self.play_channels:
300 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
301 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
302 else:
303 self.play_channels.append(message[1])
304 self.spawn(message[1], 1)
305 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
306 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
307 else:
308 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
309 else:
310 notunderstood=True
311 elif cmd=="noplay":
312 if auteur in self.ops:
313 if len(message)>1:
314 if message[1] in self.play_channels:
315 self.play_channels.remove(message[1])
316 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
317 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
318 else:
319 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
320 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
321 else:
322 notunderstood=True
323 elif cmd=="status":
324 if auteur in self.ops:
325 serv.privmsg(auteur,"Status : %s" % (self.status, ))
326 else:
327 notunderstood=True
328 elif cmd=="spawn":
329 if auteur in self.ops:
330 if len(message)>1:
331 if message[1] in self.play_channels:
332 # Le plus pratique pour pas s'embêter c'est de mettre un
333 # delay d'une seconde, comme ça .spawn() fait le boulot
334 self.spawn(message[1], time.time(), 1)
335 else:
336 serv.privmsg(auteur, "Je ne joue pas sur %s" % message[1])
337 else:
338 serv.privmsg(auteur, "Syntaxe : SPAWN <channel>")
339 else:
340 notunderstood=True
341 elif cmd=="say":
342 if auteur in self.overops and len(message)>2:
343 serv.privmsg(message[1]," ".join(message[2:]))
344 log(self.serveur,"priv",auteur," ".join(message))
345 elif len(message)<=2:
346 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
347 else:
348 notunderstood=True
349 elif cmd=="do":
350 if auteur in self.overops and len(message)>2:
351 serv.action(message[1]," ".join(message[2:]))
352 log(self.serveur,"priv",auteur," ".join(message))
353 elif len(message)<=2:
354 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
355 else:
356 notunderstood=True
357 elif cmd=="kick":
358 if auteur in self.overops and len(message)>2:
359 serv.kick(message[1],message[2]," ".join(message[3:]))
360 log(self.serveur,"priv",auteur," ".join(message))
361 elif len(message)<=2:
362 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
363 else:
364 notunderstood=True
365 elif cmd=="ops":
366 if auteur in self.overops:
367 serv.privmsg(auteur," ".join(self.ops))
368 else:
369 notunderstood=True
370 elif cmd=="overops":
371 if auteur in self.overops:
372 serv.privmsg(auteur," ".join(self.overops))
373 else:
374 notunderstood=True
375 elif cmd=="reload":
376 if auteur in self.ops:
377 self.reload(auteur)
378 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
379 else:
380 notunderstood=True
381 elif cmd=="score":
382 if len(message)>1:
383 if len(message) in [3,4] and message[1].lower()=="transfert":
384 scores=self.get_scores()
385 de,to=auteur,message[2]
386 value=scores.get(de,0)
387 if len(message)==4:
388 try:
389 asked=int(message[3])
390 except ValueError:
391 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
392 return
393 else:
394 asked=value
395 if value==0:
396 serv.privmsg(auteur,"Vous n'avez pas de points")
397 return
398 elif asked<=0:
399 serv.privmsg(auteur,"Bien tenté…")
400 return
401 elif asked>value:
402 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
403 return
404 else:
405 self.add_score(de,-asked)
406 self.add_score(to,asked)
407 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
408 elif len(message) == 2:
409 scores = self.get_scores()
410 asked = message[1]
411 if asked in scores:
412 serv.privmsg(auteur, "Score de %s : %s" % (asked, scores[asked]))
413 else:
414 serv.privmsg(auteur, "%s n'a encore jamais joué (avec succès)." % (asked,))
415 else:
416 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
417 else:
418 self.sendscore(auteur)
419 elif cmd=="scores":
420 if len(message)==1:
421 self.sendscores(auteur)
422 elif auteur in self.overops:
423 souscmd=message[1].lower()
424 if souscmd=="del":
425 if len(message)==3:
426 todelete=message[2]
427 scores=self.get_scores()
428 if scores.has_key(todelete):
429 del scores[todelete]
430 self.save_scores(scores)
431 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
432 else:
433 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
434 else:
435 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
436 elif souscmd in ["add","sub"]:
437 if len(message)==4:
438 toadd,val=message[2],message[3]
439 try:
440 val=int(val)
441 except ValueError:
442 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
443 return
444 if souscmd=="sub":
445 val=-val
446 self.add_score(toadd,val)
447 serv.privmsg(auteur,"Done")
448 else:
449 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
450 else:
451 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
452 else:
453 notunderstood=True
454 else:
455 notunderstood=True
456 if notunderstood:
457 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
458
459 def sendscore(self, to):
460 self.serv.privmsg(to, "Votre score : %s"%(self.get_scores().get(to,0)) )
461
462 def sendscores(self, to):
463 scores=self.get_scores().items()
464 # trie par score
465 scores.sort(lambda x,y:cmp(x[1],y[1]))
466 scores.reverse()
467 self.serv.privmsg(to, "Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
468 # trie par pseudo
469 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
470 self.serv.privmsg(to, "Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
471
472 def on_pubmsg(self, serv, ev):
473 if ignore_event(serv, ev):
474 return
475 auteur = irclib.nm_to_n(ev.source())
476 channel = ev.target()
477 message = ev.arguments()[0]
478 try:
479 test=bot_unicode(message)
480 except UnicodeBotError:
481 if config.utf8_trigger and not channel in self.quiet_channels:
482 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
483 return
484 pour_moi,message=self.pourmoi(serv,message)
485 if pour_moi and message.split()!=[]:
486 cmd=message.split()[0].lower()
487 try:
488 args=" ".join(message.split()[1:])
489 except:
490 args=""
491 if cmd in ["meurs","die","crève"]:
492 if auteur in self.overops:
493 log(self.serveur,channel,auteur,message+"[successful]")
494 self.mourir()
495 else:
496 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
497 log(self.serveur,channel,auteur,message+"[failed]")
498 elif cmd == "reload":
499 if auteur in self.ops:
500 log(self.serveur, channel, auteur, message+"[successful]")
501 self.reload(channel)
502 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
503 if auteur in self.ops and (not (channel in self.stay_channels)
504 or auteur in self.overops):
505 self.quitter(channel)
506 log(self.serveur,channel,auteur,message+"[successful]")
507 if channel in self.chanlist:
508 self.chanlist.remove(channel)
509 else:
510 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
511 log(self.serveur,channel,auteur,message+"[failed]")
512 elif cmd == "score":
513 self.sendscore(auteur)
514 elif cmd == "scores":
515 self.sendscores(auteur)
516 else:
517 if is_pan(message):
518 self.shot(channel, auteur)
519
520 def on_action(self, serv, ev):
521 if ignore_event(serv, ev):
522 return
523 action = ev.arguments()[0]
524 auteur = irclib.nm_to_n(ev.source())
525 channel = ev.target()
526 #~ try:
527 #~ test=bot_unicode(action)
528 #~ except UnicodeBotError:
529 #~ if config.utf8_trigger and not channel in self.quiet_channels:
530 #~ serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
531 #~ return
532 #~ mypseudo=self.nick
533
534 def on_kick(self,serv,ev):
535 auteur = irclib.nm_to_n(ev.source())
536 channel = ev.target()
537 victime = ev.arguments()[0]
538 raison = ev.arguments()[1]
539 if victime==self.nick:
540 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
541 time.sleep(2)
542 serv.join(channel)
543 #~ l1,l2=config.kick_answers,config.kick_actions
544 #~ n1,n2=len(l1),len(l2)
545 #~ i=random.randrange(n1+n2)
546 #~ if i>=n1:
547 #~ serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
548 #~ else:
549 #~ serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
550
551 def spawn(self, channel, timestamp, delay=0):
552 if channel in self.play_channels:
553 if delay>0:
554 self.serv.execute_delayed(delay, self.spawn, (channel, timestamp))
555 self.status[channel] = [1, timestamp]
556 else:
557 # on teste le timestamp pour pas s'emmêler dans les spawn
558 infos = self.status.get(channel, [0,0])
559 if infos == [1, timestamp]:
560 spawn_sentence = random.choice(config.canards) + random.choice(config.spawn_sentences)
561 self.serv.privmsg(channel, spawn_sentence.encode("utf8"))
562 self.status[channel] = [2, timestamp]
563 times_up_delay = random.randrange(*config.times_up_delays)
564 self.serv.execute_delayed(times_up_delay, self.too_slow, (channel, timestamp))
565
566 def too_slow(self, channel, timestamp):
567 infos = self.status.get(channel, [0,0])
568 if infos == [2, timestamp]:
569 self.serv.privmsg(channel, random.choice(config.times_up_sentences).encode("utf8"))
570 respawn_delay = random.randrange(*config.spawn_delays)
571 self.spawn(channel, time.time(), respawn_delay)
572
573 def shot(self, channel, auteur):
574 if self.status.get(channel, [0, 0])[0] == 2:
575 succeed = random.randrange(0,101) > config.proba_miss
576 if succeed:
577 self.serv.privmsg(channel, random.choice(config.killed_templates).format(auteur).encode("utf8"))
578 self.add_score(auteur, 1)
579 if random.randrange(0, 101) < config.proba_killed_sentence:
580 self.serv.privmsg(channel, random.choice(config.killed_sentences).encode("utf8"))
581 respawn_delay = random.randrange(*config.spawn_delays)
582 self.spawn(channel, time.time(), respawn_delay)
583 else:
584 self.serv.privmsg(channel, random.choice(config.miss_templates).format(auteur).encode("utf8"))
585 if random.randrange(0,101) < config.proba_miss_sentence:
586 self.serv.privmsg(channel, random.choice(config.miss_sentences).encode("utf8"))
587
588 def quitter(self,chan,leave_message=None):
589 if leave_message==None:
590 leave_message=random.choice(config.leave_messages)
591 self.serv.part(chan,message=leave_message.encode("utf8"))
592
593 def mourir(self):
594 quit_message=random.choice(config.quit_messages)
595 self.die(msg=quit_message.encode("utf8"))
596
597 def get_scores(self):
598 f=open(config.score_file)
599 scores=pickle.load(f)
600 f.close()
601 return scores
602
603 def add_score(self, pseudo, value):
604 scores=self.get_scores()
605 if scores.has_key(pseudo):
606 scores[pseudo]+=value
607 else:
608 scores[pseudo]=value
609 self.save_scores(scores)
610
611 def save_scores(self,scores):
612 f=open(config.score_file,"w")
613 pickle.dump(scores,f)
614 f.close()
615
616 def _getnick(self):
617 return self.serv.get_nickname()
618 nick=property(_getnick)
619
620 def reload(self, auteur=None):
621 reload(config)
622 if auteur in [None, "SIGHUP"]:
623 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
624 for to in config.report_bugs_to:
625 self.serv.privmsg(to, towrite)
626 log(self.serveur, towrite)
627 else:
628 self.serv.privmsg(auteur,"Config reloaded")
629
630 def start_as_daemon(self, outfile):
631 sys.stderr = Logger(outfile)
632 self.start()
633
634
635 class Logger(object):
636 """Pour écrire ailleurs que sur stdout"""
637 def __init__(self, filename="saturnin.full.log"):
638 self.filename = filename
639
640 def write(self, message):
641 f = open(self.filename, "a")
642 f.write(message)
643 f.close()
644
645
646 if __name__=="__main__":
647 import sys
648 if len(sys.argv)==1:
649 print "Usage : saturnin.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
650 print " --outfile sans --no-output ni --daemon n'a aucun effet"
651 exit(1)
652 serveur=sys.argv[1]
653 if "--daemon" in sys.argv:
654 thisfile = os.path.realpath(__file__)
655 thisdirectory = thisfile.rsplit("/", 1)[0]
656 os.chdir(thisdirectory)
657 daemon = True
658 else:
659 daemon = False
660 if "debug" in sys.argv or "--debug" in sys.argv:
661 debug=True
662 else:
663 debug=False
664 if "--no-output" in sys.argv or "--daemon" in sys.argv:
665 outfile = "/var/log/bots/saturnin.full.log"
666 for arg in sys.argv:
667 arg = arg.split("=")
668 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
669 outfile = arg[1]
670 sys.stdout = Logger(outfile)
671 if "--quiet" in sys.argv:
672 config.debug_stdout=False
673 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
674 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org",
675 "local":"localhost"}
676 try:
677 serveur=serveurs[serveur]
678 except KeyError:
679 print "Server Unknown : %s"%(serveur)
680 exit(404)
681 saturnin = Saturnin(serveur,debug)
682 # Si on reçoit un SIGHUP, on reload la config
683 def sighup_handler(signum, frame):
684 saturnin.reload("SIGHUP")
685 signal.signal(signal.SIGHUP, sighup_handler)
686 if daemon:
687 child_pid = os.fork()
688 if child_pid == 0:
689 os.setsid()
690 saturnin.start_as_daemon(outfile)
691 else:
692 # on enregistre le pid de saturnin
693 pidfile = "/var/run/bots/saturnin.pid"
694 for arg in sys.argv:
695 arg = arg.split("=")
696 if arg[0].strip('-') in ["pidfile"]:
697 pidfile = arg[1]
698 f = open(pidfile, "w")
699 f.write("%s\n" % child_pid)
700 f.close()
701 else:
702 saturnin.start()
703