]> gitweb.pimeys.fr Git - bots/basile.git/blob - basile.py
Possibilité de reload la config sur demande
[bots/basile.git] / basile.py
1 #!/usr/bin/python
2 # -*- coding:utf8 -*-
3
4 # Codé par 20-100 (commencé le 23/04/12)
5
6 # Un bot IRC qui, un jour, s'interfacera avec la Note Kfet 2015
7
8 import irclib
9 import ircbot
10 import threading
11 import random
12 import time
13 import socket, ssl, json
14 import pickle
15 import re
16 import os
17 from commands import getstatusoutput as ex
18
19 # on récupère la config
20 import config_basile as config
21
22 # la partie qui réfère au fichier lui-même est mieux ici
23 # sinon on réfère la config et pas le fichier lui-même
24 import os
25 config.thisfile= os.path.realpath( __file__ )
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 def get_filesize():
32 return ex("ls -s %s"%(config.thisfile))[1].split()[0]
33
34 class NKError(Exception):
35 def __init__(self,msg):
36 Exception.__init__(self)
37 self.msg=msg
38 def __str__(self):
39 return str(self.msg)
40 def __unicode__(self):
41 return unicode(self.msg)
42
43 class NKRefused(NKError):
44 pass
45
46 class NKHelloFailed(NKError):
47 pass
48
49 class NKUnknownError(NKError):
50 pass
51
52 def log(serveur,channel,auteur=None,message=None):
53 f=open(get_config_logfile(serveur),"a")
54 if auteur==message==None:
55 # alors c'est que c'est pas un channel mais juste une ligne de log
56 chain="%s %s"%(time.strftime("%F %T"),channel)
57 else:
58 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
59 f.write(chain+"\n")
60 if config.debug_stdout:
61 print chain
62 f.close()
63
64 def connect_NK():
65 sock=socket.socket()
66 try:
67 # On établit la connexion sur port 4242
68 sock.connect(("127.0.0.1",4242))
69 # On passe en SSL
70 sock=ssl.wrap_socket(sock,ca_certs='../keys/ca_.crt')
71 # On fait un hello
72 sock.write('hello "Basile"')
73 # On récupère la réponse du hello
74 out=sock.read()
75 out=json.loads(out)
76 except Exception as exc:
77 # Si on a foiré quelque part, c'est que le serveur est down
78 raise NKRefused(str(exc))
79 if out["retcode"]==0:
80 return sock
81 elif out["retcode"]==11:
82 raise NKHelloFailed(out["errmsg"])
83 else:
84 raise NKUnknownError(out["errmsg"])
85
86 def login_NK(username,password,typ="bdd"):
87 sock=connect_NK()
88 if typ=="special": # ça c'est pour Basile lui-même
89 masque='["note"]'
90 elif typ=="bdd":
91 masque='[["all"],["all"],false]'
92 try:
93 # Basile a un compte special user
94 commande='login [%s,%s,"%s",%s]'%(json.dumps(username),json.dumps(password),typ,masque)
95 sock.write(commande)
96 out=sock.read()
97 except Exception as exc:
98 # Si on a foiré quelque part, c'est que le serveur est down
99 raise NKRefused(str(exc))
100 # On vérifie ensuite que le login
101 return json.loads(out),sock
102
103
104 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
105 if case_sensitive:
106 chain=unicode(chain,"utf8")
107 else:
108 chain=unicode(chain,"utf8").lower()
109 allmatches="("+"|".join(matches)+")"
110 reg=(avant+allmatches+apres).lower()
111 o=re.match(reg,chain)
112 return o
113
114 def is_insult(chain,debug=True):
115 return is_something(chain,config.insultes,avant=".*(?:^| |')")
116 def is_not_insult(chain):
117 chain=unicode(chain,"utf8")
118 insult_regexp=u"("+u"|".join(config.insultes)+u")"
119 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
120 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
121 if re.match(reg,chain):
122 return True
123 else:
124 return False
125 def is_compliment(chain,debug=True):
126 return is_something(chain,config.compliment_triggers,avant=".*(?:^| |')")
127 def is_perdu(chain):
128 return is_something(chain,config.perdu)
129 def is_tag(chain):
130 return is_something(chain,config.tag_triggers)
131 def is_gros(chain):
132 return is_something(chain,config.gros)
133 def is_tesla(chain):
134 return is_something(chain,config.tesla_triggers,avant=u"^",apres=u"$",debug=True)
135 def is_merci(chain):
136 return is_something(chain,config.merci_triggers)
137 def is_tamere(chain):
138 return is_something(chain,config.tamere_triggers)
139 def is_bad_action_trigger(chain,pseudo):
140 return is_something(chain,config.bad_action_triggers,avant=u"^",
141 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
142 def is_good_action_trigger(chain,pseudo):
143 return is_something(chain,config.good_action_triggers,avant=u"^",
144 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
145 def is_bonjour(chain):
146 return is_something(chain,config.bonjour_triggers,avant=u"^")
147 def is_bonne_nuit(chain):
148 return is_something(chain,config.bonne_nuit_triggers,avant=u"^")
149 def is_pan(chain):
150 return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
151
152 def is_time(conf):
153 _,_,_,h,m,s,_,_,_=time.localtime()
154 return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
155 def is_day():
156 return is_time(config.daytime)
157 def is_night():
158 return is_time(config.nighttime)
159
160
161 class UnicodeBotError(Exception):
162 pass
163 def bot_unicode(chain):
164 try:
165 unicode(chain,"utf8")
166 except UnicodeDecodeError as exc:
167 raise UnicodeBotError
168
169 class Basile(ircbot.SingleServerIRCBot):
170 def __init__(self,serveur,debug=False):
171 temporary_pseudo=config.irc_pseudo+str(random.randrange(10000,100000))
172 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
173 temporary_pseudo,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
174 self.debug=debug
175 self.serveur=serveur
176 self.overops=config.overops
177 self.ops=self.overops+config.ops
178 self.report_bugs_to=config.report_bugs_to
179 self.chanlist=config.chanlist
180 self.identities=pickle.load(open("identities.pickle","r"))
181 self.stay_channels=config.stay_channels
182 self.quiet_channels=config.quiet_channels
183 self.last_perdu=0
184
185
186 def new_connection_NK(self,serv,username,password,typ="bdd"):
187 try:
188 login_result,sock=login_NK(username,password,typ)
189 droits,retcode,errmsg=login_result["msg"],login_result["retcode"],login_result["errmsg"]
190 except NKRefused as exc:
191 for report in self.report_bugs_to:
192 serv.privmsg(report,"Le Serveur NK2015 est down.")
193 return (False,None)
194 except NKHelloFailed as exc:
195 for report in self.report_bugs_to:
196 serv.privmsg(report,
197 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
198 return (False,None)
199 except NKUnknownError as exc:
200 erreurs=["Une fucking erreur inconnue s'est produite"]
201 erreurs+=str(exc).split("\n")
202 for report in self.report_bugs_to:
203 for err in erreurs:
204 serv.privmsg(report,err)
205 return (False,None)
206 except Exception as exc:
207 # Exception qui ne vient pas de la communication avec le serveur NK2015
208 log(self.serveur,"Erreur dans new_connection_NK\n"+str(exc))
209 return (False,None)
210 if retcode==0:
211 return (True,sock)
212 else:
213 return (False,None)
214
215 def give_me_my_pseudo(self,serv):
216 serv.privmsg("NickServ","RECOVER %s %s"%(config.irc_pseudo,config.irc_password))
217 serv.privmsg("NickServ","RELEASE %s %s"%(config.irc_pseudo,config.irc_password))
218 time.sleep(0.3)
219 serv.nick(config.irc_pseudo)
220
221 def on_welcome(self, serv, ev):
222 self.serv=serv # ça serv ira :)
223 self.give_me_my_pseudo(serv)
224 serv.privmsg("NickServ","identify %s"%(config.irc_password))
225 log(self.serveur,"Connected")
226 if self.debug:
227 self.chanlist=["#bot"]
228 else:
229 serv.privmsg("ChanServ", "INVITE #note-dev")
230 for c in self.chanlist:
231 log(self.serveur,"JOIN %s"%(c))
232 serv.join(c)
233 # on ouvre la connexion note de Basile, special user
234 self.nk=self.new_connection_NK(serv,config.note_pseudo,config.note_password,"special")[1]
235 if self.nk==None:
236 for report in self.report_bugs_to:
237 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
238
239 def lost(self,serv,channel,forced=False):
240 if self.last_perdu+config.time_between_perdu<time.time() or forced:
241 if not channel in self.quiet_channels or forced:
242 serv.privmsg(channel,"J'ai perdu !")
243 self.last_perdu=time.time()
244 delay=config.time_between_perdu_trigger
245 delta=config.time_between_perdu_trigger_delta
246 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
247
248 def pourmoi(self, serv, message):
249 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
250 pseudo=self.nick
251 size=len(pseudo)
252 if message[:size]==pseudo and len(message)>size and message[size]==":":
253 return (True,message[size+1:].lstrip(" "))
254 else:
255 return (False,message)
256
257 def on_privmsg(self, serv, ev):
258 message=ev.arguments()[0]
259 auteur = irclib.nm_to_n(ev.source())
260 try:
261 test=bot_unicode(message)
262 except UnicodeBotError:
263 if config.utf8_trigger:
264 serv.privmsg(auteur, random.choice(config.utf8_fail_answers).encode("utf8"))
265 return
266 message=message.split()
267 cmd=message[0].lower()
268 notunderstood=False
269 if cmd=="help":
270 helpdico={"help":["""HELP <commande>
271 Affiche de l'aide sur la commande""",None,None],
272 "identify": ["""IDENTIFY <username> <password>
273 Vérifie le mot de passe et me permet de savoir à l'avenir quel est votre pseudo note kfet.
274 Sans paramètre, je vous précise sous quel pseudo je vous connais.""",None,None],
275 "drop":["""DROP <password>
276 Vérifie le mot de passe et me fait d'oublier votre pseudo note kfet.""",None,None],
277 "solde": ["""SOLDE
278 Affiche votre solde, si je connais votre pseudo note kfet.""",
279 """SOLDE <pseudo>
280 Affiche le solde de la personne désignée (par son pseudo note).""",None],
281 "join": [None, """JOIN <channel>
282 Me fait rejoindre le channel""",None],
283 "leave": [None,"""LEAVE <channel>
284 Me fait quitter le channel (sauf s'il est dans ma stay_list).""",None],
285 "quiet": [None,"""QUIET <channel>
286 Me rend silencieux sur le channel.""",None],
287 "noquiet": [None,"""NOQUIET <channel>
288 Me rend la parole sur le channel.""",None],
289 "lost": [None,"""LOST <channel>
290 Me fait perdre sur le channel.""",None],
291 "reconnect": [None,"""RECONNECT
292 Établit à nouveau la connexion avec le serveur NK2015""",None],
293 "reload": [None,"""RELOAD
294 Recharge la configuration.""",None],
295 "say": [None,None,"""SAY <channel> <message>
296 Me fait parler sur le channel."""],
297 "do": [None,None,"""DO <channel> <action>
298 Me fait faitre une action (/me) sur le channel."""],
299 "stay": [None,None,"""STAY <channel>
300 Ajoute le channel à ma stay_list."""],
301 "nostay": [None,None,"""NOSTAY <channel>
302 Retire le channel de ma stay_list."""],
303 "ops": [None,None,"""OPS
304 Affiche la liste des ops."""],
305 "overops": [None,None,"""OVEROPS
306 Affiche la liste des overops."""],
307 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
308 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
309 "die": [None,None,"""DIE
310 Me déconnecte du serveur IRC."""]
311 }
312 helpmsg_default="Liste des commandes disponibles :\nHELP IDENTIFY DROP SOLDE"
313 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RECONNECT RELOAD"
314 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
315 op,overop=auteur in self.ops, auteur in self.overops
316 if len(message)==1:
317 helpmsg=helpmsg_default
318 if op:
319 helpmsg+=helpmsg_ops
320 if overop:
321 helpmsg+=helpmsg_overops
322 else:
323 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
324 helpmsg=helpmsgs[0]
325 if op and helpmsgs[1]:
326 if helpmsg:
327 helpmsg+="\n"+helpmsgs[1]
328 else:
329 helpmsg=helpmsgs[1]
330 if overop and helpmsgs[2]:
331 if helpmsg:
332 helpmsg+="\n"+helpmsgs[2]
333 else:
334 helpmsg=helpmsgs[2]
335 for ligne in helpmsg.split("\n"):
336 serv.privmsg(auteur,ligne)
337 elif cmd=="identify":
338 if len(message)==1:
339 if self.identities.has_key(auteur):
340 serv.privmsg(auteur,"Je vous connais sous le pseudo note %s."%(
341 self.identities[auteur].encode("utf8")))
342 else:
343 serv.privmsg(auteur,"Je ne connais pas votre pseudo note.")
344 elif len(message)>=3:
345 username,password=message[1],unicode(" ".join(message[2:]),"utf8")
346 success,_=self.new_connection_NK(serv,username,password)
347 if success:
348 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
349 serv.privmsg(auteur,"Identité enregistrée.")
350 self.identities[auteur]=username
351 pickle.dump(Xself.identities,open("identities.pickle","w"))
352 else:
353 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
354 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
355 else:
356 serv.privmsg(auteur,u"Syntaxe : IDENTIFY [<username> <password>]")
357 elif cmd=="drop":
358 if len(message)>1:
359 if self.identities.has_key(auteur):
360 password=" ".join(message[1:])
361 success,_=self.new_connection_NK(serv,self.identities[auteur],password)
362 if success:
363 del self.identities[auteur]
364 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
365 pickle.dump(self.identities,open("identities.pickle","w"))
366 serv.privmsg(auteur,"Identité oubliée.")
367 else:
368 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
369 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
370 else:
371 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
372 else:
373 serv.privmsg(auteur,"Syntaxe : DROP <password>")
374 elif cmd=="join":
375 if auteur in self.ops:
376 if len(message)>1:
377 if message[1] in self.chanlist:
378 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
379 else:
380 serv.join(message[1])
381 self.chanlist.append(message[1])
382 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
383 log(self.serveur,"priv",auteur," ".join(message))
384 else:
385 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
386 else:
387 notunderstood=True
388 elif cmd=="leave":
389 if auteur in self.ops and len(message)>1:
390 if message[1] in self.chanlist:
391 if not (message[1] in self.stay_channels) or auteur in self.overops:
392 self.quitter(message[1]," ".join(message[2:]))
393 self.chanlist.remove(message[1])
394 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
395 else:
396 serv.privmsg(auteur,"Non, je reste !")
397 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
398 else:
399 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
400 else:
401 notunderstood=True
402 elif cmd=="stay":
403 if auteur in self.overops:
404 if len(message)>1:
405 if message[1] in self.stay_channels:
406 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
407 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
408 else:
409 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
410 self.stay_channels.append(message[1])
411 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
412 else:
413 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
414 else:
415 notunderstood=True
416 elif cmd=="nostay":
417 if auteur in self.overops:
418 if len(message)>1:
419 if message[1] in self.stay_channels:
420 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
421 self.stay_channels.remove(message[1])
422 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
423 else:
424 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
425 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
426
427 else:
428 notunderstood=True
429 elif cmd=="die":
430 if auteur in self.overops:
431 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
432 self.mourir()
433 else:
434 notunderstood=True
435 elif cmd=="reload":
436 if auteur in self.ops:
437 reload(config)
438 serv.privmsg(auteur,"done")
439 else:
440 notunderstood=True
441 elif cmd=="reconnect":
442 if auteur in self.ops:
443 try:
444 self.nk=self.new_connection_NK(serv,config.note_pseudo,
445 config.note_password,"special")[1]
446 except Exception as exc:
447 self.nk=None
448 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
449 if self.nk!=None:
450 serv.privmsg(auteur,"%s: done"%(auteur))
451 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
452 else:
453 serv.privmsg(auteur,"%s: failed"%(auteur))
454 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
455 for report in self.report_bugs_to:
456 serv.privmsg(report,"Connection to NK2015 failed, invalid password ? Server dead ?")
457 else:
458 notunderstood=True
459 elif cmd=="quiet":
460 if auteur in self.ops:
461 if len(message)>1:
462 if message[1] in self.quiet_channels:
463 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
464 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
465 else:
466 self.quiet_channels.append(message[1])
467 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
468 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
469 else:
470 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
471 else:
472 notunderstood=True
473 elif cmd=="noquiet":
474 if auteur in self.ops:
475 if len(message)>1:
476 if message[1] in self.quiet_channels:
477 self.quiet_channels.remove(message[1])
478 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
479 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
480 else:
481 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
482 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
483 else:
484 notunderstood=True
485 elif cmd=="say":
486 if auteur in self.overops and len(message)>2:
487 serv.privmsg(message[1]," ".join(message[2:]))
488 log(self.serveur,"priv",auteur," ".join(message))
489 elif len(message)<=2:
490 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
491 else:
492 notunderstood=True
493 elif cmd=="do":
494 if auteur in self.overops and len(message)>2:
495 serv.action(message[1]," ".join(message[2:]))
496 log(self.serveur,"priv",auteur," ".join(message))
497 elif len(message)<=2:
498 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
499 else:
500 notunderstood=True
501 elif cmd=="kick":
502 if auteur in self.overops and len(message)>2:
503 serv.kick(message[1],message[2]," ".join(message[3:]))
504 log(self.serveur,"priv",auteur," ".join(message))
505 elif len(message)<=2:
506 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
507 else:
508 notunderstood=True
509 elif cmd=="lost":
510 if auteur in self.ops and len(message)>1:
511 serv.privmsg(message[1],"J'ai perdu !")
512 log(self.serveur,"priv",auteur," ".join(message))
513 elif len(message)<=1:
514 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
515 else:
516 notunderstood=True
517 elif cmd=="solde":
518 if len(message)==1:
519 if self.identities.has_key(auteur):
520 try:
521 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
522 ret=json.loads(self.nk.read())
523 solde=ret["msg"][0]["solde"]
524 pseudo=ret["msg"][0]["pseudo"]
525 except Exception as exc:
526 print exc
527 serv.privmsg(auteur,"failed")
528 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
529 return
530 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
531 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
532 else:
533 serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
534 elif auteur in self.ops:
535 try:
536 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
537 ret=json.loads(self.nk.read())
538 solde=ret["msg"][0]["solde"]
539 pseudo=ret["msg"][0]["pseudo"]
540 except Exception as exc:
541 serv.privmsg(auteur,"failed")
542 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
543 return
544 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
545 elif cmd=="ops":
546 if auteur in self.overops:
547 serv.privmsg(auteur," ".join(self.ops))
548 else:
549 notunderstood=True
550 elif cmd=="overops":
551 if auteur in self.overops:
552 serv.privmsg(auteur," ".join(self.overops))
553 else:
554 notunderstood=True
555 else:
556 notunderstood=True
557 if notunderstood:
558 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
559
560 def on_pubmsg(self, serv, ev):
561 auteur = irclib.nm_to_n(ev.source())
562 canal = ev.target()
563 message = ev.arguments()[0]
564 try:
565 test=bot_unicode(message)
566 except UnicodeBotError:
567 if config.utf8_trigger and not canal in self.quiet_channels:
568 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
569 return
570 pour_moi,message=self.pourmoi(serv,message)
571 if pour_moi and message.split()!=[]:
572 cmd=message.split()[0].lower()
573 try:
574 args=" ".join(message.split()[1:])
575 except:
576 args=""
577 if cmd in ["meurs","die","crève"]:
578 if auteur in self.overops:
579 log(self.serveur,canal,auteur,message+"[successful]")
580 self.mourir()
581 else:
582 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
583 log(self.serveur,canal,auteur,message+"[failed]")
584
585 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
586 if auteur in self.ops and (not (canal in self.stay_channels)
587 or auteur in self.overops):
588 self.quitter(canal)
589 log(self.serveur,canal,auteur,message+"[successful]")
590 if canal in self.chanlist:
591 self.chanlist.remove(canal)
592 else:
593 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
594 log(self.serveur,canal,auteur,message+"[failed]")
595
596 elif cmd in ["reconnect"]:
597 if auteur in self.ops:
598 try:
599 self.nk=self.new_connection_NK(serv,config.note_pseudo,
600 config.note_password,"special")[1]
601 except Exception as exc:
602 self.nk=None
603 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
604 if self.nk!=None:
605 serv.privmsg(canal,"%s: done"%(auteur))
606 log(self.serveur,canal,auteur,message+"[successful]")
607 else:
608 serv.privmsg(canal,"%s: failed"%(auteur))
609 log(self.serveur,canal,auteur,message+"[failed]")
610 for report in self.report_bugs_to:
611 serv.privmsg(report,"Connection to NK2015 failed, invalid password ? Server dead ?")
612 else:
613 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config.pas_programme_pour_tobeir).encode("utf8")))
614 log(self.serveur,canal,auteur,message+"[failed]")
615
616 elif cmd in ["deviens","pseudo"]:
617 if auteur in self.ops:
618 become=args
619 serv.nick(become)
620 log(self.serveur,canal,auteur,message+"[successful]")
621
622 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
623 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
624 elif cmd in ["ping"] and not canal in self.quiet_channels:
625 serv.privmsg(canal,"%s: pong"%(auteur))
626
627 elif cmd in ["solde","!solde"]:
628 if self.identities.has_key(auteur):
629 pseudo=self.identities[auteur]
630 try:
631 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(pseudo)))
632 ret=json.loads(self.nk.read())
633 solde=ret["msg"][0]["solde"]
634 pseudo=ret["msg"][0]["pseudo"]
635 except Exception as exc:
636 serv.privmsg(canal,"%s: failed"%(auteur))
637 log(self.serveur,canal,auteur,message+"[failed]")
638 else:
639 serv.privmsg(canal,"%s: %s (%s)"%(auteur,float(solde)/100,pseudo.encode("utf8")))
640 log(self.serveur,canal,auteur,message+"[successful]")
641 else:
642 serv.privmsg(canal,"%s: Je ne connais pas votre pseudo note."%(auteur))
643 log(self.serveur,canal,auteur,message+"[unknown]")
644 elif (re.match("!?(pain au chocolat|chocolatine)",message.lower())
645 and not canal in self.quiet_channels):
646 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
647 elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
648 if auteur in config.manzana:
649 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
650 elif auteur in config.manzana_bis:
651 serv.action(canal,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
652 else:
653 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
654 if is_insult(message) and not canal in self.quiet_channels:
655 if is_not_insult(message):
656 answer=random.choice(config.compliment_answers)
657 for ligne in answer.split("\n"):
658 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
659 else:
660 answer=random.choice(config.insultes_answers)
661 for ligne in answer.split("\n"):
662 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
663 elif is_compliment(message) and not canal in self.quiet_channels:
664 answer=random.choice(config.compliment_answers)
665 for ligne in answer.split("\n"):
666 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
667 gros_match=is_gros(message)
668 if gros_match and not canal in self.quiet_channels:
669 taille=get_filesize()
670 answer=u"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match.groups()[0],taille)
671 serv.privmsg(canal,"%s: %s"%(auteur,answer.encode("utf8")))
672 if is_tesla(message) and not canal in self.quiet_channels:
673 l1,l2=config.tesla_answers,config.tesla_actions
674 n1,n2=len(l1),len(l2)
675 i=random.randrange(n1+n2)
676 if i>=n1:
677 serv.action(canal,l2[i-n1].encode("utf8"))
678 else:
679 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
680 if is_tamere(message) and not canal in self.quiet_channels:
681 answer=random.choice(config.tamere_answers)
682 for ligne in answer.split("\n"):
683 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
684 if is_tag(message) and not canal in self.quiet_channels:
685 if auteur in self.ops:
686 action=random.choice(config.tag_actions)
687 serv.action(canal,action.encode("utf8"))
688 self.quiet_channels.append(canal)
689 else:
690 answer=random.choice(config.tag_answers)
691 for ligne in answer.split("\n"):
692 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
693 if is_merci(message):
694 answer=random.choice(config.merci_answers)
695 for ligne in answer.split("\n"):
696 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
697 out=re.match(ur"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
698 unicode(message.upper(),"utf8"))
699 if re.match("ma bite dans ton oreille",message) and not canal in self.quiet_channels:
700 serv.privmsg(canal,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
701 if out and not canal in self.quiet_channels:
702 out=out.groups()[0]
703 try:
704 out=int(out)
705 serv.privmsg(canal,"%s: %s !"%(auteur,out+1))
706 if out==2147483647:
707 serv.privmsg(canal,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
708 return
709 if out+1>1000 and random.randrange(4)==0:
710 serv.privmsg(canal,"%s: Vous savez, moi et les chiffres…"%(auteur))
711 return
712 except Exception as exc:
713 pass
714 if re.match("[A-Y]",out):
715 alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
716 serv.privmsg(canal,"%s: %s !"%(auteur,alphabet[alphabet.index(out)+1]))
717 elif out=="Z":
718 serv.privmsg(canal,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
719 elif out in "[\\":
720 serv.privmsg(canal,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
721 elif re.match(ur"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
722 def translate(mess):
723 return "".join([{u"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i]:u"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i]
724 for i in range(20)}[j]
725 for j in mess])
726 out=int(translate(out))
727 serv.privmsg(canal,"%s: %s !"%(auteur,translate(str(out+1)).encode("utf8")))
728 if is_bonjour(message) and not canal in self.quiet_channels:
729 if is_night():
730 answer=random.choice(config.night_answers)
731 elif is_day():
732 answer=random.choice(config.bonjour_answers)
733 else:
734 answer=random.choice(config.bonsoir_answers)
735 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
736 if is_bonne_nuit(message) and not canal in self.quiet_channels:
737 answer=random.choice(config.bonne_nuit_answers)
738 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
739 if is_pan(message) and not canal in self.quiet_channels:
740 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))
741 else:
742 if message in ["!pain au chocolat","!chocolatine"] and not canal in self.quiet_channels:
743 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
744 if message in ["!manzana"] and not canal in self.quiet_channels:
745 if auteur in config.manzana:
746 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
747 elif auteur in config.manzana_bis:
748 serv.action(canal,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
749 else:
750 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
751 if re.match(u'^ *(.|§|!|/|/|:|)(w|b) [0-9]+$',message.decode("utf8")) and not canal in self.quiet_channels:
752 failanswers=config.buffer_fail_answers
753 answer=random.choice(failanswers)
754 serv.privmsg(canal,("%s: %s"%(auteur,answer)).encode("utf8"))
755 if not canal in self.quiet_channels:
756 mypseudo=self.nick
757 if re.match((u"^("+u"|".join(config.bonjour_triggers)
758 +ur")( {}| all| tout le monde| (à )?tous)(\.| ?!)?$"
759 ).format(mypseudo).lower(), message.decode("utf8").strip().lower()):
760 answer=random.choice(config.bonjour_answers)
761 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
762 if (is_perdu(message) and not canal in self.quiet_channels):
763 # proba de perdre sur trigger :
764 # avant 30min (enfin, config) : 0
765 # ensuite, +25%/30min, linéairement
766 deltat=time.time()-self.last_perdu
767 barre=(deltat-config.time_between_perdu)/(2*3600.0)
768 if random.uniform(0,1)<barre:
769 serv.privmsg(canal,"%s: J'ai perdu !"%(auteur))
770 self.last_perdu=time.time()
771
772 def on_action(self, serv, ev):
773 action = ev.arguments()[0]
774 auteur = irclib.nm_to_n(ev.source())
775 channel = ev.target()
776 try:
777 test=bot_unicode(action)
778 except UnicodeBotError:
779 if config.utf8_trigger and not channel in self.quiet_channels:
780 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
781 return
782 mypseudo=self.nick
783
784 if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
785 l1,l2=config.bad_action_answers,config.bad_action_actions
786 n1,n2=len(l1),len(l2)
787 i=random.randrange(n1+n2)
788 if i>=n1:
789 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
790 else:
791 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
792 if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
793 l1,l2=config.good_action_answers,config.good_action_actions
794 n1,n2=len(l1),len(l2)
795 i=random.randrange(n1+n2)
796 if i>=n1:
797 serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
798 else:
799 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
800
801 def on_kick(self,serv,ev):
802 auteur = irclib.nm_to_n(ev.source())
803 channel = ev.target()
804 victime = ev.arguments()[0]
805 raison = ev.arguments()[1]
806 if victime==self.nick:
807 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
808 time.sleep(2)
809 serv.join(channel)
810 l1,l2=config.kick_answers,config.kick_actions
811 n1,n2=len(l1),len(l2)
812 i=random.randrange(n1+n2)
813 if i>=n1:
814 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
815 else:
816 serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
817
818 def quitter(self,chan,leave_message=None):
819 if leave_message==None:
820 leave_message=random.choice(config.leave_messages)
821 self.serv.part(chan,message=leave_message.encode("utf8"))
822
823 def mourir(self):
824 quit_message=random.choice(config.quit_messages)
825 self.die(msg=quit_message.encode("utf8"))
826
827 def _getnick(self):
828 return self.serv.get_nickname()
829 nick=property(_getnick)
830
831
832 if __name__=="__main__":
833 import sys
834 if len(sys.argv)==1:
835 print "Usage : basile.py <serveur> [--debug]"
836 exit(1)
837 serveur=sys.argv[1]
838 if "debug" in sys.argv or "--debug" in sys.argv:
839 debug=True
840 else:
841 debug=False
842 if "--quiet" in sys.argv:
843 config.debug_stdout=False
844 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
845 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
846 try:
847 serveur=serveurs[serveur]
848 except KeyError:
849 print "Server Unknown : %s"%(serveur)
850 exit(404)
851 basile=Basile(serveur,debug)
852 basile.start()