]> gitweb.pimeys.fr Git - bots/themis.git/blob - themis.py
"/', shit
[bots/themis.git] / themis.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3 # Codé par 20-100 (commencé le 21/06/12)
4
5 # Un bot IRC pour kicker à tour de bras de #déprime
6
7 import threading
8 import random
9 import time
10 import socket, ssl, json
11 import pickle
12 import re
13 import os
14 import signal
15 import sys
16
17 # Oui, j'ai recodé ma version d'irclib pour pouvoir rattrapper les SIGHUP
18 sys.path.insert(0, "/home/vincent/scripts/python-myirclib")
19 import irclib
20 import ircbot
21
22 import sys
23
24 # Fichier de conf
25 import config
26
27 def get_config_logfile(serveur):
28 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
29 return config.logfile_template%(serveurs[serveur])
30
31
32 def log(serveur,channel,auteur=None,message=None):
33 f=open(get_config_logfile(serveur),"a")
34 if auteur==message==None:
35 # alors c'est que c'est pas un channel mais juste une ligne de log
36 chain="%s %s"%(time.strftime("%F %T"),channel)
37 else:
38 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
39 f.write(chain+"\n")
40 if config.debug_stdout:
41 print chain
42 f.close()
43
44 def ignore_event(serv, ev):
45 """Retourne ``True`` si il faut ignorer cet évènement."""
46 for (blackmask, exceptmask) in config.blacklisted_masks:
47 usermask = ev.source()
48 if exceptmask is None:
49 exceptit = False
50 else:
51 exceptit = bool(irclib.mask_matches(usermask, exceptmask))
52 blackit = bool(irclib.mask_matches(usermask, blackmask))
53 if blackit and not exceptit:
54 return True
55
56 reg_is_smiley = re.compile(u".*("+u"|".join(config.smileys)+u")")
57 def is_smiley(chain):
58 chain=unicode(chain,"utf8")
59 o=re.match(reg_is_smiley,chain)
60 return o
61
62 reg_is_anglicisme = re.compile(u".*(?:^| )(" + u"|".join(config.anglicismes) + u")(?:$|\.| |,|;)")
63 def is_anglicisme(chain):
64 chain = unicode(chain, "utf8").lower()
65 o = re.match(reg_is_anglicisme, chain)
66 return o
67
68 # Cette liste contient la liste des raisons pour lesquelles on peut se faire kicker
69 # chaque élément contient :
70 # - la fonction de détection du kick (qui matchera une regexp et renverra l'objet de match)
71 # - la raison donnée au moment du kick ({0} étant remplacé par ce qui a matché)
72 config.kicking_list = [
73 [is_smiley, u'"{0}" ? Ici on déprime.'],
74 [is_anglicisme, u'"{0}" ? Get out, you and your fucking anglicism !']
75 ]
76
77 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
78 if case_sensitive:
79 chain=unicode(chain,"utf8")
80 else:
81 chain=unicode(chain,"utf8").lower()
82 allmatches="("+"|".join(matches)+")"
83 reg=(avant+allmatches+apres).lower()
84 o=re.match(reg,chain)
85 return o
86
87 def is_insult(chain,debug=True):
88 return is_something(chain,config.insultes,avant=".*(?:^| |')")
89 def is_not_insult(chain):
90 chain=unicode(chain,"utf8")
91 insult_regexp=u"("+u"|".join(config.insultes)+u")"
92 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
93 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
94 if re.match(reg,chain):
95 return True
96 else:
97 return False
98 def is_compliment(chain,debug=True):
99 return is_something(chain,config.compliment_triggers,avant=".*(?:^| |')")
100 def is_perdu(chain):
101 return is_something(chain,config.perdu)
102 def is_tag(chain):
103 return is_something(chain,config.tag_triggers)
104 def is_tesla(chain):
105 return is_something(chain,config.tesla_triggers,avant=u"^",apres=u"$",debug=True)
106 def is_merci(chain):
107 return is_something(chain,config.merci_triggers)
108 def is_tamere(chain):
109 return is_something(chain,config.tamere_triggers)
110 def is_bad_action_trigger(chain,pseudo):
111 return is_something(chain,config.bad_action_triggers,avant=u"^",
112 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
113 def is_good_action_trigger(chain,pseudo):
114 return is_something(chain,config.good_action_triggers,avant=u"^",
115 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
116 def is_bonjour(chain):
117 return is_something(chain,config.bonjour_triggers,avant=u"^")
118 def is_bonne_nuit(chain):
119 return is_something(chain,config.bonne_nuit_triggers,avant=u"^")
120 def is_pan(chain):
121 return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
122
123 def is_time(conf):
124 _,_,_,h,m,s,_,_,_=time.localtime()
125 return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
126 def is_day():
127 return is_time(config.daytime)
128 def is_night():
129 return is_time(config.nighttime)
130
131
132 class UnicodeBotError(Exception):
133 pass
134 def bot_unicode(chain):
135 try:
136 unicode(chain,"utf8")
137 except UnicodeDecodeError as exc:
138 raise UnicodeBotError
139
140 class Themis(ircbot.SingleServerIRCBot):
141 def __init__(self,serveur,debug=False):
142 temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
143 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
144 temporary_pseudo,"Des[bot]e de #déprime", 10)
145 self.debug=debug
146 self.serveur=serveur
147 self.overops=config.overops
148 self.ops=self.overops+config.ops
149 self.report_bugs_to=config.report_bugs_to
150 self.chanlist=config.chanlist
151 self.kick_channels=config.kick_channels
152 self.stay_channels=config.stay_channels
153 self.quiet_channels=config.quiet_channels
154 self.last_perdu=0
155
156
157 def give_me_my_pseudo(self,serv):
158 serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
159 serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
160 time.sleep(0.3)
161 serv.nick(config.irc_pseudo)
162
163 def give_me_my_op_status(self,serv,chan):
164 serv.privmsg("ChanServ","OP %s"%(chan))
165
166 def on_welcome(self, serv, ev):
167 self.serv=serv # ça serv ira
168 self.give_me_my_pseudo(serv)
169 serv.privmsg("NickServ","identify %s"%(config.irc_password))
170 log(self.serveur,"Connected")
171 if self.debug:
172 self.chanlist=["#bot"]
173 self.kick_channels=["#bot"]
174 for c in self.chanlist:
175 log(self.serveur,"JOIN %s"%(c))
176 serv.join(c)
177 self.give_me_my_op_status(serv,c)
178
179
180 def lost(self,serv,channel,forced=False):
181 if self.last_perdu+config.time_between_perdu<time.time() or forced:
182 if not channel in self.quiet_channels or forced:
183 serv.privmsg(channel,"J'ai perdu !")
184 self.last_perdu=time.time()
185 delay=config.time_between_perdu_trigger
186 delta=config.time_between_perdu_trigger_delta
187 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
188
189 def pourmoi(self, serv, message):
190 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
191 pseudo=self.nick
192 size=len(pseudo)
193 if message[:size]==pseudo and len(message)>size and message[size]==":":
194 return (True,message[size+1:].lstrip(" "))
195 else:
196 return (False,message)
197
198 def on_privmsg(self, serv, ev):
199 if ignore_event(serv, ev):
200 return
201 message=ev.arguments()[0]
202 auteur = irclib.nm_to_n(ev.source())
203 try:
204 test=bot_unicode(message)
205 except UnicodeBotError:
206 if config.utf8_trigger:
207 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
208 return
209 message=message.split()
210 cmd=message[0].lower()
211 notunderstood=False
212 if cmd=="help":
213 helpdico={"help":["""HELP <commande>
214 Affiche de l'aide sur la commande""",None,None],
215 "join": [None, """JOIN <channel>
216 Me fait rejoindre le channel""",None],
217 "leave": [None,"""LEAVE <channel>
218 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
219 "quiet": [None,"""QUIET <channel>
220 Me rend silencieux sur le channel.""",None],
221 "noquiet": [None,"""NOQUIET <channel>
222 Me rend la parole sur le channel.""",None],
223 "reload": [None,"""RELOAD
224 Recharge la configuration.""",None],
225 "say": [None,None,"""SAY <channel> <message>
226 Me fait parler sur le channel."""],
227 "do": [None,None,"""DO <channel> <action>
228 Me fait faitre une action (/me) sur le channel."""],
229 "stay": [None,None,"""STAY <channel>
230 Ajoute le channel à ma stay_list."""],
231 "nostay": [None,None,"""NOSTAY <channel>
232 Retire le channel de ma stay_list."""],
233 "ops": [None,None,"""OPS
234 Affiche la liste des ops."""],
235 "overops": [None,None,"""OVEROPS
236 Affiche la liste des overops."""],
237 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
238 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
239 "die": [None,None,"""DIE
240 Me déconnecte du serveur IRC."""]
241 }
242 helpmsg_default="Liste des commandes disponibles :\nHELP"
243 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RELOAD"
244 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
245 op,overop=auteur in self.ops, auteur in self.overops
246 if len(message)==1:
247 helpmsg=helpmsg_default
248 if op:
249 helpmsg+=helpmsg_ops
250 if overop:
251 helpmsg+=helpmsg_overops
252 else:
253 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
254 helpmsg=helpmsgs[0]
255 if op and helpmsgs[1]:
256 if helpmsg:
257 helpmsg+="\n"+helpmsgs[1]
258 else:
259 helpmsg=helpmsgs[1]
260 if overop and helpmsgs[2]:
261 if helpmsg:
262 helpmsg+="\n"+helpmsgs[2]
263 else:
264 helpmsg=helpmsgs[2]
265 for ligne in helpmsg.split("\n"):
266 serv.privmsg(auteur,ligne)
267 elif cmd=="join":
268 if auteur in self.ops:
269 if len(message)>1:
270 if message[1] in self.chanlist:
271 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
272 else:
273 serv.join(message[1])
274 self.chanlist.append(message[1])
275 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
276 log(self.serveur,"priv",auteur," ".join(message))
277 else:
278 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
279 else:
280 notunderstood=True
281 elif cmd=="leave":
282 if auteur in self.ops and len(message)>1:
283 if message[1] in self.chanlist:
284 if not (message[1] in self.stay_channels) or auteur in self.overops:
285 self.quitter(message[1]," ".join(message[2:]))
286 self.chanlist.remove(message[1])
287 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
288 else:
289 serv.privmsg(auteur,random.choice(config.leave_fail_messages))
290 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
291 else:
292 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
293 else:
294 notunderstood=True
295 elif cmd=="stay":
296 if auteur in self.overops:
297 if len(message)>1:
298 if message[1] in self.stay_channels:
299 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
300 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
301 else:
302 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
303 self.stay_channels.append(message[1])
304 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
305 else:
306 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
307 else:
308 notunderstood=True
309 elif cmd=="nostay":
310 if auteur in self.overops:
311 if len(message)>1:
312 if message[1] in self.stay_channels:
313 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
314 self.stay_channels.remove(message[1])
315 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
316 else:
317 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
318 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
319
320 else:
321 notunderstood=True
322 elif cmd=="die":
323 if auteur in self.overops:
324 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
325 self.mourir()
326 else:
327 notunderstood=True
328 elif cmd=="quiet":
329 if auteur in self.ops:
330 if len(message)>1:
331 if message[1] in self.quiet_channels:
332 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
333 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
334 else:
335 self.quiet_channels.append(message[1])
336 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
337 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
338 else:
339 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
340 else:
341 notunderstood=True
342 elif cmd=="noquiet":
343 if auteur in self.ops:
344 if len(message)>1:
345 if message[1] in self.quiet_channels:
346 self.quiet_channels.remove(message[1])
347 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
348 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
349 else:
350 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
351 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
352 else:
353 notunderstood=True
354 elif cmd=="reload":
355 if auteur in self.ops:
356 self.reload(auteur)
357 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
358 else:
359 notunderstood=True
360 elif cmd=="say":
361 if auteur in self.overops and len(message)>2:
362 serv.privmsg(message[1]," ".join(message[2:]))
363 log(self.serveur,"priv",auteur," ".join(message))
364 elif len(message)<=2:
365 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
366 else:
367 notunderstood=True
368 elif cmd=="do":
369 if auteur in self.overops and len(message)>2:
370 serv.action(message[1]," ".join(message[2:]))
371 log(self.serveur,"priv",auteur," ".join(message))
372 elif len(message)<=2:
373 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
374 else:
375 notunderstood=True
376 elif cmd=="kick":
377 if auteur in self.overops and len(message)>2:
378 serv.kick(message[1],message[2]," ".join(message[3:]))
379 log(self.serveur,"priv",auteur," ".join(message))
380 elif len(message)<=2:
381 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
382 else:
383 notunderstood=True
384 elif cmd=="ops":
385 if auteur in self.overops:
386 serv.privmsg(auteur," ".join(self.ops))
387 else:
388 notunderstood=True
389 elif cmd=="overops":
390 if auteur in self.overops:
391 serv.privmsg(auteur," ".join(self.overops))
392 else:
393 notunderstood=True
394 else:
395 notunderstood=True
396 if notunderstood:
397 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
398
399 def on_pubmsg(self, serv, ev):
400 if ignore_event(serv, ev):
401 return
402 auteur = irclib.nm_to_n(ev.source())
403 canal = ev.target()
404 message = ev.arguments()[0]
405 try:
406 test=bot_unicode(message)
407 except UnicodeBotError:
408 if config.utf8_trigger and not canal in self.quiet_channels:
409 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
410 return
411 pour_moi,message=self.pourmoi(serv,message)
412 for (detect, reason) in config.kicking_list:
413 matching = detect(message)
414 if matching:
415 if canal in self.kick_channels:
416 serv.kick(canal,auteur,(reason.format(matching.groups()[0])).encode("utf8"))
417 return
418 if pour_moi and message.split()!=[]:
419 cmd=message.split()[0].lower()
420 try:
421 args=" ".join(message.split()[1:])
422 except:
423 args=""
424 if cmd in ["meurs","die","crève","pends-toi"]:
425 if auteur in self.overops:
426 log(self.serveur,canal,auteur,message+"[successful]")
427 self.mourir()
428 else:
429 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config.quit_fail_messages)))
430 log(self.serveur,canal,auteur,message+"[failed]")
431 elif cmd == "reload":
432 if auteur in self.ops:
433 log(self.serveur, canal, auteur, message+"[successful]")
434 self.reload(canal)
435 elif cmd in ["part","leave","dégage","va-t-en"]:
436 if auteur in self.ops and (not (canal in self.stay_channels)
437 or auteur in self.overops):
438 self.quitter(canal)
439 log(self.serveur,canal,auteur,message+"[successful]")
440 if canal in self.chanlist:
441 self.chanlist.remove(canal)
442 else:
443 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config.leave_fail_messages)))
444 log(self.serveur,canal,auteur,message+"[failed]")
445
446 elif cmd in ["deviens","pseudo"]:
447 if auteur in self.ops:
448 become=args
449 serv.nick(become)
450 log(self.serveur,canal,auteur,message+"[successful]")
451
452 elif cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
453 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
454 elif cmd in ["ping"] and not canal in self.quiet_channels:
455 serv.privmsg(canal,"%s: pong"%(auteur))
456 # if is_insult(message) and not canal in self.quiet_channels:
457 # if is_not_insult(message):
458 # answer=random.choice(config.compliment_answers)
459 # for ligne in answer.split("\n"):
460 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
461 # else:
462 # answer=random.choice(config.insultes_answers)
463 # for ligne in answer.split("\n"):
464 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
465 # elif is_compliment(message) and not canal in self.quiet_channels:
466 # answer=random.choice(config.compliment_answers)
467 # for ligne in answer.split("\n"):
468 # serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
469 elif is_tesla(message) and not canal in self.quiet_channels:
470 l1,l2=config.tesla_answers,config.tesla_actions
471 n1,n2=len(l1),len(l2)
472 i=random.randrange(n1+n2)
473 if i>=n1:
474 serv.action(canal,l2[i-n1].encode("utf8"))
475 else:
476 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
477 elif is_tag(message) and not canal in self.quiet_channels:
478 if auteur in self.ops:
479 action=random.choice(config.tag_actions)
480 serv.action(canal,action.encode("utf8"))
481 self.quiet_channels.append(canal)
482 else:
483 answer=random.choice(config.tag_answers)
484 for ligne in answer.split("\n"):
485 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
486 elif is_bonjour(message) and not canal in self.quiet_channels:
487 if is_night():
488 answer=random.choice(config.night_answers)
489 elif is_day():
490 answer=random.choice(config.bonjour_answers)
491 else:
492 answer=random.choice(config.bonsoir_answers)
493 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
494 elif is_bonne_nuit(message) and not canal in self.quiet_channels:
495 answer=random.choice(config.bonne_nuit_answers)
496 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
497 else:
498 if not canal in self.quiet_channels:
499 mypseudo=self.nick
500 if re.match((u"^("+u"|".join(config.bonjour_triggers)
501 +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
502 ).format(mypseudo).lower(), message.strip().lower()):
503 answer=random.choice(config.bonjour_answers)
504 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
505
506 def on_action(self, serv, ev):
507 if ignore_event(serv, ev):
508 return
509 action = ev.arguments()[0]
510 auteur = irclib.nm_to_n(ev.source())
511 channel = ev.target()
512 try:
513 test=bot_unicode(action)
514 except UnicodeBotError:
515 if config.utf8_trigger and not canal in self.quiet_channels:
516 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
517 return
518 mypseudo=self.nick
519
520 # if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
521 # l1,l2=config.bad_action_answers,config.bad_action_actions
522 # n1,n2=len(l1),len(l2)
523 # i=random.randrange(n1+n2)
524 # if i>=n1:
525 # serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
526 # else:
527 # serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
528 # if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
529 # l1,l2=config.good_action_answers,config.good_action_actions
530 # n1,n2=len(l1),len(l2)
531 # i=random.randrange(n1+n2)
532 # if i>=n1:
533 # serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
534 # else:
535 # serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
536
537 def on_kick(self,serv,ev):
538 auteur = irclib.nm_to_n(ev.source())
539 channel = ev.target()
540 victime = ev.arguments()[0]
541 raison = ev.arguments()[1]
542 if victime==self.nick:
543 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
544 time.sleep(2)
545 serv.join(channel)
546 username = irclib.nm_to_u(ev.source()).lower()
547 print ev.source()
548 print channel, username
549 if channel == "#déprime" and "peb" in username or "becue" in username:
550 print "Kicking peb"
551 serv.execute_delayed(5, serv.kick, (auteur, "Va abuser de tes droits ailleurs !"))
552
553 def kicker(self, chan, pseudo, raison=None):
554 if raison==None:
555 raison = config.kick_default_reason
556 self.serv.kick(chan,pseudo,raison)
557
558 def quitter(self,chan,leave_message=None):
559 if leave_message==None:
560 leave_message=random.choice(config.leave_messages)
561 self.serv.part(chan,message=leave_message.encode("utf8"))
562
563 def mourir(self):
564 quit_message=random.choice(config.quit_messages)
565 self.die(msg=quit_message.encode("utf8"))
566
567 def _getnick(self):
568 return self.serv.get_nickname()
569 nick=property(_getnick)
570
571 def reload(self, auteur=None):
572 reload(config)
573 if auteur in [None, "SIGHUP"]:
574 towrite = "Config reloaded" + " (SIGHUP received)"*(auteur == "SIGHUP")
575 for to in config.report_bugs_to:
576 self.serv.privmsg(to, towrite)
577 log(self.serveur, towrite)
578 else:
579 self.serv.privmsg(auteur,"Config reloaded")
580
581 def start_as_daemon(self, outfile):
582 sys.stderr = Logger(outfile)
583 self.start()
584
585
586 class Logger(object):
587 """Pour écrire ailleurs que sur stdout"""
588 def __init__(self, filename="themis.full.log"):
589 self.filename = filename
590
591 def write(self, message):
592 f = open(self.filename, "a")
593 f.write(message)
594 f.close()
595
596
597 if __name__=="__main__":
598 import sys
599 if len(sys.argv)==1:
600 print "Usage : themis.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
601 print " --outfile sans --no-output ni --daemon n'a aucun effet"
602 exit(1)
603 serveur=sys.argv[1]
604 if "--daemon" in sys.argv:
605 thisfile = os.path.realpath(__file__)
606 thisdirectory = thisfile.rsplit("/", 1)[0]
607 os.chdir(thisdirectory)
608 daemon = True
609 else:
610 daemon = False
611 if "debug" in sys.argv or "--debug" in sys.argv:
612 debug=True
613 else:
614 debug=False
615 if "--no-output" in sys.argv or "--daemon" in sys.argv:
616 outfile = "/var/log/bots/themis.full.log"
617 for arg in sys.argv:
618 arg = arg.split("=")
619 if arg[0].strip('-') in ["out", "outfile", "logfile"]:
620 outfile = arg[1]
621 sys.stdout = Logger(outfile)
622 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
623 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
624 try:
625 serveur=serveurs[serveur]
626 except KeyError:
627 print "Server Unknown : %s"%(serveur)
628 exit(404)
629 themis=Themis(serveur,debug)
630 # Si on reçoit un SIGHUP, on reload la config
631 def sighup_handler(signum, frame):
632 themis.reload("SIGHUP")
633 signal.signal(signal.SIGHUP, sighup_handler)
634 if daemon:
635 child_pid = os.fork()
636 if child_pid == 0:
637 os.setsid()
638 themis.start_as_daemon(outfile)
639 else:
640 # on enregistre le pid de themis
641 pidfile = "/var/run/bots/themis.pid"
642 for arg in sys.argv:
643 arg = arg.split("=")
644 if arg[0].strip('-') in ["pidfile"]:
645 pidfile = arg[1]
646 f = open(pidfile, "w")
647 f.write("%s\n" % child_pid)
648 f.close()
649 else:
650 themis.start()
651