]> gitweb.pimeys.fr Git - bots/saturnin.git/blob - saturnin.py
IRC, I hate you
[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, scores):
465 message = ''
466 for [nick, score] in scores:
467 score = '{}: {}'.format(nick, score)
468 if len(message) < config.MAX_LENGTH - len(score):
469 message += ' ' + score
470 else:
471 self.serv.privmsg(to, message)
472 message = score
473 if message:
474 self.serv.privmsg(to, message)
475
476 def sendscores(self, to):
477 scores=self.get_scores().items()
478 # trie par score
479 scores.sort(lambda x,y:cmp(x[1],y[1]))
480 scores.reverse()
481 self.serv.privmsg(to, "Scores by score : ")
482 self._sendscores(to, scores)
483 # trie par pseudo
484 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
485 self.serv.privmsg(to, "Scores by pseudo : ")
486 self._sendscores(to, scores)
487
488 def on_pubmsg(self, serv, ev):
489 if ignore_event(serv, ev):
490 return
491 auteur = irclib.nm_to_n(ev.source())
492 channel = ev.target()
493 message = ev.arguments()[0]
494 try:
495 test=bot_unicode(message)
496 except UnicodeBotError:
497 if config.utf8_trigger and not channel in self.quiet_channels:
498 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
499 return
500 pour_moi,message=self.pourmoi(serv,message)
501 if pour_moi and message.split()!=[]:
502 cmd=message.split()[0].lower()
503 try:
504 args=" ".join(message.split()[1:])
505 except:
506 args=""
507 if cmd in ["meurs","die","crève"]:
508 if auteur in self.overops:
509 log(self.serveur,channel,auteur,message+"[successful]")
510 self.mourir()
511 else:
512 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
513 log(self.serveur,channel,auteur,message+"[failed]")
514 elif cmd == "reload":
515 if auteur in self.ops:
516 log(self.serveur, channel, auteur, message+"[successful]")
517 self.reload(channel)
518 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
519 if auteur in self.ops and (not (channel in self.stay_channels)
520 or auteur in self.overops):
521 self.quitter(channel)
522 log(self.serveur,channel,auteur,message+"[successful]")
523 if channel in self.chanlist:
524 self.chanlist.remove(channel)
525 else:
526 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
527 log(self.serveur,channel,auteur,message+"[failed]")
528 elif cmd == "score":
529 self.sendscore(auteur)
530 elif cmd == "scores":
531 self.sendscores(auteur)
532 elif cmd in [u"deviens", u"pseudo"]:
533 if auteur in self.ops:
534 become = args
535 serv.nick(become)
536 log(self.serveur, channel, auteur, message + "[successful]")
537 if is_pan(message):
538 self.shot(channel, auteur)
539
540 def on_action(self, serv, ev):
541 if ignore_event(serv, ev):
542 return
543 action = ev.arguments()[0]
544 auteur = irclib.nm_to_n(ev.source())
545 channel = ev.target()
546 #~ try:
547 #~ test=bot_unicode(action)
548 #~ except UnicodeBotError:
549 #~ if config.utf8_trigger and not channel in self.quiet_channels:
550 #~ serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
551 #~ return
552 #~ mypseudo=self.nick
553
554 def on_kick(self,serv,ev):
555 auteur = irclib.nm_to_n(ev.source())
556 channel = ev.target()
557 victime = ev.arguments()[0]
558 raison = ev.arguments()[1]
559 if victime==self.nick:
560 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
561 time.sleep(2)
562 serv.join(channel)
563 #~ l1,l2=config.kick_answers,config.kick_actions
564 #~ n1,n2=len(l1),len(l2)
565 #~ i=random.randrange(n1+n2)
566 #~ if i>=n1:
567 #~ serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
568 #~ else:
569 #~ serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
570
571 def spawn(self, channel, timestamp, delay=0):
572 if channel in self.play_channels:
573 if delay>0:
574 self.serv.execute_delayed(delay, self.spawn, (channel, timestamp))
575 self.status[channel] = [1, timestamp]
576 else:
577 # on teste le timestamp pour pas s'emmêler dans les spawn
578 infos = self.status.get(channel, [0,0])
579 if infos == [1, timestamp]:
580 spawn_sentence = random.choice(config.canards) + random.choice(config.spawn_sentences)
581 self.serv.privmsg(channel, spawn_sentence.encode("utf8"))
582 self.status[channel] = [2, timestamp]
583 times_up_delay = random.randrange(*config.times_up_delays)
584 self.serv.execute_delayed(times_up_delay, self.too_slow, (channel, timestamp))
585
586 def too_slow(self, channel, timestamp):
587 infos = self.status.get(channel, [0,0])
588 if infos == [2, timestamp]:
589 self.serv.privmsg(channel, random.choice(config.times_up_sentences).encode("utf8"))
590 respawn_delay = random.randrange(*config.spawn_delays)
591 self.spawn(channel, time.time(), respawn_delay)
592
593 def shot(self, channel, auteur):
594 if self.status.get(channel, [0, 0])[0] == 2:
595 succeed = random.randrange(0,101) > config.proba_miss
596 if succeed:
597 self.serv.privmsg(channel, random.choice(config.killed_templates).format(auteur).encode("utf8"))
598 self.add_score(auteur, 1)
599 if random.randrange(0, 101) < config.proba_killed_sentence:
600 self.serv.privmsg(channel, random.choice(config.killed_sentences).encode("utf8"))
601 respawn_delay = random.randrange(*config.spawn_delays)
602 self.spawn(channel, time.time(), respawn_delay)
603 else:
604 self.serv.privmsg(channel, random.choice(config.miss_templates).format(auteur).encode("utf8"))
605 if random.randrange(0,101) < config.proba_miss_sentence:
606 self.serv.privmsg(channel, random.choice(config.miss_sentences).encode("utf8"))
607
608 def quitter(self,chan,leave_message=None):
609 if leave_message==None:
610 leave_message=random.choice(config.leave_messages)
611 self.serv.part(chan,message=leave_message.encode("utf8"))
612
613 def mourir(self):
614 quit_message=random.choice(config.quit_messages)
615 self.die(msg=quit_message.encode("utf8"))
616
617 def get_scores(self):
618 f=open(config.score_file)
619 scores=pickle.load(f)
620 f.close()
621 return scores
622
623 def add_score(self, pseudo, value):
624 scores=self.get_scores()
625 if scores.has_key(pseudo):
626 scores[pseudo]+=value
627 else:
628 scores[pseudo]=value
629 self.save_scores(scores)
630
631 def save_scores(self,scores):
632 f=open(config.score_file,"w")
633 pickle.dump(scores,f)
634 f.close()
635
636 def _getnick(self):
637 return self.serv.get_nickname()
638 nick=property(_getnick)
639
640 def reload(self, auteur=None):
641 reload(config)
642 if auteur in [None, "SIGHUP"]:
643 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
644 for to in config.report_bugs_to:
645 self.serv.privmsg(to, towrite)
646 log(self.serveur, towrite)
647 else:
648 self.serv.privmsg(auteur,"Config reloaded")
649
650 def start_as_daemon(self, outfile):
651 sys.stderr = Logger(outfile)
652 self.start()
653
654
655 class Logger(object):
656 """Pour écrire ailleurs que sur stdout"""
657 def __init__(self, filename="saturnin.full.log"):
658 self.filename = filename
659
660 def write(self, message):
661 f = open(self.filename, "a")
662 f.write(message)
663 f.close()
664
665
666 if __name__=="__main__":
667 import sys
668 if len(sys.argv)==1:
669 print "Usage : saturnin.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
670 print " --outfile sans --no-output ni --daemon n'a aucun effet"
671 exit(1)
672 serveur=sys.argv[1]
673 if "--daemon" in sys.argv:
674 thisfile = os.path.realpath(__file__)
675 thisdirectory = thisfile.rsplit("/", 1)[0]
676 os.chdir(thisdirectory)
677 daemon = True
678 else:
679 daemon = False
680 if "debug" in sys.argv or "--debug" in sys.argv:
681 debug=True
682 else:
683 debug=False
684 if "--no-output" in sys.argv or "--daemon" in sys.argv:
685 outfile = "/var/log/bots/saturnin.full.log"
686 for arg in sys.argv:
687 arg = arg.split("=")
688 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
689 outfile = arg[1]
690 sys.stdout = Logger(outfile)
691 if "--quiet" in sys.argv:
692 config.debug_stdout=False
693 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
694 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org",
695 "local":"localhost"}
696 try:
697 serveur=serveurs[serveur]
698 except KeyError:
699 print "Server Unknown : %s"%(serveur)
700 exit(404)
701 saturnin = Saturnin(serveur,debug)
702 # Si on reçoit un SIGHUP, on reload la config
703 def sighup_handler(signum, frame):
704 saturnin.reload("SIGHUP")
705 signal.signal(signal.SIGHUP, sighup_handler)
706 if daemon:
707 child_pid = os.fork()
708 if child_pid == 0:
709 os.setsid()
710 saturnin.start_as_daemon(outfile)
711 else:
712 # on enregistre le pid de saturnin
713 pidfile = "/var/run/bots/saturnin.pid"
714 for arg in sys.argv:
715 arg = arg.split("=")
716 if arg[0].strip('-') in ["pidfile"]:
717 pidfile = arg[1]
718 f = open(pidfile, "w")
719 f.write("%s\n" % child_pid)
720 f.close()
721 else:
722 saturnin.start()
723