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