]> gitweb.pimeys.fr Git - bots/saturnin.git/blob - saturnin.py
Au revoir jben
[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 return blackit and not exceptit
70
71 class UnicodeBotError(Exception):
72 pass
73 def bot_unicode(chain):
74 try:
75 unicode(chain,"utf8")
76 except UnicodeDecodeError as exc:
77 raise UnicodeBotError
78
79 class Saturnin(ircbot.SingleServerIRCBot):
80 def __init__(self,serveur,debug=False):
81 temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
82 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
83 temporary_pseudo,"Coin ? ©braice [mais 'faut frapper 20-100]", 10)
84 self.debug=debug
85 self.serveur=serveur
86 self.overops=config.overops
87 self.ops=self.overops+config.ops
88 self.chanlist=config.chanlist
89 self.stay_channels=config.stay_channels
90 self.quiet_channels=config.quiet_channels
91 self.play_channels=config.play_channels
92 self.status = { chan : [0, None] for chan in self.play_channels }
93 # 0 : pas de spawn prévu
94 # 1 : un spawn prévu,
95 # avec en deuxième paramètre le timestamp du moment où il a été déclenché (pas du moment où il se fera)
96 # 2 : alive
97 self.last_perdu=0
98
99 def give_me_my_pseudo(self,serv):
100 serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
101 serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
102 time.sleep(0.3)
103 serv.nick(config.irc_pseudo)
104
105 def on_welcome(self, serv, ev):
106 self.serv=serv # ça serv ira :)
107 self.give_me_my_pseudo(serv)
108 serv.privmsg("NickServ","IDENTIFY %s"%(config.irc_password))
109 log(self.serveur,"Connected")
110 if self.debug:
111 self.chanlist = self.play_channels = ["#bot"]
112 self.status = { chan : [0, 0] for chan in self.play_channels }
113 for c in self.chanlist:
114 log(self.serveur,"JOIN %s"%(c))
115 serv.join(c)
116 if c in self.play_channels:
117 spawn_delay = random.randrange(*config.spawn_delays)
118 self.spawn(c, time.time(), spawn_delay)
119
120 def pourmoi(self, serv, message):
121 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
122 pseudo=self.nick
123 size=len(pseudo)
124 if message[:size]==pseudo and len(message)>size and message[size]==":":
125 return (True,message[size+1:].lstrip(" "))
126 else:
127 return (False,message)
128
129 def on_privmsg(self, serv, ev):
130 if ignore_event(serv, ev):
131 return
132 message=ev.arguments()[0]
133 auteur = irclib.nm_to_n(ev.source())
134 try:
135 test=bot_unicode(message)
136 except UnicodeBotError:
137 if config.utf8_trigger:
138 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
139 return
140 message=message.split()
141 cmd=message[0].lower()
142 notunderstood=False
143 if cmd=="help":
144 helpdico={"help":["""HELP <commande>
145 Affiche de l'aide sur la commande""",None,None],
146 "score":["""SCORE
147 Affiche votre score""", None, None],
148 "scores":["""SCORES
149 Afficher tous les scores""", None, None],
150 "join": [None, """JOIN <channel>
151 Me fait rejoindre le channel""",None],
152 "leave": [None,"""LEAVE <channel>
153 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
154 "quiet": [None,"""QUIET <channel>
155 Me rend silencieux sur le channel.""",None],
156 "noquiet": [None,"""NOQUIET <channel>
157 Me rend la parole sur le channel.""",None],
158 "play": [None, """PLAY
159 Passe un channel en mode "jouer" """,None],
160 "noplay": [None, """NOPLAY
161 Passe un channel en mode "ne pas jouer" """,None],
162 "SPAWN": [None, """SPAWN <channel>
163 Me fait spawner sur le channel.""",None],
164 "reload": [None,"""RELOAD
165 Recharge la configuration.""",None],
166 "say": [None,None,"""SAY <channel> <message>
167 Me fait parler sur le channel."""],
168 "do": [None,None,"""DO <channel> <action>
169 Me fait faitre une action (/me) sur le channel."""],
170 "stay": [None,None,"""STAY <channel>
171 Ajoute le channel à ma stay_list."""],
172 "nostay": [None,None,"""NOSTAY <channel>
173 Retire le channel de ma stay_list."""],
174 "ops": [None,None,"""OPS
175 Affiche la liste des ops."""],
176 "overops": [None,None,"""OVEROPS
177 Affiche la liste des overops."""],
178 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
179 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
180 "die": [None,None,"""DIE
181 Me déconnecte du serveur IRC."""]
182 }
183 helpmsg_default="Liste des commandes disponibles :\nHELP SCORE SCORES"
184 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET PLAY NOPLAY SPAWN"
185 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
186 op,overop=auteur in self.ops, auteur in self.overops
187 if len(message)==1:
188 helpmsg=helpmsg_default
189 if op:
190 helpmsg+=helpmsg_ops
191 if overop:
192 helpmsg+=helpmsg_overops
193 else:
194 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
195 helpmsg=helpmsgs[0]
196 if op and helpmsgs[1]:
197 if helpmsg:
198 helpmsg+="\n"+helpmsgs[1]
199 else:
200 helpmsg=helpmsgs[1]
201 if overop and helpmsgs[2]:
202 if helpmsg:
203 helpmsg+="\n"+helpmsgs[2]
204 else:
205 helpmsg=helpmsgs[2]
206 for ligne in helpmsg.split("\n"):
207 serv.privmsg(auteur,ligne)
208 elif cmd=="join":
209 if auteur in self.ops:
210 if len(message)>1:
211 if message[1] in self.chanlist:
212 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
213 else:
214 serv.join(message[1])
215 self.chanlist.append(message[1])
216 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
217 log(self.serveur,"priv",auteur," ".join(message))
218 else:
219 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
220 else:
221 notunderstood=True
222 elif cmd=="leave":
223 if auteur in self.ops and len(message)>1:
224 if message[1] in self.chanlist:
225 if not (message[1] in self.stay_channels) or auteur in self.overops:
226 self.quitter(message[1]," ".join(message[2:]))
227 self.chanlist.remove(message[1])
228 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
229 else:
230 serv.privmsg(auteur,"Non, je reste !")
231 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
232 else:
233 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
234 else:
235 notunderstood=True
236 elif cmd=="stay":
237 if auteur in self.overops:
238 if len(message)>1:
239 if message[1] in self.stay_channels:
240 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
241 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
242 else:
243 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
244 self.stay_channels.append(message[1])
245 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
246 else:
247 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
248 else:
249 notunderstood=True
250 elif cmd=="nostay":
251 if auteur in self.overops:
252 if len(message)>1:
253 if message[1] in self.stay_channels:
254 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
255 self.stay_channels.remove(message[1])
256 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
257 else:
258 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
259 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
260
261 else:
262 notunderstood=True
263 elif cmd=="die":
264 if auteur in self.overops:
265 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
266 self.mourir()
267 else:
268 notunderstood=True
269 elif cmd=="quiet":
270 if auteur in self.ops:
271 if len(message)>1:
272 if message[1] in self.quiet_channels:
273 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
274 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
275 else:
276 self.quiet_channels.append(message[1])
277 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
278 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
279 else:
280 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
281 else:
282 notunderstood=True
283 elif cmd=="noquiet":
284 if auteur in self.ops:
285 if len(message)>1:
286 if message[1] in self.quiet_channels:
287 self.quiet_channels.remove(message[1])
288 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
289 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
290 else:
291 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
292 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
293 else:
294 notunderstood=True
295 elif cmd=="play":
296 if auteur in self.ops:
297 if len(message)>1:
298 if message[1] in self.play_channels:
299 serv.privmsg(auteur,"Je play déjà sur %s."%(message[1]))
300 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
301 else:
302 self.play_channels.append(message[1])
303 self.spawn(message[1], 1)
304 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
305 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
306 else:
307 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
308 else:
309 notunderstood=True
310 elif cmd=="noplay":
311 if auteur in self.ops:
312 if len(message)>1:
313 if message[1] in self.play_channels:
314 self.play_channels.remove(message[1])
315 serv.privmsg(auteur,"Play channels : "+" ".join(self.play_channels))
316 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
317 else:
318 serv.privmsg(auteur,"Je ne play pas sur %s."%(message[1]))
319 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
320 else:
321 notunderstood=True
322 elif cmd=="status":
323 if auteur in self.ops:
324 serv.privmsg(auteur,"Status : %s" % (self.status, ))
325 else:
326 notunderstood=True
327 elif cmd=="spawn":
328 if auteur in self.ops:
329 if len(message)>1:
330 if message[1] in self.play_channels:
331 # Le plus pratique pour pas s'embêter c'est de mettre un
332 # delay d'une seconde, comme ça .spawn() fait le boulot
333 self.spawn(message[1], time.time(), 1)
334 else:
335 serv.privmsg(auteur, "Je ne joue pas sur %s" % message[1])
336 else:
337 serv.privmsg(auteur, "Syntaxe : SPAWN <channel>")
338 else:
339 notunderstood=True
340 elif cmd=="say":
341 if auteur in self.overops and len(message)>2:
342 serv.privmsg(message[1]," ".join(message[2:]))
343 log(self.serveur,"priv",auteur," ".join(message))
344 elif len(message)<=2:
345 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
346 else:
347 notunderstood=True
348 elif cmd=="do":
349 if auteur in self.overops and len(message)>2:
350 serv.action(message[1]," ".join(message[2:]))
351 log(self.serveur,"priv",auteur," ".join(message))
352 elif len(message)<=2:
353 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
354 else:
355 notunderstood=True
356 elif cmd=="kick":
357 if auteur in self.overops and len(message)>2:
358 serv.kick(message[1],message[2]," ".join(message[3:]))
359 log(self.serveur,"priv",auteur," ".join(message))
360 elif len(message)<=2:
361 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
362 else:
363 notunderstood=True
364 elif cmd=="ops":
365 if auteur in self.overops:
366 serv.privmsg(auteur," ".join(self.ops))
367 else:
368 notunderstood=True
369 elif cmd=="overops":
370 if auteur in self.overops:
371 serv.privmsg(auteur," ".join(self.overops))
372 else:
373 notunderstood=True
374 elif cmd=="reload":
375 if auteur in self.ops:
376 self.reload(auteur)
377 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
378 else:
379 notunderstood=True
380 elif cmd=="score":
381 if len(message)>1:
382 if len(message) in [3,4] and message[1].lower()=="transfert":
383 scores=self.get_scores()
384 de,to=auteur,message[2]
385 value=scores.get(de,0)
386 if len(message)==4:
387 try:
388 asked=int(message[3])
389 except ValueError:
390 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
391 return
392 else:
393 asked=value
394 if value==0:
395 serv.privmsg(auteur,"Vous n'avez pas de points")
396 return
397 elif asked<=0:
398 serv.privmsg(auteur,"Bien tenté…")
399 return
400 elif asked>value:
401 serv.privmsg(auteur,"Vous n'avez que %s points"%(value))
402 return
403 else:
404 self.add_score(de,-asked)
405 self.add_score(to,asked)
406 serv.privmsg(auteur,"Transfert de %s points de %s à %s"%(asked,de,to))
407 else:
408 serv.privmsg(auteur,"Syntaxe : SCORE TRANSFERT <pseudo> [<n>]")
409 else:
410 self.sendscore(auteur)
411 elif cmd=="scores":
412 if len(message)==1:
413 self.sendscores(auteur)
414 elif auteur in self.overops:
415 souscmd=message[1].lower()
416 if souscmd=="del":
417 if len(message)==3:
418 todelete=message[2]
419 scores=self.get_scores()
420 if scores.has_key(todelete):
421 del scores[todelete]
422 self.save_scores(scores)
423 serv.privmsg(auteur,"Score de %s supprimé"%(todelete))
424 else:
425 serv.privmsg(auteur,"Ce score n'existe pas : %s"%(todelete))
426 else:
427 serv.privmsg(auteur,"Syntaxe : SCORES DEL <pseudo>")
428 elif souscmd in ["add","sub"]:
429 if len(message)==4:
430 toadd,val=message[2],message[3]
431 try:
432 val=int(val)
433 except ValueError:
434 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
435 return
436 if souscmd=="sub":
437 val=-val
438 self.add_score(toadd,val)
439 serv.privmsg(auteur,"Done")
440 else:
441 serv.privmsg(auteur,"Syntaxe : SCORES {ADD|SUB} <pseudo> <n>")
442 else:
443 serv.privmsg(auteur,"Syntaxe : SCORES {DEL|ADD|SUB} <pseudo> [<n>]")
444 else:
445 notunderstood=True
446 else:
447 notunderstood=True
448 if notunderstood:
449 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
450
451 def sendscore(self, to):
452 self.serv.privmsg(to, "Votre score : %s"%(self.get_scores().get(to,0)) )
453
454 def sendscores(self, to):
455 scores=self.get_scores().items()
456 # trie par score
457 scores.sort(lambda x,y:cmp(x[1],y[1]))
458 scores.reverse()
459 self.serv.privmsg(to, "Scores by score : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
460 # trie par pseudo
461 scores.sort(lambda x,y:cmp(x[0].lower(),y[0].lower()))
462 self.serv.privmsg(to, "Scores by pseudo : "+" ; ".join(["%s %s"%(i[0],i[1]) for i in scores]))
463
464 def on_pubmsg(self, serv, ev):
465 if ignore_event(serv, ev):
466 return
467 auteur = irclib.nm_to_n(ev.source())
468 channel = ev.target()
469 message = ev.arguments()[0]
470 try:
471 test=bot_unicode(message)
472 except UnicodeBotError:
473 if config.utf8_trigger and not channel in self.quiet_channels:
474 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
475 return
476 pour_moi,message=self.pourmoi(serv,message)
477 if pour_moi and message.split()!=[]:
478 cmd=message.split()[0].lower()
479 try:
480 args=" ".join(message.split()[1:])
481 except:
482 args=""
483 if cmd in ["meurs","die","crève"]:
484 if auteur in self.overops:
485 log(self.serveur,channel,auteur,message+"[successful]")
486 self.mourir()
487 else:
488 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
489 log(self.serveur,channel,auteur,message+"[failed]")
490 elif cmd == "reload":
491 if auteur in self.ops:
492 log(self.serveur, channel, auteur, message+"[successful]")
493 self.reload(channel)
494 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
495 if auteur in self.ops and (not (channel in self.stay_channels)
496 or auteur in self.overops):
497 self.quitter(channel)
498 log(self.serveur,channel,auteur,message+"[successful]")
499 if channel in self.chanlist:
500 self.chanlist.remove(channel)
501 else:
502 serv.privmsg(channel,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
503 log(self.serveur,channel,auteur,message+"[failed]")
504 elif cmd == "score":
505 self.sendscore(auteur)
506 elif cmd == "scores":
507 self.sendscores(auteur)
508 else:
509 if is_pan(message):
510 self.shot(channel, auteur)
511
512 def on_action(self, serv, ev):
513 if ignore_event(serv, ev):
514 return
515 action = ev.arguments()[0]
516 auteur = irclib.nm_to_n(ev.source())
517 channel = ev.target()
518 #~ try:
519 #~ test=bot_unicode(action)
520 #~ except UnicodeBotError:
521 #~ if config.utf8_trigger and not channel in self.quiet_channels:
522 #~ serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
523 #~ return
524 #~ mypseudo=self.nick
525
526 def on_kick(self,serv,ev):
527 auteur = irclib.nm_to_n(ev.source())
528 channel = ev.target()
529 victime = ev.arguments()[0]
530 raison = ev.arguments()[1]
531 if victime==self.nick:
532 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
533 time.sleep(2)
534 serv.join(channel)
535 #~ l1,l2=config.kick_answers,config.kick_actions
536 #~ n1,n2=len(l1),len(l2)
537 #~ i=random.randrange(n1+n2)
538 #~ if i>=n1:
539 #~ serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
540 #~ else:
541 #~ serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
542
543 def spawn(self, channel, timestamp, delay=0):
544 if channel in self.play_channels:
545 if delay>0:
546 self.serv.execute_delayed(delay, self.spawn, (channel, timestamp))
547 self.status[channel] = [1, timestamp]
548 else:
549 # on teste le timestamp pour pas s'emmêler dans les spawn
550 infos = self.status.get(channel, [0,0])
551 if infos == [1, timestamp]:
552 spawn_sentence = random.choice(config.canards) + random.choice(config.spawn_sentences)
553 self.serv.privmsg(channel, spawn_sentence.encode("utf8"))
554 self.status[channel] = [2, timestamp]
555 times_up_delay = random.randrange(*config.times_up_delays)
556 self.serv.execute_delayed(times_up_delay, self.too_slow, (channel, timestamp))
557
558 def too_slow(self, channel, timestamp):
559 infos = self.status.get(channel, [0,0])
560 if infos == [2, timestamp]:
561 self.serv.privmsg(channel, random.choice(config.times_up_sentences).encode("utf8"))
562 respawn_delay = random.randrange(*config.spawn_delays)
563 self.spawn(channel, time.time(), respawn_delay)
564
565 def shot(self, channel, auteur):
566 if self.status.get(channel, [0, 0])[0] == 2:
567 succeed = random.randrange(0,101) > config.proba_miss
568 if succeed:
569 self.serv.privmsg(channel, random.choice(config.killed_templates).format(auteur).encode("utf8"))
570 self.add_score(auteur, 1)
571 if random.randrange(0, 101) < config.proba_killed_sentence:
572 self.serv.privmsg(channel, random.choice(config.killed_sentences).encode("utf8"))
573 respawn_delay = random.randrange(*config.spawn_delays)
574 self.spawn(channel, time.time(), respawn_delay)
575 else:
576 self.serv.privmsg(channel, random.choice(config.miss_templates).format(auteur).encode("utf8"))
577 if random.randrange(0,101) < config.proba_miss_sentence:
578 self.serv.privmsg(channel, random.choice(config.miss_sentences).encode("utf8"))
579
580 def quitter(self,chan,leave_message=None):
581 if leave_message==None:
582 leave_message=random.choice(config.leave_messages)
583 self.serv.part(chan,message=leave_message.encode("utf8"))
584
585 def mourir(self):
586 quit_message=random.choice(config.quit_messages)
587 self.die(msg=quit_message.encode("utf8"))
588
589 def get_scores(self):
590 f=open(config.score_file)
591 scores=pickle.load(f)
592 f.close()
593 return scores
594
595 def add_score(self, pseudo, value):
596 scores=self.get_scores()
597 if scores.has_key(pseudo):
598 scores[pseudo]+=value
599 else:
600 scores[pseudo]=value
601 self.save_scores(scores)
602
603 def save_scores(self,scores):
604 f=open(config.score_file,"w")
605 pickle.dump(scores,f)
606 f.close()
607
608 def _getnick(self):
609 return self.serv.get_nickname()
610 nick=property(_getnick)
611
612 def reload(self, auteur=None):
613 reload(config)
614 if auteur in [None, "SIGHUP"]:
615 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
616 for to in config.report_bugs_to:
617 self.serv.privmsg(to, towrite)
618 log(self.serveur, towrite)
619 else:
620 self.serv.privmsg(auteur,"Config reloaded")
621
622 def start_as_daemon(self, outfile):
623 sys.stderr = Logger(outfile)
624 self.start()
625
626
627 class Logger(object):
628 """Pour écrire ailleurs que sur stdout"""
629 def __init__(self, filename="saturnin.full.log"):
630 self.filename = filename
631
632 def write(self, message):
633 f = open(self.filename, "a")
634 f.write(message)
635 f.close()
636
637
638 if __name__=="__main__":
639 import sys
640 if len(sys.argv)==1:
641 print "Usage : saturnin.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
642 print " --outfile sans --no-output ni --daemon n'a aucun effet"
643 exit(1)
644 serveur=sys.argv[1]
645 if "--daemon" in sys.argv:
646 thisfile = os.path.realpath(__file__)
647 thisdirectory = thisfile.rsplit("/", 1)[0]
648 os.chdir(thisdirectory)
649 daemon = True
650 else:
651 daemon = False
652 if "debug" in sys.argv or "--debug" in sys.argv:
653 debug=True
654 else:
655 debug=False
656 if "--no-output" in sys.argv or "--daemon" in sys.argv:
657 outfile = "/var/log/bots/saturnin.full.log"
658 for arg in sys.argv:
659 arg = arg.split("=")
660 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
661 outfile = arg[1]
662 sys.stdout = Logger(outfile)
663 if "--quiet" in sys.argv:
664 config.debug_stdout=False
665 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
666 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org",
667 "local":"localhost"}
668 try:
669 serveur=serveurs[serveur]
670 except KeyError:
671 print "Server Unknown : %s"%(serveur)
672 exit(404)
673 saturnin = Saturnin(serveur,debug)
674 # Si on reçoit un SIGHUP, on reload la config
675 def sighup_handler(signum, frame):
676 saturnin.reload("SIGHUP")
677 signal.signal(signal.SIGHUP, sighup_handler)
678 if daemon:
679 child_pid = os.fork()
680 if child_pid == 0:
681 os.setsid()
682 saturnin.start_as_daemon(outfile)
683 else:
684 # on enregistre le pid de saturnin
685 pidfile = "/var/run/bots/saturnin.pid"
686 for arg in sys.argv:
687 arg = arg.split("=")
688 if arg[0].strip('-') in ["pidfile"]:
689 pidfile = arg[1]
690 f = open(pidfile, "w")
691 f.write("%s\n" % child_pid)
692 f.close()
693 else:
694 saturnin.start()
695