banned/src/handler.py

192 lines
6.1 KiB
Python

# The bot commands implemented in here are present no matter which module is loaded
import base64
import imp
import socket
import traceback
import botconfig
from oyoyo.parse import parse_nick
from src import decorators
from src import logger
from src import settings as var
from src import wolfgame
log = logger("errors.log")
alog = logger(None)
def notify_error(cli, chan, target_logger):
msg = "An error has occurred and has been logged."
tb = traceback.format_exc()
target_logger(tb)
if (not botconfig.PASTEBIN_ERRORS) or (chan != botconfig.DEV_CHANNEL):
# Don't send a duplicate message if DEV_CHANNEL is the current channel.
cli.msg(chan, msg)
if botconfig.PASTEBIN_ERRORS and botconfig.DEV_CHANNEL:
try:
with socket.socket() as sock:
sock.connect(("termbin.com", 9999))
sock.send(tb.encode("utf-8", "replace") + b"\n")
url = sock.recv(1024).decode("utf-8")
except socket.error:
target_logger(traceback.format_exc())
else:
cli.msg(botconfig.DEV_CHANNEL, " ".join((msg, url)))
def on_privmsg(cli, rawnick, chan, msg, notice = False):
try:
prefixes = getattr(var, "STATUSMSG_PREFIXES")
except AttributeError:
pass
else:
if botconfig.IGNORE_HIDDEN_COMMANDS and chan[0] in prefixes:
return
if (notice and ((chan != botconfig.NICK and not botconfig.ALLOW_NOTICE_COMMANDS) or
(chan == botconfig.NICK and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))):
return # not allowed in settings
if chan == botconfig.NICK:
chan = parse_nick(rawnick)[0]
if "" in wolfgame.COMMANDS.keys():
for fn in wolfgame.COMMANDS[""]:
try:
fn(cli, rawnick, chan, msg)
except Exception:
if botconfig.DEBUG_MODE:
raise
else:
notify_error(cli, chan, log)
for x in set(list(COMMANDS.keys()) + list(wolfgame.COMMANDS.keys())):
if chan != parse_nick(rawnick)[0] and not msg.lower().startswith(botconfig.CMD_CHAR):
break # channel message but no prefix; ignore
if msg.lower().startswith(botconfig.CMD_CHAR+x):
h = msg[len(x)+len(botconfig.CMD_CHAR):]
elif not x or msg.lower().startswith(x):
h = msg[len(x):]
else:
continue
if not h or h[0] == " ":
for fn in COMMANDS.get(x, []) + (wolfgame.COMMANDS.get(x, [])):
try:
fn(cli, rawnick, chan, h.lstrip())
except Exception:
if botconfig.DEBUG_MODE:
raise
else:
notify_error(cli, chan, log)
def unhandled(cli, prefix, cmd, *args):
if cmd in set(list(HOOKS.keys()) + list(wolfgame.HOOKS.keys())):
largs = list(args)
for i,arg in enumerate(largs):
if isinstance(arg, bytes): largs[i] = arg.decode('ascii')
for fn in HOOKS.get(cmd, []) + wolfgame.HOOKS.get(cmd, []):
try:
fn(cli, prefix, *largs)
except Exception as e:
if botconfig.DEBUG_MODE:
raise e
else:
notify_error(cli, botconfig.CHANNEL, log)
COMMANDS = {}
HOOKS = {}
cmd = decorators.generate(COMMANDS)
hook = decorators.generate(HOOKS, raw_nick=True, permissions=False)
def connect_callback(cli):
def prepare_stuff(*args):
# just in case we haven't managed to successfully auth yet
if not botconfig.SASL_AUTHENTICATION:
cli.ns_identify(botconfig.PASS)
cli.join(botconfig.CHANNEL)
if botconfig.ALT_CHANNELS:
cli.join(botconfig.ALT_CHANNELS)
if botconfig.DEV_CHANNEL:
cli.join(",".join(chan.lstrip("".join(var.STATUSMSG_PREFIXES)) for chan in botconfig.DEV_CHANNEL.split(",")))
cli.msg("ChanServ", "op "+botconfig.CHANNEL)
cli.cap("REQ", "extended-join")
cli.cap("REQ", "account-notify")
wolfgame.connect_callback(cli)
cli.nick(botconfig.NICK) # very important (for regain/release)
prepare_stuff = hook("endofmotd", hookid=294)(prepare_stuff)
def mustregain(cli, *blah):
if not botconfig.PASS:
return
cli.ns_regain()
def mustrelease(cli, *rest):
if not botconfig.PASS:
return # prevents the bot from trying to release without a password
cli.ns_release()
cli.nick(botconfig.NICK)
@hook("unavailresource", hookid=239)
@hook("nicknameinuse", hookid=239)
def must_use_temp_nick(cli, *etc):
cli.nick(botconfig.NICK+"_")
cli.user(botconfig.NICK, "")
decorators.unhook(HOOKS, 239)
hook("unavailresource")(mustrelease)
hook("nicknameinuse")(mustregain)
if botconfig.SASL_AUTHENTICATION:
@hook("authenticate")
def auth_plus(cli, something, plus):
if plus == "+":
nick_b = bytes(botconfig.USERNAME if botconfig.USERNAME else botconfig.NICK, "utf-8")
pass_b = bytes(botconfig.PASS, "utf-8")
secrt_msg = b'\0'.join((nick_b, nick_b, pass_b))
cli.send("AUTHENTICATE " + base64.b64encode(secrt_msg).decode("utf-8"))
@hook("cap")
def on_cap(cli, svr, mynick, ack, cap):
if ack.upper() == "ACK" and "sasl" in cap:
cli.send("AUTHENTICATE PLAIN")
elif ack.upper() == "NAK" and "sasl" in cap:
cli.quit()
alog("Server does not support SASL authentication")
@hook("903")
def on_successful_auth(cli, blah, blahh, blahhh):
cli.cap("END")
@hook("904")
@hook("905")
@hook("906")
@hook("907")
def on_failure_auth(cli, *etc):
cli.quit()
alog("Authentication failed. Did you fill the account name "+
"in botconfig.USERNAME if it's different from the bot nick?")
@hook("ping")
def on_ping(cli, prefix, server):
cli.send('PONG', server)
# vim: set expandtab:sw=4:ts=4: