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