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"]
71 config_thisfile
= os
.path
.realpath( __file__
)
73 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
75 # config spéciale-iota
76 config_buffer_fail_answers
=[u
"Pas de chance !",u
"Révisez vos classiques !",
77 u
"Encore un effort, je sais que vous pouvez le faire. ;)",
78 u
"Where did you learn to type?"]
80 # config "jeu", d'ailleurs, j'ai perdu.
81 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))"
82 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)"
83 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
84 config_regexp_faire
=u
"fais"
85 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
86 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
87 u
"perdiss(e(|s|nt)|i(ons|ez))",
88 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
90 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
92 u
"trouvant",u
"trouvaille(|s)",
94 u
"victoire(|s)",u
"vaincu(|s|e|es)",
95 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
96 u
"jeu(|x)",u
"game(|s)"]
97 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
98 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
99 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
102 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"]
103 config_tag_actions
=[u
"se tait",u
"se tient coi"]
105 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
106 u
"Dès que cela sera utile.",
107 u
"Une autre fois, peut-être.",
108 u
"Si je me tais, qui vous rappellera combien vous me devez ?",
109 u
"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
110 u
"Je ne ressens pas de besoin irrésistible de me taire, navré."]
113 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
114 config_tesla_answers
=[
117 u
"En quoi puis-je me rendre utile ?",
118 u
"On a besoin de moi ?"
120 config_tesla_actions
=[u
"est là",u
"attend des instructions",u
"est toujours disponible"]
122 # config en cas de non-insulte
123 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa",u
"efficace"]
124 config_compliment_answers
=[
125 u
"Merci, c'est gentil de votre part. :)",
126 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
127 u
"Je vous remercie.",
128 u
"C'est trop d'honneur.",
129 u
"Vous êtes bien aimable."
133 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
134 config_merci_answers
=[u
"Mais de rien.",u
"À votre service. ;)",u
"Quand vous voulez. :)",
135 u
"Tout le plaisir est pour moi."]
138 config_tamere_triggers
=[u
"ta mère"]
139 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
140 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
141 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
142 u
"""N'avait-on pas dit "pas les mamans" ?"""]
144 # config pour les actions désagréables à Basile
145 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
146 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
147 config_bad_action_answers
=[
148 u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
149 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
150 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
151 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
152 u
"{}: Entre nous, cela vous gratifie-t-il ?",
153 u
"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
154 u
"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
156 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à ?"]
158 # config pour les actions agréables à Basile
159 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
160 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…"]
161 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"]
163 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
164 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"]
165 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"]
166 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é."]
167 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, {} ?"]
168 config_daytime
= [7,18]
169 config_nighttime
= [3, 6]
172 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nenuit"]
173 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 !"]
175 # config PEB est encore en train d'abuser de ses droits.
176 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 !"]
177 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."]
179 # config on m'a demandé de mourir
180 config_quit_messages
=[u
"Bien que cela me désole, je me vois dans l'obligation de vous abandonner."]
183 class NKError(Exception):
184 def __init__(self
,msg
):
185 Exception.__init
__(self
)
189 def __unicode__(self
):
190 return unicode(self
.msg
)
192 class NKRefused(NKError
):
195 class NKHelloFailed(NKError
):
198 class NKUnknownError(NKError
):
201 def log(serveur
,channel
,auteur
=None,message
=None):
202 f
=open(get_config_logfile(serveur
),"a")
203 if auteur
==message
==None:
204 # alors c'est que c'est pas un channel mais juste une ligne de log
205 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
207 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
209 if config_debug_stdout
:
216 # On établit la connexion sur port 4242
217 sock
.connect(("127.0.0.1",4242))
219 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
221 sock
.write('hello "Basile"')
222 # On récupère la réponse du hello
225 except Exception as exc
:
226 # Si on a foiré quelque part, c'est que le serveur est down
227 raise NKRefused(str(exc
))
228 if out
["retcode"]==0:
230 elif out
["retcode"]==11:
231 raise NKHelloFailed(out
["errmsg"])
233 raise NKUnknownError(out
["errmsg"])
235 def login_NK(username
,password
,typ
="bdd"):
237 if typ
=="special": # ça c'est pour Basile lui-même
240 masque
='[["all"],["all"],false]'
242 # Basile a un compte special user
243 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
246 except Exception as exc
:
247 # Si on a foiré quelque part, c'est que le serveur est down
248 raise NKRefused(str(exc
))
249 # On vérifie ensuite que le login
250 return json
.loads(out
),sock
253 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
255 chain
=unicode(chain
,"utf8")
257 chain
=unicode(chain
,"utf8").lower()
258 allmatches
="("+"|".join(matches
)+")"
259 reg
=(avant
+allmatches
+apres
).lower()
260 o
=re
.match(reg
,chain
)
263 def is_insult(chain
,debug
=True):
264 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
265 def is_not_insult(chain
):
266 chain
=unicode(chain
,"utf8")
267 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
268 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
269 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
270 if re
.match(reg
,chain
):
274 def is_compliment(chain
,debug
=True):
275 return is_something(chain
,config_compliment_triggers
,avant
=".*(?:^| |')")
277 return is_something(chain
,config_perdu
)
279 return is_something(chain
,config_tag_triggers
)
281 return is_something(chain
,config_gros
)
283 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
285 return is_something(chain
,config_merci_triggers
)
286 def is_tamere(chain
):
287 return is_something(chain
,config_tamere_triggers
)
288 def is_bad_action_trigger(chain
,pseudo
):
289 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
290 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
291 def is_good_action_trigger(chain
,pseudo
):
292 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
293 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
294 def is_bonjour(chain
):
295 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
296 def is_bonne_nuit(chain
):
297 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
299 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
302 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
303 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
305 return is_time(config_daytime
)
307 return is_time(config_nighttime
)
310 class UnicodeBotError(Exception):
312 def bot_unicode(chain
):
314 unicode(chain
,"utf8")
315 except UnicodeDecodeError as exc
:
316 raise UnicodeBotError
318 class Basile(ircbot
.SingleServerIRCBot
):
319 def __init__(self
,serveur
,debug
=False):
320 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
321 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
322 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
325 self
.overops
=config_overops
326 self
.ops
=self
.overops
+config_ops
327 self
.report_bugs_to
=config_report_bugs_to
328 self
.chanlist
=config_chanlist
330 self
.identities
=pickle
.load(open("identities.pickle","r"))
331 self
.stay_channels
=config_stay_channels
332 self
.quiet_channels
=config_quiet_channels
336 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
338 login_result
,sock
=login_NK(username
,password
,typ
)
339 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
340 except NKRefused
as exc
:
341 for report
in self
.report_bugs_to
:
342 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
344 except NKHelloFailed
as exc
:
345 for report
in self
.report_bugs_to
:
347 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
349 except NKUnknownError
as exc
:
350 erreurs
=["Une fucking erreur inconnue s'est produite"]
351 erreurs
+=str(exc
).split("\n")
352 for report
in self
.report_bugs_to
:
354 serv
.privmsg(report
,err
)
356 except Exception as exc
:
357 # Exception qui ne vient pas de la communication avec le serveur NK2015
358 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
365 def give_me_my_pseudo(self
,serv
):
366 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
367 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
369 serv
.nick(config_irc_pseudo
)
371 def on_welcome(self
, serv
, ev
):
372 self
.serv
=serv
# ça serv ira :)
373 self
.give_me_my_pseudo(serv
)
374 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
375 log(self
.serveur
,"Connected")
377 self
.chanlist
=["#bot"]
378 for c
in self
.chanlist
:
379 log(self
.serveur
,"JOIN %s"%(c))
381 # on ouvre la connexion note de Basile, special user
382 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
384 for report
in self
.report_bugs_to
:
385 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
387 def lost(self
,serv
,channel
,forced
=False):
388 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
389 if not channel
in self
.quiet_channels
or forced
:
390 serv
.privmsg(channel
,"J'ai perdu !")
391 self
.last_perdu
=time
.time()
392 delay
=config_time_between_perdu_trigger
393 delta
=config_time_between_perdu_trigger_delta
394 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
396 def pourmoi(self
, serv
, message
):
397 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
400 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
401 return (True,message
[size
+1:].lstrip(" "))
403 return (False,message
)
405 def on_privmsg(self
, serv
, ev
):
406 message
=ev
.arguments()[0]
407 auteur
= irclib
.nm_to_n(ev
.source())
409 test
=bot_unicode(message
)
410 except UnicodeBotError
:
412 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
414 message
=message
.split()
415 cmd
=message
[0].lower()
418 if not len(message
) in [2,3]:
419 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
423 username
=(message
[1])
424 password
=" ".join(message
[2:])
426 password
=" ".join(message
[1:])
427 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
429 self
.sockets
[username
]=sock
430 serv
.privmsg(auteur
,"Connection successful")
431 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
433 serv
.privmsg(auteur
,"Connection failed")
434 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
437 helpdico
={"connect": """CONNECT [<username>] <password>
438 Ouvre une connexion au serveur NoteKfet.
439 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
440 "identify": """IDENTIFY <username> <password>
441 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
442 Sans paramètre, je réponds sous quel pseudo je te connais.""",
443 "drop":"""DROP <password>
444 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
445 helpmsg_default
="""Liste des commandes :
446 HELP Affiche de l'aide sur une commande.
447 CONNECT Ouvre une connection au serveur Note Kfet.
448 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
449 DROP Me fait oublier ton identité.
450 SOLDE Obtenir ton solde"""
452 JOIN Faire rejoindre un chan
453 LEAVE Faire quitter un chan
454 QUIET Se taire sur un chan
455 NOQUIET Opposé de QUIET
456 LOST Perdre sur un chan
457 SOLDE <pseudo> Donner le solde de quelqu'un"""
459 SAY Fait envoyer un message sur un chan ou à une personne
460 DO Fait faire une action sur un chan
461 STAY Ignorera les prochains LEAVE pour un chan
462 NOSTAY Opposé de STAY
465 helpmsg
=helpmsg_default
466 if auteur
in self
.ops
:
468 if auteur
in self
.overops
:
469 helpmsg
+=helpmsg_overops
471 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
472 for ligne
in helpmsg
.split("\n"):
473 serv
.privmsg(auteur
,ligne
)
474 elif cmd
=="identify":
476 if self
.identities
.has_key(auteur
):
477 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
478 self
.identities
[auteur
].encode("utf8")))
480 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
481 elif len(message
)>=3:
482 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
483 success
,_
=self
.new_connection_NK(serv
,username
,password
)
485 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
486 serv
.privmsg(auteur
,"Identité enregistrée.")
487 self
.identities
[auteur
]=username
488 pickle
.dump(self
.identities
,open("identities.pickle","w"))
490 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
491 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
493 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
496 if self
.identities
.has_key(auteur
):
497 password
=" ".join(message
[1:])
498 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
500 del self
.identities
[auteur
]
501 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
502 pickle
.dump(self
.identities
,open("identities.pickle","w"))
503 serv
.privmsg(auteur
,"Identité oubliée.")
505 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
506 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
508 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
510 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
512 if auteur
in self
.ops
:
514 if message
[1] in self
.chanlist
:
515 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
517 serv
.join(message
[1])
518 self
.chanlist
.append(message
[1])
519 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
520 log(self
.serveur
,"priv",auteur
," ".join(message
))
522 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
526 if auteur
in self
.ops
and len(message
)>1:
527 if message
[1] in self
.chanlist
:
528 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
529 serv
.part(message
[1])
530 self
.chanlist
.remove(message
[1])
531 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
533 serv
.privmsg(auteur
,"Non, je reste !")
534 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
536 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
540 if auteur
in self
.overops
:
542 if message
[1] in self
.stay_channels
:
543 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
544 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
546 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
547 self
.stay_channels
.append(message
[1])
548 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
550 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
554 if auteur
in self
.overops
:
556 if message
[1] in self
.stay_channels
:
557 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
558 self
.stay_channels
.remove(message
[1])
559 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
561 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
562 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
567 if auteur
in self
.overops
:
568 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
569 quit_message
=random
.choice(config_quit_messages
)
570 self
.die(message
=quit_message
)
574 if auteur
in self
.ops
:
576 if message
[1] in self
.quiet_channels
:
577 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
578 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
580 self
.quiet_channels
.append(message
[1])
581 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
582 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
584 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
588 if auteur
in self
.ops
:
590 if message
[1] in self
.quiet_channels
:
591 self
.quiet_channels
.remove(message
[1])
592 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
593 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
595 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
596 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
600 if auteur
in self
.overops
and len(message
)>2:
601 serv
.privmsg(message
[1]," ".join(message
[2:]))
602 log(self
.serveur
,"priv",auteur
," ".join(message
))
603 elif len(message
)<=2:
604 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
608 if auteur
in self
.overops
and len(message
)>2:
609 serv
.action(message
[1]," ".join(message
[2:]))
610 log(self
.serveur
,"priv",auteur
," ".join(message
))
611 elif len(message
)<=2:
612 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
616 if auteur
in self
.overops
and len(message
)>2:
617 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
618 log(self
.serveur
,"priv",auteur
," ".join(message
))
619 elif len(message
)<=2:
620 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
624 if auteur
in self
.ops
and len(message
)>1:
625 serv
.privmsg(message
[1],"J'ai perdu !")
626 log(self
.serveur
,"priv",auteur
," ".join(message
))
627 elif len(message
)<=1:
628 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
633 if self
.identities
.has_key(auteur
):
635 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
636 ret
=json
.loads(self
.nk
.read())
637 solde
=ret
["msg"][0]["solde"]
638 pseudo
=ret
["msg"][0]["pseudo"]
639 except Exception as exc
:
641 serv
.privmsg(auteur
,"failed")
642 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
644 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
646 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
647 elif auteur
in self
.ops
:
649 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
650 ret
=json
.loads(self
.nk
.read())
651 solde
=ret
["msg"][0]["solde"]
652 pseudo
=ret
["msg"][0]["pseudo"]
653 except Exception as exc
:
654 serv
.privmsg(auteur
,"failed")
655 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
657 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
661 serv
.privmsg(auteur
,"Je n'ai pas compris. Essayez HELP…")
663 def on_pubmsg(self
, serv
, ev
):
664 auteur
= irclib
.nm_to_n(ev
.source())
666 message
= ev
.arguments()[0]
668 test
=bot_unicode(message
)
669 except UnicodeBotError
:
670 if not canal
in self
.quiet_channels
:
672 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
674 pour_moi
,message
=self
.pourmoi(serv
,message
)
675 if pour_moi
and message
.split()!=[]:
676 cmd
=message
.split()[0].lower()
678 args
=" ".join(message
.split()[1:])
681 if cmd
in ["meurs","die","crève"]:
682 if auteur
in self
.overops
:
683 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
686 serv
.privmsg(canal
,"%s: mourrez vous-même !"%(auteur))
687 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
689 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
690 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
691 or auteur
in self
.overops
):
692 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
693 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
694 if canal
in self
.chanlist
:
695 self
.chanlist
.remove(canal
)
697 serv
.privmsg(canal
,"%s: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(auteur))
698 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
700 elif cmd
in ["reconnect"]:
701 if auteur
in self
.ops
:
703 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
704 config_note_password
,"special")[1]
705 except Exception as exc
:
707 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
709 serv
.privmsg(canal
,"%s: done"%(auteur))
710 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
712 serv
.privmsg(canal
,"%s: failed"%(auteur))
713 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
714 for report
in self
.report_bugs_to
:
715 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
717 serv
.privmsg(canal
,"%s: crève !"%(auteur))
718 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
720 elif cmd
in ["deviens","pseudo"]:
721 if auteur
in self
.ops
:
724 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
726 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
727 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
728 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
729 serv
.privmsg(canal
,"%s: pong"%(auteur))
731 elif cmd
in ["solde","!solde"]:
732 if self
.identities
.has_key(auteur
):
733 pseudo
=self
.identities
[auteur
]
735 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
736 ret
=json
.loads(self
.nk
.read())
737 solde
=ret
["msg"][0]["solde"]
738 pseudo
=ret
["msg"][0]["pseudo"]
739 except Exception as exc
:
740 serv
.privmsg(canal
,"%s: failed"%(auteur))
741 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
743 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
744 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
746 serv
.privmsg(canal
,"%s: Je ne connais pas votre pseudo note."%(auteur))
747 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
748 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
749 and not canal
in self
.quiet_channels
):
750 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
751 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
752 if auteur
in config_manzana
:
753 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
755 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
756 if is_insult(message
) and not canal
in self
.quiet_channels
:
757 if is_not_insult(message
):
758 answer
=random
.choice(config_compliment_answers
)
759 for ligne
in answer
.split("\n"):
760 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
762 answer
=random
.choice(config_insultes_answers
)
763 for ligne
in answer
.split("\n"):
764 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
765 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
766 answer
=random
.choice(config_compliment_answers
)
767 for ligne
in answer
.split("\n"):
768 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
769 gros_match
=is_gros(message
)
770 if gros_match
and not canal
in self
.quiet_channels
:
771 taille
=get_filesize()
772 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
773 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
774 if is_tesla(message
) and not canal
in self
.quiet_channels
:
775 l1
,l2
=config_tesla_answers
,config_tesla_actions
776 n1
,n2
=len(l1
),len(l2
)
777 i
=random
.randrange(n1
+n2
)
779 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
781 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
782 if is_tamere(message
) and not canal
in self
.quiet_channels
:
783 answer
=random
.choice(config_tamere_answers
)
784 for ligne
in answer
.split("\n"):
785 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
786 if is_tag(message
) and not canal
in self
.quiet_channels
:
787 if auteur
in self
.ops
:
788 action
=random
.choice(config_tag_actions
)
789 serv
.action(canal
,action
.encode("utf8"))
790 self
.quiet_channels
.append(canal
)
792 answer
=random
.choice(config_tag_answers
)
793 for ligne
in answer
.split("\n"):
794 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
795 if is_merci(message
):
796 answer
=random
.choice(config_merci_answers
)
797 for ligne
in answer
.split("\n"):
798 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
799 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
800 unicode(message
.upper(),"utf8"))
801 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
802 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
803 if out
and not canal
in self
.quiet_channels
:
807 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
809 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
811 if out
+1>1000 and random
.randrange(4)==0:
812 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
814 except Exception as exc
:
816 if re
.match("[A-Y]",out
):
817 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
818 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
820 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
822 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
823 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
825 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
826 for i
in range(20)}[j
]
828 out
=int(translate(out
))
829 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
830 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
832 answer
=random
.choice(config_night_answers
)
834 answer
=random
.choice(config_bonjour_answers
)
836 answer
=random
.choice(config_bonsoir_answers
)
837 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
838 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
839 answer
=random
.choice(config_bonne_nuit_answers
)
840 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
841 if is_pan(message
) and not canal
in self
.quiet_channels
:
842 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))
844 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
845 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
846 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
847 if auteur
in config_manzana
:
848 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
850 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
851 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
852 failanswers
=config_buffer_fail_answers
853 answer
=random
.choice(failanswers
)
854 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
855 if not canal
in self
.quiet_channels
:
857 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
858 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
859 ).format(mypseudo
).lower(), message
.strip().lower()):
860 answer
=random
.choice(config_bonjour_answers
)
861 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
862 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
863 # proba de perdre sur trigger :
864 # avant 30min (enfin, config) : 0
865 # ensuite, +25%/30min, linéairement
866 deltat
=time
.time()-self
.last_perdu
867 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
868 if random
.uniform(0,1)<barre
:
869 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
870 self
.last_perdu
=time
.time()
872 def on_action(self
, serv
, ev
):
873 action
= ev
.arguments()[0]
874 auteur
= irclib
.nm_to_n(ev
.source())
875 channel
= ev
.target()
877 test
=bot_unicode(action
)
878 except UnicodeBotError
:
879 serv
.privmsg(channel
,
880 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
884 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
885 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
886 n1
,n2
=len(l1
),len(l2
)
887 i
=random
.randrange(n1
+n2
)
889 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
891 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
892 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
893 l1
,l2
=config_good_action_answers
,config_good_action_actions
894 n1
,n2
=len(l1
),len(l2
)
895 i
=random
.randrange(n1
+n2
)
897 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
899 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
901 def on_kick(self
,serv
,ev
):
902 auteur
= irclib
.nm_to_n(ev
.source())
903 channel
= ev
.target()
904 victime
= ev
.arguments()[0]
905 raison
= ev
.arguments()[1]
906 if victime
==self
.nick
:
907 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
910 l1
,l2
=config_kick_answers
,config_kick_actions
911 n1
,n2
=len(l1
),len(l2
)
912 i
=random
.randrange(n1
+n2
)
914 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
916 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
919 return self
.serv
.get_nickname()
920 nick
=property(_getnick
)
923 if __name__
=="__main__":
926 print "Usage : basile.py <serveur> [--debug]"
929 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
933 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
934 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
936 serveur
=serveurs
[serveur
]
938 print "Server Unknown : %s"%(serveur)
940 basile
=Basile(serveur
,debug
)