Пример #1
0
class AliasController:
    def inject(self, registry):
        self.command_alias_service = registry.get_instance("command_alias_service")
        self.text = registry.get_instance("text")

    @command(command="alias", params=[Const("list")], access_level="all",
             description="List command aliases")
    def alias_list_cmd(self, request, _):
        data = self.command_alias_service.get_enabled_aliases()
        count = len(data)
        padded_rows = self.text.pad_table(list(map(lambda row: [row.alias, row.command], data)))

        blob = ""
        for cols in padded_rows:
            blob += "  ".join(cols) + "\n"

        return ChatBlob(f"Aliases ({count})", blob)

    @command(command="alias", params=[Const("add"), Any("alias"), Any("command")], access_level="admin",
             description="Add a command alias", sub_command="modify")
    def alias_add_cmd(self, request, _, alias, command_str):
        if self.command_alias_service.add_alias(alias, command_str, force_enable=True):
            return f"Alias <highlight>{alias}</highlight> for command <highlight>{command_str}</highlight> added successfully."
        else:
            return f"Cannot add alias <highlight>{alias}</highlight> since there is already an active alias with that name."

    @command(command="alias", params=[Options(["rem", "remove"]), Any("alias")], access_level="admin",
             description="Remove a command alias", sub_command="modify")
    def alias_remove_cmd(self, request, _, alias):
        if self.command_alias_service.remove_alias(alias):
            return f"Alias <highlight>{alias}</highlight> has been removed successfully."
        else:
            return f"Could not find alias <highlight>{alias}</highlight>."
Пример #2
0
class SqlController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.command_alias_service = registry.get_instance("command_alias_service")
        self.getresp = registry.get_instance("translation_service").get_response

    def start(self):
        self.command_alias_service.add_alias("querysql", "sql query")
        self.command_alias_service.add_alias("executesql", "sql exec")

    @command(command="sql", params=[Const("query"), Any("sql_statement")], access_level="superadmin",
             description="Execute a SQL query and return the results")
    def sql_query_cmd(self, request, _, sql):
        try:
            results = self.db.query(sql)
            return ChatBlob(self.getresp("module/system", "sql_blob_title", {"count": len(results)}),
                            json.dumps(results, indent=4, sort_keys=True))
        except Exception as e:
            return self.getresp("module/system", "sql_fail", {"error": str(e)})

    @command(command="sql", params=[Const("exec"), Any("sql_statement")], access_level="superadmin",
             description="Execute a SQL query and return number of affected rows")
    def sql_exec_cmd(self, request, _, sql):
        try:
            row_count = self.db.exec(sql)
            return self.getresp("module/system", "sql_exec_success", {"count": row_count})
        except Exception as e:
            return self.getresp("module/system", "sql_fail", {"error": str(e)})
Пример #3
0
class SqlController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")

    def start(self):
        self.command_alias_service.add_alias("querysql", "sql query")
        self.command_alias_service.add_alias("executesql", "sql exec")

    @command(command="sql",
             params=[Const("query"), Any("sql_statement")],
             access_level="superadmin",
             description="Execute a SQL query and return the results")
    def sql_query_cmd(self, request, _, sql):
        try:
            results = self.db.query(sql)
            return ChatBlob("Results (%d)" % len(results),
                            json.dumps(results, indent=4, sort_keys=True))
        except Exception as e:
            return "There was an error executing your query: %s" % str(e)

    @command(
        command="sql",
        params=[Const("exec"), Any("sql_statement")],
        access_level="superadmin",
        description="Execute a SQL query and return number of affected rows")
    def sql_exec_cmd(self, request, _, sql):
        try:
            row_count = self.db.exec(sql)
            return "%d row(s) affected." % row_count
        except Exception as e:
            return "There was an error executing your query: %s" % str(e)
Пример #4
0
class AliasController:
    def __init__(self):
        pass

    def inject(self, registry):
        self.command_alias_manager = registry.get_instance(
            "command_alias_manager")

    def start(self):
        pass

    @command(command="alias",
             params=[Const("list")],
             access_level="all",
             description="List command aliases")
    def alias_list_cmd(self, channel, sender, reply, args):
        blob = ""
        data = self.command_alias_manager.get_enabled_aliases()
        count = len(data)
        for row in data:
            blob += row.alias + " - " + row.command + "\n"

        reply(ChatBlob("Aliases (%d)" % count, blob))

    @command(command="alias",
             params=[Const("add"), Any("alias"),
                     Any("command")],
             access_level="superadmin",
             description="Add a command alias",
             sub_command="modify")
    def alias_add_cmd(self, channel, sender, reply, args):
        alias = args[1]
        command = args[2]
        if self.command_alias_manager.add_alias(alias, command):
            reply(
                "Alias <highlight>%s<end> for command <highlight>%s<end> added successfully."
                % (alias, command))
        else:
            reply(
                "Cannot add alias <highlight>%s<end> since there is already an active alias with that name."
                % alias)

    @command(command="alias",
             params=[Options(["rem", "remove"]),
                     Any("alias")],
             access_level="superadmin",
             description="Remove a command alias",
             sub_command="modify")
    def alias_remove_cmd(self, channel, sender, reply, args):
        alias = args[1]
        if self.command_alias_manager.remove_alias(alias):
            reply("Alias <highlight>%s<end> has been removed successfully." %
                  alias)
        else:
            reply("Could not find alias <highlight>%s<end>." % alias)
Пример #5
0
class AliasController:
    def inject(self, registry):
        self.command_alias_service = registry.get_instance(
            "command_alias_service")
        self.ts: TranslationService = registry.get_instance(
            "translation_service")
        self.getresp = self.ts.get_response

    @command(command="alias",
             params=[Const("list")],
             access_level="all",
             description="List command aliases")
    def alias_list_cmd(self, request, _):
        blob = ""
        data = self.command_alias_service.get_enabled_aliases()
        count = len(data)
        for row in data:
            blob += row.alias + " - " + row.command + "\n"

        return ChatBlob(
            self.getresp("module/config", "alias_blob_title",
                         {"amount": count}), blob)

    @command(command="alias",
             params=[Const("add"), Any("alias"),
                     Any("command")],
             access_level="admin",
             description="Add a command alias",
             sub_command="modify")
    def alias_add_cmd(self, request, _, alias, command_str):
        if self.command_alias_service.add_alias(alias,
                                                command_str,
                                                force_enable=True):
            return self.getresp("module/config", "alias_add_success", {
                "alias": alias,
                "cmd": command_str
            })
        else:
            return self.getresp("module/config", "alias_add_fail",
                                {"alias": alias})

    @command(command="alias",
             params=[Options(["rem", "remove"]),
                     Any("alias")],
             access_level="admin",
             description="Remove a command alias",
             sub_command="modify")
    def alias_remove_cmd(self, request, _, alias):
        if self.command_alias_service.remove_alias(alias):
            return self.getresp("module/config", "alias_rem_success",
                                {"alias": alias})
        else:
            return self.getresp("module/config", "alias_rem_fail",
                                {"alias": alias})
Пример #6
0
    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS discord_char_link (discord_id BIGINT NOT NULL, char_id INT NOT NULL)"
        )

        self.message_hub_service.register_message_destination(
            self.MESSAGE_SOURCE, self.handle_incoming_relay_message, [
                "private_channel", "org_channel", "websocket_relay",
                "tell_relay", "shutdown_notice"
            ], [self.MESSAGE_SOURCE])

        self.register_discord_command_handler(
            self.discord_link_cmd, "discord",
            [Const("link"), Character("ao_character")])
        self.register_discord_command_handler(self.discord_unlink_cmd,
                                              "discord", [Const("unlink")])

        self.ts.register_translation("module/discord", self.load_discord_msg)

        self.setting_service.register(self.module_name, "discord_enabled",
                                      False, BooleanSettingType(),
                                      "Enable the Discord relay")
        self.setting_service.register(self.module_name, "discord_bot_token",
                                      "", HiddenSettingType(allow_empty=True),
                                      "Discord bot token")
        self.setting_service.register(
            self.module_name, "discord_channel_id", "",
            TextSettingType(allow_empty=True),
            "Discord channel id for relaying messages to and from",
            "You can get the Discord channel ID by right-clicking on a channel name in Discord and then clicking \"Copy ID\""
        )
        self.setting_service.register(self.module_name, "discord_embed_color",
                                      "#00FF00", ColorSettingType(),
                                      "Discord embedded message color")
        self.setting_service.register(
            self.module_name, "relay_color_prefix", "#FCA712",
            ColorSettingType(),
            "Set the prefix color for messages coming from Discord")
        self.setting_service.register(
            self.module_name, "relay_color_name", "#808080",
            ColorSettingType(),
            "Set the color of the name for messages coming from Discord")
        self.setting_service.register(
            self.module_name, "relay_color_message", "#00DE42",
            ColorSettingType(),
            "Set the color of the content for messages coming from Discord")

        self.setting_service.register_change_listener(
            "discord_channel_id", self.update_discord_channel)
        self.setting_service.register_change_listener(
            "discord_enabled", self.update_discord_state)
Пример #7
0
class BanController:
    def inject(self, registry):
        self.text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.ban_service = registry.get_instance("ban_service")
        self.command_alias_service = registry.get_instance("command_alias_service")

    def start(self):
        self.command_alias_service.add_alias("unban", "ban rem")

    @command(command="ban", params=[Const("list", is_optional=True)], access_level="moderator",
             description="Show the ban list")
    def ban_list_cmd(self, request, _):
        t = int(time.time())
        data = self.ban_service.get_ban_list()
        blob = ""
        for row in data:
            ends = "never" if row.finished_at == -1 else self.util.format_timestamp(row.finished_at)
            time_left = "" if row.finished_at == -1 else " (%s left)" % self.util.time_to_readable(row.finished_at - t)

            blob += "<pagebreak>Name: <highlight>%s<end>\n" % row.name
            blob += "Added: <highlight>%s<end>\n" % self.util.format_timestamp(row.created_at)
            blob += "By: <highlight>%s<end>\n" % row.sender_name
            blob += "Ends: <highlight>%s<end>%s\n" % (ends, time_left)
            blob += "Reason: <highlight>%s<end>\n\n" % row.reason

        return ChatBlob("Ban List (%d)" % len(data), blob)

    @command(command="ban", params=[Options(["rem", "remove"]), Character("character")], access_level="moderator",
             description="Remove a character from the ban list")
    def ban_remove_cmd(self, request, _, char):
        if not char.char_id:
            return "Could not find <highlight>%s<end>." % char.name
        elif not self.ban_service.get_ban(char.char_id):
            return "<highlight>%s<end> is not banned." % char.name
        else:
            self.ban_service.remove_ban(char.char_id)
            return "<highlight>%s<end> has been removed from the ban list." % char.name

    @command(command="ban", params=[Const("add", is_optional=True), Character("character"), Time("duration", is_optional=True), Any("reason", is_optional=True)], access_level="moderator",
             description="Add a character to the ban list")
    def ban_add_cmd(self, request, _, char, duration, reason):
        reason = reason or ""

        if not char.char_id:
            return "Could not find <highlight>%s<end>." % char.name
        elif self.ban_service.get_ban(char.char_id):
            return "<highlight>%s<end> is already banned." % char.name
        else:
            self.ban_service.add_ban(char.char_id, request.sender.char_id, duration, reason)
            return "<highlight>%s<end> has been added to the ban list." % char.name
class TopicController:
    def inject(self, registry):
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.util = registry.get_instance("util")

    @setting(name="topic", value="", description="The bot topic")
    def topic(self):
        return DictionarySettingType()

    @command(command="topic",
             params=[],
             access_level="all",
             description="Show the current topic")
    def topic_show_command(self, request):
        topic = self.topic().get_value()
        if topic:
            time_string = self.util.time_to_readable(
                int(time.time()) - topic["created_at"])
            return "Topic: <highlight>%s<end> [set by <highlight>%s<end>][%s ago]" % (
                topic["topic_message"], topic["created_by"]["name"],
                time_string)
        else:
            return "There is no current topic."

    @command(command="topic",
             params=[Const("clear")],
             access_level="all",
             description="Clears the current topic")
    def topic_clear_command(self, request, _):
        self.topic().set_value("")

        return "The topic has been cleared."

    @command(command="topic",
             params=[Const("set", is_optional=True),
                     Any("topic_message")],
             access_level="all",
             description="Set the current topic")
    def topic_set_command(self, request, _, topic_message):
        topic = {
            "topic_message": topic_message,
            "created_by": sender,
            "created_at": int(time.time())
        }

        self.topic().set_value(topic)

        return "The topic has been set."
