예제 #1
0
    async def consume(self) -> None:
        """
        This function is a real consumer, get event message from event queue
         and handle it
        """
        if self.__queue is None:
            raise RuntimeError("missing event queue for Handler")

        while True:
            if self.__queue.empty():
                # If this consumer is idle too long, will be exited
                if self.__pending_exit:
                    break

                await asyncio.sleep(1)
                continue

            try:
                item = self.__queue.get_nowait()
                self.__active_time = time.time()

                self.__task_count = self.__task_count + 1
                await self.handle(item=item)
                self.__task_count = self.__task_count - 1
            except ValueError as err:
                Logger.error(RuntimeError("Event has closed unexpectedly, consumer cannot get anything"))
            except queue.Empty as err:
                await asyncio.sleep(0.5)
예제 #2
0
파일: Bot.py 프로젝트: RMTT/khlbot
    def subprocess_initializer(log_queue, name="worker"):
        """
        Initializer of worker subprocess
        :param log_queue: Log queue
        :param name: process name
        """
        Logger.worker_configure(_queue=log_queue)

        multiprocessing.current_process().name = name

        Logger.info(f"active new worker: Process-{os.getpid()} {name}")
예제 #3
0
    async def check_timeout(self):
        """
        Check the idle time of consumer
        """
        while True:
            if time.time() - self.__active_time >= self.__timeout and self.__task_count == 0:
                Logger.warning(
                    f"consumers on [Process-{os.getpid()} {multiprocessing.current_process().name}] "
                    f"is pending to exit, because it's idle too long")
                self.__pending_exit = True

            await asyncio.sleep(self.__timeout)
예제 #4
0
파일: Bot.py 프로젝트: RMTT/khlbot
        async def check_task() -> None:
            while True:
                done_or_canceled_count = 0
                for task in tasks:
                    try:
                        if task.done() or task.cancelled():
                            done_or_canceled_count = done_or_canceled_count + 1
                            continue

                        exception = task.exception()

                        if exception is not None:
                            Logger.error(exception)
                    except asyncio.CancelledError as err:
                        Logger.error(
                            RuntimeError(
                                f"a consumer on [Process-{os.getpid()} {multiprocessing.current_process().name}] "
                                f"has cancelled unexpectedly"))
                        Logger.error(err)
                    except asyncio.InvalidStateError:
                        pass
                if done_or_canceled_count == consumer_number:
                    Logger.warning(
                        f"consumers on [Process-{os.getpid()} {multiprocessing.current_process().name}] "
                        f"have all done or canceled, worker stopped")
                    return
                await asyncio.sleep(2)
예제 #5
0
파일: BaseHandler.py 프로젝트: RMTT/khlbot
    async def handle_command(self, text: str, event, **kwargs) -> None:
        """
        Parse commands and its parameters
        :param text: The text contains commands and its parameters
        :param event: KHL Event object
        :param kwargs: extra data
        :return: None
        """
        if len(self.get_commands()) == 0:
            return

        for commands in self.get_commands():
            for command in commands:
                pos = text.find(command)

                if pos != -1:
                    params = []
                    for _ in range(commands[command][
                            CONFIG.COMMANDER_KEY_PARAM_NUMBER]):
                        next_space = text.find(' ', pos + len(command) + 1)
                        if next_space != -1:
                            params.append(text[pos + len(command) +
                                               1:next_space])
                        else:
                            params.append(text[pos + len(command) + 1:])

                        if len(params[-1]) == 0:
                            params = []
                            break

                        pos = next_space + 1

                    if len(params) == commands[command][
                            CONFIG.COMMANDER_KEY_PARAM_NUMBER]:
                        func = commands[command][CONFIG.COMMANDER_KEY_HANDLE]
                        kwargs[CONFIG.BOT_KEY_EVENT] = event
                        try:
                            await func(*params, **kwargs)
                        except Exception as err:
                            if isinstance(func, functools.partial):
                                func_name = func.func.__name__
                            else:
                                func_name = func.__name__

                            Logger.warning(
                                f"command [{command}] and it's handle function [{func_name}] raise some error"
                            )
                            Logger.error(err)
                        break
