# Path: cardinal/decorators.py
# def command(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Command must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.commands = triggers
#         return f
# 
#     return wrap
# 
# def help(lines):
#     # For backwards compatibility
#     if isinstance(lines, str):
#         lines = [lines]
# 
#     if not isinstance(lines, list):
#         raise TypeError("Help must be a help string or list of help strings")
# 
#     def wrap(f):
#         # Create help list or prepend to it
#         if not hasattr(f, 'help'):
#             f.help = lines
#         else:
#             f.help = lines + f.help
# 
#         return f
# 
#     return wrap
# 
# def event(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Event must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.events = triggers
#         return f
# 
#     return wrap
# 
# Path: cardinal/util.py
# def is_action(message):
#     """Checks if a message is a /me message."""
#     return message.startswith("\x01ACTION")
# 
# def parse_action(nick, message):
#     """Parses a /me message like an IRC client might.
# 
#     e.g. "/me dances." -> "* Cardinal dances."
#     """
#     if not is_action(message):
#         raise ValueError("This message is not an ACTION message")
# 
#     message = message[len("\x01ACTION "):]
#     if message and message[-1] == "\x01":
#         message = message[:-1]
# 
#     return "* {}{}".format(
#         nick,
#         " " + message if message else "",
#     )
# 
# def strip_formatting(line):
#     """Removes mIRC control code formatting"""
#     return re.sub(r"(?:\x03\d\d?,\d\d?|\x03\d\d?|[\x01-\x1f])", "", line)


from datetime import datetime, timezone
from cardinal.decorators import command, help, event
from cardinal.util import (
    is_action,
    parse_action,
    strip_formatting,
)
            message += "{} set {}'s topic to \"{}\".".format(
                nick,
                params[0],
                strip_formatting(params[1]),
            )
        elif action == QUIT:
            message += "{} quit{}.".format(
                nick,
                (" ({})".format(strip_formatting(params[0]))
                 if params[0] else
                 ""),
            )

        return message

    @command('seen')
    @help("Returns the last time a user was seen, and their last action.")
    @help("Syntax: .seen <user>")
    def seen(self, cardinal, user, channel, msg):
        try:
            nick = msg.split(' ')[1]
        except IndexError:
            return cardinal.sendMsg(channel, 'Syntax: .seen <user>')

        if nick.lower() == user.nick.lower():
            cardinal.sendMsg(channel, "{}: Don't be daft.".format(user.nick))
            return

        cardinal.sendMsg(channel, self.format_seen(nick))


# Path: cardinal/decorators.py
# def command(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Command must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.commands = triggers
#         return f
# 
#     return wrap
# 
# def help(lines):
#     # For backwards compatibility
#     if isinstance(lines, str):
#         lines = [lines]
# 
#     if not isinstance(lines, list):
#         raise TypeError("Help must be a help string or list of help strings")
# 
#     def wrap(f):
#         # Create help list or prepend to it
#         if not hasattr(f, 'help'):
#             f.help = lines
#         else:
#             f.help = lines + f.help
# 
#         return f
# 
#     return wrap
# 
# def event(triggers):
#     if isinstance(triggers, str):
#         triggers = [triggers]
# 
#     if not isinstance(triggers, list):
#         raise TypeError("Event must be a trigger string or list of triggers")
# 
#     def wrap(f):
#         f.events = triggers
#         return f
# 
#     return wrap
# 
# Path: cardinal/util.py
# def is_action(message):
#     """Checks if a message is a /me message."""
#     return message.startswith("\x01ACTION")
# 
# def parse_action(nick, message):
#     """Parses a /me message like an IRC client might.
# 
#     e.g. "/me dances." -> "* Cardinal dances."
#     """
#     if not is_action(message):
#         raise ValueError("This message is not an ACTION message")
# 
#     message = message[len("\x01ACTION "):]
#     if message and message[-1] == "\x01":
#         message = message[:-1]
# 
#     return "* {}{}".format(
#         nick,
#         " " + message if message else "",
#     )
# 
# def strip_formatting(line):
#     """Removes mIRC control code formatting"""
#     return re.sub(r"(?:\x03\d\d?,\d\d?|\x03\d\d?|[\x01-\x1f])", "", line)


# Path: plugins/seen/plugin.py
from datetime import datetime, timezone
from cardinal.decorators import command, help, event
from cardinal.util import (
    is_action,
    parse_action,
    strip_formatting,
)

            message += "{} set {}'s topic to \"{}\".".format(
                nick,
                params[0],
                strip_formatting(params[1]),
            )
        elif action == QUIT:
            message += "{} quit{}.".format(
                nick,
                (" ({})".format(strip_formatting(params[0]))
                 if params[0] else
                 ""),
            )

        return message

    @command('seen')
    @help("Returns the last time a user was seen, and their last action.")
    @help("Syntax: .seen <user>")
    def seen(self, cardinal, user, channel, msg):
        try:
            nick = msg.split(' ')[1]
        except IndexError:
            return cardinal.sendMsg(channel, 'Syntax: .seen <user>')

        if nick.lower() == user.nick.lower():
            cardinal.sendMsg(channel, "{}: Don't be daft.".format(user.nick))
            return

        cardinal.sendMsg(channel, self.format_seen(nick))


    @command('tell')