Пример #9
0
class AliasController:
    def inject(self, registry):
        self.command_alias_service = registry.get_instance(
            "command_alias_service")

    @command(command="alias",
             params=[Const("list")],
             access_level="all",
             description="List command aliases")
    def alias_list_cmd(self, request, _):
        blob = ""
        data = self.command_alias_service.get_enabled_aliases()
        count = len(data)
        for row in data:
            blob += row.alias + " - " + row.command + "\n"

        return ChatBlob("Aliases (%d)" % count, blob)

    @command(command="alias",
             params=[Const("add"), Any("alias"),
                     Any("command")],
             access_level="admin",
             description="Add a command alias",
             sub_command="modify")
    def alias_add_cmd(self, request, _, alias, command_str):
        if self.command_alias_service.add_alias(alias,
                                                command_str,
                                                force_enable=True):
            return "Alias <highlight>%s<end> for command <highlight>%s<end> added successfully." % (
                alias, command_str)
        else:
            return "Cannot add alias <highlight>%s<end> since there is already an active alias with that name." % alias

    @command(command="alias",
             params=[Options(["rem", "remove"]),
                     Any("alias")],
             access_level="admin",
             description="Remove a command alias",
             sub_command="modify")
    def alias_remove_cmd(self, request, _, alias):
        if self.command_alias_service.remove_alias(alias):
            return "Alias <highlight>%s<end> has been removed successfully." % alias
        else:
            return "Could not find alias <highlight>%s<end>." % alias
class LeaderController:
    def __init__(self):
        self.leader = None

    def inject(self, registry):
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.access_service = registry.get_instance("access_service")
        self.character_service = registry.get_instance("character_service")

    @command(command="leader", params=[], access_level="all",
             description="Show the current raidleader")
    def leader_show_command(self, request):
        if self.leader:
            return "The current raidleader is <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader)
        else:
            return "There is no current raidleader."

    @command(command="leader", params=[Const("set")], access_level="all",
             description="Set yourself as raidleader")
    def leader_set_self_command(self, request, _):
        if self.leader == request.sender.char_id:
            self.leader = None
            return "You have been removed as raidleader."
        elif not self.leader:
            self.leader = request.sender.char_id
            return "You have been set as raidleader."
        elif self.access_service.has_sufficient_access_level(request.sender.char_id, self.leader):
            self.leader = request.sender.char_id
            return "You have taken leader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader)
        else:
            return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader)

    @command(command="leader", params=[Const("set", is_optional=True), Character("character")], access_level="all",
             description="Set another character as raidleader")
    def leader_set_other_command(self, request, _, char):
        if not char.char_id:
            return "Could not find <highlight>%s<end>." % char.name

        if not self.leader or self.access_service.has_sufficient_access_level(request.sender.char_id, self.leader):
            self.leader = char.char_id
            return "<highlight>%s<end> has been set as raidleader." % char.name
        else:
            return "You do not have a high enough access level to take raidleader from <highlight>%s<end>." % self.character_service.resolve_char_to_name(self.leader)
Пример #11
0
class SendMessageController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.command_service = registry.get_instance("command_service")
        self.getresp = registry.get_instance(
            "translation_service").get_response

    @command(command="send",
             params=[Const("tell"),
                     Character("character"),
                     Any("message")],
             access_level="superadmin",
             description="Send a message to a character from the bot")
    def send_tell_cmd(self, request, _, char, message):
        if char.char_id:
            self.bot.send_private_message(char.char_id,
                                          message,
                                          add_color=False,
                                          conn=request.conn)
            return self.getresp("module/system", "msg_sent")
        else:
            return self.getresp("global", "char_not_found",
                                {"char": char.name})

    @command(command="send",
             params=[Const("org"), Any("message")],
             access_level="superadmin",
             description="Send a message to the org channel from the bot")
    def send_org_cmd(self, request, _, message):
        for _id, conn in self.bot.get_conns(lambda x: x.is_main):
            self.bot.send_org_message(message, add_color=False, conn=conn)
        return self.getresp("module/system", "msg_sent")

    @command(command="send",
             params=[Const("priv"), Any("message")],
             access_level="superadmin",
             description="Send a message to the private channel from the bot")
    def send_priv_cmd(self, request, _, message):
        self.bot.send_private_channel_message(message,
                                              add_color=False,
                                              conn=self.bot.get_primary_conn())
        return self.getresp("module/system", "msg_sent")
Пример #12
0
class NotesController:
    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.text = registry.get_instance("text")
        self.alts_service = registry.get_instance("alts_service")

    def start(self):
        self.db.exec("CREATE TABLE IF NOT EXISTS notes ("
                     "id INT PRIMARY KEY AUTO_INCREMENT, "
                     "char_id INT NOT NULL, "
                     "note TEXT NOT NULL,"
                     "created_at INT NOT NULL)")

    @command(command="notes", params=[], access_level="all",
             description="Show your notes")
    def notes_list_cmd(self, request):
        alts = self.alts_service.get_alts(request.sender.char_id)

        cnt = 0
        blob = ""
        for alt in alts:
            data = self.db.query("SELECT * FROM notes WHERE char_id = ? ORDER BY created_at DESC", [alt.char_id])
            alt_cnt = len(data)
            cnt += alt_cnt

            if alt_cnt:
                blob += "\n<header2>%s<end>\n" % alt.name
                for row in data:
                    blob += "%s %s\n\n" % (row.note, self.text.make_chatcmd("Remove", "/tell <myname> notes remove %d" % row.id))

        return ChatBlob("Notes for %s (%d)" % (alts[0].name, cnt), blob)

    @command(command="notes", params=[Const("add"), Any("note")], access_level="all",
             description="Add a note")
    def notes_add_cmd(self, request, _, note):
        self.db.exec("INSERT INTO notes (char_id, note, created_at) VALUES (?, ?, ?)", [request.sender.char_id, note, int(time.time())])

        return "Note added successfully."

    @command(command="notes", params=[Options(["rem", "remove"]), Int("note_id")], access_level="all",
             description="Remove a note")
    def notes_remove_cmd(self, request, _, note_id):
        note = self.db.query_single("SELECT n.*, p.name FROM notes n LEFT JOIN player p ON n.char_id = p.char_id WHERE n.id = ?", [note_id])

        if not note:
            return "Could not find note with ID <highlight>%d<end>." % note_id

        if self.alts_service.get_main(request.sender.char_id).char_id != self.alts_service.get_main(note.char_id).char_id:
            return "You must be a confirmed alt of <highlight>%s<end> to remove this note." % note.name

        self.db.exec("DELETE FROM notes WHERE id = ?", [note_id])

        return "Note with ID <highlight>%d<end> deleted successfully." % note_id
Пример #13
0
class AssistController:
    def __init__(self):
        self.assist = []

    def inject(self, registry):
        self.leader_controller = registry.get_instance("leader_controller")

    @command(command="assist",
             params=[],
             access_level="all",
             description="Show current assist targets")
    def assist_command(self, request):
        return self.get_assist_output()

    @command(command="assist",
             params=[Const("clear")],
             access_level="all",
             description="Clear all assist targets",
             sub_command="modify")
    def assist_clear_command(self, request, _):
        if not self.assist:
            return "No assist targets set."

        if not self.leader_controller.can_use_command(request.sender.char_id):
            return LeaderController.NOT_LEADER_MSG
        else:
            self.assist = []
            return "Assist targets have been cleared."

    @command(
        command="assist",
        params=[Any("assist_targets")],
        access_level="all",
        description="Set one or more assist targets",
        sub_command="modify",
        extended_description="Multiple assist targets should be space-delimited"
    )
    def assist_set_command(self, request, assist_targets):
        targets = assist_targets.split(" ")

        if not self.leader_controller.can_use_command(request.sender.char_id):
            return LeaderController.NOT_LEADER_MSG
        else:
            self.assist = targets
            return self.get_assist_output()

    def get_assist_output(self):
        if not self.assist:
            return "No assist targets set."

        return "/macro assist " + " \\n ".join(
            map(lambda x: "/assist " + x.capitalize(), reversed(self.assist)))
Пример #14
0
class QueueController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.command_alias_service = registry.get_instance("command_alias_service")
        self.getresp = registry.get_instance("translation_service").get_response

    def start(self):
        self.command_alias_service.add_alias("clearqueue", "queue clear")

    @command(command="queue", params=[Const("clear")], access_level="moderator",
             description="Clear the outgoing message queue")
    def queue_clear_cmd(self, request, _):
        num_messages = len(self.bot.conns["main"].packet_queue)
        self.bot.conns["main"].packet_queue.clear()
        return self.getresp("module/system", "clear_queue", {"count": num_messages})
Пример #15
0
    def test_multiple(self):
        param1 = Multiple(Int("num"))
        self.assertEqual([1], self.param_test_helper(param1, "1"))
        self.assertEqual([1, 2, 3], self.param_test_helper(param1, "1 2 3"))

        param2 = Multiple(Const("something"))
        self.assertEqual(["something"], self.param_test_helper(param2, "something"))
        self.assertEqual(["something", "something", "something"],
                         self.param_test_helper(param2, "something something something"))

        param3 = Multiple(Time("time"))
        self.assertEqual([60], self.param_test_helper(param3, "1m"))
        self.assertEqual([304], self.param_test_helper(param3, "5M4S"))
        self.assertEqual([60, 304, 14521], self.param_test_helper(param3, "1m 5M4S 4h2m1s"))

        param4 = Multiple(Item("item"))
        self.assertEqual([{'low_id': 246817, 'high_id': 246817, 'ql': 200, 'name': 'Novictum Seed'}],
                         self.param_test_helper(param4, "<a href=\"itemref://246817/246817/200\">Novictum Seed</a>"))

        self.assertEqual([{'low_id': 246817, 'high_id': 246817, 'ql': 200, 'name': 'Novictum Seed'},
                          {'low_id': 100, 'high_id': 101, 'ql': 300, 'name': 'It\'s cool'}],
                         self.param_test_helper(param4, "<a href=\"itemref://246817/246817/200\">Novictum Seed</a> "
                                                        "<a href=\"itemref://100/101/300\">It's cool</a>"))

        self.assertEqual([{'low_id': 246817, 'high_id': 246817, 'ql': 200, 'name': 'Novictum Seed'},
                          {'low_id': 100, 'high_id': 101, 'ql': 300, 'name': 'It\'s cool'},
                          {'low_id': 12345, 'high_id': 54321, 'ql': 123, 'name': 'It Works!'}],
                         self.param_test_helper(param4,
                                                "<a href=\"itemref://246817/246817/200\">Novictum Seed</a> "
                                                "<a href=\"itemref://100/101/300\">It's cool</a> "
                                                "<a href=\"itemref://12345/54321/123\">It Works!</a>"))

        # no spaces
        self.assertEqual([{'low_id': 246817, 'high_id': 246817, 'ql': 200, 'name': 'Novictum Seed'},
                          {'low_id': 100, 'high_id': 101, 'ql': 300, 'name': 'It\'s cool'},
                          {'low_id': 12345, 'high_id': 54321, 'ql': 123, 'name': 'It Works!'}],
                         self.param_test_helper(param4,
                                                "<a href=\"itemref://246817/246817/200\">Novictum Seed</a>"
                                                "<a href=\"itemref://100/101/300\">It's cool</a>"
                                                "<a href=\"itemref://12345/54321/123\">It Works!</a>"))

        param5 = Multiple(Any("time"))
        self.assertEqual(["test"], self.param_test_helper(param5, "test"))
        self.assertEqual(["test1", "test2"], self.param_test_helper(param5, "test1 test2"))
        self.assertEqual(["test1", "test2", "test3"], self.param_test_helper(param5, "test1 test2 test3"))
Пример #16
0
class LinksController:
    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.text = registry.get_instance("text")

    def start(self):
        self.db.exec("CREATE TABLE IF NOT EXISTS links ("
                     "id INT PRIMARY KEY AUTO_INCREMENT,"
                     "char_id INT NOT NULL,"
                     "website VARCHAR(255) NOT NULL,"
                     "comments VARCHAR(255) NOT NULL,"
                     "created_at INT NOT NULL);")

    @command(command="links", params=[], access_level="all",
             description="Show links")
    def links_list_cmd(self, request):
        data = self.db.query("SELECT l.*, p.name FROM links l LEFT JOIN player p ON l.char_id = p.char_id ORDER BY name ASC")

        blob = ""
        for row in data:
            blob += "%s <highlight>%s<end> [%s] %s" % (self.text.make_chatcmd("[Link]", "/start %s" % row.website),
                                                       row.comments,
                                                       row.name,
                                                       self.text.make_chatcmd("Remove", "/tell <myname> links remove %d" % row.id))

        return ChatBlob("Links (%d)" % len(data), blob)

    @command(command="links", params=[Const("add"), Any("website"), Any("comment")], access_level="moderator",
             description="Add a link")
    def links_add_cmd(self, request, _, website, comment):
        if not website.startswith("https://") and not website.startswith("http://"):
            return "Website must start with 'http://' or 'https://'."

        self.db.exec("INSERT INTO links (char_id, website, comments, created_at) VALUES (?, ?, ?, ?)", [request.sender.char_id, website, comment, int(time.time())])
        return "Link added successfully."

    @command(command="links", params=[Options(["rem", "remove"]), Int("link_id")], access_level="moderator",
             description="Remove a link")
    def links_remove_cmd(self, request, _, link_id):
        link = self.db.query_single("SELECT * FROM links WHERE id = ?", [link_id])
        if not link:
            return "Could not find link with ID <highlight>%d<end>." % link_id

        self.db.exec("DELETE FROM links WHERE id = ?", [link_id])
        return "Link has been deleted"
Пример #17
0
class QueueController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")

    def start(self):
        self.command_alias_service.add_alias("clearqueue", "queue clear")

    @command(command="queue",
             params=[Const("clear")],
             access_level="moderator",
             description="Clear the outgoing message queue")
    def queue_clear_cmd(self, request, _):
        num_messages = len(self.bot.packet_queue)
        self.bot.packet_queue.clear()

        return "Cleared <highlight>%d<end> messages from the outgoing message queue." % num_messages
Пример #18
0
class CacheController:
    invalid_chars = ["/", "\\", ".."]

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.text = registry.get_instance("text")

    @command(command="cache", params=[Const("delete"), Any("category"), Any("filename")], access_level="superadmin",
             description="Manually remove a cache entry")
    def cache_remove_cmd(self, request, _, category, filename):
        full_file_path = os.sep.join([".", "data", "cache", category, filename])
        full_file_path = os.path.normpath(full_file_path)
        if not full_file_path.startswith(os.path.normpath(os.sep.join([".", "data", "cache"]))):
            return f"<highlight>{full_file_path}</highlight> is not a valid cache entry."

        if os.path.isfile(full_file_path):
            os.remove(full_file_path)
            return f"Cache entry <highlight>{full_file_path}</highlight> has been removed."
        else:
            return f"Cache entry <highlight>{full_file_path}</highlight> does not exist or is not a file."
Пример #19
0
class QueueController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.command_alias_service = registry.get_instance("command_alias_service")
        self.command_service = registry.get_instance("command_service")

    def start(self):
        self.command_alias_service.add_alias("clearqueue", "queue clear")

    @command(command="queue", params=[Const("clear")], access_level="moderator",
             description="Clear the outgoing message queue")
    def queue_clear_cmd(self, request, _):
        num_messages = len(request.conn.packet_queue)
        request.conn.packet_queue.clear()
        return f"Cleared <highlight>{num_messages}</highlight> messages from the outgoing message queue."

    @command(command="massmsg", params=[Any("command")], access_level="moderator",
             description="Force the reply of the specified command to be sent via non-main bots")
    def massmsg_cmd(self, request, command_str):
        def reply(msg):
            self.bot.send_mass_message(request.sender.char_id, msg, conn=request.conn)

        self.command_service.process_command(command_str, request.channel, request.sender.char_id, reply, request.conn)
Пример #20
0
class TowerAttackController:
    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.event_service = registry.get_instance("event_service")
        self.playfield_controller = registry.get_instance(
            "playfield_controller")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")
        self.public_channel_service = registry.get_instance(
            "public_channel_service")

    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS tower_attacker (id INT PRIMARY KEY AUTO_INCREMENT, att_org_name VARCHAR(50) NOT NULL, att_faction VARCHAR(10) NOT NULL, "
            "att_char_id INT, att_char_name VARCHAR(20) NOT NULL, att_level INT NOT NULL, att_ai_level INT NOT NULL, att_profession VARCHAR(15) NOT NULL, "
            "x_coord INT NOT NULL, y_coord INT NOT NULL, is_victory SMALLINT NOT NULL, "
            "tower_battle_id INT NOT NULL, created_at INT NOT NULL)")
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS tower_battle (id INT PRIMARY KEY AUTO_INCREMENT, playfield_id INT NOT NULL, site_number INT NOT NULL, "
            "def_org_name VARCHAR(50) NOT NULL, def_faction VARCHAR(10) NOT NULL, is_finished INT NOT NULL, battle_type VARCHAR(20) NOT NULL, last_updated INT NOT NULL)"
        )

        self.command_alias_service.add_alias("victory", "attacks")

    @command(command="attacks",
             params=[NamedParameters(["page"])],
             access_level="all",
             description="Show recent tower attacks and victories")
    def attacks_cmd(self, request, named_params):
        page = int(named_params.page or "1")

        page_size = 30
        offset = (page - 1) * page_size

        sql = """
            SELECT
                b.*,
                a.*,
                COALESCE(a.att_level, 0) AS att_level,
                COALESCE(a.att_ai_level, 0) AS att_ai_level,
                p.short_name,
                b.id AS battle_id
            FROM
                tower_battle b
                LEFT JOIN tower_attacker a ON
                    a.tower_battle_id = b.id
                LEFT JOIN playfields p ON
                    p.id = b.playfield_id
            ORDER BY
                b.last_updated DESC,
                a.created_at DESC
            LIMIT %d, %d
        """ % (offset, page_size)

        data = self.db.query(sql)
        t = int(time.time())

        blob = self.check_for_all_towers_channel()

        if page > 1:
            blob += "   " + self.text.make_chatcmd(
                "<< Page %d" % (page - 1), self.get_chat_command(page - 1))
        if len(data) > 0:
            blob += "   Page " + str(page)
            blob += "   " + self.text.make_chatcmd(
                "Page %d >>" % (page + 1), self.get_chat_command(page + 1))
            blob += "\n"

        current_battle_id = -1
        for row in data:
            if current_battle_id != row.battle_id:
                blob += "\n<pagebreak>"
                current_battle_id = row.battle_id
                blob += self.format_battle_info(row, t)
                blob += self.text.make_chatcmd(
                    "More Info",
                    "/tell <myname> attacks battle %d" % row.battle_id) + "\n"
                blob += "<header2>Attackers:<end>\n"

            blob += "<tab>" + self.format_attacker(row) + "\n"

        return ChatBlob("Tower Attacks", blob)

    @command(command="attacks",
             params=[Const("battle"), Int("battle_id")],
             access_level="all",
             description="Show battle info for a specific battle")
    def attacks_battle_cmd(self, request, _, battle_id):
        battle = self.db.query_single(
            "SELECT b.*, p.short_name FROM tower_battle b LEFT JOIN playfields p ON p.id = b.playfield_id WHERE b.id = ?",
            [battle_id])
        if not battle:
            return "Could not find battle with ID <highlight>%d<end>." % battle_id

        t = int(time.time())

        attackers = self.db.query(
            "SELECT * FROM tower_attacker WHERE tower_battle_id = ? ORDER BY created_at DESC",
            [battle_id])

        first_activity = attackers[-1].created_at if len(
            attackers) > 0 else battle.last_updated

        blob = self.check_for_all_towers_channel()
        blob += self.format_battle_info(battle, t)
        blob += "Duration: <highlight>%s<end>\n\n" % self.util.time_to_readable(
            battle.last_updated - first_activity)
        blob += "<header2>Attackers:<end>\n"

        for row in attackers:
            blob += "<tab>" + self.format_attacker(row)
            blob += " " + self.format_timestamp(row.created_at, t)
            blob += "\n"

        return ChatBlob("Battle Info %d" % battle_id, blob)

    @event(event_type=TowerController.TOWER_ATTACK_EVENT,
           description="Record tower attacks",
           is_hidden=True)
    def tower_attack_event(self, event_type, event_data):
        t = int(time.time())
        site_number = self.find_closest_site_number(
            event_data.location.playfield.id, event_data.location.x_coord,
            event_data.location.y_coord)

        attacker = event_data.attacker or {}
        defender = event_data.defender

        battle = self.find_or_create_battle(event_data.location.playfield.id,
                                            site_number, defender.org_name,
                                            defender.faction, "attack", t)

        self.db.exec(
            "INSERT INTO tower_attacker (att_org_name, att_faction, att_char_id, att_char_name, att_level, att_ai_level, att_profession, "
            "x_coord, y_coord, is_victory, tower_battle_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
            [
                attacker.get("org_name", ""),
                attacker.get("faction", ""),
                attacker.get("char_id", 0),
                attacker.get("name", ""),
                attacker.get("level", 0),
                attacker.get("ai_level", 0),
                attacker.get("profession", ""), event_data.location.x_coord,
                event_data.location.y_coord, 0, battle.id, t
            ])

    @event(event_type=TowerController.TOWER_VICTORY_EVENT,
           description="Record tower victories",
           is_hidden=True)
    def tower_victory_event(self, event_type, event_data):
        t = int(time.time())

        if event_data.type == "attack":
            row = self.get_last_attack(event_data.winner.faction,
                                       event_data.winner.org_name,
                                       event_data.loser.faction,
                                       event_data.loser.org_name,
                                       event_data.location.playfield.id, t)

            if not row:
                site_number = 0
                is_finished = 1
                self.db.exec(
                    "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, is_finished, battle_type, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?)",
                    [
                        event_data.location.playfield.id, site_number,
                        event_data.loser.org_name, event_data.loser.faction,
                        is_finished, event_data.type, t
                    ])
                battle_id = self.db.last_insert_id()

                attacker = event_data.winner or {}
                self.db.exec(
                    "INSERT INTO tower_attacker (att_org_name, att_faction, att_char_id, att_char_name, att_level, att_ai_level, att_profession, "
                    "x_coord, y_coord, is_victory, tower_battle_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                    [
                        attacker.get("org_name", ""),
                        attacker.get("faction", ""),
                        attacker.get("char_id", 0),
                        attacker.get("name", ""),
                        attacker.get("level", 0),
                        attacker.get("ai_level", 0),
                        attacker.get("profession", ""), 0, 0, 0, battle_id, t
                    ])
            else:
                is_victory = 1
                self.db.exec(
                    "UPDATE tower_attacker SET is_victory = ? WHERE id = ?",
                    [is_victory, row.attack_id])

                is_finished = 1
                self.db.exec(
                    "UPDATE tower_battle SET is_finished = ?, last_updated = ? WHERE id = ?",
                    [is_finished, t, row.battle_id])
        elif event_data.type == "terminated":
            site_number = 0
            is_finished = 1
            self.db.exec(
                "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, is_finished, battle_type, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?)",
                [
                    event_data.location.playfield.id, site_number,
                    event_data.loser.org_name, event_data.loser.faction,
                    is_finished, event_data.type, t
                ])
        else:
            raise Exception("Unknown victory event type: '%s'" %
                            event_data.type)

    def format_attacker(self, row):
        if row.att_char_name:
            level = ("%d/<green>%d<end>" % (row.att_level, row.att_ai_level)
                     ) if row.att_ai_level > 0 else "%d" % row.att_level
            org = row.att_org_name + " " if row.att_org_name else ""
            victor = " - <notice>Winner!<end>" if row.is_victory else ""
            return "%s (%s %s) %s(%s)%s" % (row.att_char_name, level,
                                            row.att_profession, org,
                                            row.att_faction, victor)
        else:
            return "Unknown attacker"

    def find_closest_site_number(self, playfield_id, x_coord, y_coord):
        sql = """
            SELECT
                site_number,
                ((x_distance * x_distance) + (y_distance * y_distance)) radius
            FROM
                (SELECT
                    playfield_id,
                    site_number,
                    min_ql,
                    max_ql,
                    x_coord,
                    y_coord,
                    site_name,
                    (x_coord - ?) as x_distance,
                    (y_coord - ?) as y_distance
                FROM
                    tower_site
                WHERE
                    playfield_id = ?) t
            ORDER BY
                radius ASC
            LIMIT 1"""

        row = self.db.query_single(sql, [x_coord, y_coord, playfield_id])
        if row:
            return row.site_number
        else:
            return 0

    def find_or_create_battle(self, playfield_id, site_number, org_name,
                              faction, battle_type, t):
        last_updated = t - (8 * 3600)
        is_finished = 0

        sql = """
            SELECT
                *
            FROM
                tower_battle
            WHERE
                playfield_id = ?
                AND site_number = ?
                AND is_finished = ?
                AND def_org_name = ?
                AND def_faction = ?
                AND last_updated >= ?
        """

        battle = self.db.query_single(sql, [
            playfield_id, site_number, is_finished, org_name, faction,
            last_updated
        ])

        if battle:
            self.db.exec(
                "UPDATE tower_battle SET last_updated = ? WHERE id = ?",
                [t, battle.id])
            return battle
        else:
            self.db.exec(
                "INSERT INTO tower_battle (playfield_id, site_number, def_org_name, def_faction, is_finished, battle_type, last_updated) VALUES (?, ?, ?, ?, ?, ?, ?)",
                [
                    playfield_id, site_number, org_name, faction, is_finished,
                    battle_type, t
                ])
            return self.db.query_single(
                "SELECT * FROM tower_battle WHERE id = ?",
                [self.db.last_insert_id()])

    def get_last_attack(self, att_faction, att_org_name, def_faction,
                        def_org_name, playfield_id, t):
        last_updated = t - (8 * 3600)
        is_finished = 0

        sql = """
            SELECT
                b.id AS battle_id,
                a.id AS attack_id
            FROM
                tower_battle b
                JOIN tower_attacker a ON
                    a.tower_battle_id = b.id 
            WHERE
                a.att_faction = ?
                AND a.att_org_name = ?
                AND b.def_faction = ?
                AND b.def_org_name = ?
                AND b.playfield_id = ?
                AND b.is_finished = ?
                AND b.last_updated >= ?
            ORDER BY
                last_updated DESC
            LIMIT 1"""

        return self.db.query_single(sql, [
            att_faction, att_org_name, def_faction, def_org_name, playfield_id,
            is_finished, last_updated
        ])

    def format_battle_info(self, row, t):
        blob = ""
        defeated = " - <notice>Defeated!<end>" if row.is_finished else ""
        blob += "Site: <highlight>%s %s<end>\n" % (row.short_name,
                                                   row.site_number or "?")
        blob += "Defender: <highlight>%s<end> (%s)%s\n" % (
            row.def_org_name, row.def_faction, defeated)
        blob += "Last Activity: %s\n" % self.format_timestamp(
            row.last_updated, t)
        return blob

    def format_timestamp(self, t, current_t):
        return "<highlight>%s<end> (%s ago)" % (self.util.format_datetime(
            t), self.util.time_to_readable(current_t - t))

    def get_chat_command(self, page):
        return "/tell <myname> attacks --page=%d" % page

    def check_for_all_towers_channel(self):
        if not self.public_channel_service.get_channel_name(
                TowerController.ALL_TOWERS_ID):
            return "Notice: The bot must belong to an org and be promoted to a rank that is high enough to have the All Towers channel (e.g., Squad Commander) in order for the <symbol>attacks command to work correctly.\n\n"
        else:
            return ""
Пример #21
0
class MigrateController:
    DATABASE_TYPE_MYSQL = "mysql"
    DATABASE_TYPE_SQLITE = "sqlite"

    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.character_service = registry.get_instance("character_service")
        self.alts_service = registry.get_instance("alts_service")
        self.pork_service = registry.get_instance("pork_service")

    @event(event_type="connect",
           description="Configure migration controller",
           is_system=True)
    def connect_event(self, event_type, event_data):
        self.db2 = DB()

        # Optional: the name of the bot character that the budabot/bebot ran as
        # if the bot name is the same, then you can leave this blank, otherwise you must fill in this value
        bot_name = ""

        # Optional: the org_id of the org
        # the bot will use the org_id of the primary conn if this is not set, which is usually correct
        org_id = 0

        # if your budabot/bebot used mysql, then uncomment the second line below and fill out the appropriate values
        # otherwise, if your budabot used sqlite, then uncomment the first line below and enter the path to the sqlite db file
        # do NOT uncomment both of them
        # REQUIRED: uncomment ONE of these two lines below

        # self.db2.connect_sqlite("./data/budabot.db")
        # self.db2.connect_mysql(host="localhost", port=3306, username="", password="", database_name="")

        self.bot_name = bot_name.lower(
        ) if bot_name else self.bot.get_primary_conn().get_char_name().lower()
        self.org_id = org_id if org_id else self.bot.get_primary_conn().org_id
        self.dimension = self.bot.dimension

        # TODO in each command, check if db has been initialized properly first

    @command(command="bebot",
             params=[Const("migrate"), Const("alts")],
             access_level="superadmin",
             description="Migrate alts from a Bebot database")
    def migrate_bebot_alts_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT a.alt, u1.char_id AS alt_char_id, a.main, u2.char_id AS main_char_id "
            "FROM alts a "
            f"LEFT JOIN {self.bot_name}_users u1 ON a.alt = u1.nickname "
            f"LEFT JOIN {self.bot_name}_users u2 ON a.main = u2.nickname "
            "WHERE a.confirmed = 1 "
            "ORDER BY main, alt")
        current_main = None
        current_main_id = None
        count_inactive = 0
        count_success = 0
        count_failure = 0

        request.reply("Processing %s alt records..." % len(data))

        for row in data:
            if row.main != current_main:
                current_main = row.main
                current_main_id = self.resolve_to_char_id(
                    row.main, row.main_char_id)

            if not current_main_id:
                self.logger.warning(
                    f"Could not resolve main char '{current_main}' to char id")
                count_inactive += 1
                continue

            alt_id = self.resolve_to_char_id(row.alt, row.alt_char_id)
            if not alt_id:
                self.logger.warning(
                    f"Could not resolve alt char '{row.alt}' to char id")
                count_inactive += 1
                continue

            msg, result = self.alts_service.add_alt(current_main_id, alt_id)
            if result:
                count_success += 1
            else:
                count_failure += 1

        return f"<highlight>{count_success}</highlight> alts were migrated successfully, " \
               f"<highlight>{count_failure}</highlight> alts failed to be added, " \
               f"and <highlight>{count_inactive}</highlight> chars were inactive and could not be resolved to char ids."

    @command(command="bebot",
             params=[Const("migrate"), Const("logon")],
             access_level="superadmin",
             description="Migrate logon messages from a Bebot database")
    def migrate_bebot_logon_cmd(self, request, _1, _2):
        data = self.db2.query(f"SELECT message, id FROM {self.bot_name}_logon")

        request.reply("Processing %s logon records..." % len(data))

        for row in data:
            self.db.exec(
                "INSERT INTO log_messages (char_id, logon) VALUES(?, ?)",
                [row.id, row.message])

        return f"Successfully migrated <highlight>%d</highlight> logon messages." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("admins")],
             access_level="superadmin",
             description="Migrate admins from a Budabot database")
    def migrate_budabot_admins_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT a.name, p.charid AS char_id, CASE WHEN adminlevel = 4 THEN 'admin' WHEN adminlevel = 3 THEN 'moderator' END AS access_level "
            f"FROM admin_{self.bot_name} a LEFT JOIN players p ON (a.name = p.name AND p.dimension = ?) "
            "WHERE p.charid > 0", [self.dimension])
        with self.db.transaction():
            for row in data:
                char_id = self.resolve_to_char_id(row.name, row.char_id)

                if char_id and row.access_level:
                    self.db.exec("DELETE FROM admin WHERE char_id = ?",
                                 [char_id])
                    self.db.exec(
                        "INSERT INTO admin (char_id, access_level) VALUES (?, ?)",
                        [char_id, row.access_level])

        return f"Successfully migrated <highlight>%d</highlight> admin characters." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("banlist")],
             access_level="superadmin",
             description="Migrate ban list from a Budabot database")
    def migrate_budabot_banlist_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT b.charid AS char_id, p.charid AS sender_char_id, time AS created_at, banend AS finished_at, reason "
            f"FROM banlist_{self.bot_name} b JOIN players p ON (b.admin = p.name AND p.dimension = ?)"
            "WHERE p.charid > 0", [self.dimension])
        with self.db.transaction():
            for row in data:
                self.db.exec("DELETE FROM ban_list WHERE char_id = ?",
                             [row.char_id])
                self.db.exec(
                    "INSERT INTO ban_list (char_id, sender_char_id, created_at, finished_at, reason, ended_early) VALUES (?, ?, ?, ?, ?, ?)",
                    [
                        row.char_id, row.sender_char_id, row.created_at,
                        row.finished_at, row.reason, 0
                    ])

        return f"Successfully migrated <highlight>%d</highlight> banned characters." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("alts")],
             access_level="superadmin",
             description="Migrate alts from a Budabot database")
    def migrate_budabot_alts_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT p1.charid AS main_char_id, p2.charid AS alt_char_id "
            "FROM alts a JOIN players p1 ON (p1.name = a.main AND p1.dimension = ?) "
            "JOIN players p2 ON (p2.name = a.alt AND p2.dimension = ?)"
            "WHERE validated = 1 AND p1.charid > 0 AND p2.charid > 0 ORDER BY a.main ASC",
            [self.dimension, self.dimension])
        with self.db.transaction():
            current_main = 0
            group_id = 0
            for row in data:
                if row.main_char_id != current_main:
                    current_main = row.main_char_id
                    group_id = self.db.query_single(
                        "SELECT (COALESCE(MAX(group_id), 0) + 1) AS next_group_id FROM alts"
                    ).next_group_id
                    self.db.exec("DELETE FROM alts WHERE char_id = ?",
                                 [row.main_char_id])
                    self.db.exec(
                        "INSERT INTO alts (char_id, group_id, status) VALUES (?, ?, ?)",
                        [row.main_char_id, group_id, AltsService.MAIN])

                self.db.exec("DELETE FROM alts WHERE char_id = ?",
                             [row.alt_char_id])
                self.db.exec(
                    "INSERT INTO alts (char_id, group_id, status) VALUES (?, ?, ?)",
                    [row.alt_char_id, group_id, AltsService.CONFIRMED])

        return f"Successfully migrated <highlight>%d</highlight> alt characters." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("members")],
             access_level="superadmin",
             description="Migrate members from a Budabot database")
    def migrate_budabot_members_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT m.name AS sender, p.charid AS char_id, m.autoinv AS auto_invite "
            f"FROM members_{self.bot_name} m JOIN players p ON (m.name = p.name AND p.dimension = ?) "
            "WHERE p.charid > 0", [self.dimension])

        num = 0
        for row in data:
            char_id = self.resolve_to_char_id(row.sender, row.char_id)

            if char_id:
                num += 1
                self.db.exec("DELETE FROM member WHERE char_id = ?",
                             [row.char_id])
                self.db.exec(
                    "INSERT INTO member (char_id, auto_invite) VALUES (?, ?)",
                    [row.char_id, row.auto_invite])

        return f"Successfully migrated <highlight>{num}</highlight> members."

    @command(command="budabot",
             params=[Const("migrate"), Const("quotes")],
             access_level="superadmin",
             description="Migrate quotes from a Budabot database")
    def migrate_budabot_quotes_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT q.poster, p.charid AS char_id, q.id, q.msg, q.dt "
            "FROM quote q LEFT JOIN players p ON (q.poster = p.name AND p.dimension = ?)",
            [self.dimension])
        count_inactive = 0

        request.reply("Processing %s quote records..." % len(data))

        for row in data:
            char_id = self.resolve_to_char_id(row.poster, row.char_id)

            if not char_id:
                char_id = -1
                count_inactive += 1

            self.db.exec("DELETE FROM quote WHERE id = ?", [row.id])
            self.db.exec(
                "INSERT INTO quote (id, char_id, created_at, content) VALUES (?, ?, ?, ?)",
                [row.id, char_id, row.dt, row.msg])

        return f"Quotes successfully migrated. <highlight>{count_inactive}</highlight> posters were inactive and could not be resolved to char ids."

    @command(command="budabot",
             params=[Const("migrate"), Const("log_messages")],
             access_level="superadmin",
             description="Migrate log messages from a Budabot database")
    def migrate_budabot_log_messages_cmd(self, request, _1, _2):
        data = self.db2.query(
            f"SELECT p2.charid AS char_id, p1.sender, p1.name, p1.value "
            f"FROM preferences_{self.bot_name} p1 LEFT JOIN players p2 ON (p1.sender = p2.name AND p2.dimension = ?) "
            "WHERE p1.name = 'logon_msg' OR p1.name = 'logoff_msg'",
            [self.dimension])
        count_inactive = 0
        count_logon = 0
        count_logoff = 0

        request.reply("Processing %s log messages records..." % len(data))

        for row in data:
            char_id = self.resolve_to_char_id(row.sender, row.char_id)

            if not char_id:
                count_inactive += 1
            else:
                existing = self.db.query_single(
                    "SELECT 1 FROM log_messages WHERE char_id = ?", [char_id])

                if not existing:
                    self.db.exec(
                        "INSERT INTO log_messages (char_id, logon, logoff) VALUES (?, NULL, NULL)",
                        [char_id])

                if row.name == 'logon_msg' and row.value:
                    self.db.exec(
                        "UPDATE log_messages SET logon = ? WHERE char_id = ?",
                        [row.value, char_id])
                    count_logon += 1
                elif row.name == 'logoff_msg' and row.value:
                    self.db.exec(
                        "UPDATE log_messages SET logoff = ? WHERE char_id = ?",
                        [row.value, char_id])
                    count_logoff += 1

        return f"<highlight>{count_logon}</highlight> logon and <highlight>{count_logoff}</highlight> logoff messages successfully migrated. " \
               f"<highlight>{count_inactive}</highlight> messages were from inactive characters that could not be resolved to char ids."

    @command(command="budabot",
             params=[Const("migrate"), Const("name_history")],
             access_level="superadmin",
             description="Migrate name history from a Budabot database")
    def migrate_budabot_name_history_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT charid AS char_id, name, dt AS created_at FROM name_history"
        )

        request.reply(
            "Processing %s name history records. This may take some time..." %
            len(data))

        with self.db.transaction():
            for row in data:
                self.db.exec(
                    "DELETE FROM name_history WHERE char_id = ? AND name = ?",
                    [row.char_id, row.name])
                self.db.exec(
                    "INSERT INTO name_history (char_id, name, created_at) VALUES (?, ?, ?)",
                    [row.char_id, row.name, row.created_at])

        return f"Successfully migrated <highlight>%d</highlight> name history records." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("news")],
             access_level="superadmin",
             description="Migrate news from a Budabot database")
    def migrate_budabot_news_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT n.name AS poster, p.charid AS char_id, news, sticky, time AS created_at, deleted AS deleted_at "
            "FROM news n JOIN players p ON (n.name = p.name AND p.dimension = ?) WHERE p.charid > 0",
            [self.dimension])
        for row in data:
            char_id = self.resolve_to_char_id(row.poster, row.char_id)

            if not char_id:
                char_id = -1

            self.db.exec("DELETE FROM news WHERE char_id = ? AND news = ?",
                         [char_id, row.news])
            self.db.exec(
                "INSERT INTO news (char_id, news, sticky, created_at, deleted_at) VALUES (?, ?, ?, ?, ?)",
                [
                    char_id, row.news, row.sticky, row.created_at,
                    row.deleted_at
                ])

        return f"Successfully migrated <highlight>%d</highlight> news records." % len(
            data)

    @command(command="budabot",
             params=[Const("migrate"), Const("notes")],
             access_level="superadmin",
             description="Migrate notes from a Budabot database")
    def migrate_budabot_notes_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT n.added_by AS sender, p.charid AS char_id, n.note, n.dt AS created_at "
            "FROM notes n JOIN players p ON (p.name = n.added_by AND p.dimension = ?) WHERE p.charid > 0",
            [self.dimension])

        num = 0
        for row in data:
            char_id = self.resolve_to_char_id(row.sender, row.char_id)

            if char_id:
                num += 1
                self.db.exec(
                    "DELETE FROM notes WHERE char_id = ? AND note = ?",
                    [char_id, row.note])
                self.db.exec(
                    "INSERT INTO notes (char_id, note, created_at) VALUES (?, ?, ?)",
                    [char_id, row.note, row.created_at])

        return f"Successfully migrated <highlight>{num}</highlight> note records."

    @command(command="budabot",
             params=[Const("migrate"), Const("last_seen")],
             access_level="superadmin",
             description="Migrate last_seen data from a Budabot database")
    def migrate_budabot_last_seen_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT o.name AS sender, p.charid AS char_id, logged_off AS last_seen "
            f"FROM org_members_{self.bot_name} o JOIN players p ON (o.name = p.name AND p.dimension = ?) "
            "WHERE p.charid > 0", [self.dimension])

        num = 0
        with self.db.transaction():
            for row in data:
                char_id = self.resolve_to_char_id(row.sender, row.char_id)

                if char_id:
                    num += 1
                    self.db.exec("DELETE FROM last_seen WHERE char_id = ?",
                                 [char_id])
                    self.db.exec(
                        "INSERT INTO last_seen (char_id, dt) VALUES (?, ?)",
                        [char_id, row.last_seen])

        return f"Successfully migrated <highlight>{num}</highlight> last seen records."

    @command(command="budabot",
             params=[Const("migrate"), Const("cloak_status")],
             access_level="superadmin",
             description="Migrate cloak status records from a Budabot database"
             )
    def migrate_budabot_cloak_status_cmd(self, request, _1, _2):
        if not self.org_id:
            return "Could not migrate cloak status record since org id is not set."

        data = self.db2.query(
            "SELECT o.player AS name, p.charid AS char_id, action, time AS created_at "
            f"FROM org_city_{self.bot_name} o JOIN players p ON (o.player = p.name AND p.dimension = ?) "
            "WHERE p.charid > 0", [self.dimension])

        num = 0
        with self.db.transaction():
            self.db.exec("DELETE FROM cloak_status WHERE org_id = ?",
                         [self.org_id])
            for row in data:
                char_id = self.resolve_to_char_id(row.name, row.char_id)

                if char_id:
                    num += 1
                    self.db.exec(
                        "INSERT INTO cloak_status (char_id, action, created_at, org_id) VALUES (?, ?, ?, ?)",
                        [char_id, row.action, row.created_at, self.org_id])

        return f"Successfully migrated <highlight>{num}</highlight> cloak status records."

    @command(command="budabot",
             params=[Const("migrate"), Const("org_activity")],
             access_level="superadmin",
             description="Migrate org activity records from a Budabot database"
             )
    def migrate_budabot_org_activity_cmd(self, request, _1, _2):
        if not self.org_id:
            return "Could not migrate cloak status record since org id is not set."

        request.reply("Processing records. This may take some time...")

        data = self.db2.query(
            "SELECT o.actor AS actor_name, p1.charid AS actor_char_id, o.actee AS actee_name, p2.charid AS actee_char_id, action, time AS created_at "
            "FROM org_history o JOIN players p1 ON (o.actor = p1.name AND p1.dimension = ?) JOIN players p2 ON (o.actee = p2.name AND p2.dimension = ?) "
            "WHERE p1.charid > 0 AND p2.charid > 0",
            [self.dimension, self.dimension])

        num = 0
        with self.db.transaction():
            self.db.exec("DELETE FROM org_activity WHERE org_id = ?",
                         [self.org_id])
            for row in data:
                actor_char_id = self.resolve_to_char_id(
                    row.actor_name, row.actor_char_id)
                actee_char_id = self.resolve_to_char_id(
                    row.actee_name, row.actee_char_id)

                if actor_char_id and actee_char_id:
                    num += 1
                    self.db.exec(
                        "INSERT INTO org_activity (actor_char_id, actee_char_id, action, created_at, org_id) VALUES (?, ?, ?, ?, ?)",
                        [
                            actor_char_id, actee_char_id, row.action,
                            row.created_at, self.org_id
                        ])

        return f"Successfully migrated <highlight>{num}</highlight> org activity records."

    @command(
        command="budabot",
        params=[Const("migrate"), Const("players")],
        access_level="superadmin",
        description="Migrate character info records from a Budabot database")
    def migrate_budabot_player_cmd(self, request, _1, _2):
        data = self.db2.query(
            "SELECT * FROM players WHERE charid > 0 AND dimension = ?",
            [self.dimension])

        request.reply("Processing %s records. This may take some time..." %
                      len(data))

        num = 0
        with self.db.transaction():
            for row in data:
                if row.charid:
                    num += 1
                    self.db.exec("DELETE FROM player WHERE char_id = ?",
                                 [row.charid])
                    self.db.exec(
                        "INSERT INTO player (ai_level, ai_rank, breed, char_id, dimension, faction, first_name, gender, head_id, last_name, "
                        "last_updated, level, name, org_id, org_name, org_rank_id, org_rank_name, profession, profession_title, pvp_rating, pvp_title, source) "
                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                        [
                            row.ai_level, row.ai_rank, row.breed, row.charid,
                            row.dimension, row.faction, row.firstname,
                            row.gender, row.head_id if row.head_id else 0,
                            row.lastname, row.last_update, row.level, row.name,
                            row.guild_id, row.guild, row.guild_rank_id or 0,
                            row.guild_rank, row.profession, row.prof_title,
                            row.pvp_rating if row.pvp_rating else 0,
                            row.pvp_title if row.pvp_title else "", row.source
                        ])

            # maybe this is needed also? self.db.exec("DELETE FROM player WHERE char_id = 4294967295")

        return f"Successfully migrated <highlight>{num}</highlight> character info records."

    def resolve_to_char_id(self, name, char_id):
        if char_id and char_id > 0:
            return char_id

        char_id = self.character_service.resolve_char_to_id(name)
        if char_id:
            return char_id

        char_info = self.pork_service.get_character_info(name)
        if char_info:
            return char_info.char_id

        return None
