def nick(bot, event, *args):
    '''Adds a nickname. Format is /bot nick <nickname>'''
    if len(args) == 1:
        added = add_nickname(bot, event, event.user.id_.chat_id, args[0].lower())
        if added:
            yield from bot.coro_send_message(event.conv, _(added))
    elif len(args) == 3 and is_admin(bot, event) and args[0] == '--set':
        added = add_nickname(bot, event, args[1], args[2].lower())
        if added:
            yield from bot.coro_send_message(event.conv, _(added))
    else:
        yield from bot.coro_send_message(event.conv, _("Too many args"))
示例#2
0
def score(bot, event, *args):
    '''Get the score for a user. Format is /bot score <name>
   To increment scores, do <name>++ or <name>--'''
    if len(args) == 1:
        if args[0].lower() == '--high':
            msg = get_high_score(bot)
        elif args[0].lower() == '--low':
            msg = get_low_score(bot)
        else:
            name = args[0].lower()
            msg = get_score(bot, name)
    elif args[0].lower() == '--set' and len(args) == 3 and is_admin(bot, event):
        name = args[1].lower()
        val = args[2].lower()
        msg = set_score(bot, name, int(val))
    else:
        msg = _("Wrong number of arguments!")
    yield from bot.coro_send_message(event.conv, msg)
示例#3
0
def handle_command(parts):
    is_admin = admin.is_admin(request.form['user_id'])
    cmd_name = parts[0]
    if cmd_name in command_map:
        cmd_data = command_map[cmd_name]
        if cmd_data['admin'] and not is_admin:
            return msg("You don't have permission to run {}".format(cmd_name))
        if cmd_data['args'] is not None and  len(parts) < (1 + len(cmd_data['args'])):
            return msg("Need {} arg(s): {}".format(len(cmd_data['args']), args_to_string(cmd_data['args'])))
        args = parts[1:]
        r = None
        if cmd_data['args'] is None:
            r = cmd_data['func'](args)
        else:
            r = cmd_data['func'](*args)
        if r:
            return r
        return None
    return "Unknown ++ command " + parts[0]
示例#4
0
def quote(bot, event, *args):
    '''Manipulate quotes. Format is /bot quote [-a, -l]
To add quotes: /bot quote -a quote - author
To list all quotes: /bot quote -l
To list specific person's quotes: /bot quote -l name
To get a specific quote: /bot quote quote_number
To get a random quote: /bot quote
To get a random quote for a specifc author: /bot quote name'''
    msg = None
    try:
        conn = sqlite3.connect('bot.db')
        if not args:
            msg = retrieve(conn, None, None, full=False)
        elif args[0] not in ['-a', '-d', '-l', '-e'] and args[0].startswith('-'):
            msg = "Invalid Flag"
        elif args[0] in ['-d', '-e']:
            if len(args) < 2:
               msg = "You're missing arguments!"
            elif is_admin(bot, event) or is_tag(bot, event, 'quote-admin'): # admin only quote functions
                if args[0] == "-d": # delete quotes
                    delete(conn, args[1])
                    msg = "Successfully deleted quote"
                elif args[0] == "-e": # edit quotes
                    msg = edit(conn, args[1], " ".join(args[2:]))
            else:
                msg = "You're not an admin!"
        elif args[0] == "-l": # list quotes from author
            if len(args) == 1:
                msg = str(retrieve(conn, None, True))
            else:
                msg = str(retrieve(conn, args[1], True))
        elif args[0] == "-a":
            text = " ".join(args[1:]).split(' - ')
            if event.user.first_name.lower() == text[1]:
                msg = "You can't submit your own quote!" # self-submission
            else:
                if is_admin(bot, event) or is_tag(bot, event, 'quote-admin'):
                    msg = add(conn, text[0], text[1])
                else:
                    rply = add(conn, text[0], text[1], admin=False)
                    yield from bot.coro_send_message(CONTROL, _(rply[1]))
                    msg = rply[0]
        else:
            text = " ".join(args)
            author = True if not text.isnumeric() else False
            msg = retrieve(conn, text, author, full=False)
        #to_send = _(msg)
        print(msg)
        split = msg.split('\n')
        if len(split) > 4:
            msg = "<b>Message truncated:</b> See full message at {}\nHere are the last 4 lines: \n{}".format(
            	ixio(msg), "\n".join(split[-4:])) 
        #yield from bot.coro_send_message(event.conv, _(str(len(leng))))        
        yield from bot.coro_send_message(event.conv, _(msg))
        conn.close()
    except TypeError:
        msg = 'No such quote'
        yield from bot.coro_send_message(event.conv, _(msg))
    except BaseException as e:
        msg = '{} -- {}'.format(str(e), event.text)
        raise e
        yield from bot.coro_send_message(CONTROL, _(msg))
