Ejemplo n.º 1
0
 def command(cls, irc_c, msg, cmd):
     cmd.expandargs(["add a",
                     "remove r",
                     "list l"])
     if len(cmd.args['root']) > 0:
         nick = cmd.args['root'][0]
     else:
         nick = msg.sender
     # 1. Check if this is the right nick
     # get the user ID
     user_id = DB.get_user_id(nick)
     if user_id is None:
         msg.reply("I don't know anyone called '{}'.".format(nick))
         return
     # 2. Add new aliases
     if 'add' in cmd:
         if nick.lower() != msg.sender.lower() and not defer.controller(cmd):
             raise CommandError("You can't add an alias for someone else.")
         aliases = cmd['add']
         # db has add_alias, but that needs user ID
         for alias in aliases:
             if alias.lower() == msg.sender.lower():
                 continue
             if DB.add_alias(user_id, alias, 1):
                 msg.reply("{} already has the alias {}!".format(nick,alias))
         msg.reply("Added aliases to {}: {}".format(nick, ", ".join(aliases)))
         irc_c.PRIVMSG(CONFIG.home, "{} added alias {}".format(nick,alias))
     if 'remove' in cmd:
         if nick.lower() != msg.sender.lower() and not defer.controller(cmd):
             raise CommandError("You can't remove an alias from someone else.")
         aliases = cmd['remove']
         # db has add_alias, but that needs user ID
         for alias in aliases:
             if not DB.remove_alias(user_id, alias, 1):
                 msg.reply("{} didn't have the alias {}!".format(nick,alias))
         msg.reply("Removed aliases from {}: {}".format(nick, ", ".join(aliases)))
     if 'list' in cmd:
         # get all aliases associated with the user
         aliases = DB.get_aliases(user_id)
         msg.reply("I've seen {} go by the names: {}"
                   .format(nick if nick != msg.sender else "you",
                           ", ".join(aliases)))
     if not any(['add' in cmd,'remove' in cmd,'list' in cmd]):
         raise CommandError("Add or remove aliases to a nick with --add "
                            "and --remove. See all nicks with --list")
Ejemplo n.º 2
0
 def issue_raw(irc_c, msg, cmd):
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     if cmd.args['root'][1][0] == '/':
         cmd.args['root'].pop(0)
     msg.reply("Issuing that...")
     msg.reply(" ".join(cmd.args['root'])[1:])
     irc_c.RAW(" ".join(cmd.args['root'])[1:])
Ejemplo n.º 3
0
 def command(cls, irc_c, msg, cmd):
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     if (defer.check(cmd, 'jarvis', 'Secretary_Helen')):
         return
     msg.reply(kill_bye())
     irc_c.RAW("QUIT See you on the other side")
     irc_c.client.die()
Ejemplo n.º 4
0
 def command(cls, irc_c, msg, cmd):
     if (defer.check(cmd, 'jarvis', 'Secretary_Helen')):
         return
     # reboot the bot completely
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     msg.reply("Rebooting...")
     irc_c.RAW("QUIT Rebooting, will be back soon!")
     os.execl(sys.executable, sys.executable, *sys.argv)
Ejemplo n.º 5
0
 def command(cls, irc_c, msg, cmd):
     # arg 1 should be a url name
     if 'sample' in cmd:
         samples = [
             'scp-173', 'scp-1111', 'scp-3939', 'cone', 'scp-series',
             'listpages-magic-and-you', 'scp-4205', 'omega-k',
             'component:ar-theme', 'fragment:scp-3939-64'
         ]
         msg.reply("Adding sample data...")
         propagate.get_wiki_data_for(samples, reply=msg.reply)
     elif 'tales' in cmd:
         if not defer.controller(cmd):
             raise CommandError("I'm afriad I can't let you do that.")
         msg.reply("Fetching all tales... this will take a few minutes.")
         tales = SCPWiki.select({'tags_all': ['tale']})
         pprint(tales)
         propagate.get_wiki_data_for(tales, reply=msg.reply)
     elif 'all' in cmd:
         if not defer.controller(cmd):
             raise CommandError("I'm afriad I can't let you do that.")
         msg.reply("Propagating all pages...")
         propagate.get_all_pages(reply=msg.reply)
     elif 'metadata' in cmd:
         meta_urls = [
             'attribution-metadata', 'scp-series', 'scp-series-2',
             'scp-series-3', 'scp-series-4', 'scp-series-5', 'scp-series-6'
         ]
         # meta_urls = ['attribution-metadata']
         # XXX TODO replace with getting pages tagged "metadata"
         msg.reply("Propagating metadata...")
         for url in meta_urls:
             propagate.get_metadata(url, reply=msg.reply)
     elif len(cmd.args['root']) > 0:
         propagate.get_wiki_data_for(cmd.args['root'], reply=msg.reply)
     else:
         raise CommandError("Bad command")
     msg.reply("Done!")