Пример #22
0
class AdminController:
    def __init__(self):
        pass

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.admin_manager = registry.get_instance("admin_manager")
        self.character_manager = registry.get_instance("character_manager")
        self.pork_manager = registry.get_instance("pork_manager")

    @command(command="admin",
             params=[],
             access_level="all",
             description="Show the admin list")
    def admin_list_cmd(self, channel, sender, reply, args):
        admins = self.admin_manager.get_all()
        superadmin = self.pork_manager.get_character_info(self.bot.superadmin)
        superadmin.access_level = "superadmin"
        admins.insert(0, superadmin)

        blob = ""
        current_access_level = ""
        for row in admins:
            if row.access_level != current_access_level:
                blob += "\n<header2>%s<end>\n" % row.access_level.capitalize()
                current_access_level = row.access_level

            blob += row.name + "\n"

        reply(ChatBlob("Admin List (%d)" % len(admins), blob))

    @command(command="admin",
             params=[Const("add"), Any("character")],
             access_level="superadmin",
             description="Add an admin",
             sub_command="modify")
    def admin_add_cmd(self, channel, sender, reply, args):
        name = args[1].capitalize()
        char_id = self.character_manager.resolve_char_to_id(name)

        if not char_id:
            reply("Could not find character <highlight>%s<end>." % name)
            return

        if self.admin_manager.add(char_id, AdminManager.ADMIN):
            reply(
                "Character <highlight>%s<end> added as <highlight>%s<end> successfully."
                % (name, AdminManager.ADMIN))
        else:
            reply(
                "Could not add character <highlight>%s<end> as <highlight>%s<end>."
                % (name, AdminManager.ADMIN))

    @command(command="admin",
             params=[Options(["remove", "rem"]),
                     Any("character")],
             access_level="superadmin",
             description="Remove an admin",
             sub_command="modify")
    def admin_remove_cmd(self, channel, sender, reply, args):
        name = args[1].capitalize()
        char_id = self.character_manager.resolve_char_to_id(name)

        if not char_id:
            reply("Could not find character <highlight>%s<end>." % name)
            return

        if self.admin_manager.remove(char_id):
            reply(
                "Character <highlight>%s<end> removed as <highlight>%s<end> successfully."
                % (name, AdminManager.ADMIN))
        else:
            reply(
                "Could not remove character <highlight>%s<end> as <highlight>%s<end>."
                % (name, AdminManager.ADMIN))

    @command(command="moderator",
             params=[Const("add"), Any("character")],
             access_level="superadmin",
             description="Add a moderator",
             sub_command="modify")
    def moderator_add_cmd(self, channel, sender, reply, args):
        name = args[1].capitalize()
        char_id = self.character_manager.resolve_char_to_id(name)

        if not char_id:
            reply("Could not find character <highlight>%s<end>." % name)
            return

        if self.admin_manager.add(char_id, AdminManager.MODERATOR):
            reply(
                "Character <highlight>%s<end> added as <highlight>%s<end> successfully."
                % (name, AdminManager.MODERATOR))
        else:
            reply(
                "Could not add character <highlight>%s<end> as <highlight>%s<end>."
                % (name, AdminManager.MODERATOR))

    @command(command="moderator",
             params=[Options(["remove", "rem"]),
                     Any("character")],
             access_level="superadmin",
             description="Remove a moderator",
             sub_command="modify")
    def moderator_remove_cmd(self, channel, sender, reply, args):
        name = args[1].capitalize()
        char_id = self.character_manager.resolve_char_to_id(name)

        if not char_id:
            reply("Could not find character <highlight>%s<end>." % name)
            return

        if self.admin_manager.remove(char_id):
            reply(
                "Character <highlight>%s<end> removed as <highlight>%s<end> successfully."
                % (name, AdminManager.MODERATOR))
        else:
            reply(
                "Could not remove character <highlight>%s<end> as <highlight>%s<end>."
                % (name, AdminManager.MODERATOR))
