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 !"]
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_ction_answers
=[u
"owi \o/",u
"{}: c'est gentil ! ♡"]
122 config_good_action_acions
=[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 if re
.match(reg
,chain
):
215 def is_insult(chain
,debug
=True):
216 return is_something(chain
,config_insultes
,avant
=".*(^| |')")
217 def is_not_insult(chain
):
218 chain
=unicode(chain
,"utf8")
219 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
220 middle_regexp
=u
"(un(|e) ((putain|enfoiré) d(e |'))*|)(| super )( (gros|petit|grand|énorme) |)"
221 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
222 if re
.match(reg
,chain
):
227 return is_something(chain
,config_perdu
)
229 return is_something(chain
,config_tag_triggers
)
231 return is_something(chain
,config_gros
)
233 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
235 return is_something(chain
,config_merci_triggers
)
236 def is_tamere(chain
):
237 return is_something(chain
,config_tamere_triggers
)
238 def is_bad_action_trigger(chain
,pseudo
):
239 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
240 apres
="( [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
241 def is_good_action_trigger(chain
,pseudo
):
242 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
243 apres
="( [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
245 return re
.match(u
"^(pan|bim|bang) .*$",unicode(chain
,"utf8").lower().strip())
249 class UnicodeBotError(Exception):
251 def bot_unicode(chain
):
253 unicode(chain
,"utf8")
254 except UnicodeDecodeError as exc
:
255 raise UnicodeBotError
257 class Basile(ircbot
.SingleServerIRCBot
):
258 def __init__(self
,serveur
,debug
=False):
259 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
260 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
261 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
264 self
.overops
=config_overops
265 self
.ops
=self
.overops
+config_ops
266 self
.report_bugs_to
=config_report_bugs_to
267 self
.chanlist
=config_chanlist
269 self
.identities
=pickle
.load(open("identities.pickle","r"))
270 self
.stay_channels
=config_stay_channels
271 self
.quiet_channels
=config_quiet_channels
275 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
277 login_result
,sock
=login_NK(username
,password
,typ
)
278 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
279 except NKRefused
as exc
:
280 for report
in self
.report_bugs_to
:
281 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
283 except NKHelloFailed
as exc
:
284 for report
in self
.report_bugs_to
:
286 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
288 except NKUnknownError
as exc
:
289 erreurs
=["Une fucking erreur inconnue s'est produite"]
290 erreurs
+=str(exc
).split("\n")
291 for report
in self
.report_bugs_to
:
293 serv
.privmsg(report
,err
)
295 except Exception as exc
:
296 # Exception qui ne vient pas de la communication avec le serveur NK2015
297 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
304 def give_me_my_pseudo(self
,serv
):
305 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
306 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
308 serv
.nick(config_irc_pseudo
)
310 def on_welcome(self
, serv
, ev
):
311 self
.serv
=serv
# ça serv ira :)
312 self
.give_me_my_pseudo(serv
)
313 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
314 log(self
.serveur
,"Connected")
316 self
.chanlist
=["#bot"]
317 for c
in self
.chanlist
:
318 log(self
.serveur
,"JOIN %s"%(c))
320 # on ouvre la connexion note de Basile, special user
321 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
323 for report
in self
.report_bugs_to
:
324 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
326 def lost(self
,serv
,channel
,forced
=False):
327 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
328 if not channel
in self
.quiet_channels
or forced
:
329 serv
.privmsg(channel
,"J'ai perdu !")
330 self
.last_perdu
=time
.time()
331 delay
=config_time_between_perdu_trigger
332 delta
=config_time_between_perdu_trigger_delta
333 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
335 def try_tamere(self
,serv
,channel
,auteur
,message
):
336 """Essaye de trigger un ta mère"""
337 #pas à chaque fois quand même
338 if random
.randrange(4)==0:
339 debuts
=u
"("+config_regexp_etre
+u
"|"+config_regexp_etre_avec_c
+u
")"
340 adjectifs
={u
"bon(|ne|s|nes)":u
"bonne",
341 u
"baisable(|s)":u
"baisable",
342 u
"faisable(|s)":u
"faisable",
343 u
"pas ch(ère(|s)|er(|s))":u
"pas chère",
344 u
"facile(|s)":u
"facile",
345 u
"chaud(|e|s|es)":u
"chaude",
346 u
"gratuit(|e|s|es)":u
"gratuite",
347 u
"payant(|e|s|es)":u
"payante",
348 u
"ouvert(|e|s|es)":u
"ouverte",
350 u
"plein(|s|es)":u
"pleine",
351 u
"bien plein(|e|s|es)":u
"bien pleine",
352 u
"innocent(|e|s|es)":u
"innocente"}
353 adj_reg
=u
"(?P<adjectif>"+u
"|".join(adjectifs
.keys())+u
")"
354 reg
=u
".*(^| |')"+debuts
+u
" "+adj_reg
+u
"($|,|;|\.| ).*"
355 matched
=re
.match(reg
,message
)
357 # il faut repasser l'adjectif au féminin singulier
358 found
=matched
.groupdict()["adjectif"]
359 for adj
in adjectifs
.keys():
360 if re
.match(adj
,found
):
361 adjectif
=adjectifs
[adj
]
363 serv
.privmsg(channel
,(u
"%s: c'est ta mère qui est %s !"%(auteur
,adjectif
)).encode("utf8"))
364 elif random
.randrange(5)==0:
365 # deuxième type de trigger, mais moins probable
366 matched
=re
.match(adj_reg
,message
)
368 found
=matched
.groupdict()["adjectif"]
369 for adj
in adjectifs
.keys():
370 if re
.match(adj
,found
):
371 adjectif
=adjectifs
[adj
]
373 fille
=random
.choice([u
"mère",u
"soœur"])
374 serv
.privmsg(channel
,(u
"%s: et ta %s, elle est %s ?"%
375 (auteur
,fille
,adjectif
)).encode("utf8"))
377 # troisième type de trigger
378 cpgt
=config_premier_groupe_terminaisons
379 verbes
={u
"tourn"+cpgt
:u
"tourne",
380 u
"balad"+cpgt
+u
" sur le trottoir":u
"se balade sur le trottoir",
381 u
"prom(e|è)n"+cpgt
+" sur le trottoir":u
"se promène sur le trottoir",
383 vb_reg
=u
".*(^| )(?P<verbe>"+"|".join(verbes
.keys())+")( |,|;|\.|$)"
384 matched
=re
.match(vb_reg
,message
)
386 found
=matched
.groupdict()["verbe"]
387 for vb
in verbes
.keys():
388 if re
.match(vb
,found
):
391 fille
=random
.choice([u
"mère",u
"soœur"])
392 serv
.privmsg(channel
,(u
"%s: et ta %s, elle %s ?"%
393 (auteur
,fille
,verbe
)).encode("utf8"))
394 def pourmoi(self
, serv
, message
):
395 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
398 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
399 return (True,message
[size
+1:].lstrip(" "))
401 return (False,message
)
403 def on_privmsg(self
, serv
, ev
):
404 message
=ev
.arguments()[0]
405 auteur
= irclib
.nm_to_n(ev
.source())
407 test
=bot_unicode(message
)
408 except UnicodeBotError
:
410 "Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…")
412 message
=message
.split()
413 cmd
=message
[0].lower()
416 if not len(message
) in [2,3]:
417 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
421 username
=(message
[1])
422 password
=" ".join(message
[2:])
424 password
=" ".join(message
[1:])
425 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
427 self
.sockets
[username
]=sock
428 serv
.privmsg(auteur
,"Connection successful")
429 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
431 serv
.privmsg(auteur
,"Connection failed")
432 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
435 helpdico
={"connect": """CONNECT [<username>] <password>
436 Ouvre une connexion au serveur NoteKfet.
437 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
438 "identify": """IDENTIFY <username> <password>
439 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
440 Sans paramètre, je réponds sous quel pseudo je te connais.""",
441 "drop":"""DROP <password>
442 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
443 helpmsg_default
="""Liste des commandes :
444 HELP Affiche de l'aide sur une commande.
445 CONNECT Ouvre une connection au serveur Note Kfet.
446 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
447 DROP Me fait oublier ton identité.
448 SOLDE Obtenir ton solde"""
450 JOIN Faire rejoindre un chan
451 LEAVE Faire quitter un chan
452 QUIET Se taire sur un chan
453 NOQUIET Opposé de QUIET
454 LOST Perdre sur un chan
455 SOLDE <pseudo> Donner le solde de quelqu'un"""
457 SAY Fait envoyer un message sur un chan ou à une personne
458 DO Fait faire une action sur un chan
459 STAY Ignorera les prochains LEAVE pour un chan
460 NOSTAY Opposé de STAY
463 helpmsg
=helpmsg_default
464 if auteur
in self
.ops
:
466 if auteur
in self
.overops
:
467 helpmsg
+=helpmsg_overops
469 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
470 for ligne
in helpmsg
.split("\n"):
471 serv
.privmsg(auteur
,ligne
)
472 elif cmd
=="identify":
474 if self
.identities
.has_key(auteur
):
475 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
476 self
.identities
[auteur
].encode("utf8")))
478 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
479 elif len(message
)>=3:
480 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
481 success
,_
=self
.new_connection_NK(serv
,username
,password
)
483 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
484 serv
.privmsg(auteur
,"Identité enregistrée.")
485 self
.identities
[auteur
]=username
486 pickle
.dump(self
.identities
,open("identities.pickle","w"))
488 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
489 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
491 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
494 if self
.identities
.has_key(auteur
):
495 password
=" ".join(message
[1:])
496 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
498 del self
.identities
[auteur
]
499 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
500 pickle
.dump(self
.identities
,open("identities.pickle","w"))
501 serv
.privmsg(auteur
,"Identité oubliée.")
503 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
504 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
506 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
508 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
510 if auteur
in self
.ops
:
512 if message
[1] in self
.chanlist
:
513 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
515 serv
.join(message
[1])
516 self
.chanlist
.append(message
[1])
517 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
518 log(self
.serveur
,"priv",auteur
," ".join(message
))
520 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
524 if auteur
in self
.ops
and len(message
)>1:
525 if message
[1] in self
.chanlist
:
526 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
527 serv
.part(message
[1])
528 self
.chanlist
.remove(message
[1])
529 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
531 serv
.privmsg(auteur
,"Non, je reste !")
532 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
534 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
538 if auteur
in self
.overops
:
540 if message
[1] in self
.stay_channels
:
541 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
542 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
544 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
545 self
.stay_channels
.append(message
[1])
546 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
548 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
552 if auteur
in self
.overops
:
554 if message
[1] in self
.stay_channels
:
555 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
556 self
.stay_channels
.remove(message
[1])
557 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
559 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
560 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
565 if auteur
in self
.overops
:
566 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
571 if auteur
in self
.ops
:
573 if message
[1] in self
.quiet_channels
:
574 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
575 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
577 self
.quiet_channels
.append(message
[1])
578 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
579 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
581 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
585 if auteur
in self
.ops
:
587 if message
[1] in self
.quiet_channels
:
588 self
.quiet_channels
.remove(message
[1])
589 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
590 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
592 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
593 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
597 if auteur
in self
.overops
and len(message
)>2:
598 serv
.privmsg(message
[1]," ".join(message
[2:]))
599 log(self
.serveur
,"priv",auteur
," ".join(message
))
600 elif len(message
)<=2:
601 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
605 if auteur
in self
.overops
and len(message
)>2:
606 serv
.action(message
[1]," ".join(message
[2:]))
607 log(self
.serveur
,"priv",auteur
," ".join(message
))
608 elif len(message
)<=2:
609 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
613 if auteur
in self
.overops
and len(message
)>2:
614 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
615 log(self
.serveur
,"priv",auteur
," ".join(message
))
616 elif len(message
)<=2:
617 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
621 if auteur
in self
.ops
and len(message
)>1:
622 serv
.privmsg(message
[1],"J'ai perdu !")
623 log(self
.serveur
,"priv",auteur
," ".join(message
))
624 elif len(message
)<=1:
625 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
630 if self
.identities
.has_key(auteur
):
632 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
633 ret
=json
.loads(self
.nk
.read())
634 solde
=ret
["msg"][0]["solde"]
635 pseudo
=ret
["msg"][0]["pseudo"]
636 except Exception as exc
:
638 serv
.privmsg(auteur
,"failed")
639 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
641 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
643 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
644 elif auteur
in self
.ops
:
646 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
647 ret
=json
.loads(self
.nk
.read())
648 solde
=ret
["msg"][0]["solde"]
649 pseudo
=ret
["msg"][0]["pseudo"]
650 except Exception as exc
:
651 serv
.privmsg(auteur
,"failed")
652 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
654 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
658 serv
.privmsg(auteur
,"Je n'ai pas compris. Essaye HELP…")
660 def on_pubmsg(self
, serv
, ev
):
661 auteur
= irclib
.nm_to_n(ev
.source())
663 message
= ev
.arguments()[0]
665 test
=bot_unicode(message
)
666 except UnicodeBotError
:
667 if not canal
in self
.quiet_channels
:
669 "%s: Euh, tu fais de la merde avec ton encodage là, j'ai failli crasher…"%(auteur))
671 pour_moi
,message
=self
.pourmoi(serv
,message
)
672 if pour_moi
and message
.split()!=[]:
673 cmd
=message
.split()[0].lower()
675 args
=" ".join(message
.split()[1:])
678 if cmd
in ["meurs","die","crève"]:
679 if auteur
in self
.overops
:
680 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
683 serv
.privmsg(canal
,"%s: crève !"%(auteur))
684 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
686 elif cmd
in ["part","leave","dégage"]:
687 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
688 or auteur
in self
.overops
):
689 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
690 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
691 if canal
in self
.chanlist
:
692 self
.chanlist
.remove(canal
)
694 serv
.privmsg(canal
,"%s: Non, je reste !"%(auteur))
695 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
697 elif cmd
in ["reconnect"]:
698 if auteur
in self
.ops
:
700 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
701 config_note_password
,"special")[1]
702 except Exception as exc
:
704 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
706 serv
.privmsg(canal
,"%s: done"%(auteur))
707 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
709 serv
.privmsg(canal
,"%s: failed"%(auteur))
710 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
711 for report
in self
.report_bugs_to
:
712 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
714 serv
.privmsg(canal
,"%s: crève !"%(auteur))
715 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
717 elif cmd
in ["deviens","pseudo"]:
718 if auteur
in self
.ops
:
721 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
723 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
724 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
725 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
726 serv
.privmsg(canal
,"%s: pong"%(auteur))
728 elif cmd
in ["solde","!solde"]:
729 if self
.identities
.has_key(auteur
):
730 pseudo
=self
.identities
[auteur
]
732 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
733 ret
=json
.loads(self
.nk
.read())
734 solde
=ret
["msg"][0]["solde"]
735 pseudo
=ret
["msg"][0]["pseudo"]
736 except Exception as exc
:
737 serv
.privmsg(canal
,"%s: failed"%(auteur))
738 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
740 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
741 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
743 serv
.privmsg(canal
,"%s: Je ne connais pas ton pseudo note."%(auteur))
744 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
745 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
746 and not canal
in self
.quiet_channels
):
747 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
748 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
749 if auteur
=="[20-100]":
750 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
752 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
753 if is_insult(message
) and not canal
in self
.quiet_channels
:
754 if is_not_insult(message
):
755 answer
=random
.choice(config_compliment_answers
)
756 for ligne
in answer
.split("\n"):
757 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
759 answer
=random
.choice(config_insultes_answers
)
760 for ligne
in answer
.split("\n"):
761 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
762 if is_gros(message
) and not canal
in self
.quiet_channels
:
763 taille
=get_filesize()
764 answer
=u
"Mais non, je ne suis pas gros, %sKo tout au plus…"%(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")))
876 def on_kick(self
,serv
,ev
):
877 auteur
= irclib
.nm_to_n(ev
.source())
878 channel
= ev
.target()
879 victime
= ev
.arguments()[0]
880 raison
= ev
.arguments()[1]
881 if victime
==self
.nick
:
882 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
885 l1
,l2
=config_kick_actions
,config_kick_answers
886 n1
,n2
=len(l1
),len(l2
)
887 i
=random
.randrange(n1
+n2
)
889 serv
.action(channel
,l2
[i
-n1
].encode("utf8"))
891 serv
.privmsg(channel
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
894 return self
.serv
.get_nickname()
895 nick
=property(_getnick
)
898 if __name__
=="__main__":
901 print "Usage : basile.py <serveur> [--debug]"
904 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
908 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
909 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
911 serveur
=serveurs
[serveur
]
913 print "Server Unknown : %s"%(serveur)
915 basile
=Basile(serveur
,debug
)