4 # Codé par 20-100 le 23/04/12
6 # Un test de bot irc, parce que c'est cool
13 import socket
, ssl
, json
17 from commands
import getstatusoutput
as ex
20 config_debug_stdout
=True
21 if "--quiet" in sys
.argv
:
22 config_debug_stdout
=False
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]"]
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",
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 !"]
61 config_gros
=[u
"gros",u
"énorme",u
"lourd"]
63 config_buffer_fail_answers
=["haha !","You type like you drive","encore un effort ;)"]
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)",
74 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
76 u
"trouvant",u
"trouvaille(|s)",
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
85 config_tag_triggers
=[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 :°",
90 u
"Non, j'ai pas envie",
91 u
"Peut-être quand toi tu la fermeras, et encore…"]
93 config_tesla_triggers
=[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"]
97 config_compliment_triggers
=[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."]
100 config_merci_triggers
=[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."]
104 config_tamere_triggers
=[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 ?",
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\""]
112 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse)",
113 u
"vomit sur",u
"slap(|s)"]
114 config_bad_action_answers
=[u
"Hey ! Mais qu'est-ce que j'ai fait ?",
117 u
"Mais j'ai rien demandé moi !"]
118 config_bad_action_actions
=[u
"prend de la distance, par précaution…",u
"part en courant",u
"esquive"]
120 config_good_action_triggers
=[u
"fait (des bisous|un calin) à",u
"embrasse",u
"caline",u
"caresse"]
121 config_good_action_answers
=[u
"owi \o/",u
"c'est gentil ! ♡"]
122 config_good_action_actions
=[u
"ronronne",u
"est content"]
124 config_bonjour_triggers
=[u
"(s|)(a|'|)lu(t|)",u
"hello",u
"pl(o|i)p",u
"pr(ou|ü)t",u
"bonjour",u
"bonsoir",u
"coucou"]
125 config_bonjour_answers
=[u
"Salut {}",u
"Hello {} :)",u
"Bonjour {}",u
"Hello {}",u
"{}: hello",u
"{}: bonjour"]
127 config_kick_answers
=[u
"Ben qu'est-ce que j'ai fait ? :(",u
"Mais euh, j'ai rien fait de mal…","{} a le /kick facile :)"]
128 config_kick_actions
=[u
"se tiendra à carreaux",u
"essaiera de ne plus provoquer les foudres de {}"]
130 config_thisfile
= os
.path
.realpath( __file__
)
132 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
134 class NKError(Exception):
135 def __init__(self
,msg
):
136 Exception.__init
__(self
)
140 def __unicode__(self
):
141 return unicode(self
.msg
)
143 class NKRefused(NKError
):
146 class NKHelloFailed(NKError
):
149 class NKUnknownError(NKError
):
152 def log(serveur
,channel
,auteur
=None,message
=None):
153 f
=open(get_config_logfile(serveur
),"a")
154 if auteur
==message
==None:
155 # alors c'est que c'est pas un channel mais juste une ligne de log
156 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
158 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
160 if config_debug_stdout
:
167 # On établit la connexion sur port 4242
168 sock
.connect(("127.0.0.1",4242))
170 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
172 sock
.write('hello "Basile"')
173 # On récupère la réponse du hello
176 except Exception as exc
:
177 # Si on a foiré quelque part, c'est que le serveur est down
178 raise NKRefused(str(exc
))
179 if out
["retcode"]==0:
181 elif out
["retcode"]==11:
182 raise NKHelloFailed(out
["errmsg"])
184 raise NKUnknownError(out
["errmsg"])
186 def login_NK(username
,password
,typ
="bdd"):
188 if typ
=="special": # ça c'est pour Basile lui-même
191 masque
='[["all"],["all"],false]'
193 # Basile a un compte special user
194 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
197 except Exception as exc
:
198 # Si on a foiré quelque part, c'est que le serveur est down
199 raise NKRefused(str(exc
))
200 # On vérifie ensuite que le login
201 return json
.loads(out
),sock
204 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
206 chain
=unicode(chain
,"utf8")
208 chain
=unicode(chain
,"utf8").lower()
209 allmatches
="("+"|".join(matches
)+")"
210 reg
=(avant
+allmatches
+apres
).lower()
211 o
=re
.match(reg
,chain
)
214 def is_insult(chain
,debug
=True):
215 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
216 def is_not_insult(chain
):
217 chain
=unicode(chain
,"utf8")
218 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
219 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
220 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
221 if re
.match(reg
,chain
):
226 return is_something(chain
,config_perdu
)
228 return is_something(chain
,config_tag_triggers
)
230 return is_something(chain
,config_gros
)
232 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
234 return is_something(chain
,config_merci_triggers
)
235 def is_tamere(chain
):
236 return is_something(chain
,config_tamere_triggers
)
237 def is_bad_action_trigger(chain
,pseudo
):
238 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
239 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
240 def is_good_action_trigger(chain
,pseudo
):
241 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
242 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
244 return re
.match(u
"^(pan|bim|bang) .*$",unicode(chain
,"utf8").lower().strip())
248 class UnicodeBotError(Exception):
250 def bot_unicode(chain
):
252 unicode(chain
,"utf8")
253 except UnicodeDecodeError as exc
:
254 raise UnicodeBotError
256 class Basile(ircbot
.SingleServerIRCBot
):
257 def __init__(self
,serveur
,debug
=False):
258 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
259 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
260 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
263 self
.overops
=config_overops
264 self
.ops
=self
.overops
+config_ops
265 self
.report_bugs_to
=config_report_bugs_to
266 self
.chanlist
=config_chanlist
268 self
.identities
=pickle
.load(open("identities.pickle","r"))
269 self
.stay_channels
=config_stay_channels
270 self
.quiet_channels
=config_quiet_channels
274 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
276 login_result
,sock
=login_NK(username
,password
,typ
)
277 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
278 except NKRefused
as exc
:
279 for report
in self
.report_bugs_to
:
280 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
282 except NKHelloFailed
as exc
:
283 for report
in self
.report_bugs_to
:
285 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
287 except NKUnknownError
as exc
:
288 erreurs
=["Une fucking erreur inconnue s'est produite"]
289 erreurs
+=str(exc
).split("\n")
290 for report
in self
.report_bugs_to
:
292 serv
.privmsg(report
,err
)
294 except Exception as exc
:
295 # Exception qui ne vient pas de la communication avec le serveur NK2015
296 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
303 def give_me_my_pseudo(self
,serv
):
304 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
305 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
307 serv
.nick(config_irc_pseudo
)
309 def on_welcome(self
, serv
, ev
):
310 self
.serv
=serv
# ça serv ira :)
311 self
.give_me_my_pseudo(serv
)
312 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
313 log(self
.serveur
,"Connected")
315 self
.chanlist
=["#bot"]
316 for c
in self
.chanlist
:
317 log(self
.serveur
,"JOIN %s"%(c))
319 # on ouvre la connexion note de Basile, special user
320 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
322 for report
in self
.report_bugs_to
:
323 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
325 def lost(self
,serv
,channel
,forced
=False):
326 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
327 if not channel
in self
.quiet_channels
or forced
:
328 serv
.privmsg(channel
,"J'ai perdu !")
329 self
.last_perdu
=time
.time()
330 delay
=config_time_between_perdu_trigger
331 delta
=config_time_between_perdu_trigger_delta
332 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
334 def try_tamere(self
,serv
,channel
,auteur
,message
):
335 """Essaye de trigger un ta mère"""
336 #pas à chaque fois quand même
337 if random
.randrange(4)==0:
338 debuts
=u
"("+config_regexp_etre
+u
"|"+config_regexp_etre_avec_c
+u
")"
339 adjectifs
={u
"bon(|ne|s|nes)":u
"bonne",
340 u
"baisable(|s)":u
"baisable",
341 u
"faisable(|s)":u
"faisable",
342 u
"pas ch(ère(|s)|er(|s))":u
"pas chère",
343 u
"facile(|s)":u
"facile",
344 u
"chaud(|e|s|es)":u
"chaude",
345 u
"gratuit(|e|s|es)":u
"gratuite",
346 u
"payant(|e|s|es)":u
"payante",
347 u
"ouvert(|e|s|es)":u
"ouverte",
349 u
"plein(|s|es)":u
"pleine",
350 u
"bien plein(|e|s|es)":u
"bien pleine",
351 u
"innocent(|e|s|es)":u
"innocente"}
352 adj_reg
=u
"(?P<adjectif>"+u
"|".join(adjectifs
.keys())+u
")"
353 reg
=u
".*(^| |')"+debuts
+u
" "+adj_reg
+u
"($|,|;|\.| ).*"
354 matched
=re
.match(reg
,message
)
356 # il faut repasser l'adjectif au féminin singulier
357 found
=matched
.groupdict()["adjectif"]
358 for adj
in adjectifs
.keys():
359 if re
.match(adj
,found
):
360 adjectif
=adjectifs
[adj
]
362 serv
.privmsg(channel
,(u
"%s: c'est ta mère qui est %s !"%(auteur
,adjectif
)).encode("utf8"))
363 elif random
.randrange(5)==0:
364 # deuxième type de trigger, mais moins probable
365 matched
=re
.match(adj_reg
,message
)
367 found
=matched
.groupdict()["adjectif"]
368 for adj
in adjectifs
.keys():
369 if re
.match(adj
,found
):
370 adjectif
=adjectifs
[adj
]
372 fille
=random
.choice([u
"mère",u
"soœur"])
373 serv
.privmsg(channel
,(u
"%s: et ta %s, elle est %s ?"%
374 (auteur
,fille
,adjectif
)).encode("utf8"))
376 # troisième type de trigger
377 cpgt
=config_premier_groupe_terminaisons
378 verbes
={u
"tourn"+cpgt
:u
"tourne",
379 u
"balad"+cpgt
+u
" sur le trottoir":u
"se balade sur le trottoir",
380 u
"prom(e|è)n"+cpgt
+" sur le trottoir":u
"se promène sur le trottoir",
382 vb_reg
=u
".*(^| )(?P<verbe>"+"|".join(verbes
.keys())+")( |,|;|\.|$)"
383 matched
=re
.match(vb_reg
,message
)
385 found
=matched
.groupdict()["verbe"]
386 for vb
in verbes
.keys():
387 if re
.match(vb
,found
):
390 fille
=random
.choice([u
"mère",u
"soœur"])
391 serv
.privmsg(channel
,(u
"%s: et ta %s, elle %s ?"%
392 (auteur
,fille
,verbe
)).encode("utf8"))
393 def pourmoi(self
, serv
, message
):
394 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
397 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
398 return (True,message
[size
+1:].lstrip(" "))
400 return (False,message
)
402 def on_privmsg(self
, serv
, ev
):
403 message
=ev
.arguments()[0]
404 auteur
= irclib
.nm_to_n(ev
.source())
406 test
=bot_unicode(message
)
407 except UnicodeBotError
:
409 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
411 message
=message
.split()
412 cmd
=message
[0].lower()
415 if not len(message
) in [2,3]:
416 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
420 username
=(message
[1])
421 password
=" ".join(message
[2:])
423 password
=" ".join(message
[1:])
424 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
426 self
.sockets
[username
]=sock
427 serv
.privmsg(auteur
,"Connection successful")
428 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
430 serv
.privmsg(auteur
,"Connection failed")
431 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
434 helpdico
={"connect": """CONNECT [<username>] <password>
435 Ouvre une connexion au serveur NoteKfet.
436 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
437 "identify": """IDENTIFY <username> <password>
438 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
439 Sans paramètre, je réponds sous quel pseudo je te connais.""",
440 "drop":"""DROP <password>
441 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
442 helpmsg_default
="""Liste des commandes :
443 HELP Affiche de l'aide sur une commande.
444 CONNECT Ouvre une connection au serveur Note Kfet.
445 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
446 DROP Me fait oublier ton identité.
447 SOLDE Obtenir ton solde"""
449 JOIN Faire rejoindre un chan
450 LEAVE Faire quitter un chan
451 QUIET Se taire sur un chan
452 NOQUIET Opposé de QUIET
453 LOST Perdre sur un chan
454 SOLDE <pseudo> Donner le solde de quelqu'un"""
456 SAY Fait envoyer un message sur un chan ou à une personne
457 DO Fait faire une action sur un chan
458 STAY Ignorera les prochains LEAVE pour un chan
459 NOSTAY Opposé de STAY
462 helpmsg
=helpmsg_default
463 if auteur
in self
.ops
:
465 if auteur
in self
.overops
:
466 helpmsg
+=helpmsg_overops
468 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
469 for ligne
in helpmsg
.split("\n"):
470 serv
.privmsg(auteur
,ligne
)
471 elif cmd
=="identify":
473 if self
.identities
.has_key(auteur
):
474 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
475 self
.identities
[auteur
].encode("utf8")))
477 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
478 elif len(message
)>=3:
479 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
480 success
,_
=self
.new_connection_NK(serv
,username
,password
)
482 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
483 serv
.privmsg(auteur
,"Identité enregistrée.")
484 self
.identities
[auteur
]=username
485 pickle
.dump(self
.identities
,open("identities.pickle","w"))
487 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
488 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
490 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
493 if self
.identities
.has_key(auteur
):
494 password
=" ".join(message
[1:])
495 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
497 del self
.identities
[auteur
]
498 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
499 pickle
.dump(self
.identities
,open("identities.pickle","w"))
500 serv
.privmsg(auteur
,"Identité oubliée.")
502 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
503 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
505 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
507 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
509 if auteur
in self
.ops
:
511 if message
[1] in self
.chanlist
:
512 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
514 serv
.join(message
[1])
515 self
.chanlist
.append(message
[1])
516 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
517 log(self
.serveur
,"priv",auteur
," ".join(message
))
519 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
523 if auteur
in self
.ops
and len(message
)>1:
524 if message
[1] in self
.chanlist
:
525 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
526 serv
.part(message
[1])
527 self
.chanlist
.remove(message
[1])
528 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
530 serv
.privmsg(auteur
,"Non, je reste !")
531 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
533 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
537 if auteur
in self
.overops
:
539 if message
[1] in self
.stay_channels
:
540 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
541 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
543 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
544 self
.stay_channels
.append(message
[1])
545 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
547 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
551 if auteur
in self
.overops
:
553 if message
[1] in self
.stay_channels
:
554 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
555 self
.stay_channels
.remove(message
[1])
556 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
558 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
559 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
564 if auteur
in self
.overops
:
565 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
570 if auteur
in self
.ops
:
572 if message
[1] in self
.quiet_channels
:
573 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
574 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
576 self
.quiet_channels
.append(message
[1])
577 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
578 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
580 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
584 if auteur
in self
.ops
:
586 if message
[1] in self
.quiet_channels
:
587 self
.quiet_channels
.remove(message
[1])
588 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
589 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
591 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
592 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
596 if auteur
in self
.overops
and len(message
)>2:
597 serv
.privmsg(message
[1]," ".join(message
[2:]))
598 log(self
.serveur
,"priv",auteur
," ".join(message
))
599 elif len(message
)<=2:
600 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
604 if auteur
in self
.overops
and len(message
)>2:
605 serv
.action(message
[1]," ".join(message
[2:]))
606 log(self
.serveur
,"priv",auteur
," ".join(message
))
607 elif len(message
)<=2:
608 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
612 if auteur
in self
.overops
and len(message
)>2:
613 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
614 log(self
.serveur
,"priv",auteur
," ".join(message
))
615 elif len(message
)<=2:
616 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
620 if auteur
in self
.ops
and len(message
)>1:
621 serv
.privmsg(message
[1],"J'ai perdu !")
622 log(self
.serveur
,"priv",auteur
," ".join(message
))
623 elif len(message
)<=1:
624 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
629 if self
.identities
.has_key(auteur
):
631 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
632 ret
=json
.loads(self
.nk
.read())
633 solde
=ret
["msg"][0]["solde"]
634 pseudo
=ret
["msg"][0]["pseudo"]
635 except Exception as exc
:
637 serv
.privmsg(auteur
,"failed")
638 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
640 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
642 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
643 elif auteur
in self
.ops
:
645 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
646 ret
=json
.loads(self
.nk
.read())
647 solde
=ret
["msg"][0]["solde"]
648 pseudo
=ret
["msg"][0]["pseudo"]
649 except Exception as exc
:
650 serv
.privmsg(auteur
,"failed")
651 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
653 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
657 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
659 def on_pubmsg(self
, serv
, ev
):
660 auteur
= irclib
.nm_to_n(ev
.source())
662 message
= ev
.arguments()[0]
664 test
=bot_unicode(message
)
665 except UnicodeBotError
:
666 if not canal
in self
.quiet_channels
:
668 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
670 pour_moi
,message
=self
.pourmoi(serv
,message
)
671 if pour_moi
and message
.split()!=[]:
672 cmd
=message
.split()[0].lower()
674 args
=" ".join(message
.split()[1:])
677 if cmd
in ["meurs","die","crève"]:
678 if auteur
in self
.overops
:
679 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
682 serv
.privmsg(canal
,"%s: crève !"%(auteur))
683 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
685 elif cmd
in ["part","leave","dégage"]:
686 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
687 or auteur
in self
.overops
):
688 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
689 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
690 if canal
in self
.chanlist
:
691 self
.chanlist
.remove(canal
)
693 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
694 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
696 elif cmd
in ["reconnect"]:
697 if auteur
in self
.ops
:
699 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
700 config_note_password
,"special")[1]
701 except Exception as exc
:
703 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
705 serv
.privmsg(canal
,"%s: done"%(auteur))
706 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
708 serv
.privmsg(canal
,"%s: failed"%(auteur))
709 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
710 for report
in self
.report_bugs_to
:
711 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
713 serv
.privmsg(canal
,"%s: crève !"%(auteur))
714 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
716 elif cmd
in ["deviens","pseudo"]:
717 if auteur
in self
.ops
:
720 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
722 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
723 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
724 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
725 serv
.privmsg(canal
,"%s: pong"%(auteur))
727 elif cmd
in ["solde","!solde"]:
728 if self
.identities
.has_key(auteur
):
729 pseudo
=self
.identities
[auteur
]
731 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
732 ret
=json
.loads(self
.nk
.read())
733 solde
=ret
["msg"][0]["solde"]
734 pseudo
=ret
["msg"][0]["pseudo"]
735 except Exception as exc
:
736 serv
.privmsg(canal
,"%s: failed"%(auteur))
737 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
739 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
740 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
742 serv
.privmsg(canal
,"%s: Je ne connais pas ton pseudo note."%(auteur))
743 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
744 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
745 and not canal
in self
.quiet_channels
):
746 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
747 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
748 if auteur
=="[20-100]":
749 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
751 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
752 if is_insult(message
) and not canal
in self
.quiet_channels
:
753 if is_not_insult(message
):
754 answer
=random
.choice(config_compliment_answers
)
755 for ligne
in answer
.split("\n"):
756 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
758 answer
=random
.choice(config_insultes_answers
)
759 for ligne
in answer
.split("\n"):
760 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
761 gros_match
=is_gros(message
)
762 if gros_match
and not canal
in self
.quiet_channels
:
763 taille
=get_filesize()
764 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
765 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
766 if is_tesla(message
) and not canal
in self
.quiet_channels
:
767 l1
,l2
=config_tesla_answers
,config_tesla_actions
768 n1
,n2
=len(l1
),len(l2
)
769 i
=random
.randrange(n1
+n2
)
771 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
773 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
774 if is_tamere(message
) and not canal
in self
.quiet_channels
:
775 answer
=random
.choice(config_tamere_answers
)
776 for ligne
in answer
.split("\n"):
777 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
778 if is_tag(message
) and not canal
in self
.quiet_channels
:
779 if auteur
in self
.ops
:
780 action
=random
.choice(config_tag_actions
)
781 serv
.action(canal
,action
.encode("utf8"))
782 self
.quiet_channels
.append(canal
)
784 answer
=random
.choice(config_tag_answers
)
785 for ligne
in answer
.split("\n"):
786 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
787 if is_merci(message
):
788 answer
=random
.choice(config_merci_answers
)
789 for ligne
in answer
.split("\n"):
790 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
791 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
792 unicode(message
.upper(),"utf8"))
793 if out
and not canal
in self
.quiet_channels
:
797 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
798 if out
+1>1000 and random
.randrange(4)==0:
799 serv
.privmsg(canal
,"%s: Tu sais, je peux continuer longtemps comme ça…"%(auteur))
801 serv
.privmsg(canal
,"%s: Tu croyais m'avoir sur le maxint ? J'suis en python mon vieux, 'va falloir trouver mieux…"%(auteur))
803 except Exception as exc
:
805 if re
.match("[A-Y]",out
):
806 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
807 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
809 serv
.privmsg(canal
,"%s: pfff, j'ai l'air malin maintenant… [ ?"%(auteur))
811 serv
.privmsg(canal
,"%s: nan mais il faut qu'on arrête, ça va finir par poser des problèmes…"%(auteur))
812 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
814 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
815 for i
in range(20)}[j
]
817 out
=int(translate(out
))
818 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
819 if (not canal
in self
.quiet_channels
820 and re
.match((u
"^("+"|".join(config_bonjour_triggers
)+").*").lower(),message
.lower()) ):
821 answer
=random
.choice(config_bonjour_answers
)
822 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
823 if is_pan(message
) and not canal
in self
.quiet_channels
:
824 serv
.privmsg(canal
,"%s: c'est pas sur moi qu'il faut tirer !"%(auteur))
826 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
827 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
828 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
829 if auteur
=="[20-100]":
830 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
832 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
833 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
834 failanswers
=config_buffer_fail_answers
835 answer
=random
.choice(failanswers
)
836 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
))
837 if not canal
in self
.quiet_channels
:
838 self
.try_tamere(serv
,canal
,auteur
,message
)
840 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
841 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
842 ).format(mypseudo
).lower(), message
.strip().lower()):
843 answer
=random
.choice(config_bonjour_answers
)
844 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
845 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
846 # proba de perdre sur trigger :
847 # avant 30min (enfin, config) : 0
848 # ensuite, +25%/30min, linéairement
849 deltat
=time
.time()-self
.last_perdu
850 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
851 if random
.uniform(0,1)<barre
:
852 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
853 self
.last_perdu
=time
.time()
855 def on_action(self
, serv
, ev
):
856 action
= ev
.arguments()[0]
857 auteur
= irclib
.nm_to_n(ev
.source())
858 channel
= ev
.target()
860 test
=bot_unicode(action
)
861 except UnicodeBotError
:
862 serv
.privmsg(channel
,
863 "%s : Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
867 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
868 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
869 n1
,n2
=len(l1
),len(l2
)
870 i
=random
.randrange(n1
+n2
)
872 serv
.action(channel
,l2
[i
-n1
].encode("utf8"))
874 serv
.privmsg(channel
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
875 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
876 l1
,l2
=config_good_action_answers
,config_good_action_actions
877 n1
,n2
=len(l1
),len(l2
)
878 i
=random
.randrange(n1
+n2
)
880 serv
.action(channel
,l2
[i
-n1
].encode("utf8"))
882 serv
.privmsg(channel
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
884 def on_kick(self
,serv
,ev
):
885 auteur
= irclib
.nm_to_n(ev
.source())
886 channel
= ev
.target()
887 victime
= ev
.arguments()[0]
888 raison
= ev
.arguments()[1]
889 if victime
==self
.nick
:
890 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
893 l1
,l2
=config_kick_answers
,config_kick_actions
894 n1
,n2
=len(l1
),len(l2
)
895 i
=random
.randrange(n1
+n2
)
897 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
899 serv
.privmsg(channel
,"%s: %s"%(auteur
,l1
[i
].format(auteur
).encode("utf8")))
902 return self
.serv
.get_nickname()
903 nick
=property(_getnick
)
906 if __name__
=="__main__":
909 print "Usage : basile.py <serveur> [--debug]"
912 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
916 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
917 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
919 serveur
=serveurs
[serveur
]
921 print "Server Unknown : %s"%(serveur)
923 basile
=Basile(serveur
,debug
)