]> gitweb.pimeys.fr Git - bots/basile.git/blob - basile.py
J'avais déjà prévu de répondre à l'émetteur
[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]_"]
36 config_ops=["PEB","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 calin) à",u"embrasse",u"caline",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_kick_answers=[u"Ben qu'est-ce que j'ai fait ? :(",u"Mais euh, j'ai rien fait de mal…","{} a le /kick facile :)"]
128 config_kick_actions=[u"se tiendra à carreaux",u"essaiera de ne plus provoquer les foudres de {}"]
129
130 config_thisfile= os.path.realpath( __file__ )
131 def get_filesize():
132 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
133
134 class NKError(Exception):
135 def __init__(self,msg):
136 Exception.__init__(self)
137 self.msg=msg
138 def __str__(self):
139 return str(self.msg)
140 def __unicode__(self):
141 return unicode(self.msg)
142
143 class NKRefused(NKError):
144 pass
145
146 class NKHelloFailed(NKError):
147 pass
148
149 class NKUnknownError(NKError):
150 pass
151
152 def log(serveur,channel,auteur=None,message=None):
153 f=open(get_config_logfile(serveur),"a")
154 if auteur==message==None:
155 # alors c'est que c'est pas un channel mais juste une ligne de log
156 chain="%s %s"%(time.strftime("%F %T"),channel)
157 else:
158 chain="%s [%s:%s] %s"%(time.strftime("%F %T"),channel,auteur,message)
159 f.write(chain+"\n")
160 if config_debug_stdout:
161 print chain
162 f.close()
163
164 def connect_NK():
165 sock=socket.socket()
166 try:
167 # On établit la connexion sur port 4242
168 sock.connect(("127.0.0.1",4242))
169 # On passe en SSL
170 sock=ssl.wrap_socket(sock,ca_certs='../keys/ca_.crt')
171 # On fait un hello
172 sock.write('hello "Basile"')
173 # On récupère la réponse du hello
174 out=sock.read()
175 out=json.loads(out)
176 except Exception as exc:
177 # Si on a foiré quelque part, c'est que le serveur est down
178 raise NKRefused(str(exc))
179 if out["retcode"]==0:
180 return sock
181 elif out["retcode"]==11:
182 raise NKHelloFailed(out["errmsg"])
183 else:
184 raise NKUnknownError(out["errmsg"])
185
186 def login_NK(username,password,typ="bdd"):
187 sock=connect_NK()
188 if typ=="special": # ça c'est pour Basile lui-même
189 masque='["note"]'
190 elif typ=="bdd":
191 masque='[["all"],["all"],false]'
192 try:
193 # Basile a un compte special user
194 commande='login [%s,%s,"%s",%s]'%(json.dumps(username),json.dumps(password),typ,masque)
195 sock.write(commande)
196 out=sock.read()
197 except Exception as exc:
198 # Si on a foiré quelque part, c'est que le serveur est down
199 raise NKRefused(str(exc))
200 # On vérifie ensuite que le login
201 return json.loads(out),sock
202
203
204 def is_something(chain,matches,avant=u".*(?:^| )",apres=u"(?:$|\.| |,|;).*",case_sensitive=False,debug=False):
205 if case_sensitive:
206 chain=unicode(chain,"utf8")
207 else:
208 chain=unicode(chain,"utf8").lower()
209 allmatches="("+"|".join(matches)+")"
210 reg=(avant+allmatches+apres).lower()
211 o=re.match(reg,chain)
212 return o
213
214 def is_insult(chain,debug=True):
215 return is_something(chain,config_insultes,avant=".*(?:^| |')")
216 def is_not_insult(chain):
217 chain=unicode(chain,"utf8")
218 insult_regexp=u"("+u"|".join(config_insultes)+u")"
219 middle_regexp=u"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
220 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
221 if re.match(reg,chain):
222 return True
223 else:
224 return False
225 def is_perdu(chain):
226 return is_something(chain,config_perdu)
227 def is_tag(chain):
228 return is_something(chain,config_tag_triggers)
229 def is_gros(chain):
230 return is_something(chain,config_gros)
231 def is_tesla(chain):
232 return is_something(chain,config_tesla_triggers,avant=u"^",apres=u"$",debug=True)
233 def is_merci(chain):
234 return is_something(chain,config_merci_triggers)
235 def is_tamere(chain):
236 return is_something(chain,config_tamere_triggers)
237 def is_bad_action_trigger(chain,pseudo):
238 return is_something(chain,config_bad_action_triggers,avant=u"^",
239 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
240 def is_good_action_trigger(chain,pseudo):
241 return is_something(chain,config_good_action_triggers,avant=u"^",
242 apres="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
243 def is_pan(chain):
244 return re.match(u"^(pan|bim|bang) .*$",unicode(chain,"utf8").lower().strip())
245
246
247
248 class UnicodeBotError(Exception):
249 pass
250 def bot_unicode(chain):
251 try:
252 unicode(chain,"utf8")
253 except UnicodeDecodeError as exc:
254 raise UnicodeBotError
255
256 class Basile(ircbot.SingleServerIRCBot):
257 def __init__(self,serveur,debug=False):
258 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
259 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
260 temporary_pseudo,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
261 self.debug=debug
262 self.serveur=serveur
263 self.overops=config_overops
264 self.ops=self.overops+config_ops
265 self.report_bugs_to=config_report_bugs_to
266 self.chanlist=config_chanlist
267 self.sockets={}
268 self.identities=pickle.load(open("identities.pickle","r"))
269 self.stay_channels=config_stay_channels
270 self.quiet_channels=config_quiet_channels
271 self.last_perdu=0
272
273
274 def new_connection_NK(self,serv,username,password,typ="bdd"):
275 try:
276 login_result,sock=login_NK(username,password,typ)
277 droits,retcode,errmsg=login_result["msg"],login_result["retcode"],login_result["errmsg"]
278 except NKRefused as exc:
279 for report in self.report_bugs_to:
280 serv.privmsg(report,"Le Serveur NK2015 est down.")
281 return (False,None)
282 except NKHelloFailed as exc:
283 for report in self.report_bugs_to:
284 serv.privmsg(report,
285 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
286 return (False,None)
287 except NKUnknownError as exc:
288 erreurs=["Une fucking erreur inconnue s'est produite"]
289 erreurs+=str(exc).split("\n")
290 for report in self.report_bugs_to:
291 for err in erreurs:
292 serv.privmsg(report,err)
293 return (False,None)
294 except Exception as exc:
295 # Exception qui ne vient pas de la communication avec le serveur NK2015
296 log(self.serveur,"Erreur dans new_connection_NK\n"+str(exc))
297 return (False,None)
298 if retcode==0:
299 return (True,sock)
300 else:
301 return (False,None)
302
303 def give_me_my_pseudo(self,serv):
304 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
305 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
306 time.sleep(0.3)
307 serv.nick(config_irc_pseudo)
308
309 def on_welcome(self, serv, ev):
310 self.serv=serv # ça serv ira :)
311 self.give_me_my_pseudo(serv)
312 serv.privmsg("NickServ","identify %s"%(config_irc_password))
313 log(self.serveur,"Connected")
314 if self.debug:
315 self.chanlist=["#bot"]
316 for c in self.chanlist:
317 log(self.serveur,"JOIN %s"%(c))
318 serv.join(c)
319 # on ouvre la connexion note de Basile, special user
320 self.nk=self.new_connection_NK(serv,config_note_pseudo,config_note_password,"special")[1]
321 if self.nk==None:
322 for report in self.report_bugs_to:
323 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
324
325 def lost(self,serv,channel,forced=False):
326 if self.last_perdu+config_time_between_perdu<time.time() or forced:
327 if not channel in self.quiet_channels or forced:
328 serv.privmsg(channel,"J'ai perdu !")
329 self.last_perdu=time.time()
330 delay=config_time_between_perdu_trigger
331 delta=config_time_between_perdu_trigger_delta
332 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
333
334 def try_tamere(self,serv,channel,auteur,message):
335 """Essaye de trigger un ta mère"""
336 #pas à chaque fois quand même
337 if random.randrange(4)==0:
338 debuts=u"("+config_regexp_etre+u"|"+config_regexp_etre_avec_c+u")"
339 adjectifs={u"bon(|ne|s|nes)":u"bonne",
340 u"baisable(|s)":u"baisable",
341 u"faisable(|s)":u"faisable",
342 u"pas ch(ère(|s)|er(|s))":u"pas chère",
343 u"facile(|s)":u"facile",
344 u"chaud(|e|s|es)":u"chaude",
345 u"gratuit(|e|s|es)":u"gratuite",
346 u"payant(|e|s|es)":u"payante",
347 u"ouvert(|e|s|es)":u"ouverte",
348 u"open":u"open",
349 u"plein(|s|es)":u"pleine",
350 u"bien plein(|e|s|es)":u"bien pleine",
351 u"innocent(|e|s|es)":u"innocente"}
352 adj_reg=u"(?P<adjectif>"+u"|".join(adjectifs.keys())+u")"
353 reg=u".*(^| |')"+debuts+u" "+adj_reg+u"($|,|;|\.| ).*"
354 matched=re.match(reg,message)
355 if matched:
356 # il faut repasser l'adjectif au féminin singulier
357 found=matched.groupdict()["adjectif"]
358 for adj in adjectifs.keys():
359 if re.match(adj,found):
360 adjectif=adjectifs[adj]
361 break
362 serv.privmsg(channel,(u"%s: c'est ta mère qui est %s !"%(auteur,adjectif)).encode("utf8"))
363 elif random.randrange(5)==0:
364 # deuxième type de trigger, mais moins probable
365 matched=re.match(adj_reg,message)
366 if matched:
367 found=matched.groupdict()["adjectif"]
368 for adj in adjectifs.keys():
369 if re.match(adj,found):
370 adjectif=adjectifs[adj]
371 break
372 fille=random.choice([u"mère",u"soœur"])
373 serv.privmsg(channel,(u"%s: et ta %s, elle est %s ?"%
374 (auteur,fille,adjectif)).encode("utf8"))
375 else:
376 # troisième type de trigger
377 cpgt=config_premier_groupe_terminaisons
378 verbes={u"tourn"+cpgt:u"tourne",
379 u"balad"+cpgt+u" sur le trottoir":u"se balade sur le trottoir",
380 u"prom(e|è)n"+cpgt+" sur le trottoir":u"se promène sur le trottoir",
381 u"_srqhbkjjn":""}
382 vb_reg=u".*(^| )(?P<verbe>"+"|".join(verbes.keys())+")( |,|;|\.|$)"
383 matched=re.match(vb_reg,message)
384 if matched:
385 found=matched.groupdict()["verbe"]
386 for vb in verbes.keys():
387 if re.match(vb,found):
388 verbe=verbes[vb]
389 break
390 fille=random.choice([u"mère",u"soœur"])
391 serv.privmsg(channel,(u"%s: et ta %s, elle %s ?"%
392 (auteur,fille,verbe)).encode("utf8"))
393 def pourmoi(self, serv, message):
394 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
395 pseudo=self.nick
396 size=len(pseudo)
397 if message[:size]==pseudo and len(message)>size and message[size]==":":
398 return (True,message[size+1:].lstrip(" "))
399 else:
400 return (False,message)
401
402 def on_privmsg(self, serv, ev):
403 message=ev.arguments()[0]
404 auteur = irclib.nm_to_n(ev.source())
405 try:
406 test=bot_unicode(message)
407 except UnicodeBotError:
408 serv.privmsg(auteur,
409 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
410 return
411 message=message.split()
412 cmd=message[0].lower()
413 notunderstood=False
414 if cmd=="connect":
415 if not len(message) in [2,3]:
416 serv.privmsg(auteur,"Syntaxe : CONNECT [<username>] <password>")
417 return
418 username=auteur
419 if len(message)>2:
420 username=(message[1])
421 password=" ".join(message[2:])
422 else:
423 password=" ".join(message[1:])
424 success,sock=self.new_connection_NK(serv,username,password)
425 if success:
426 self.sockets[username]=sock
427 serv.privmsg(auteur,"Connection successful")
428 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
429 else:
430 serv.privmsg(auteur,"Connection failed")
431 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
432
433 elif cmd=="help":
434 helpdico={"connect": """CONNECT [<username>] <password>
435 Ouvre une connexion au serveur NoteKfet.
436 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
437 "identify": """IDENTIFY <username> <password>
438 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
439 Sans paramètre, je réponds sous quel pseudo je te connais.""",
440 "drop":"""DROP <password>
441 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
442 helpmsg_default="""Liste des commandes :
443 HELP Affiche de l'aide sur une commande.
444 CONNECT Ouvre une connection au serveur Note Kfet.
445 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
446 DROP Me fait oublier ton identité.
447 SOLDE Obtenir ton solde"""
448 helpmsg_ops="""
449 JOIN Faire rejoindre un chan
450 LEAVE Faire quitter un chan
451 QUIET Se taire sur un chan
452 NOQUIET Opposé de QUIET
453 LOST Perdre sur un chan
454 SOLDE <pseudo> Donner le solde de quelqu'un"""
455 helpmsg_overops="""
456 SAY Fait envoyer un message sur un chan ou à une personne
457 DO Fait faire une action sur un chan
458 STAY Ignorera les prochains LEAVE pour un chan
459 NOSTAY Opposé de STAY
460 DIE Mourir"""
461 if len(message)==1:
462 helpmsg=helpmsg_default
463 if auteur in self.ops:
464 helpmsg+=helpmsg_ops
465 if auteur in self.overops:
466 helpmsg+=helpmsg_overops
467 else:
468 helpmsg=helpdico.get(message[1].lower(),"Commande inconnue.")
469 for ligne in helpmsg.split("\n"):
470 serv.privmsg(auteur,ligne)
471 elif cmd=="identify":
472 if len(message)==1:
473 if self.identities.has_key(auteur):
474 serv.privmsg(auteur,"Je te connais sous le pseudo note %s."%(
475 self.identities[auteur].encode("utf8")))
476 else:
477 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
478 elif len(message)>=3:
479 username,password=message[1],unicode(" ".join(message[2:]),"utf8")
480 success,_=self.new_connection_NK(serv,username,password)
481 if success:
482 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
483 serv.privmsg(auteur,"Identité enregistrée.")
484 self.identities[auteur]=username
485 pickle.dump(self.identities,open("identities.pickle","w"))
486 else:
487 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
488 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
489 else:
490 serv.privmsg(auteur,u"Syntaxe : IDENTIFY [<username> <password>]")
491 elif cmd=="drop":
492 if len(message)>1:
493 if self.identities.has_key(auteur):
494 password=" ".join(message[1:])
495 success,_=self.new_connection_NK(serv,self.identities[auteur],password)
496 if success:
497 del self.identities[auteur]
498 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
499 pickle.dump(self.identities,open("identities.pickle","w"))
500 serv.privmsg(auteur,"Identité oubliée.")
501 else:
502 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
503 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
504 else:
505 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
506 else:
507 serv.privmsg(auteur,"Syntaxe : DROP <password>")
508 elif cmd=="join":
509 if auteur in self.ops:
510 if len(message)>1:
511 if message[1] in self.chanlist:
512 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
513 else:
514 serv.join(message[1])
515 self.chanlist.append(message[1])
516 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
517 log(self.serveur,"priv",auteur," ".join(message))
518 else:
519 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
520 else:
521 notunderstood=True
522 elif cmd=="leave":
523 if auteur in self.ops and len(message)>1:
524 if message[1] in self.chanlist:
525 if not (message[1] in self.stay_channels) or auteur in self.overops:
526 serv.part(message[1])
527 self.chanlist.remove(message[1])
528 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
529 else:
530 serv.privmsg(auteur,"Non, je reste !")
531 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
532 else:
533 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
534 else:
535 notunderstood=True
536 elif cmd=="stay":
537 if auteur in self.overops:
538 if len(message)>1:
539 if message[1] in self.stay_channels:
540 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
541 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
542 else:
543 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
544 self.stay_channels.append(message[1])
545 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
546 else:
547 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
548 else:
549 notunderstood=True
550 elif cmd=="nostay":
551 if auteur in self.overops:
552 if len(message)>1:
553 if message[1] in self.stay_channels:
554 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
555 self.stay_channels.remove(message[1])
556 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
557 else:
558 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
559 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
560
561 else:
562 notunderstood=True
563 elif cmd=="die":
564 if auteur in self.overops:
565 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
566 self.die()
567 else:
568 notunderstood=True
569 elif cmd=="quiet":
570 if auteur in self.ops:
571 if len(message)>1:
572 if message[1] in self.quiet_channels:
573 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
574 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
575 else:
576 self.quiet_channels.append(message[1])
577 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
578 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
579 else:
580 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
581 else:
582 notunderstood=True
583 elif cmd=="noquiet":
584 if auteur in self.ops:
585 if len(message)>1:
586 if message[1] in self.quiet_channels:
587 self.quiet_channels.remove(message[1])
588 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
589 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
590 else:
591 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
592 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
593 else:
594 notunderstood=True
595 elif cmd=="say":
596 if auteur in self.overops and len(message)>2:
597 serv.privmsg(message[1]," ".join(message[2:]))
598 log(self.serveur,"priv",auteur," ".join(message))
599 elif len(message)<=2:
600 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
601 else:
602 notunderstood=True
603 elif cmd=="do":
604 if auteur in self.overops and len(message)>2:
605 serv.action(message[1]," ".join(message[2:]))
606 log(self.serveur,"priv",auteur," ".join(message))
607 elif len(message)<=2:
608 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
609 else:
610 notunderstood=True
611 elif cmd=="kick":
612 if auteur in self.overops and len(message)>2:
613 serv.kick(message[1],message[2]," ".join(message[3:]))
614 log(self.serveur,"priv",auteur," ".join(message))
615 elif len(message)<=2:
616 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo>")
617 else:
618 notunderstood=True
619 elif cmd=="lost":
620 if auteur in self.ops and len(message)>1:
621 serv.privmsg(message[1],"J'ai perdu !")
622 log(self.serveur,"priv",auteur," ".join(message))
623 elif len(message)<=1:
624 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
625 else:
626 notunderstood=True
627 elif cmd=="solde":
628 if len(message)==1:
629 if self.identities.has_key(auteur):
630 try:
631 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
632 ret=json.loads(self.nk.read())
633 solde=ret["msg"][0]["solde"]
634 pseudo=ret["msg"][0]["pseudo"]
635 except Exception as exc:
636 print exc
637 serv.privmsg(auteur,"failed")
638 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
639 return
640 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
641 else:
642 serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
643 elif auteur in self.ops:
644 try:
645 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
646 ret=json.loads(self.nk.read())
647 solde=ret["msg"][0]["solde"]
648 pseudo=ret["msg"][0]["pseudo"]
649 except Exception as exc:
650 serv.privmsg(auteur,"failed")
651 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
652 return
653 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
654 else:
655 notunderstood=True
656 if notunderstood:
657 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
658
659 def on_pubmsg(self, serv, ev):
660 auteur = irclib.nm_to_n(ev.source())
661 canal = ev.target()
662 message = ev.arguments()[0]
663 try:
664 test=bot_unicode(message)
665 except UnicodeBotError:
666 if not canal in self.quiet_channels:
667 serv.privmsg(canal,
668 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
669 return
670 pour_moi,message=self.pourmoi(serv,message)
671 if pour_moi and message.split()!=[]:
672 cmd=message.split()[0].lower()
673 try:
674 args=" ".join(message.split()[1:])
675 except:
676 args=""
677 if cmd in ["meurs","die","crève"]:
678 if auteur in self.overops:
679 log(self.serveur,canal,auteur,message+"[successful]")
680 self.die()
681 else:
682 serv.privmsg(canal,"%s: crève !"%(auteur))
683 log(self.serveur,canal,auteur,message+"[failed]")
684
685 elif cmd in ["part","leave","dégage"]:
686 if auteur in self.ops and (not (canal in self.stay_channels)
687 or auteur in self.overops):
688 serv.part(canal,message="Éjecté par %s"%(auteur))
689 log(self.serveur,canal,auteur,message+"[successful]")
690 if canal in self.chanlist:
691 self.chanlist.remove(canal)
692 else:
693 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
694 log(self.serveur,canal,auteur,message+"[failed]")
695
696 elif cmd in ["reconnect"]:
697 if auteur in self.ops:
698 try:
699 self.nk=self.new_connection_NK(serv,config_note_pseudo,
700 config_note_password,"special")[1]
701 except Exception as exc:
702 self.nk=None
703 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
704 if self.nk!=None:
705 serv.privmsg(canal,"%s: done"%(auteur))
706 log(self.serveur,canal,auteur,message+"[successful]")
707 else:
708 serv.privmsg(canal,"%s: failed"%(auteur))
709 log(self.serveur,canal,auteur,message+"[failed]")
710 for report in self.report_bugs_to:
711 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
712 else:
713 serv.privmsg(canal,"%s: crève !"%(auteur))
714 log(self.serveur,canal,auteur,message+"[failed]")
715
716 elif cmd in ["deviens","pseudo"]:
717 if auteur in self.ops:
718 become=args
719 serv.nick(become)
720 log(self.serveur,canal,auteur,message+"[successful]")
721
722 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
723 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
724 elif cmd in ["ping"] and not canal in self.quiet_channels:
725 serv.privmsg(canal,"%s: pong"%(auteur))
726
727 elif cmd in ["solde","!solde"]:
728 if self.identities.has_key(auteur):
729 pseudo=self.identities[auteur]
730 try:
731 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(pseudo)))
732 ret=json.loads(self.nk.read())
733 solde=ret["msg"][0]["solde"]
734 pseudo=ret["msg"][0]["pseudo"]
735 except Exception as exc:
736 serv.privmsg(canal,"%s: failed"%(auteur))
737 log(self.serveur,canal,auteur,message+"[failed]")
738 else:
739 serv.privmsg(canal,"%s: %s (%s)"%(auteur,float(solde)/100,pseudo.encode("utf8")))
740 log(self.serveur,canal,auteur,message+"[successful]")
741 else:
742 serv.privmsg(canal,"%s: Je ne connais pas ton pseudo note."%(auteur))
743 log(self.serveur,canal,auteur,message+"[unknown]")
744 elif (re.match("!?(pain au chocolat|chocolatine)",message.lower())
745 and not canal in self.quiet_channels):
746 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
747 elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
748 if auteur=="[20-100]":
749 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
750 else:
751 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
752 if is_insult(message) and not canal in self.quiet_channels:
753 if is_not_insult(message):
754 answer=random.choice(config_compliment_answers)
755 for ligne in answer.split("\n"):
756 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
757 else:
758 answer=random.choice(config_insultes_answers)
759 for ligne in answer.split("\n"):
760 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
761 gros_match=is_gros(message)
762 if gros_match and not canal in self.quiet_channels:
763 taille=get_filesize()
764 answer=u"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match.groups()[0],taille)
765 serv.privmsg(canal,"%s: %s"%(auteur,answer.encode("utf8")))
766 if is_tesla(message) and not canal in self.quiet_channels:
767 l1,l2=config_tesla_answers,config_tesla_actions
768 n1,n2=len(l1),len(l2)
769 i=random.randrange(n1+n2)
770 if i>=n1:
771 serv.action(canal,l2[i-n1].encode("utf8"))
772 else:
773 serv.privmsg(canal,"%s: %s"%(auteur,l1[i].encode("utf8")))
774 if is_tamere(message) and not canal in self.quiet_channels:
775 answer=random.choice(config_tamere_answers)
776 for ligne in answer.split("\n"):
777 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
778 if is_tag(message) and not canal in self.quiet_channels:
779 if auteur in self.ops:
780 action=random.choice(config_tag_actions)
781 serv.action(canal,action.encode("utf8"))
782 self.quiet_channels.append(canal)
783 else:
784 answer=random.choice(config_tag_answers)
785 for ligne in answer.split("\n"):
786 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
787 if is_merci(message):
788 answer=random.choice(config_merci_answers)
789 for ligne in answer.split("\n"):
790 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
791 out=re.match(ur"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
792 unicode(message.upper(),"utf8"))
793 if out and not canal in self.quiet_channels:
794 out=out.groups()[0]
795 try:
796 out=int(out)
797 serv.privmsg(canal,"%s: %s !"%(auteur,out+1))
798 if out+1>1000 and random.randrange(4)==0:
799 serv.privmsg(canal,"%s: Tu sais, je peux continuer longtemps comme ça…"%(auteur))
800 if out==2147483647:
801 serv.privmsg(canal,"%s: Tu croyais m'avoir sur le maxint ? J'suis en python mon vieux, 'va falloir trouver mieux…"%(auteur))
802 return
803 except Exception as exc:
804 pass
805 if re.match("[A-Y]",out):
806 alphabet="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
807 serv.privmsg(canal,"%s: %s !"%(auteur,alphabet[alphabet.index(out)+1]))
808 elif out=="Z":
809 serv.privmsg(canal,"%s: pfff, j'ai l'air malin maintenant… [ ?"%(auteur))
810 elif out in "[\\":
811 serv.privmsg(canal,"%s: nan mais il faut qu'on arrête, ça va finir par poser des problèmes…"%(auteur))
812 elif re.match(ur"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out):
813 def translate(mess):
814 return "".join([{u"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i]:u"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i]
815 for i in range(20)}[j]
816 for j in mess])
817 out=int(translate(out))
818 serv.privmsg(canal,"%s: %s !"%(auteur,translate(str(out+1)).encode("utf8")))
819 if (not canal in self.quiet_channels
820 and re.match((u"^("+"|".join(config_bonjour_triggers)+").*").lower(),message.lower()) ):
821 answer=random.choice(config_bonjour_answers)
822 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
823 if is_pan(message) and not canal in self.quiet_channels:
824 serv.privmsg(canal,"%s: c'est pas sur moi qu'il faut tirer !"%(auteur))
825 else:
826 if message in ["!pain au chocolat","!chocolatine"] and not canal in self.quiet_channels:
827 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
828 if message in ["!manzana"] and not canal in self.quiet_channels:
829 if auteur=="[20-100]":
830 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
831 else:
832 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
833 if re.match('^(.|§|:|)(w|b) [0-9]+$',message) and not canal in self.quiet_channels:
834 failanswers=config_buffer_fail_answers
835 answer=random.choice(failanswers)
836 serv.privmsg(canal,"%s: %s"%(auteur,answer))
837 if not canal in self.quiet_channels:
838 self.try_tamere(serv,canal,auteur,message)
839 mypseudo=self.nick
840 if re.match((u"^("+u"|".join(config_bonjour_triggers)
841 +u")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
842 ).format(mypseudo).lower(), message.strip().lower()):
843 answer=random.choice(config_bonjour_answers)
844 serv.privmsg(canal,answer.format(auteur).encode("utf8"))
845 if (is_perdu(message) and not canal in self.quiet_channels):
846 # proba de perdre sur trigger :
847 # avant 30min (enfin, config) : 0
848 # ensuite, +25%/30min, linéairement
849 deltat=time.time()-self.last_perdu
850 barre=(deltat-config_time_between_perdu)/(2*3600.0)
851 if random.uniform(0,1)<barre:
852 serv.privmsg(canal,"%s: J'ai perdu !"%(auteur))
853 self.last_perdu=time.time()
854
855 def on_action(self, serv, ev):
856 action = ev.arguments()[0]
857 auteur = irclib.nm_to_n(ev.source())
858 channel = ev.target()
859 try:
860 test=bot_unicode(action)
861 except UnicodeBotError:
862 serv.privmsg(channel,
863 "%s : Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
864 return
865 mypseudo=self.nick
866
867 if is_bad_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
868 l1,l2=config_bad_action_answers,config_bad_action_actions
869 n1,n2=len(l1),len(l2)
870 i=random.randrange(n1+n2)
871 if i>=n1:
872 serv.action(channel,l2[i-n1].encode("utf8"))
873 else:
874 serv.privmsg(channel,"%s: %s"%(auteur,l1[i].encode("utf8")))
875 if is_good_action_trigger(action,mypseudo) and not channel in self.quiet_channels:
876 l1,l2=config_good_action_answers,config_good_action_actions
877 n1,n2=len(l1),len(l2)
878 i=random.randrange(n1+n2)
879 if i>=n1:
880 serv.action(channel,l2[i-n1].encode("utf8"))
881 else:
882 serv.privmsg(channel,"%s: %s"%(auteur,l1[i].encode("utf8")))
883
884 def on_kick(self,serv,ev):
885 auteur = irclib.nm_to_n(ev.source())
886 channel = ev.target()
887 victime = ev.arguments()[0]
888 raison = ev.arguments()[1]
889 if victime==self.nick:
890 log(self.serveur,"%s kické par %s (raison : %s)" %(victime,auteur,raison))
891 time.sleep(2)
892 serv.join(channel)
893 l1,l2=config_kick_actions,config_kick_answers
894 n1,n2=len(l1),len(l2)
895 i=random.randrange(n1+n2)
896 if i>=n1:
897 serv.action(channel,l2[i-n1].encode("utf8"))
898 else:
899 serv.privmsg(channel,"%s: %s"%(auteur,l1[i].encode("utf8")))
900
901 def _getnick(self):
902 return self.serv.get_nickname()
903 nick=property(_getnick)
904
905
906 if __name__=="__main__":
907 import sys
908 if len(sys.argv)==1:
909 print "Usage : basile.py <serveur> [--debug]"
910 exit(1)
911 serveur=sys.argv[1]
912 if "debug" in sys.argv or "--debug" in sys.argv:
913 debug=True
914 else:
915 debug=False
916 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
917 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
918 try:
919 serveur=serveurs[serveur]
920 except KeyError:
921 print "Server Unknown : %s"%(serveur)
922 exit(404)
923 basile=Basile(serveur,debug)
924 basile.start()