Ejemplo n.º 6
0
 def command(cls, irc_c, msg, cmd):
     """Update from github"""
     if (defer.check(cmd, 'jarvis', 'Secretary_Helen')):
         return
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     msg.reply("Updating...")
     try:
         g = git.cmd.Git(".")
         g.pull()
     except Exception as e:
         msg.reply("Update failed.")
         raise
     msg.reply("Update successful - now would be a good time to reboot.")
Ejemplo n.º 7
0
 def command(cls, irc_c, msg, cmd):
     cmd.expandargs(["obfuscate o", "colour color c"])
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     if len(cmd.args['root']) == 0:
         raise CommandError("Must specify a recipient and message")
     if len(cmd.args['root']) == 1:
         raise CommandError("Must specify a message")
     if cmd.args['root'][0][0] == '/' or cmd.args['root'][1][0] == '/':
         # This is an IRC command
         say.issue_raw(irc_c, msg, cmd)
     else:
         message = " ".join(cmd.args['root'][1:])
         if 'obfuscate' in cmd and msg.raw_channel is not None:
             message = gib.obfuscate(message,
                                     DB.get_aliases(None) + ["ops"])
         if 'colour' in cmd:
             print(nickColor(message))
             msg.reply("Printed that to console")
         irc_c.PRIVMSG(cmd.args['root'][0], message)
         if not cmd.args['root'][0] == msg.raw_channel:
             msg.reply("Saying that to {}".format(cmd.args['root'][0]))
Ejemplo n.º 8
0
 def command(cls, irc_c, msg, cmd):
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     if cls.has_refactored:
         raise CommandError("Already refactored once this reload.")
     if 'callback' in cmd:
         print(cmd['callback'])
         if cmd['callback'][0] == "msg.reply":
             callback = msg.reply
         else:
             raise CommandError("Unknown callback")
     else:
         callback = None
     try:
         if 'sql' in cmd:
             DB.issue(" ".join(cmd['sql']), callback=callback)
         else:
             refactor.refactor_database(irc_c)
             cls.has_refactored = True
     except:
         msg.reply("Refactoring failed.")
         raise
     msg.reply("Refactoring succeeded.")
