async def get_server(self) -> dict: logger.debug("Getting polling server...") if self.user_id is None: self.user_id = (await self.api.request("users.get", {}))["response"][0]["id"] return (await self.api.request("messages.getLongPollServer", {}))["response"]
async def validate_request(self, request: dict) -> dict: """ Validates requests from VK, to change validations change API.request_validators (list of RequestValidator's) """ for validator in self.request_validators: request = await validator.validate(request) logger.debug("API request was validated") return request # type: ignore
async def get_server(self) -> dict: logger.debug("Getting polling server...") if self.group_id is None: self.group_id = (await self.api.request("groups.getById", {}))["response"][0]["id"] return (await self.api.request("groups.getLongPollServer", {"group_id": self.group_id}))[ "response" ]
async def pre(self, method: str, url: str, data: typing.Optional[dict] = None, **kwargs): logger.debug(f"{method.upper()} Request to {url}; data={data} " + "; ".join(f"{k}={v}" for k, v in kwargs.items()))
async def get_callback_confirmation_code(self) -> str: logger.debug("Getting callback confirmation code...") return (await self.api.request( "groups.getCallbackConfirmationCode", { "group_id": self.group_id, }, ))["response"]["code"]
async def get_callback_servers( self, servers_ids: Optional[List[int]] = None) -> List[Dict[str, Any]]: logger.debug("Getting callback servers...") data: Dict[str, Any] = {"group_id": self.group_id} if servers_ids is not None: data.update({"server_ids": ",".join(map(str, servers_ids))}) return (await self.api.request("groups.getCallbackServers", data))["response"]["items"]
async def validate_response( self, method: str, data: dict, response: typing.Union[dict, str] ) -> typing.Union[dict, typing.NoReturn]: """ Validates response from VK, to change validations change API.response_validators (list of ResponseValidator's) """ for validator in self.response_validators: response = await validator.validate(method, data, response, self) logger.debug("API response was validated") return response # type: ignore
def restart(): """ https://github.com/cherrypy/cherrypy/blob/0857fa81eb0ab647c7b59a019338bab057f7748b/cherrypy/process/wspbus.py#L305 """ args = sys.argv[:] logger.debug("Restarting: %s" % " ".join(args)) args.insert(0, sys.executable) if sys.platform == "win32": args = ['"%s"' % arg for arg in args] os.chdir(_startup_cwd) os.execv(sys.executable, args)
async def run_polling(self, custom_polling: Optional[ABCPolling] = None ) -> NoReturn: polling = custom_polling or self.polling logger.info(f"Starting polling for {polling.api!r}") async for event in polling.listen(): # type: ignore logger.debug(f"New event was received: {event}") for update in event["updates"]: await self.router.route(update, polling.api)
async def add_callback_server(self) -> int: logger.debug("Adding callback server...") data = { "group_id": self.group_id, "url": self.url, "title": self.title, "secret_key": self.secret_key, } return (await self.api.request("groups.addCallbackServer", data))["response"]["server_id"]
async def route(self, event: dict, ctx_api: "ABCAPI") -> None: logger.debug("Routing update {}".format(event)) for view in self.views.values(): try: if not await view.process_event(event): continue await view.handle_event(event, ctx_api, self.state_dispenser) except BaseException as e: await self.error_handler.handle(e)
async def request(self, method: str, data: dict) -> dict: """ Makes a single request opening a session """ async with self.http as session: response = await session.request_text( "POST", self.API_URL + method, data=data, # type: ignore params={"access_token": self.token, "v": self.API_VERSION}, ) logger.debug("Request {} with {} data returned {}".format(method, data, response)) return await self.validate_response(method, data, response)
async def run_polling(self, custom_polling: Optional["ABCPolling"] = None): polling = custom_polling or self.polling logger.info("Starting polling for {!r}", polling.api) async for event in polling.listen(): logger.debug("New event was received: {}", event) for update in event["updates"]: if not self.task_each_event: await self.router.route(update, polling.api) else: self.loop.create_task(self.router.route(update, polling.api))
async def run_polling(self, custom_polling: Optional["ABCPolling"] = None) -> NoReturn: polling = custom_polling or self.polling logger.info(f"Starting polling for {polling.api!r}") async for event in polling.listen(): # type: ignore logger.debug(f"New event was received: {event}") for update in event.get("updates", []): if not self.task_each_event: await self.router.route(update, polling.api) else: self.loop.create_task(self.router.route(update, polling.api))
def run_multibot(bot: Bot, apis: Iterable[ABCAPI], polling_type: Type[ABCPolling] = BotPolling): """ Add run_polling with polling constructed from derived apis :param bot: Bot main instance (api is not required) :param apis: Iterable of apis :param polling_type: polling type to be ran """ for i, api_instance in enumerate(apis): logger.debug(f"Connecting API (index: {i})") polling = polling_type().construct(api_instance) api_instance.http = SingleSessionManager(AiohttpClient) bot.loop_wrapper.add_task(bot.run_polling(custom_polling=polling)) bot.loop_wrapper.run_forever(bot.loop)
async def set_callback_settings(self, server_id: int, params: Optional[Dict[str, bool]] = None): """Search values in https://dev.vk.com/method/groups.getCallbackSettings""" logger.debug("Setting callback settings...") data = {"group_id": self.group_id, "server_id": server_id} if params is not None: for k, v in params.items(): data.update({k: v}) await self.api.request("groups.setCallbackSettings", data)
async def get_event(self, server: dict) -> dict: logger.debug("Making long request to get event with longpoll...") return await self.api.http_client.request_json( "{}?act=a_check&key={}&ts={}&wait={}&rps_delay={}".format( server["server"], server["key"], server["ts"], self.wait, self.rps_delay, ), method="POST", )
async def listen(self) -> AsyncIterator[dict]: # type: ignore server = await self.get_server() logger.debug("Starting listening to longpoll") while not self.stop: try: event = await self.get_event(server) if not event.get("ts"): server = await self.get_server() continue server["ts"] = event["ts"] yield event except BaseException as e: await self.error_handler.handle(e)
async def handle_event(self, event: T_contra, ctx_api: "ABCAPI", state_dispenser: "ABCStateDispenser") -> Any: logger.debug("Handling event ({}) with message view".format( self.get_event_type(event))) context_variables: dict = {} handle_responses = [] handlers = [] mw_instances = await self.pre_middleware(event, context_variables) if mw_instances is None: logger.info("Handling stopped, pre_middleware returned error") return for handler_basement in self.get_handler_basements(event): event_model = self.get_event_model(handler_basement, event) if isinstance(event_model, dict): event_model["ctx_api"] = ctx_api else: setattr(event_model, "unprepared_ctx_api", ctx_api) result = await handler_basement.handler.filter(event_model) logger.debug("Handler {} returned {}".format( handler_basement.handler, result)) if result is False: continue elif isinstance(result, dict): context_variables.update(result) handler_response = await handler_basement.handler.handle( event_model, **context_variables) handle_responses.append(handler_response) handlers.append(handler_basement.handler) return_handler = self.handler_return_manager.get_handler( handler_response) if return_handler is not None: await return_handler( self.handler_return_manager, handler_response, event_model, context_variables, ) if handler_basement.handler.blocking: break await self.post_middleware(mw_instances, handle_responses, handlers)
async def pre_middleware(self, event: "MessageMin", context_variables: dict) -> Optional[Exception]: """Run all of the pre middleware methods and return an exception if any error occurs""" self.middleware_instances.clear() for middleware in self.middlewares: mw_instance = middleware(event) await mw_instance.pre() if not mw_instance.can_forward: logger.debug( f"{mw_instance} returned error {mw_instance.error}") return mw_instance.error self.middleware_instances.append(mw_instance) context_variables.update(mw_instance.context_update)
def run_multibot( user: "******", apis: Iterable["ABCAPI"], polling_type: Type["ABCPolling"] = UserPolling ): """Add run_polling with polling constructed from derived apis :param user: User main instance (api is not required) :param apis: Iterable of apis :param polling_type: polling type to be ran """ for i, api_instance in enumerate(apis): logger.debug("Connecting API (index: {})", i) polling = polling_type().construct(api_instance) api_instance.http_client = SingleAiohttpClient() user.loop_wrapper.add_task(user.run_polling(custom_polling=polling)) user.loop_wrapper.run_forever(user.loop)
def synchronous_wrapper(*args, **kwargs): try: return func(*args, **kwargs) except exception as e: if exception_handler is not None: return exception_handler(e, *args, **kwargs) elif just_log: logger.error( f"{func.__name__} (handling with swear) has thrown an exception: \n\n{traceback.format_exc()}" ) elif just_return: return e finally: logger.debug(f"Function {func.__name__} was handled with swear")
async def edit_callback_server(self, server_id: int, secret_key: Optional[str] = None): logger.debug("Editing callback server...") data = { "group_id": self.group_id, "server_id": server_id, "url": self.url, "title": self.title, "secret_key": self.secret_key, } if secret_key is not None: data.update({"secret_key": secret_key}) await self.api.request("groups.editCallbackServer", data)
async def handle_event(self, event: dict, ctx_api: "ABCAPI", state_dispenser: "ABCStateDispenser") -> Any: logger.debug("Handling event ({}) with message view".format( event.get("event_id"))) context_variables = {} message = message_min(event, ctx_api) message.state_peer = await state_dispenser.cast( self.get_state_key(event)) for text_ax in self.default_text_approximators: message.text = text_ax(message) for middleware in self.middlewares: response = await middleware.pre(message) if response == MiddlewareResponse(False): return elif isinstance(response, dict): context_variables.update(response) handle_responses = [] handlers = [] for handler in self.handlers: result = await handler.filter(message) logger.debug("Handler {} returned {}".format(handler, result)) if result is False: continue elif isinstance(result, dict): context_variables.update(result) handler_response = await handler.handle(message, **context_variables) handle_responses.append(handler_response) handlers.append(handler) return_handler = self.handler_return_manager.get_handler( handler_response) if return_handler is not None: await return_handler(self.handler_return_manager, handler_response, message, context_variables) if handler.blocking: break for middleware in self.middlewares: await middleware.post(message, self, handle_responses, handlers)
async def listen(self) -> AsyncGenerator[dict, None]: server = await self.get_server() logger.debug("Starting listening to longpoll") while not self.stop: try: event = await self.get_event(server) if not event.get("ts"): server = await self.get_server() continue server["ts"] = event["ts"] yield event except ServerConnectionError: server = await self.get_server() except Exception as e: await self.error_handler.handle(e)
async def request_many( self, requests: typing.Iterable[APIRequest] # type: ignore ) -> typing.AsyncIterator[dict]: """ Makes many requests opening one session """ async with self.http as session: for request in requests: method, data = request.method, await self.validate_request(request.data) # type: ignore response = await session.request_text( "POST", self.API_URL + method, data=data, # noqa params={"access_token": self.token, "v": self.API_VERSION}, # noqa ) logger.debug("Request {} with {} data returned {}".format(method, data, response)) yield await self.validate_response(method, data, response)
async def request_many( self, requests: Iterable[APIRequest] # type: ignore ) -> AsyncIterator[dict]: """Makes many requests opening one session""" for request in requests: method, data = request.method, await self.validate_request(request.data) # type: ignore async with self.token_generator as token: response = await self.http_client.request_json( self.API_URL + method, method="POST", data=data, # noqa params={"access_token": token, "v": self.API_VERSION}, # noqa ) logger.debug("Request {} with {} data returned {}".format(method, data, response)) yield await self.validate_response(method, data, response)
async def handle_event(self, event: T_contra, ctx_api: "ABCAPI", state_dispenser: "ABCStateDispenser") -> None: # For user event mapping, consider checking out # https://dev.vk.com/api/user-long-poll/getting-started logger.debug("Handling event ({}) with message view".format( self.get_event_type(event))) context_variables: dict = {} message = await self.get_message(event, ctx_api) message.state_peer = await state_dispenser.cast( self.get_state_key(message)) for text_ax in self.default_text_approximators: message.text = text_ax(message) mw_instances = await self.pre_middleware(message, context_variables) if mw_instances is None: logger.info("Handling stopped, pre_middleware returned error") return handle_responses = [] handlers = [] for handler in self.handlers: result = await handler.filter(message) logger.debug("Handler {} returned {}".format(handler, result)) if result is False: continue elif isinstance(result, dict): context_variables.update(result) handler_response = await handler.handle(message, **context_variables) handle_responses.append(handler_response) handlers.append(handler) return_handler = self.handler_return_manager.get_handler( handler_response) if return_handler is not None: await return_handler(self.handler_return_manager, handler_response, message, context_variables) if handler.blocking: break await self.post_middleware(mw_instances, handle_responses, handlers)
async def handle_event(self, event: dict, ctx_api: "ABCAPI", state_dispenser: "ABCStateDispenser") -> Any: logger.debug("Handling event ({}) with message view".format( event.get("event_id"))) handler_basement = self.handlers[GroupEventType(event["type"])] context_variables = {} event_model = handler_basement.dataclass(**event) if isinstance(event_model, dict): event_model["ctx_api"] = ctx_api else: setattr(event_model, "unprepared_ctx_api", ctx_api) for middleware in self.middlewares: response = await middleware.pre(event_model) if response == MiddlewareResponse(False): return elif isinstance(response, dict): context_variables.update(response) result = await handler_basement.handler.filter(event_model) logger.debug("Handler {} returned {}".format(handler_basement.handler, result)) if result is False: return elif isinstance(result, dict): context_variables.update(result) handler_response = await handler_basement.handler.handle( event_model, **context_variables) return_handler = self.handler_return_manager.get_handler( handler_response) if return_handler is not None: await return_handler( self.handler_return_manager, handler_response, event_model, context_variables, ) for middleware in self.middlewares: await middleware.post(event_model, self, [handler_response], [handler_basement.handler])
async def post_middleware( self, mw_instances: List[BaseMiddleware], handle_responses: Optional[List] = None, handlers: Optional[List["ABCHandler"]] = None, ): for middleware in mw_instances: # Update or leave value middleware.handle_responses = handle_responses or middleware.handle_responses middleware.handlers = handlers or middleware.handlers await middleware.post() if not middleware.can_forward: logger.debug("{} post returned error {}", middleware, middleware.error) return middleware.error