# The bot commands implemented in here are present no matter which module is loaded import base64 import socket import sys import traceback from oyoyo.parse import parse_nick import botconfig import src.settings as var from src import decorators, logger, wolfgame log = logger("errors.log") alog = logger(None) sys.stderr.target_logger = log hook = decorators.hook 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 try: getattr(var, "CASEMAPPING") except AttributeError: # some kind of hack for strange networks which don't put server name in some of the NOTICEs on startup if not rawnick: return if notice and "!" not in rawnick and chan in ("*", "AUTH"): # On-connect message before RPL_ISUPPORT is sent. return log("Server did not send a case mapping; falling back to rfc1459.") var.CASEMAPPING = "rfc1459" if (notice and ((not var.irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_NOTICE_COMMANDS) or (var.irc_equals(chan, botconfig.NICK) and not botconfig.ALLOW_PRIVATE_NOTICE_COMMANDS))): return # not allowed in settings if var.irc_equals(chan, botconfig.NICK): chan = parse_nick(rawnick)[0] for fn in decorators.COMMANDS[""]: try: fn.caller(cli, rawnick, chan, msg) except Exception: sys.stderr.write(traceback.format_exc()) if botconfig.DEBUG_MODE: raise for x in list(decorators.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 decorators.COMMANDS.get(x, []): try: fn.caller(cli, rawnick, chan, h.lstrip()) except Exception: sys.stderr.write(traceback.format_exc()) if botconfig.DEBUG_MODE: raise def unhandled(cli, prefix, cmd, *args): if cmd in decorators.HOOKS: largs = list(args) for i,arg in enumerate(largs): if isinstance(arg, bytes): largs[i] = arg.decode('ascii') for fn in decorators.HOOKS.get(cmd, []): try: fn.func(cli, prefix, *largs) except Exception: sys.stderr.write(traceback.format_exc()) if botconfig.DEBUG_MODE: raise def connect_callback(cli): @hook("endofmotd", hookid=294) @hook("nomotd", hookid=294) def prepare_stuff(cli, *args): # just in case we haven't managed to successfully auth yet if not botconfig.SASL_AUTHENTICATION: cli.ns_identify(botconfig.USERNAME or botconfig.NICK, botconfig.PASS, nickserv=var.NICKSERV, command=var.NICKSERV_IDENTIFY_COMMAND) channels = {botconfig.CHANNEL} if botconfig.ALT_CHANNELS: channels.update(botconfig.ALT_CHANNELS.split(",")) if botconfig.DEV_CHANNEL: channels.update(chan.lstrip("".join(var.STATUSMSG_PREFIXES)) for chan in botconfig.DEV_CHANNEL.split(",")) cli.join(",".join(channels)) if var.CHANSERV_OP_COMMAND: cli.msg(var.CHANSERV, var.CHANSERV_OP_COMMAND.format(channel=botconfig.CHANNEL)) cli.nick(botconfig.NICK) # very important (for regain/release) wolfgame.connect_callback(cli) def mustregain(cli, *blah): if not botconfig.PASS: return cli.ns_regain(nickserv=var.NICKSERV, command=var.NICKSERV_REGAIN_COMMAND) def mustrelease(cli, *rest): if not botconfig.PASS: return # prevents the bot from trying to release without a password cli.ns_release(nickserv=var.NICKSERV, command=var.NICKSERV_RELEASE_COMMAND) 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, "") hook.unhook(239) hook("unavailresource")(mustrelease) hook("nicknameinuse")(mustregain) request_caps = {"account-notify", "extended-join", "multi-prefix"} if botconfig.SASL_AUTHENTICATION: request_caps.add("sasl") supported_caps = set() @hook("cap") def on_cap(cli, svr, mynick, cmd, caps, star=None): if cmd == "LS": if caps == "*": # Multi-line LS supported_caps.update(star.split()) else: supported_caps.update(caps.split()) if botconfig.SASL_AUTHENTICATION and "sasl" not in supported_caps: alog("Server does not support SASL authentication") cli.quit() common_caps = request_caps & supported_caps if common_caps: cli.cap("REQ", ":{0}".format(" ".join(common_caps))) elif cmd == "ACK": if "sasl" in caps: cli.send("AUTHENTICATE PLAIN") else: cli.cap("END") elif cmd == "NAK": # This isn't supposed to happen. The server claimed to support a # capability but now claims otherwise. alog("Server refused capabilities: {0}".format(" ".join(caps))) if sys.stderr.cli is None: sys.stderr.cli = cli # first connection if botconfig.SASL_AUTHENTICATION: @hook("authenticate") def auth_plus(cli, something, plus): if plus == "+": account = (botconfig.USERNAME or botconfig.NICK).encode("utf-8") password = botconfig.PASS.encode("utf-8") auth_token = base64.b64encode(b"\0".join((account, account, password))).decode("utf-8") cli.send("AUTHENTICATE " + auth_token) @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): alog("Authentication failed. Did you fill the account name " "in botconfig.USERNAME if it's different from the bot nick?") cli.quit() @hook("ping") def on_ping(cli, prefix, server): cli.send('PONG', server) # vim: set expandtab:sw=4:ts=4: