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"]
41 # config "mais PEB aussi est passé par là"
42 config_manzana_bis
= ["PEB"]
44 # config "tu m'traites ?"
45 config_insultes
=[u
"conna(rd|sse)",u
"pute",u
"con(|ne)",u
"enf(oiré|lure)",
46 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",
47 u
"pétasse",u
"enculé",u
"chagasse",u
"cagole",u
"abruti",u
"ahuri",u
"analphabète",u
"andouille",
48 u
"atardé",u
"avorton",u
"bachibouzouk",u
"(balais|brosse) (de|à) chiotte(|s)",
49 u
"batard",u
"blaireau",u
"bouffon",u
"branque",u
"bouseux",u
"branleur",u
"catin",u
"chacal",
50 u
"charogne",u
"chiant(|e)",u
"chieur",u
"cochon",u
"coprophage",u
"couillon",u
"crapule",u
"crevard",
51 u
"cruche",u
"cuistre",u
"ducon",u
"décérébré",
52 u
"emmerdeur",u
"feignasse",u
"fainéant",u
"fourbe",u
"freluquet",u
"frigide",
53 u
"garce",u
"glandu",u
"gogol",u
"goujat",u
"gourdasse",u
"gredin",u
"gringalet",u
"grognasse",
54 u
"naze",u
"truie",u
"iconoclaste",
55 u
"peigne(-|)cul",u
"ignare",u
"illétré",u
"lèche(|-)cul",u
"malotru",u
"motherfucker",u
"nabot",u
"nigaud",
56 u
"nul",u
"escroc",u
"pouffiasse",u
"pourriture",u
"raclure",u
"relou",u
"sagouin",u
"putain",
58 config_insultes_answers
=[
59 u
"Oh non ! Quelle insulte ! Je crois que je ne m'en relèverai jamais…\nEnfin presque.",
60 u
"J'entends comme un vague murmure, vous disiez ?",
61 u
"Je vais prendre ça pour un compliment.",
62 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…",
63 u
"Permettez-moi de vous retourner le compliment.",
64 u
"Votre indélicatesse vous sied à ravir.",
65 u
"Parfois, je me demande pourquoi je fais encore ce métier…",
66 u
"Le saviez-vous : l'invective ne déshonore que son auteur.",
67 u
"Le saviez-vous : vous perdez plus de temps à m'insulter qu'à vous taire.",
68 u
"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
70 # config "à peine quelques kilos octets"
71 config_gros
=[u
"gros",u
"énorme",u
"lourd"]
72 config_thisfile
= os
.path
.realpath( __file__
)
74 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
76 # config spéciale-iota
77 config_buffer_fail_answers
=[u
"Pas de chance !",u
"Révisez vos classiques !",
78 u
"Encore un effort, je sais que vous pouvez le faire. ;)",
79 u
"Where did you learn to type?"]
81 # config "jeu", d'ailleurs, j'ai perdu.
82 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))"
83 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)"
84 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
85 config_regexp_faire
=u
"fais"
86 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
87 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
88 u
"perdiss(e(|s|nt)|i(ons|ez))",
89 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
91 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
93 u
"trouvant",u
"trouvaille(|s)",
95 u
"victoire(|s)",u
"vaincu(|s|e|es)",
96 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
97 u
"jeu(|x)",u
"game(|s)"]
98 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
99 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
100 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
103 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"]
104 config_tag_actions
=[u
"se tait",u
"se tient coi"]
106 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
107 u
"Dès que cela sera utile.",
108 u
"Une autre fois, peut-être.",
109 u
"Si je me tais, qui vous rappellera combien vous me devez ?",
110 u
"J'aurais aimé accéder à votre requête, mais après mûre réflexion, j'en ai perdu l'envie.",
111 u
"Je ne ressens pas de besoin irrésistible de me taire, navré."]
114 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
115 config_tesla_answers
=[
118 u
"En quoi puis-je me rendre utile ?",
119 u
"On a besoin de moi ?"
121 config_tesla_actions
=[u
"est là",u
"attend des instructions",u
"est toujours disponible"]
123 # config en cas de non-insulte
124 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa",u
"efficace"]
125 config_compliment_answers
=[
126 u
"Merci, c'est gentil de votre part. :)",
127 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",
128 u
"Je vous remercie.",
129 u
"C'est trop d'honneur.",
130 u
"Vous êtes bien aimable."
134 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
135 config_merci_answers
=[u
"Mais de rien.",u
"À votre service. ;)",u
"Quand vous voulez. :)",
136 u
"Tout le plaisir est pour moi."]
139 config_tamere_triggers
=[u
"ta mère"]
140 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
141 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
142 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
143 u
"""N'avait-on pas dit "pas les mamans" ?"""]
145 # config pour les actions désagréables à Basile
146 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
147 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
148 config_bad_action_answers
=[
149 u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
150 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
151 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
152 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public.",
153 u
"{}: Entre nous, cela vous gratifie-t-il ?",
154 u
"{}: Une telle relation entre nous deux n'est pas saine, revenons à quelque chose de plus conventionnel. :D",
155 u
"J'ai la désagréable impression que {} cherche comment tuer le temps en ce moment…"
157 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à ?"]
159 # config pour les actions agréables à Basile
160 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
161 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…"]
162 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"]
164 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
165 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"]
166 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"]
167 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é."]
168 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, {} ?"]
169 config_daytime
= [7,18]
170 config_nighttime
= [3, 6]
173 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nenuit"]
174 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 !"]
176 # config PEB est encore en train d'abuser de ses droits.
177 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 !"]
178 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."]
180 # config on m'a demandé de mourir/partir
181 config_quit_messages
=[u
"Bien que cela me désole, je me vois dans l'obligation de vous abandonner."]
182 config_leave_messages
=config_quit_messages
184 class NKError(Exception):
185 def __init__(self
,msg
):
186 Exception.__init
__(self
)
190 def __unicode__(self
):
191 return unicode(self
.msg
)
193 class NKRefused(NKError
):
196 class NKHelloFailed(NKError
):
199 class NKUnknownError(NKError
):
202 def log(serveur
,channel
,auteur
=None,message
=None):
203 f
=open(get_config_logfile(serveur
),"a")
204 if auteur
==message
==None:
205 # alors c'est que c'est pas un channel mais juste une ligne de log
206 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
208 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
210 if config_debug_stdout
:
217 # On établit la connexion sur port 4242
218 sock
.connect(("127.0.0.1",4242))
220 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
222 sock
.write('hello "Basile"')
223 # On récupère la réponse du hello
226 except Exception as exc
:
227 # Si on a foiré quelque part, c'est que le serveur est down
228 raise NKRefused(str(exc
))
229 if out
["retcode"]==0:
231 elif out
["retcode"]==11:
232 raise NKHelloFailed(out
["errmsg"])
234 raise NKUnknownError(out
["errmsg"])
236 def login_NK(username
,password
,typ
="bdd"):
238 if typ
=="special": # ça c'est pour Basile lui-même
241 masque
='[["all"],["all"],false]'
243 # Basile a un compte special user
244 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
247 except Exception as exc
:
248 # Si on a foiré quelque part, c'est que le serveur est down
249 raise NKRefused(str(exc
))
250 # On vérifie ensuite que le login
251 return json
.loads(out
),sock
254 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
256 chain
=unicode(chain
,"utf8")
258 chain
=unicode(chain
,"utf8").lower()
259 allmatches
="("+"|".join(matches
)+")"
260 reg
=(avant
+allmatches
+apres
).lower()
261 o
=re
.match(reg
,chain
)
264 def is_insult(chain
,debug
=True):
265 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
266 def is_not_insult(chain
):
267 chain
=unicode(chain
,"utf8")
268 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
269 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
270 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
271 if re
.match(reg
,chain
):
275 def is_compliment(chain
,debug
=True):
276 return is_something(chain
,config_compliment_triggers
,avant
=".*(?:^| |')")
278 return is_something(chain
,config_perdu
)
280 return is_something(chain
,config_tag_triggers
)
282 return is_something(chain
,config_gros
)
284 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
286 return is_something(chain
,config_merci_triggers
)
287 def is_tamere(chain
):
288 return is_something(chain
,config_tamere_triggers
)
289 def is_bad_action_trigger(chain
,pseudo
):
290 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
291 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
292 def is_good_action_trigger(chain
,pseudo
):
293 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
294 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
295 def is_bonjour(chain
):
296 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
297 def is_bonne_nuit(chain
):
298 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
300 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
303 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
304 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
306 return is_time(config_daytime
)
308 return is_time(config_nighttime
)
311 class UnicodeBotError(Exception):
313 def bot_unicode(chain
):
315 unicode(chain
,"utf8")
316 except UnicodeDecodeError as exc
:
317 raise UnicodeBotError
319 class Basile(ircbot
.SingleServerIRCBot
):
320 def __init__(self
,serveur
,debug
=False):
321 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
322 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
323 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
326 self
.overops
=config_overops
327 self
.ops
=self
.overops
+config_ops
328 self
.report_bugs_to
=config_report_bugs_to
329 self
.chanlist
=config_chanlist
331 self
.identities
=pickle
.load(open("identities.pickle","r"))
332 self
.stay_channels
=config_stay_channels
333 self
.quiet_channels
=config_quiet_channels
337 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
339 login_result
,sock
=login_NK(username
,password
,typ
)
340 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
341 except NKRefused
as exc
:
342 for report
in self
.report_bugs_to
:
343 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
345 except NKHelloFailed
as exc
:
346 for report
in self
.report_bugs_to
:
348 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
350 except NKUnknownError
as exc
:
351 erreurs
=["Une fucking erreur inconnue s'est produite"]
352 erreurs
+=str(exc
).split("\n")
353 for report
in self
.report_bugs_to
:
355 serv
.privmsg(report
,err
)
357 except Exception as exc
:
358 # Exception qui ne vient pas de la communication avec le serveur NK2015
359 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
366 def give_me_my_pseudo(self
,serv
):
367 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
368 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
370 serv
.nick(config_irc_pseudo
)
372 def on_welcome(self
, serv
, ev
):
373 self
.serv
=serv
# ça serv ira :)
374 self
.give_me_my_pseudo(serv
)
375 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
376 log(self
.serveur
,"Connected")
378 self
.chanlist
=["#bot"]
379 for c
in self
.chanlist
:
380 log(self
.serveur
,"JOIN %s"%(c))
382 # on ouvre la connexion note de Basile, special user
383 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
385 for report
in self
.report_bugs_to
:
386 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
388 def lost(self
,serv
,channel
,forced
=False):
389 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
390 if not channel
in self
.quiet_channels
or forced
:
391 serv
.privmsg(channel
,"J'ai perdu !")
392 self
.last_perdu
=time
.time()
393 delay
=config_time_between_perdu_trigger
394 delta
=config_time_between_perdu_trigger_delta
395 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
397 def pourmoi(self
, serv
, message
):
398 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
401 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
402 return (True,message
[size
+1:].lstrip(" "))
404 return (False,message
)
406 def on_privmsg(self
, serv
, ev
):
407 message
=ev
.arguments()[0]
408 auteur
= irclib
.nm_to_n(ev
.source())
410 test
=bot_unicode(message
)
411 except UnicodeBotError
:
413 "Si je n'avais pas été créé avec la plus grande attention, votre encodage aurait eu raison de moi…")
415 message
=message
.split()
416 cmd
=message
[0].lower()
419 if not len(message
) in [2,3]:
420 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
424 username
=(message
[1])
425 password
=" ".join(message
[2:])
427 password
=" ".join(message
[1:])
428 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
430 self
.sockets
[username
]=sock
431 serv
.privmsg(auteur
,"Connection successful")
432 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
434 serv
.privmsg(auteur
,"Connection failed")
435 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
438 helpdico
={"connect": """CONNECT [<username>] <password>
439 Ouvre une connexion au serveur NoteKfet.
440 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
441 "identify": """IDENTIFY <username> <password>
442 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
443 Sans paramètre, je réponds sous quel pseudo je te connais.""",
444 "drop":"""DROP <password>
445 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
446 helpmsg_default
="""Liste des commandes :
447 HELP Affiche de l'aide sur une commande.
448 CONNECT Ouvre une connection au serveur Note Kfet.
449 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
450 DROP Me fait oublier ton identité.
451 SOLDE Obtenir ton solde"""
453 JOIN Faire rejoindre un chan
454 LEAVE Faire quitter un chan
455 QUIET Se taire sur un chan
456 NOQUIET Opposé de QUIET
457 LOST Perdre sur un chan
458 SOLDE <pseudo> Donner le solde de quelqu'un"""
460 SAY Fait envoyer un message sur un chan ou à une personne
461 DO Fait faire une action sur un chan
462 STAY Ignorera les prochains LEAVE pour un chan
463 NOSTAY Opposé de STAY
466 helpmsg
=helpmsg_default
467 if auteur
in self
.ops
:
469 if auteur
in self
.overops
:
470 helpmsg
+=helpmsg_overops
472 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
473 for ligne
in helpmsg
.split("\n"):
474 serv
.privmsg(auteur
,ligne
)
475 elif cmd
=="identify":
477 if self
.identities
.has_key(auteur
):
478 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
479 self
.identities
[auteur
].encode("utf8")))
481 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
482 elif len(message
)>=3:
483 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
484 success
,_
=self
.new_connection_NK(serv
,username
,password
)
486 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
487 serv
.privmsg(auteur
,"Identité enregistrée.")
488 self
.identities
[auteur
]=username
489 pickle
.dump(self
.identities
,open("identities.pickle","w"))
491 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
492 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
494 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
497 if self
.identities
.has_key(auteur
):
498 password
=" ".join(message
[1:])
499 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
501 del self
.identities
[auteur
]
502 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
503 pickle
.dump(self
.identities
,open("identities.pickle","w"))
504 serv
.privmsg(auteur
,"Identité oubliée.")
506 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
507 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
509 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
511 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
513 if auteur
in self
.ops
:
515 if message
[1] in self
.chanlist
:
516 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
518 serv
.join(message
[1])
519 self
.chanlist
.append(message
[1])
520 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
521 log(self
.serveur
,"priv",auteur
," ".join(message
))
523 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
527 if auteur
in self
.ops
and len(message
)>1:
528 if message
[1] in self
.chanlist
:
529 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
530 self
.quitter(message
[1]," ".join(message
[2:]))
531 self
.chanlist
.remove(message
[1])
532 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
534 serv
.privmsg(auteur
,"Non, je reste !")
535 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
537 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
541 if auteur
in self
.overops
:
543 if message
[1] in self
.stay_channels
:
544 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
545 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
547 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
548 self
.stay_channels
.append(message
[1])
549 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
551 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
555 if auteur
in self
.overops
:
557 if message
[1] in self
.stay_channels
:
558 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
559 self
.stay_channels
.remove(message
[1])
560 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
562 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
563 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
568 if auteur
in self
.overops
:
569 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
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 aurait eu raison de moi…"%(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
):
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))
754 elif auteur
in config_manzana_bis
:
755 serv
.action(canal
,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
757 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
758 if is_insult(message
) and not canal
in self
.quiet_channels
:
759 if is_not_insult(message
):
760 answer
=random
.choice(config_compliment_answers
)
761 for ligne
in answer
.split("\n"):
762 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
764 answer
=random
.choice(config_insultes_answers
)
765 for ligne
in answer
.split("\n"):
766 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
767 elif is_compliment(message
) and not canal
in self
.quiet_channels
:
768 answer
=random
.choice(config_compliment_answers
)
769 for ligne
in answer
.split("\n"):
770 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
771 gros_match
=is_gros(message
)
772 if gros_match
and not canal
in self
.quiet_channels
:
773 taille
=get_filesize()
774 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
775 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
776 if is_tesla(message
) and not canal
in self
.quiet_channels
:
777 l1
,l2
=config_tesla_answers
,config_tesla_actions
778 n1
,n2
=len(l1
),len(l2
)
779 i
=random
.randrange(n1
+n2
)
781 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
783 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
784 if is_tamere(message
) and not canal
in self
.quiet_channels
:
785 answer
=random
.choice(config_tamere_answers
)
786 for ligne
in answer
.split("\n"):
787 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
788 if is_tag(message
) and not canal
in self
.quiet_channels
:
789 if auteur
in self
.ops
:
790 action
=random
.choice(config_tag_actions
)
791 serv
.action(canal
,action
.encode("utf8"))
792 self
.quiet_channels
.append(canal
)
794 answer
=random
.choice(config_tag_answers
)
795 for ligne
in answer
.split("\n"):
796 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
797 if is_merci(message
):
798 answer
=random
.choice(config_merci_answers
)
799 for ligne
in answer
.split("\n"):
800 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
801 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
802 unicode(message
.upper(),"utf8"))
803 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
804 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
805 if out
and not canal
in self
.quiet_channels
:
809 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
811 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
813 if out
+1>1000 and random
.randrange(4)==0:
814 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
816 except Exception as exc
:
818 if re
.match("[A-Y]",out
):
819 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
820 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
822 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
824 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
825 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
827 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
828 for i
in range(20)}[j
]
830 out
=int(translate(out
))
831 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
832 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
834 answer
=random
.choice(config_night_answers
)
836 answer
=random
.choice(config_bonjour_answers
)
838 answer
=random
.choice(config_bonsoir_answers
)
839 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
840 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
841 answer
=random
.choice(config_bonne_nuit_answers
)
842 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
843 if is_pan(message
) and not canal
in self
.quiet_channels
:
844 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))
846 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
847 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
848 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
849 if auteur
in config_manzana
:
850 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
851 elif auteur
in config_manzana_bis
:
852 serv
.action(canal
,"sert un grand verre de jus de pomme à %s : tout le monde sait qu'il ne boit pas."%(auteur))
854 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
855 if re
.match(u
'^ *(.|§|!|/|/|:|)(w|b) [0-9]+$',message
.decode("utf8")) and not canal
in self
.quiet_channels
:
856 failanswers
=config_buffer_fail_answers
857 answer
=random
.choice(failanswers
)
858 serv
.privmsg(canal
,("%s: %s"%(auteur
,answer
)).encode("utf8"))
859 if not canal
in self
.quiet_channels
:
861 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
862 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
863 ).format(mypseudo
).lower(), message
.strip().lower()):
864 answer
=random
.choice(config_bonjour_answers
)
865 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
866 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
867 # proba de perdre sur trigger :
868 # avant 30min (enfin, config) : 0
869 # ensuite, +25%/30min, linéairement
870 deltat
=time
.time()-self
.last_perdu
871 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
872 if random
.uniform(0,1)<barre
:
873 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
874 self
.last_perdu
=time
.time()
876 def on_action(self
, serv
, ev
):
877 action
= ev
.arguments()[0]
878 auteur
= irclib
.nm_to_n(ev
.source())
879 channel
= ev
.target()
881 test
=bot_unicode(action
)
882 except UnicodeBotError
:
883 serv
.privmsg(channel
,
884 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
888 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
889 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
890 n1
,n2
=len(l1
),len(l2
)
891 i
=random
.randrange(n1
+n2
)
893 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
895 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
896 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
897 l1
,l2
=config_good_action_answers
,config_good_action_actions
898 n1
,n2
=len(l1
),len(l2
)
899 i
=random
.randrange(n1
+n2
)
901 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
903 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
905 def on_kick(self
,serv
,ev
):
906 auteur
= irclib
.nm_to_n(ev
.source())
907 channel
= ev
.target()
908 victime
= ev
.arguments()[0]
909 raison
= ev
.arguments()[1]
910 if victime
==self
.nick
:
911 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
914 l1
,l2
=config_kick_answers
,config_kick_actions
915 n1
,n2
=len(l1
),len(l2
)
916 i
=random
.randrange(n1
+n2
)
918 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
920 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
922 def quitter(self
,chan
,leave_message
=None):
923 if leave_message
==None:
924 leave_message
=random
.choice(config_leave_messages
)
925 self
.serv
.part(chan
,message
=leave_message
.encode("utf8"))
928 quit_message
=random
.choice(config_quit_messages
)
929 self
.die(msg
=quit_message
.encode("utf8"))
932 return self
.serv
.get_nickname()
933 nick
=property(_getnick
)
936 if __name__
=="__main__":
939 print "Usage : basile.py <serveur> [--debug]"
942 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
946 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
947 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
949 serveur
=serveurs
[serveur
]
951 print "Server Unknown : %s"%(serveur)
953 basile
=Basile(serveur
,debug
)