Ejemplo n.º 9
0
    def command(cls, irc_c, msg, cmd):
        if (defer.check(cmd, 'jarvis')): return
        cmd.expandargs([
            "no-cache n", "user u author a", "channel c", "size s",
            "roulette r", "regex x", "minlength length l", "me", "help h"
        ])
        if 'help' in cmd:
            msg.reply("Usage: .gib [--channel #channel] [--user user] "
                      "[--no-cache]")
            return
        channels = [msg.raw_channel]
        users = []
        # root has 1 num, 1 string, 1 string startswith #
        for arg in cmd.args['root']:
            if arg.startswith('#'):
                raise CommandError("Try .gib -c {}".format(arg))
            else:
                raise CommandError("Try .gib -u {}".format(arg))
        if 'channel' in cmd:
            if len(cmd['channel']) == 0:
                raise CommandError("When using the --channel/-c filter, "
                                   "at least one channel must be specified")
            if cmd['channel'][0] == "all":
                if defer.controller(cmd):
                    channels = DB.get_all_channels()
                    msg.reply("Gibbing from all channels I'm in:")
                else:
                    msg.reply("Gibbing from all channels you're in:")
                    # get all channels this user is in
                    raise MyFaultError("This isn't implemented yet.")
            else:
                for channel in cmd['channel']:
                    if not channel.startswith('#'):
                        raise CommandError("Channel names must start with #.")
                channels = cmd['channel']
        elif msg.raw_channel is None:
            raise CommandError("Specify a channel to gib from with "
                               "--channel/-c")
        if 'user' in cmd:
            if len(cmd['user']) == 0:
                raise CommandError("When using the --user/-u filter, "
                                   "at least one user must be specified")
            users = cmd['user']
        if 'size' in cmd:
            try:
                cls.size = int(cmd['size'][0])
            except ValueError:
                raise CommandError("Sizes must be numbers")
        else:
            cls.size = 3
        # ignore gib cache?
        if 'no-cache' in cmd:
            cls.nocache = True
        else:
            cls.nocache = False
        if 'limit' in cmd:
            try:
                limit = int(cmd['limit'][0])
            except ValueError:
                raise CommandError(
                    "When using --limit, the limit must be an int")
            if limit < 200:
                raise CommandError("When using --limit, the limit cannot be "
                                   "lower than 200")
        else:
            limit = CONFIG['gib']['limit']
            if not limit:
                limit = 5000
        if 'roulette' in cmd:
            if len(cmd['roulette']) == 0:
                raise CommandError("When using roulette mode, you must "
                                   "specify a roulette type")
            roulette_type = cmd['roulette'][0]
            if roulette_type not in ['video', 'image', 'youtube', 'yt']:
                raise CommandError("The roulette type must be either "
                                   "'image' or one of 'video','youtube','yt'")
            limit = None
        # can only gib a channel both the user and the bot are in
        for channel in channels:
            if channel is msg.raw_channel:
                continue
            if msg.raw_channel is not None \
               and cmd['channel'][0] != 'all' \
               and not all(x in DB.get_channel_members(channel)
                           for x in [msg.sender, CONFIG.nick]):
                raise CommandError("Both you and the bot must be in a channel "
                                   "in order to gib it.")
            if msg.raw_channel is not None \
               and channel != msg.raw_channel \
               and not defer.controller(cmd):
                raise CommandError("You can only gib the current channel (or "
                                   "any channel from PMs)")
        # Run a check to see if we need to reevaluate the model or not
        if cls.channels == channels and cls.users == users \
           and not cls.nocache:
            print("Reusing Markov model")
        else:
            cls.model = None
            cls.channels = channels
            if len(cls.channels) == 0: cls.channels = [msg.raw_channel]
            cls.users = users
            if len(cls.users) == 0: cls.users = [None]
        # are we gibbing or rouletting?
        if 'roulette' in cmd:
            urls = cls.roulette(roulette_type)
            msg.reply("{} {} · ({} link{} found)".format(
                emojize(":game_die:"), random.choice(urls), len(urls),
                ("s" if len(urls) > 1 else "")))
            return
        if 'regex' in cmd:
            if len(cmd['regex']) == 0:
                raise CommandError("When using the regex filter, you must "
                                   "specify a regex")
            patterns = cmd['regex']
            for pattern in patterns:
                try:
                    re.compile(pattern)
                except re.error as e:
                    raise CommandError("'{}' isn't a valid regular "
                                       "expression: {}".format(pattern, e))
        else:
            patterns = []
        if 'me' in cmd:
            patterns.append(r"\u0001ACTION ")
        if 'minlength' in cmd:
            if len(cmd['minlength']) == 0:
                raise CommandError("When using the minimum length modifier "
                                   "(--length/-l), you must specify a "
                                   "minimum length")
            minlength = cmd['minlength'][0]
            if not isint(minlength):
                raise CommandError("When using the minimum length modifier "
                                   "(--length/-l), the minimum length must be "
                                   "an integer")
            minlength = int(minlength)
        else:
            minlength = 0
        # gibbing:
        try:
            sentence = cls.get_gib_sentence(limit=limit,
                                            minlength=minlength,
                                            patterns=patterns)
            if sentence is None:
                raise AttributeError
        except (RuntimeError, AttributeError):
            raise MyFaultError(
                "Looks like {} spoken enough in {} just yet.{}".format(
                    ("you haven't" if msg.sender in users and len(users) == 1
                     else "nobody has" if len(users) == 0 else "{} hasn't".
                     format(users[0]) if len(users) == 1 else "they haven't"),
                    (channels[0] if len(channels) == 1
                     and channels[0] == msg.raw_channel else "that channel"
                     if len(channels) == 1 else "those channels"),
                    " ({} messages)".format(
                        len(cls.model.to_dict()['parsed_sentences']) if cls.
                        model is not None else 0)))
        # first: remove a ping at the beginning of the sentence
        pattern = r"^(\S+[:,]\s+)(.*)$"
        match = re.match(pattern, sentence)
        if match:
            sentence = match.group(2).strip()
        # second: modify any words that match the names of channel members
        sentence = gib.obfuscate(sentence,
                                 DB.get_channel_members(msg.raw_channel))
        # match any unmatched pairs
        sentence = gib.bracketify(sentence, (r"\"\b", "\""),
                                  (r"\b[.!?]*\"", "\""))
        sentence = gib.bracketify(sentence, (r"`\b", "`"), (r"\b[.!?]*`", "`"))
        sentence = gib.bracketify(sentence, (r"\(", "("), (r"\)", ")"))
        sentence = gib.bracketify(sentence, (r"\[", "["), (r"\}", "]"))
        sentence = gib.bracketify(sentence, (r"\{", "{"), (r"\}", "}"))

        cmd.command = cmd.command.lower()
        if "oo" in cmd.command:
            sentence = re.sub(r"[aeiou]", "oob", sentence)
        elif "o" in cmd.command:
            sentence = re.sub(r"[aeiou]", "ob", sentence)
        if cmd.command.startswith("b") and cmd.command.endswith("g"):
            sentence = sentence.upper()
        msg.reply(sentence)
