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-Pste"]
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
"Mais je ne vous permets pas ! Enfin, pas comme ça…"]
66 # config "à peine quelques kilos octets"
67 config_gros
=[u
"gros",u
"énorme",u
"lourd"]
69 # config spéciale-iota
70 config_buffer_fail_answers
=["Pas de chance !","Révisez vos classiques !","Encore un effort, je sais que vous pouvez le faire. ;)"]
72 # config "jeu", d'ailleurs, j'ai perdu.
73 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))"
74 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)"
75 config_regexp_etre_avec_c
=u
"c'(e(s|st)|étai(t|ent))"
76 config_regexp_faire
=u
"fais"
77 config_perdu
=[u
"perd(|s|ons|ez|ent|r(e|ai|as|a|ons|ez|ont)|(|r)(ais|ait|ions|iez|aient))"
78 u
"perd(i(s|t|rent)|î(mes|tes|t))", # oui, j'ai inclus qu'il perdît
79 u
"perdiss(e(|s|nt)|i(ons|ez))",
80 u
"perdu(|s|e|es)",u
"perdant(|s|e|es)",u
"perte(|s)",
82 u
"(gagn|trouv)"+config_premier_groupe_terminaisons
,u
"gagnant(|s|e|es)",u
"gain(|s)",
84 u
"trouvant",u
"trouvaille(|s)",
86 u
"victoire(|s)",u
"vaincu(|s|e|es)",
87 u
"loose",u
"lost",u
"looser(|s)",u
"win(|ner)(|s)",
88 u
"jeu(|x)",u
"game(|s)"]
89 config_time_between_perdu_trigger
=3600*3 #temps moyen pour perdre en l'absence de trigger
90 config_time_between_perdu_trigger_delta
= 30*60 #marge autorisée autour de ^^^
91 config_time_between_perdu
=30*60 #temps pendant lequel on ne peut pas perdre
94 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"]
95 config_tag_actions
=[u
"se tait",u
"se tient coi"]
97 u
"Ç'aurait été avec plaisir, mais je ne crois pas que vous puissiez vous passer de mes services.",
98 u
"Dès que cela sera utile.",
99 u
"Une autre fois, peut-être"]
102 config_tesla_triggers
=[u
"t('|u )es là \?",u
"\?",u
"plop \?",u
"plouf \?"]
103 config_tesla_answers
=[u
"Oui, je suis là.",u
"J'écoute.",u
"En quoi puis-je me rendre utile ?"]
104 config_tesla_actions
=[u
"est là",u
"attend des instructions"]
106 # config en cas de non-insulte
107 config_compliment_triggers
=[u
"gentil",u
"cool",u
"sympa"]
108 config_compliment_answers
=[u
"Merci, c'est gentil de votre part. :)",
109 u
"Permettez-moi de vous retourner le compliment, sans ironie cette fois.",u
"Je vous remercie."]
112 config_merci_triggers
=[u
"merci",u
"remercie",u
"thx",u
"thank(|s)"]
113 config_merci_answers
=[u
"Mais de rien.",u
"À votre service ;)",u
"Quand vous voulez ^^",
114 u
"Tout le plaisir est pour moi."]
117 config_tamere_triggers
=[u
"ta mère"]
118 config_tamere_answers
=[u
"Laissez donc ma mère en dehors de ça !",
119 u
"Puis-je préciser que je n'ai pas de mère ? Seulement deux pères…",
120 u
"""Un certain Max chantait "♩ J'ai vu ta mère sur chat rouleeeeeeette ♫", vous êtes de sa famille ?""",
121 u
"""N'avait-on pas dit "pas les mamans" ?"""]
123 # config pour les actions désagréables à Basile
124 config_bad_action_triggers
=[u
"(frappe|cogne|tape)(| sur)",u
"(démolit|dégomme|fouette|agresse|tabasse)",
125 u
"(vomit|pisse|chie|crache) sur",u
"slap(|s)"]
126 config_bad_action_answers
=[u
"Je ne peux pas dire que j'apprécie, mais je l'ai sans doute bien mérité.",
127 u
"{}: Pourquoi tant de violence en ce monde si doux ?",
128 u
"""Si je n'étais pas aussi prude, je dirais "Mais euh…", cependant, je me contenterai de hausser un sourcil.""",
129 u
"{}: J'aurais préféré que vous ne fassiez pas cela en public."]
130 config_bad_action_actions
=[u
"prend de la distance, par précaution…",u
"esquive",u
"est bon pour prendre une semaine de repos… virtuel !"]
132 # config pour les actions agréables à Basile
133 config_good_action_triggers
=[u
"fait (:?des bisous|un c(?:â|a)lin|des c(?:â|a)lins) à",u
"embrasse",u
"c(?:â|a)line",u
"caresse"]
134 config_good_action_answers
=[u
":D",u
"{}: Moi aussi je vous aime. ♡"]
135 config_good_action_actions
=[u
"ronronne",u
"aimerait exprimer avec des mots simples le bonheur que {} lui procure !"]
137 # config bonjour/bonsoir/que fais-tu encore debout à cette heure, gros sale !
138 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"]
139 config_bonjour_answers
=[u
"Bien le bonjour, {}.",u
"Bonjour {}.",u
"{}: bonjour."]
140 config_bonsoir_answers
=[u
"Bonsoir {} !",u
"{}: bonsoir."]
141 config_night_answers
=[u
"{}: vous m'avez fait peur, je m'étais assoupi !", u
"Debout à une heure pareille, {} ? Que vous arrive-t-il ?"]
142 config_daytime
= [7,18]
143 config_nighttime
= [3, 6]
146 config_bonne_nuit_triggers
=[u
"bonne nuit",u
"'?nite",u
"'?nuit",u
"'?night",u
"good night",u
"'?nunuit"]
147 config_bonne_nuit_answers
=[u
"{}: thanks, make sweet dreams tonight ! ;)",u
"Bonne nuit {}.",u
"À demain {}. :)"]
149 # config PEB est encore en train d'abuser de ses droits.
150 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."]
151 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 {}"]
153 config_thisfile
= os
.path
.realpath( __file__
)
155 return ex("ls -s %s"%(config_thisfile))[1].split()[0]
157 class NKError(Exception):
158 def __init__(self
,msg
):
159 Exception.__init
__(self
)
163 def __unicode__(self
):
164 return unicode(self
.msg
)
166 class NKRefused(NKError
):
169 class NKHelloFailed(NKError
):
172 class NKUnknownError(NKError
):
175 def log(serveur
,channel
,auteur
=None,message
=None):
176 f
=open(get_config_logfile(serveur
),"a")
177 if auteur
==message
==None:
178 # alors c'est que c'est pas un channel mais juste une ligne de log
179 chain
="%s %s"%(time
.strftime("%F %T"),channel
)
181 chain
="%s [%s:%s] %s"%(time
.strftime("%F %T"),channel
,auteur
,message
)
183 if config_debug_stdout
:
190 # On établit la connexion sur port 4242
191 sock
.connect(("127.0.0.1",4242))
193 sock
=ssl
.wrap_socket(sock
,ca_certs
='../keys/ca_.crt')
195 sock
.write('hello "Basile"')
196 # On récupère la réponse du hello
199 except Exception as exc
:
200 # Si on a foiré quelque part, c'est que le serveur est down
201 raise NKRefused(str(exc
))
202 if out
["retcode"]==0:
204 elif out
["retcode"]==11:
205 raise NKHelloFailed(out
["errmsg"])
207 raise NKUnknownError(out
["errmsg"])
209 def login_NK(username
,password
,typ
="bdd"):
211 if typ
=="special": # ça c'est pour Basile lui-même
214 masque
='[["all"],["all"],false]'
216 # Basile a un compte special user
217 commande
='login [%s,%s,"%s",%s]'%(json
.dumps(username
),json
.dumps(password
),typ
,masque
)
220 except Exception as exc
:
221 # Si on a foiré quelque part, c'est que le serveur est down
222 raise NKRefused(str(exc
))
223 # On vérifie ensuite que le login
224 return json
.loads(out
),sock
227 def is_something(chain
,matches
,avant
=u
".*(?:^| )",apres
=u
"(?:$|\.| |,|;).*",case_sensitive
=False,debug
=False):
229 chain
=unicode(chain
,"utf8")
231 chain
=unicode(chain
,"utf8").lower()
232 allmatches
="("+"|".join(matches
)+")"
233 reg
=(avant
+allmatches
+apres
).lower()
234 o
=re
.match(reg
,chain
)
237 def is_insult(chain
,debug
=True):
238 return is_something(chain
,config_insultes
,avant
=".*(?:^| |')")
239 def is_not_insult(chain
):
240 chain
=unicode(chain
,"utf8")
241 insult_regexp
=u
"("+u
"|".join(config_insultes
)+u
")"
242 middle_regexp
=u
"(une? (?:(?:putain|enfoiré) d(?:e |'))*|)(?:| super )(?: (?:gros|petit|grand|énorme) |)"
243 reg
=".*pas %s%s.*"%(middle_regexp
,insult_regexp
)
244 if re
.match(reg
,chain
):
249 return is_something(chain
,config_perdu
)
251 return is_something(chain
,config_tag_triggers
)
253 return is_something(chain
,config_gros
)
255 return is_something(chain
,config_tesla_triggers
,avant
=u
"^",apres
=u
"$",debug
=True)
257 return is_something(chain
,config_merci_triggers
)
258 def is_tamere(chain
):
259 return is_something(chain
,config_tamere_triggers
)
260 def is_bad_action_trigger(chain
,pseudo
):
261 return is_something(chain
,config_bad_action_triggers
,avant
=u
"^",
262 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
263 def is_good_action_trigger(chain
,pseudo
):
264 return is_something(chain
,config_good_action_triggers
,avant
=u
"^",
265 apres
="(?: [a-z]*ment)? %s($|\.| |,|;).*"%(pseudo))
266 def is_bonjour(chain
):
267 return is_something(chain
,config_bonjour_triggers
,avant
=u
"^")
268 def is_bonne_nuit(chain
):
269 return is_something(chain
,config_bonne_nuit_triggers
,avant
=u
"^")
271 return re
.match(u
"^(pan|bim|bang)( .*)?$",unicode(chain
,"utf8").lower().strip())
274 _
,_
,_
,h
,m
,s
,_
,_
,_
=time
.localtime()
275 return (conf
[0],0,0)<(h
,m
,s
)<(conf
[1],0,0)
277 return is_time(config_daytime
)
279 return is_time(config_nighttime
)
282 class UnicodeBotError(Exception):
284 def bot_unicode(chain
):
286 unicode(chain
,"utf8")
287 except UnicodeDecodeError as exc
:
288 raise UnicodeBotError
290 class Basile(ircbot
.SingleServerIRCBot
):
291 def __init__(self
,serveur
,debug
=False):
292 temporary_pseudo
=config_irc_pseudo
+str(random
.randrange(10000,100000))
293 ircbot
.SingleServerIRCBot
.__init
__(self
, [(serveur
, 6667)],
294 temporary_pseudo
,"Basile, le bot irc.[Codé par 20-100, fouettez-le]", 10)
297 self
.overops
=config_overops
298 self
.ops
=self
.overops
+config_ops
299 self
.report_bugs_to
=config_report_bugs_to
300 self
.chanlist
=config_chanlist
302 self
.identities
=pickle
.load(open("identities.pickle","r"))
303 self
.stay_channels
=config_stay_channels
304 self
.quiet_channels
=config_quiet_channels
308 def new_connection_NK(self
,serv
,username
,password
,typ
="bdd"):
310 login_result
,sock
=login_NK(username
,password
,typ
)
311 droits
,retcode
,errmsg
=login_result
["msg"],login_result
["retcode"],login_result
["errmsg"]
312 except NKRefused
as exc
:
313 for report
in self
.report_bugs_to
:
314 serv
.privmsg(report
,"Le Serveur NK2015 est down.")
316 except NKHelloFailed
as exc
:
317 for report
in self
.report_bugs_to
:
319 "La version du site utilisée n'est pas supportée par le serveur NK2015.")
321 except NKUnknownError
as exc
:
322 erreurs
=["Une fucking erreur inconnue s'est produite"]
323 erreurs
+=str(exc
).split("\n")
324 for report
in self
.report_bugs_to
:
326 serv
.privmsg(report
,err
)
328 except Exception as exc
:
329 # Exception qui ne vient pas de la communication avec le serveur NK2015
330 log(self
.serveur
,"Erreur dans new_connection_NK\n"+str(exc
))
337 def give_me_my_pseudo(self
,serv
):
338 serv
.privmsg("NickServ","RECOVER %s %s"%(config_irc_pseudo
,config_irc_password
))
339 serv
.privmsg("NickServ","RELEASE %s %s"%(config_irc_pseudo
,config_irc_password
))
341 serv
.nick(config_irc_pseudo
)
343 def on_welcome(self
, serv
, ev
):
344 self
.serv
=serv
# ça serv ira :)
345 self
.give_me_my_pseudo(serv
)
346 serv
.privmsg("NickServ","identify %s"%(config_irc_password))
347 log(self
.serveur
,"Connected")
349 self
.chanlist
=["#bot"]
350 for c
in self
.chanlist
:
351 log(self
.serveur
,"JOIN %s"%(c))
353 # on ouvre la connexion note de Basile, special user
354 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,config_note_password
,"special")[1]
356 for report
in self
.report_bugs_to
:
357 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
359 def lost(self
,serv
,channel
,forced
=False):
360 if self
.last_perdu
+config_time_between_perdu
<time
.time() or forced
:
361 if not channel
in self
.quiet_channels
or forced
:
362 serv
.privmsg(channel
,"J'ai perdu !")
363 self
.last_perdu
=time
.time()
364 delay
=config_time_between_perdu_trigger
365 delta
=config_time_between_perdu_trigger_delta
366 serv
.execute_delayed(random
.randrange(delay
-delta
,delay
+delta
),self
.lost
,(serv
,channel
))
368 def pourmoi(self
, serv
, message
):
369 """renvoie (False,lemessage) ou (True, le message amputé de "pseudo: ")"""
372 if message
[:size
]==pseudo
and len(message
)>size
and message
[size
]==":":
373 return (True,message
[size
+1:].lstrip(" "))
375 return (False,message
)
377 def on_privmsg(self
, serv
, ev
):
378 message
=ev
.arguments()[0]
379 auteur
= irclib
.nm_to_n(ev
.source())
381 test
=bot_unicode(message
)
382 except UnicodeBotError
:
384 "Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…")
386 message
=message
.split()
387 cmd
=message
[0].lower()
390 if not len(message
) in [2,3]:
391 serv
.privmsg(auteur
,"Syntaxe : CONNECT [<username>] <password>")
395 username
=(message
[1])
396 password
=" ".join(message
[2:])
398 password
=" ".join(message
[1:])
399 success
,sock
=self
.new_connection_NK(serv
,username
,password
)
401 self
.sockets
[username
]=sock
402 serv
.privmsg(auteur
,"Connection successful")
403 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
405 serv
.privmsg(auteur
,"Connection failed")
406 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
409 helpdico
={"connect": """CONNECT [<username>] <password>
410 Ouvre une connexion au serveur NoteKfet.
411 Si <username> n'est pas précisé, j'utiliserais l'identité sous laquelle je te connais, ou, à défaut, ton pseudo.""",
412 "identify": """IDENTIFY <username> <password>
413 Vérifie le mot de passe et me permet de savoir à l'avenir quel est ton pseudo note kfet.
414 Sans paramètre, je réponds sous quel pseudo je te connais.""",
415 "drop":"""DROP <password>
416 Vérifie le mot de passe et me fait d'oublier ton pseudo note kfet."""}
417 helpmsg_default
="""Liste des commandes :
418 HELP Affiche de l'aide sur une commande.
419 CONNECT Ouvre une connection au serveur Note Kfet.
420 IDENTIFY Me permet de savoir qui tu es sur la note kfet.
421 DROP Me fait oublier ton identité.
422 SOLDE Obtenir ton solde"""
424 JOIN Faire rejoindre un chan
425 LEAVE Faire quitter un chan
426 QUIET Se taire sur un chan
427 NOQUIET Opposé de QUIET
428 LOST Perdre sur un chan
429 SOLDE <pseudo> Donner le solde de quelqu'un"""
431 SAY Fait envoyer un message sur un chan ou à une personne
432 DO Fait faire une action sur un chan
433 STAY Ignorera les prochains LEAVE pour un chan
434 NOSTAY Opposé de STAY
437 helpmsg
=helpmsg_default
438 if auteur
in self
.ops
:
440 if auteur
in self
.overops
:
441 helpmsg
+=helpmsg_overops
443 helpmsg
=helpdico
.get(message
[1].lower(),"Commande inconnue.")
444 for ligne
in helpmsg
.split("\n"):
445 serv
.privmsg(auteur
,ligne
)
446 elif cmd
=="identify":
448 if self
.identities
.has_key(auteur
):
449 serv
.privmsg(auteur
,"Je te connais sous le pseudo note %s."%(
450 self
.identities
[auteur
].encode("utf8")))
452 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
453 elif len(message
)>=3:
454 username
,password
=message
[1],unicode(" ".join(message
[2:]),"utf8")
455 success
,_
=self
.new_connection_NK(serv
,username
,password
)
457 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
458 serv
.privmsg(auteur
,"Identité enregistrée.")
459 self
.identities
[auteur
]=username
460 pickle
.dump(self
.identities
,open("identities.pickle","w"))
462 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
463 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
465 serv
.privmsg(auteur
,u
"Syntaxe : IDENTIFY [<username> <password>]")
468 if self
.identities
.has_key(auteur
):
469 password
=" ".join(message
[1:])
470 success
,_
=self
.new_connection_NK(serv
,self
.identities
[auteur
],password
)
472 del self
.identities
[auteur
]
473 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
474 pickle
.dump(self
.identities
,open("identities.pickle","w"))
475 serv
.privmsg(auteur
,"Identité oubliée.")
477 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
478 serv
.privmsg(auteur
,"Mot de passe invalide. (ou serveur down)")
480 serv
.privmsg(auteur
,"Je ne connais pas ton pseudo note.")
482 serv
.privmsg(auteur
,"Syntaxe : DROP <password>")
484 if auteur
in self
.ops
:
486 if message
[1] in self
.chanlist
:
487 serv
.privmsg(auteur
,"Je suis déjà sur %s"%(message
[1]))
489 serv
.join(message
[1])
490 self
.chanlist
.append(message
[1])
491 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
492 log(self
.serveur
,"priv",auteur
," ".join(message
))
494 serv
.privmsg(auteur
,"Channels : "+" ".join(self
.chanlist
))
498 if auteur
in self
.ops
and len(message
)>1:
499 if message
[1] in self
.chanlist
:
500 if not (message
[1] in self
.stay_channels
) or auteur
in self
.overops
:
501 serv
.part(message
[1])
502 self
.chanlist
.remove(message
[1])
503 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
505 serv
.privmsg(auteur
,"Non, je reste !")
506 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
508 serv
.privmsg(auteur
,"Je ne suis pas sur %s"%(message
[1]))
512 if auteur
in self
.overops
:
514 if message
[1] in self
.stay_channels
:
515 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
516 serv
.privmsg(auteur
,"Je stay déjà sur %s."%(message
[1]))
518 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
519 self
.stay_channels
.append(message
[1])
520 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
522 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
526 if auteur
in self
.overops
:
528 if message
[1] in self
.stay_channels
:
529 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
530 self
.stay_channels
.remove(message
[1])
531 serv
.privmsg(auteur
,"Stay channels : "+" ".join(self
.stay_channels
))
533 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
534 serv
.privmsg(auteur
,"Je ne stay pas sur %s."%(message
[1]))
539 if auteur
in self
.overops
:
540 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
545 if auteur
in self
.ops
:
547 if message
[1] in self
.quiet_channels
:
548 serv
.privmsg(auteur
,"Je me la ferme déjà sur %s"%(message
[1]))
549 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
551 self
.quiet_channels
.append(message
[1])
552 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
553 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
555 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
559 if auteur
in self
.ops
:
561 if message
[1] in self
.quiet_channels
:
562 self
.quiet_channels
.remove(message
[1])
563 serv
.privmsg(auteur
,"Quiet channels : "+" ".join(self
.quiet_channels
))
564 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[successful]")
566 serv
.privmsg(auteur
,"Je ne me la ferme pas sur %s."%(message
[1]))
567 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
571 if auteur
in self
.overops
and len(message
)>2:
572 serv
.privmsg(message
[1]," ".join(message
[2:]))
573 log(self
.serveur
,"priv",auteur
," ".join(message
))
574 elif len(message
)<=2:
575 serv
.privmsg(auteur
,"Syntaxe : SAY <channel> <message>")
579 if auteur
in self
.overops
and len(message
)>2:
580 serv
.action(message
[1]," ".join(message
[2:]))
581 log(self
.serveur
,"priv",auteur
," ".join(message
))
582 elif len(message
)<=2:
583 serv
.privmsg(auteur
,"Syntaxe : DO <channel> <action>")
587 if auteur
in self
.overops
and len(message
)>2:
588 serv
.kick(message
[1],message
[2]," ".join(message
[3:]))
589 log(self
.serveur
,"priv",auteur
," ".join(message
))
590 elif len(message
)<=2:
591 serv
.privmsg(auteur
,"Syntaxe : KICK <channel> <pseudo>")
595 if auteur
in self
.ops
and len(message
)>1:
596 serv
.privmsg(message
[1],"J'ai perdu !")
597 log(self
.serveur
,"priv",auteur
," ".join(message
))
598 elif len(message
)<=1:
599 serv
.privmsg(auteur
,"Syntaxe : LOST <channel>")
604 if self
.identities
.has_key(auteur
):
606 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(self
.identities
[auteur
])))
607 ret
=json
.loads(self
.nk
.read())
608 solde
=ret
["msg"][0]["solde"]
609 pseudo
=ret
["msg"][0]["pseudo"]
610 except Exception as exc
:
612 serv
.privmsg(auteur
,"failed")
613 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
615 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
617 serv
.privmsg(canal
,"Je ne connais pas ton pseudo note.")
618 elif auteur
in self
.ops
:
620 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(message
[1])))
621 ret
=json
.loads(self
.nk
.read())
622 solde
=ret
["msg"][0]["solde"]
623 pseudo
=ret
["msg"][0]["pseudo"]
624 except Exception as exc
:
625 serv
.privmsg(auteur
,"failed")
626 log(self
.serveur
,"priv",auteur
," ".join(message
)+"[failed]")
628 serv
.privmsg(auteur
,"%s (%s)"%(float(solde
)/100,pseudo
.encode("utf8")))
632 serv
.privmsg(auteur
,"Je n'ai pas compris. Essayez HELP…")
634 def on_pubmsg(self
, serv
, ev
):
635 auteur
= irclib
.nm_to_n(ev
.source())
637 message
= ev
.arguments()[0]
639 test
=bot_unicode(message
)
640 except UnicodeBotError
:
641 if not canal
in self
.quiet_channels
:
643 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
645 pour_moi
,message
=self
.pourmoi(serv
,message
)
646 if pour_moi
and message
.split()!=[]:
647 cmd
=message
.split()[0].lower()
649 args
=" ".join(message
.split()[1:])
652 if cmd
in ["meurs","die","crève"]:
653 if auteur
in self
.overops
:
654 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
657 serv
.privmsg(canal
,"%s: mourrez vous-même !"%(auteur))
658 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
660 elif cmd
in ["part","leave","dégage","va-t-en","tut'tiresailleurs,c'estmesgalets"]:
661 if auteur
in self
.ops
and (not (canal
in self
.stay_channels
)
662 or auteur
in self
.overops
):
663 serv
.part(canal
,message
="Éjecté par %s"%(auteur))
664 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
665 if canal
in self
.chanlist
:
666 self
.chanlist
.remove(canal
)
668 serv
.privmsg(canal
,"%s: Navré, mais je me vois contraint de refuser, je ne peux pas céder aux exigences du premier venu."%(auteur))
669 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
671 elif cmd
in ["reconnect"]:
672 if auteur
in self
.ops
:
674 self
.nk
=self
.new_connection_NK(serv
,config_note_pseudo
,
675 config_note_password
,"special")[1]
676 except Exception as exc
:
678 log(self
.serveur
,"""Erreur dans on_pubmsg/"cmd in ["reconnect"]\n"""+str(exc
))
680 serv
.privmsg(canal
,"%s: done"%(auteur))
681 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
683 serv
.privmsg(canal
,"%s: failed"%(auteur))
684 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
685 for report
in self
.report_bugs_to
:
686 serv
.privmsg(report
,"Connection to NK2015 failed, invalid password ?")
688 serv
.privmsg(canal
,"%s: crève !"%(auteur))
689 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
691 elif cmd
in ["deviens","pseudo"]:
692 if auteur
in self
.ops
:
695 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
697 if cmd
in ["meur", "meurt","meurre","meurres"] and not canal
in self
.quiet_channels
:
698 serv
.privmsg(canal
,'%s: Mourir, impératif, 2ème personne du singulier : "meurs" (de rien)'%(auteur))
699 elif cmd
in ["ping"] and not canal
in self
.quiet_channels
:
700 serv
.privmsg(canal
,"%s: pong"%(auteur))
702 elif cmd
in ["solde","!solde"]:
703 if self
.identities
.has_key(auteur
):
704 pseudo
=self
.identities
[auteur
]
706 self
.nk
.write('search ["x",["pseudo"],%s]'%(json
.dumps(pseudo
)))
707 ret
=json
.loads(self
.nk
.read())
708 solde
=ret
["msg"][0]["solde"]
709 pseudo
=ret
["msg"][0]["pseudo"]
710 except Exception as exc
:
711 serv
.privmsg(canal
,"%s: failed"%(auteur))
712 log(self
.serveur
,canal
,auteur
,message
+"[failed]")
714 serv
.privmsg(canal
,"%s: %s (%s)"%(auteur
,float(solde
)/100,pseudo
.encode("utf8")))
715 log(self
.serveur
,canal
,auteur
,message
+"[successful]")
717 serv
.privmsg(canal
,"%s: Je ne connais pas votre pseudo note."%(auteur))
718 log(self
.serveur
,canal
,auteur
,message
+"[unknown]")
719 elif (re
.match("!?(pain au chocolat|chocolatine)",message
.lower())
720 and not canal
in self
.quiet_channels
):
721 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
722 elif re
.match("!?manzana",message
.lower()) and not canal
in self
.quiet_channels
:
723 if auteur
in config_manzana
:
724 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
726 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
727 if is_insult(message
) and not canal
in self
.quiet_channels
:
728 if is_not_insult(message
):
729 answer
=random
.choice(config_compliment_answers
)
730 for ligne
in answer
.split("\n"):
731 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
733 answer
=random
.choice(config_insultes_answers
)
734 for ligne
in answer
.split("\n"):
735 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
736 gros_match
=is_gros(message
)
737 if gros_match
and not canal
in self
.quiet_channels
:
738 taille
=get_filesize()
739 answer
=u
"Mais non, je ne suis pas %s, %sKo tout au plus…"%(gros_match
.groups()[0],taille
)
740 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
.encode("utf8")))
741 if is_tesla(message
) and not canal
in self
.quiet_channels
:
742 l1
,l2
=config_tesla_answers
,config_tesla_actions
743 n1
,n2
=len(l1
),len(l2
)
744 i
=random
.randrange(n1
+n2
)
746 serv
.action(canal
,l2
[i
-n1
].encode("utf8"))
748 serv
.privmsg(canal
,"%s: %s"%(auteur
,l1
[i
].encode("utf8")))
749 if is_tamere(message
) and not canal
in self
.quiet_channels
:
750 answer
=random
.choice(config_tamere_answers
)
751 for ligne
in answer
.split("\n"):
752 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
753 if is_tag(message
) and not canal
in self
.quiet_channels
:
754 if auteur
in self
.ops
:
755 action
=random
.choice(config_tag_actions
)
756 serv
.action(canal
,action
.encode("utf8"))
757 self
.quiet_channels
.append(canal
)
759 answer
=random
.choice(config_tag_answers
)
760 for ligne
in answer
.split("\n"):
761 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
762 if is_merci(message
):
763 answer
=random
.choice(config_merci_answers
)
764 for ligne
in answer
.split("\n"):
765 serv
.privmsg(canal
,"%s: %s"%(auteur
,ligne
.encode("utf8")))
766 out
=re
.match(ur
"^([A-Z[]|\\|[0-9]+|(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+)(?:| \?| !)$",
767 unicode(message
.upper(),"utf8"))
768 if re
.match("ma bite dans ton oreille",message
) and not canal
in self
.quiet_channels
:
769 serv
.privmsg(canal
,"%s: Seul un olasd peut imiter un olasd dans un de ses grands jours !"%(auteur))
770 if out
and not canal
in self
.quiet_channels
:
774 serv
.privmsg(canal
,"%s: %s !"%(auteur
,out
+1))
776 serv
.privmsg(canal
,"%s: Ciel, un maxint ! Heureusement que je suis en python…"%(auteur))
778 if out
+1>1000 and random
.randrange(4)==0:
779 serv
.privmsg(canal
,"%s: Vous savez, moi et les chiffres…"%(auteur))
781 except Exception as exc
:
783 if re
.match("[A-Y]",out
):
784 alphabet
="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
785 serv
.privmsg(canal
,"%s: %s !"%(auteur
,alphabet
[alphabet
.index(out
)+1]))
787 serv
.privmsg(canal
,"%s: Je ne vous remercie pas, j'ai l'air idiot ainsi… [ ?"%(auteur))
789 serv
.privmsg(canal
,"%s: Nous devrions nous en tenir là, ça va finir par poser des problèmes…"%(auteur))
790 elif re
.match(ur
"(¹|²|³|⁴|⁵|⁶|⁷|⁸|⁹|⁰)+",out
):
792 return "".join([{u
"⁰¹²³⁴⁵⁶⁷⁸⁹0123456789"[i
]:u
"0123456789⁰¹²³⁴⁵⁶⁷⁸⁹"[i
]
793 for i
in range(20)}[j
]
795 out
=int(translate(out
))
796 serv
.privmsg(canal
,"%s: %s !"%(auteur
,translate(str(out
+1)).encode("utf8")))
797 if is_bonjour(message
) and not canal
in self
.quiet_channels
:
799 answer
=random
.choice(config_night_answers
)
801 answer
=random
.choice(config_bonjour_answers
)
803 answer
=random
.choice(config_bonsoir_answers
)
804 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
805 if is_bonne_nuit(message
) and not canal
in self
.quiet_channels
:
806 answer
=random
.choice(config_bonne_nuit_answers
)
807 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
808 if is_pan(message
) and not canal
in self
.quiet_channels
:
809 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))
811 if message
in ["!pain au chocolat","!chocolatine"] and not canal
in self
.quiet_channels
:
812 serv
.action(canal
,"sert un pain au chocolat à %s"%(auteur))
813 if message
in ["!manzana"] and not canal
in self
.quiet_channels
:
814 if auteur
in config_manzana
:
815 serv
.action(canal
,"sert une bouteille de manzana à %s"%(auteur))
817 serv
.action(canal
,"sert un verre de manzana à %s"%(auteur))
818 if re
.match('^(.|§|:|)(w|b) [0-9]+$',message
) and not canal
in self
.quiet_channels
:
819 failanswers
=config_buffer_fail_answers
820 answer
=random
.choice(failanswers
)
821 serv
.privmsg(canal
,"%s: %s"%(auteur
,answer
))
822 if not canal
in self
.quiet_channels
:
824 if re
.match((u
"^("+u
"|".join(config_bonjour_triggers
)
825 +u
")( {}| all| tout le monde|(|à) tous)(\.|( |)!|)$"
826 ).format(mypseudo
).lower(), message
.strip().lower()):
827 answer
=random
.choice(config_bonjour_answers
)
828 serv
.privmsg(canal
,answer
.format(auteur
).encode("utf8"))
829 if (is_perdu(message
) and not canal
in self
.quiet_channels
):
830 # proba de perdre sur trigger :
831 # avant 30min (enfin, config) : 0
832 # ensuite, +25%/30min, linéairement
833 deltat
=time
.time()-self
.last_perdu
834 barre
=(deltat
-config_time_between_perdu
)/(2*3600.0)
835 if random
.uniform(0,1)<barre
:
836 serv
.privmsg(canal
,"%s: J'ai perdu !"%(auteur))
837 self
.last_perdu
=time
.time()
839 def on_action(self
, serv
, ev
):
840 action
= ev
.arguments()[0]
841 auteur
= irclib
.nm_to_n(ev
.source())
842 channel
= ev
.target()
844 test
=bot_unicode(action
)
845 except UnicodeBotError
:
846 serv
.privmsg(channel
,
847 "%s: Si je n'avais pas été créé avec la plus grande attention, votre encodage m'aurait déjà tué…"%(auteur))
851 if is_bad_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
852 l1
,l2
=config_bad_action_answers
,config_bad_action_actions
853 n1
,n2
=len(l1
),len(l2
)
854 i
=random
.randrange(n1
+n2
)
856 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
858 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
859 if is_good_action_trigger(action
,mypseudo
) and not channel
in self
.quiet_channels
:
860 l1
,l2
=config_good_action_answers
,config_good_action_actions
861 n1
,n2
=len(l1
),len(l2
)
862 i
=random
.randrange(n1
+n2
)
864 serv
.action(channel
,l2
[i
-n1
].format(auteur
).format(auteur
).encode("utf8"))
866 serv
.privmsg(channel
,l1
[i
].format(auteur
).format(auteur
).encode("utf8"))
868 def on_kick(self
,serv
,ev
):
869 auteur
= irclib
.nm_to_n(ev
.source())
870 channel
= ev
.target()
871 victime
= ev
.arguments()[0]
872 raison
= ev
.arguments()[1]
873 if victime
==self
.nick
:
874 log(self
.serveur
,"%s kické par %s (raison : %s)" %(victime
,auteur
,raison
))
877 l1
,l2
=config_kick_answers
,config_kick_actions
878 n1
,n2
=len(l1
),len(l2
)
879 i
=random
.randrange(n1
+n2
)
881 serv
.action(channel
,l2
[i
-n1
].format(auteur
).encode("utf8"))
883 serv
.privmsg(channel
,l1
[i
].format(auteur
).encode("utf8"))
886 return self
.serv
.get_nickname()
887 nick
=property(_getnick
)
890 if __name__
=="__main__":
893 print "Usage : basile.py <serveur> [--debug]"
896 if "debug" in sys
.argv
or "--debug" in sys
.argv
:
900 serveurs
={"a♡":"acoeur.crans.org","acoeur":"acoeur.crans.org","acoeur.crans.org":"acoeur.crans.org",
901 "irc":"irc.crans.org","crans":"irc.crans.org","irc.crans.org":"irc.crans.org"}
903 serveur
=serveurs
[serveur
]
905 print "Server Unknown : %s"%(serveur)
907 basile
=Basile(serveur
,debug
)