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"]
71 # config spéciale-iota
72 config_buffer_fail_answers
=[u
"Pas de chance !",u
"Révisez vos classiques !",
73 u
"Encore un effort, je sais que vous pouvez le faire. ;)",
74 u
"Where did you learn to type?"]
76 # config "jeu", d'ailleurs, j'ai perdu.
77 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))"
78 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)"
79 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
80 config_regexp_faire
=u
"fais"
81 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
82 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
83 u
"perdiss(e(|s|nt)|i(ons|ez))",
84 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
86 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
88 u
"trouvant",u
"trouvaille(|s)",
90 u
"victoire(|s)",u
"vaincu(|s|e|es)",
91 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
92 u
"jeu(|x)",u
"game(|s)"]
93 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
94 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
95 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
98 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"]
99 config_tag_actions
=[u
"se tait",u
"se tient coi"]
101 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
102 u
"Dès que cela sera utile.",
103 u
"Une autre fois, peut-être.",
104 u
"Si je me tais, qui vous rappellera combien vous me devez ?",
105 u
"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
106 u
"Je ne ressens pas de besoin irrésistible de me taire, navré."]
109 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
110 config_tesla_answers
=[
113 u
"En quoi puis-je me rendre utile ?",
114 u
"On a besoin de moi ?"
116 config_tesla_actions
=[u
"est là",u
"attend des instructions",u
"est toujours disponible"]
118 # config en cas de non-insulte
119 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa",u
"efficace"]
120 config_compliment_answers
=[
121 u
"Merci, c'est gentil de votre part. :)",
122 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
123 u
"Je vous remercie.",
124 u
"C'est trop d'honneur.",
125 u
"Vous êtes bien aimable."
129 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
130 config_merci_answers
=[u
"Mais de rien.",u
"À votre service. ;)",u
"Quand vous voulez. :)",
131 u
"Tout le plaisir est pour moi."]
134 config_tamere_triggers
=[u
"ta mère"]
135 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
136 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
137 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
138 u
"""N'avait-on pas dit "pas les mamans" ?"""]
140 # config pour les actions désagréables à Basile
141 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
142 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
143 config_bad_action_answers
=[
144 u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
145 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
146 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
147 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
148 u
"{}: Entre nous, cela vous gratifie-t-il ?",
149 u
"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
150 u
"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
152 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à ?"]
154 # config pour les actions agréables à Basile
155 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
156 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…"]
157 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"]
159 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
160 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"]
161 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"]
162 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é."]
163 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, {} ?"]
164 config_daytime
= [7,18]
165 config_nighttime
= [3, 6]
168 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nenuit"]
169 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 !"]
171 # config PEB est encore en train d'abuser de ses droits.
172 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 !"]
173 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."]
175 config_thisfile
= os
.path
.realpath( __file__
)
177 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
179 class NKError(Exception):
180 def __init__(self
,msg
):
181 Exception.__init
__(self
)
185 def __unicode__(self
):
186 return unicode(self
.msg
)
188 class NKRefused(NKError
):
191 class NKHelloFailed(NKError
):
194 class NKUnknownError(NKError
):
197 def log(serveur
,channel
,auteur
=None,message
=None):
198 f
=open(get_config_logfile(serveur
),"a")
199 if auteur
==message
==None:
200 # alors c'est que c'est pas un channel mais juste une ligne de log
201 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
203 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
205 if config_debug_stdout
:
212 # On établit la connexion sur port 4242
213 sock
.connect(("127.0.0.1",4242))
215 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
217 sock
.write('hello "Basile"')
218 # On récupère la réponse du hello
221 except Exception as exc
:
222 # Si on a foiré quelque part, c'est que le serveur est down
223 raise NKRefused(str(exc
))
224 if out
["retcode"]==0:
226 elif out
["retcode"]==11:
227 raise NKHelloFailed(out
["errmsg"])
229 raise NKUnknownError(out
["errmsg"])
231 def login_NK(username
,password
,typ
="bdd"):
233 if typ
=="special": # ça c'est pour Basile lui-même
236 masque
='[["all"],["all"],false]'
238 # Basile a un compte special user
239 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
242 except Exception as exc
:
243 # Si on a foiré quelque part, c'est que le serveur est down
244 raise NKRefused(str(exc
))
245 # On vérifie ensuite que le login
246 return json
.loads(out
),sock
249 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
251 chain
=unicode(chain
,"utf8")
253 chain
=unicode(chain
,"utf8").lower()
254 allmatches
="("+"|".join(matches
)+")"
255 reg
=(avant
+allmatches
+apres
).lower()
256 o
=re
.match(reg
,chain
)
259 def is_insult(chain
,debug
=True):
260 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
261 def is_not_insult(chain
):
262 chain
=unicode(chain
,"utf8")
263 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
264 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
265 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
266 if re
.match(reg
,chain
):
270 def is_compliment(chain
,debug
=True):
271 return is_something(chain
,config_compliment_triggers
,avant
=".*(?:^| |')")
273 return is_something(chain
,config_perdu
)
275 return is_something(chain
,config_tag_triggers
)
277 return is_something(chain
,config_gros
)
279 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
281 return is_something(chain
,config_merci_triggers
)
282 def is_tamere(chain
):
283 return is_something(chain
,config_tamere_triggers
)
284 def is_bad_action_trigger(chain
,pseudo
):
285 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
286 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
287 def is_good_action_trigger(chain
,pseudo
):
288 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
289 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
290 def is_bonjour(chain
):
291 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
292 def is_bonne_nuit(chain
):
293 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
295 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
298 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
299 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
301 return is_time(config_daytime
)
303 return is_time(config_nighttime
)
306 class UnicodeBotError(Exception):
308 def bot_unicode(chain
):
310 unicode(chain
,"utf8")
311 except UnicodeDecodeError as exc
:
312 raise UnicodeBotError
314 class Basile(ircbot
.SingleServerIRCBot
):
315 def __init__(self
,serveur
,debug
=False):
316 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
317 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
318 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
321 self
.overops
=config_overops
322 self
.ops
=self
.overops
+config_ops
323 self
.report_bugs_to
=config_report_bugs_to
324 self
.chanlist
=config_chanlist
326 self
.identities
=pickle
.load(open("identities.pickle","r"))
327 self
.stay_channels
=config_stay_channels
328 self
.quiet_channels
=config_quiet_channels
332 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
334 login_result
,sock
=login_NK(username
,password
,typ
)
335 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
336 except NKRefused
as exc
:
337 for report
in self
.report_bugs_to
:
338 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
340 except NKHelloFailed
as exc
:
341 for report
in self
.report_bugs_to
:
343 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
345 except NKUnknownError
as exc
:
346 erreurs
=["Une fucking erreur inconnue s'est produite"]
347 erreurs
+=str(exc
).split("\n")
348 for report
in self
.report_bugs_to
:
350 serv
.privmsg(report
,err
)
352 except Exception as exc
:
353 # Exception qui ne vient pas de la communication avec le serveur NK2015
354 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
361 def give_me_my_pseudo(self
,serv
):
362 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
363 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
365 serv
.nick(config_irc_pseudo
)
367 def on_welcome(self
, serv
, ev
):
368 self
.serv
=serv
# ça serv ira :)
369 self
.give_me_my_pseudo(serv
)
370 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
371 log(self
.serveur
,"Connected")
373 self
.chanlist
=["#bot"]
374 for c
in self
.chanlist
:
375 log(self
.serveur
,"JOIN %s"%(c))
377 # on ouvre la connexion note de Basile, special user
378 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
380 for report
in self
.report_bugs_to
:
381 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
383 def lost(self
,serv
,channel
,forced
=False):
384 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
385 if not channel
in self
.quiet_channels
or forced
:
386 serv
.privmsg(channel
,"J'ai perdu !")
387 self
.last_perdu
=time
.time()
388 delay
=config_time_between_perdu_trigger
389 delta
=config_time_between_perdu_trigger_delta
390 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
392 def pourmoi(self
, serv
, message
):
393 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
396 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
397 return (True,message
[size
+1:].lstrip(" "))
399 return (False,message
)
401 def on_privmsg(self
, serv
, ev
):
402 message
=ev
.arguments()[0]
403 auteur
= irclib
.nm_to_n(ev
.source())
405 test
=bot_unicode(message
)
406 except UnicodeBotError
:
408 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
410 message
=message
.split()
411 cmd
=message
[0].lower()
414 if not len(message
) in [2,3]:
415 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
419 username
=(message
[1])
420 password
=" ".join(message
[2:])
422 password
=" ".join(message
[1:])
423 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
425 self
.sockets
[username
]=sock
426 serv
.privmsg(auteur
,"Connection successful")
427 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
429 serv
.privmsg(auteur
,"Connection failed")
430 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
433 helpdico
={"connect": """CONNECT [<username>] <password>
434 Ouvre une connexion au serveur NoteKfet.
435 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
436 "identify": """IDENTIFY <username> <password>
437 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
438 Sans paramètre, je réponds sous quel pseudo je te connais.""",
439 "drop":"""DROP <password>
440 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
441 helpmsg_default
="""Liste des commandes :
442 HELP Affiche de l'aide sur une commande.
443 CONNECT Ouvre une connection au serveur Note Kfet.
444 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
445 DROP Me fait oublier ton identité.
446 SOLDE Obtenir ton solde"""
448 JOIN Faire rejoindre un chan
449 LEAVE Faire quitter un chan
450 QUIET Se taire sur un chan
451 NOQUIET Opposé de QUIET
452 LOST Perdre sur un chan
453 SOLDE <pseudo> Donner le solde de quelqu'un"""
455 SAY Fait envoyer un message sur un chan ou à une personne
456 DO Fait faire une action sur un chan
457 STAY Ignorera les prochains LEAVE pour un chan
458 NOSTAY Opposé de STAY
461 helpmsg
=helpmsg_default
462 if auteur
in self
.ops
:
464 if auteur
in self
.overops
:
465 helpmsg
+=helpmsg_overops
467 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
468 for ligne
in helpmsg
.split("\n"):
469 serv
.privmsg(auteur
,ligne
)
470 elif cmd
=="identify":
472 if self
.identities
.has_key(auteur
):
473 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
474 self
.identities
[auteur
].encode("utf8")))
476 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
477 elif len(message
)>=3:
478 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
479 success
,_
=self
.new_connection_NK(serv
,username
,password
)
481 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
482 serv
.privmsg(auteur
,"Identité enregistrée.")
483 self
.identities
[auteur
]=username
484 pickle
.dump(self
.identities
,open("identities.pickle","w"))
486 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
487 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
489 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
492 if self
.identities
.has_key(auteur
):
493 password
=" ".join(message
[1:])
494 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
496 del self
.identities
[auteur
]
497 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
498 pickle
.dump(self
.identities
,open("identities.pickle","w"))
499 serv
.privmsg(auteur
,"Identité oubliée.")
501 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
502 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
504 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
506 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
508 if auteur
in self
.ops
:
510 if message
[1] in self
.chanlist
:
511 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
513 serv
.join(message
[1])
514 self
.chanlist
.append(message
[1])
515 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
516 log(self
.serveur
,"priv",auteur
," ".join(message
))
518 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
522 if auteur
in self
.ops
and len(message
)>1:
523 if message
[1] in self
.chanlist
:
524 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
525 serv
.part(message
[1])
526 self
.chanlist
.remove(message
[1])
527 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
529 serv
.privmsg(auteur
,"Non, je reste !")
530 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
532 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
536 if auteur
in self
.overops
:
538 if message
[1] in self
.stay_channels
:
539 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
540 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
542 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
543 self
.stay_channels
.append(message
[1])
544 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
546 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
550 if auteur
in self
.overops
:
552 if message
[1] in self
.stay_channels
:
553 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
554 self
.stay_channels
.remove(message
[1])
555 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
557 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
558 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
563 if auteur
in self
.overops
:
564 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
569 if auteur
in self
.ops
:
571 if message
[1] in self
.quiet_channels
:
572 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
573 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
575 self
.quiet_channels
.append(message
[1])
576 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
577 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
579 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
583 if auteur
in self
.ops
:
585 if message
[1] in self
.quiet_channels
:
586 self
.quiet_channels
.remove(message
[1])
587 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
588 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
590 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
591 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
595 if auteur
in self
.overops
and len(message
)>2:
596 serv
.privmsg(message
[1]," ".join(message
[2:]))
597 log(self
.serveur
,"priv",auteur
," ".join(message
))
598 elif len(message
)<=2:
599 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
603 if auteur
in self
.overops
and len(message
)>2:
604 serv
.action(message
[1]," ".join(message
[2:]))
605 log(self
.serveur
,"priv",auteur
," ".join(message
))
606 elif len(message
)<=2:
607 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
611 if auteur
in self
.overops
and len(message
)>2:
612 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
613 log(self
.serveur
,"priv",auteur
," ".join(message
))
614 elif len(message
)<=2:
615 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
619 if auteur
in self
.ops
and len(message
)>1:
620 serv
.privmsg(message
[1],"J'ai perdu !")
621 log(self
.serveur
,"priv",auteur
," ".join(message
))
622 elif len(message
)<=1:
623 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
628 if self
.identities
.has_key(auteur
):
630 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
631 ret
=json
.loads(self
.nk
.read())
632 solde
=ret
["msg"][0]["solde"]
633 pseudo
=ret
["msg"][0]["pseudo"]
634 except Exception as exc
:
636 serv
.privmsg(auteur
,"failed")
637 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
639 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
641 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
642 elif auteur
in self
.ops
:
644 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
645 ret
=json
.loads(self
.nk
.read())
646 solde
=ret
["msg"][0]["solde"]
647 pseudo
=ret
["msg"][0]["pseudo"]
648 except Exception as exc
:
649 serv
.privmsg(auteur
,"failed")
650 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
652 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
656 serv
.privmsg(auteur
,"Je n'ai pas compris. Essayez HELP…")
658 def on_pubmsg(self
, serv
, ev
):
659 auteur
= irclib
.nm_to_n(ev
.source())
661 message
= ev
.arguments()[0]
663 test
=bot_unicode(message
)
664 except UnicodeBotError
:
665 if not canal
in self
.quiet_channels
:
667 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
669 pour_moi
,message
=self
.pourmoi(serv
,message
)
670 if pour_moi
and message
.split()!=[]:
671 cmd
=message
.split()[0].lower()
673 args
=" ".join(message
.split()[1:])
676 if cmd
in ["meurs","die","crève"]:
677 if auteur
in self
.overops
:
678 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
681 serv
.privmsg(canal
,"%s: mourrez vous-même !"%(auteur))
682 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
684 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
685 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
686 or auteur
in self
.overops
):
687 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
688 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
689 if canal
in self
.chanlist
:
690 self
.chanlist
.remove(canal
)
692 serv
.privmsg(canal
,"%s: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(auteur))
693 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
695 elif cmd
in ["reconnect"]:
696 if auteur
in self
.ops
:
698 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
699 config_note_password
,"special")[1]
700 except Exception as exc
:
702 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
704 serv
.privmsg(canal
,"%s: done"%(auteur))
705 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
707 serv
.privmsg(canal
,"%s: failed"%(auteur))
708 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
709 for report
in self
.report_bugs_to
:
710 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
712 serv
.privmsg(canal
,"%s: crève !"%(auteur))
713 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
715 elif cmd
in ["deviens","pseudo"]:
716 if auteur
in self
.ops
:
719 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
721 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
722 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
723 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
724 serv
.privmsg(canal
,"%s: pong"%(auteur))
726 elif cmd
in ["solde","!solde"]:
727 if self
.identities
.has_key(auteur
):
728 pseudo
=self
.identities
[auteur
]
730 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
731 ret
=json
.loads(self
.nk
.read())
732 solde
=ret
["msg"][0]["solde"]
733 pseudo
=ret
["msg"][0]["pseudo"]
734 except Exception as exc
:
735 serv
.privmsg(canal
,"%s: failed"%(auteur))
736 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
738 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
739 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
741 serv
.privmsg(canal
,"%s: Je ne connais pas votre pseudo note."%(auteur))
742 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
743 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
744 and not canal
in self
.quiet_channels
):
745 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
746 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
747 if auteur
in config_manzana
:
748 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
750 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
751 if is_insult(message
) and not canal
in self
.quiet_channels
:
752 if is_not_insult(message
):
753 answer
=random
.choice(config_compliment_answers
)
754 for ligne
in answer
.split("\n"):
755 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
757 answer
=random
.choice(config_insultes_answers
)
758 for ligne
in answer
.split("\n"):
759 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
760 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
761 answer
=random
.choice(config_compliment_answers
)
762 for ligne
in answer
.split("\n"):
763 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
764 gros_match
=is_gros(message
)
765 if gros_match
and not canal
in self
.quiet_channels
:
766 taille
=get_filesize()
767 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
768 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
769 if is_tesla(message
) and not canal
in self
.quiet_channels
:
770 l1
,l2
=config_tesla_answers
,config_tesla_actions
771 n1
,n2
=len(l1
),len(l2
)
772 i
=random
.randrange(n1
+n2
)
774 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
776 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
777 if is_tamere(message
) and not canal
in self
.quiet_channels
:
778 answer
=random
.choice(config_tamere_answers
)
779 for ligne
in answer
.split("\n"):
780 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
781 if is_tag(message
) and not canal
in self
.quiet_channels
:
782 if auteur
in self
.ops
:
783 action
=random
.choice(config_tag_actions
)
784 serv
.action(canal
,action
.encode("utf8"))
785 self
.quiet_channels
.append(canal
)
787 answer
=random
.choice(config_tag_answers
)
788 for ligne
in answer
.split("\n"):
789 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
790 if is_merci(message
):
791 answer
=random
.choice(config_merci_answers
)
792 for ligne
in answer
.split("\n"):
793 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
794 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
795 unicode(message
.upper(),"utf8"))
796 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
797 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
798 if out
and not canal
in self
.quiet_channels
:
802 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
804 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
806 if out
+1>1000 and random
.randrange(4)==0:
807 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
809 except Exception as exc
:
811 if re
.match("[A-Y]",out
):
812 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
813 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
815 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
817 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
818 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
820 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
821 for i
in range(20)}[j
]
823 out
=int(translate(out
))
824 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
825 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
827 answer
=random
.choice(config_night_answers
)
829 answer
=random
.choice(config_bonjour_answers
)
831 answer
=random
.choice(config_bonsoir_answers
)
832 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
833 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
834 answer
=random
.choice(config_bonne_nuit_answers
)
835 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
836 if is_pan(message
) and not canal
in self
.quiet_channels
:
837 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))
839 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
840 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
841 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
842 if auteur
in config_manzana
:
843 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
845 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
846 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
847 failanswers
=config_buffer_fail_answers
848 answer
=random
.choice(failanswers
)
849 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
850 if not canal
in self
.quiet_channels
:
852 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
853 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
854 ).format(mypseudo
).lower(), message
.strip().lower()):
855 answer
=random
.choice(config_bonjour_answers
)
856 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
857 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
858 # proba de perdre sur trigger :
859 # avant 30min (enfin, config) : 0
860 # ensuite, +25%/30min, linéairement
861 deltat
=time
.time()-self
.last_perdu
862 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
863 if random
.uniform(0,1)<barre
:
864 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
865 self
.last_perdu
=time
.time()
867 def on_action(self
, serv
, ev
):
868 action
= ev
.arguments()[0]
869 auteur
= irclib
.nm_to_n(ev
.source())
870 channel
= ev
.target()
872 test
=bot_unicode(action
)
873 except UnicodeBotError
:
874 serv
.privmsg(channel
,
875 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
879 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
880 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
881 n1
,n2
=len(l1
),len(l2
)
882 i
=random
.randrange(n1
+n2
)
884 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
886 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
887 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
888 l1
,l2
=config_good_action_answers
,config_good_action_actions
889 n1
,n2
=len(l1
),len(l2
)
890 i
=random
.randrange(n1
+n2
)
892 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
894 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
896 def on_kick(self
,serv
,ev
):
897 auteur
= irclib
.nm_to_n(ev
.source())
898 channel
= ev
.target()
899 victime
= ev
.arguments()[0]
900 raison
= ev
.arguments()[1]
901 if victime
==self
.nick
:
902 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
905 l1
,l2
=config_kick_answers
,config_kick_actions
906 n1
,n2
=len(l1
),len(l2
)
907 i
=random
.randrange(n1
+n2
)
909 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
911 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
914 return self
.serv
.get_nickname()
915 nick
=property(_getnick
)
918 if __name__
=="__main__":
921 print "Usage : basile.py <serveur> [--debug]"
924 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
928 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
929 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
931 serveur
=serveurs
[serveur
]
933 print "Server Unknown : %s"%(serveur)
935 basile
=Basile(serveur
,debug
)