]> gitweb.pimeys.fr Git - bots/basile.git/blob - basile.py
a0046b3631d2c5ac11734c7836e14c456cde5fc2
[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 "say": [None,None,"""SAY <channel> <message>
294 Me fait parler sur le channel."""],
295 "do": [None,None,"""DO <channel> <action>
296 Me fait faitre une action (/me) sur le channel."""],
297 "stay": [None,None,"""STAY <channel>
298 Ajoute le channel à ma stay_list."""],
299 "nostay": [None,None,"""NOSTAY <channel>
300 Retire le channel de ma stay_list."""],
301 "ops": [None,None,"""OPS
302 Affiche la liste des ops."""],
303 "overops": [None,None,"""OVEROPS
304 Affiche la liste des overops."""],
305 "kick": [None,None,"""KICK <channel> <pseudo> [<raison>]
306 Kicke <pseudo> du channel (Il faut bien entendu que j'y sois op)."""],
307 "die": [None,None,"""DIE
308 Me déconnecte du serveur IRC."""]
309 }
310 helpmsg_default="Liste des commandes disponibles :\nHELP IDENTIFY DROP SOLDE"
311 helpmsg_ops=" JOIN LEAVE QUIET NOQUIET LOST RECONNECT"
312 helpmsg_overops=" SAY DO STAY NOSTAY OPS OVEROPS KICK DIE"
313 op,overop=auteur in self.ops, auteur in self.overops
314 if len(message)==1:
315 helpmsg=helpmsg_default
316 if op:
317 helpmsg+=helpmsg_ops
318 if overop:
319 helpmsg+=helpmsg_overops
320 else:
321 helpmsgs=helpdico.get(message[1].lower(),["Commande inconnue.",None,None])
322 helpmsg=helpmsgs[0]
323 if op and helpmsgs[1]:
324 if helpmsg:
325 helpmsg+="\n"+helpmsgs[1]
326 else:
327 helpmsg=helpmsgs[1]
328 if overop and helpmsgs[2]:
329 if helpmsg:
330 helpmsg+="\n"+helpmsgs[2]
331 else:
332 helpmsg=helpmsgs[2]
333 for ligne in helpmsg.split("\n"):
334 serv.privmsg(auteur,ligne)
335 elif cmd=="identify":
336 if len(message)==1:
337 if self.identities.has_key(auteur):
338 serv.privmsg(auteur,"Je vous connais sous le pseudo note %s."%(
339 self.identities[auteur].encode("utf8")))
340 else:
341 serv.privmsg(auteur,"Je ne connais pas votre pseudo note.")
342 elif len(message)>=3:
343 username,password=message[1],unicode(" ".join(message[2:]),"utf8")
344 success,_=self.new_connection_NK(serv,username,password)
345 if success:
346 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
347 serv.privmsg(auteur,"Identité enregistrée.")
348 self.identities[auteur]=username
349 pickle.dump(Xself.identities,open("identities.pickle","w"))
350 else:
351 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
352 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
353 else:
354 serv.privmsg(auteur,u"Syntaxe : IDENTIFY [<username> <password>]")
355 elif cmd=="drop":
356 if len(message)>1:
357 if self.identities.has_key(auteur):
358 password=" ".join(message[1:])
359 success,_=self.new_connection_NK(serv,self.identities[auteur],password)
360 if success:
361 del self.identities[auteur]
362 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
363 pickle.dump(self.identities,open("identities.pickle","w"))
364 serv.privmsg(auteur,"Identité oubliée.")
365 else:
366 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
367 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
368 else:
369 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
370 else:
371 serv.privmsg(auteur,"Syntaxe : DROP <password>")
372 elif cmd=="join":
373 if auteur in self.ops:
374 if len(message)>1:
375 if message[1] in self.chanlist:
376 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
377 else:
378 serv.join(message[1])
379 self.chanlist.append(message[1])
380 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
381 log(self.serveur,"priv",auteur," ".join(message))
382 else:
383 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
384 else:
385 notunderstood=True
386 elif cmd=="leave":
387 if auteur in self.ops and len(message)>1:
388 if message[1] in self.chanlist:
389 if not (message[1] in self.stay_channels) or auteur in self.overops:
390 self.quitter(message[1]," ".join(message[2:]))
391 self.chanlist.remove(message[1])
392 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
393 else:
394 serv.privmsg(auteur,"Non, je reste !")
395 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
396 else:
397 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
398 else:
399 notunderstood=True
400 elif cmd=="stay":
401 if auteur in self.overops:
402 if len(message)>1:
403 if message[1] in self.stay_channels:
404 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
405 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
406 else:
407 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
408 self.stay_channels.append(message[1])
409 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
410 else:
411 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
412 else:
413 notunderstood=True
414 elif cmd=="nostay":
415 if auteur in self.overops:
416 if len(message)>1:
417 if message[1] in self.stay_channels:
418 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
419 self.stay_channels.remove(message[1])
420 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
421 else:
422 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
423 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
424
425 else:
426 notunderstood=True
427 elif cmd=="die":
428 if auteur in self.overops:
429 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
430 self.mourir()
431 else:
432 notunderstood=True
433 elif cmd=="reconnect":
434 if auteur in self.ops:
435 try:
436 self.nk=self.new_connection_NK(serv,config.note_pseudo,
437 config.note_password,"special")[1]
438 except Exception as exc:
439 self.nk=None
440 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
441 if self.nk!=None:
442 serv.privmsg(auteur,"%s: done"%(auteur))
443 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
444 else:
445 serv.privmsg(auteur,"%s: failed"%(auteur))
446 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
447 for report in self.report_bugs_to:
448 serv.privmsg(report,"Connection to NK2015 failed, invalid password ? Server dead ?")
449 else:
450 notunderstood=True
451 elif cmd=="quiet":
452 if auteur in self.ops:
453 if len(message)>1:
454 if message[1] in self.quiet_channels:
455 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
456 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
457 else:
458 self.quiet_channels.append(message[1])
459 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
460 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
461 else:
462 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
463 else:
464 notunderstood=True
465 elif cmd=="noquiet":
466 if auteur in self.ops:
467 if len(message)>1:
468 if message[1] in self.quiet_channels:
469 self.quiet_channels.remove(message[1])
470 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
471 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
472 else:
473 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
474 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
475 else:
476 notunderstood=True
477 elif cmd=="say":
478 if auteur in self.overops and len(message)>2:
479 serv.privmsg(message[1]," ".join(message[2:]))
480 log(self.serveur,"priv",auteur," ".join(message))
481 elif len(message)<=2:
482 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
483 else:
484 notunderstood=True
485 elif cmd=="do":
486 if auteur in self.overops and len(message)>2:
487 serv.action(message[1]," ".join(message[2:]))
488 log(self.serveur,"priv",auteur," ".join(message))
489 elif len(message)<=2:
490 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
491 else:
492 notunderstood=True
493 elif cmd=="kick":
494 if auteur in self.overops and len(message)>2:
495 serv.kick(message[1],message[2]," ".join(message[3:]))
496 log(self.serveur,"priv",auteur," ".join(message))
497 elif len(message)<=2:
498 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo> [<raison>]")
499 else:
500 notunderstood=True
501 elif cmd=="lost":
502 if auteur in self.ops and len(message)>1:
503 serv.privmsg(message[1],"J'ai perdu !")
504 log(self.serveur,"priv",auteur," ".join(message))
505 elif len(message)<=1:
506 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
507 else:
508 notunderstood=True
509 elif cmd=="solde":
510 if len(message)==1:
511 if self.identities.has_key(auteur):
512 try:
513 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
514 ret=json.loads(self.nk.read())
515 solde=ret["msg"][0]["solde"]
516 pseudo=ret["msg"][0]["pseudo"]
517 except Exception as exc:
518 print exc
519 serv.privmsg(auteur,"failed")
520 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
521 return
522 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
523 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
524 else:
525 serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
526 elif auteur in self.ops:
527 try:
528 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
529 ret=json.loads(self.nk.read())
530 solde=ret["msg"][0]["solde"]
531 pseudo=ret["msg"][0]["pseudo"]
532 except Exception as exc:
533 serv.privmsg(auteur,"failed")
534 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
535 return
536 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
537 elif cmd=="ops":
538 if auteur in self.overops:
539 serv.privmsg(auteur," ".join(self.ops))
540 else:
541 notunderstood=True
542 elif cmd=="overops":
543 if auteur in self.overops:
544 serv.privmsg(auteur," ".join(self.overops))
545 else:
546 notunderstood=True
547 else:
548 notunderstood=True
549 if notunderstood:
550 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
551
552 def on_pubmsg(self, serv, ev):
553 auteur = irclib.nm_to_n(ev.source())
554 canal = ev.target()
555 message = ev.arguments()[0]
556 try:
557 test=bot_unicode(message)
558 except UnicodeBotError:
559 if config.utf8_trigger and not canal in self.quiet_channels:
560 serv.privmsg(canal, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
561 return
562 pour_moi,message=self.pourmoi(serv,message)
563 if pour_moi and message.split()!=[]:
564 cmd=message.split()[0].lower()
565 try:
566 args=" ".join(message.split()[1:])
567 except:
568 args=""
569 if cmd in ["meurs","die","crève"]:
570 if auteur in self.overops:
571 log(self.serveur,canal,auteur,message+"[successful]")
572 self.mourir()
573 else:
574 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.quit_fail_messages))).encode("utf8"))
575 log(self.serveur,canal,auteur,message+"[failed]")
576
577 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
578 if auteur in self.ops and (not (canal in self.stay_channels)
579 or auteur in self.overops):
580 self.quitter(canal)
581 log(self.serveur,canal,auteur,message+"[successful]")
582 if canal in self.chanlist:
583 self.chanlist.remove(canal)
584 else:
585 serv.privmsg(canal,("%s: %s"%(auteur,random.choice(config.leave_fail_messages))).encode("utf8"))
586 log(self.serveur,canal,auteur,message+"[failed]")
587
588 elif cmd in ["reconnect"]:
589 if auteur in self.ops:
590 try:
591 self.nk=self.new_connection_NK(serv,config.note_pseudo,
592 config.note_password,"special")[1]
593 except Exception as exc:
594 self.nk=None
595 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
596 if self.nk!=None:
597 serv.privmsg(canal,"%s: done"%(auteur))
598 log(self.serveur,canal,auteur,message+"[successful]")
599 else:
600 serv.privmsg(canal,"%s: failed"%(auteur))
601 log(self.serveur,canal,auteur,message+"[failed]")
602 for report in self.report_bugs_to:
603 serv.privmsg(report,"Connection to NK2015 failed, invalid password ? Server dead ?")
604 else:
605 serv.privmsg(canal,"%s: %s"%(auteur,random.choice(config.pas_programme_pour_tobeir).encode("utf8")))
606 log(self.serveur,canal,auteur,message+"[failed]")
607
608 elif cmd in ["deviens","pseudo"]:
609 if auteur in self.ops:
610 become=args
611 serv.nick(become)
612 log(self.serveur,canal,auteur,message+"[successful]")
613
614 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
615 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
616 elif cmd in ["ping"] and not canal in self.quiet_channels:
617 serv.privmsg(canal,"%s: pong"%(auteur))
618
619 elif cmd in ["solde","!solde"]:
620 if self.identities.has_key(auteur):
621 pseudo=self.identities[auteur]
622 try:
623 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(pseudo)))
624 ret=json.loads(self.nk.read())
625 solde=ret["msg"][0]["solde"]
626 pseudo=ret["msg"][0]["pseudo"]
627 except Exception as exc:
628 serv.privmsg(canal,"%s: failed"%(auteur))
629 log(self.serveur,canal,auteur,message+"[failed]")
630 else:
631 serv.privmsg(canal,"%s: %s (%s)"%(auteur,float(solde)/100,pseudo.encode("utf8")))
632 log(self.serveur,canal,auteur,message+"[successful]")
633 else:
634 serv.privmsg(canal,"%s: Je ne connais pas votre pseudo note."%(auteur))
635 log(self.serveur,canal,auteur,message+"[unknown]")
636 elif (re.match("!?(pain au chocolat|chocolatine)",message.lower())
637 and not canal in self.quiet_channels):
638 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
639 elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
640 if auteur in config.manzana:
641 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
642 elif auteur in config.manzana_bis:
643 serv.action(canal,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
644 else:
645 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
646 if is_insult(message) and not canal in self.quiet_channels:
647 if is_not_insult(message):
648 answer=random.choice(config.compliment_answers)
649 for ligne in answer.split("\n"):
650 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
651 else:
652 answer=random.choice(config.insultes_answers)
653 for ligne in answer.split("\n"):
654 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
655 elif is_compliment(message) and not canal in self.quiet_channels:
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 gros_match=is_gros(message)
660 if gros_match and not canal in self.quiet_channels:
661 taille=get_filesize()
662 answer=u"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match.groups()[0],taille)
663 serv.privmsg(canal,"%s: %s"%(auteur,answer.encode("utf8")))
664 if is_tesla(message) and not canal in self.quiet_channels:
665 l1,l2=config.tesla_answers,config.tesla_actions
666 n1,n2=len(l1),len(l2)
667 i=random.randrange(n1+n2)
668 if i>=n1:
669 serv.action(canal,l2[i-n1].encode("utf8"))
670 else:
671 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
672 if is_tamere(message) and not canal in self.quiet_channels:
673 answer=random.choice(config.tamere_answers)
674 for ligne in answer.split("\n"):
675 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
676 if is_tag(message) and not canal in self.quiet_channels:
677 if auteur in self.ops:
678 action=random.choice(config.tag_actions)
679 serv.action(canal,action.encode("utf8"))
680 self.quiet_channels.append(canal)
681 else:
682 answer=random.choice(config.tag_answers)
683 for ligne in answer.split("\n"):
684 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
685 if is_merci(message):
686 answer=random.choice(config.merci_answers)
687 for ligne in answer.split("\n"):
688 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
689 out=re.match(ur"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
690 unicode(message.upper(),"utf8"))
691 if re.match("ma bite dans ton oreille",message) and not canal in self.quiet_channels:
692 serv.privmsg(canal,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
693 if out and not canal in self.quiet_channels:
694 out=out.groups()[0]
695 try:
696 out=int(out)
697 serv.privmsg(canal,"%s: %s !"%(auteur,out+1))
698 if out==2147483647:
699 serv.privmsg(canal,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
700 return
701 if out+1>1000 and random.randrange(4)==0:
702 serv.privmsg(canal,"%s: Vous savez, moi et les chiffres…"%(auteur))
703 return
704 except Exception as exc:
705 pass
706 if re.match("[A-Y]",out):
707 alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
708 serv.privmsg(canal,"%s: %s !"%(auteur,alphabet[alphabet.index(out)+1]))
709 elif out=="Z":
710 serv.privmsg(canal,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
711 elif out in "[\\":
712 serv.privmsg(canal,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
713 elif re.match(ur"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
714 def translate(mess):
715 return "".join([{u"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i]:u"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i]
716 for i in range(20)}[j]
717 for j in mess])
718 out=int(translate(out))
719 serv.privmsg(canal,"%s: %s !"%(auteur,translate(str(out+1)).encode("utf8")))
720 if is_bonjour(message) and not canal in self.quiet_channels:
721 if is_night():
722 answer=random.choice(config.night_answers)
723 elif is_day():
724 answer=random.choice(config.bonjour_answers)
725 else:
726 answer=random.choice(config.bonsoir_answers)
727 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
728 if is_bonne_nuit(message) and not canal in self.quiet_channels:
729 answer=random.choice(config.bonne_nuit_answers)
730 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
731 if is_pan(message) and not canal in self.quiet_channels:
732 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))
733 else:
734 if message in ["!pain au chocolat","!chocolatine"] and not canal in self.quiet_channels:
735 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
736 if message in ["!manzana"] and not canal in self.quiet_channels:
737 if auteur in config.manzana:
738 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
739 elif auteur in config.manzana_bis:
740 serv.action(canal,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
741 else:
742 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
743 if re.match(u'^ *(.|§|!|/|/|:|)(w|b) [0-9]+$',message.decode("utf8")) and not canal in self.quiet_channels:
744 failanswers=config.buffer_fail_answers
745 answer=random.choice(failanswers)
746 serv.privmsg(canal,("%s: %s"%(auteur,answer)).encode("utf8"))
747 if not canal in self.quiet_channels:
748 mypseudo=self.nick
749 if re.match((u"^("+u"|".join(config.bonjour_triggers)
750 +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
751 ).format(mypseudo).lower(), message.strip().lower()):
752 answer=random.choice(config.bonjour_answers)
753 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
754 if (is_perdu(message) and not canal in self.quiet_channels):
755 # proba de perdre sur trigger :
756 # avant 30min (enfin, config) : 0
757 # ensuite, +25%/30min, linéairement
758 deltat=time.time()-self.last_perdu
759 barre=(deltat-config.time_between_perdu)/(2*3600.0)
760 if random.uniform(0,1)<barre:
761 serv.privmsg(canal,"%s: J'ai perdu !"%(auteur))
762 self.last_perdu=time.time()
763
764 def on_action(self, serv, ev):
765 action = ev.arguments()[0]
766 auteur = irclib.nm_to_n(ev.source())
767 channel = ev.target()
768 try:
769 test=bot_unicode(action)
770 except UnicodeBotError:
771 if config.utf8_trigger and not channel in self.quiet_channels:
772 serv.privmsg(channel, (u"%s: %s"%(auteur,random.choice(config.utf8_fail_answers))).encode("utf8"))
773 return
774 mypseudo=self.nick
775
776 if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
777 l1,l2=config.bad_action_answers,config.bad_action_actions
778 n1,n2=len(l1),len(l2)
779 i=random.randrange(n1+n2)
780 if i>=n1:
781 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
782 else:
783 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
784 if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
785 l1,l2=config.good_action_answers,config.good_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).format(auteur).encode("utf8"))
790 else:
791 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
792
793 def on_kick(self,serv,ev):
794 auteur = irclib.nm_to_n(ev.source())
795 channel = ev.target()
796 victime = ev.arguments()[0]
797 raison = ev.arguments()[1]
798 if victime==self.nick:
799 log(self.serveur,"%s kické de %s par %s (raison : %s)" %(victime,channel,auteur,raison))
800 time.sleep(2)
801 serv.join(channel)
802 l1,l2=config.kick_answers,config.kick_actions
803 n1,n2=len(l1),len(l2)
804 i=random.randrange(n1+n2)
805 if i>=n1:
806 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
807 else:
808 serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
809
810 def quitter(self,chan,leave_message=None):
811 if leave_message==None:
812 leave_message=random.choice(config.leave_messages)
813 self.serv.part(chan,message=leave_message.encode("utf8"))
814
815 def mourir(self):
816 quit_message=random.choice(config.quit_messages)
817 self.die(msg=quit_message.encode("utf8"))
818
819 def _getnick(self):
820 return self.serv.get_nickname()
821 nick=property(_getnick)
822
823
824 if __name__=="__main__":
825 import sys
826 if len(sys.argv)==1:
827 print "Usage : basile.py <serveur> [--debug]"
828 exit(1)
829 serveur=sys.argv[1]
830 if "debug" in sys.argv or "--debug" in sys.argv:
831 debug=True
832 else:
833 debug=False
834 if "--quiet" in sys.argv:
835 config.debug_stdout=False
836 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
837 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
838 try:
839 serveur=serveurs[serveur]
840 except KeyError:
841 print "Server Unknown : %s"%(serveur)
842 exit(404)
843 basile=Basile(serveur,debug)
844 basile.start()