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
"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
68 # config "à peine quelques kilos octets"
69 config_gros
=[u
"gros",u
"énorme",u
"lourd"]
70 config_thisfile
= os
.path
.realpath( __file__
)
72 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
74 # config spéciale-iota
75 config_buffer_fail_answers
=[u
"Pas de chance !",u
"Révisez vos classiques !",
76 u
"Encore un effort, je sais que vous pouvez le faire. ;)",
77 u
"Where did you learn to type?"]
79 # config "jeu", d'ailleurs, j'ai perdu.
80 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))"
81 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)"
82 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
83 config_regexp_faire
=u
"fais"
84 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
85 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
86 u
"perdiss(e(|s|nt)|i(ons|ez))",
87 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
89 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
91 u
"trouvant",u
"trouvaille(|s)",
93 u
"victoire(|s)",u
"vaincu(|s|e|es)",
94 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
95 u
"jeu(|x)",u
"game(|s)"]
96 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
97 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
98 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
101 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"]
102 config_tag_actions
=[u
"se tait",u
"se tient coi"]
104 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
105 u
"Dès que cela sera utile.",
106 u
"Une autre fois, peut-être.",
107 u
"Si je me tais, qui vous rappellera combien vous me devez ?",
108 u
"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
109 u
"Je ne ressens pas de besoin irrésistible de me taire, navré."]
112 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
113 config_tesla_answers
=[
116 u
"En quoi puis-je me rendre utile ?",
117 u
"On a besoin de moi ?"
119 config_tesla_actions
=[u
"est là",u
"attend des instructions",u
"est toujours disponible"]
121 # config en cas de non-insulte
122 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa",u
"efficace"]
123 config_compliment_answers
=[
124 u
"Merci, c'est gentil de votre part. :)",
125 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
126 u
"Je vous remercie.",
127 u
"C'est trop d'honneur.",
128 u
"Vous êtes bien aimable."
132 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
133 config_merci_answers
=[u
"Mais de rien.",u
"À votre service. ;)",u
"Quand vous voulez. :)",
134 u
"Tout le plaisir est pour moi."]
137 config_tamere_triggers
=[u
"ta mère"]
138 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
139 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
140 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
141 u
"""N'avait-on pas dit "pas les mamans" ?"""]
143 # config pour les actions désagréables à Basile
144 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
145 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
146 config_bad_action_answers
=[
147 u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
148 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
149 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
150 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
151 u
"{}: Entre nous, cela vous gratifie-t-il ?",
152 u
"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
153 u
"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
155 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à ?"]
157 # config pour les actions agréables à Basile
158 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
159 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…"]
160 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"]
162 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
163 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"]
164 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"]
165 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é."]
166 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, {} ?"]
167 config_daytime
= [7,18]
168 config_nighttime
= [3, 6]
171 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nenuit"]
172 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 !"]
174 # config PEB est encore en train d'abuser de ses droits.
175 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 !"]
176 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."]
178 # config on m'a demandé de mourir
179 config_quit_messages
=[u
"Bien que cela me désole, je me vois dans l'obligation de vous abandonner."]
182 class NKError(Exception):
183 def __init__(self
,msg
):
184 Exception.__init
__(self
)
188 def __unicode__(self
):
189 return unicode(self
.msg
)
191 class NKRefused(NKError
):
194 class NKHelloFailed(NKError
):
197 class NKUnknownError(NKError
):
200 def log(serveur
,channel
,auteur
=None,message
=None):
201 f
=open(get_config_logfile(serveur
),"a")
202 if auteur
==message
==None:
203 # alors c'est que c'est pas un channel mais juste une ligne de log
204 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
206 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
208 if config_debug_stdout
:
215 # On établit la connexion sur port 4242
216 sock
.connect(("127.0.0.1",4242))
218 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
220 sock
.write('hello "Basile"')
221 # On récupère la réponse du hello
224 except Exception as exc
:
225 # Si on a foiré quelque part, c'est que le serveur est down
226 raise NKRefused(str(exc
))
227 if out
["retcode"]==0:
229 elif out
["retcode"]==11:
230 raise NKHelloFailed(out
["errmsg"])
232 raise NKUnknownError(out
["errmsg"])
234 def login_NK(username
,password
,typ
="bdd"):
236 if typ
=="special": # ça c'est pour Basile lui-même
239 masque
='[["all"],["all"],false]'
241 # Basile a un compte special user
242 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
245 except Exception as exc
:
246 # Si on a foiré quelque part, c'est que le serveur est down
247 raise NKRefused(str(exc
))
248 # On vérifie ensuite que le login
249 return json
.loads(out
),sock
252 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
254 chain
=unicode(chain
,"utf8")
256 chain
=unicode(chain
,"utf8").lower()
257 allmatches
="("+"|".join(matches
)+")"
258 reg
=(avant
+allmatches
+apres
).lower()
259 o
=re
.match(reg
,chain
)
262 def is_insult(chain
,debug
=True):
263 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
264 def is_not_insult(chain
):
265 chain
=unicode(chain
,"utf8")
266 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
267 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
268 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
269 if re
.match(reg
,chain
):
273 def is_compliment(chain
,debug
=True):
274 return is_something(chain
,config_compliment_triggers
,avant
=".*(?:^| |')")
276 return is_something(chain
,config_perdu
)
278 return is_something(chain
,config_tag_triggers
)
280 return is_something(chain
,config_gros
)
282 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
284 return is_something(chain
,config_merci_triggers
)
285 def is_tamere(chain
):
286 return is_something(chain
,config_tamere_triggers
)
287 def is_bad_action_trigger(chain
,pseudo
):
288 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
289 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
290 def is_good_action_trigger(chain
,pseudo
):
291 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
292 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
293 def is_bonjour(chain
):
294 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
295 def is_bonne_nuit(chain
):
296 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
298 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
301 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
302 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
304 return is_time(config_daytime
)
306 return is_time(config_nighttime
)
309 class UnicodeBotError(Exception):
311 def bot_unicode(chain
):
313 unicode(chain
,"utf8")
314 except UnicodeDecodeError as exc
:
315 raise UnicodeBotError
317 class Basile(ircbot
.SingleServerIRCBot
):
318 def __init__(self
,serveur
,debug
=False):
319 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
320 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
321 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
324 self
.overops
=config_overops
325 self
.ops
=self
.overops
+config_ops
326 self
.report_bugs_to
=config_report_bugs_to
327 self
.chanlist
=config_chanlist
329 self
.identities
=pickle
.load(open("identities.pickle","r"))
330 self
.stay_channels
=config_stay_channels
331 self
.quiet_channels
=config_quiet_channels
335 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
337 login_result
,sock
=login_NK(username
,password
,typ
)
338 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
339 except NKRefused
as exc
:
340 for report
in self
.report_bugs_to
:
341 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
343 except NKHelloFailed
as exc
:
344 for report
in self
.report_bugs_to
:
346 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
348 except NKUnknownError
as exc
:
349 erreurs
=["Une fucking erreur inconnue s'est produite"]
350 erreurs
+=str(exc
).split("\n")
351 for report
in self
.report_bugs_to
:
353 serv
.privmsg(report
,err
)
355 except Exception as exc
:
356 # Exception qui ne vient pas de la communication avec le serveur NK2015
357 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
364 def give_me_my_pseudo(self
,serv
):
365 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
366 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
368 serv
.nick(config_irc_pseudo
)
370 def on_welcome(self
, serv
, ev
):
371 self
.serv
=serv
# ça serv ira :)
372 self
.give_me_my_pseudo(serv
)
373 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
374 log(self
.serveur
,"Connected")
376 self
.chanlist
=["#bot"]
377 for c
in self
.chanlist
:
378 log(self
.serveur
,"JOIN %s"%(c))
380 # on ouvre la connexion note de Basile, special user
381 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
383 for report
in self
.report_bugs_to
:
384 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
386 def lost(self
,serv
,channel
,forced
=False):
387 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
388 if not channel
in self
.quiet_channels
or forced
:
389 serv
.privmsg(channel
,"J'ai perdu !")
390 self
.last_perdu
=time
.time()
391 delay
=config_time_between_perdu_trigger
392 delta
=config_time_between_perdu_trigger_delta
393 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
395 def pourmoi(self
, serv
, message
):
396 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
399 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
400 return (True,message
[size
+1:].lstrip(" "))
402 return (False,message
)
404 def on_privmsg(self
, serv
, ev
):
405 message
=ev
.arguments()[0]
406 auteur
= irclib
.nm_to_n(ev
.source())
408 test
=bot_unicode(message
)
409 except UnicodeBotError
:
411 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
413 message
=message
.split()
414 cmd
=message
[0].lower()
417 if not len(message
) in [2,3]:
418 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
422 username
=(message
[1])
423 password
=" ".join(message
[2:])
425 password
=" ".join(message
[1:])
426 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
428 self
.sockets
[username
]=sock
429 serv
.privmsg(auteur
,"Connection successful")
430 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
432 serv
.privmsg(auteur
,"Connection failed")
433 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
436 helpdico
={"connect": """CONNECT [<username>] <password>
437 Ouvre une connexion au serveur NoteKfet.
438 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
439 "identify": """IDENTIFY <username> <password>
440 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
441 Sans paramètre, je réponds sous quel pseudo je te connais.""",
442 "drop":"""DROP <password>
443 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
444 helpmsg_default
="""Liste des commandes :
445 HELP Affiche de l'aide sur une commande.
446 CONNECT Ouvre une connection au serveur Note Kfet.
447 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
448 DROP Me fait oublier ton identité.
449 SOLDE Obtenir ton solde"""
451 JOIN Faire rejoindre un chan
452 LEAVE Faire quitter un chan
453 QUIET Se taire sur un chan
454 NOQUIET Opposé de QUIET
455 LOST Perdre sur un chan
456 SOLDE <pseudo> Donner le solde de quelqu'un"""
458 SAY Fait envoyer un message sur un chan ou à une personne
459 DO Fait faire une action sur un chan
460 STAY Ignorera les prochains LEAVE pour un chan
461 NOSTAY Opposé de STAY
464 helpmsg
=helpmsg_default
465 if auteur
in self
.ops
:
467 if auteur
in self
.overops
:
468 helpmsg
+=helpmsg_overops
470 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
471 for ligne
in helpmsg
.split("\n"):
472 serv
.privmsg(auteur
,ligne
)
473 elif cmd
=="identify":
475 if self
.identities
.has_key(auteur
):
476 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
477 self
.identities
[auteur
].encode("utf8")))
479 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
480 elif len(message
)>=3:
481 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
482 success
,_
=self
.new_connection_NK(serv
,username
,password
)
484 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
485 serv
.privmsg(auteur
,"Identité enregistrée.")
486 self
.identities
[auteur
]=username
487 pickle
.dump(self
.identities
,open("identities.pickle","w"))
489 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
490 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
492 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
495 if self
.identities
.has_key(auteur
):
496 password
=" ".join(message
[1:])
497 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
499 del self
.identities
[auteur
]
500 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
501 pickle
.dump(self
.identities
,open("identities.pickle","w"))
502 serv
.privmsg(auteur
,"Identité oubliée.")
504 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
505 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
507 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
509 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
511 if auteur
in self
.ops
:
513 if message
[1] in self
.chanlist
:
514 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
516 serv
.join(message
[1])
517 self
.chanlist
.append(message
[1])
518 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
519 log(self
.serveur
,"priv",auteur
," ".join(message
))
521 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
525 if auteur
in self
.ops
and len(message
)>1:
526 if message
[1] in self
.chanlist
:
527 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
528 serv
.part(message
[1])
529 self
.chanlist
.remove(message
[1])
530 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
532 serv
.privmsg(auteur
,"Non, je reste !")
533 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
535 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
539 if auteur
in self
.overops
:
541 if message
[1] in self
.stay_channels
:
542 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
543 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
545 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
546 self
.stay_channels
.append(message
[1])
547 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
549 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
553 if auteur
in self
.overops
:
555 if message
[1] in self
.stay_channels
:
556 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
557 self
.stay_channels
.remove(message
[1])
558 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
560 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
561 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
566 if auteur
in self
.overops
:
567 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
568 quit_message
=random
.choice(config_quit_messages
)
569 self
.die(message
=quit_message
)
573 if auteur
in self
.ops
:
575 if message
[1] in self
.quiet_channels
:
576 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
577 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
579 self
.quiet_channels
.append(message
[1])
580 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
581 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
583 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
587 if auteur
in self
.ops
:
589 if message
[1] in self
.quiet_channels
:
590 self
.quiet_channels
.remove(message
[1])
591 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
592 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
594 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
595 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
599 if auteur
in self
.overops
and len(message
)>2:
600 serv
.privmsg(message
[1]," ".join(message
[2:]))
601 log(self
.serveur
,"priv",auteur
," ".join(message
))
602 elif len(message
)<=2:
603 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
607 if auteur
in self
.overops
and len(message
)>2:
608 serv
.action(message
[1]," ".join(message
[2:]))
609 log(self
.serveur
,"priv",auteur
," ".join(message
))
610 elif len(message
)<=2:
611 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
615 if auteur
in self
.overops
and len(message
)>2:
616 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
617 log(self
.serveur
,"priv",auteur
," ".join(message
))
618 elif len(message
)<=2:
619 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
623 if auteur
in self
.ops
and len(message
)>1:
624 serv
.privmsg(message
[1],"J'ai perdu !")
625 log(self
.serveur
,"priv",auteur
," ".join(message
))
626 elif len(message
)<=1:
627 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
632 if self
.identities
.has_key(auteur
):
634 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
635 ret
=json
.loads(self
.nk
.read())
636 solde
=ret
["msg"][0]["solde"]
637 pseudo
=ret
["msg"][0]["pseudo"]
638 except Exception as exc
:
640 serv
.privmsg(auteur
,"failed")
641 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
643 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
645 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
646 elif auteur
in self
.ops
:
648 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
649 ret
=json
.loads(self
.nk
.read())
650 solde
=ret
["msg"][0]["solde"]
651 pseudo
=ret
["msg"][0]["pseudo"]
652 except Exception as exc
:
653 serv
.privmsg(auteur
,"failed")
654 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
656 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
660 serv
.privmsg(auteur
,"Je n'ai pas compris. Essayez HELP…")
662 def on_pubmsg(self
, serv
, ev
):
663 auteur
= irclib
.nm_to_n(ev
.source())
665 message
= ev
.arguments()[0]
667 test
=bot_unicode(message
)
668 except UnicodeBotError
:
669 if not canal
in self
.quiet_channels
:
671 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
673 pour_moi
,message
=self
.pourmoi(serv
,message
)
674 if pour_moi
and message
.split()!=[]:
675 cmd
=message
.split()[0].lower()
677 args
=" ".join(message
.split()[1:])
680 if cmd
in ["meurs","die","crève"]:
681 if auteur
in self
.overops
:
682 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
685 serv
.privmsg(canal
,"%s: mourrez vous-même !"%(auteur))
686 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
688 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
689 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
690 or auteur
in self
.overops
):
691 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
692 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
693 if canal
in self
.chanlist
:
694 self
.chanlist
.remove(canal
)
696 serv
.privmsg(canal
,"%s: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(auteur))
697 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
699 elif cmd
in ["reconnect"]:
700 if auteur
in self
.ops
:
702 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
703 config_note_password
,"special")[1]
704 except Exception as exc
:
706 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
708 serv
.privmsg(canal
,"%s: done"%(auteur))
709 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
711 serv
.privmsg(canal
,"%s: failed"%(auteur))
712 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
713 for report
in self
.report_bugs_to
:
714 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
716 serv
.privmsg(canal
,"%s: crève !"%(auteur))
717 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
719 elif cmd
in ["deviens","pseudo"]:
720 if auteur
in self
.ops
:
723 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
725 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
726 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
727 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
728 serv
.privmsg(canal
,"%s: pong"%(auteur))
730 elif cmd
in ["solde","!solde"]:
731 if self
.identities
.has_key(auteur
):
732 pseudo
=self
.identities
[auteur
]
734 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
735 ret
=json
.loads(self
.nk
.read())
736 solde
=ret
["msg"][0]["solde"]
737 pseudo
=ret
["msg"][0]["pseudo"]
738 except Exception as exc
:
739 serv
.privmsg(canal
,"%s: failed"%(auteur))
740 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
742 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
743 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
745 serv
.privmsg(canal
,"%s: Je ne connais pas votre pseudo note."%(auteur))
746 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
747 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
748 and not canal
in self
.quiet_channels
):
749 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
750 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
751 if auteur
in config_manzana
:
752 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
754 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
755 if is_insult(message
) and not canal
in self
.quiet_channels
:
756 if is_not_insult(message
):
757 answer
=random
.choice(config_compliment_answers
)
758 for ligne
in answer
.split("\n"):
759 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
761 answer
=random
.choice(config_insultes_answers
)
762 for ligne
in answer
.split("\n"):
763 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
764 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
765 answer
=random
.choice(config_compliment_answers
)
766 for ligne
in answer
.split("\n"):
767 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
768 gros_match
=is_gros(message
)
769 if gros_match
and not canal
in self
.quiet_channels
:
770 taille
=get_filesize()
771 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
772 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
773 if is_tesla(message
) and not canal
in self
.quiet_channels
:
774 l1
,l2
=config_tesla_answers
,config_tesla_actions
775 n1
,n2
=len(l1
),len(l2
)
776 i
=random
.randrange(n1
+n2
)
778 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
780 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
781 if is_tamere(message
) and not canal
in self
.quiet_channels
:
782 answer
=random
.choice(config_tamere_answers
)
783 for ligne
in answer
.split("\n"):
784 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
785 if is_tag(message
) and not canal
in self
.quiet_channels
:
786 if auteur
in self
.ops
:
787 action
=random
.choice(config_tag_actions
)
788 serv
.action(canal
,action
.encode("utf8"))
789 self
.quiet_channels
.append(canal
)
791 answer
=random
.choice(config_tag_answers
)
792 for ligne
in answer
.split("\n"):
793 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
794 if is_merci(message
):
795 answer
=random
.choice(config_merci_answers
)
796 for ligne
in answer
.split("\n"):
797 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
798 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
799 unicode(message
.upper(),"utf8"))
800 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
801 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
802 if out
and not canal
in self
.quiet_channels
:
806 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
808 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
810 if out
+1>1000 and random
.randrange(4)==0:
811 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
813 except Exception as exc
:
815 if re
.match("[A-Y]",out
):
816 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
817 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
819 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
821 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
822 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
824 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
825 for i
in range(20)}[j
]
827 out
=int(translate(out
))
828 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
829 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
831 answer
=random
.choice(config_night_answers
)
833 answer
=random
.choice(config_bonjour_answers
)
835 answer
=random
.choice(config_bonsoir_answers
)
836 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
837 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
838 answer
=random
.choice(config_bonne_nuit_answers
)
839 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
840 if is_pan(message
) and not canal
in self
.quiet_channels
:
841 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))
843 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
844 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
845 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
846 if auteur
in config_manzana
:
847 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
849 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
850 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
851 failanswers
=config_buffer_fail_answers
852 answer
=random
.choice(failanswers
)
853 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
854 if not canal
in self
.quiet_channels
:
856 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
857 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
858 ).format(mypseudo
).lower(), message
.strip().lower()):
859 answer
=random
.choice(config_bonjour_answers
)
860 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
861 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
862 # proba de perdre sur trigger :
863 # avant 30min (enfin, config) : 0
864 # ensuite, +25%/30min, linéairement
865 deltat
=time
.time()-self
.last_perdu
866 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
867 if random
.uniform(0,1)<barre
:
868 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
869 self
.last_perdu
=time
.time()
871 def on_action(self
, serv
, ev
):
872 action
= ev
.arguments()[0]
873 auteur
= irclib
.nm_to_n(ev
.source())
874 channel
= ev
.target()
876 test
=bot_unicode(action
)
877 except UnicodeBotError
:
878 serv
.privmsg(channel
,
879 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
883 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
884 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
885 n1
,n2
=len(l1
),len(l2
)
886 i
=random
.randrange(n1
+n2
)
888 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
890 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
891 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
892 l1
,l2
=config_good_action_answers
,config_good_action_actions
893 n1
,n2
=len(l1
),len(l2
)
894 i
=random
.randrange(n1
+n2
)
896 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
898 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
900 def on_kick(self
,serv
,ev
):
901 auteur
= irclib
.nm_to_n(ev
.source())
902 channel
= ev
.target()
903 victime
= ev
.arguments()[0]
904 raison
= ev
.arguments()[1]
905 if victime
==self
.nick
:
906 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
909 l1
,l2
=config_kick_answers
,config_kick_actions
910 n1
,n2
=len(l1
),len(l2
)
911 i
=random
.randrange(n1
+n2
)
913 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
915 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
918 return self
.serv
.get_nickname()
919 nick
=property(_getnick
)
922 if __name__
=="__main__":
925 print "Usage : basile.py <serveur> [--debug]"
928 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
932 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
933 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
935 serveur
=serveurs
[serveur
]
937 print "Server Unknown : %s"%(serveur)
939 basile
=Basile(serveur
,debug
)