]> gitweb.pimeys.fr Git - bots/basile.git/blob - basile.py
Renommages de variables
[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"]
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_ction_answers=[u"owi \o/",u"{}: c'est gentil ! ♡"]
122 config_good_action_acions=[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 if re.match(reg,chain):
212 return True
213 return False
214
215 def is_insult(chain,debug=True):
216 return is_something(chain,config_insultes,avant=".*(^| |')")
217 def is_not_insult(chain):
218 chain=unicode(chain,"utf8")
219 insult_regexp=u"("+u"|".join(config_insultes)+u")"
220 middle_regexp=u"(un(|e) ((putain|enfoiré) d(e |'))*|)(| super )( (gros|petit|grand|énorme) |)"
221 reg=".*pas %s%s.*"%(middle_regexp,insult_regexp)
222 if re.match(reg,chain):
223 return True
224 else:
225 return False
226 def is_perdu(chain):
227 return is_something(chain,config_perdu)
228 def is_tag(chain):
229 return is_something(chain,config_tag_triggers)
230 def is_gros(chain):
231 return is_something(chain,config_gros)
232 def is_tesla(chain):
233 return is_something(chain,config_tesla_triggers,avant=u"^",apres=u"$",debug=True)
234 def is_merci(chain):
235 return is_something(chain,config_merci_triggers)
236 def is_tamere(chain):
237 return is_something(chain,config_tamere_triggers)
238 def is_bad_action_trigger(chain,pseudo):
239 return is_something(chain,config_bad_action_triggers,avant=u"^",
240 apres="( [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
241 def is_good_action_trigger(chain,pseudo):
242 return is_something(chain,config_good_action_triggers,avant=u"^",
243 apres="( [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
244 def is_pan(chain):
245 return re.match(u"^(pan|bim|bang) .*$",unicode(chain,"utf8").lower().strip())
246
247
248
249 class UnicodeBotError(Exception):
250 pass
251 def bot_unicode(chain):
252 try:
253 unicode(chain,"utf8")
254 except UnicodeDecodeError as exc:
255 raise UnicodeBotError
256
257 class Basile(ircbot.SingleServerIRCBot):
258 def __init__(self,serveur,debug=False):
259 temporary_pseudo=config_irc_pseudo+str(random.randrange(10000,100000))
260 ircbot.SingleServerIRCBot.__init__(self, [(serveur, 6667)],
261 temporary_pseudo,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
262 self.debug=debug
263 self.serveur=serveur
264 self.overops=config_overops
265 self.ops=self.overops+config_ops
266 self.report_bugs_to=config_report_bugs_to
267 self.chanlist=config_chanlist
268 self.sockets={}
269 self.identities=pickle.load(open("identities.pickle","r"))
270 self.stay_channels=config_stay_channels
271 self.quiet_channels=config_quiet_channels
272 self.last_perdu=0
273
274
275 def new_connection_NK(self,serv,username,password,typ="bdd"):
276 try:
277 login_result,sock=login_NK(username,password,typ)
278 droits,retcode,errmsg=login_result["msg"],login_result["retcode"],login_result["errmsg"]
279 except NKRefused as exc:
280 for report in self.report_bugs_to:
281 serv.privmsg(report,"Le Serveur NK2015 est down.")
282 return (False,None)
283 except NKHelloFailed as exc:
284 for report in self.report_bugs_to:
285 serv.privmsg(report,
286 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
287 return (False,None)
288 except NKUnknownError as exc:
289 erreurs=["Une fucking erreur inconnue s'est produite"]
290 erreurs+=str(exc).split("\n")
291 for report in self.report_bugs_to:
292 for err in erreurs:
293 serv.privmsg(report,err)
294 return (False,None)
295 except Exception as exc:
296 # Exception qui ne vient pas de la communication avec le serveur NK2015
297 log(self.serveur,"Erreur dans new_connection_NK\n"+str(exc))
298 return (False,None)
299 if retcode==0:
300 return (True,sock)
301 else:
302 return (False,None)
303
304 def give_me_my_pseudo(self,serv):
305 serv.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo,config_irc_password))
306 serv.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo,config_irc_password))
307 time.sleep(0.3)
308 serv.nick(config_irc_pseudo)
309
310 def on_welcome(self, serv, ev):
311 self.serv=serv # ça serv ira :)
312 self.give_me_my_pseudo(serv)
313 serv.privmsg("NickServ","identify %s"%(config_irc_password))
314 log(self.serveur,"Connected")
315 if self.debug:
316 self.chanlist=["#bot"]
317 for c in self.chanlist:
318 log(self.serveur,"JOIN %s"%(c))
319 serv.join(c)
320 # on ouvre la connexion note de Basile, special user
321 self.nk=self.new_connection_NK(serv,config_note_pseudo,config_note_password,"special")[1]
322 if self.nk==None:
323 for report in self.report_bugs_to:
324 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
325
326 def lost(self,serv,channel,forced=False):
327 if self.last_perdu+config_time_between_perdu<time.time() or forced:
328 if not channel in self.quiet_channels or forced:
329 serv.privmsg(channel,"J'ai perdu !")
330 self.last_perdu=time.time()
331 delay=config_time_between_perdu_trigger
332 delta=config_time_between_perdu_trigger_delta
333 serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
334
335 def try_tamere(self,serv,channel,auteur,message):
336 """Essaye de trigger un ta mère"""
337 #pas à chaque fois quand même
338 if random.randrange(4)==0:
339 debuts=u"("+config_regexp_etre+u"|"+config_regexp_etre_avec_c+u")"
340 adjectifs={u"bon(|ne|s|nes)":u"bonne",
341 u"baisable(|s)":u"baisable",
342 u"faisable(|s)":u"faisable",
343 u"pas ch(ère(|s)|er(|s))":u"pas chère",
344 u"facile(|s)":u"facile",
345 u"chaud(|e|s|es)":u"chaude",
346 u"gratuit(|e|s|es)":u"gratuite",
347 u"payant(|e|s|es)":u"payante",
348 u"ouvert(|e|s|es)":u"ouverte",
349 u"open":u"open",
350 u"plein(|s|es)":u"pleine",
351 u"bien plein(|e|s|es)":u"bien pleine",
352 u"innocent(|e|s|es)":u"innocente"}
353 adj_reg=u"(?P<adjectif>"+u"|".join(adjectifs.keys())+u")"
354 reg=u".*(^| |')"+debuts+u" "+adj_reg+u"($|,|;|\.| ).*"
355 matched=re.match(reg,message)
356 if matched:
357 # il faut repasser l'adjectif au féminin singulier
358 found=matched.groupdict()["adjectif"]
359 for adj in adjectifs.keys():
360 if re.match(adj,found):
361 adjectif=adjectifs[adj]
362 break
363 serv.privmsg(channel,(u"%s: c'est ta mère qui est %s !"%(auteur,adjectif)).encode("utf8"))
364 elif random.randrange(5)==0:
365 # deuxième type de trigger, mais moins probable
366 matched=re.match(adj_reg,message)
367 if matched:
368 found=matched.groupdict()["adjectif"]
369 for adj in adjectifs.keys():
370 if re.match(adj,found):
371 adjectif=adjectifs[adj]
372 break
373 fille=random.choice([u"mère",u"soœur"])
374 serv.privmsg(channel,(u"%s: et ta %s, elle est %s ?"%
375 (auteur,fille,adjectif)).encode("utf8"))
376 else:
377 # troisième type de trigger
378 cpgt=config_premier_groupe_terminaisons
379 verbes={u"tourn"+cpgt:u"tourne",
380 u"balad"+cpgt+u" sur le trottoir":u"se balade sur le trottoir",
381 u"prom(e|è)n"+cpgt+" sur le trottoir":u"se promène sur le trottoir",
382 u"_srqhbkjjn":""}
383 vb_reg=u".*(^| )(?P<verbe>"+"|".join(verbes.keys())+")( |,|;|\.|$)"
384 matched=re.match(vb_reg,message)
385 if matched:
386 found=matched.groupdict()["verbe"]
387 for vb in verbes.keys():
388 if re.match(vb,found):
389 verbe=verbes[vb]
390 break
391 fille=random.choice([u"mère",u"soœur"])
392 serv.privmsg(channel,(u"%s: et ta %s, elle %s ?"%
393 (auteur,fille,verbe)).encode("utf8"))
394 def pourmoi(self, serv, message):
395 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
396 pseudo=self.nick
397 size=len(pseudo)
398 if message[:size]==pseudo and len(message)>size and message[size]==":":
399 return (True,message[size+1:].lstrip(" "))
400 else:
401 return (False,message)
402
403 def on_privmsg(self, serv, ev):
404 message=ev.arguments()[0]
405 auteur = irclib.nm_to_n(ev.source())
406 try:
407 test=bot_unicode(message)
408 except UnicodeBotError:
409 serv.privmsg(auteur,
410 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
411 return
412 message=message.split()
413 cmd=message[0].lower()
414 notunderstood=False
415 if cmd=="connect":
416 if not len(message) in [2,3]:
417 serv.privmsg(auteur,"Syntaxe : CONNECT [<username>] <password>")
418 return
419 username=auteur
420 if len(message)>2:
421 username=(message[1])
422 password=" ".join(message[2:])
423 else:
424 password=" ".join(message[1:])
425 success,sock=self.new_connection_NK(serv,username,password)
426 if success:
427 self.sockets[username]=sock
428 serv.privmsg(auteur,"Connection successful")
429 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
430 else:
431 serv.privmsg(auteur,"Connection failed")
432 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
433
434 elif cmd=="help":
435 helpdico={"connect": """CONNECT [<username>] <password>
436 Ouvre une connexion au serveur NoteKfet.
437 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
438 "identify": """IDENTIFY <username> <password>
439 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
440 Sans paramètre, je réponds sous quel pseudo je te connais.""",
441 "drop":"""DROP <password>
442 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
443 helpmsg_default="""Liste des commandes :
444 HELP Affiche de l'aide sur une commande.
445 CONNECT Ouvre une connection au serveur Note Kfet.
446 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
447 DROP Me fait oublier ton identité.
448 SOLDE Obtenir ton solde"""
449 helpmsg_ops="""
450 JOIN Faire rejoindre un chan
451 LEAVE Faire quitter un chan
452 QUIET Se taire sur un chan
453 NOQUIET Opposé de QUIET
454 LOST Perdre sur un chan
455 SOLDE <pseudo> Donner le solde de quelqu'un"""
456 helpmsg_overops="""
457 SAY Fait envoyer un message sur un chan ou à une personne
458 DO Fait faire une action sur un chan
459 STAY Ignorera les prochains LEAVE pour un chan
460 NOSTAY Opposé de STAY
461 DIE Mourir"""
462 if len(message)==1:
463 helpmsg=helpmsg_default
464 if auteur in self.ops:
465 helpmsg+=helpmsg_ops
466 if auteur in self.overops:
467 helpmsg+=helpmsg_overops
468 else:
469 helpmsg=helpdico.get(message[1].lower(),"Commande inconnue.")
470 for ligne in helpmsg.split("\n"):
471 serv.privmsg(auteur,ligne)
472 elif cmd=="identify":
473 if len(message)==1:
474 if self.identities.has_key(auteur):
475 serv.privmsg(auteur,"Je te connais sous le pseudo note %s."%(
476 self.identities[auteur].encode("utf8")))
477 else:
478 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
479 elif len(message)>=3:
480 username,password=message[1],unicode(" ".join(message[2:]),"utf8")
481 success,_=self.new_connection_NK(serv,username,password)
482 if success:
483 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
484 serv.privmsg(auteur,"Identité enregistrée.")
485 self.identities[auteur]=username
486 pickle.dump(self.identities,open("identities.pickle","w"))
487 else:
488 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
489 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
490 else:
491 serv.privmsg(auteur,u"Syntaxe : IDENTIFY [<username> <password>]")
492 elif cmd=="drop":
493 if len(message)>1:
494 if self.identities.has_key(auteur):
495 password=" ".join(message[1:])
496 success,_=self.new_connection_NK(serv,self.identities[auteur],password)
497 if success:
498 del self.identities[auteur]
499 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
500 pickle.dump(self.identities,open("identities.pickle","w"))
501 serv.privmsg(auteur,"Identité oubliée.")
502 else:
503 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
504 serv.privmsg(auteur,"Mot de passe invalide. (ou serveur down)")
505 else:
506 serv.privmsg(auteur,"Je ne connais pas ton pseudo note.")
507 else:
508 serv.privmsg(auteur,"Syntaxe : DROP <password>")
509 elif cmd=="join":
510 if auteur in self.ops:
511 if len(message)>1:
512 if message[1] in self.chanlist:
513 serv.privmsg(auteur,"Je suis déjà sur %s"%(message[1]))
514 else:
515 serv.join(message[1])
516 self.chanlist.append(message[1])
517 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
518 log(self.serveur,"priv",auteur," ".join(message))
519 else:
520 serv.privmsg(auteur,"Channels : "+" ".join(self.chanlist))
521 else:
522 notunderstood=True
523 elif cmd=="leave":
524 if auteur in self.ops and len(message)>1:
525 if message[1] in self.chanlist:
526 if not (message[1] in self.stay_channels) or auteur in self.overops:
527 serv.part(message[1])
528 self.chanlist.remove(message[1])
529 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
530 else:
531 serv.privmsg(auteur,"Non, je reste !")
532 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
533 else:
534 serv.privmsg(auteur,"Je ne suis pas sur %s"%(message[1]))
535 else:
536 notunderstood=True
537 elif cmd=="stay":
538 if auteur in self.overops:
539 if len(message)>1:
540 if message[1] in self.stay_channels:
541 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
542 serv.privmsg(auteur,"Je stay déjà sur %s."%(message[1]))
543 else:
544 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
545 self.stay_channels.append(message[1])
546 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
547 else:
548 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
549 else:
550 notunderstood=True
551 elif cmd=="nostay":
552 if auteur in self.overops:
553 if len(message)>1:
554 if message[1] in self.stay_channels:
555 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
556 self.stay_channels.remove(message[1])
557 serv.privmsg(auteur,"Stay channels : "+" ".join(self.stay_channels))
558 else:
559 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
560 serv.privmsg(auteur,"Je ne stay pas sur %s."%(message[1]))
561
562 else:
563 notunderstood=True
564 elif cmd=="die":
565 if auteur in self.overops:
566 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
567 self.die()
568 else:
569 notunderstood=True
570 elif cmd=="quiet":
571 if auteur in self.ops:
572 if len(message)>1:
573 if message[1] in self.quiet_channels:
574 serv.privmsg(auteur,"Je me la ferme déjà sur %s"%(message[1]))
575 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
576 else:
577 self.quiet_channels.append(message[1])
578 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
579 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
580 else:
581 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
582 else:
583 notunderstood=True
584 elif cmd=="noquiet":
585 if auteur in self.ops:
586 if len(message)>1:
587 if message[1] in self.quiet_channels:
588 self.quiet_channels.remove(message[1])
589 serv.privmsg(auteur,"Quiet channels : "+" ".join(self.quiet_channels))
590 log(self.serveur,"priv",auteur," ".join(message)+"[successful]")
591 else:
592 serv.privmsg(auteur,"Je ne me la ferme pas sur %s."%(message[1]))
593 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
594 else:
595 notunderstood=True
596 elif cmd=="say":
597 if auteur in self.overops and len(message)>2:
598 serv.privmsg(message[1]," ".join(message[2:]))
599 log(self.serveur,"priv",auteur," ".join(message))
600 elif len(message)<=2:
601 serv.privmsg(auteur,"Syntaxe : SAY <channel> <message>")
602 else:
603 notunderstood=True
604 elif cmd=="do":
605 if auteur in self.overops and len(message)>2:
606 serv.action(message[1]," ".join(message[2:]))
607 log(self.serveur,"priv",auteur," ".join(message))
608 elif len(message)<=2:
609 serv.privmsg(auteur,"Syntaxe : DO <channel> <action>")
610 else:
611 notunderstood=True
612 elif cmd=="kick":
613 if auteur in self.overops and len(message)>2:
614 serv.kick(message[1],message[2]," ".join(message[3:]))
615 log(self.serveur,"priv",auteur," ".join(message))
616 elif len(message)<=2:
617 serv.privmsg(auteur,"Syntaxe : KICK <channel> <pseudo>")
618 else:
619 notunderstood=True
620 elif cmd=="lost":
621 if auteur in self.ops and len(message)>1:
622 serv.privmsg(message[1],"J'ai perdu !")
623 log(self.serveur,"priv",auteur," ".join(message))
624 elif len(message)<=1:
625 serv.privmsg(auteur,"Syntaxe : LOST <channel>")
626 else:
627 notunderstood=True
628 elif cmd=="solde":
629 if len(message)==1:
630 if self.identities.has_key(auteur):
631 try:
632 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(self.identities[auteur])))
633 ret=json.loads(self.nk.read())
634 solde=ret["msg"][0]["solde"]
635 pseudo=ret["msg"][0]["pseudo"]
636 except Exception as exc:
637 print exc
638 serv.privmsg(auteur,"failed")
639 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
640 return
641 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
642 else:
643 serv.privmsg(canal,"Je ne connais pas ton pseudo note.")
644 elif auteur in self.ops:
645 try:
646 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(message[1])))
647 ret=json.loads(self.nk.read())
648 solde=ret["msg"][0]["solde"]
649 pseudo=ret["msg"][0]["pseudo"]
650 except Exception as exc:
651 serv.privmsg(auteur,"failed")
652 log(self.serveur,"priv",auteur," ".join(message)+"[failed]")
653 return
654 serv.privmsg(auteur,"%s (%s)"%(float(solde)/100,pseudo.encode("utf8")))
655 else:
656 notunderstood=True
657 if notunderstood:
658 serv.privmsg(auteur,"Je n'ai pas compris. Essaye HELP…")
659
660 def on_pubmsg(self, serv, ev):
661 auteur = irclib.nm_to_n(ev.source())
662 canal = ev.target()
663 message = ev.arguments()[0]
664 try:
665 test=bot_unicode(message)
666 except UnicodeBotError:
667 if not canal in self.quiet_channels:
668 serv.privmsg(canal,
669 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
670 return
671 pour_moi,message=self.pourmoi(serv,message)
672 if pour_moi and message.split()!=[]:
673 cmd=message.split()[0].lower()
674 try:
675 args=" ".join(message.split()[1:])
676 except:
677 args=""
678 if cmd in ["meurs","die","crève"]:
679 if auteur in self.overops:
680 log(self.serveur,canal,auteur,message+"[successful]")
681 self.die()
682 else:
683 serv.privmsg(canal,"%s: crève !"%(auteur))
684 log(self.serveur,canal,auteur,message+"[failed]")
685
686 elif cmd in ["part","leave","dégage"]:
687 if auteur in self.ops and (not (canal in self.stay_channels)
688 or auteur in self.overops):
689 serv.part(canal,message="Éjecté par %s"%(auteur))
690 log(self.serveur,canal,auteur,message+"[successful]")
691 if canal in self.chanlist:
692 self.chanlist.remove(canal)
693 else:
694 serv.privmsg(canal,"%s: Non, je reste !"%(auteur))
695 log(self.serveur,canal,auteur,message+"[failed]")
696
697 elif cmd in ["reconnect"]:
698 if auteur in self.ops:
699 try:
700 self.nk=self.new_connection_NK(serv,config_note_pseudo,
701 config_note_password,"special")[1]
702 except Exception as exc:
703 self.nk=None
704 log(self.serveur,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc))
705 if self.nk!=None:
706 serv.privmsg(canal,"%s: done"%(auteur))
707 log(self.serveur,canal,auteur,message+"[successful]")
708 else:
709 serv.privmsg(canal,"%s: failed"%(auteur))
710 log(self.serveur,canal,auteur,message+"[failed]")
711 for report in self.report_bugs_to:
712 serv.privmsg(report,"Connection to NK2015 failed, invalid password ?")
713 else:
714 serv.privmsg(canal,"%s: crève !"%(auteur))
715 log(self.serveur,canal,auteur,message+"[failed]")
716
717 elif cmd in ["deviens","pseudo"]:
718 if auteur in self.ops:
719 become=args
720 serv.nick(become)
721 log(self.serveur,canal,auteur,message+"[successful]")
722
723 if cmd in ["meur", "meurt","meurre","meurres"] and not canal in self.quiet_channels:
724 serv.privmsg(canal,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
725 elif cmd in ["ping"] and not canal in self.quiet_channels:
726 serv.privmsg(canal,"%s: pong"%(auteur))
727
728 elif cmd in ["solde","!solde"]:
729 if self.identities.has_key(auteur):
730 pseudo=self.identities[auteur]
731 try:
732 self.nk.write('search ["x",["pseudo"],%s]'%(json.dumps(pseudo)))
733 ret=json.loads(self.nk.read())
734 solde=ret["msg"][0]["solde"]
735 pseudo=ret["msg"][0]["pseudo"]
736 except Exception as exc:
737 serv.privmsg(canal,"%s: failed"%(auteur))
738 log(self.serveur,canal,auteur,message+"[failed]")
739 else:
740 serv.privmsg(canal,"%s: %s (%s)"%(auteur,float(solde)/100,pseudo.encode("utf8")))
741 log(self.serveur,canal,auteur,message+"[successful]")
742 else:
743 serv.privmsg(canal,"%s: Je ne connais pas ton pseudo note."%(auteur))
744 log(self.serveur,canal,auteur,message+"[unknown]")
745 elif (re.match("!?(pain au chocolat|chocolatine)",message.lower())
746 and not canal in self.quiet_channels):
747 serv.action(canal,"sert un pain au chocolat à %s"%(auteur))
748 elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
749 if auteur=="[20-100]":
750 serv.action(canal,"sert une bouteille de manzana à %s"%(auteur))
751 else:
752 serv.action(canal,"sert un verre de manzana à %s"%(auteur))
753 if is_insult(message) and not canal in self.quiet_channels:
754 if is_not_insult(message):
755 answer=random.choice(config_compliment_answers)
756 for ligne in answer.split("\n"):
757 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
758 else:
759 answer=random.choice(config_insultes_answers)
760 for ligne in answer.split("\n"):
761 serv.privmsg(canal,"%s: %s"%(auteur,ligne.encode("utf8")))
762 if is_gros(message) and not canal in self.quiet_channels:
763 taille=get_filesize()
764 answer=u"Mais non, je ne suis pas gros, %sKo tout au plus…"%(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
876 def on_kick(self,serv,ev):
877 auteur = irclib.nm_to_n(ev.source())
878 channel = ev.target()
879 victime = ev.arguments()[0]
880 raison = ev.arguments()[1]
881 if victime==self.nick:
882 log(self.serveur,"%s kické par %s (raison : %s)" %(victime,auteur,raison))
883 time.sleep(2)
884 serv.join(channel)
885 l1,l2=config_kick_actions,config_kick_answers
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 _getnick(self):
894 return self.serv.get_nickname()
895 nick=property(_getnick)
896
897
898 if __name__=="__main__":
899 import sys
900 if len(sys.argv)==1:
901 print "Usage : basile.py <serveur> [--debug]"
902 exit(1)
903 serveur=sys.argv[1]
904 if "debug" in sys.argv or "--debug" in sys.argv:
905 debug=True
906 else:
907 debug=False
908 serveurs={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
909 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
910 try:
911 serveur=serveurs[serveur]
912 except KeyError:
913 print "Server Unknown : %s"%(serveur)
914 exit(404)
915 basile=Basile(serveur,debug)
916 basile.start()