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