Ejemplo n.º 10
0
 def command(cls, irc_c, msg, cmd):
     if not defer.controller(cmd):
         raise CommandError("I'm afriad I can't let you do that.")
         return
     if len(cmd.args['root']) < 1:
         raise CommandError("Must specify the wiki to analyse")
     if len(cmd.args['root']) == 2:
         abort_limit = int(cmd.args['root'][1])
     else:
         abort_limit = -1
     if msg.sender != 'Croquembouche':
         msg.reply("Only Croquembouche can do that, sorry!")
         return
     target = WikidotAPI(cmd.args['root'][0])
     msg.reply("Fetching data from {}...".format(cmd.args['root'][0]))
     # 1. get a list of all pages
     # 2. get the filenames associated with those pages
     # 3. get the file metas associated with those files
     # 4. save everything to a spreadsheet (csv would be fine)
     # ---
     # 1. get a list of all pages
     pages = target.select({})
     msg.reply("I found {} pages.".format(len(pages)))
     msg.reply("Getting file names...")
     # pages is now a list of page names
     # make a list for files
     files_list = [] # this is the master list
     percents = [math.ceil(i*len(pages)) for i in numpy.linspace(0, 1, 101)]
     # get select_files per 1 page
     for i,page in enumerate(pages):
         if i in percents:
             msg.reply("{}% complete".format(percents.index(i)))
         try:
             pages[i] = {'page': page, 'files': target.select_files({'page': page})}
         except Exception as e:
             msg.reply("Error on {}: {}".format(page['page'],str(e)))
         print("Found {} files for {}".format(len(pages[i]['files']),page))
         if i == abort_limit:
             msg.reply("Process aborted after {} pages".format(abort_limit))
             break
     # TODO loop over pages and remove errored entries
     msg.reply("Getting info for files...")
     for i,page in enumerate(pages):
         if i in percents:
             msg.reply("{}% complete".format(percents.index(i)))
         # for each page, get_files_meta can take up to 10 files
         # no problem for 10 or less files - what to do when there's more?
         # chunks the file into 10s but then we need to get the index?
         # or do we
         for files in chunks(page['files'], 10):
             print(files)
             try:
                 f = target.get_files_meta({'page': page['page'],
                                            'files': files})
                 pprint(f)
                 for filename in files:
                     f[filename]['page'] = page['page']
                     files_list.append(f[filename])
             except Exception as e:
                 msg.reply("Error on {} of {}: {}".format(files,page['page'],str(e)))
         if i == abort_limit:
             msg.reply("Process aborted after {} pages".format(abort_limit))
             break
     msg.reply("List of files created.")
     msg.reply("Outputting to .csv...")
     with open("wiki_analysis.csv",'w') as f:
         w = csv.DictWriter(f, files_list[0].keys())
         w.writeheader()
         w.writerows(files_list)
     msg.reply("Done.")