示例#5
0
 def test_is_admin_empty_json_list(self):
     self.set_admins_file('[]')
     self.assertFalse(admin.is_admin('lol', log=self.flog))
     self.assertFalse(self.flog.logged)
示例#6
0
 def test_is_admin_invalid_user(self):
     self.set_admins_file('["blah"]')
     self.assertFalse(admin.is_admin('', log=self.flog.log))
     self.assertFalse(self.flog.logged)
示例#7
0
 def test_is_admin_empty_user(self):
     nick = 'testnick!blablabla.324.324.890'
     self.set_admins_file('["{}"]'.format(nick))
     self.assertTrue(admin.is_admin(nick, log=self.flog.log))
     self.assertFalse(self.flog.logged)
示例#8
0
 def assert_fail_and_log(self):
     self.assertFalse(admin.is_admin('test', log=self.flog.log))
     self.assertTrue(self.flog.logged)
示例#9
0
def poll(bot, event, *args):
    '''Creates a poll. Format is /bot poll [--add, --delete, --list, --vote] [pollnum, pollname] [vote]'''
    if args:
        if args[0] == '--add' and is_admin(bot, event):
            if len(args) > 1:
                name = ' '.join(args[1:])
                msg = add(bot, name)
        elif args[0] == '--add' and not is_admin(bot, event):
            request = submit_for_approval(bot, event)
            msg = request[0]
            yield from bot.coro_send_message(CONTROL, _(request[1]))
            #msg = _("{}: Can't do that.").format(event.user.full_name)
        elif args[0] == '--delete' and is_admin(bot, event):
            name = ' '.join(args[1:])
            msg = delete(bot, name)
        elif args[0] == '--delete' and not is_admin(bot, event):
            msg = _("{}: Can't do that.").format(event.user.full_name)
        elif args[0] == '--vote':
            if args[1].isdigit():
                pollnum = int(args[1]) - 1
                msg = vote(bot, event, ' '.join(args[2:]), "default", pollnum)
            else:
                vote_ = ' '.join(args[1:]).split(' - ')[0]
                name = ' '.join(args[1:]).split(' - ')[1]
                msg = vote(bot, event, vote_, name, -1)
        elif args[0] == '--list':
            msg = list(bot)
        elif args[0] == '--results':
            if args[1].isdigit():
                path = bot.memory.get_by_path(['polls'])
                pollnum = int(args[1]) - 1
                keys = []
                for poll in path:
                    keys.append(poll)
                if len(keys) > 0 and len(keys) >= pollnum:
                    poll = keys[pollnum]
                    msg = results(bot, poll)
                else:
                    msg = _("Not that many polls")
            else:
                poll = ' '.join(args[1:])
                msg = results(bot, poll)
        elif args[0] == '--help':
            if args[1] == '--set' and is_admin(bot, event):
                if args[2].isdigit():
                    pollnum = int(args[2]) - 1
                    msg = set_help(bot, pollnum, ' '.join(args[3:]))
                else:
                    msg = _("What number poll do you want to add help for?")
            elif args[1] == '--set' and not is_admin(bot, event):
                request = submit_for_approval(bot, event)
                msg = request[0]
                yield from bot.coro_send_message(CONTROL, _(request[1]))
            else:
                if args[1].isdigit():
                    pollnum = int(args[1]) - 1
                    msg = get_help(bot, "default", pollnum)
                else:
                    msg = get_help(bot, ' '.join(args[1:]), -1)
        else:
            if args[0].isdigit():
                pollnum = int(args[0]) - 1
                msg = vote(bot, event, ' '.join(args[1:]), "default", pollnum)
            else:
                vote_ = ' '.join(args).split(' - ')[0]
                name = ' '.join(args).split(' - ')[1]
                msg = vote(bot, event, vote_, name, -1)
    else:
        msg = _(
            "Creates a poll. Format is /bot poll [--add, --delete, --list, --vote, --results] [pollnum, pollname] [vote]")
    yield from bot.coro_send_message(event.conv, msg)
    bot.memory.save()
示例#10
0
def urban(bot, event, *args):
    """lookup a term on Urban Dictionary.
    supplying no parameters will get you a random term.
    DISCLAIMER: all definitions are from http://www.urbandictionary.com/ - the bot and its
    creators/maintainers take no responsibility for any hurt feelings.
    """
    if not bot.memory.exists(["blacklisted"]):
        bot.memory.set_by_path(["blacklisted"], [])
        bot.memory.save()
    blacklisted = bot.memory.get_by_path(["blacklisted"])
    if (args and not args[0] == '--blacklist') or not args:
        term = " ".join(args)
        if not term:
            url = "http://www.urbandictionary.com/random.php"
        else:
            url = "http://www.urbandictionary.com/define.php?term=%s" % \
                  urlquote(term)

        f = urlopen(url)
        data = f.read().decode('utf-8')

        urbanDictParser = UrbanDictParser()
        try:
            urbanDictParser.feed(data)
        except IndexError:
            # apparently, nothing was returned
            pass

        if len(urbanDictParser.translations) > 0:
            the_definition = urbanDictParser.translations[0]
            if the_definition["word"].lower() not in blacklisted:
                html_text = ""
                html_text += '<b>"' + \
                    the_definition["word"] + '"</b><br /><br />'
                if "def" in the_definition:
                    html_text += _("<b>definition:</b> ") + the_definition[
                        "def"].strip().replace("\n", "<br />") + '<br /><br />'
                if "example" in the_definition:
                    html_text += _("<b>example:</b> ") + \
                        the_definition["example"].strip(
                    ).replace("\n", "<br />")

                yield from bot.coro_send_message(event.conv, html_text)
            else:
                word = urbanDictParser.translations[0]["word"]
                yield from bot.coro_send_message(event.conv, _("{} is blacklisted").format(word))
        else:
            if term:
                yield from bot.coro_send_message(event.conv, _('<i>no urban dictionary definition for "{}"</i>').format(term))
            else:
                yield from bot.coro_send_message(event.conv, _('<i>no term from urban dictionary</i>'))
    elif args[0] == '--blacklist' and is_admin(bot, event):
        term = ' '.join(args[1:]).lower()
        if term not in blacklisted:
            blacklisted.append(term)
            bot.memory.set_by_path(["blacklisted"], blacklisted)
            bot.memory.save()
            yield from bot.coro_send_message(event.conv, _("{} blacklisted").format(term))
        else:
            yield from bot.coro_send_message(event.conv, _("{} is already blacklisted").format(term))
    elif args[0] == '--blacklist' and not is_admin(bot, event):
        yield from bot.coro_send_message(event.conv, _("Ask an admin to do that"))
示例#11
0
def mention(bot, event, *args):
    """alert a @mentioned user"""

    """allow mentions to be disabled via global or per-conversation config"""
    config_mentions_enabled = False if bot.get_config_suboption(
        event.conv.id_, 'mentions.enabled') is False else True
    if not config_mentions_enabled:
        logger.info(
            "mentions explicitly disabled by config for {}".format(event.conv_id))
        return

    """minimum length check for @mention"""
    minimum_length = bot.get_config_suboption(
        event.conv_id, 'mentionminlength')
    if not minimum_length:
        minimum_length = 2
    username = args[0].strip()
    if len(username) < minimum_length:
        logger.debug("@mention from {} ({}) too short (== '{}')".format(
            event.user.full_name, event.user.id_.chat_id, username))
        return

    users_in_chat = event.conv.users
    mention_chat_ids = []

    """sync room support"""
    if bot.get_config_option('syncing_enabled'):
        sync_room_list = bot.get_config_option('sync_rooms')
        if sync_room_list:
            """scan through each room group"""
            for rooms_group in sync_room_list:
                if event.conv_id in rooms_group:
                    """current conversation is part of a syncroom group, add "external" users"""
                    for syncedroom in rooms_group:
                        if event.conv_id is not syncedroom:
                            users_in_chat += bot.get_users_in_conversation(
                                syncedroom)
                    users_in_chat = list(set(users_in_chat))  # make unique
                    logger.debug("@mention in a syncroom: {} user(s) present".format(
                        len(users_in_chat)))
                    break

    """
    /bot mention <fragment> test
    """
    noisy_mention_test = False
    if len(args) == 2 and args[1] == "test":
        noisy_mention_test = True

    initiator_has_dnd = _user_has_dnd(bot, event.user.id_.chat_id)

    """
    quidproquo: users can only @mention if they themselves are @mentionable (i.e. have a 1-on-1 with the bot)
    """
    conv_1on1_initiator = yield from bot.get_1to1(event.user.id_.chat_id)
    if bot.get_config_option("mentionquidproquo"):
        if conv_1on1_initiator:
            if initiator_has_dnd:
                logger.info("quidproquo: user {} ({}) has DND active".format(
                    event.user.full_name, event.user.id_.chat_id))
                if noisy_mention_test or bot.get_config_suboption(event.conv_id, 'mentionerrors'):
                    yield from bot.coro_send_message(
                        event.conv,
                        _("<b>{}</b>, you cannot @mention anyone until your DND status is toggled off.").format(
                            event.user.full_name))
                return
            else:
                logger.debug("quidproquo: user {} ({}) has 1-on-1".format(
                    event.user.full_name, event.user.id_.chat_id))
        else:
            logger.info("quidproquo: user {} ({}) has no 1-on-1".format(
                event.user.full_name, event.user.id_.chat_id))
            if noisy_mention_test or bot.get_config_suboption(event.conv_id, 'mentionerrors'):
                yield from bot.coro_send_message(
                    event.conv,
                    _("<b>{}</b> cannot @mention anyone until they say something to me first.").format(
                        event.user.full_name))
            return

    """track mention statistics"""
    user_tracking = {
        "mentioned": [],
        "ignored": [],
        "failed": {
            "pushbullet": [],
            "one2one": [],
        }
    }

    """
    begin mentioning users as long as they exist in the current conversation...
    """

    conversation_name = bot.conversations.get_name(event.conv)
    logger.info("@mention '{}' in '{}' ({})".format(
        username, conversation_name, event.conv.id_))
    username_lower = username.lower()
    username_upper = username.upper()

    """is @all available globally/per-conversation/initiator?"""
    if username_lower == "all":
        if not bot.get_config_suboption(event.conv.id_, 'mentionall'):

            """global toggle is off/not set, check admins"""
            logger.debug(
                "@all in {}: disabled/unset global/per-conversation".format(event.conv.id_))
            admins_list = bot.get_config_suboption(event.conv_id, 'admins')
            if not is_admin(bot, event):

                """initiator is not an admin, check whitelist"""
                logger.debug("@all in {}: user {} ({}) is not admin".format(
                    event.conv.id_, event.user.full_name, event.user.id_.chat_id))
                all_whitelist = bot.get_config_suboption(
                    event.conv_id, 'mentionallwhitelist')
                if all_whitelist is None or event.user_id.chat_id not in all_whitelist:

                    logger.warning("@all in {}: user {} ({}) blocked".format(
                        event.conv.id_, event.user.full_name, event.user.id_.chat_id))
                    if conv_1on1_initiator:
                        yield from bot.coro_send_message(
                            conv_1on1_initiator,
                            _("You are not allowed to use @all in <b>{}</b>").format(
                                conversation_name))
                    if noisy_mention_test or bot.get_config_suboption(event.conv_id, 'mentionerrors'):
                        yield from bot.coro_send_message(
                            event.conv,
                            _("<b>{}</b> blocked from using <i>@all</i>").format(
                                event.user.full_name))
                    return
                else:
                    logger.info("@all in {}: allowed, {} ({}) is whitelisted".format(
                        event.conv.id_, event.user.full_name, event.user.id_.chat_id))
            else:
                logger.info("@all in {}: allowed, {} ({}) is an admin".format(
                    event.conv.id_, event.user.full_name, event.user.id_.chat_id))
        else:
            logger.info(
                "@all in {}: enabled global/per-conversation".format(event.conv.id_))

    """generate a list of users to be @mentioned"""
    exact_nickname_matches = []
    exact_fragment_matches = []
    mention_list = []
    for u in users_in_chat:

        # mentions also checks nicknames if one is configured
        #  exact matches only! see following IF block
        nickname = ""
        nickname_lower = ""
        if bot.memory.exists(['user_data', u.id_.chat_id, "nickname"]):
            nickname = bot.memory.get_by_path(
                ['user_data', u.id_.chat_id, "nickname"])
            nickname_lower = nickname.lower()

        _normalised_full_name_upper = remove_accents(u.full_name.upper())

        if (username_lower == "all" or

                username_lower in u.full_name.replace(" ", "").lower() or
            username_upper in _normalised_full_name_upper.replace(" ", "") or

                username_lower in u.full_name.replace(" ", "_").lower() or
            username_upper in _normalised_full_name_upper.replace(" ", "_") or

                username_lower == nickname_lower or
                username in u.full_name.split(" ")) and is_admin(bot, event):

            logger.info("user {} ({}) is present".format(
                u.full_name, u.id_.chat_id))

            if u.is_self:
                """bot cannot be @mentioned"""
                logger.debug("suppressing bot mention by {} ({})".format(
                    event.user.full_name, event.user.id_.chat_id))
                continue

            if u.id_.chat_id == event.user.id_.chat_id and username_lower == "all":
                """prevent initiating user from receiving duplicate @all"""
                logger.debug("suppressing @all for {} ({})".format(
                    event.user.full_name, event.user.id_.chat_id))
                continue

            if u.id_.chat_id == event.user.id_.chat_id and not noisy_mention_test:
                """prevent initiating user from mentioning themselves"""
                logger.debug("suppressing @self for {} ({})".format(
                    event.user.full_name, event.user.id_.chat_id))
                continue

            if u.id_.chat_id in mention_chat_ids:
                """prevent most duplicate mentions (in the case of syncouts)"""
                logger.debug("suppressing duplicate mention for {} ({})".format(
                    event.user.full_name, event.user.id_.chat_id))
                continue

            if bot.memory.exists(["donotdisturb"]):
                if _user_has_dnd(bot, u.id_.chat_id):
                    logger.info("suppressing @mention for {} ({})".format(
                        u.full_name, u.id_.chat_id))
                    user_tracking["ignored"].append(u.full_name)
                    continue

            if username_lower == nickname_lower:
                if u not in exact_nickname_matches:
                    exact_nickname_matches.append(u)

            if (username in u.full_name.split(" ") or
                    username_upper in _normalised_full_name_upper.split(" ")):

                if u not in exact_fragment_matches:
                    exact_fragment_matches.append(u)

            if u not in mention_list:
                mention_list.append(u)

    if len(exact_nickname_matches) == 1:
        """prioritise exact nickname matches"""
        logger.info("prioritising nickname match for {}".format(
            exact_nickname_matches[0].full_name))
        mention_list = exact_nickname_matches
    elif len(exact_fragment_matches) == 1:
        """prioritise case-sensitive fragment matches"""
        logger.info("prioritising single case-sensitive fragment match for {}".format(
            exact_fragment_matches[0].full_name))
        mention_list = exact_fragment_matches
    elif len(exact_fragment_matches) > 1 and len(exact_fragment_matches) < len(mention_list):
        logger.info("prioritising multiple case-sensitive fragment match for {}".format(
            exact_fragment_matches[0].full_name))
        mention_list = exact_fragment_matches

    if len(mention_list) > 1 and username_lower != "all":

        send_multiple_user_message = True

        if bot.memory.exists(['user_data', event.user.id_.chat_id, "mentionmultipleusermessage"]):
            send_multiple_user_message = bot.memory.get_by_path(
                ['user_data', event.user.id_.chat_id, "mentionmultipleusermessage"])

        if send_multiple_user_message or noisy_mention_test:
            if conv_1on1_initiator:
                text_html = _('{} users would be mentioned with "@{}"! Be more specific. List of matching users:<br />').format(
                    len(mention_list), username, conversation_name)

                for u in mention_list:
                    text_html += u.full_name
                    if bot.memory.exists(['user_data', u.id_.chat_id, "nickname"]):
                        text_html += ' (' + bot.memory.get_by_path(
                            ['user_data', u.id_.chat_id, "nickname"]) + ')'
                    text_html += '<br />'

                text_html += "<br /><em>To toggle this message on/off, use <b>/bot bemorespecific</b></em>"

                yield from bot.coro_send_message(conv_1on1_initiator, text_html)

        logger.warning(
            "@{} not sent due to multiple recipients".format(username_lower))
        return  # SHORT-CIRCUIT

    """support for reprocessor
    override the source name by defining event._external_source"""
    source_name = event.user.full_name
    if hasattr(event, '_external_source'):
        source_name = event._external_source

    """send @mention alerts"""
    for u in mention_list:
        alert_via_1on1 = True

        """pushbullet integration"""
        if bot.memory.exists(['user_data', u.id_.chat_id, "pushbullet"]):
            pushbullet_config = bot.memory.get_by_path(
                ['user_data', u.id_.chat_id, "pushbullet"])
            if pushbullet_config is not None:
                if pushbullet_config["api"] is not None:
                    success = False
                    try:
                        pb = PushBullet(pushbullet_config["api"])
                        push = pb.push_note(
                            _("{} mentioned you in {}").format(
                                source_name,
                                conversation_name),
                            event.text)
                        if isinstance(push, tuple):
                            # backward-compatibility for pushbullet library <
                            # 0.8.0
                            success = push[0]
                        elif isinstance(push, dict):
                            success = True
                        else:
                            raise TypeError(
                                "unknown return from pushbullet library: {}".format(push))
                    except Exception as e:
                        logger.exception("pushbullet error")

                    if success:
                        user_tracking["mentioned"].append(u.full_name)
                        logger.info("{} ({}) alerted via pushbullet".format(
                            u.full_name, u.id_.chat_id))
                        alert_via_1on1 = False  # disable 1on1 alert
                    else:
                        user_tracking["failed"][
                            "pushbullet"].append(u.full_name)
                        logger.warning("pushbullet alert failed for {} ({})".format(
                            u.full_name, u.id_.chat_id))

        if alert_via_1on1:
            """send alert with 1on1 conversation"""
            conv_1on1 = yield from bot.get_1to1(u.id_.chat_id)
            if conv_1on1:
                yield from bot.coro_send_message(
                    conv_1on1,
                    _("<b>{}</b> @mentioned you in <i>{}</i>:<br />{}").format(
                        source_name,
                        conversation_name,
                        event.text))  # prevent internal parser from removing <tags>
                mention_chat_ids.append(u.id_.chat_id)
                user_tracking["mentioned"].append(u.full_name)
                logger.info("{} ({}) alerted via 1on1 ({})".format(
                    u.full_name, u.id_.chat_id, conv_1on1.id_))
            else:
                user_tracking["failed"]["one2one"].append(u.full_name)
                if bot.get_config_suboption(event.conv_id, 'mentionerrors'):
                    yield from bot.coro_send_message(
                        event.conv,
                        _("@mention didn't work for <b>{}</b>. User must say something to me first.").format(
                            u.full_name))
                logger.warning("user {} ({}) could not be alerted via 1on1".format(
                    u.full_name, u.id_.chat_id))

    if noisy_mention_test:
        text_html = _("<b>@mentions:</b><br />")
        if len(user_tracking["failed"]["one2one"]) > 0:
            text_html = text_html + \
                _("1-to-1 fail: <i>{}</i><br />").format(
                    ", ".join(user_tracking["failed"]["one2one"]))
        if len(user_tracking["failed"]["pushbullet"]) > 0:
            text_html = text_html + _("PushBullet fail: <i>{}</i><br />").format(
                ", ".join(user_tracking["failed"]["pushbullet"]))
        if len(user_tracking["ignored"]) > 0:
            text_html = text_html + \
                _("Ignored (DND): <i>{}</i><br />").format(
                    ", ".join(user_tracking["ignored"]))
        if len(user_tracking["mentioned"]) > 0:
            text_html = text_html + \
                _("Alerted: <i>{}</i><br />").format(
                    ", ".join(user_tracking["mentioned"]))
        else:
            text_html = text_html + \
                _("Nobody was successfully @mentioned ;-(<br />")

        if len(user_tracking["failed"]["one2one"]) > 0:
            text_html = text_html + \
                _("Users failing 1-to-1 need to say something to me privately first.<br />")

        yield from bot.coro_send_message(event.conv, text_html)
