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