Пример #23
0
class AuctionController:
    def __init__(self):
        self.auction: AuctionStrategy = None

    @setting(name="auction_length",
             value="90s",
             description="Regular auction length in seconds")
    def auction_length(self):
        return TimeSettingType()

    @setting(name="auction_announce_interval",
             value="15s",
             description="Auction announce interval")
    def auction_announce_interval(self):
        return TimeSettingType()

    @setting(name="auction_late_bid_extension",
             value="10s",
             description="How much to extend auction in case of late bid")
    def auction_late_bid_extension(self):
        return TimeSettingType()

    @setting(name="auction_late_bid_threshold",
             value="10s",
             description="Threshold for a bid to be considered late")
    def auction_late_bid_threshold(self):
        return TimeSettingType()

    @command(command="auction",
             params=[],
             description="Show auction status",
             access_level="member")
    def auction_cmd(self, request):
        if not self.is_auction_running():
            return "No auction running."

        return self.auction.get_auction_list()

    @command(command="auction",
             params=[Const("start"), Any("items")],
             description="Start an auction, with one or more items",
             access_level="moderator",
             sub_command="modify")
    def auction_start_cmd(self, request, _, items):
        if self.is_auction_running():
            return "Auction already running."

        items = re.findall(
            r"(([^<]+)?<a href=[\"\']itemref://(\d+)/(\d+)/(\d+)[\"\']>([^<]+)</a>([^<]+)?)",
            items)
        # TODO choose auction strategy impl
        self.auction = AuctionStrategy()
        for item in items:
            self.auction.add_item(item[0])

        auction_length = self.auction_length().get_value()
        announce_interval = self.auction_announce_interval().get_value()
        late_bid_threshold = self.auction_late_bid_threshold().get_value()
        late_bid_extension = self.auction_late_bid_extension().get_value()

        return self.auction.start(request.sender, auction_length,
                                  announce_interval, late_bid_threshold,
                                  late_bid_extension)

    @command(command="auction",
             params=[Options(["cancel", "end"])],
             description="Cancel ongoing auction",
             access_level="moderator",
             sub_command="modify")
    def auction_cancel_cmd(self, request, _):
        if not self.is_auction_running():
            return "No auction running."

        result = self.auction.cancel(request.sender)
        self.auction = None
        return result

    @command(command="auction",
             params=[
                 Const("bid"),
                 Int("amount", is_optional=True),
                 Const("all", is_optional=True),
                 Int("item_index", is_optional=True)
             ],
             description="Bid on an item",
             access_level="member")
    def auction_bid_cmd(self, request, _, amount, all_amount, item_index):
        if not self.is_auction_running():
            return "No auction running."

        return self.auction.add_bid(request.sender, all_amount or amount,
                                    item_index)

    def is_auction_running(self):
        return self.auction and self.auction.is_running
Пример #24
0
class TopicController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")
        self.private_channel_service: PrivateChannelService = registry.get_instance(
            "private_channel_service")

    def start(self):
        self.command_alias_service.add_alias("motd", "topic")

    @setting(name="topic", value="", description="The bot topic")
    def topic(self):
        return DictionarySettingType()

    @command(command="topic",
             params=[],
             access_level="all",
             description="Show the current topic")
    def topic_show_command(self, request):
        topic = self.topic().get_value()
        if topic:
            return self.format_topic_message(topic)
        else:
            return "There is no current topic."

    @command(command="topic",
             params=[Options(["clear", "unset"])],
             access_level="all",
             description="Clears the current topic")
    def topic_clear_command(self, request, _):
        self.topic().set_value("")

        return "The topic has been cleared."

    @command(command="topic",
             params=[Const("set", is_optional=True),
                     Any("topic_message")],
             access_level="all",
             description="Set the current topic")
    def topic_set_command(self, request, _, topic_message):
        sender = DictObject({
            "name": request.sender.name,
            "char_id": request.sender.char_id
        })

        topic = {
            "topic_message": topic_message,
            "created_by": sender,
            "created_at": int(time.time())
        }

        self.topic().set_value(topic)

        return "The topic has been set."

    def format_topic_message(self, topic):
        time_string = self.util.time_to_readable(
            int(time.time()) - topic["created_at"])
        return "Topic: <highlight>%s<end> [set by <highlight>%s<end>][%s ago]" % (
            topic["topic_message"], topic["created_by"]["name"], time_string)

    @event(PrivateChannelService.JOINED_PRIVATE_CHANNEL_EVENT,
           "Show topic to characters joining the private channel")
    def show_topic(self, _, event_data):
        topic = self.topic().get_value()
        if topic:
            self.bot.send_private_message(event_data.char_id,
                                          self.format_topic_message(topic))

    @event(PrivateChannelService.LEFT_PRIVATE_CHANNEL_EVENT,
           "Clear topic when there are no characters in the private channel")
    def clear_topic(self, _, event_data):
        if self.topic().get_value() and len(self.private_channel_service.
                                            get_all_in_private_channel()) == 0:
            self.topic().set_value("")
Пример #25
0
class CloakController:
    MESSAGE_SOURCE = "cloak_reminder"
    CLOAK_EVENT = "cloak"

    CLOAK_STATUS_OFF = "off"
    CLOAK_STATUS_ON = "on"
    CLOAK_STATUS_MANUAL = "on*"

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.util = registry.get_instance("util")
        self.text = registry.get_instance("text")
        self.character_service = registry.get_instance("character_service")
        self.command_alias_service = registry.get_instance("command_alias_service")
        self.timer_controller = registry.get_instance("timer_controller", is_optional=True)
        self.event_service = registry.get_instance("event_service")
        self.access_service = registry.get_instance("access_service")
        self.message_hub_service = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.bot.register_packet_handler(PublicChannelMessage.id, self.handle_public_message)
        self.event_service.register_event_type(self.CLOAK_EVENT)
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.db.exec("CREATE TABLE IF NOT EXISTS cloak_status (char_id INT NOT NULL, action VARCHAR(10) NOT NULL, created_at INT NOT NULL, org_id INT NOT NULL)")
        self.command_alias_service.add_alias("city", "cloak")

    @command(command="cloak", params=[Const("history"), Int("org_id")], access_level="org_member",
             description="Shows the cloak history")
    def cloak_history_command(self, request, _, org_id):
        data = self.db.query("SELECT c.*, p.name FROM cloak_status c LEFT JOIN player p ON c.char_id = p.char_id "
                             "WHERE c.org_id = ? ORDER BY created_at DESC LIMIT 20", [org_id])

        blob = ""
        for row in data:
            action = self.get_cloak_status_display(row.action)
            blob += "%s turned the device %s at %s.\n" % (row.name, action, self.util.format_datetime(row.created_at))

        conn = self.bot.get_conn_by_org_id(org_id)
        org_name = conn.get_org_name()
        return ChatBlob(f"Cloak History for {org_name}", blob)

    @command(command="cloak", params=[NamedFlagParameters(["all"])], access_level="org_member",
             description="Shows the cloak status")
    def cloak_command(self, request, flag_params):
        t = int(time.time())

        if flag_params.all:
            blob = ""
            for _id, conn in self.bot.get_conns(lambda x: x.is_main and x.org_id):
                row = self.db.query_single("SELECT c.char_id, c.action, c.created_at, p.name FROM cloak_status c LEFT JOIN player p ON c.char_id = p.char_id "
                                           "WHERE c.org_id = ? ORDER BY created_at DESC LIMIT 1", [conn.org_id])

                org_name = conn.get_org_name()
                if row:
                    action = self.get_cloak_status_display(row.action)
                    time_str = self.util.time_to_readable(t - row.created_at)
                    history_link = self.text.make_tellcmd("History", f"cloak history {conn.org_id}")
                    blob += f"{org_name} - {action} [{time_str} ago] {history_link}\n"
                else:
                    blob += f"{org_name} - Unknown\n"

            title = "Cloak Status"

            return ChatBlob(title, blob)
        else:
            conn = request.conn
            if not conn.org_id:
                return "This bot is not a member of an org."

            row = self.db.query_single("SELECT c.char_id, c.action, c.created_at, p.name FROM cloak_status c LEFT JOIN player p ON c.char_id = p.char_id "
                                       "WHERE c.org_id = ? ORDER BY created_at DESC LIMIT 1", [conn.org_id])

            org_name = conn.get_org_name()
            if row:
                action = self.get_cloak_status_display(row.action)
                time_str = self.util.time_to_readable(t - row.created_at)
                return f"{org_name} - {action} [{time_str} ago]"
            else:
                return f"{org_name} - Unknown cloak status"

    @command(command="cloak", params=[Options(["raise", "on"]), Int("org_id", is_optional=True)], access_level="org_member",
             description="Manually raises the cloak status on the bot")
    def cloak_raise_command(self, request, _, org_id):
        org_id = org_id or request.conn.org_id

        if not org_id:
            return "This bot is not a member of an org."

        row = self.db.query_single("SELECT action FROM cloak_status WHERE org_id = ?", [org_id])
        if row and (row.action == self.CLOAK_STATUS_ON or row.action == self.CLOAK_STATUS_MANUAL):
            return "The cloaking device is already <green>enabled</green>."

        self.db.exec("INSERT INTO cloak_status (char_id, action, created_at, org_id) VALUES (?, ?, ?, ?)",
                     [request.sender.char_id, self.CLOAK_STATUS_MANUAL, int(time.time()), org_id])
        return "The cloaking device has been set to enabled in the bot (you must still enable the cloak if it is disabled)."

    @event(event_type=CLOAK_EVENT, description="Record when the city cloak is turned off and on", is_system=True)
    def city_cloak_event(self, event_type, event_data):
        self.db.exec("INSERT INTO cloak_status (char_id, action, created_at, org_id) VALUES (?, ?, ?, ?)",
                     [event_data.char_id, event_data.action, int(time.time()), event_data.conn.org_id])

    @timerevent(budatime="15m", description="Reminds the players when cloak can be raised")
    def cloak_reminder_event(self, event_type, event_data):
        messages = []
        for _id, conn in self.bot.get_conns(lambda x: x.is_main and x.org_id):
            row = self.db.query_single("SELECT c.*, p.name FROM cloak_status c LEFT JOIN player p ON c.char_id = p.char_id "
                                       "WHERE c.org_id = ? ORDER BY created_at DESC LIMIT 1", [conn.org_id])
            if row:
                one_hour = 3600
                t = int(time.time())
                time_until_change = row.created_at + one_hour - t
                if row.action == self.CLOAK_STATUS_OFF and 0 >= time_until_change > (one_hour * 6 * -1):
                    time_str = self.util.time_to_readable(t - row.created_at)
                    org_name = conn.get_org_name()
                    messages.append(f"The cloaking device for org <highlight>{org_name}</highlight> is <orange>disabled</orange> but can be enabled. "
                                    f"<highlight>{row.name}</highlight> disabled it {time_str} ago.")

        if messages:
            self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, None, "\n".join(messages))

    @event(event_type=CLOAK_EVENT, description="Set a timer for when cloak can be raised and lowered")
    def city_cloak_timer_event(self, event_type, event_data):
        if event_data.action == self.CLOAK_STATUS_OFF:
            timer_name = f"Raise City Cloak ({event_data.conn.get_org_name()})"
        elif event_data.action == self.CLOAK_STATUS_ON:
            timer_name = f"Lower City Cloak ({event_data.conn.get_org_name()})"
        else:
            raise Exception(f"Unknown cloak action '{event_data.action}'")

        timer_name = self.timer_controller.get_timer_name(timer_name)

        self.timer_controller.add_timer(timer_name, event_data.char_id, PublicChannelService.ORG_CHANNEL_COMMAND, int(time.time()), 3600)

    def handle_public_message(self, conn: Conn, packet: PublicChannelMessage):
        if not conn.is_main:
            return

        ext_msg = packet.extended_message
        if ext_msg and ext_msg.category_id == 1001 and ext_msg.instance_id == 1:
            char_name = ext_msg.params[0]
            char_id = self.character_service.resolve_char_to_id(char_name)
            action = ext_msg.params[1]
            self.event_service.fire_event(self.CLOAK_EVENT, DictObject({"char_id": char_id, "name": char_name, "action": action, "conn": conn}))

    def get_cloak_status_display(self, status):
        if status == self.CLOAK_STATUS_ON:
            return "<green>on</green>"
        elif status == self.CLOAK_STATUS_MANUAL:
            return "<green>on*</green>"
        elif status == self.CLOAK_STATUS_OFF:
            return "<orange>off</orange>"
        else:
            return "<highlight>Unknown</highlight>"
