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