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