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