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