예제 #6
0
파일: KHLWss.py 프로젝트: RMTT/khlbot
    def get_gateway(cls, token, url) -> {str, None}:
        """
        Get gateway address of khl websocket
        :param url: url of khl gateway api
        :param token: khl bot token
        :return: khl websocket address
        """
        headers = {
            "Authorization": f"Bot {token}"
        }

        try:
            response = requests.get(url, headers=headers)
            json_data = response.json()

            if json_data["code"] != 0:
                return None

            return json_data["data"]["url"]
        except Exception as e:
            Logger.error(e)
예제 #7
0
파일: utils.py 프로젝트: RMTT/khlbot
async def get_from_khl_api(url, body, token, success=None, failed=None):
    headers = {"Authorization": f"Bot {token}"}

    async with aiohttp.ClientSession() as session:
        try:
            async with session.get(url=url, params=body,
                                   headers=headers) as response:
                json_rep = await response.json()

                if response.status == 200 and json_rep["code"] == 0:
                    if success is not None:
                        success(json_rep)
                else:
                    if failed is not None:
                        failed(json_rep)

                return json_rep
        except Exception as e:
            Logger.error(e)
            if failed is not None:
                failed({"code": -1, "message": str(e)})
예제 #8
0
파일: BaseHandler.py 프로젝트: RMTT/khlbot
    async def handle_subscribe(self, _type, condition: dict, handle,
                               event: Event) -> None:
        """
        Handle subscribe events
        :param _type: KHL Event type
        :param condition: Conditions to filter event
        :param handle: Handle function
        :param event: Event Object
        :return: None
        """
        can_run = True

        if condition is not None:
            for item in condition:
                keys = str(item).strip().split('.')
                step = None
                for key in keys:
                    if step is None:
                        step = condition[key]
                    else:
                        step = step[key]
                if step != condition[item]:
                    can_run = False
                    break

        if can_run:
            kwargs = {CONFIG.COMMANDER_KEY_EVENT: event}
            try:
                await handle(**kwargs)
            except Exception as err:
                if isinstance(handle, functools.partial):
                    func_name = handle.func.__name__
                else:
                    func_name = handle.__name__

                Logger.warning(
                    f"subscribe event function [{func_name}] raises an error")
                Logger.error(err)
예제 #9
0
파일: Bot.py 프로젝트: RMTT/khlbot
    def run(self) -> None:
        """
        The entry for launch bot, in this function, bot will connect several components:
        Handler(Consumer), KHLWss(Producer), Commander(Task)
        """
        if self.__handler is None:
            Logger.error(Exception("Please set messages handler."))
            return

        try:
            multiprocessing.current_process(
            ).name = "Bot"  # set main process name

            atexit.register(self.__exit_handler)  # handle exit event

            # handle some signal
            signal.signal(signal.SIGINT, Bot.signal_handler)
            signal.signal(signal.SIGTERM, Bot.signal_handler)

            # set up logger
            self.__log_listener = Logger.listener_configure(
                _queue=self.__log_queue)
            self.__log_listener.start()

            self.__wss.event_queue = self.__queue
            self.__handler.event_queue = self.__queue

            for commander in self.__commanders:
                self.__handler.add_commands(commander.get_commands())
                self.__handler.add_subscribes(commander.get_subscribes())

            loop = asyncio.get_event_loop()
            self.__loop = loop

            loop.create_task(self.__wss.start())
            loop.create_task(self.__check_queue_size())

            self.__launch_subprocess(is_leader=True)
            self.__running_process_count = self.__running_process_count + 1

            # launch interval tasks
            self.__launch_interval()

            loop.run_forever()
        except Exception as e:
            Logger.error(e)
            Logger.error(RuntimeError("fatal error, bot will be terminated"))
예제 #10
0
파일: BaseHandler.py 프로젝트: RMTT/khlbot
    async def handle(self, item) -> None:
        """
        Same as Handler.handle
        """
        if item[CONFIG.BOT_KEY_MESSAGE_TYPE] == CONFIG.BOT_MESSAGE_TYPE_EVENT:
            try:
                item = item[CONFIG.BOT_KEY_MESSAGE_DATA]
                event = Event(item)

                if event.type == CONFIG.KHL_MSG_TEXT:
                    await self.handle_command(item["content"], event=event)
                elif event.type == CONFIG.KHL_MSG_SYSTEM:
                    system_event_type = event.extra.type
                    _subscribes = self.get_subscribes()
                    if system_event_type is not None and system_event_type in _subscribes:
                        for item in _subscribes[system_event_type]:
                            await self.handle_subscribe(
                                _type=system_event_type,
                                condition=item[
                                    CONFIG.COMMANDER_KEY_CONDITIONS],
                                handle=item[CONFIG.COMMANDER_KEY_HANDLE],
                                event=event)
            except ValueError as err:
                Logger.warning("Please check commands configuration")
                Logger.error(err)

        elif item[CONFIG.
                  BOT_KEY_MESSAGE_TYPE] == CONFIG.BOT_MESSAGE_TYPE_INTERVAL:
            try:
                await item[CONFIG.COMMANDER_KEY_HANDLE]()
            except Exception as err:
                func = item[CONFIG.COMMANDER_KEY_HANDLE]
                if isinstance(func, functools.partial):
                    func_name = func.func.__name__
                else:
                    func_name = func.__name__

                Logger.warning(
                    f"interval function [{func_name}] raises an error")
                Logger.error(err)
예제 #11
0
파일: Bot.py 프로젝트: RMTT/khlbot
    def __launch_subprocess(self, is_leader: bool = False) -> None:
        """
        Driver function for starting new subprocess
        :param is_leader: If True, start a leader subprocess for consumers,
         it means the first subprocess for consumers, will not be exited when the process is idle
        :return: None
        """
        def process_count(result=None):
            self.__running_process_count = self.__running_process_count - 1

        def process_error(err):
            Logger.warning("Have a error when launch worker")
            Logger.error(err)

            process_count()

        self.__pool.apply_async(
            Bot.subprocess_consumer,
            (self.__handler, self.__config[CONFIG.MAX_CONSUMER_NUMBER],
             is_leader),
            error_callback=lambda err: Logger.error(err),
            callback=process_count)
예제 #12
0
파일: KHLWss.py 프로젝트: RMTT/khlbot
    async def start(self) -> None:
        """
        Launch websocket, and do some important operations
        :return: None
        """
        Logger.info("Getting KHL Websocket address")
        uri = KHLWss.get_gateway(token=self.__token, url=KHL_API_BASEURL + KHL_API_GATEWAY)
        if uri is None:
            Logger.error(Exception("Failed to get websocket address"))
            return

        Logger.info(f"Success to get websocket address")
        async with websockets.client.connect(uri) as ws_connection:
            Logger.info("Connect to websocket success, launch bot")

            asyncio.create_task(self.heartbeat(ws_connection))

            while True:
                try:
                    if ws_connection is None or not ws_connection.open:
                        Logger.warning("Reconnecting to khl websocket")
                        uri = KHLWss.get_gateway(token=self.__token, url=KHL_API_BASEURL + KHL_API_GATEWAY)  # uri可能已经更新
                        ws_connection = await websockets.client.connect(uri)
                        Logger.warning("Reconnect to khl websocket success")

                    msg = await ws_connection.recv()
                    json_rep = decompress_to_json(msg)

                    if json_rep['s'] == 0:
                        self.latest_sn = json_rep["sn"]
                        if self.__event_queue is not None:
                            try:
                                self.__event_queue.put_nowait({
                                    CONFIG.BOT_KEY_MESSAGE_TYPE: CONFIG.BOT_MESSAGE_TYPE_EVENT,
                                    CONFIG.BOT_KEY_MESSAGE_DATA: json_rep['d']
                                })
                            except ValueError as err:
                                Logger.error(RuntimeError("Event queue has closed unexpectedly"))
                                Logger.error(err)
                            except queue.Full:
                                Logger.warning("Event queue is full")


                except (websockets.ConnectionClosedError, websockets.ConnectionClosed) as e:
                    Logger.warning("The websockets have closed unexpectedly")

                    Logger.error(e)

                    # Try reconnection after 3 seconds, avoid too many times to reconnection on a short time
                    await asyncio.sleep(3)
                except Exception as e:
                    Logger.warning("The websockets have closed unexpectedly")

                    Logger.error(e)

                    await asyncio.sleep(3)
예제 #13
0
파일: Bot.py 프로젝트: RMTT/khlbot
 def signal_handler(sig=None, frame=None):
     Logger.warning("khlbot will be exited")
     exit(0)
예제 #14
0
파일: Bot.py 프로젝트: RMTT/khlbot
        def process_error(err):
            Logger.warning("Have a error when launch worker")
            Logger.error(err)

            process_count()