async def task_handler(self, handler, event, user): try: log.info( f'create task with timeout for user[{user}] event[{event.id}]') await asyncio.wait_for(handler.handle(event, self, user), timeout=self.bot.task_timeout_s) except asyncio.TimeoutError: log.info( "task for user[{user_id}] with eventId[{event_id}] cancelled by timeout ({s})s" .format(user_id=user.id, event_id=event.id, s=self.bot.task_timeout)) except asyncio.CancelledError: log.info( f"task for user[{user.id}] with eventId[{event.id}] cancelled'" ) except Exception as e: log.exception(e) finally: log.info(f'finally task for {user}') user.handler = None while not user.events.empty(): remaining_event = await user.events.get() log.info( f'move event {remaining_event.id} from user[{user.id}] queue into bot queue' ) await self.bot.events.put(remaining_event)
async def handle(self, event, dispatcher, user=None): if self.callback: log.info(f"call '{self.callback.__name__}' via '{self.__class__.__name__}'") kwargs = {'bot': dispatcher.bot, 'event': event} if user: kwargs['user'] = user await self.callback(**kwargs)
async def stop_polling(self): log.info('stop polling') self.is_polling = False if isinstance(self._polling_task, Task): self._polling_task.cancel() if isinstance(self._dispatcher_task, Task): self._dispatcher_task.cancel() self._polling_task = None self._dispatcher_task = None
async def start_polling(self): for item, message in ((self.is_polling, 'polling already run'), (self._polling_task, 'polling task already exists'), (self._dispatcher_task, 'dispatcher task already exists')): if item: log.warning(message) break else: log.info('start polling') self.is_polling = True self._polling_task = self.loop.create_task(self._polling()) self._dispatcher_task = self.loop.create_task( self.dispatcher.dispatch())
async def stop(self): log.info('stop bot') self.is_running = False await self.stop_polling() await self._session.close()
async def dispatch(self): while self.bot.is_running and self.bot.is_polling: task_len = len(asyncio.Task.all_tasks()) if task_len < self.bot.task_max_len: # get event from queue event = await self.bot.events.get() # prepare user data user_id = event.data.get('from', {}).get('userId', None) if not user_id: user_id = event.data.get('from', {}).get('chatId') user = self.bot.users.get(user_id, User(user_id, self.bot)) self.bot.users[user_id] = user try: log.info(f"dispatching event[{event.id}]") processed = False for handler in (h for h in self.handlers if h.check(event=event, dispatcher=self)): log.info( f'handle event[{event.id}] by handler[{handler}]') if user.task and not user.task.done(): if isinstance(handler, DefaultHandler) or [ i for i in handler.ignore if i is user.handler ]: log.info( f"handler[{handler}] was cancelled because user[{user}] have active task" ) break else: try: log.info( f'attempt cancel user {user_id} task') user.handler = None user.task.cancel() await user.task except Exception as e: log.info(e) if handler.multiline: user.task = self.bot.loop.create_task( self.task_handler(handler, event, user)) user.parent_event_id = event.id user.handler = handler else: log.info(f'create task for user[{user}]') self.bot.loop.create_task( handler.handle(event, self)) processed = True if not processed and user.task and not user.task.done(): log.info( f'put event[{event.id}] into user[{user_id}] queue' ) await self.bot.users[user_id].events.put(event) except StopDispatching: log.debug( "Caught '{}' exception, stopping dispatching.".format( StopDispatching.__name__)) except Exception: log.exception("Exception while dispatching event!") else: log.critical('task limit was reached: {}'.format(task_len)) await asyncio.sleep(1)