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