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