class PrivateChannelManager:
    JOINED_PRIVATE_CHANNEL_EVENT = "private_channel_joined"
    LEFT_PRIVATE_CHANNEL_EVENT = "private_channel_left"

    def __init__(self):
        self.logger = Logger("private_channel_manager")
        self.private_channel_chars = {}

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

    def pre_start(self):
        self.event_manager.register_event_type(self.JOINED_PRIVATE_CHANNEL_EVENT)
        self.event_manager.register_event_type(self.LEFT_PRIVATE_CHANNEL_EVENT)
        self.bot.add_packet_handler(server_packets.PrivateChannelClientJoined.id, self.handle_private_channel_client_joined)
        self.bot.add_packet_handler(server_packets.PrivateChannelClientLeft.id, self.handle_private_channel_client_left)
        self.bot.add_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message)

    def handle_private_channel_message(self, packet: server_packets.PrivateChannelMessage):
        char_name = self.character_manager.get_char_name(packet.char_id)
        self.logger.log_chat("Private Channel", char_name, packet.message)

    def handle_private_channel_client_joined(self, packet: server_packets.PrivateChannelClientJoined):
        if packet.private_channel_id == self.bot.char_id:
            self.private_channel_chars[packet.char_id] = packet
            self.logger.log_chat("Private Channel", None, "%s joined the channel." % self.character_manager.get_char_name(packet.char_id))
            self.event_manager.fire_event(self.JOINED_PRIVATE_CHANNEL_EVENT, packet)

    def handle_private_channel_client_left(self, packet: server_packets.PrivateChannelClientLeft):
        if packet.private_channel_id == self.bot.char_id:
            del self.private_channel_chars[packet.char_id]
            self.logger.log_chat("Private Channel", None, "%s left the channel." % self.character_manager.get_char_name(packet.char_id))
            self.event_manager.fire_event(self.LEFT_PRIVATE_CHANNEL_EVENT, packet)

    def invite(self, char_id):
        self.bot.send_packet(client_packets.PrivateChannelInvite(char_id))

    def kick(self, char_id):
        self.bot.send_packet(client_packets.PrivateChannelKick(char_id))

    def kickall(self):
        self.bot.send_packet(client_packets.PrivateChannelKickAll())

    def in_private_channel(self, char_id):
        return char_id in self.private_channel_chars
Ejemplo n.º 2
0
class PrivateChannelService:
    PRIVATE_CHANNEL_MESSAGE_EVENT = "private_channel_message"
    JOINED_PRIVATE_CHANNEL_EVENT = "private_channel_joined"
    LEFT_PRIVATE_CHANNEL_EVENT = "private_channel_left"

    def __init__(self):
        self.logger = Logger(__name__)
        self.private_channel_chars = {}

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")
        self.access_service = registry.get_instance("access_service")

    def pre_start(self):
        self.event_service.register_event_type(self.JOINED_PRIVATE_CHANNEL_EVENT)
        self.event_service.register_event_type(self.LEFT_PRIVATE_CHANNEL_EVENT)
        self.event_service.register_event_type(self.PRIVATE_CHANNEL_MESSAGE_EVENT)

        self.bot.register_packet_handler(server_packets.PrivateChannelClientJoined.id, self.handle_private_channel_client_joined)
        self.bot.register_packet_handler(server_packets.PrivateChannelClientLeft.id, self.handle_private_channel_client_left)
        # priority must be above that of CommandService in order for relaying of commands to work correctly
        self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message, priority=30)

        self.access_service.register_access_level("guest", 90, self.in_private_channel)

    def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage):
        if conn.id != "main":
            return

        if packet.private_channel_id == self.bot.get_char_id():
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat("Private Channel", char_name, packet.message)
            self.event_service.fire_event(self.PRIVATE_CHANNEL_MESSAGE_EVENT, packet)

    def handle_private_channel_client_joined(self, conn: Conn, packet: server_packets.PrivateChannelClientJoined):
        if conn.id != "main":
            return

        if packet.private_channel_id == self.bot.get_char_id():
            self.private_channel_chars[packet.char_id] = packet
            self.logger.log_chat("Private Channel", None, "%s joined the channel." % self.character_service.get_char_name(packet.char_id))
            self.event_service.fire_event(self.JOINED_PRIVATE_CHANNEL_EVENT, packet)

    def handle_private_channel_client_left(self, conn: Conn, packet: server_packets.PrivateChannelClientLeft):
        if conn.id != "main":
            return

        if packet.private_channel_id == self.bot.get_char_id():
            del self.private_channel_chars[packet.char_id]
            self.logger.log_chat("Private Channel", None, "%s left the channel." % self.character_service.get_char_name(packet.char_id))
            self.event_service.fire_event(self.LEFT_PRIVATE_CHANNEL_EVENT, packet)

    def invite(self, char_id):
        if char_id != self.bot.get_char_id():
            self.bot.send_packet(client_packets.PrivateChannelInvite(char_id))

    def kick(self, char_id):
        if char_id != self.bot.get_char_id():
            self.bot.send_packet(client_packets.PrivateChannelKick(char_id))

    def kickall(self):
        self.bot.send_packet(client_packets.PrivateChannelKickAll())

    def in_private_channel(self, char_id):
        return char_id in self.private_channel_chars

    def get_all_in_private_channel(self):
        return self.private_channel_chars
Ejemplo n.º 3
0
class PublicChannelService:
    ORG_CHANNEL_COMMAND_EVENT = "org_channel_command"
    ORG_CHANNEL_MESSAGE_EVENT = "org_channel_message"
    ORG_MSG_EVENT = "org_msg"
    ORG_CHANNEL_COMMAND = "org"

    ORG_MSG_CHANNEL_ID = 42949672961

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

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.db = registry.get_instance("db")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")
        self.setting_service = registry.get_instance("setting_service")
        self.command_service = registry.get_instance("command_service")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.LoginOK.id,
                                         self.handle_login_ok)
        self.bot.register_packet_handler(server_packets.PublicChannelJoined.id,
                                         self.add)
        self.bot.register_packet_handler(server_packets.PublicChannelLeft.id,
                                         self.remove)
        self.bot.register_packet_handler(
            server_packets.PublicChannelMessage.id,
            self.public_channel_message)

        self.event_service.register_event_type(self.ORG_CHANNEL_COMMAND_EVENT)
        self.event_service.register_event_type(self.ORG_CHANNEL_MESSAGE_EVENT)
        self.event_service.register_event_type(self.ORG_MSG_EVENT)

        self.command_service.register_command_channel("Org Channel",
                                                      self.ORG_CHANNEL_COMMAND)

    def start(self):
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS org_name_cache (org_id INT NOT NULL, name VARCHAR(255) NOT NULL)"
        )

    def handle_login_ok(self, conn: Conn, packet: server_packets.LoginOK):
        if not conn.is_main:
            return

    def add(self, conn: Conn, packet: server_packets.PublicChannelJoined):
        if not conn.is_main:
            return

        conn.channels[packet.channel_id] = packet
        if not conn.org_id and self.is_org_channel_id(packet.channel_id):
            conn.org_channel_id = packet.channel_id
            conn.org_id = 0x00ffffffff & packet.channel_id

            row = self.db.query_single(
                "SELECT name FROM org_name_cache WHERE org_id = ?",
                [conn.org_id])

            if packet.name != "Clan (name unknown)":
                source = "chat_server"
                if not row:
                    self.db.exec(
                        "INSERT INTO org_name_cache (org_id, name) VALUES (?, ?)",
                        [conn.org_id, packet.name])
                elif packet.name != row.name:
                    self.db.exec(
                        "UPDATE org_name_cache SET name = ? WHERE org_id = ?",
                        [packet.name, conn.org_id])
                conn.org_name = packet.name
            elif row:
                source = "cache"
                conn.org_name = row.name
            else:
                source = "none"

            self.logger.info(
                f"Org info for '{conn.id}': {conn.org_name} ({conn.org_id}); source: '{source}'"
            )

    def remove(self, conn: Conn, packet: server_packets.PublicChannelLeft):
        if not conn.is_main:
            return

        del conn.channels[packet.channel_id]

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

        if conn.org_channel_id == packet.channel_id:
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat(conn, "Org Channel", char_name, message)

            if conn.char_id == packet.char_id:
                return

            if not self.handle_public_channel_command(conn, packet):
                self.event_service.fire_event(
                    self.ORG_CHANNEL_MESSAGE_EVENT,
                    DictObject({
                        "char_id": packet.char_id,
                        "name": char_name,
                        "message": message,
                        "extended_message": packet.extended_message,
                        "conn": conn
                    }))
        elif packet.channel_id == self.ORG_MSG_CHANNEL_ID:
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat(conn, "Org Msg", char_name, message)
            self.event_service.fire_event(
                self.ORG_MSG_EVENT,
                DictObject({
                    "char_id": packet.char_id,
                    "name": char_name,
                    "message": packet.message,
                    "extended_message": packet.extended_message,
                    "conn": conn
                }))

    def handle_public_channel_command(
            self, conn: Conn, packet: server_packets.PublicChannelMessage):
        if not self.setting_service.get("accept_commands_from_slave_bots"
                                        ).get_value() and not conn.is_main:
            return False

        # since the command symbol is required in the org channel,
        # the command_str must have length of at least 2 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 2:
            return False

        # ignore leading space
        message = packet.message.lstrip()

        def reply(msg):
            self.bot.send_org_message(msg, conn=conn)
            self.event_service.fire_event(
                self.ORG_CHANNEL_COMMAND_EVENT,
                DictObject({
                    "char_id": None,
                    "name": None,
                    "message": msg,
                    "conn": conn
                }))

        if message.startswith(self.setting_service.get("symbol").get_value()
                              ) and conn.org_channel_id == packet.channel_id:
            char_name = self.character_service.get_char_name(packet.char_id)
            self.event_service.fire_event(
                self.ORG_CHANNEL_COMMAND_EVENT,
                DictObject({
                    "char_id": packet.char_id,
                    "name": char_name,
                    "message": packet.message,
                    "conn": conn
                }))

            self.command_service.process_command(
                self.command_service.trim_command_symbol(message),
                self.ORG_CHANNEL_COMMAND, packet.char_id, reply, conn)
            return True
        else:
            return False

    def is_org_channel_id(self, channel_id):
        return channel_id >> 32 == 3
