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