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