Ejemplo n.º 4
0
class Tyrbot:
    CONNECT_EVENT = "connect"
    PRIVATE_MSG_EVENT = "private_msg"

    def __init__(self):
        super().__init__()
        self.logger = Logger(__name__)
        self.ready = False
        self.packet_handlers = {}
        self.superadmin = None
        self.status: BotStatus = BotStatus.SHUTDOWN
        self.dimension = None
        self.last_timer_event = 0
        self.start_time = int(time.time())
        self.version = "0.7-beta"
        self.incoming_queue = FifoQueue()
        self.mass_message_queue = None
        self.conns = DictObject()
        self.primary_conn_id = None

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.public_channel_service: PublicChannelService = registry.get_instance(
            "public_channel_service")
        self.text: Text = registry.get_instance("text")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.access_service: AccessService = registry.get_instance(
            "access_service")
        self.event_service = registry.get_instance("event_service")
        self.job_scheduler = registry.get_instance("job_scheduler")

    def init(self, config, registry, mmdb_parser):
        self.mmdb_parser = mmdb_parser
        self.superadmin = config.superadmin.capitalize()
        self.dimension = config.server.dimension

        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_config (command VARCHAR(50) NOT NULL, sub_command VARCHAR(50) NOT NULL, access_level VARCHAR(50) NOT NULL, channel VARCHAR(50) NOT NULL, "
            "module VARCHAR(50) NOT NULL, enabled SMALLINT NOT NULL, verified SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS event_config (event_type VARCHAR(50) NOT NULL, event_sub_type VARCHAR(50) NOT NULL, handler VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, "
            "module VARCHAR(50) NOT NULL, enabled SMALLINT NOT NULL, verified SMALLINT NOT NULL, is_hidden SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS timer_event (event_type VARCHAR(50) NOT NULL, event_sub_type VARCHAR(50) NOT NULL, handler VARCHAR(255) NOT NULL, next_run INT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS setting (name VARCHAR(50) NOT NULL, value VARCHAR(255) NOT NULL, description VARCHAR(255) NOT NULL, module VARCHAR(50) NOT NULL, verified SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_alias (alias VARCHAR(50) NOT NULL, command VARCHAR(1024) NOT NULL, enabled SMALLINT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS command_usage (command VARCHAR(255) NOT NULL, handler VARCHAR(255) NOT NULL, char_id INT NOT NULL, channel VARCHAR(20) NOT NULL, created_at INT NOT NULL)"
        )
        self.db.exec(
            "CREATE TABLE IF NOT EXISTS ban_list (char_id INT NOT NULL, sender_char_id INT NOT NULL, created_at INT NOT NULL, finished_at INT NOT NULL, reason VARCHAR(255) NOT NULL, ended_early SMALLINT NOT NULL)"
        )

        self.db.exec("UPDATE db_version SET verified = 0")
        self.db.exec(
            "UPDATE db_version SET verified = 1 WHERE file = 'db_version'")

        # prepare commands, events, and settings
        self.db.exec("UPDATE command_config SET verified = 0")
        self.db.exec("UPDATE event_config SET verified = 0")
        self.db.exec("UPDATE setting SET verified = 0")

        with self.db.transaction():
            registry.pre_start_all()
            registry.start_all()

        # remove commands, events, and settings that are no longer registered
        self.db.exec("DELETE FROM db_version WHERE verified = 0")
        self.db.exec("DELETE FROM command_config WHERE verified = 0")
        self.db.exec("DELETE FROM event_config WHERE verified = 0")
        self.db.exec(
            "DELETE FROM timer_event WHERE handler NOT IN (SELECT handler FROM event_config WHERE event_type = ?)",
            ["timer"])
        self.db.exec("DELETE FROM setting WHERE verified = 0")

        self.status = BotStatus.RUN

    def pre_start(self):
        self.access_service.register_access_level("superadmin", 10,
                                                  self.check_superadmin)
        self.event_service.register_event_type(self.CONNECT_EVENT)
        self.event_service.register_event_type(self.PRIVATE_MSG_EVENT)

    def start(self):
        self.setting_service.register(
            "core.system", "symbol", "!",
            TextSettingType(["!", "#", "*", "@", "$", "+", "-"]),
            "Symbol for executing bot commands")

        self.setting_service.register(
            "core.system", "org_channel_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in org channel")
        self.setting_service.register(
            "core.system", "private_message_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in private messages")
        self.setting_service.register(
            "core.system", "private_channel_max_page_length", 7500,
            NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
            "Maximum size of blobs in private channel")

        self.setting_service.register(
            "core.system", "accept_commands_from_slave_bots", False,
            BooleanSettingType(),
            "Accept and respond to commands sent to slave bots (only applies if you have added slave bots in the config)"
        )

        self.setting_service.register("core.colors", "header_color", "#FFFF00",
                                      ColorSettingType(), "Color for headers")
        self.setting_service.register("core.colors", "header2_color",
                                      "#FCA712", ColorSettingType(),
                                      "Color for sub-headers")
        self.setting_service.register("core.colors", "highlight_color",
                                      "#00BFFF", ColorSettingType(),
                                      "Color for highlight")
        self.setting_service.register("core.colors", "notice_color", "#FF8C00",
                                      ColorSettingType(),
                                      "Color for important notices")

        self.setting_service.register("core.colors", "neutral_color",
                                      "#E6E1A6", ColorSettingType(),
                                      "Color for neutral faction")
        self.setting_service.register("core.colors", "omni_color", "#FA8484",
                                      ColorSettingType(),
                                      "Color for omni faction")
        self.setting_service.register("core.colors", "clan_color", "#F79410",
                                      ColorSettingType(),
                                      "Color for clan faction")
        self.setting_service.register("core.colors", "unknown_color",
                                      "#FF0000", ColorSettingType(),
                                      "Color for unknown faction")

        self.setting_service.register("core.colors", "org_channel_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default org channel color")
        self.setting_service.register("core.colors", "private_channel_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default private channel color")
        self.setting_service.register("core.colors", "private_message_color",
                                      "#89D2E8", ColorSettingType(),
                                      "Default private message color")
        self.setting_service.register("core.colors", "blob_color", "#FFFFFF",
                                      ColorSettingType(),
                                      "Default blob content color")

        self.register_packet_handler(server_packets.PrivateMessage.id,
                                     self.handle_private_message,
                                     priority=40)

    def check_superadmin(self, char_id):
        char_name = self.character_service.resolve_char_to_name(char_id)
        return char_name == self.superadmin

    def connect(self, config):
        for i, bot in enumerate(config.bots):
            if "id" in bot:
                _id = bot.id
            else:
                _id = "bot" + str(i)

            if i == 0:
                self.primary_conn_id = _id

            conn = self.create_conn(_id)
            conn.connect(config.server.host, config.server.port)

            # only create the mass_message_queue if there is at least 1 non-main bot
            if not bot.is_main and not self.mass_message_queue:
                self.mass_message_queue = FifoQueue()

            packet = conn.login(bot.username,
                                bot.password,
                                bot.character,
                                is_main=bot.is_main)
            if not packet:
                self.status = BotStatus.ERROR
                return False
            else:
                self.incoming_queue.put((conn, packet))

            self.create_conn_thread(
                conn, None if bot.is_main else self.mass_message_queue)

        return True

    def create_conn_thread(self, conn: Conn, mass_message_queue=None):
        def read_packets():
            try:
                while self.status == BotStatus.RUN:
                    packet = conn.read_packet(1)
                    if packet:
                        self.incoming_queue.put((conn, packet))

                    while mass_message_queue and not mass_message_queue.empty(
                    ) and conn.packet_queue.is_empty():
                        packet = mass_message_queue.get_or_default(block=False)
                        if packet:
                            conn.add_packet_to_queue(packet)

            except (EOFError, OSError) as e:
                self.status = BotStatus.ERROR
                self.logger.error("", e)
                raise e

        dthread = threading.Thread(target=read_packets, daemon=True)
        dthread.start()

    def create_conn(self, _id):
        if _id in self.conns:
            raise Exception(f"A connection with id {_id} already exists")

        def failure_callback():
            self.status = BotStatus.ERROR

        conn = Conn(_id, failure_callback)
        self.conns[_id] = conn
        return conn

    def disconnect(self):
        # wait for all threads to stop reading packets, then disconnect them all
        time.sleep(2)
        for _id, conn in self.get_conns():
            conn.disconnect()

    def run(self):
        start = time.time()

        # wait for flood of packets from login to stop sending
        time_waited = 0
        while time_waited < 2:
            if not self.iterate(1):
                time_waited += 1

        self.logger.info("Login complete (%fs)" % (time.time() - start))

        start = time.time()
        self.event_service.fire_event("connect", None)
        self.event_service.run_timer_events_at_startup()
        self.event_service.check_for_timer_events(int(start))
        self.logger.info("Connect events finished (%fs)" %
                         (time.time() - start))

        time_waited = 0
        while time_waited < 2:
            if not self.iterate(1):
                time_waited += 1

        self.ready = True
        timestamp = int(time.time())

        while self.status == BotStatus.RUN:
            try:
                timestamp = int(time.time())
                self.check_for_timer_events(timestamp)

                self.iterate()
            except Exception as e:
                self.logger.error("", e)

        # run any pending jobs/events
        self.check_for_timer_events(timestamp + 1)

        return self.status

    def check_for_timer_events(self, timestamp):
        # timer events will execute no more often than once per second
        if self.last_timer_event < timestamp:
            self.last_timer_event = timestamp
            self.job_scheduler.check_for_scheduled_jobs(timestamp)
            self.event_service.check_for_timer_events(timestamp)

    def register_packet_handler(self, packet_id: int, handler, priority=50):
        """
        Call during pre_start

        Args:
            packet_id: int
            handler: (conn, packet) -> void
            priority: int
        """

        if len(inspect.signature(handler).parameters) != 2:
            raise Exception(
                "Incorrect number of arguments for handler '%s.%s()'" %
                (handler.__module__, handler.__name__))

        handlers = self.packet_handlers.get(packet_id, [])
        handlers.append(DictObject({"priority": priority, "handler": handler}))
        self.packet_handlers[packet_id] = sorted(handlers,
                                                 key=lambda x: x.priority)

    def remove_packet_handler(self, packet_id, handler):
        handlers = self.packet_handlers.get(packet_id, [])
        for h in handlers:
            if h.handler == handler:
                handlers.remove(h)

    def iterate(self, timeout=0.1):
        conn, packet = self.incoming_queue.get_or_default(block=True,
                                                          timeout=timeout,
                                                          default=(None, None))
        if packet:
            if isinstance(packet, server_packets.SystemMessage):
                packet = self.system_message_ext_msg_handling(packet)
                self.logger.log_chat(conn, "SystemMessage", None,
                                     packet.extended_message.get_message())
            elif isinstance(packet, server_packets.PublicChannelMessage):
                packet = self.public_channel_message_ext_msg_handling(packet)
            elif isinstance(packet,
                            server_packets.BuddyAdded) and packet.char_id == 0:
                return

            for handler in self.packet_handlers.get(packet.id, []):
                handler.handler(conn, packet)

        return packet

    def public_channel_message_ext_msg_handling(
            self, packet: server_packets.PublicChannelMessage):
        msg = packet.message
        if msg.startswith("~&") and msg.endswith("~"):
            try:
                msg = msg[2:-1].encode("utf-8")
                category_id = self.mmdb_parser.read_base_85(msg[0:5])
                instance_id = self.mmdb_parser.read_base_85(msg[5:10])
                template = self.mmdb_parser.get_message_string(
                    category_id, instance_id)
                params = self.mmdb_parser.parse_params(msg[10:])
                packet.extended_message = ExtendedMessage(
                    category_id, instance_id, template, params)
            except Exception as e:
                self.logger.error(
                    "Error handling extended message for packet: " +
                    str(packet), e)

        return packet

    def system_message_ext_msg_handling(self,
                                        packet: server_packets.SystemMessage):
        try:
            category_id = 20000
            instance_id = packet.message_id
            template = self.mmdb_parser.get_message_string(
                category_id, instance_id)
            params = self.mmdb_parser.parse_params(packet.message_args)
            packet.extended_message = ExtendedMessage(category_id, instance_id,
                                                      template, params)
        except Exception as e:
            self.logger.error(
                "Error handling extended message: " + str(packet), e)

        return packet

    def send_org_message(self, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if not conn.org_channel_id:
            self.logger.debug(
                f"Ignoring message to org channel for {conn.id} since the org_channel_id is unknown"
            )
        else:
            color = self.setting_service.get(
                "org_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "org_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PublicChannelMessage(
                    conn.org_channel_id, color + page, "")
                conn.add_packet_to_queue(packet)

    def send_private_message(self, char_id, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if char_id is None:
            raise Exception("Cannot send message, char_id is empty")
        else:
            color = self.setting_service.get(
                "private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "private_message_max_page_length").get_value())
            for page in pages:
                self.logger.log_tell(
                    conn, "To", self.character_service.get_char_name(char_id),
                    page)
                packet = client_packets.PrivateMessage(char_id, color + page,
                                                       "\0")
                conn.add_packet_to_queue(packet)

    def send_private_channel_message(self,
                                     msg,
                                     private_channel_id=None,
                                     add_color=True,
                                     conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if private_channel_id is None:
            private_channel_id = conn.get_char_id()

        color = self.setting_service.get(
            "private_channel_color").get_font_color() if add_color else ""
        pages = self.get_text_pages(
            msg, conn,
            self.setting_service.get(
                "private_channel_max_page_length").get_value())
        for page in pages:
            packet = client_packets.PrivateChannelMessage(
                private_channel_id, color + page, "\0")
            conn.send_packet(packet)

    def send_mass_message(self, char_id, msg, add_color=True, conn=None):
        if not conn:
            conn = self.get_primary_conn()

        if not char_id:
            self.logger.warning("Could not send message to empty char_id")
        else:
            color = self.setting_service.get(
                "private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(
                msg, conn,
                self.setting_service.get(
                    "private_message_max_page_length").get_value())
            for page in pages:
                if self.mass_message_queue:
                    packet = client_packets.PrivateMessage(
                        char_id, color + page, "\0")
                    self.mass_message_queue.put(packet)
                else:
                    packet = client_packets.PrivateMessage(
                        char_id, color + page, "spam")
                    self.get_primary_conn().send_packet(packet)

    def send_message_to_other_org_channels(self, msg, from_conn: Conn):
        for _id, conn in self.get_conns(
                lambda x: x.is_main and x.org_id and x != from_conn):
            self.send_org_message(msg, conn=conn)

    def handle_private_message(self, conn: Conn,
                               packet: server_packets.PrivateMessage):
        char_name = self.character_service.get_char_name(packet.char_id)
        self.logger.log_tell(conn, "From", char_name, packet.message)
        self.event_service.fire_event(
            self.PRIVATE_MSG_EVENT,
            DictObject({
                "char_id": packet.char_id,
                "name": char_name,
                "message": packet.message,
                "conn": conn
            }))

    def get_text_pages(self, msg, conn, max_page_length):
        if isinstance(msg, ChatBlob):
            return self.text.paginate(msg,
                                      conn,
                                      max_page_length=max_page_length)
        else:
            return [self.text.format_message(msg, conn)]

    def is_ready(self):
        return self.ready

    def shutdown(self):
        self.status = BotStatus.SHUTDOWN

    def restart(self):
        self.status = BotStatus.RESTART

    def get_primary_conn_id(self):
        return self.primary_conn_id

    def get_primary_conn(self):
        return self.conns[self.get_primary_conn_id()]

    def get_conn_by_char_id(self, char_id):
        for _id, conn in self.get_conns():
            if char_id == conn.get_char_id():
                return conn
        return None

    def get_conn_by_org_id(self, org_id):
        for _id, conn in self.get_conns():
            if conn.org_id == org_id:
                return conn
        return None

    # placeholder to keep track of things that need to be fixed/updated
    def get_temp_conn(self):
        return self.get_primary_conn()

    def get_conns(self, conn_filter=None):
        if conn_filter:
            return [(_id, conn) for _id, conn in self.conns.items()
                    if conn_filter(conn)]
        else:
            return self.conns.items()
Ejemplo n.º 5
0
class Tyrbot:
    CONNECT_EVENT = "connect"
    PACKET_EVENT = "packet"
    PRIVATE_MSG_EVENT = "private_msg"

    OUTGOING_ORG_MESSAGE_EVENT = "outgoing_org_message"
    OUTGOING_PRIVATE_MESSAGE_EVENT = "outgoing_private_message"
    OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT = "outgoing_private_channel_message"

    def __init__(self):
        super().__init__()
        self.logger = Logger(__name__)
        self.ready = False
        self.packet_handlers = {}
        self.superadmin = None
        self.status: BotStatus = BotStatus.SHUTDOWN
        self.dimension = None
        self.last_timer_event = 0
        self.start_time = int(time.time())
        self.version = "0.5-beta"
        self.incoming_queue = FifoQueue()
        self.mass_message_queue = None
        self.conns = DictObject()

    def inject(self, registry):
        self.db = registry.get_instance("db")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.public_channel_service: PublicChannelService = registry.get_instance("public_channel_service")
        self.text: Text = registry.get_instance("text")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.access_service: AccessService = registry.get_instance("access_service")
        self.event_service = registry.get_instance("event_service")
        self.job_scheduler = registry.get_instance("job_scheduler")

    def init(self, config, registry, paths, mmdb_parser):
        self.mmdb_parser = mmdb_parser
        self.superadmin = config.superadmin.capitalize()
        self.dimension = config.server.dimension

        self.db.exec("UPDATE db_version SET verified = 0")
        self.db.exec("UPDATE db_version SET verified = 1 WHERE file = 'db_version'")

        self.load_sql_files(paths)

        # prepare commands, events, and settings
        self.db.exec("UPDATE command_config SET verified = 0")
        self.db.exec("UPDATE event_config SET verified = 0")
        self.db.exec("UPDATE setting SET verified = 0")

        with self.db.transaction():
            registry.pre_start_all()
            registry.start_all()

        # remove commands, events, and settings that are no longer registered
        self.db.exec("DELETE FROM db_version WHERE verified = 0")
        self.db.exec("DELETE FROM command_config WHERE verified = 0")
        self.db.exec("DELETE FROM event_config WHERE verified = 0")
        self.db.exec("DELETE FROM timer_event WHERE handler NOT IN (SELECT handler FROM event_config WHERE event_type = ?)", ["timer"])
        self.db.exec("DELETE FROM setting WHERE verified = 0")

        self.status = BotStatus.RUN

    def pre_start(self):
        self.access_service.register_access_level("superadmin", 10, self.check_superadmin)
        self.event_service.register_event_type(self.CONNECT_EVENT)
        self.event_service.register_event_type(self.PACKET_EVENT)
        self.event_service.register_event_type(self.PRIVATE_MSG_EVENT)
        self.event_service.register_event_type(self.OUTGOING_ORG_MESSAGE_EVENT)
        self.event_service.register_event_type(self.OUTGOING_PRIVATE_MESSAGE_EVENT)
        self.event_service.register_event_type(self.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT)

    def start(self):
        self.setting_service.register_new("core.system", "symbol", "!", TextSettingType(["!", "#", "*", "@", "$", "+", "-"]), "Symbol for executing bot commands")

        self.setting_service.register_new("core.system", "org_channel_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in org channel")
        self.setting_service.register_new("core.system", "private_message_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in private messages")
        self.setting_service.register_new("core.system", "private_channel_max_page_length", 7500,
                                          NumberSettingType([4500, 6000, 7500, 9000, 10500, 12000]),
                                          "Maximum size of blobs in private channel")

        self.setting_service.register_new("core.system", "org_id", "", NumberSettingType(allow_empty=True), "Override the default org id",
                                          "This setting is is for development/debug purposes and should not be changed unless you understand the implications")
        self.setting_service.register_new("core.system", "org_name", "", TextSettingType(allow_empty=True), "The exact org name of the bot",
                                          "This setting is automatically set by the bot and should not be changed manually")

        self.setting_service.register_new("core.colors", "header_color", "#FFFF00", ColorSettingType(), "Color for headers")
        self.setting_service.register_new("core.colors", "header2_color", "#FCA712", ColorSettingType(), "Color for sub-headers")
        self.setting_service.register_new("core.colors", "highlight_color", "#00BFFF", ColorSettingType(), "Color for highlight")
        self.setting_service.register_new("core.colors", "notice_color", "#FF8C00", ColorSettingType(), "Color for important notices")

        self.setting_service.register_new("core.colors", "neutral_color", "#E6E1A6", ColorSettingType(), "Color for neutral faction")
        self.setting_service.register_new("core.colors", "omni_color", "#FA8484", ColorSettingType(), "Color for omni faction")
        self.setting_service.register_new("core.colors", "clan_color", "#F79410", ColorSettingType(), "Color for clan faction")
        self.setting_service.register_new("core.colors", "unknown_color", "#FF0000", ColorSettingType(), "Color for unknown faction")

        self.setting_service.register_new("core.colors", "org_channel_color", "#89D2E8", ColorSettingType(), "Default org channel color")
        self.setting_service.register_new("core.colors", "private_channel_color", "#89D2E8", ColorSettingType(), "Default private channel color")
        self.setting_service.register_new("core.colors", "private_message_color", "#89D2E8", ColorSettingType(), "Default private message color")
        self.setting_service.register_new("core.colors", "blob_color", "#FFFFFF", ColorSettingType(), "Default blob content color")

        self.register_packet_handler(server_packets.PrivateMessage.id, self.handle_private_message, priority=40)

    def check_superadmin(self, char_id):
        char_name = self.character_service.resolve_char_to_name(char_id)
        return char_name == self.superadmin

    def connect(self, config):
        conn = self.create_conn("main")
        conn.connect(config.server.host, config.server.port)
        packet = conn.login(config.username, config.password, config.character)
        if not packet:
            self.status = BotStatus.ERROR
            return False
        else:
            self.incoming_queue.put((conn, packet))

        self.mass_message_queue = FifoQueue()
        self.create_conn_thread(conn, self.mass_message_queue)

        if "slaves" in config:
            for i, slave in enumerate(config.slaves):
                conn = self.create_conn("slave" + str(i))
                conn.connect(config.server.host, config.server.port)

                packet = conn.login(slave.username, slave.password, slave.character)
                if not packet:
                    self.status = BotStatus.ERROR
                    return False
                else:
                    self.incoming_queue.put((conn, packet))

                self.create_conn_thread(conn, self.mass_message_queue)

        return True

    def create_conn_thread(self, conn: Conn, mass_message_queue=None):
        def read_packets():
            try:
                while self.status == BotStatus.RUN:
                    packet = conn.read_packet(1)
                    if packet:
                        self.incoming_queue.put((conn, packet))

                    while mass_message_queue and not mass_message_queue.empty() and conn.packet_queue.is_empty():
                        packet = mass_message_queue.get_or_default(block=False)
                        if packet:
                            conn.add_packet_to_queue(packet)

            except (EOFError, OSError) as e:
                self.status = BotStatus.ERROR
                self.logger.error("", e)
                raise e

        dthread = threading.Thread(target=read_packets, daemon=True)
        dthread.start()

    def create_conn(self, _id):
        if _id in self.conns:
            raise Exception(f"A connection with id {_id} already exists")

        conn = Conn(_id, self.disconnect)
        self.conns[_id] = conn
        return conn

    # passthrough
    def send_packet(self, packet):
        self.conns["main"].send_packet(packet)

    def disconnect(self):
        # wait for all threads to stop reading packets, then disconnect them all
        time.sleep(2)
        for _id, conn in self.conns.items():
            conn.disconnect()

    def run(self):
        start = time.time()

        # wait for flood of packets from login to stop sending
        time_waited = 0
        while time_waited < 5:
            if not self.iterate(1):
                time_waited += 1

        self.logger.info("Login complete (%fs)" % (time.time() - start))

        start = time.time()
        self.event_service.fire_event("connect", None)
        self.event_service.run_timer_events_at_startup()
        self.logger.info("Connect events finished (%fs)" % (time.time() - start))

        self.ready = True
        timestamp = int(time.time())

        while self.status == BotStatus.RUN:
            try:
                timestamp = int(time.time())
                self.check_for_timer_events(timestamp)

                self.iterate()
            except (EOFError, OSError) as e:
                raise e
            except Exception as e:
                self.logger.error("", e)

        # run any pending jobs/events
        self.check_for_timer_events(timestamp + 1)

        return self.status

    def check_for_timer_events(self, timestamp):
        # timer events will execute no more often than once per second
        if self.last_timer_event < timestamp:
            self.last_timer_event = timestamp
            self.job_scheduler.check_for_scheduled_jobs(timestamp)
            self.event_service.check_for_timer_events(timestamp)

    def register_packet_handler(self, packet_id: int, handler, priority=50):
        """
        Call during pre_start

        Args:
            packet_id: int
            handler: (conn, packet) -> void
            priority: int
        """

        if len(inspect.signature(handler).parameters) != 2:
            raise Exception("Incorrect number of arguments for handler '%s.%s()'" % (handler.__module__, handler.__name__))

        handlers = self.packet_handlers.get(packet_id, [])
        handlers.append(DictObject({"priority": priority, "handler": handler}))
        self.packet_handlers[packet_id] = sorted(handlers, key=lambda x: x.priority)

    def remove_packet_handler(self, packet_id, handler):
        handlers = self.packet_handlers.get(packet_id, [])
        for h in handlers:
            if h.handler == handler:
                handlers.remove(h)

    def iterate(self, timeout=0.1):
        conn, packet = self.incoming_queue.get_or_default(block=True, timeout=timeout, default=(None, None))
        if packet:
            if isinstance(packet, server_packets.SystemMessage):
                packet = self.system_message_ext_msg_handling(packet)
            elif isinstance(packet, server_packets.PublicChannelMessage):
                packet = self.public_channel_message_ext_msg_handling(packet)
            if isinstance(packet, server_packets.BuddyAdded):
                if packet.char_id == 0:
                    return

            for handler in self.packet_handlers.get(packet.id, []):
                handler.handler(conn, packet)

            self.event_service.fire_event("packet:" + str(packet.id), packet)

        return packet

    def public_channel_message_ext_msg_handling(self, packet: server_packets.PublicChannelMessage):
        msg = packet.message
        if msg.startswith("~&") and msg.endswith("~"):
            try:
                msg = msg[2:-1].encode("utf-8")
                category_id = self.mmdb_parser.read_base_85(msg[0:5])
                instance_id = self.mmdb_parser.read_base_85(msg[5: 10])
                template = self.mmdb_parser.get_message_string(category_id, instance_id)
                params = self.mmdb_parser.parse_params(msg[10:])
                packet.extended_message = ExtendedMessage(category_id, instance_id, template, params)
            except Exception as e:
                self.logger.error("Error handling extended message for packet: " + str(packet), e)

        return packet

    def system_message_ext_msg_handling(self, packet: server_packets.SystemMessage):
        try:
            category_id = 20000
            instance_id = packet.message_id
            template = self.mmdb_parser.get_message_string(category_id, instance_id)
            params = self.mmdb_parser.parse_params(packet.message_args)
            packet.extended_message = ExtendedMessage(category_id, instance_id, template, params)
            self.logger.log_chat("SystemMessage", None, packet.extended_message.get_message())
        except Exception as e:
            self.logger.error("Error handling extended message: " + str(packet), e)

        return packet

    def send_org_message(self, msg, add_color=True, fire_outgoing_event=True, conn_id="main"):
        org_channel_id = self.public_channel_service.org_channel_id
        if org_channel_id is None:
            self.logger.debug("ignoring message to org channel since the org_channel_id is unknown")
        else:
            color = self.setting_service.get("org_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("org_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PublicChannelMessage(org_channel_id, color + page, "")
                self.conns[conn_id].add_packet_to_queue(packet)

            if fire_outgoing_event:
                self.event_service.fire_event(self.OUTGOING_ORG_MESSAGE_EVENT, DictObject({"org_channel_id": org_channel_id,
                                                                                           "message": msg}))

    def send_private_message(self, char, msg, add_color=True, fire_outgoing_event=True, conn_id="main"):
        char_id = self.character_service.resolve_char_to_id(char)
        if char_id is None:
            self.logger.warning("Could not send message to %s, could not find char id" % char)
        else:
            color = self.setting_service.get("private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_message_max_page_length").get_value())
            for page in pages:
                self.logger.log_tell("To", self.character_service.get_char_name(char_id), page)
                packet = client_packets.PrivateMessage(char_id, color + page, "\0")
                self.conns[conn_id].add_packet_to_queue(packet)

            if fire_outgoing_event:
                self.event_service.fire_event(self.OUTGOING_PRIVATE_MESSAGE_EVENT, DictObject({"char_id": char_id,
                                                                                               "message": msg}))

    def send_private_channel_message(self, msg, private_channel=None, add_color=True, fire_outgoing_event=True, conn_id="main"):
        if private_channel is None:
            private_channel_id = self.get_char_id()
        else:
            private_channel_id = self.character_service.resolve_char_to_id(private_channel)

        if private_channel_id is None:
            self.logger.warning("Could not send message to private channel %s, could not find private channel" % private_channel)
        else:
            color = self.setting_service.get("private_channel_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_channel_max_page_length").get_value())
            for page in pages:
                packet = client_packets.PrivateChannelMessage(private_channel_id, color + page, "\0")
                self.conns[conn_id].send_packet(packet)

            if fire_outgoing_event and private_channel_id == self.get_char_id():
                self.event_service.fire_event(self.OUTGOING_PRIVATE_CHANNEL_MESSAGE_EVENT, DictObject({"private_channel_id": private_channel_id,
                                                                                                       "message": msg}))

    def send_mass_message(self, char_id, msg, add_color=True):
        if not char_id:
            self.logger.warning("Could not send message to empty char_id")
        else:
            color = self.setting_service.get("private_message_color").get_font_color() if add_color else ""
            pages = self.get_text_pages(msg, self.setting_service.get("private_message_max_page_length").get_value())
            for page in pages:
                # self.logger.log_tell("To", self.character_service.get_char_name(char_id), page)
                if self.mass_message_queue:
                    packet = client_packets.PrivateMessage(char_id, color + page, "\0")
                    self.mass_message_queue.put(packet)
                else:
                    packet = client_packets.PrivateMessage(char_id, color + page, "spam")
                    self.conns["main"].send_packet(packet)

    def handle_private_message(self, conn: Conn, packet: server_packets.PrivateMessage):
        if conn.id != "main":
            return

        self.logger.log_tell("From", self.character_service.get_char_name(packet.char_id), packet.message)
        self.event_service.fire_event(self.PRIVATE_MSG_EVENT, packet)

    def get_text_pages(self, msg, max_page_length):
        if isinstance(msg, ChatBlob):
            return self.text.paginate(msg, max_page_length=max_page_length)
        else:
            return [self.text.format_message(msg)]

    def is_ready(self):
        return self.ready

    def shutdown(self):
        self.status = BotStatus.SHUTDOWN

    def restart(self):
        self.status = BotStatus.RESTART

    def load_sql_files(self, paths):
        dirs = flatmap(lambda x: os.walk(x), paths)
        dirs = filter(lambda y: not y[0].endswith("__pycache__"), dirs)

        def get_files(tup):
            return map(lambda x: os.path.join(tup[0], x), tup[2])

        # get files from subdirectories
        files = flatmap(get_files, dirs)
        files = filter(lambda z: z.endswith(".sql"), files)

        base_path = os.getcwd()
        for file in files:
            self.db.load_sql_file(file, base_path)

    def get_char_name(self):
        return self.conns["main"].char_name

    def get_char_id(self):
        return self.conns["main"].char_id
Ejemplo n.º 6
0
class DarkController:
    relay_channel_id = None
    relay_name = None
    MESSAGE_SOURCE = "darknet"
    message_regex = re.compile(r"^(<font color='#\S+'>){2}\[([a-zA-Z]{2,})\]<\/font> <font color='#\S+'>(.+)<\/font> <font color='#\S+'>\[(.+)\]<\/font> \[(.+)\]$", re.DOTALL)

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

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance("setting_service")
        self.character_service: CharacterService = registry.get_instance("character_service")
        self.message_hub_service = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.PrivateChannelInvited.id, self.handle_private_channel_invite, 50)
        self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message)
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.setting_service.register(self.module_name, "dark_relay", "false", BooleanSettingType(), "Is the Module Enabled?")
        self.setting_service.register(self.module_name, "dark_wts", "true", BooleanSettingType(), "Is the WTS channel visible?")
        self.setting_service.register(self.module_name, "dark_wtb", "true", BooleanSettingType(), "Is the WTB channel visible?")
        self.setting_service.register(self.module_name, "dark_lr", "true", BooleanSettingType(), "Is the Lootrights channel visible?")
        self.setting_service.register(self.module_name, "dark_gen", "true", BooleanSettingType(), "Is the General channel visible?")
        self.setting_service.register(self.module_name, "dark_pvp", "true", BooleanSettingType(), "Is the PvP channel visible?")
        self.setting_service.register(self.module_name, "dark_pvm", "true", BooleanSettingType(), "Is the PVM channel visible?")
        self.setting_service.register(self.module_name, "dark_event", "true", BooleanSettingType(), "Is the Event channel visible?")

    def handle_private_channel_invite(self, conn: Conn, packet: server_packets.PrivateChannelInvited):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if "Darknet" == self.character_service.get_char_name(packet.private_channel_id):
            channel_name = self.character_service.get_char_name(packet.private_channel_id)
            conn.send_packet(client_packets.PrivateChannelJoin(packet.private_channel_id))
            self.logger.info("Joined private channel {channel}".format(channel=channel_name))
            self.relay_channel_id = packet.private_channel_id
            self.relay_name = channel_name

    def handle_private_channel_message(self, conn, packet: server_packets.PrivateChannelMessage):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if packet.private_channel_id == self.relay_channel_id:
            if conn.get_char_id() == packet.char_id:
                return
            if packet.char_id != self.relay_channel_id:
                return
            channel_name = self.character_service.get_char_name(packet.private_channel_id)
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat(conn, "Private Channel(%s)" % channel_name, char_name, packet.message)
            message = packet.message.lstrip()
            self.process_incoming_relay_message(message)

    def process_incoming_relay_message(self, message):
        if re.search(self.message_regex, message):
            cont = re.findall(self.message_regex, message)
            cont = cont[0]
            ch = cont[1].lower()
            msg = cont[2]
            tell = cont[3]
            report = cont[4]
            if ch == "wts":
                if self.setting_service.get_value("dark_wts") == "0":
                    return
                channel = "<red>[WTS]</red>"
            elif ch == "wtb":
                if self.setting_service.get_value("dark_wtb") == "0":
                    return
                channel = "<green>[WTB]</green>"
            elif ch == "lootrights":
                if self.setting_service.get_value("dark_lr") == "0":
                    return
                channel = "<violet>[LR]</violet>"
            elif ch == "general":
                if self.setting_service.get_value("dark_gen") == "0":
                    return
                channel = "<notice>[Gen]</notice>"
            elif ch == "pvm":
                if self.setting_service.get_value("dark_pvm") == "0":
                    return
                channel = "<cyan>[PvM]</cyan>"
            elif ch == "event":
                if self.setting_service.get_value("dark_event") == "0":
                    return
                channel = "<highlight>[Event]</highlight>"
            elif ch == "pvp":
                if self.setting_service.get_value("dark_pvp") == "0":
                    return
                channel = "<grey>[PvP]</grey>"
            elif ch == "auction":
                channel = "<yellow>[AUCTION]</yellow>"
            else:
                return

            message = "<orange>%s<end> [%s] [%s]" % (msg, tell, report)
            self.message_hub_service.send_message(self.MESSAGE_SOURCE, None, channel, message)
Ejemplo n.º 7
0
class DarknetController:
    relay_channel_id = None
    relay_name = None
    MESSAGE_SOURCE = "darknet"
    message_regex = re.compile(
        r"^(<font color='#\S+'>){2}\[([a-zA-Z]{2,})\]<\/font> (.+)$",
        re.DOTALL)

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

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.message_hub_service = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.bot.register_packet_handler(
            server_packets.PrivateChannelInvited.id,
            self.handle_private_channel_invite, 50)
        self.bot.register_packet_handler(
            server_packets.PrivateChannelMessage.id,
            self.handle_private_channel_message)
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.setting_service.register(self.module_name, "dark_relay", "false",
                                      BooleanSettingType(),
                                      "Is the Module Enabled?")
        self.setting_service.register(self.module_name, "dark_wts", "true",
                                      BooleanSettingType(),
                                      "Is the WTS channel visible?")
        self.setting_service.register(self.module_name, "dark_wtb", "true",
                                      BooleanSettingType(),
                                      "Is the WTB channel visible?")
        self.setting_service.register(self.module_name, "dark_lr", "true",
                                      BooleanSettingType(),
                                      "Is the Lootrights channel visible?")
        self.setting_service.register(self.module_name, "dark_gen", "true",
                                      BooleanSettingType(),
                                      "Is the General channel visible?")
        self.setting_service.register(self.module_name, "dark_pvp", "true",
                                      BooleanSettingType(),
                                      "Is the PvP channel visible?")
        self.setting_service.register(self.module_name, "dark_pvm", "true",
                                      BooleanSettingType(),
                                      "Is the PVM channel visible?")
        self.setting_service.register(self.module_name, "dark_event", "true",
                                      BooleanSettingType(),
                                      "Is the Event channel visible?")

    def handle_private_channel_invite(
            self, conn: Conn, packet: server_packets.PrivateChannelInvited):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if "Darknet" == self.character_service.get_char_name(
                packet.private_channel_id):
            channel_name = self.character_service.get_char_name(
                packet.private_channel_id)
            conn.send_packet(
                client_packets.PrivateChannelJoin(packet.private_channel_id))
            self.logger.info("Joined private channel {channel}".format(
                channel=channel_name))
            self.relay_channel_id = packet.private_channel_id
            self.relay_name = channel_name

    def handle_private_channel_message(
            self, conn, packet: server_packets.PrivateChannelMessage):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if packet.private_channel_id == self.relay_channel_id:
            if packet.char_id != self.relay_channel_id:
                return
            channel_name = self.character_service.get_char_name(
                packet.private_channel_id)
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat(conn, "Private Channel(%s)" % channel_name,
                                 char_name, packet.message)
            message = packet.message.lstrip()
            self.process_incoming_relay_message(message)

    def process_incoming_relay_message(self, message):
        if re.search(self.message_regex, message):
            cont = re.findall(self.message_regex, message)
            cont = cont[0]
            channel = cont[1]
            ch = channel.lower()
            rest_of_message = cont[2]
            if ch == "wts" and self.setting_service.get_value(
                    "dark_wts") == "0":
                return
            elif ch == "wtb" and self.setting_service.get_value(
                    "dark_wtb") == "0":
                return
            elif ch == "lootrights" and self.setting_service.get_value(
                    "dark_lr") == "0":
                return
            elif ch == "general" and self.setting_service.get_value(
                    "dark_gen") == "0":
                return
            elif ch == "pvm" and self.setting_service.get_value(
                    "dark_pvm") == "0":
                return
            elif ch == "event" and self.setting_service.get_value(
                    "dark_event") == "0":
                return
            elif ch == "pvp" and self.setting_service.get_value(
                    "dark_pvp") == "0":
                return

            if ch == "lootrights":
                channel = "LR"
            elif ch == "gen":
                channel = "Gen"

            channel_formatted = "[<highlight>%s</highlight>]" % channel

            self.message_hub_service.send_message(self.MESSAGE_SOURCE, None,
                                                  channel_formatted,
                                                  rest_of_message)
Ejemplo n.º 8
0
class DarknetController:
    relay_channel_id = None
    relay_name = None
    MESSAGE_SOURCE = "darknet"
    message_regex = re.compile(
        r"^(<font color='#\S+'>){2}\[([a-zA-Z]{2,})\]<\/font> (.+)$",
        re.DOTALL)

    DARKNET_NAME = "Darknet"

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

    def inject(self, registry):
        self.bot: Tyrbot = registry.get_instance("bot")
        self.setting_service: SettingService = registry.get_instance(
            "setting_service")
        self.character_service: CharacterService = registry.get_instance(
            "character_service")
        self.message_hub_service = registry.get_instance("message_hub_service")

    def pre_start(self):
        self.bot.register_packet_handler(
            server_packets.PrivateChannelInvited.id,
            self.handle_private_channel_invite, 50)
        self.bot.register_packet_handler(
            server_packets.PrivateChannelMessage.id,
            self.handle_private_channel_message)
        self.bot.register_packet_handler(server_packets.PrivateMessage.id,
                                         self.handle_private_message)
        self.message_hub_service.register_message_source(self.MESSAGE_SOURCE)

    def start(self):
        self.setting_service.register(
            self.module_name,
            "dark_relay",
            "false",
            BooleanSettingType(),
            "Is the Module Enabled?",
            extended_description=
            "Use !messagehub to control where Darknet messages are relayed to")
        self.setting_service.register(self.module_name, "dark_wts", "true",
                                      BooleanSettingType(),
                                      "Is the WTS channel visible?")
        self.setting_service.register(self.module_name, "dark_wtb", "true",
                                      BooleanSettingType(),
                                      "Is the WTB channel visible?")
        self.setting_service.register(self.module_name, "dark_lr", "true",
                                      BooleanSettingType(),
                                      "Is the Lootrights channel visible?")
        self.setting_service.register(self.module_name, "dark_gen", "true",
                                      BooleanSettingType(),
                                      "Is the General channel visible?")
        self.setting_service.register(self.module_name, "dark_pvp", "true",
                                      BooleanSettingType(),
                                      "Is the PvP channel visible?")
        self.setting_service.register(self.module_name, "dark_pvm", "true",
                                      BooleanSettingType(),
                                      "Is the PVM channel visible?")
        self.setting_service.register(self.module_name, "dark_event", "true",
                                      BooleanSettingType(),
                                      "Is the Event channel visible?")

        self.setting_service.register_change_listener(
            "dark_relay", self.update_darket_status)

    def handle_private_channel_invite(
            self, conn: Conn, packet: server_packets.PrivateChannelInvited):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if self.DARKNET_NAME == self.character_service.get_char_name(
                packet.private_channel_id):
            channel_name = self.character_service.get_char_name(
                packet.private_channel_id)
            conn.send_packet(
                client_packets.PrivateChannelJoin(packet.private_channel_id))
            self.logger.info("Joined private channel {channel}".format(
                channel=channel_name))
            self.relay_channel_id = packet.private_channel_id
            self.relay_name = channel_name

    def handle_private_channel_message(
            self, conn, packet: server_packets.PrivateChannelMessage):
        if not conn.is_main:
            pass

        if self.setting_service.get_value("dark_relay") == "0":
            return

        if packet.private_channel_id == self.relay_channel_id:
            if packet.char_id != self.relay_channel_id:
                return
            channel_name = self.character_service.get_char_name(
                packet.private_channel_id)
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat(conn, "Private Channel(%s)" % channel_name,
                                 char_name, packet.message)
            message = packet.message.lstrip()
            self.process_incoming_relay_message(message)

    def handle_private_message(self, conn,
                               packet: server_packets.PrivateMessage):
        if not conn.is_main:
            pass

        #if self.setting_service.get_value("dark_relay") == "0":
        #    return

        char_id = self.character_service.resolve_char_to_id(self.DARKNET_NAME)
        if packet.char_id == char_id:
            message = packet.message.lstrip()
            self.message_hub_service.send_message(
                self.MESSAGE_SOURCE, None,
                f"[<highlight>{self.DARKNET_NAME}</highlight>]", message)

    def process_incoming_relay_message(self, message):
        if re.search(self.message_regex, message):
            cont = re.findall(self.message_regex, message)
            cont = cont[0]
            channel = cont[1]
            ch = channel.lower()
            rest_of_message = cont[2]
            if ch == "wts" and self.setting_service.get_value(
                    "dark_wts") == "0":
                return
            elif ch == "wtb" and self.setting_service.get_value(
                    "dark_wtb") == "0":
                return
            elif ch == "lootrights" and self.setting_service.get_value(
                    "dark_lr") == "0":
                return
            elif ch == "general" and self.setting_service.get_value(
                    "dark_gen") == "0":
                return
            elif ch == "pvm" and self.setting_service.get_value(
                    "dark_pvm") == "0":
                return
            elif ch == "event" and self.setting_service.get_value(
                    "dark_event") == "0":
                return
            elif ch == "pvp" and self.setting_service.get_value(
                    "dark_pvp") == "0":
                return

            if ch == "lootrights":
                channel = "LR"
            elif ch == "gen":
                channel = "Gen"

            channel_formatted = "[<highlight>%s</highlight>]" % channel

            self.message_hub_service.send_message(self.MESSAGE_SOURCE, None,
                                                  channel_formatted,
                                                  rest_of_message)

    def update_darket_status(self, setting_name, old_value, new_value):
        char_id = self.character_service.resolve_char_to_id(self.DARKNET_NAME)
        if not char_id:
            self.logger.warning(
                f"Could not resolve {self.DARKNET_NAME} to a char id.")
        else:
            if new_value:
                self.bot.send_private_message(char_id,
                                              "register",
                                              add_color=False)
                self.bot.send_private_message(char_id,
                                              "autoinvite on",
                                              add_color=False)
                self.bot.send_private_message(char_id, "join", add_color=False)
            else:
                self.bot.send_private_message(char_id,
                                              "leave",
                                              add_color=False)
                self.bot.send_private_message(char_id,
                                              "autoinvite off",
                                              add_color=False)
                self.bot.send_private_message(char_id,
                                              "unregister",
                                              add_color=False)
Ejemplo n.º 9
0
class PrivateChannelService:
    PRIVATE_CHANNEL_MESSAGE_EVENT = "private_channel_message"
    PRIVATE_CHANNEL_COMMAND_EVENT = "private_channel_command"
    JOINED_PRIVATE_CHANNEL_EVENT = "private_channel_joined"
    LEFT_PRIVATE_CHANNEL_EVENT = "private_channel_left"

    PRIVATE_CHANNEL_COMMAND = "priv"

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

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.setting_service = registry.get_instance("setting_service")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")
        self.access_service = registry.get_instance("access_service")
        self.command_service = registry.get_instance("command_service")

    def pre_start(self):
        self.bot.register_packet_handler(server_packets.PrivateChannelClientJoined.id, self.handle_private_channel_client_joined)
        self.bot.register_packet_handler(server_packets.PrivateChannelClientLeft.id, self.handle_private_channel_client_left)
        self.bot.register_packet_handler(server_packets.PrivateChannelMessage.id, self.handle_private_channel_message)

        self.event_service.register_event_type(self.JOINED_PRIVATE_CHANNEL_EVENT)
        self.event_service.register_event_type(self.LEFT_PRIVATE_CHANNEL_EVENT)
        self.event_service.register_event_type(self.PRIVATE_CHANNEL_MESSAGE_EVENT)
        self.event_service.register_event_type(self.PRIVATE_CHANNEL_COMMAND_EVENT)

        self.access_service.register_access_level("guest", 90, self.in_any_private_channel)

        self.command_service.register_command_channel("Private Channel", self.PRIVATE_CHANNEL_COMMAND)

    def handle_private_channel_message(self, conn: Conn, packet: server_packets.PrivateChannelMessage):
        char_name = self.character_service.get_char_name(packet.char_id)
        if packet.private_channel_id != conn.char_id:
            # channel_name = self.character_service.get_char_name(packet.private_channel_id)
            # self.logger.log_chat(conn, f"Private Channel({channel_name})", char_name, packet.message)
            pass
        else:
            self.logger.log_chat(conn, "Private Channel", char_name, packet.message)

            if not conn.is_main or conn.char_id == packet.char_id:
                return

            if not self.handle_private_channel_command(conn, packet):
                self.event_service.fire_event(self.PRIVATE_CHANNEL_MESSAGE_EVENT, DictObject({"char_id": packet.char_id,
                                                                                              "name": char_name,
                                                                                              "message": packet.message,
                                                                                              "conn": conn}))

    def handle_private_channel_client_joined(self, conn: Conn, packet: server_packets.PrivateChannelClientJoined):
        char_name = self.character_service.get_char_name(packet.char_id)
        if packet.private_channel_id != conn.char_id:
            # channel_name = self.character_service.get_char_name(packet.private_channel_id)
            # self.logger.log_chat(conn, f"Private Channel({channel_name}", None, f"{char_name} joined the channel.")
            pass
        else:
            self.logger.log_chat(conn, "Private Channel", None, f"{char_name} joined the channel.")
            conn.private_channel[packet.char_id] = packet

            if conn.is_main:
                self.event_service.fire_event(self.JOINED_PRIVATE_CHANNEL_EVENT, DictObject({"char_id": packet.char_id,
                                                                                             "name": char_name,
                                                                                             "conn": conn}))

    def handle_private_channel_client_left(self, conn: Conn, packet: server_packets.PrivateChannelClientLeft):
        char_name = self.character_service.get_char_name(packet.char_id)
        if packet.private_channel_id != conn.char_id:
            # channel_name = self.character_service.get_char_name(packet.private_channel_id)
            # self.logger.log_chat(conn, f"Private Channel({channel_name})", None, f"{char_name} left the channel.")
            pass
        else:
            self.logger.log_chat(conn, "Private Channel", None, f"{char_name} left the channel.")
            del conn.private_channel[packet.char_id]

            if conn.is_main:
                self.event_service.fire_event(self.LEFT_PRIVATE_CHANNEL_EVENT, DictObject({"char_id": packet.char_id,
                                                                                           "name": char_name,
                                                                                           "conn": conn}))

    def handle_private_channel_command(self, conn: Conn, packet: server_packets.PrivateChannelMessage):
        if not self.setting_service.get("accept_commands_from_slave_bots").get_value() and not conn.is_main:
            return False

        # since the command symbol is required in the private channel,
        # the command_str must have length of at least 2 in order to be valid,
        # otherwise it is ignored
        if len(packet.message) < 2:
            return False

        # ignore leading space
        message = packet.message.lstrip()

        def reply(msg):
            self.bot.send_private_channel_message(msg, private_channel_id=conn.char_id, conn=conn)
            self.event_service.fire_event(self.PRIVATE_CHANNEL_COMMAND_EVENT,
                                          DictObject({"char_id": None, "name": None, "message": msg, "conn": conn}))

        if message.startswith(self.setting_service.get("symbol").get_value()) and packet.private_channel_id == conn.get_char_id():
            char_name = self.character_service.get_char_name(packet.char_id)
            self.event_service.fire_event(self.PRIVATE_CHANNEL_COMMAND_EVENT,
                                          DictObject({"char_id": packet.char_id, "name": char_name, "message": packet.message, "conn": conn}))

            self.command_service.process_command(
                self.command_service.trim_command_symbol(message),
                self.PRIVATE_CHANNEL_COMMAND,
                packet.char_id,
                reply,
                conn)
            return True
        else:
            return False

    def invite(self, char_id, conn: Conn):
        if char_id != conn.char_id and conn.is_main:
            conn.send_packet(client_packets.PrivateChannelInvite(char_id))

    def kick(self, char_id, conn: Conn):
        if char_id != conn.char_id:
            conn.send_packet(client_packets.PrivateChannelKick(char_id))

    def kick_from_all(self, char_id):
        for _id, conn in self.bot.get_conns():
            if char_id in conn.private_channel:
                conn.send_packet(client_packets.PrivateChannelKick(char_id))

    def kickall(self, conn: Conn):
        conn.send_packet(client_packets.PrivateChannelKickAll())

    def in_any_private_channel(self, char_id):
        for _id, conn in self.bot.get_conns():
            if char_id in conn.private_channel:
                return True
        return False
Ejemplo n.º 10
0
class PublicChannelService:
    ORG_CHANNEL_MESSAGE_EVENT = "org_channel_message"
    ORG_MSG_EVENT = "org_msg"

    ORG_MSG_CHANNEL_ID = 42949672961

    def __init__(self):
        self.logger = Logger(__name__)
        self.name_to_id = {}
        self.id_to_name = {}
        self.org_channel_id = None
        self.org_id = None
        self.org_name = None

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")

    def pre_start(self):
        self.bot.add_packet_handler(server_packets.PublicChannelJoined.id, self.add)
        self.bot.add_packet_handler(server_packets.PublicChannelLeft.id, self.remove)
        # priority must be above that of CommandService in order for relaying of commands to work correctly
        self.bot.add_packet_handler(server_packets.PublicChannelMessage.id, self.public_channel_message, priority=30)
        self.event_service.register_event_type(self.ORG_CHANNEL_MESSAGE_EVENT)
        self.event_service.register_event_type(self.ORG_MSG_EVENT)

    def get_channel_id(self, channel_name):
        return self.name_to_id.get(channel_name)

    def get_channel_name(self, channel_id):
        return self.id_to_name.get(channel_id, None)

    def add(self, packet: server_packets.PublicChannelJoined):
        self.id_to_name[packet.channel_id] = packet.name
        self.name_to_id[packet.name] = packet.channel_id
        if self.is_org_channel_id(packet.channel_id):
            self.org_channel_id = packet.channel_id
            self.org_id = 0x00ffffffff & packet.channel_id

            self.logger.debug("Org Id: %d" % self.org_id)
            self.logger.debug("Org Name: %s" % packet.name)

            if packet.name != "Clan (name unknown)":
                self.org_name = packet.name

    def remove(self, packet: server_packets.PublicChannelLeft):
        channel_name = self.get_channel_name(packet.channel_id)
        del self.id_to_name[packet.channel_id]
        del self.name_to_id[channel_name]

    def public_channel_message(self, packet: server_packets.PublicChannelMessage):
        if self.is_org_channel_id(packet.channel_id):
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat("Org Channel", char_name, message)
            self.event_service.fire_event(self.ORG_CHANNEL_MESSAGE_EVENT, packet)
        elif packet.channel_id == self.ORG_MSG_CHANNEL_ID:
            char_name = self.character_service.get_char_name(packet.char_id)
            if packet.extended_message:
                message = packet.extended_message.get_message()
            else:
                message = packet.message
            self.logger.log_chat("Org Msg", char_name, message)
            self.event_service.fire_event(self.ORG_MSG_EVENT, packet)

    def is_org_channel_id(self, channel_id):
        return channel_id >> 32 == 3

    def get_org_id(self):
        return self.org_id

    def get_org_name(self):
        return self.org_name

    def get_all_public_channels(self):
        return self.id_to_name
class PublicChannelService:
    ORG_MESSAGE_EVENT = "org_message"

    def __init__(self):
        self.logger = Logger(__name__)
        self.name_to_id = {}
        self.id_to_name = {}
        self.org_channel_id = None
        self.org_id = None
        self.org_name = None

    def inject(self, registry):
        self.bot = registry.get_instance("bot")
        self.event_service = registry.get_instance("event_service")
        self.character_service = registry.get_instance("character_service")

    def pre_start(self):
        self.bot.add_packet_handler(server_packets.PublicChannelJoined.id,
                                    self.add)
        self.bot.add_packet_handler(server_packets.PublicChannelLeft.id,
                                    self.remove)
        self.bot.add_packet_handler(server_packets.PublicChannelMessage.id,
                                    self.public_channel_message)
        self.event_service.register_event_type(self.ORG_MESSAGE_EVENT)

    def get_channel_id(self, channel_name):
        return self.name_to_id.get(channel_name, None)

    def get_channel_name(self, channel_id):
        return self.id_to_name[channel_id]

    def add(self, packet: server_packets.PublicChannelJoined):
        self.id_to_name[packet.channel_id] = packet.name
        self.name_to_id[packet.name] = packet.channel_id
        if self.is_org_channel_id(packet.channel_id):
            self.org_channel_id = packet.channel_id
            self.org_id = 0x00ffffffff & packet.channel_id

            self.logger.debug("Org Id: %d" % self.org_id)
            self.logger.debug("Org Name: %s" % packet.name)

            if packet.name != "Clan (name unknown)":
                self.org_name = packet.name

    def remove(self, packet: server_packets.PublicChannelLeft):
        channel_name = self.get_channel_name(packet.channel_id)
        del self.id_to_name[packet.channel_id]
        del self.name_to_id[channel_name]

    def public_channel_message(self,
                               packet: server_packets.PublicChannelMessage):
        if self.is_org_channel_id(packet.channel_id):
            char_name = self.character_service.get_char_name(packet.char_id)
            self.logger.log_chat("Org Channel", char_name, packet.message)
            self.event_service.fire_event(self.ORG_MESSAGE_EVENT, packet)

    def is_org_channel_id(self, channel_id):
        return channel_id >> 32 == 3

    def get_org_id(self):
        return self.org_id

    def get_org_name(self):
        return self.org_name