Пример #26
0
class LogController:
    def inject(self, registry):
        self.db: DB = registry.get_instance("db")

    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS log_messages (char_id INT NOT NULL PRIMARY KEY, logon TEXT, logoff TEXT)"
        )

    @command(command="logon",
             params=[],
             access_level="member",
             description="Check your current logon message")
    def check_current_logon(self, request):
        current_logon = self.get_logon(request.sender.char_id)
        if current_logon:
            return "%s's logon message is: %s" % (request.sender.name,
                                                  current_logon)
        else:
            return "Your logon message has not been set."

    @command(command="logon",
             params=[Const("clear")],
             access_level="member",
             description="Clear your logon message")
    def clear_logon(self, request, params):
        if self.db.query_single(
                "SELECT logon FROM log_messages WHERE char_id=?;",
            [request.sender.char_id]):
            self.db.exec("UPDATE log_messages SET logon=NULL WHERE char_id=?;",
                         [request.sender.char_id])
        return "Your logon message has been cleared."

    @command(command="logon",
             params=[Any("logon_message")],
             access_level="member",
             description="Set your logon message")
    def set_logon(self, request: CommandRequest, logon_message):
        if self.db.query_single(
                "SELECT logon FROM log_messages WHERE char_id=?;",
            [request.sender.char_id]):
            self.db.exec("UPDATE log_messages SET logon=? WHERE char_id=?;",
                         [logon_message, request.sender.char_id])
        else:
            self.db.exec(
                "INSERT INTO log_messages (char_id, logon) VALUES(?, ?);",
                [request.sender.char_id, logon_message])
        return "Your new logon message is: %s" % logon_message

    @command(command="logoff",
             params=[],
             access_level="member",
             description="Check your current logoff message")
    def check_current_logoff(self, request):
        current_logoff = self.get_logoff(request.sender.char_id)
        if current_logoff:
            return "%s's logoff message is: %s" % (request.sender.name,
                                                   current_logoff)
        else:
            return "Your logoff message has not been set."

    @command(command="logoff",
             params=[Const("clear")],
             access_level="member",
             description="Clear your logoff message")
    def clear_logoff(self, request, params):
        if self.db.query_single(
                "SELECT logoff FROM log_messages WHERE char_id=?;",
            [request.sender.char_id]):
            self.db.exec(
                "UPDATE log_messages SET logoff=NULL WHERE char_id=?;",
                [request.sender.char_id])
        return "Your logoff message has been cleared."

    @command(command="logoff",
             params=[Any("logoff_message")],
             access_level="member",
             description="Set your logoff message")
    def set_logoff(self, request: CommandRequest, logoff_message):
        if self.db.query_single(
                "SELECT logoff FROM log_messages WHERE char_id=?;",
            [request.sender.char_id]):
            self.db.exec("UPDATE log_messages SET logoff=? WHERE char_id=?;",
                         [logoff_message, request.sender.char_id])
        else:
            self.db.exec(
                "INSERT INTO log_messages (char_id, logoff) VALUES(?, ?);",
                [request.sender.char_id, logoff_message])
        return "Your new logoff message is: %s" % logoff_message

    def get_logon(self, char_id):
        row = self.db.query_single(
            "SELECT * FROM log_messages WHERE char_id=?", [char_id])
        if row and row.logon:
            return "<grey>" + row.logon + "</grey>"
        return ""

    def get_logoff(self, char_id):
        row = self.db.query_single(
            "SELECT * FROM log_messages WHERE char_id=?", [char_id])
        if row and row.logoff:
            return "<grey>" + row.logoff + "</grey>"
        return ""
Пример #27
0
class TowerController:
    def __init__(self):
        self.logger = Logger(__name__)

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")
        self.setting_service = registry.get_instance("setting_service")
        self.playfield_controller: PlayfieldController = registry.get_instance(
            "playfield_controller")
        self.level_controller = registry.get_instance("level_controller")

    def pre_start(self):
        self.db.load_sql_file(self.module_dir + "/" + "tower_site.sql")
        self.db.load_sql_file(self.module_dir + "/" + "tower_site_bounds.sql")

    def start(self):
        self.command_alias_service.add_alias("hot", "lc open")

        self.setting_service.register(
            self.module_name, "tower_api_address",
            "https://tower-api.jkbff.com/v1/api/towers",
            TextSettingType(["https://tower-api.jkbff.com/v1/api/towers"]),
            "The address of the Tower API")
        self.setting_service.register(self.module_name,
                                      "tower_api_custom_headers", "",
                                      DictionarySettingType(),
                                      "Custom headers for the Tower API")

    @command(command="lc",
             params=[],
             access_level="guest",
             description=
             "See a list of playfields containing land control tower sites")
    def lc_list_cmd(self, request):
        data = self.db.query(
            "SELECT id, long_name, short_name FROM playfields WHERE id IN (SELECT DISTINCT playfield_id FROM tower_site) ORDER BY short_name"
        )

        blob = ""
        for row in data:
            blob += "[%d] %s <highlight>%s</highlight>\n" % (
                row.id,
                self.text.make_tellcmd(
                    row.long_name, "lc %s" % row.short_name), row.short_name)

        blob += "\n" + self.get_lc_blob_footer()

        return ChatBlob("Land Control Playfields (%d)" % len(data), blob)

    if FeatureFlags.USE_TOWER_API:

        @command(command="lc",
                 params=[Const("org"),
                         Any("org", is_optional=True)],
                 access_level="guest",
                 description="See a list of land control tower sites by org")
        def lc_org_cmd(self, request, _, org):
            params = list()
            params.append(("enabled", "true"))
            if not org:
                org = str(request.conn.org_id)
                if not org:
                    return "Bot does not belong to an org so an org name or org id must be specified."

            if org.isdigit():
                params.append(("org_id", org))
            else:
                for org_name_piece in org.split(" "):
                    params.append(("org_name", "%" + org_name_piece + "%"))

            data = self.lookup_tower_info(params).results

            t = int(time.time())
            grouped_data = self.util.group_by(data, lambda x:
                                              (x.org_id, x.org_name))
            blob = ""
            for k, v in grouped_data.items():
                v = sorted(v, key=lambda x: x.ql)

                org_blob = ""
                ct_types = []
                ql_total = 0
                for ct in v:
                    ct_types.append(self.get_ct_type(ct.ql))
                    ql_total += ct.ql
                    org_blob += self.format_site_info(ct, t) + "\n"

                blob += f"<pagebreak><header2>{k[1]} ({k[0]})</header2>"
                blob += " Types: <highlight>" + ", ".join(
                    ct_types
                ) + f"</highlight> Total CT QL: <highlight>{ql_total}</highlight>\n\n"
                blob += org_blob + "\n"

            return ChatBlob(f"Org Info for '{org}' ({len(data)})", blob)

        @command(
            command="lc",
            params=[
                Options([
                    "all", "open", "closed", "penalty", "unplanted", "disabled"
                ]),
                Options(["omni", "clan", "neutral", "all"], is_optional=True),
                Int("pvp_level", is_optional=True),
                Time("time", is_optional=True)
            ],
            access_level="guest",
            description=
            "See a list of land control tower sites by QL, faction, and open status",
            extended_description=
            "The time param only applies when the first param is either 'open' or 'closed'"
        )
        def lc_search_cmd(self, request, site_status, faction, pvp_level,
                          time_offset):
            t = int(time.time())
            relative_time = t + (time_offset or 0)

            min_ql = 1
            max_ql = 300
            if pvp_level:
                level_info = self.level_controller.get_level_info(pvp_level)
                if not level_info:
                    return "PVP level must be between 1 and 220."
                min_ql = level_info.pvp_min
                max_ql = level_info.pvp_max

            params = list()

            if site_status.lower() == "disabled":
                params.append(("enabled", "false"))
            else:
                params.append(("enabled", "true"))

            if min_ql > 1:
                params.append(("min_ql", min_ql))

            if max_ql < 300:
                params.append(("max_ql", max_ql))

            if faction and faction != "all":
                params.append(("faction", faction))

            if site_status.lower() == "open":
                params.append(("min_close_time", relative_time))
                params.append(("max_close_time", relative_time + (3600 * 6)))
            elif site_status.lower() == "closed":
                params.append(("min_close_time", relative_time + (3600 * 6)))
                params.append(("max_close_time", relative_time + (3600 * 24)))
            elif site_status.lower() == "penalty":
                params.append(("penalty", "true"))
            elif site_status.lower() == "unplanted":
                params.append(("planted", "false"))

            data = self.lookup_tower_info(params).results

            blob = ""
            for row in data:
                blob += "<pagebreak>" + self.format_site_info(row, t) + "\n"

            if blob:
                blob += self.get_lc_blob_footer()

            title = "Tower Info: %s" % site_status.capitalize()
            if min_ql > 1 or max_ql < 300:
                title += " QL %d - %d" % (min_ql, max_ql)
            if faction:
                title += " [%s]" % faction.capitalize()
            if time_offset:
                title += " in " + self.util.time_to_readable(time_offset)
            title += " (%d)" % len(data)

            return ChatBlob(title, blob)

    @command(command="lc",
             params=[Any("playfield"),
                     Int("site_number", is_optional=True)],
             access_level="guest",
             description=
             "See a list of land control tower sites in a particular playfield"
             )
    def lc_playfield_cmd(self, request, playfield_name, site_number):
        playfield = self.playfield_controller.get_playfield_by_name_or_id(
            playfield_name)
        if not playfield:
            return f"Could not find playfield <highlight>{playfield_name}</highlight>."

        data = self.get_tower_site_info(playfield.id, site_number)

        blob = ""
        t = int(time.time())
        for row in data:
            blob += "<pagebreak>" + self.format_site_info(row, t) + "\n"

        blob += self.get_lc_blob_footer()

        if site_number:
            title = "Tower Info: %s %d" % (playfield.long_name, site_number)
        else:
            title = "Tower Info: %s (%d)" % (playfield.long_name, len(data))

        return ChatBlob(title, blob)

    def format_site_info(self, row, t):
        blob = "<highlight>%s %d</highlight> (QL %d-%d) %s\n" % (
            row.playfield_short_name, row.site_number, row.min_ql, row.max_ql,
            row.site_name)

        if row.get("org_name"):
            current_day_time = t % 86400
            value = datetime.fromtimestamp(row.close_time, tz=pytz.UTC)
            current_status_time = row.close_time - current_day_time
            if current_status_time < 0:
                current_status_time += 86400

            status = ""
            if current_status_time <= 3600:
                status += "<red>5%%</red> (closes in %s)" % self.util.time_to_readable(
                    current_status_time)
            elif current_status_time <= (3600 * 6):
                status += "<orange>25%%</orange> (closes in %s)" % self.util.time_to_readable(
                    current_status_time)
            else:
                status += "<green>75%%</green> (opens in %s)" % self.util.time_to_readable(
                    current_status_time - (3600 * 6))

            if row.penalty_until > t:
                status += " <red>Penalty</red> (for %s)" % self.util.time_to_readable(
                    row.penalty_until - t)

            blob += "%s (%d) [%s] <highlight>QL %d</highlight> %s %s\n" % (
                row.org_name, row.org_id,
                self.text.get_formatted_faction(row.faction), row.ql,
                self.text.make_chatcmd(
                    "%dx%d" %
                    (row.x_coord, row.y_coord), "/waypoint %d %d %d" %
                    (row.x_coord, row.y_coord, row.playfield_id)),
                self.util.time_to_readable(t - row.created_at))
            blob += "Close Time: <highlight>%s</highlight> %s\n" % (
                value.strftime("%H:%M:%S %Z"), status)
        else:
            blob += "%s\n" % self.text.make_chatcmd(
                "%dx%d" % (row.x_coord, row.y_coord), "/waypoint %d %d %d" %
                (row.x_coord, row.y_coord, row.playfield_id))
            if not row.enabled:
                blob += "<red>Disabled</red>\n"

        return blob

    def get_tower_site_info(self, playfield_id, site_number):
        if FeatureFlags.USE_TOWER_API:
            params = list()
            params.append(("playfield_id", playfield_id))
            if site_number:
                params.append(("site_number", site_number))

            data = self.lookup_tower_info(params).results
        else:
            if site_number:
                data = self.db.query(
                    "SELECT t.*, p.short_name AS playfield_short_name, p.long_name AS playfield_long_name "
                    "FROM tower_site t JOIN playfields p ON t.playfield_id = p.id WHERE t.playfield_id = ? AND site_number = ?",
                    [playfield_id, site_number])
            else:
                data = self.db.query(
                    "SELECT t.*, p.short_name AS playfield_short_name, p.long_name AS playfield_long_name "
                    "FROM tower_site t JOIN playfields p ON t.playfield_id = p.id WHERE t.playfield_id = ?",
                    [playfield_id])

        return data

    def lookup_tower_info(self, params):
        url = self.setting_service.get("tower_api_address").get_value()

        headers = self.setting_service.get(
            "tower_api_custom_headers").get_value() or {}
        headers.update({"User-Agent": f"Tyrbot {self.bot.version}"})
        r = requests.get(url, params, headers=headers, timeout=5)
        result = DictObject(r.json())

        return result

    def get_lc_blob_footer(self):
        return "Thanks to Draex and Unk for providing the tower information. And a special thanks to Trey."

    def get_ct_type(self, ql):
        if ql < 34:
            return "I"
        elif ql < 82:
            return "II"
        elif ql < 129:
            return "III"
        elif ql < 177:
            return "IV"
        elif ql < 201:
            return "V"
        elif ql < 226:
            return "VI"
        else:
            return "VII"
