]> gitweb.pimeys.fr Git - bots/basile.git/blob - basile.py
Et Basile 2.0 fût (de bière)
[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 import sys
20 config_debug_stdout=True
21 if "--quiet" in sys.argv:
22 config_debug_stdout=False
23
24 config_irc_password="NK2015BasileB0t"
25 config_irc_pseudo="Basile"
26 config_chanlist=["#bot","#flood"]
27 config_stay_channels=["#bot","#flood"]
28 config_quiet_channels=[]
29 config_note_pseudo="Basile"
30 config_note_password="NK2015BasileB0tr4nd0omp4assword]6_+{#]78{"
31 config_logfile_template="basile.%s.log"
32 def get_config_logfile(serveur):
33 serveurs={"acoeur.crans.org":"acoeur","irc.crans.org":"crans"}
34 return config_logfile_template%(serveurs[serveur])
35 config_overops=["[20-100]","[20-100]_", "PEB"]
36 config_ops=["Nit"]
37 config_report_bugs_to=["[20-100]"]
38
39 # config "ce bot a été codé par 20-100, tu te rappelles ?"
40 config_manzana = ["[20-100]", "Petite-Pste"]
41
42 # config "tu m'traites ?"
43 config_insultes=[u"conna(rd|sse)",u"pute",u"con(|ne)",u"enf(oiré|lure)",
44 u"sal(op(|e(|rie)|ard)|aud)",u"p(e|')tite bite",u"imbécile",u"idiot",u"stupid(|e)",u"débile",u"crétin",
45 u"pétasse",u"enculé",u"chagasse",u"cagole",u"abruti",u"ahuri",u"analphabète",u"andouille",
46 u"atardé",u"avorton",u"bachibouzouk",u"(balais|brosse) (de|à) chiotte(|s)",
47 u"batard",u"blaireau",u"bouffon",u"branque",u"bouseux",u"branleur",u"catin",u"chacal",
48 u"charogne",u"chiant(|e)",u"chieur",u"cochon",u"coprophage",u"couillon",u"crapule",u"crevard",
49 u"cruche",u"cuistre",u"ducon",u"décérébré",
50 u"emmerdeur",u"feignasse",u"fainéant",u"fourbe",u"freluquet",u"frigide",
51 u"garce",u"glandu",u"gogol",u"goujat",u"gourdasse",u"gredin",u"gringalet",u"grognasse",
52 u"naze",u"truie",u"iconoclaste",
53 u"peigne(-|)cul",u"ignare",u"illétré",u"lèche(|-)cul",u"malotru",u"motherfucker",u"nabot",u"nigaud",
54 u"nul",u"escroc",u"pouffiasse",u"pourriture",u"raclure",u"relou",u"sagouin",u"putain",
55 u"péripatéticienne"]
56 config_insultes_answers=[
57 u"Oh non ! Quelle insulte ! Je crois que je ne m'en relèverai jamais…\nEnfin presque.",
58 u"J'entends comme un vague murmure, vous disiez ?",
59 u"Je vais prendre ça pour un compliment.",
60 u"Vous savez, pour vous c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
61 u"Permettez-moi de vous retourner le compliment.",
62 u"Votre indélicatesse vous sied à ravir.",
63 u"Parfois, je me demande pourquoi je fais encore ce métier…",
64 u"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
65
66 # config "à peine quelques kilos octets"
67 config_gros=[u"gros",u"énorme",u"lourd"]
68
69 # config spéciale-iota
70 config_buffer_fail_answers=["Pas de chance !","Révisez vos classiques !","Encore un effort, je sais que vous pouvez le faire. ;)"]
71
72 # config "jeu", d'ailleurs, j'ai perdu.
73 config_premier_groupe_terminaisons=u"(e|es|ons|ez|ent|er(|ai|as|a|ons|ez|ont)|(|er)(ais|ait|ions|iez|aient)|(a(i|s|)|â(mes|tes|t)|èrent)|ass(e(|s|nt)|i(ons|ez))|é(|s|e|es))"
74 config_regexp_etre=u"(être|suis|e(s|t)|so(mmes|nt)|êtes|(ét|ser)(ai(s|t|ent)|i(ons|ent)|)|ser(ai|as|a|ons|ez|ont)|so(i(s|t|ent)|y(ons|ez))|f(u(s|t|rent)|û(mes|tes|t))|fuss(e(|s|nt)|i(ons|ez))|étant)"
75 config_regexp_etre_avec_c=u"c'(e(s|st)|étai(t|ent))"
76 config_regexp_faire=u"fais"
77 config_perdu=[u"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
78 u"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
79 u"perdiss(e(|s|nt)|i(ons|ez))",
80 u"perdu(|s|e|es)",u"perdant(|s|e|es)",u"perte(|s)",
81
82 u"(gagn|trouv)"+config_premier_groupe_terminaisons,u"gagnant(|s|e|es)",u"gain(|s)",
83
84 u"trouvant",u"trouvaille(|s)",
85
86 u"victoire(|s)",u"vaincu(|s|e|es)",
87 u"loose",u"lost",u"looser(|s)",u"win(|ner)(|s)",
88 u"jeu(|x)",u"game(|s)"]
89 config_time_between_perdu_trigger=3600*3 #temps moyen pour perdre en l'absence de trigger
90 config_time_between_perdu_trigger_delta = 30*60 #marge autorisée autour de ^^^
91 config_time_between_perdu=30*60 #temps pendant lequel on ne peut pas perdre
92
93 # config "tais-toi"
94 config_tag_triggers=[u"t(|a)g",u"ta gueule",u"la ferme",u"ferme( |-)la",u"tais-toi",u"chut",u"tu fais trop de bruit",u"tu parles trop"]
95 config_tag_actions=[u"se tait",u"se tient coi"]
96 config_tag_answers=[
97 u"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
98 u"Dès que cela sera utile.",
99 u"Une autre fois, peut-être"]
100
101 # config ping
102 config_tesla_triggers=[u"t('|u )es là \?",u"\?",u"plop \?",u"plouf \?"]
103 config_tesla_answers=[u"Oui, je suis là.",u"J'écoute.",u"En quoi puis-je me rendre utile ?"]
104 config_tesla_actions=[u"est là",u"attend des instructions"]
105
106 # config en cas de non-insulte
107 config_compliment_triggers=[u"gentil",u"cool",u"sympa"]
108 config_compliment_answers=[u"Merci, c'est gentil de votre part. :)",
109 u"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",u"Je vous remercie."]
110
111 # config merci
112 config_merci_triggers=[u"merci",u"remercie",u"thx",u"thank(|s)"]
113 config_merci_answers=[u"Mais de rien.",u"À votre service ;)",u"Quand vous voulez ^^",
114 u"Tout le plaisir est pour moi."]
115
116 # config "ta mère"
117 config_tamere_triggers=[u"ta mère"]
118 config_tamere_answers=[u"Laissez donc ma mère en dehors de ça !",
119 u"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
120 u"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
121 u"""N'avait-on pas dit "pas les mamans" ?"""]
122
123 # config pour les actions désagréables à Basile
124 config_bad_action_triggers=[u"(frappe|cogne|tape)(| sur)",u"(démolit|dégomme|fouette|agresse|tabasse)",
125 u"(vomit|pisse|chie|crache) sur",u"slap(|s)"]
126 config_bad_action_answers=[u"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
127 u"{}: Pourquoi tant de violence en ce monde si doux ?",
128 u"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
129 u"{}: J'aurais préféré que vous ne fassiez pas cela en public."]
130 config_bad_action_actions=[u"prend de la distance, par précaution…",u"esquive",u"est bon pour prendre une semaine de repos… virtuel !"]
131
132 # config pour les actions agréables à Basile
133 config_good_action_triggers=[u"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u"embrasse",u"c(?:â|a)line",u"caresse"]
134 config_good_action_answers=[u":D",u"{}: Moi aussi je vous aime. ♡"]
135 config_good_action_actions=[u"ronronne",u"aimerait exprimer avec des mots simples le bonheur que {} lui procure !"]
136
137 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
138 config_bonjour_triggers=[u"(s|)(a|'|)lu(t|)",u"hello",u"pl(o|i)p",u"pr(ou|ü)t",u"bonjour",u"bonsoir",u"coucou"]
139 config_bonjour_answers=[u"Bien le bonjour, {}.",u"Bonjour {}.",u"{}: bonjour."]
140 config_bonsoir_answers=[u"Bonsoir {} !",u"{}: bonsoir."]
141 config_night_answers=[u"{}: vous m'avez fait peur, je m'étais assoupi !", u"Debout à une heure pareille, {} ? Que vous arrive-t-il ?"]
142 config_daytime = [7,18]
143 config_nighttime = [3, 6]
144
145 # config dodo
146 config_bonne_nuit_triggers=[u"bonne nuit",u"'?nite",u"'?nuit",u"'?night",u"good night",u"'?nunuit"]
147 config_bonne_nuit_answers=[u"{}: thanks, make sweet dreams tonight ! ;)",u"Bonne nuit {}.",u"À demain {}. :)"]
148
149 # config PEB est encore en train d'abuser de ses droits.
150 config_kick_answers=[u"Suis-je de trop ici ?",u"{}: Je m'excuse pour ce bruit indu qui a stimulé votre colère",u"{} a le /kick facile, sans doute la fatigue."]
151 config_kick_actions=[u"sera désormais exemplaire",u"prépare une lettre d'excuses à {}",u"essaiera de ne plus s'attirer les foudres de {}"]
152
153 config_thisfile= os.path.realpath( __file__ )
154 def get_filesize():
155 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
156
157 class NKError(Exception):
158 def __init__(self,msg):
159 Exception.__init__(self)
160 self.msg=msg
161 def __str__(self):
162 return str(self.msg)
163 def __unicode__(self):
164 return unicode(self.msg)
165
166 class NKRefused(NKError):
167 pass
168
169 class NKHelloFailed(NKError):
170 pass
171
172 class NKUnknownError(NKError):
173 pass
174
175 def log(serveur,channel,auteur=None,message=None):
176 f=open(get_config_logfile(serveur),"a")
177 if auteur==message==None:
178 # alors c'est que c'est pas un channel mais juste une ligne de log
179 chain="%s %s"%(time.strftime("%F %T"),channel)
180 else:
181 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
182 f.write(chain+"\n")
183 if config_debug_stdout:
184 print chain
185 f.close()
186
187 def connect_NK():
188 sock=socket.socket()
189 try:
190 # On établit la connexion sur port 4242
191 sock.connect(("127.0.0.1",4242))
192 # On passe en SSL
193 sock=ssl.wrap_socket(sock,ca_certs='../keys/ca_.crt')
194 # On fait un hello
195 sock.write('hello "Basile"')
196 # On récupère la réponse du hello
197 out=sock.read()
198 out=json.loads(out)
199 except Exception as exc:
200 # Si on a foiré quelque part, c'est que le serveur est down
201 raise NKRefused(str(exc))
202 if out["retcode"]==0:
203 return sock
204 elif out["retcode"]==11:
205 raise NKHelloFailed(out["errmsg"])
206 else:
207 raise NKUnknownError(out["errmsg"])
208
209 def login_NK(username,password,typ="bdd"):
210 sock=connect_NK()
211 if typ=="special": # ça c'est pour Basile lui-même
212 masque='["note"]'
213 elif typ=="bdd":
214 masque='[["all"],["all"],false]'
215 try:
216 # Basile a un compte special user
217 commande='login [%s,%s,"%s",%s]'%(json.dumps(username),json.dumps(password),typ,masque)
218 sock.write(commande)
219 out=sock.read()
220 except Exception as exc:
221 # Si on a foiré quelque part, c'est que le serveur est down
222 raise NKRefused(str(exc))
223 # On vérifie ensuite que le login
224 return json.loads(out),sock
225
226
227 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
228 if case_sensitive:
229 chain=unicode(chain,"utf8")
230 else:
231 chain=unicode(chain,"utf8").lower()
232 allmatches="("+"|".join(matches)+")"
233 reg=(avant+allmatches+apres).lower()
234 o=re.match(reg,chain)
235 return o
236
237 def is_insult(chain,debug=True):
238 return is_something(chain,config_insultes,avant=".*(?:^| |')")
239 def is_not_insult(chain):
240 chain=unicode(chain,"utf8")
241 insult_regexp=u"("+u"|".join(config_insultes)+u")"
242 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
243 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
244 if re.match(reg,chain):
245 return True
246 else:
247 return False
248 def is_perdu(chain):
249 return is_something(chain,config_perdu)
250 def is_tag(chain):
251 return is_something(chain,config_tag_triggers)
252 def is_gros(chain):
253 return is_something(chain,config_gros)
254 def is_tesla(chain):
255 return is_something(chain,config_tesla_triggers,avant=u"^",apres=u"$",debug=True)
256 def is_merci(chain):
257 return is_something(chain,config_merci_triggers)
258 def is_tamere(chain):
259 return is_something(chain,config_tamere_triggers)
260 def is_bad_action_trigger(chain,pseudo):
261 return is_something(chain,config_bad_action_triggers,avant=u"^",
262 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
263 def is_good_action_trigger(chain,pseudo):
264 return is_something(chain,config_good_action_triggers,avant=u"^",
265 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
266 def is_bonjour(chain):
267 return is_something(chain,config_bonjour_triggers,avant=u"^")
268 def is_bonne_nuit(chain):
269 return is_something(chain,config_bonne_nuit_triggers,avant=u"^")
270 def is_pan(chain):
271 return re.match(u"^(pan|bim|bang)( .*)?$",unicode(chain,"utf8").lower().strip())
272
273 def is_time(conf):
274 _,_,_,h,m,s,_,_,_=time.localtime()
275 return (conf[0],0,0)<(h,m,s)<(conf[1],0,0)
276 def is_day():
277 return is_time(config_daytime)
278 def is_night():
279 return is_time(config_nighttime)
280
281
282 class UnicodeBotError(Exception):
283 pass
284 def bot_unicode(chain):
285 try:
286 unicode(chain,"utf8")
287 except UnicodeDecodeError as exc:
288 raise UnicodeBotError
289
290 class Basile(ircbot.SingleServerIRCBot):
291 def __init__(self,serveur,debug=False):
292 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
293 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
294 temporary_pseudo,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
295 self.debug=debug
296 self.serveur=serveur
297 self.overops=config_overops
298 self.ops=self.overops+config_ops
299 self.report_bugs_to=config_report_bugs_to
300 self.chanlist=config_chanlist
301 self.sockets={}
302 self.identities=pickle.load(open("identities.pickle","r"))
303 self.stay_channels=config_stay_channels
304 self.quiet_channels=config_quiet_channels
305 self.last_perdu=0
306
307
308 def new_connection_NK(self,serv,username,password,typ="bdd"):
309 try:
310 login_result,sock=login_NK(username,password,typ)
311 droits,retcode,errmsg=login_result["msg"],login_result["retcode"],login_result["errmsg"]
312 except NKRefused as exc:
313 for report in self.report_bugs_to:
314 serv.privmsg(report,"Le Serveur NK2015 est down.")
315 return (False,None)
316 except NKHelloFailed as exc:
317 for report in self.report_bugs_to:
318 serv.privmsg(report,
319 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
320 return (False,None)
321 except NKUnknownError as exc:
322 erreurs=["Une fucking erreur inconnue s'est produite"]
323 erreurs+=str(exc).split("\n")
324 for report in self.report_bugs_to:
325 for err in erreurs:
326 serv.privmsg(report,err)
327 return (False,None)
328 except Exception as exc:
329 # Exception qui ne vient pas de la communication avec le serveur NK2015
330 log(self.serveur,"Erreur dans new_connection_NK\n"+str(exc))
331 return (False,None)
332 if retcode==0:
333 return (True,sock)
334 else:
335 return (False,None)
336
337 def give_me_my_pseudo(self,serv):
338 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
339 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
340 time.sleep(0.3)
341 serv.nick(config_irc_pseudo)
342
343 def on_welcome(self, serv, ev):
344 self.serv=serv # ça serv ira :)
345 self.give_me_my_pseudo(serv)
346 serv.privmsg("NickServ","identify %s"%(config_irc_password))
347 log(self.serveur,"Connected")
348 if self.debug:
349 self.chanlist=["#bot"]
350 for c in self.chanlist:
351 log(self.serveur,"JOIN %s"%(c))
352 serv.join(c)
353 # on ouvre la connexion note de Basile, special user
354 self.nk=self.new_connection_NK(serv,config_note_pseudo,config_note_password,"special")[1]
355 if self.nk==None:
356 for report in self.report_bugs_to:
357 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
358
359 def lost(self,serv,channel,forced=False):
360 if self.last_perdu+config_time_between_perdu<time.time() or forced:
361 if not channel in self.quiet_channels or forced:
362 serv.privmsg(channel,"J'ai perdu !")
363 self.last_perdu=time.time()
364 delay=config_time_between_perdu_trigger
365 delta=config_time_between_perdu_trigger_delta
366 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
367
368 def pourmoi(self, serv, message):
369 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
370 pseudo=self.nick
371 size=len(pseudo)
372 if message[:size]==pseudo and len(message)>size and message[size]==":":
373 return (True,message[size+1:].lstrip(" "))
374 else:
375 return (False,message)
376
377 def on_privmsg(self, serv, ev):
378 message=ev.arguments()[0]
379 auteur = irclib.nm_to_n(ev.source())
380 try:
381 test=bot_unicode(message)
382 except UnicodeBotError:
383 serv.privmsg(auteur,
384 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
385 return
386 message=message.split()
387 cmd=message[0].lower()
388 notunderstood=False
389 if cmd=="connect":
390 if not len(message) in [2,3]:
391 serv.privmsg(auteur,"Syntaxe : CONNECT [<username>] <password>")
392 return
393 username=auteur
394 if len(message)>2:
395 username=(message[1])
396 password=" ".join(message[2:])
397 else:
398 password=" ".join(message[1:])
399 success,sock=self.new_connection_NK(serv,username,password)
400 if success:
401 self.sockets[username]=sock
402 serv.privmsg(auteur,"Connection successful")
403 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
404 else:
405 serv.privmsg(auteur,"Connection failed")
406 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
407
408 elif cmd=="help":
409 helpdico={"connect": """CONNECT [<username>] <password>
410 Ouvre une connexion au serveur NoteKfet.
411 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
412 "identify": """IDENTIFY <username> <password>
413 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
414 Sans paramètre, je réponds sous quel pseudo je te connais.""",
415 "drop":"""DROP <password>
416 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
417 helpmsg_default="""Liste des commandes :
418 HELP Affiche de l'aide sur une commande.
419 CONNECT Ouvre une connection au serveur Note Kfet.
420 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
421 DROP Me fait oublier ton identité.
422 SOLDE Obtenir ton solde"""
423 helpmsg_ops="""
424 JOIN Faire rejoindre un chan
425 LEAVE Faire quitter un chan
426 QUIET Se taire sur un chan
427 NOQUIET Opposé de QUIET
428 LOST Perdre sur un chan
429 SOLDE <pseudo> Donner le solde de quelqu'un"""
430 helpmsg_overops="""
431 SAY Fait envoyer un message sur un chan ou à une personne
432 DO Fait faire une action sur un chan
433 STAY Ignorera les prochains LEAVE pour un chan
434 NOSTAY Opposé de STAY
435 DIE Mourir"""
436 if len(message)==1:
437 helpmsg=helpmsg_default
438 if auteur in self.ops:
439 helpmsg+=helpmsg_ops
440 if auteur in self.overops:
441 helpmsg+=helpmsg_overops
442 else:
443 helpmsg=helpdico.get(message[1].lower(),"Commande inconnue.")
444 for ligne in helpmsg.split("\n"):
445 serv.privmsg(auteur,ligne)
446 elif cmd=="identify":
447 if len(message)==1:
448 if self.identities.has_key(auteur):
449 serv.privmsg(auteur,"Je te connais sous le pseudo note %s."%(
450 self.identities[auteur].encode("utf8")))
451 else:
452 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
453 elif len(message)>=3:
454 username,password=message[1],unicode(" ".join(message[2:]),"utf8")
455 success,_=self.new_connection_NK(serv,username,password)
456 if success:
457 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
458 serv.privmsg(auteur,"Identité enregistrée.")
459 self.identities[auteur]=username
460 pickle.dump(self.identities,open("identities.pickle","w"))
461 else:
462 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
463 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
464 else:
465 serv.privmsg(auteur,u"Syntaxe : IDENTIFY [<username> <password>]")
466 elif cmd=="drop":
467 if len(message)>1:
468 if self.identities.has_key(auteur):
469 password=" ".join(message[1:])
470 success,_=self.new_connection_NK(serv,self.identities[auteur],password)
471 if success:
472 del self.identities[auteur]
473 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
474 pickle.dump(self.identities,open("identities.pickle","w"))
475 serv.privmsg(auteur,"Identité oubliée.")
476 else:
477 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
478 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
479 else:
480 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
481 else:
482 serv.privmsg(auteur,"Syntaxe : DROP <password>")
483 elif cmd=="join":
484 if auteur in self.ops:
485 if len(message)>1:
486 if message[1] in self.chanlist:
487 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
488 else:
489 serv.join(message[1])
490 self.chanlist.append(message[1])
491 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
492 log(self.serveur,"priv",auteur," ".join(message))
493 else:
494 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
495 else:
496 notunderstood=True
497 elif cmd=="leave":
498 if auteur in self.ops and len(message)>1:
499 if message[1] in self.chanlist:
500 if not (message[1] in self.stay_channels) or auteur in self.overops:
501 serv.part(message[1])
502 self.chanlist.remove(message[1])
503 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
504 else:
505 serv.privmsg(auteur,"Non, je reste !")
506 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
507 else:
508 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
509 else:
510 notunderstood=True
511 elif cmd=="stay":
512 if auteur in self.overops:
513 if len(message)>1:
514 if message[1] in self.stay_channels:
515 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
516 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
517 else:
518 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
519 self.stay_channels.append(message[1])
520 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
521 else:
522 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
523 else:
524 notunderstood=True
525 elif cmd=="nostay":
526 if auteur in self.overops:
527 if len(message)>1:
528 if message[1] in self.stay_channels:
529 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
530 self.stay_channels.remove(message[1])
531 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
532 else:
533 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
534 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
535
536 else:
537 notunderstood=True
538 elif cmd=="die":
539 if auteur in self.overops:
540 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
541 self.die()
542 else:
543 notunderstood=True
544 elif cmd=="quiet":
545 if auteur in self.ops:
546 if len(message)>1:
547 if message[1] in self.quiet_channels:
548 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
549 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
550 else:
551 self.quiet_channels.append(message[1])
552 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
553 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
554 else:
555 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
556 else:
557 notunderstood=True
558 elif cmd=="noquiet":
559 if auteur in self.ops:
560 if len(message)>1:
561 if message[1] in self.quiet_channels:
562 self.quiet_channels.remove(message[1])
563 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
564 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
565 else:
566 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
567 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
568 else:
569 notunderstood=True
570 elif cmd=="say":
571 if auteur in self.overops and len(message)>2:
572 serv.privmsg(message[1]," ".join(message[2:]))
573 log(self.serveur,"priv",auteur," ".join(message))
574 elif len(message)<=2:
575 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
576 else:
577 notunderstood=True
578 elif cmd=="do":
579 if auteur in self.overops and len(message)>2:
580 serv.action(message[1]," ".join(message[2:]))
581 log(self.serveur,"priv",auteur," ".join(message))
582 elif len(message)<=2:
583 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
584 else:
585 notunderstood=True
586 elif cmd=="kick":
587 if auteur in self.overops and len(message)>2:
588 serv.kick(message[1],message[2]," ".join(message[3:]))
589 log(self.serveur,"priv",auteur," ".join(message))
590 elif len(message)<=2:
591 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo>")
592 else:
593 notunderstood=True
594 elif cmd=="lost":
595 if auteur in self.ops and len(message)>1:
596 serv.privmsg(message[1],"J'ai perdu !")
597 log(self.serveur,"priv",auteur," ".join(message))
598 elif len(message)<=1:
599 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
600 else:
601 notunderstood=True
602 elif cmd=="solde":
603 if len(message)==1:
604 if self.identities.has_key(auteur):
605 try:
606 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
607 ret=json.loads(self.nk.read())
608 solde=ret["msg"][0]["solde"]
609 pseudo=ret["msg"][0]["pseudo"]
610 except Exception as exc:
611 print exc
612 serv.privmsg(auteur,"failed")
613 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
614 return
615 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
616 else:
617 serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
618 elif auteur in self.ops:
619 try:
620 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
621 ret=json.loads(self.nk.read())
622 solde=ret["msg"][0]["solde"]
623 pseudo=ret["msg"][0]["pseudo"]
624 except Exception as exc:
625 serv.privmsg(auteur,"failed")
626 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
627 return
628 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
629 else:
630 notunderstood=True
631 if notunderstood:
632 serv.privmsg(auteur,"Je n'ai pas compris. Essayez HELP…")
633
634 def on_pubmsg(self, serv, ev):
635 auteur = irclib.nm_to_n(ev.source())
636 canal = ev.target()
637 message = ev.arguments()[0]
638 try:
639 test=bot_unicode(message)
640 except UnicodeBotError:
641 if not canal in self.quiet_channels:
642 serv.privmsg(canal,
643 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
644 return
645 pour_moi,message=self.pourmoi(serv,message)
646 if pour_moi and message.split()!=[]:
647 cmd=message.split()[0].lower()
648 try:
649 args=" ".join(message.split()[1:])
650 except:
651 args=""
652 if cmd in ["meurs","die","crève"]:
653 if auteur in self.overops:
654 log(self.serveur,canal,auteur,message+"[successful]")
655 self.die()
656 else:
657 serv.privmsg(canal,"%s: mourrez vous-même !"%(auteur))
658 log(self.serveur,canal,auteur,message+"[failed]")
659
660 elif cmd in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
661 if auteur in self.ops and (not (canal in self.stay_channels)
662 or auteur in self.overops):
663 serv.part(canal,message="Éjecté par %s"%(auteur))
664 log(self.serveur,canal,auteur,message+"[successful]")
665 if canal in self.chanlist:
666 self.chanlist.remove(canal)
667 else:
668 serv.privmsg(canal,"%s: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(auteur))
669 log(self.serveur,canal,auteur,message+"[failed]")
670
671 elif cmd in ["reconnect"]:
672 if auteur in self.ops:
673 try:
674 self.nk=self.new_connection_NK(serv,config_note_pseudo,
675 config_note_password,"special")[1]
676 except Exception as exc:
677 self.nk=None
678 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
679 if self.nk!=None:
680 serv.privmsg(canal,"%s: done"%(auteur))
681 log(self.serveur,canal,auteur,message+"[successful]")
682 else:
683 serv.privmsg(canal,"%s: failed"%(auteur))
684 log(self.serveur,canal,auteur,message+"[failed]")
685 for report in self.report_bugs_to:
686 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
687 else:
688 serv.privmsg(canal,"%s: crève !"%(auteur))
689 log(self.serveur,canal,auteur,message+"[failed]")
690
691 elif cmd in ["deviens","pseudo"]:
692 if auteur in self.ops:
693 become=args
694 serv.nick(become)
695 log(self.serveur,canal,auteur,message+"[successful]")
696
697 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
698 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
699 elif cmd in ["ping"] and not canal in self.quiet_channels:
700 serv.privmsg(canal,"%s: pong"%(auteur))
701
702 elif cmd in ["solde","!solde"]:
703 if self.identities.has_key(auteur):
704 pseudo=self.identities[auteur]
705 try:
706 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(pseudo)))
707 ret=json.loads(self.nk.read())
708 solde=ret["msg"][0]["solde"]
709 pseudo=ret["msg"][0]["pseudo"]
710 except Exception as exc:
711 serv.privmsg(canal,"%s: failed"%(auteur))
712 log(self.serveur,canal,auteur,message+"[failed]")
713 else:
714 serv.privmsg(canal,"%s: %s (%s)"%(auteur,float(solde)/100,pseudo.encode("utf8")))
715 log(self.serveur,canal,auteur,message+"[successful]")
716 else:
717 serv.privmsg(canal,"%s: Je ne connais pas votre pseudo note."%(auteur))
718 log(self.serveur,canal,auteur,message+"[unknown]")
719 elif (re.match("!?(pain au chocolat|chocolatine)",message.lower())
720 and not canal in self.quiet_channels):
721 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
722 elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
723 if auteur in config_manzana:
724 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
725 else:
726 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
727 if is_insult(message) and not canal in self.quiet_channels:
728 if is_not_insult(message):
729 answer=random.choice(config_compliment_answers)
730 for ligne in answer.split("\n"):
731 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
732 else:
733 answer=random.choice(config_insultes_answers)
734 for ligne in answer.split("\n"):
735 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
736 gros_match=is_gros(message)
737 if gros_match and not canal in self.quiet_channels:
738 taille=get_filesize()
739 answer=u"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match.groups()[0],taille)
740 serv.privmsg(canal,"%s: %s"%(auteur,answer.encode("utf8")))
741 if is_tesla(message) and not canal in self.quiet_channels:
742 l1,l2=config_tesla_answers,config_tesla_actions
743 n1,n2=len(l1),len(l2)
744 i=random.randrange(n1+n2)
745 if i>=n1:
746 serv.action(canal,l2[i-n1].encode("utf8"))
747 else:
748 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
749 if is_tamere(message) and not canal in self.quiet_channels:
750 answer=random.choice(config_tamere_answers)
751 for ligne in answer.split("\n"):
752 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
753 if is_tag(message) and not canal in self.quiet_channels:
754 if auteur in self.ops:
755 action=random.choice(config_tag_actions)
756 serv.action(canal,action.encode("utf8"))
757 self.quiet_channels.append(canal)
758 else:
759 answer=random.choice(config_tag_answers)
760 for ligne in answer.split("\n"):
761 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
762 if is_merci(message):
763 answer=random.choice(config_merci_answers)
764 for ligne in answer.split("\n"):
765 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
766 out=re.match(ur"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
767 unicode(message.upper(),"utf8"))
768 if re.match("ma bite dans ton oreille",message) and not canal in self.quiet_channels:
769 serv.privmsg(canal,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
770 if out and not canal in self.quiet_channels:
771 out=out.groups()[0]
772 try:
773 out=int(out)
774 serv.privmsg(canal,"%s: %s !"%(auteur,out+1))
775 if out==2147483647:
776 serv.privmsg(canal,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
777 return
778 if out+1>1000 and random.randrange(4)==0:
779 serv.privmsg(canal,"%s: Vous savez, moi et les chiffres…"%(auteur))
780 return
781 except Exception as exc:
782 pass
783 if re.match("[A-Y]",out):
784 alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
785 serv.privmsg(canal,"%s: %s !"%(auteur,alphabet[alphabet.index(out)+1]))
786 elif out=="Z":
787 serv.privmsg(canal,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
788 elif out in "[\\":
789 serv.privmsg(canal,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
790 elif re.match(ur"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
791 def translate(mess):
792 return "".join([{u"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i]:u"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i]
793 for i in range(20)}[j]
794 for j in mess])
795 out=int(translate(out))
796 serv.privmsg(canal,"%s: %s !"%(auteur,translate(str(out+1)).encode("utf8")))
797 if is_bonjour(message) and not canal in self.quiet_channels:
798 if is_night():
799 answer=random.choice(config_night_answers)
800 elif is_day():
801 answer=random.choice(config_bonjour_answers)
802 else:
803 answer=random.choice(config_bonsoir_answers)
804 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
805 if is_bonne_nuit(message) and not canal in self.quiet_channels:
806 answer=random.choice(config_bonne_nuit_answers)
807 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
808 if is_pan(message) and not canal in self.quiet_channels:
809 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))
810 else:
811 if message in ["!pain au chocolat","!chocolatine"] and not canal in self.quiet_channels:
812 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
813 if message in ["!manzana"] and not canal in self.quiet_channels:
814 if auteur in config_manzana:
815 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
816 else:
817 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
818 if re.match('^(.|§|:|)(w|b) [0-9]+$',message) and not canal in self.quiet_channels:
819 failanswers=config_buffer_fail_answers
820 answer=random.choice(failanswers)
821 serv.privmsg(canal,"%s: %s"%(auteur,answer))
822 if not canal in self.quiet_channels:
823 mypseudo=self.nick
824 if re.match((u"^("+u"|".join(config_bonjour_triggers)
825 +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
826 ).format(mypseudo).lower(), message.strip().lower()):
827 answer=random.choice(config_bonjour_answers)
828 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
829 if (is_perdu(message) and not canal in self.quiet_channels):
830 # proba de perdre sur trigger :
831 # avant 30min (enfin, config) : 0
832 # ensuite, +25%/30min, linéairement
833 deltat=time.time()-self.last_perdu
834 barre=(deltat-config_time_between_perdu)/(2*3600.0)
835 if random.uniform(0,1)<barre:
836 serv.privmsg(canal,"%s: J'ai perdu !"%(auteur))
837 self.last_perdu=time.time()
838
839 def on_action(self, serv, ev):
840 action = ev.arguments()[0]
841 auteur = irclib.nm_to_n(ev.source())
842 channel = ev.target()
843 try:
844 test=bot_unicode(action)
845 except UnicodeBotError:
846 serv.privmsg(channel,
847 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
848 return
849 mypseudo=self.nick
850
851 if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
852 l1,l2=config_bad_action_answers,config_bad_action_actions
853 n1,n2=len(l1),len(l2)
854 i=random.randrange(n1+n2)
855 if i>=n1:
856 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
857 else:
858 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
859 if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
860 l1,l2=config_good_action_answers,config_good_action_actions
861 n1,n2=len(l1),len(l2)
862 i=random.randrange(n1+n2)
863 if i>=n1:
864 serv.action(channel,l2[i-n1].format(auteur).format(auteur).encode("utf8"))
865 else:
866 serv.privmsg(channel,l1[i].format(auteur).format(auteur).encode("utf8"))
867
868 def on_kick(self,serv,ev):
869 auteur = irclib.nm_to_n(ev.source())
870 channel = ev.target()
871 victime = ev.arguments()[0]
872 raison = ev.arguments()[1]
873 if victime==self.nick:
874 log(self.serveur,"%s kické par %s (raison : %s)" %(victime,auteur,raison))
875 time.sleep(2)
876 serv.join(channel)
877 l1,l2=config_kick_answers,config_kick_actions
878 n1,n2=len(l1),len(l2)
879 i=random.randrange(n1+n2)
880 if i>=n1:
881 serv.action(channel,l2[i-n1].format(auteur).encode("utf8"))
882 else:
883 serv.privmsg(channel,l1[i].format(auteur).encode("utf8"))
884
885 def _getnick(self):
886 return self.serv.get_nickname()
887 nick=property(_getnick)
888
889
890 if __name__=="__main__":
891 import sys
892 if len(sys.argv)==1:
893 print "Usage : basile.py <serveur> [--debug]"
894 exit(1)
895 serveur=sys.argv[1]
896 if "debug" in sys.argv or "--debug" in sys.argv:
897 debug=True
898 else:
899 debug=False
900 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
901 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
902 try:
903 serveur=serveurs[serveur]
904 except KeyError:
905 print "Server Unknown : %s"%(serveur)
906 exit(404)
907 basile=Basile(serveur,debug)
908 basile.start()