Exemplo n.º 1
0
def cmd_fact(bot, trigger, db=None):
    """
    Lists known facts, list details on a fact, or rescans the fact database.

    !fact - Lists all known facts
    !fact FACT [full] - Shows detailed stats on the specified fact.  'full' dumps all translations to a PM.
    !fact LANGUAGE [full] - Shows detailed stats on the specified language.  'full' dumps all facts to a PM.

    The following commands require privileges:
    !fact import [-f] - Reimports JSON data.  -f overwrites existing rows.
    !fact add <id> <text> - Creates a new fact or updates an existing one.  <id> must be of the format <factname>-<lang>
        Aliases: set
    !fact del <id> <text> - Deletes a fact.  <id> must be of the format <factname>-<lang>
        Aliases: delete remove
    """
    pm = functools.partial(bot.say, destination=trigger.nick)
    parts = re.split(r'\s+', trigger.group(2),
                     maxsplit=2) if trigger.group(2) else None
    command = parts.pop(0).lower() if parts else None
    option = parts.pop(0).lower() if parts else None
    extra = parts[0] if parts else None

    if not command:
        # List known facts.
        unique_facts = list(Fact.unique_names(db))
        if not unique_facts:
            return bot.reply(
                "Like Jon Snow, I know nothing.  (Or there's a problem with the fact database.)"
            )
        line = "{} known fact(s): {}".format(len(unique_facts),
                                             ", ".join(unique_facts))
        if len(line) > 400:
            lines = textwrap.wrap(line, 400, break_long_words=False)
            for l in lines:
                bot.say(l)
            return
        return bot.say(line)

    @require_overseer(
        'Sorry, but you need to be an overseer or higher to execute this command.'
    )
    def cmd_fact_import(bot, trigger):
        import_facts(bot, merge=(option == '-f'))
        return bot.say("Facts imported.")

    if command == 'import':
        return cmd_fact_import(bot, trigger)

    @require_overseer(
        'Sorry, but you need to be an overseer or higher to execute this command.'
    )
    def cmd_fact_edit(bot, trigger):
        if not option:
            bot.reply("Missing fact.")
            return NOLIMIT
        if '-' not in option:
            bot.reply(
                "Fact must include a language specifier.  (Perhaps you meant '{name}-{lang}'?)"
                .format(name=option, lang=bot.memory['ratfacts']['lang'][0]))
            return NOLIMIT
        name, lang = option.rsplit('-', 1)
        if command in ('add', 'set'):
            message = extra.strip() if extra else None
            if not message:
                bot.reply("Can't add a blank fact.")
                return NOLIMIT
            fact = db.merge(
                Fact(name=name,
                     lang=lang,
                     message=extra,
                     author=str(trigger.nick)))
            is_new = not inspect(fact).persistent
            db.commit()
            bot.reply(("Added " if is_new else "Updated ") + format_fact(fact))
            return NOLIMIT
        fact = Fact.find(db, name=name, lang=lang)
        if fact:
            db.delete(fact)
            db.commit()
            bot.reply("Deleted " + format_fact(fact))
        else:
            bot.reply("No such fact.")
        return NOLIMIT

    if command in ('add', 'set', 'del', 'delete', 'remove'):
        return cmd_fact_edit(bot, trigger)

    def _translation_stats(exists, missing, s='translation', p='translations'):
        if exists:
            exists = "{count} {word} ({names})".format(
                count=len(exists),
                word=s if len(exists) == 1 else p,
                names=", ".join(sorted(exists)))
        else:
            exists = "no " + p
        if missing:
            missing = "missing {count} ({names})".format(count=len(missing),
                                                         names=", ".join(
                                                             sorted(missing)))
        else:
            missing = "none missing"
        return exists + ", " + missing

    # See if it's the name of a fact or a lang
    full = option == 'full'
    for attr, opposite, name, opposite_name_s, opposite_name_p in [
        ('name', 'lang', 'fact', 'translation', 'translations'),
        ('lang', 'name', 'language', 'fact', 'facts')
    ]:
        if not db.query(
                Fact.query(db, order_by=False, **{
                    attr: command
                }).exists()).scalar():
            continue
        sq = Fact.unique_query(db,
                               field=getattr(Fact, opposite),
                               order_by=False).subquery()
        sq_opp = getattr(sq.c, opposite)
        fact_opp = getattr(Fact, opposite)
        fact_col = getattr(Fact, attr)

        query = (db.query(Fact, sq_opp).select_from(
            sq.outerjoin(Fact, (sq_opp == fact_opp) &
                         (fact_col == command))).order_by(
                             Fact.message.is_(None), sq_opp))
        exists = set()
        missing = set()
        if full:
            if not trigger.is_privmsg:
                bot.reply("Messaging you what I know about {} '{}'".format(
                    name, command))
            pm("Fact search for {} '{}'".format(name, command))

        for fact, key in query:
            if fact and full:
                pm(format_fact(fact))
            (exists if fact else missing).add(key)

        summary = ("{} '{}': ".format(name.title(), command) +
                   _translation_stats(
                       exists, missing, s=opposite_name_s, p=opposite_name_p))
        if full:
            lines = textwrap.wrap(summary, 400, break_long_words=False)
            for l in lines:
                pm(l)
            return NOLIMIT
        else:
            lines = textwrap.wrap(summary, 400, break_long_words=False)
            for l in lines:
                bot.say(l)
            return NOLIMIT

    bot.reply(
        "'{}' is not a known fact, language, or subcommand".format(command))
    return NOLIMIT
Exemplo n.º 2
0
def cmd_fact(bot, trigger, db=None):
    """
    Lists known facts, list details on a fact, or rescans the fact database.

    !fact - Lists all known facts
    !fact FACT [full] - Shows detailed stats on the specified fact.  'full' dumps all translations to a PM.
    !fact LANGUAGE [full] - Shows detailed stats on the specified language.  'full' dumps all facts to a PM.

    The following commands require privileges:
    !fact import [-f] - Reimports JSON data.  -f overwrites existing rows.
    !fact full - Dumps all facts, all languages to a PM.
    !fact add <id> <text> - Creates a new fact or updates an existing one.  <id> must be of the format <factname>-<lang>
        Aliases: set
    !fact del <id> <text> - Deletes a fact.  <id> must be of the format <factname>-<lang>
        Aliases: delete remove
    """
    pm = functools.partial(bot.say, destination=trigger.nick)
    parts = re.split(r'\s+', trigger.group(2), maxsplit=2) if trigger.group(2) else None
    command = parts.pop(0).lower() if parts else None
    option = parts.pop(0).lower() if parts else None
    extra = parts[0] if parts else None

    if not command:
        # List known facts.
        unique_facts = list(Fact.unique_names(db))
        if not unique_facts:
            return bot.reply("Like Jon Snow, I know nothing.  (Or there's a problem with the fact database.)")
        return bot.say("{} known fact(s): {}".format(len(unique_facts), ", ".join(unique_facts)))

    @require_overseer('Sorry, but you need to be an overseer or higher to execute this command.')
    def cmd_fact_import(bot, trigger):
        import_facts(bot, merge=(option == '-f'))
        return bot.say("Facts imported.")

    if command == 'import':
        return cmd_fact_import(bot, trigger)

    @require_overseer('Sorry, but you need to be an overseer or higher to execute this command.')
    def cmd_fact_full(bot, trigger):
        if not trigger.is_privmsg:
            bot.reply("Messaging you the complete fact database.")
        pm("Language search order is {}".format(", ".join(bot.memory['ratfacts']['lang'])))
        for fact in Fact.findall(db):
            pm(format_fact(fact))
        pm("-- End of list --")
        return NOLIMIT

    if command == 'full':
        return cmd_fact_full(bot, trigger)

    @require_overseer('Sorry, but you need to be an overseer or higher to execute this command.')
    def cmd_fact_edit(bot, trigger):
        if not option:
            bot.reply("Missing fact.")
            return NOLIMIT
        if '-' not in option:
            bot.reply(
                "Fact must include a language specifier.  (Perhaps you meant '{name}-{lang}'?)"
                    .format(name=option, lang=bot.memory['ratfacts']['lang'][0])
            )
            return NOLIMIT
        name, lang = option.rsplit('-', 1)
        if command in ('add', 'set'):
            message = extra.strip() if extra else None
            if not message:
                bot.reply("Can't add a blank fact.")
                return NOLIMIT
            fact = db.merge(Fact(name=name, lang=lang, message=extra, author=str(trigger.nick)))
            is_new = not inspect(fact).persistent
            db.commit()
            bot.reply(("Added " if is_new else "Updated ") + format_fact(fact))
            return NOLIMIT
        fact = Fact.find(db, name=name, lang=lang)
        if fact:
            db.delete(fact)
            db.commit()
            bot.reply("Deleted " + format_fact(fact))
        else:
            bot.reply("No such fact.")
        return NOLIMIT

    if command in ('add', 'set', 'del', 'delete', 'remove'):
        return cmd_fact_edit(bot, trigger)

    def _translation_stats(exists, missing, s='translation', p='translations'):
        if exists:
            exists = "{count} {word} ({names})".format(
                count=len(exists), word=s if len(exists) == 1 else p, names=", ".join(sorted(exists))
            )
        else:
            exists = "no " + p
        if missing:
            missing = "missing {count} ({names})".format(count=len(missing), names=", ".join(sorted(missing)))
        else:
            missing = "none missing"
        return exists + ", " + missing

    # See if it's the name of a fact or a lang
    full = option == 'full'
    for attr, opposite, name, opposite_name_s, opposite_name_p in [
        ('name', 'lang', 'fact', 'translation', 'translations'),
        ('lang', 'name', 'language', 'fact', 'facts')
    ]:
        if not db.query(Fact.query(db, order_by=False, **{attr: command}).exists()).scalar():
            continue
        sq = Fact.unique_query(db, field=getattr(Fact, opposite), order_by=False).subquery()
        sq_opp = getattr(sq.c, opposite)
        fact_opp = getattr(Fact, opposite)
        fact_col = getattr(Fact, attr)

        query = (
            db.query(Fact, sq_opp)
                .select_from(sq.outerjoin(Fact, (sq_opp == fact_opp) & (fact_col == command)))
                .order_by(Fact.message.is_(None), sq_opp)
        )
        exists = set()
        missing = set()
        if full:
            if not trigger.is_privmsg:
                bot.reply("Messaging you what I know about {} '{}'".format(name, command))
            pm("Fact search for {} '{}'".format(name, command))

        for fact, key in query:
            if fact and full:
                pm(format_fact(fact))
            (exists if fact else missing).add(key)

        summary = (
            "{} '{}': ".format(name.title(), command) +
            _translation_stats(exists, missing, s=opposite_name_s, p=opposite_name_p)
        )
        if full:
            pm(summary)
            return NOLIMIT
        else:
            bot.say(summary)
            return NOLIMIT

    bot.reply("'{}' is not a known fact, language, or subcommand".format(command))
    return NOLIMIT