Пример #28
0
class ConfigEventsController:
    def inject(self, registry):
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.command_service = registry.get_instance("command_service")
        self.event_service = registry.get_instance("event_service")
        self.setting_service = registry.get_instance("setting_service")
        self.ts: TranslationService = registry.get_instance("translation_service")
        self.getresp = self.ts.get_response

    @command(command="config", params=[Const("event"), Any("event_type"), Any("event_handler"), Options(["enable", "disable"])], access_level="admin",
             description="Enable or disable an event")
    def config_event_status_cmd(self, request, _, event_type, event_handler, action):
        event_type = event_type.lower()
        event_handler = event_handler.lower()
        action = action.lower()
        event_base_type, event_sub_type = self.event_service.get_event_type_parts(event_type)
        enabled = 1 if action == "enable" else 0

        if not self.event_service.is_event_type(event_base_type):
            return self.getresp("module/config", "unknown event", {"type", event_type})

        count = self.event_service.update_event_status(event_base_type, event_sub_type, event_handler, enabled)

        if count == 0:
            return self.getresp("module/config", "event_enable_fail", {"type": event_type, "handler": event_handler})
        else:
            action = self.getresp("module/config", "enabled_high" if action == "enable" else "disabled_high")
            return self.getresp("module/config", "event_enable_success", {"type": event_type,
                                                                          "handler": event_handler,
                                                                          "changedto": action})

    @command(command="config", params=[Const("event"), Any("event_type"), Any("event_handler"), Const("run")], access_level="admin",
             description="Execute a timed event immediately")
    def config_event_run_cmd(self, request, _1, event_type, event_handler, _2):
        action = "run"
        event_type = event_type.lower()
        event_handler = event_handler.lower()
        event_base_type, event_sub_type = self.event_service.get_event_type_parts(event_type)

        if not self.event_service.is_event_type(event_base_type):
            return self.getresp("module/config", "unknown event", {"type", event_type})

        row = self.db.query_single("SELECT e.event_type, e.event_sub_type, e.handler, t.next_run FROM timer_event t "
                                   "JOIN event_config e ON t.event_type = e.event_type AND t.handler = e.handler "
                                   "WHERE e.event_type = ? AND e.event_sub_type = ? AND e.handler LIKE ?", [event_base_type, event_sub_type, event_handler])

        if not row:
            return self.getresp("module/config", "event_enable_fail", {"type": event_type, "handler": event_handler})
        elif row.event_type != "timer":
            return self.getresp("module/config", "event_manual")
        else:
            self.event_service.execute_timed_event(row, int(time.time()))
            action = self.getresp("module/config", "run")
            return self.getresp("module/config", "event_enable_success", {"type": event_type,
                                                                          "handler": event_handler,
                                                                          "changedto": action})

    @command(command="config", params=[Const("eventlist")], access_level="admin",
             description="List all events")
    def config_eventlist_cmd(self, request, _):
        sql = "SELECT module, event_type, event_sub_type, handler, description, enabled FROM event_config WHERE is_hidden = 0"
        sql += " ORDER BY module, event_type, event_sub_type, handler"
        data = self.db.query(sql)

        blob = ""
        current_module = ""
        for row in data:
            if current_module != row.module:
                blob += "\n<pagebreak><header2>%s<end>\n" % row.module
                current_module = row.module

            event_type_key = self.format_event_type(row)

            on_link = self.text.make_chatcmd("On", "/tell <myname> config event %s %s enable" % (event_type_key, row.handler))
            off_link = self.text.make_chatcmd("Off", "/tell <myname> config event %s %s disable" % (event_type_key, row.handler))

            blob += "%s [%s] %s %s - %s\n" % (event_type_key, self.format_enabled(row.enabled), on_link, off_link, row.description)

        return ChatBlob(self.getresp("module/config", "blob_events", {"amount": len(data)}), blob)

    def format_enabled(self, enabled):
        return "<green>E<end>" if enabled else "<red>D<end>"

    def format_event_type(self, row):
        if row.event_sub_type:
            return row.event_type + ":" + row.event_sub_type
        else:
            return row.event_type
Пример #29
0
class RandomController:
    def inject(self, registry):
        self.db: DB = registry.get_instance("db")
        self.text: Text = registry.get_instance("text")
        self.util = registry.get_instance("util")
        self.character_service = registry.get_instance("character_service")
        self.command_alias_service = registry.get_instance(
            "command_alias_service")

    def start(self):
        self.command_alias_service.add_alias("verify", "roll verify")

    @command(
        command="random",
        params=[Any("items")],
        access_level="all",
        description="Randomly order a list of elements",
        extended_description="Enter a space-delimited list of items to randomize"
    )
    def random_command(self, request, items):
        items = items.split(" ")
        random.shuffle(items)
        return " ".join(items)

    @command(command="roll",
             params=[Int("start_value", is_optional=True),
                     Int("end_value")],
             access_level="all",
             description="Roll a number between 1 and a number")
    def roll_command(self, request, start_value, end_value):
        start_value = start_value or 1

        if start_value > end_value:
            start_value, end = end_value, start_value

        result = random.randint(start_value, end_value)

        self.db.exec(
            "INSERT INTO roll (char_id, min_value, max_value, result, created_at) VALUES (?, ?, ?, ?, ?)",
            [
                request.sender.char_id, start_value, end_value, result,
                int(time.time())
            ])

        return "Rolling between %d and %d: <highlight>%d<end>. /tell <myname> roll verify %d" % (
            start_value, end_value, result, self.db.last_insert_id())

    @command(command="roll",
             params=[Const("verify"), Int("roll_id")],
             access_level="all",
             description="Verify a roll that happened")
    def roll_verify_command(self, request, _, roll_id):
        row = self.db.query_single("SELECT * FROM roll WHERE id = ?",
                                   [roll_id])

        if not row:
            return "Could not find roll with id <highlight>%d<end>." % roll_id
        else:
            time_string = self.util.time_to_readable(
                int(time.time()) - row.created_at)
            name = self.character_service.resolve_char_to_name(row.char_id)
            return "Rolling between %d and %d: <highlight>%d<end>. %s ago for %s." % (
                row.min_value, row.max_value, row.result, time_string, name)
Пример #30
0
class AltsController:
    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.alts_service = registry.get_instance("alts_service")
        self.buddy_service = registry.get_instance("buddy_service")
        self.ts: TranslationService = registry.get_instance("translation_service")
        self.getresp = self.ts.get_response

    def start(self):
        self.ts.register_translation("module/alts", self.load_alts_msg)

    def load_alts_msg(self):
        with open("modules/core/alts/alts.msg", mode="r", encoding="UTF-8") as f:
            return hjson.load(f)

    @command(command="alts", params=[], access_level="all",
             description="Show your alts")
    def alts_list_cmd(self, request):
        alts = self.alts_service.get_alts(request.sender.char_id)
        blob = self.format_alt_list(alts)

        return ChatBlob(self.getresp("module/alts", "list", {"char": alts[0].name, "amount": len(alts)}), blob)

    def get_alt_status(self, status):
        if status == AltsService.MAIN:
            return " - [main]"
        else:
            return ""

    @command(command="alts", params=[Const("setmain")], access_level="all",
             description="Set a new main", extended_description="You must run this from the character you want to be your new main")
    def alts_setmain_cmd(self, request, _):
        msg, result = self.alts_service.set_as_main(request.sender.char_id)

        if result:
            return self.getresp("module/alts", "new_main", {"char":request.sender.name})
        elif msg == "not_an_alt":
            return self.getresp("module/alts", "not_an_alt")
        elif msg == "already_main":
            return self.getresp("module/alts", "already_main")
        else:
            raise Exception("Unknown msg: " + msg)

    @command(command="alts", params=[Const("add"), Character("character")], access_level="all",
             description="Add an alt")
    def alts_add_cmd(self, request, _, alt_char):
        if not alt_char.char_id:
            return self.getresp("global", "char_not_found", {"char":alt_char.name})
        elif alt_char.char_id == request.sender.char_id:
            return self.getresp("module/alts", "add_fail_self")

        msg, result = self.alts_service.add_alt(request.sender.char_id, alt_char.char_id)
        if result:
            self.bot.send_private_message(alt_char.char_id, self.getresp("module/alts", "add_success_target",
                                                                         {"char": request.sender.name}))
            return self.getresp("module/alts", "add_success_self", {"char": alt_char.name})
        elif msg == "another_main":
            return self.getresp("module/alts", "add_fail_already", {"char": alt_char.name})
        else:
            raise Exception("Unknown msg: " + msg)

    @command(command="alts", params=[Options(["rem", "remove"]), Character("character")], access_level="all",
             description="Remove an alt")
    def alts_remove_cmd(self, request, _, alt_char):
        if not alt_char.char_id:
            return self.getresp("global", "char_not_found", {"char":alt_char.name})

        msg, result = self.alts_service.remove_alt(request.sender.char_id, alt_char.char_id)
        if result:
            return self.getresp("module/alts", "rem_success", {"char": alt_char.name})
        elif msg == "not_alt":
            return self.getresp("module/alts", "rem_fail_not", {"char": alt_char.name})
        elif msg == "remove_main":
            return self.getresp("module/alts", "rem_fail_main")
        else:
            raise Exception("Unknown msg: " + msg)

    @command(command="alts", params=[Character("character")], access_level="member",
             description="Show alts of another character", sub_command="show")
    def alts_list_other_cmd(self, request, char):
        if not char.char_id:
            return self.getresp("global", "char_not_found", {"char":char.name})

        alts = self.alts_service.get_alts(char.char_id)
        blob = self.format_alt_list(alts)

        return ChatBlob(self.getresp("module/alts", "alts_list", {"char": alts[0].name, "amount": len(alts)}), blob)

    def format_alt_list(self, alts):
        blob = ""
        for alt in alts:
            blob += "<highlight>%s<end> (%d/<green>%d<end>) %s %s" % (alt.name, alt.level, alt.ai_level, alt.faction, alt.profession)
            if self.buddy_service.is_online(alt.char_id):
                blob += " [<green>Online<end>]"
            blob += "\n"
        return blob