示例#12
0
文件: irc.py 项目: memery/phoenicz
def handle(line, settings, state, log=logger.log):
    # TODO: Write docstring about how this yields responses
    user, command, arguments = ircparser.split(line)
    nick = ircparser.get_nick(user)

    if command == 'PING':
        yield 'PONG :' + arguments[0]

    if command == 'PONG':
        state['pinged'] = False

    if command == '433':
        def new_nick(nick):
            nick = nick[:min(len(nick), 6)] # determine how much to shave off to make room for random chars
            return '{}_{}'.format(nick, ''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(2)))

        log('warning', '[statekeeping] Nick {} already in use, trying another one.'.format(state['nick']))
        state['nick'] = new_nick(settings['irc']['nick'])
        yield 'NICK {}'.format(state['nick'])

    if command == 'JOIN' and nick == state['nick']:
        # TODO: Fancy logging
        print('--> joined {}'.format(arguments[0]))

        if state['joined_channel']:
            # TODO: raise some sort of illegal state exception. remember to test for it
            # then what? have we joined two channels? what the shit are we supposed to do?
            pass

        state['joined_channel'] = arguments[0]

    if command == 'KICK' and arguments[1] == state['nick']:
        log('warning', '[statekeeping] Kicked from channel {} because {}'.format(arguments[0], ' '.join(arguments[1:])))

        if arguments[0] != state['joined_channel']:
            # TODO: raise some sort of illegal state exception
            pass

        state['joined_channel'] = None
        settings['irc']['channel'] = None

    if command == 'PRIVMSG':
        # Make the author the target for replies if it is a private message
        channel = nick if arguments[0] == state['nick'] else arguments[0]

        message = ' '.join(arguments[1:])
        # TODO: Turn this into some sort of cooler logging
        print('{}> {}'.format(channel, message))

        # TODO: Better admin shit, this is just poc/temporary
        if admin.is_admin(user):
            admin_result = admin.parse_admin_command(message, state['nick'])
            if admin_result:
                yield ircparser.make_privmsg(channel, admin_result)

        if message == 'hello, world':
            yield ircparser.make_privmsg(channel, 'why, hello!')
    else:
        log('raw', line)


    # TODO: Fix the state object so this isn't needed
    if 'joined_channel' not in state:
        state['joined_channel'] = None

    if not state['joined_channel'] and settings['irc']['channel']:
        yield 'JOIN {}'.format(settings['irc']['channel'])