4 # Codé par 20-100 (commencé le 23/04/12)
6 """ Un bot IRC destiné à s'interfacer avec la Note Kfet 2015 """
18 # Oui, j'ai recodé ma version d'irclib pour pouvoir rattrapper les SIGHUP
19 sys
.path
.insert(0, "/home/vincent/scripts/python-myirclib")
23 from commands
import getstatusoutput
as ex
27 #: Module responsable du dialogue avec la NoteKfet2015
30 # la partie qui réfère au fichier lui-même est mieux ici
31 # sinon on réfère la config et pas le fichier lui-même
33 config
.thisfile
= os
.path
.realpath( __file__
)
35 def get_config_logfile(serveur
):
36 serveurs
={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
37 return config
.logfile_template
%(serveurs
[serveur
])
40 return ex("ls -s %s"%(config
.thisfile
))[1].split()[0]
42 def log(serveur
,channel
,auteur
=None,message
=None):
43 f
=open(get_config_logfile(serveur
),"a")
44 if auteur
==message
==None:
45 # alors c'est que c'est pas un channel mais juste une ligne de log
46 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
48 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
50 if config
.debug_stdout
:
54 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
56 chain
=unicode(chain
,"utf8")
58 chain
=unicode(chain
,"utf8").lower()
59 allmatches
="("+"|".join(matches
)+")"
60 reg
=(avant
+allmatches
+apres
).lower()
64 def is_insult(chain
,debug
=True):
65 return is_something(chain
,config
.insultes
,avant
=".*(?:^| |')")
66 def is_not_insult(chain
):
67 chain
=unicode(chain
,"utf8")
68 insult_regexp
=u
"("+u
"|".join(config
.insultes
)+u
")"
69 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
70 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
71 if re
.match(reg
,chain
):
75 def is_compliment(chain
,debug
=True):
76 return is_something(chain
,config
.compliment_triggers
,avant
=".*(?:^| |')")
78 return is_something(chain
,config
.perdu
)
80 return is_something(chain
,config
.tag_triggers
)
82 return is_something(chain
,config
.gros
)
84 return is_something(chain
,config
.tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
86 return is_something(chain
,config
.merci_triggers
)
88 return is_something(chain
,config
.tamere_triggers
)
89 def is_bad_action_trigger(chain
,pseudo
):
90 return is_something(chain
,config
.bad_action_triggers
,avant
=u
"^",
91 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
92 def is_good_action_trigger(chain
,pseudo
):
93 return is_something(chain
,config
.good_action_triggers
,avant
=u
"^",
94 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
95 def is_bonjour(chain
):
96 return is_something(chain
,config
.bonjour_triggers
,avant
=u
"^")
97 def is_bonne_nuit(chain
):
98 return is_something(chain
,config
.bonne_nuit_triggers
,avant
=u
"^")
100 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
103 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
104 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
106 return is_time(config
.daytime
)
108 return is_time(config
.nighttime
)
111 class UnicodeBotError(Exception):
114 class CrashError(Exception):
115 """Pour pouvoir faire crasher Basile, parce que ça a l'air drôle"""
116 def __init__(self
, msg
=u
""):
117 Exception.__init
__(self
, msg
)
119 def bot_unicode(chain
):
121 unicode(chain
,"utf8")
122 except UnicodeDecodeError as exc
:
123 raise UnicodeBotError
126 class Basile(ircbot
.SingleServerIRCBot
):
127 def __init__(self
,serveur
,debug
=False):
128 temporary_pseudo
=config
.irc_pseudo
+str(random
.randrange(10000,100000))
129 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
130 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
133 self
.overops
=config
.overops
134 self
.ops
=self
.overops
+config
.ops
135 self
.report_bugs_to
=config
.report_bugs_to
136 self
.chanlist
=config
.chanlist
137 self
.identities
=pickle
.load(open("identities.pickle","r"))
138 self
.stay_channels
=config
.stay_channels
139 self
.quiet_channels
=config
.quiet_channels
143 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
144 """Renvoie (``True``, <une socket ouverte et authentifiée sur la NoteKfet2015>)
145 ou bien (``False``, None)"""
147 login_result
,sock
=nk
.login(username
,password
,typ
)
148 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
149 except nk
.NKRefused
as exc
:
150 for report
in self
.report_bugs_to
:
151 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
153 except nk
.NKHelloFailed
as exc
:
154 for report
in self
.report_bugs_to
:
156 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
158 except nk
.NKUnknownError
as exc
:
159 erreurs
=["Une fucking erreur inconnue s'est produite"]
160 erreurs
+=str(exc
).split("\n")
161 for report
in self
.report_bugs_to
:
163 serv
.privmsg(report
,err
)
165 except Exception as exc
:
166 # Exception qui ne vient pas de la communication avec le serveur NK2015
167 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
174 def give_me_my_pseudo(self
,serv
):
175 """Récupère le pseudo auprès de NickServ."""
176 serv
.privmsg("NickServ","RECOVER %s %s"%(config
.irc_pseudo
,config
.irc_password
))
177 serv
.privmsg("NickServ","RELEASE %s %s"%(config
.irc_pseudo
,config
.irc_password
))
179 serv
.nick(config
.irc_pseudo
)
181 def on_welcome(self
, serv
, ev
):
182 self
.serv
=serv
# ça serv ira :)
183 self
.give_me_my_pseudo(serv
)
184 serv
.privmsg("NickServ","identify %s"%(config
.irc_password
))
185 log(self
.serveur
,"Connected")
187 self
.chanlist
=["#bot"]
188 for c
in self
.chanlist
:
189 log(self
.serveur
,"JOIN %s"%(c))
191 # on ouvre la connexion note de Basile, special user
192 self
.nk
=self
.new_connection_NK(serv
,config
.note_pseudo
,config
.note_password
,"special")[1]
194 for report
in self
.report_bugs_to
:
195 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
197 def lost(self
,serv
,channel
,forced
=False):
198 if self
.last_perdu
+config
.time_between_perdu
<time
.time() or forced
:
199 if not channel
in self
.quiet_channels
or forced
:
200 serv
.privmsg(channel
,"J'ai perdu !")
201 self
.last_perdu
=time
.time()
202 delay
=config
.time_between_perdu_trigger
203 delta
=config
.time_between_perdu_trigger_delta
204 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
206 def pourmoi(self
, serv
, message
):
207 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
210 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
211 return (True,message
[size
+1:].lstrip(" "))
213 return (False,message
)
215 def on_privmsg(self
, serv
, ev
):
216 message
=ev
.arguments()[0]
217 auteur
= irclib
.nm_to_n(ev
.source())
219 test
=bot_unicode(message
)
220 except UnicodeBotError
:
221 if config
.utf8_trigger
:
222 serv
.privmsg(auteur
, random
.choice(config
.utf8_fail_answers
).encode("utf8"))
224 message
=message
.split()
225 cmd
=message
[0].lower()
228 helpdico
={"help":["""HELP <commande>
229 Affiche de l'aide sur la commande""",None,None],
230 "identify": ["""IDENTIFY <username> <password>
231 Vérifie le mot de passe et me permet de savoir à l'avenir quel est votre pseudo note kfet.
232 Sans paramètre, je vous précise sous quel pseudo je vous connais.""",None,None],
233 "drop":["""DROP <password>
234 Vérifie le mot de passe et me fait d'oublier votre pseudo note kfet.""",None,None],
236 Affiche votre solde, si je connais votre pseudo note kfet.""",
238 Affiche le solde de la personne désignée (par son pseudo note).""",None],
239 "join": [None, """JOIN <channel>
240 Me fait rejoindre le channel""",None],
241 "leave": [None,"""LEAVE <channel>
242 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
243 "quiet": [None,"""QUIET <channel>
244 Me rend silencieux sur le channel.""",None],
245 "noquiet": [None,"""NOQUIET <channel>
246 Me rend la parole sur le channel.""",None],
247 "lost": [None,"""LOST <channel>
248 Me fait perdre sur le channel.""",None],
249 "reconnect": [None,"""RECONNECT
250 Établit à nouveau la connexion avec le serveur NK2015""",None],
251 "reload": [None,"""RELOAD
252 Recharge la configuration.""",None],
253 "say": [None,None,"""SAY <channel> <message>
254 Me fait parler sur le channel."""],
255 "do": [None,None,"""DO <channel> <action>
256 Me fait faitre une action (/me) sur le channel."""],
257 "stay": [None,None,"""STAY <channel>
258 Ajoute le channel à ma stay_list."""],
259 "nostay": [None,None,"""NOSTAY <channel>
260 Retire le channel de ma stay_list."""],
261 "ops": [None,None,"""OPS
262 Affiche la liste des ops."""],
263 "overops": [None,None,"""OVEROPS
264 Affiche la liste des overops."""],
265 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
266 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
267 "die": [None,None,"""DIE
268 Me déconnecte du serveur IRC."""],
269 "crash": [None,None,"""CRASH
272 helpmsg_default
="Liste des commandes disponibles :\nHELP IDENTIFY DROP SOLDE"
273 helpmsg_ops
=" JOIN LEAVE QUIET NOQUIET LOST RECONNECT RELOAD"
274 helpmsg_overops
=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE CRASH"
275 op
,overop
=auteur
in self
.ops
, auteur
in self
.overops
277 helpmsg
=helpmsg_default
281 helpmsg
+=helpmsg_overops
283 helpmsgs
=helpdico
.get(message
[1].lower(),["Commande inconnue.",None,None])
285 if op
and helpmsgs
[1]:
287 helpmsg
+="\n"+helpmsgs
[1]
290 if overop
and helpmsgs
[2]:
292 helpmsg
+="\n"+helpmsgs
[2]
295 for ligne
in helpmsg
.split("\n"):
296 serv
.privmsg(auteur
,ligne
)
297 elif cmd
=="identify":
299 if self
.identities
.has_key(auteur
):
300 serv
.privmsg(auteur
,"Je vous connais sous le pseudo note %s."%(
301 self
.identities
[auteur
].encode("utf8")))
303 serv
.privmsg(auteur
,"Je ne connais pas votre pseudo note.")
304 elif len(message
)>=3:
305 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
306 success
,_
=self
.new_connection_NK(serv
,username
,password
)
308 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
309 serv
.privmsg(auteur
,"Identité enregistrée.")
310 self
.identities
[auteur
]=username
311 pickle
.dump(Xself
.identities
,open("identities.pickle","w"))
313 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
314 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
316 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
319 if self
.identities
.has_key(auteur
):
320 password
=" ".join(message
[1:])
321 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
323 del self
.identities
[auteur
]
324 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
325 pickle
.dump(self
.identities
,open("identities.pickle","w"))
326 serv
.privmsg(auteur
,"Identité oubliée.")
328 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
329 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
331 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
333 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
335 if auteur
in self
.ops
:
337 if message
[1] in self
.chanlist
:
338 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
340 serv
.join(message
[1])
341 self
.chanlist
.append(message
[1])
342 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
343 log(self
.serveur
,"priv",auteur
," ".join(message
))
345 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
349 if auteur
in self
.ops
and len(message
)>1:
350 if message
[1] in self
.chanlist
:
351 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
352 self
.quitter(message
[1]," ".join(message
[2:]))
353 self
.chanlist
.remove(message
[1])
354 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
356 serv
.privmsg(auteur
,"Non, je reste !")
357 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
359 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
363 if auteur
in self
.overops
:
365 if message
[1] in self
.stay_channels
:
366 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
367 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
369 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
370 self
.stay_channels
.append(message
[1])
371 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
373 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
377 if auteur
in self
.overops
:
379 if message
[1] in self
.stay_channels
:
380 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
381 self
.stay_channels
.remove(message
[1])
382 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
384 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
385 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
390 if auteur
in self
.overops
:
391 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
396 if auteur
in self
.overops
:
397 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
398 self
.crash("priv", auteur
)
402 if auteur
in self
.ops
:
404 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
407 elif cmd
=="reconnect":
408 if auteur
in self
.ops
:
410 self
.nk
=self
.new_connection_NK(serv
,config
.note_pseudo
,
411 config
.note_password
,"special")[1]
412 except Exception as exc
:
414 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
416 serv
.privmsg(auteur
,"%s: done"%(auteur))
417 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
419 serv
.privmsg(auteur
,"%s: failed"%(auteur))
420 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
421 for report
in self
.report_bugs_to
:
422 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ? Server dead ?")
426 if auteur
in self
.ops
:
428 if message
[1] in self
.quiet_channels
:
429 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
430 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
432 self
.quiet_channels
.append(message
[1])
433 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
434 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
436 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
440 if auteur
in self
.ops
:
442 if message
[1] in self
.quiet_channels
:
443 self
.quiet_channels
.remove(message
[1])
444 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
445 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
447 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
448 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
452 if auteur
in self
.overops
and len(message
)>2:
453 serv
.privmsg(message
[1]," ".join(message
[2:]))
454 log(self
.serveur
,"priv",auteur
," ".join(message
))
455 elif len(message
)<=2:
456 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
460 if auteur
in self
.overops
and len(message
)>2:
461 serv
.action(message
[1]," ".join(message
[2:]))
462 log(self
.serveur
,"priv",auteur
," ".join(message
))
463 elif len(message
)<=2:
464 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
468 if auteur
in self
.overops
and len(message
)>2:
469 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
470 log(self
.serveur
,"priv",auteur
," ".join(message
))
471 elif len(message
)<=2:
472 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
476 if auteur
in self
.ops
and len(message
)>1:
477 serv
.privmsg(message
[1],"J'ai perdu !")
478 log(self
.serveur
,"priv",auteur
," ".join(message
))
479 elif len(message
)<=1:
480 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
485 if self
.identities
.has_key(auteur
):
487 self
.nk
.write('["search", ["x",["pseudo"],%s]]'%(json
.dumps(self
.identities
[auteur
])))
488 ret
=json
.loads(self
.nk
.read())
489 solde
=ret
["msg"][0]["solde"]
490 pseudo
=ret
["msg"][0]["pseudo"]
491 except Exception as exc
:
493 serv
.privmsg(auteur
,"failed")
494 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
496 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
497 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
499 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
500 elif auteur
in self
.ops
:
502 self
.nk
.write('["search", ["x",["pseudo"],%s]]'%(json
.dumps(message
[1])))
503 ret
=json
.loads(self
.nk
.read())
504 solde
=ret
["msg"][0]["solde"]
505 pseudo
=ret
["msg"][0]["pseudo"]
506 except Exception as exc
:
507 serv
.privmsg(auteur
,"failed")
508 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
510 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
512 if auteur
in self
.overops
:
513 serv
.privmsg(auteur
," ".join(self
.ops
))
517 if auteur
in self
.overops
:
518 serv
.privmsg(auteur
," ".join(self
.overops
))
524 serv
.privmsg(auteur
,"Je n'ai pas compris. Essayez HELP…")
526 def on_pubmsg(self
, serv
, ev
):
527 auteur
= irclib
.nm_to_n(ev
.source())
529 message
= ev
.arguments()[0]
531 test
=bot_unicode(message
)
532 except UnicodeBotError
:
533 if config
.utf8_trigger
and not canal
in self
.quiet_channels
:
534 serv
.privmsg(canal
, (u
"%s: %s"%(auteur
,random
.choice(config
.utf8_fail_answers
))).encode("utf8"))
536 pour_moi
,message
=self
.pourmoi(serv
,message
)
537 if pour_moi
and message
.split()!=[]:
538 cmd
=message
.split()[0].lower()
540 args
=" ".join(message
.split()[1:])
543 if cmd
in ["meurs","die","crève"]:
544 if auteur
in self
.overops
:
545 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
548 serv
.privmsg(canal
,("%s: %s"%(auteur
,random
.choice(config
.quit_fail_messages
))).encode("utf8"))
549 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
550 elif cmd
== "reload":
551 if auteur
in self
.ops
:
552 log(self
.serveur
, canal
, auteur
, message
+"[successful]")
555 if auteur
in self
.overops
:
556 self
.crash(auteur
, message
)
557 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
558 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
559 or auteur
in self
.overops
):
561 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
562 if canal
in self
.chanlist
:
563 self
.chanlist
.remove(canal
)
565 serv
.privmsg(canal
,("%s: %s"%(auteur
,random
.choice(config
.leave_fail_messages
))).encode("utf8"))
566 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
568 elif cmd
in ["reconnect"]:
569 if auteur
in self
.ops
:
571 self
.nk
=self
.new_connection_NK(serv
,config
.note_pseudo
,
572 config
.note_password
,"special")[1]
573 except Exception as exc
:
575 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
577 serv
.privmsg(canal
,"%s: done"%(auteur))
578 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
580 serv
.privmsg(canal
,"%s: failed"%(auteur))
581 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
582 for report
in self
.report_bugs_to
:
583 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ? Server dead ?")
585 serv
.privmsg(canal
,"%s: %s"%(auteur
,random
.choice(config
.pas_programme_pour_tobeir
).encode("utf8")))
586 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
588 elif cmd
in ["deviens","pseudo"]:
589 if auteur
in self
.ops
:
592 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
594 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
595 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
596 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
597 serv
.privmsg(canal
,"%s: pong"%(auteur))
599 elif cmd
in ["solde","!solde"]:
600 if self
.identities
.has_key(auteur
):
601 pseudo
=self
.identities
[auteur
]
603 self
.nk
.write('["search", ["x",["pseudo"],%s]]'%(json
.dumps(pseudo
)))
604 ret
=json
.loads(self
.nk
.read())
605 solde
=ret
["msg"][0]["solde"]
606 pseudo
=ret
["msg"][0]["pseudo"]
607 except Exception as exc
:
608 serv
.privmsg(canal
,"%s: failed"%(auteur))
609 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
611 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
612 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
614 serv
.privmsg(canal
,"%s: Je ne connais pas votre pseudo note."%(auteur))
615 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
616 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
617 and not canal
in self
.quiet_channels
):
618 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
619 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
620 if auteur
in config
.manzana
:
621 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
622 elif auteur
in config
.manzana_bis
:
623 serv
.action(canal
,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
625 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
626 if is_insult(message
) and not canal
in self
.quiet_channels
:
627 if is_not_insult(message
):
628 answer
=random
.choice(config
.compliment_answers
)
629 for ligne
in answer
.split("\n"):
630 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
632 answer
=random
.choice(config
.insultes_answers
)
633 for ligne
in answer
.split("\n"):
634 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
635 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
636 answer
=random
.choice(config
.compliment_answers
)
637 for ligne
in answer
.split("\n"):
638 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
639 gros_match
=is_gros(message
)
640 if gros_match
and not canal
in self
.quiet_channels
:
641 taille
=get_filesize()
642 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
643 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
644 if is_tesla(message
) and not canal
in self
.quiet_channels
:
645 l1
,l2
=config
.tesla_answers
,config
.tesla_actions
646 n1
,n2
=len(l1
),len(l2
)
647 i
=random
.randrange(n1
+n2
)
649 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
651 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
652 if is_tamere(message
) and not canal
in self
.quiet_channels
:
653 answer
=random
.choice(config
.tamere_answers
)
654 for ligne
in answer
.split("\n"):
655 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
656 if is_tag(message
) and not canal
in self
.quiet_channels
:
657 if auteur
in self
.ops
:
658 action
=random
.choice(config
.tag_actions
)
659 serv
.action(canal
,action
.encode("utf8"))
660 self
.quiet_channels
.append(canal
)
662 answer
=random
.choice(config
.tag_answers
)
663 for ligne
in answer
.split("\n"):
664 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
665 if is_merci(message
):
666 answer
=random
.choice(config
.merci_answers
)
667 for ligne
in answer
.split("\n"):
668 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
669 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
670 unicode(message
.upper(),"utf8"))
671 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
672 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
673 if out
and not canal
in self
.quiet_channels
:
674 out
= out
.groups()[0]
677 serv
.privmsg(canal
,"%s: %s !"%(auteur
, iout
+ 1))
678 if iout
== 2147483647:
679 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…" % (auteur
))
681 if iout
+ 1 > 1000 and random
.randrange(4)==0:
682 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
684 except Exception as exc
:
686 if re
.match("[A-Y]",out
):
687 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
688 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
690 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
692 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
693 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
695 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
696 for i
in range(20)}[j
]
698 out
=int(translate(out
))
699 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
700 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
702 answer
=random
.choice(config
.night_answers
)
704 answer
=random
.choice(config
.bonjour_answers
)
706 answer
=random
.choice(config
.bonsoir_answers
)
707 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
708 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
709 answer
=random
.choice(config
.bonne_nuit_answers
)
710 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
711 if is_pan(message
) and not canal
in self
.quiet_channels
:
712 serv
.privmsg(canal
,"%s: ce n'est pas sur moi qu'il faut tirer, même si je sais que j'attire l'œil !"%(auteur))
714 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
715 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
716 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
717 if auteur
in config
.manzana
:
718 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
719 elif auteur
in config
.manzana_bis
:
720 serv
.action(canal
,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
722 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
723 if re
.match(u
'^ *(.|§|!|/|/|:|)(w|b) [0-9]+$',message
.decode("utf8")) and not canal
in self
.quiet_channels
:
724 failanswers
=config
.buffer_fail_answers
725 answer
=random
.choice(failanswers
)
726 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
727 if not canal
in self
.quiet_channels
:
729 if re
.match((u
"^("+u
"|".join(config
.bonjour_triggers
)
730 +ur
")( {}| all| tout le monde| (à )?tous)(\.| ?!)?$"
731 ).format(mypseudo
).lower(), message
.decode("utf8").strip().lower()):
732 answer
=random
.choice(config
.bonjour_answers
)
733 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
734 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
735 # proba de perdre sur trigger :
736 # avant 30min (enfin, config) : 0
737 # ensuite, +25%/30min, linéairement
738 deltat
=time
.time()-self
.last_perdu
739 barre
=(deltat
-config
.time_between_perdu
)/(2*3600.0)
740 if random
.uniform(0,1)<barre
:
741 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
742 self
.last_perdu
=time
.time()
744 def on_action(self
, serv
, ev
):
745 action
= ev
.arguments()[0]
746 auteur
= irclib
.nm_to_n(ev
.source())
747 channel
= ev
.target()
749 test
=bot_unicode(action
)
750 except UnicodeBotError
:
751 if config
.utf8_trigger
and not channel
in self
.quiet_channels
:
752 serv
.privmsg(channel
, (u
"%s: %s"%(auteur
,random
.choice(config
.utf8_fail_answers
))).encode("utf8"))
756 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
757 l1
,l2
=config
.bad_action_answers
,config
.bad_action_actions
758 n1
,n2
=len(l1
),len(l2
)
759 i
=random
.randrange(n1
+n2
)
761 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
763 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
764 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
765 l1
,l2
=config
.good_action_answers
,config
.good_action_actions
766 n1
,n2
=len(l1
),len(l2
)
767 i
=random
.randrange(n1
+n2
)
769 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
771 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
773 def on_kick(self
,serv
,ev
):
774 auteur
= irclib
.nm_to_n(ev
.source())
775 channel
= ev
.target()
776 victime
= ev
.arguments()[0]
777 raison
= ev
.arguments()[1]
778 if victime
==self
.nick
:
779 log(self
.serveur
,"%s kické de %s par %s (raison : %s)" %(victime
,channel
,auteur
,raison
))
782 l1
,l2
=config
.kick_answers
,config
.kick_actions
783 n1
,n2
=len(l1
),len(l2
)
784 i
=random
.randrange(n1
+n2
)
786 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
788 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
790 def quitter(self
,chan
,leave_message
=None):
791 if leave_message
==None:
792 leave_message
=random
.choice(config
.leave_messages
)
793 self
.serv
.part(chan
,message
=leave_message
.encode("utf8"))
796 quit_message
=random
.choice(config
.quit_messages
)
797 self
.die(msg
=quit_message
.encode("utf8"))
800 return self
.serv
.get_nickname()
801 nick
=property(_getnick
)
803 def reload(self
, auteur
=None):
805 if auteur
in [None, "SIGHUP"]:
806 towrite
= "Config reloaded" + " (SIGHUP received)"*(auteur
== "SIGHUP")
807 for to
in config
.report_bugs_to
:
808 self
.serv
.privmsg(to
, towrite
)
809 log(self
.serveur
, towrite
)
811 self
.serv
.privmsg(auteur
,"Config reloaded")
813 def crash(self
, chan
="nowhere", who
="nobody"):
814 where
= u
"en privé" if chan
== "priv" else u
"sur le chan %s" % chan
815 raise CrashError(u
"Crash demandé par %s %s" % (who
, where
))
817 def start_as_daemon(self
, outfile
):
818 sys
.stderr
= Logger(outfile
)
822 class Logger(object):
823 """Pour écrire ailleurs que sur stdout"""
824 def __init__(self
, filename
="basile.full.log"):
825 self
.filename
= filename
827 def write(self
, message
):
828 f
= open(self
.filename
, "a")
834 print "Usage : basile.py <serveur> [--debug] [--no-output] [--daemon [--pidfile]] [--outfile]"
835 print " --outfile sans --no-output ni --daemon n'a aucun effet"
838 if "--daemon" in sys
.argv
:
839 thisfile
= os
.path
.realpath(__file__
)
840 thisdirectory
= thisfile
.rsplit("/", 1)[0]
841 os
.chdir(thisdirectory
)
845 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
849 if "--quiet" in sys
.argv
:
850 config
.debug_stdout
=False
851 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
852 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
853 if "--no-output" in sys
.argv
or "--daemon" in sys
.argv
:
854 outfile
= "/var/log/bots/basile.full.log"
857 if arg
[0].strip('-') in ["out", "outfile", "logfile"]:
859 sys
.stdout
= Logger(outfile
)
861 serveur
=serveurs
[serveur
]
863 print "Server Unknown : %s"%(serveur)
865 basile
=Basile(serveur
,debug
)
866 # Si on reçoit un SIGHUP, on reload la config
867 def sighup_handler(signum
, frame
):
868 basile
.reload("SIGHUP")
869 signal
.signal(signal
.SIGHUP
, sighup_handler
)
871 child_pid
= os
.fork()
874 basile
.start_as_daemon(outfile
)
876 # on enregistre le pid de basile
877 pidfile
= "/var/run/bots/basile.pid"
880 if arg
[0].strip('-') in ["pidfile"]:
882 f
= open(pidfile
, "w")
883 f
.write("%s\n" % child_pid
)
888 if __name__
== "__main__":