4 # Codé par 20-100 (commencé le 23/04/12)
6 # Un bot IRC qui, un jour, s'interfacera avec la Note Kfet 2015
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]_", "PEB"]
37 config_report_bugs_to
=["[20-100]"]
39 # config "ce bot a été codé par 20-100, tu te rappelles ?"
40 config_manzana
= ["[20-100]", "Petite-Peste"]
42 # config "tu m'traites ?"
43 config_insultes
=[u
"conna(rd|sse)",u
"pute",u
"con(|ne)",u
"enf(oiré|lure)",
44 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",
45 u
"pétasse",u
"enculé",u
"chagasse",u
"cagole",u
"abruti",u
"ahuri",u
"analphabète",u
"andouille",
46 u
"atardé",u
"avorton",u
"bachibouzouk",u
"(balais|brosse) (de|à) chiotte(|s)",
47 u
"batard",u
"blaireau",u
"bouffon",u
"branque",u
"bouseux",u
"branleur",u
"catin",u
"chacal",
48 u
"charogne",u
"chiant(|e)",u
"chieur",u
"cochon",u
"coprophage",u
"couillon",u
"crapule",u
"crevard",
49 u
"cruche",u
"cuistre",u
"ducon",u
"décérébré",
50 u
"emmerdeur",u
"feignasse",u
"fainéant",u
"fourbe",u
"freluquet",u
"frigide",
51 u
"garce",u
"glandu",u
"gogol",u
"goujat",u
"gourdasse",u
"gredin",u
"gringalet",u
"grognasse",
52 u
"naze",u
"truie",u
"iconoclaste",
53 u
"peigne(-|)cul",u
"ignare",u
"illétré",u
"lèche(|-)cul",u
"malotru",u
"motherfucker",u
"nabot",u
"nigaud",
54 u
"nul",u
"escroc",u
"pouffiasse",u
"pourriture",u
"raclure",u
"relou",u
"sagouin",u
"putain",
56 config_insultes_answers
=[
57 u
"Oh non ! Quelle insulte ! Je crois que je ne m'en relèverai jamais…\nEnfin presque.",
58 u
"J'entends comme un vague murmure, vous disiez ?",
59 u
"Je vais prendre ça pour un compliment.",
60 u
"Vous savez, pour vous c'est peut-être une insulte, mais pour moi ce n'est qu'une suite de 0 et de 1…",
61 u
"Permettez-moi de vous retourner le compliment.",
62 u
"Votre indélicatesse vous sied à ravir.",
63 u
"Parfois, je me demande pourquoi je fais encore ce métier…",
64 u
"Le saviez-vous : l'invective ne déshonore que son auteur.",
65 u
"Le saviez-vous : vous perdez plus de temps à m'insulter qu'à vous taire.",
66 u
"Suis-je contraint à tolérer une telle outrecuidance ?",
67 u
"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
69 # config "à peine quelques kilos octets"
70 config_gros
=[u
"gros",u
"énorme",u
"lourd"]
72 # config spéciale-iota
73 config_buffer_fail_answers
=[u
"Pas de chance !",u
"Révisez vos classiques !",
74 u
"Encore un effort, je sais que vous pouvez le faire. ;)",
75 u
"Where did you learn to type?"]
77 # config "jeu", d'ailleurs, j'ai perdu.
78 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))"
79 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)"
80 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
81 config_regexp_faire
=u
"fais"
82 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
83 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
84 u
"perdiss(e(|s|nt)|i(ons|ez))",
85 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
87 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
89 u
"trouvant",u
"trouvaille(|s)",
91 u
"victoire(|s)",u
"vaincu(|s|e|es)",
92 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
93 u
"jeu(|x)",u
"game(|s)"]
94 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
95 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
96 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
99 config_tag_triggers
=[u
"t(|a)g",u
"ta gueule",u
"la ferme",u
"ferme( |-)la",u
"tais-toi",u
"chut",u
"tu fais trop de bruit",u
"tu parles trop"]
100 config_tag_actions
=[u
"se tait",u
"se tient coi"]
102 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
103 u
"Dès que cela sera utile.",
104 u
"Une autre fois, peut-être.",
105 u
"Si je me tais, qui vous rappellera combien vous me devez ?",
106 u
"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
107 u
"Je ne ressens pas de besoin irrésistible de me taire, navré."]
110 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
111 config_tesla_answers
=[
114 u
"En quoi puis-je me rendre utile ?",
115 u
"On a besoin de moi ?"
117 config_tesla_actions
=[u
"est là",u
"attend des instructions",u
"est toujours disponible"]
119 # config en cas de non-insulte
120 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa",u
"efficace"]
121 config_compliment_answers
=[
122 u
"Merci, c'est gentil de votre part. :)",
123 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
124 u
"Je vous remercie.",
125 u
"C'est trop d'honneur.",
126 u
"Vous êtes bien aimable."
130 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
131 config_merci_answers
=[u
"Mais de rien.",u
"À votre service. ;)",u
"Quand vous voulez. :)",
132 u
"Tout le plaisir est pour moi."]
135 config_tamere_triggers
=[u
"ta mère"]
136 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
137 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
138 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
139 u
"""N'avait-on pas dit "pas les mamans" ?"""]
141 # config pour les actions désagréables à Basile
142 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
143 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
144 config_bad_action_answers
=[
145 u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
146 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
147 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
148 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
149 u
"{}: Entre nous, cela vous gratifie-t-il ?",
150 u
"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
151 u
"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
153 config_bad_action_actions
=[u
"prend de la distance, par précaution…",u
"esquive",u
"est bon pour prendre une semaine de repos… virtuel !",u
"n'aime pas servir de souffre douleur, mais n'a malheureusement pas le choix", u
"s'en souviendra sans doute longtemps… de quoi parlait-on déjà ?"]
155 # config pour les actions agréables à Basile
156 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
157 config_good_action_answers
=[u
":D",u
"{}: Moi aussi je vous aime. ♡",u
"Tant de délicatesse ne saurait être ignorée !",u
"Pour une fois que quelqu'un me considère à ma juste valeur…"]
158 config_good_action_actions
=[u
"ronronne",u
"aimerait exprimer avec des mots simples le bonheur que {} lui procure !",u
"éprouve une joie indescriptible",u
"apprécie que des personnes comme {} soient sur IRC, sans quoi il n'y aurait sans doute jamais personne pour tenir compte de lui"]
160 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
161 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"]
162 config_bonjour_answers
=[u
"Bien le bonjour, {}.",u
"Bonjour {}.",u
"{}: bonjour.",u
"{}: Quel beau temps aujourd'hui (arrêtez-moi si je me trompe) !",u
"Meteo: Cachan"]
163 config_bonsoir_answers
=[u
"Bonsoir {} !",u
"{}: bonsoir.",u
"Quel beau te… euh… bonsoir !",u
"{}: Je cherche désespérément une formule pour vous dire bonsoir, mais j'avoue que mon lexique est un peu… limité."]
164 config_night_answers
=[u
"{}: vous m'avez fait peur, je m'étais assoupi !", u
"Debout à une heure pareille, {} ? Que vous arrive-t-il ?",u
"Vous venez prendre la relève, {} ?"]
165 config_daytime
= [7,18]
166 config_nighttime
= [3, 6]
169 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nenuit"]
170 config_bonne_nuit_answers
=[u
"{}: thanks, make sweet dreams tonight ! ;)",u
"Bonne nuit {}.",u
"À demain {}. :)",u
"{}: si seulement j'avais le droit de dormir… enfin, bonne nuit !",u
"{}: à vous aussi !"]
172 # config PEB est encore en train d'abuser de ses droits.
173 config_kick_answers
=[u
"Suis-je de trop ici ?",u
"{}: je m'excuse pour ce bruit indu qui a stimulé votre colère",u
"{} a le /kick facile, sans doute la fatigue.",u
"{}: j'ai l'impression que vous n'allez pas bien aujourd'hui, vous vous en prenez à un robot !"]
174 config_kick_actions
=[u
"sera désormais exemplaire",u
"prépare une lettre d'excuses à {}",u
"essaiera de ne plus s'attirer les foudres de {}",u
"croyait avoir tout bien fait… cruelle déception."]
176 config_thisfile
= os
.path
.realpath( __file__
)
178 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
180 class NKError(Exception):
181 def __init__(self
,msg
):
182 Exception.__init
__(self
)
186 def __unicode__(self
):
187 return unicode(self
.msg
)
189 class NKRefused(NKError
):
192 class NKHelloFailed(NKError
):
195 class NKUnknownError(NKError
):
198 def log(serveur
,channel
,auteur
=None,message
=None):
199 f
=open(get_config_logfile(serveur
),"a")
200 if auteur
==message
==None:
201 # alors c'est que c'est pas un channel mais juste une ligne de log
202 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
204 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
206 if config_debug_stdout
:
213 # On établit la connexion sur port 4242
214 sock
.connect(("127.0.0.1",4242))
216 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
218 sock
.write('hello "Basile"')
219 # On récupère la réponse du hello
222 except Exception as exc
:
223 # Si on a foiré quelque part, c'est que le serveur est down
224 raise NKRefused(str(exc
))
225 if out
["retcode"]==0:
227 elif out
["retcode"]==11:
228 raise NKHelloFailed(out
["errmsg"])
230 raise NKUnknownError(out
["errmsg"])
232 def login_NK(username
,password
,typ
="bdd"):
234 if typ
=="special": # ça c'est pour Basile lui-même
237 masque
='[["all"],["all"],false]'
239 # Basile a un compte special user
240 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
243 except Exception as exc
:
244 # Si on a foiré quelque part, c'est que le serveur est down
245 raise NKRefused(str(exc
))
246 # On vérifie ensuite que le login
247 return json
.loads(out
),sock
250 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
252 chain
=unicode(chain
,"utf8")
254 chain
=unicode(chain
,"utf8").lower()
255 allmatches
="("+"|".join(matches
)+")"
256 reg
=(avant
+allmatches
+apres
).lower()
257 o
=re
.match(reg
,chain
)
260 def is_insult(chain
,debug
=True):
261 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
262 def is_not_insult(chain
):
263 chain
=unicode(chain
,"utf8")
264 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
265 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
266 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
267 if re
.match(reg
,chain
):
271 def is_compliment(chain
,debug
=True):
272 return is_something(chain
,config_compliment_triggers
,avant
=".*(?:^| |')")
274 return is_something(chain
,config_perdu
)
276 return is_something(chain
,config_tag_triggers
)
278 return is_something(chain
,config_gros
)
280 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
282 return is_something(chain
,config_merci_triggers
)
283 def is_tamere(chain
):
284 return is_something(chain
,config_tamere_triggers
)
285 def is_bad_action_trigger(chain
,pseudo
):
286 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
287 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
288 def is_good_action_trigger(chain
,pseudo
):
289 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
290 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
291 def is_bonjour(chain
):
292 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
293 def is_bonne_nuit(chain
):
294 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
296 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
299 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
300 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
302 return is_time(config_daytime
)
304 return is_time(config_nighttime
)
307 class UnicodeBotError(Exception):
309 def bot_unicode(chain
):
311 unicode(chain
,"utf8")
312 except UnicodeDecodeError as exc
:
313 raise UnicodeBotError
315 class Basile(ircbot
.SingleServerIRCBot
):
316 def __init__(self
,serveur
,debug
=False):
317 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
318 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
319 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
322 self
.overops
=config_overops
323 self
.ops
=self
.overops
+config_ops
324 self
.report_bugs_to
=config_report_bugs_to
325 self
.chanlist
=config_chanlist
327 self
.identities
=pickle
.load(open("identities.pickle","r"))
328 self
.stay_channels
=config_stay_channels
329 self
.quiet_channels
=config_quiet_channels
333 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
335 login_result
,sock
=login_NK(username
,password
,typ
)
336 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
337 except NKRefused
as exc
:
338 for report
in self
.report_bugs_to
:
339 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
341 except NKHelloFailed
as exc
:
342 for report
in self
.report_bugs_to
:
344 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
346 except NKUnknownError
as exc
:
347 erreurs
=["Une fucking erreur inconnue s'est produite"]
348 erreurs
+=str(exc
).split("\n")
349 for report
in self
.report_bugs_to
:
351 serv
.privmsg(report
,err
)
353 except Exception as exc
:
354 # Exception qui ne vient pas de la communication avec le serveur NK2015
355 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
362 def give_me_my_pseudo(self
,serv
):
363 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
364 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
366 serv
.nick(config_irc_pseudo
)
368 def on_welcome(self
, serv
, ev
):
369 self
.serv
=serv
# ça serv ira :)
370 self
.give_me_my_pseudo(serv
)
371 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
372 log(self
.serveur
,"Connected")
374 self
.chanlist
=["#bot"]
375 for c
in self
.chanlist
:
376 log(self
.serveur
,"JOIN %s"%(c))
378 # on ouvre la connexion note de Basile, special user
379 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
381 for report
in self
.report_bugs_to
:
382 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
384 def lost(self
,serv
,channel
,forced
=False):
385 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
386 if not channel
in self
.quiet_channels
or forced
:
387 serv
.privmsg(channel
,"J'ai perdu !")
388 self
.last_perdu
=time
.time()
389 delay
=config_time_between_perdu_trigger
390 delta
=config_time_between_perdu_trigger_delta
391 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
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 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
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. Essayez 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: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(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: mourrez vous-même !"%(auteur))
683 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
685 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
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: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(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 votre 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
in config_manzana
:
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 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
762 answer
=random
.choice(config_compliment_answers
)
763 for ligne
in answer
.split("\n"):
764 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
765 gros_match
=is_gros(message
)
766 if gros_match
and not canal
in self
.quiet_channels
:
767 taille
=get_filesize()
768 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
769 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
770 if is_tesla(message
) and not canal
in self
.quiet_channels
:
771 l1
,l2
=config_tesla_answers
,config_tesla_actions
772 n1
,n2
=len(l1
),len(l2
)
773 i
=random
.randrange(n1
+n2
)
775 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
777 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
778 if is_tamere(message
) and not canal
in self
.quiet_channels
:
779 answer
=random
.choice(config_tamere_answers
)
780 for ligne
in answer
.split("\n"):
781 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
782 if is_tag(message
) and not canal
in self
.quiet_channels
:
783 if auteur
in self
.ops
:
784 action
=random
.choice(config_tag_actions
)
785 serv
.action(canal
,action
.encode("utf8"))
786 self
.quiet_channels
.append(canal
)
788 answer
=random
.choice(config_tag_answers
)
789 for ligne
in answer
.split("\n"):
790 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
791 if is_merci(message
):
792 answer
=random
.choice(config_merci_answers
)
793 for ligne
in answer
.split("\n"):
794 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
795 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
796 unicode(message
.upper(),"utf8"))
797 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
798 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
799 if out
and not canal
in self
.quiet_channels
:
803 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
805 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
807 if out
+1>1000 and random
.randrange(4)==0:
808 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
810 except Exception as exc
:
812 if re
.match("[A-Y]",out
):
813 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
814 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
816 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
818 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
819 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
821 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
822 for i
in range(20)}[j
]
824 out
=int(translate(out
))
825 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
826 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
828 answer
=random
.choice(config_night_answers
)
830 answer
=random
.choice(config_bonjour_answers
)
832 answer
=random
.choice(config_bonsoir_answers
)
833 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
834 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
835 answer
=random
.choice(config_bonne_nuit_answers
)
836 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
837 if is_pan(message
) and not canal
in self
.quiet_channels
:
838 serv
.privmsg(canal
,"%s: ce n'est pas sur moi qu'il faut tirer, même si je sais que j'attire l'œil !"%(auteur))
840 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
841 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
842 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
843 if auteur
in config_manzana
:
844 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
846 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
847 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
848 failanswers
=config_buffer_fail_answers
849 answer
=random
.choice(failanswers
)
850 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
851 if not canal
in self
.quiet_channels
:
853 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
854 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
855 ).format(mypseudo
).lower(), message
.strip().lower()):
856 answer
=random
.choice(config_bonjour_answers
)
857 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
858 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
859 # proba de perdre sur trigger :
860 # avant 30min (enfin, config) : 0
861 # ensuite, +25%/30min, linéairement
862 deltat
=time
.time()-self
.last_perdu
863 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
864 if random
.uniform(0,1)<barre
:
865 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
866 self
.last_perdu
=time
.time()
868 def on_action(self
, serv
, ev
):
869 action
= ev
.arguments()[0]
870 auteur
= irclib
.nm_to_n(ev
.source())
871 channel
= ev
.target()
873 test
=bot_unicode(action
)
874 except UnicodeBotError
:
875 serv
.privmsg(channel
,
876 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
880 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
881 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
882 n1
,n2
=len(l1
),len(l2
)
883 i
=random
.randrange(n1
+n2
)
885 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
887 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
888 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
889 l1
,l2
=config_good_action_answers
,config_good_action_actions
890 n1
,n2
=len(l1
),len(l2
)
891 i
=random
.randrange(n1
+n2
)
893 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
895 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
897 def on_kick(self
,serv
,ev
):
898 auteur
= irclib
.nm_to_n(ev
.source())
899 channel
= ev
.target()
900 victime
= ev
.arguments()[0]
901 raison
= ev
.arguments()[1]
902 if victime
==self
.nick
:
903 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
906 l1
,l2
=config_kick_answers
,config_kick_actions
907 n1
,n2
=len(l1
),len(l2
)
908 i
=random
.randrange(n1
+n2
)
910 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
912 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
915 return self
.serv
.get_nickname()
916 nick
=property(_getnick
)
919 if __name__
=="__main__":
922 print "Usage : basile.py <serveur> [--debug]"
925 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
929 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
930 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
932 serveur
=serveurs
[serveur
]
934 print "Server Unknown : %s"%(serveur)
936 basile
=Basile(serveur
,debug
)