Ejemplo n.º 11
0
    def command(cls, irc_c, msg, cmd):
        """Record and broadcast messages"""
        if not defer.controller(cmd):
            raise CommandError("You're not authorised to do that")
        cmd.expandargs(["output o", "format f", "restrict-channel-name"])
        # get the action - start, stop or status
        if len(cmd.args['root']) == 0:
            raise CommandError("Usage: .record [start|stop|status|page] "
                               "[--output location] [--format format]")
        if len(cmd.args['root']) > 1:
            raise CommandError("Only one action can be taken at a time.")
        action = cmd.args['root'][0]
        if action == 'page':
            msg.reply(
                "Output page: http://topia.wikidot.com/tars:recording-output")
            return
        if action == 'status':
            if msg.raw_channel in cls.recording_channels():
                msg.reply("Currently recording in this channel. "
                          "Use `.record stop` to stop the recording.")
            else:
                msg.reply("Not currently recording in this channel.")
            if defer.controller(cmd) and msg.raw_channel is None:
                # if a controller asks in pm, show all channels
                msg.reply("Currently recording in: {}".format(", ".join(
                    [s['channel'] for s in cls.settings if s['recording']])))
            return
        elif action == 'start':
            if msg.raw_channel is None:
                raise CommandError("You can't record PMs.")
            if msg.raw_channel in cls.recording_channels():
                raise CommandError("Already recording in {}".format(
                    msg.raw_channel))
            else:
                msg.reply("Starting recording messages in {}".format(
                    msg.raw_channel))
                if 'restrict-channel-name' in cmd:
                    msg.reply("I will hide the channel name from the output.")
        elif action == 'stop':
            if msg.raw_channel not in cls.recording_channels():
                raise CommandError("Not recording in {}".format(
                    msg.raw_channel))
            else:
                msg.reply("Stopping recording in {}".format(msg.raw_channel))
                if 'restrict-channel-name' in cmd:
                    msg.reply("I will hide the channel name from the output.")
        else:
            raise CommandError("Action must be one of start, stop, status")
        # get arguments and save to vars
        output_location = None
        if 'output' in cmd:
            if cmd['output'][0] not in ['topia', 'here', 'both']:
                raise CommandError(
                    "Output location must be topia, here or both")
            output_location = cmd['output'][0]
        output_format = None
        if 'format' in cmd:
            if cmd['format'][0] not in ['json', 'txt', 'ftml']:
                raise CommandError("Format type must be json, txt or ftml")
            output_format = cmd['format'][0]

        # after everything else, set this channel as recording or not
        if action == 'start':
            # get the most recent message id in this channel
            start_id = DB.get_most_recent_message(msg.raw_channel)
            # add this channel to the settings list
            cls.settings.append({
                'channel': msg.raw_channel,
                'recording': True,
                'location': output_location,
                'format': output_format,
                'start_id': start_id,
                'hide': 'restrict-channel-name' in cmd
            })
        if action == 'stop':
            sett = [
                s for s in cls.settings if s['channel'] == msg.raw_channel
            ][0]
            end_id = DB.get_most_recent_message(msg.raw_channel)
            messages = DB.get_messages_between(msg.raw_channel,
                                               sett['start_id'], end_id)
            if sett['location'] in ['topia', None]:
                msg.reply("Uploading {} messages to topia...".format(
                    len(messages)))
                # get page content
                page = Topia.get_page({'page': "tars:recording-output"})
                content = page['content']
                content += "\r\n\r\n====\r\n\r\n"
                content += "+ Recording on {} from {}".format(
                    datetime.today().strftime('%Y-%m-%d'),
                    sett['channel'] if not sett['hide'] else "[REDACTED]")
                content += "\r\n\r\n"
                for message in messages:
                    if message['kind'] == 'PRIVMSG':
                        content += "||~ {} ||~ ##{}|{}## || {} ||".format(
                            (datetime.fromtimestamp(
                                message['timestamp']).strftime("%H:%M:%S")),
                            nickColor(message['sender'],
                                      True), message['sender'],
                            message['message'].replace("||", "@@||@@"))
                    elif message['kind'] == 'NICK':
                        content += "||~ {} ||||~ ##{}|{}## → ##{}|{}## ||".format(
                            (datetime.fromtimestamp(
                                message['timestamp']).strftime("%H:%M:%S")),
                            nickColor(message['sender'],
                                      True), message['sender'],
                            nickColor(message['message'],
                                      True), message['message'])
                    elif message['kind'] in ['JOIN', 'PART', 'QUIT']:
                        content += "||~ {} ||||~ {} ##{}|{}## {} ||".format(
                            (datetime.fromtimestamp(
                                message['timestamp']).strftime("%H:%M:%S")),
                            "→" if message['kind'] == 'JOIN' else "←",
                            nickColor(message['sender'],
                                      True), message['sender'],
                            "joined" if message['kind'] == 'JOIN' else "left")
                    else:
                        content += "|||||| Error code {} ||".format(
                            message['id'])
                    content += "\r\n"
                content += "\r\n"
                okchars = string.printable + "→←"
                content = "".join(filter(lambda x: x in okchars, content))
                # then format for wikidot
                Topia.save_page({
                    'page': "tars:recording-output",
                    'content': content,
                    'revision_comment': "New recording",
                    'notify_watchers': "true"
                })
                msg.reply(
                    "Done! http://topia.wikidot.com/tars:recording-output")
            else:
                raise MyFaultError("Unknown location")
            cls.settings.remove(sett)
Ejemplo n.º 12
0
    def command(cls, irc_c, msg, cmd):
        """Ping everyone in the channel"""
        cmd.expandargs([
            "message msg m",  # message to be PM'd
            "target t",  # channel op level target
            "channel c",  # channel to get names from
            "help h",
        ])

        # TODO remove this check in the argparse refactor
        if len(cmd.args['root']) > 0 or 'help' in cmd:
            raise CommandError("Usage: ..pingall [--target level] [--message "
                               "message]. If -m is not set, ping will happen "
                               "in this channel. If -m is set, message will "
                               "be sent to users in PM.")

        # TODO extend this to channel operator
        if not defer.controller(cmd):
            raise CommandError("You're not authorised to do that")

        cmd.expandargs(["channel c"])
        if 'channel' in cmd:
            if not defer.controller(cmd):
                raise MyFaultError("You're not authorised to extract the "
                                   "nicks of another channel")
            channel = cmd['channel'][0]
        else:
            channel = msg.raw_channel
        if 'target' in cmd:
            if len(cmd['target']) != 1:
                raise CommandError("Specify a target as a channel user mode "
                                   "symbol: one of +, %, @, &, ~")
            if not cmd['target'][0] in '+%@&~' and len(cmd['target'][0]) == 1:
                raise CommandError("When using the --target/-t argument, the "
                                   "target must be a channel user mode: one "
                                   "of +, %, @, &, ~")
        # Issue a fresh NAMES request and await the response
        defer.get_users(irc_c, channel)
        try:
            response = await_signal(irc_c, 'NAMES_RESPONSE', timeout=5.0)
            # returned data is the channel name
            assert response == channel
        except (TimeoutError, AssertionError):
            # response to success/failure is the same, so doesn't matter
            pass
        finally:
            members = DB.get_occupants(channel, True, levels=True)
        if 'target' in cmd:
            modes = '+%@&~'
            members = [
                nick for nick, mode in members if mode is not None
                and modes.find(mode) >= modes.find(cmd['target'][0])
            ]
        else:
            members = [nick for nick, mode in members]
        if 'message' in cmd:
            message = " ".join(cmd.args['message'])
            for member in members:
                irc_c.PRIVMSG(
                    member,
                    "{} (from {} in {})".format(" ".join(cmd.args['root']),
                                                msg.sender, msg.raw_channel))
            msg.reply("Message sent to selected users.")
        else:
            msg.reply("{}: ping!".format(", ".join(members)))
Ejemplo n.º 13
0
 def command(cls, irc_c, msg, cmd):
     cmd.expandargs(["table tables t",
                     "user users u"])
     # No argument given - show the db structure
     if len(cmd.args) == 1:
         msg.reply("https://raw.githubusercontent.com/"
                   "rossjrw/tars/master/database.png")
         return
     # Table - print a list of tables, or a given table
     if 'table' in cmd:
         if len(cmd['table']) > 0:
             # print a specific table
             msg.reply("Printing contents of table {} to console."
                       .format(cmd['table'][0]))
             DB.print_one_table(cmd['table'][0])
         else:
             # print a list of all tables
             tables = DB.get_all_tables()
             msg.reply("Printed a list of tables to console. {} total."
                       .format(len(tables)))
             print(" ".join(tables))
     # Users - print a list of users, or from a given channel
     if 'user' in cmd:
         users = DB.get_all_users()
         if len(users) == 0:
             msg.reply("There are no users.")
         else:
             msg.reply("Printed a list of users to console. {} total."
                       .format(len(users)))
             print(" ".join([nickColor(u) for u in users]))
     if 'id' in cmd:
         # we want to find the id of something
         if len(cmd.args['root']) != 2:
             search = msg.sender
         else:
             search = cmd.args['root'][1]
         id, type = DB.get_generic_id(search)
         if id:
             if type == 'user' and search == msg.sender:
                 msg.reply("{}, your ID is {}.".format(msg.sender, id))
             elif search == "TARS":
                 msg.reply("My ID is {}.".format(id))
             else:
                 msg.reply("{}'s ID is {}.".format(search, id))
         else:
             if type == 'channel':
                 msg.reply("I don't know the channel '{}'."
                          .format(search))
             else:
                 msg.reply("I don't know anything called '{}'."
                           .format(search))
     if 'alias' in cmd:
         search = cmd.args['root'][1] if len(cmd.args['root']) > 1 \
                                      else msg.sender
         aliases = DB.get_aliases(search)
         # should be None or a list of lists
         if aliases is None:
             msg.reply("I don't know anyone with the alias '{}'."
                       .format(search))
         else:
             msg.reply("I know {} users with the alias '{}'."
                       .format(len(aliases), search))
             for i,group in enumerate(aliases):
                 msg.reply("\x02{}.\x0F {}"
                           .format(i+1, ", ".join(group)))
     if 'occ' in cmd:
         if len(cmd.args['root']) < 2:
             raise CommandError("Specify a channel to get the occupants of")
         msg.reply("Printing occupants of {} to console"
                   .format(cmd.args['root'][1]))
         users = DB.get_occupants(cmd.args['root'][1], True)
         if isinstance(users[0], int):
             pprint(users)
         else:
             pprint([nickColor(user) for user in users])
     if 'sql' in cmd:
         if not defer.controller(cmd):
             raise CommandError("I'm afriad I can't let you do that.")
         try:
             DB.print_selection(" ".join(cmd['sql']), 'str' in cmd)
             msg.reply("Printing that selection to console")
         except:
             msg.reply("There was a problem with the selection")
             raise