f.write((chain + u"\n").encode("utf-8"))
f.close()
if config.debug_stdout:
- print chain
+ print chain.encode("utf-8")
+
+def ignore_event(serv, ev):
+ """Retourne ``True`` si il faut ignorer cet évènement."""
+ for (blackmask, exceptlist) in config.blacklisted_masks:
+ usermask = ev.source()
+ blackit = bool(irclib.mask_matches(usermask, blackmask))
+ exceptit = any([bool(irclib.mask_matches(usermask, exceptmask)) for exceptmask in exceptlist])
+ if exceptit: # Il est exempté
+ return False
+ else:
+ if blackit: # Il n'est pas exempté et matche la blacklist
+ return True
def regex_join(liste, avant=u".*(?:^| )", apres=u"(?:$|\.| |,|;).*"):
"""Fabrique une regexp à partir d'une liste d'éléments à matcher."""
self.quiet_channels = config.quiet_channels
self.last_perdu = 0
+ ### Communication NK
def new_connection_NK(self, serv, username, password, typ="bdd"):
"""Renvoie (``True``, <une socket ouverte et authentifiée sur la NoteKfet2015>)
ou bien (``False``, None)"""
try:
login_result, sock = nk.login(username, password, typ)
- droits, retcode, errmsg = login_result["msg"], login_result["retcode"], login_result["errmsg"]
+ info, retcode, errmsg = login_result["msg"], login_result["retcode"], login_result["errmsg"]
except nk.NKRefused as exc:
for report in self.report_bugs_to:
serv.privmsg(report, "Le Serveur NK2015 est down.")
- return (False, None)
+ return (False, None, None)
except nk.NKHelloFailed as exc:
for report in self.report_bugs_to:
serv.privmsg(report,
"La version du protocole utilisée n'est pas supportée par le serveur NK2015.")
- return (False, None)
+ return (False, None, None)
except nk.NKUnknownError as exc:
erreurs = ["Une fucking erreur inconnue s'est produite"]
erreurs += str(exc).split("\n")
for report in self.report_bugs_to:
for err in erreurs:
serv.privmsg(report, err)
- return (False, None)
+ return (False, None, None)
except Exception as exc:
# Exception qui ne vient pas de la communication avec le serveur NK2015
log(self.serveur, "Erreur dans new_connection_NK\n" + str(exc))
- return (False, None)
+ return (False, None, None)
if retcode == 0:
- return (True, sock)
+ return (True, info, sock)
else:
- return (False, None)
-
+ return (False, None, None)
+
+ ### Utilitaires
+ def _getnick(self):
+ """Récuère le nick effectif du bot sur le serveur."""
+ return self.serv.get_nickname()
+ nick = property(_getnick)
+
def give_me_my_pseudo(self, serv):
"""Récupère le pseudo auprès de NickServ."""
serv.privmsg("NickServ", "RECOVER %s %s" % (config.irc_pseudo, config.irc_password))
time.sleep(0.3)
serv.nick(config.irc_pseudo)
+ def pourmoi(self, serv, message):
+ """Renvoie (False, lemessage) ou (True, le message amputé de "pseudo: ")"""
+ pseudo = self.nick
+ pseudo = pseudo.decode("utf-8")
+ size = len(pseudo)
+ if message[:size] == pseudo and len(message) > size and message[size] == ":":
+ return (True, message[size+1:].lstrip(" "))
+ else:
+ return (False, message)
+
+ ### Exécution d'actions
+ def lost(self, serv, channel, forced=False):
+ """Réaction à un trigger de perdu.
+ Annonce "J'ai perdu" sur le channel si on n'a pas perdu depuis un certain temps."""
+ if self.last_perdu + config.time_between_perdu < time.time() or forced:
+ if not channel in self.quiet_channels or forced:
+ serv.privmsg(channel, "J'ai perdu !")
+ self.last_perdu=time.time()
+ delay=config.time_between_perdu_trigger
+ delta=config.time_between_perdu_trigger_delta
+ serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
+
+ def quitter(self, chan, leave_message=None):
+ """Quitter un channel avec un message customisable."""
+ if leave_message == None:
+ leave_message = random.choice(config.leave_messages)
+ self.serv.part(chan, message=leave_message.encode("utf8"))
+
+ def mourir(self):
+ """Se déconnecter du serveur IRC avec un message customisable."""
+ quit_message = random.choice(config.quit_messages)
+ self.die(msg=quit_message.encode("utf8"))
+
+ def execute_reload(self, auteur=None):
+ """Recharge la config."""
+ reload(config)
+ regexp_compile()
+ if auteur in [None, "SIGHUP"]:
+ towrite = "Config reloaded" + " (SIGHUP received)" * (auteur == "SIGHUP")
+ for to in config.report_bugs_to:
+ self.serv.privmsg(to, towrite)
+ log(self.serveur, towrite)
+ return True, None
+ else:
+ return True, u"Config reloaded"
+
+ def crash(self, chan="nowhere", who="nobody"):
+ """Fait crasher le bot."""
+ where = "en privé" if chan == "priv" else "sur le chan %s" % chan
+ raise CrashError((u"Crash demandé par %s %s" % (who, where)).encode("utf-8"))
+
+ ACTIONS = {
+ "reload" : execute_reload,
+ }
+
+ def execute_something(self, something, params, place=None, auteur=None):
+ """Exécute une action et répond son résultat à ``auteur``
+ sur un chan ou en privé en fonction de ``place``"""
+ action = self.ACTIONS[something]
+ success, message = action(self, **params)
+ if message:
+ if irclib.is_channel(place):
+ message = "%s: %s" % (auteur, message.encode("utf-8"))
+ self.serv.privmsg(place, message)
+ log(self.serveur, place, auteur, something + "%r" % params + ("[successful]" if success else "[failed]"))
+
+ ### Surcharge des events du Bot
def on_welcome(self, serv, ev):
"""À l'arrivée sur le serveur."""
self.serv = serv # ça serv ira :)
log(self.serveur, "JOIN %s" % (c))
serv.join(c)
# on ouvre la connexion note de Basile, special user
- self.nk = self.new_connection_NK(serv, config.note_pseudo, config.note_password, "special")[1]
+ self.nk = self.new_connection_NK(serv, config.note_pseudo, config.note_password, "special")[2]
if self.nk == None:
for report in self.report_bugs_to:
serv.privmsg(report, "Connection to NK2015 failed, invalid password ?")
- def lost(self, serv, channel, forced=False):
- """Réaction à un trigger de perdu.
- Annonce "J'ai perdu" sur le channel si on n'a pas perdu depuis un certain temps."""
- if self.last_perdu + config.time_between_perdu < time.time() or forced:
- if not channel in self.quiet_channels or forced:
- serv.privmsg(channel, "J'ai perdu !")
- self.last_perdu=time.time()
- delay=config.time_between_perdu_trigger
- delta=config.time_between_perdu_trigger_delta
- serv.execute_delayed(random.randrange(delay-delta,delay+delta),self.lost,(serv,channel))
-
- def pourmoi(self, serv, message):
- """Renvoie (False, lemessage) ou (True, le message amputé de "pseudo: ")"""
- pseudo = self.nick
- pseudo = pseudo.decode("utf-8")
- size = len(pseudo)
- if message[:size] == pseudo and len(message) > size and message[size] == ":":
- return (True, message[size+1:].lstrip(" "))
- else:
- return (False, message)
-
def on_privmsg(self, serv, ev):
"""À la réception d'un message en privé."""
+ if ignore_event(serv, ev):
+ return
message = ev.arguments()[0]
auteur = irclib.nm_to_n(ev.source())
try:
if len(message) == 1:
if self.identities.has_key(auteur):
serv.privmsg(auteur, "Je vous connais sous le pseudo note %s." % (
- self.identities[auteur].encode("utf8")))
+ self.identities[auteur]["pseudo"].encode("utf8")))
else:
serv.privmsg(auteur, "Je ne connais pas votre pseudo note.")
elif len(message) >= 3:
username, password = message[1], " ".join(message[2:])
- success, _ = self.new_connection_NK(serv, username, password)
+ success, info, _ = self.new_connection_NK(serv, username, password)
if success:
log(self.serveur, "priv", auteur, " ".join(message) + "[successful]")
serv.privmsg(auteur, "Identité enregistrée.")
- self.identities[auteur] = username
+ self.identities[auteur] = info
json.dump(self.identities, open(config.identities_file,"w"))
else:
log(self.serveur, "priv", auteur, " ".join(message) + "[failed]")
if len(message) > 1:
if self.identities.has_key(auteur):
password = " ".join(message[1:])
- success, _ = self.new_connection_NK(serv, self.identities[auteur], password)
+ success, _, _ = self.new_connection_NK(serv, self.identities[auteur], password)
if success:
del self.identities[auteur]
log(self.serveur, "priv", auteur, " ".join(message) + "[successful]")
if auteur in self.ops and len(message) > 1:
if message[1] in self.chanlist:
if not (message[1] in self.stay_channels) or auteur in self.overops:
- self.quitter(message[1], " ".join(message[2:]))
+ self.quitter(message[1].encode("utf-8"), " ".join(message[2:]))
self.chanlist.remove(message[1])
log(self.serveur, "priv", auteur, " ".join(message) + "[successful]")
else:
notunderstood = True
elif cmd == u"reload":
if auteur in self.ops:
- self.reload(auteur)
- log(self.serveur, "priv", auteur, " ".join(message) + "[successful]")
+ self.execute_something("reload", {"auteur" : auteur}, place=auteur, auteur=auteur)
else:
notunderstood = True
elif cmd == u"reconnect":
if auteur in self.ops:
try:
self.nk = self.new_connection_NK(serv, config.note_pseudo,
- config.note_password, "special")[1]
+ config.note_password, "special")[2]
except Exception as exc:
self.nk = None
log(self.serveur, 'Erreur dans on_pubmsg/"cmd in ["reconnect"]\n' + str(exc))
notunderstood = True
elif cmd == u"say":
if auteur in self.overops and len(message) > 2:
- serv.privmsg(message[1], " ".join(message[2:]))
+ serv.privmsg(message[1].encode("utf-8"), (u" ".join(message[2:])).encode("utf-8"))
log(self.serveur, "priv", auteur, " ".join(message))
elif len(message) <= 2:
serv.privmsg(auteur, "Syntaxe : SAY <channel> <message>")
notunderstood = True
elif cmd == u"kick":
if auteur in self.overops and len(message) > 2:
- serv.kick(message[1], message[2], " ".join(message[3:]))
+ serv.kick(message[1].encode("utf-8"), message[2].encode("utf-8"), " ".join(message[3:]).encode("utf-8"))
log(self.serveur, "priv", auteur, " ".join(message))
elif len(message) <= 2:
serv.privmsg(auteur, "Syntaxe : KICK <channel> <pseudo> [<raison>]")
elif cmd == u"solde":
if len(message) == 1:
if self.identities.has_key(auteur):
- try:
- self.nk.write('["search", ["x",["pseudo"],%s]]' % (json.dumps(self.identities[auteur])))
- ret = json.loads(self.nk.read())
- solde = ret["msg"][0]["solde"]
- pseudo = ret["msg"][0]["pseudo"]
- except Exception as exc:
- print exc
- serv.privmsg(auteur, "failed")
- log(self.serveur, "priv", auteur, " ".join(message) + "[failed]")
- return
- serv.privmsg(auteur, "%s (%s)" % (float(solde)/100, pseudo.encode("utf8")))
- log(self.serveur, "priv", auteur, " ".join(message) + "[successful]")
+ success, solde, pseudo = nk.get_solde(self.nk, self.identities[auteur]["idbde"], serv, auteur)
+ if success:
+ serv.privmsg(auteur, "%.2f (%s)" % (solde/100.0, pseudo.encode("utf8")))
+ log(self.serveur, "priv", auteur, " ".join(message) + ("[successful]" if success else "[failed]"))
else:
serv.privmsg(canal, "Je ne connais pas ton pseudo note.")
- elif auteur in self.ops:
- try:
- self.nk.write('["search", ["x",["pseudo"],%s]]' % (json.dumps(message[1])))
- ret = json.loads(self.nk.read())
- solde = ret["msg"][0]["solde"]
- pseudo = ret["msg"][0]["pseudo"]
- except Exception as exc:
- serv.privmsg(auteur, "failed")
- log(self.serveur, "priv", auteur, " ".join(message) + "[failed]")
- return
- serv.privmsg(auteur, "%s (%s)" % (float(solde)/100, pseudo.encode("utf8")))
elif cmd == u"ops":
if auteur in self.overops:
serv.privmsg(auteur, " ".join(self.ops))
def on_pubmsg(self, serv, ev):
"""À la réception d'un message sur un channel."""
+ if ignore_event(serv, ev):
+ return
auteur = irclib.nm_to_n(ev.source())
canal = ev.target()
message = ev.arguments()[0]
log(self.serveur, canal, auteur, message + "[failed]")
elif cmd == u"reload":
if auteur in self.ops:
- log(self.serveur, canal, auteur, message + "[successful]")
- self.reload(canal)
+ self.execute_something("reload", {"auteur" : auteur}, place=canal, auteur=auteur)
elif cmd == u"crash":
if auteur in self.overops:
self.crash(auteur, message)
if auteur in self.ops:
try:
self.nk = self.new_connection_NK(serv, config.note_pseudo,
- config.note_password, "special")[1]
+ config.note_password, "special")[2]
except Exception as exc:
self.nk = None
log(self.serveur, 'Erreur dans on_pubmsg/"cmd in ["reconnect"]\n' + str(exc))
elif cmd in [u"ping"] and not canal in self.quiet_channels:
serv.privmsg(canal, "%s: pong" % (auteur))
- elif cmd in [u"solde", u"!solde"]:
+ elif cmd in [u"solde", u"!solde", u"!coca"] or cmd.startswith("!"):
if self.identities.has_key(auteur):
- pseudo = self.identities[auteur]
- try:
- self.nk.write(json.dumps(["search", ["x", ["pseudo"], pseudo]]))
- ret = json.loads(self.nk.read())
- solde = ret["msg"][0]["solde"]
- pseudo = ret["msg"][0]["pseudo"]
- except Exception as exc:
- serv.privmsg(canal, "%s: failed"%(auteur))
- log(self.serveur, canal, auteur, message + "[failed]")
- else:
- serv.privmsg(canal, "%s: %s (%s)" % (auteur, float(solde)/100, pseudo.encode("utf8")))
- log(self.serveur, canal, auteur, message + "[successful]")
+ idbde = self.identities[auteur]["idbde"]
+ if cmd in [u"solde", u"!solde"]:
+ success, solde, pseudo = nk.get_solde(self.nk, self.identities[auteur]["idbde"], serv, canal)
+ if success:
+ serv.privmsg(canal, "%s: %s (%s)" % (auteur, float(solde)/100, pseudo.encode("utf8")))
+ elif cmd in [u"!coca"] or cmd.startswith("!"):
+ success = nk.consomme(self.nk, self.identities[auteur]["idbde"], message[1:], serv, canal)
+ log(self.serveur, canal, auteur, message + ("[successful]" if success else "[failed]"))
else:
serv.privmsg(canal, "%s: Je ne connais pas votre pseudo note." % (auteur))
log(self.serveur, canal, auteur, message + "[unknown]")
- elif (re.match("!?(pain au chocolat|chocolatine)", message.lower())
+ elif (re.match("(pain au chocolat|chocolatine)", message.lower())
and not canal in self.quiet_channels):
serv.action(canal, "sert un pain au chocolat à %s" % (auteur))
- elif re.match("!?manzana",message.lower()) and not canal in self.quiet_channels:
+ elif re.match("manzana",message.lower()) and not canal in self.quiet_channels:
if auteur in config.manzana:
serv.action(canal, "sert une bouteille de manzana à %s" % (auteur))
elif auteur in config.manzana_bis:
def on_action(self, serv, ev):
"""À la réception d'une action."""
+ if ignore_event(serv, ev):
+ return
action = ev.arguments()[0]
auteur = irclib.nm_to_n(ev.source())
channel = ev.target()
victime = ev.arguments()[0]
raison = ev.arguments()[1]
if victime == self.nick:
- log(self.serveur, "%s kické de %s par %s (raison : %s)" % (victime, channel, auteur, raison))
+ log(self.serveur, u"%s kické de %s par %s (raison : %s)" % (victime, channel.decode("utf-8"), auteur, raison))
time.sleep(2)
serv.join(channel)
l1, l2 = config.kick_answers, config.kick_actions
else:
serv.privmsg(channel, l1[i].format(auteur).encode("utf8"))
- def quitter(self, chan, leave_message=None):
- """Quitter un channel avec un message customisable."""
- if leave_message == None:
- leave_message = random.choice(config.leave_messages)
- self.serv.part(chan, message=leave_message.encode("utf8"))
-
- def mourir(self):
- """Se déconnecter du serveur IRC avec un message customisable."""
- quit_message = random.choice(config.quit_messages)
- self.die(msg=quit_message.encode("utf8"))
-
- def _getnick(self):
- """Récuère le nick effectif du bot sur le serveur."""
- return self.serv.get_nickname()
- nick = property(_getnick)
-
- def reload(self, auteur=None):
- """Recharge la config."""
- reload(config)
- regexp_compile()
- if auteur in [None, "SIGHUP"]:
- towrite = "Config reloaded" + " (SIGHUP received)" * (auteur == "SIGHUP")
- for to in config.report_bugs_to:
- self.serv.privmsg(to, towrite)
- log(self.serveur, towrite)
- else:
- self.serv.privmsg(auteur, "Config reloaded")
-
- def crash(self, chan="nowhere", who="nobody"):
- """Fait crasher le bot."""
- where = "en privé" if chan == "priv" else "sur le chan %s" % chan
- raise CrashError("Crash demandé par %s %s" % (who, where))
-
+ ### .fork trick
def start_as_daemon(self, outfile):
sys.stderr = Logger(outfile)
self.start()
basile = Basile(serveur,debug)
# Si on reçoit un SIGHUP, on reload la config
def sighup_handler(signum, frame):
- basile.reload("SIGHUP")
+ basile.execute_reload(auteur="SIGHUP")
signal.signal(signal.SIGHUP, sighup_handler)
# Daemonization
if daemon: