def __init__(self, settings, logger=None, handler=None, loop=asyncio.get_event_loop()): self.logger = None self.init_logger(logger) self.logger.info("Initializing bot") self.loop = loop self.values = {} self.server = "" self.longpoll_request = None self.main_task = None self.settings = settings self.logger.info("Initializing vk clients") self.api = VkController(settings, logger=self.logger) self.logger.info("Loading plugins") if handler: self.handler = handler else: self.handler = MessageHandler(self, self.api, initiate_plugins=False) self.handler.initiate_plugins() signal.signal(signal.SIGINT, lambda x, y: self.stop_bot(True)) self.logger.info("Bot succesfully initialized")
class Bot: __slots__ = ("logger", "loop", "values", "server", "main_task", "longpoll_request", "settings", "api", "handler", "logger_file") def __init__(self, settings, logger=None, handler=None, loop=asyncio.get_event_loop()): self.logger = None self.init_logger(logger) self.logger.info("Initializing bot") self.loop = loop self.values = {} self.server = "" self.longpoll_request = None self.main_task = None self.settings = settings self.logger.info("Initializing vk clients") self.api = VkController(settings, logger=self.logger) self.logger.info("Loading plugins") if handler: self.handler = handler else: self.handler = MessageHandler(self, self.api, initiate_plugins=False) self.handler.initiate_plugins() signal.signal(signal.SIGINT, lambda x, y: self.stop_bot(True)) self.logger.info("Bot succesfully initialized") def init_logger(self, logger): if not logger: logger = logging.Logger("sketal", level=logging.INFO) formatter = logging.Formatter( fmt=u'%(filename)-10s [%(asctime)s] %(levelname)-8s: %(message)s', datefmt='%y.%m.%d %H:%M:%S') file_handler = logging.FileHandler('logs.txt') file_handler.setLevel(logging.DEBUG) file_handler.setFormatter(formatter) self.logger_file = file_handler stream_handler = logging.StreamHandler() stream_handler.setLevel(logging.INFO) stream_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.addHandler(stream_handler) self.logger = logger async def init_long_polling(self, update=0): result = None retries = 10 for x in range(retries): result = await self.api(sender=self.api.target_client ).messages.getLongPollServer(use_ssl=1, lp_version=2) if result: break time.sleep(0.5) if not result: self.logger.error("Unable to connect to VK's long polling server") exit() last_ts = 0 longpoll_key = "" if 'ts' in self.values: last_ts = self.values['ts'] if 'key' in self.values: longpoll_key = self.values['key'] if update == 0: self.server = "https://" + result['server'] longpoll_key = result['key'] last_ts = result['ts'] elif update == 3: longpoll_key = result['key'] last_ts = result['ts'] elif update == 2: longpoll_key = result['key'] self.values = { 'act': 'a_check', 'key': longpoll_key, 'ts': last_ts, 'wait': 20, 'mode': 10, 'version': 2 } async def process_longpoll_event(self, new_event): if not new_event: return event_id = new_event[0] if event_id != 4: evnt = LongpollEvent(self.api, event_id, new_event) return await self.process_event(evnt) data = MessageEventData() data.msg_id = new_event[1] data.attaches = new_event[6] data.time = int(new_event[4]) try: data.user_id = int(data.attaches['from']) data.chat_id = int(new_event[3]) - 2000000000 data.is_multichat = True del data.attaches['from'] except KeyError: data.user_id = int(new_event[3]) data.is_multichat = False # https://vk.com/dev/using_longpoll_2 flags = parse_msg_flags(new_event[2]) if flags['outbox']: if not self.settings.READ_OUT: return data.is_out = True data.full_text = new_event[5].replace('<br>', '\n') if "fwd" in data.attaches: data.forwarded = MessageEventData.parse_brief_forwarded_messages_from_lp( data.attaches["fwd"]) del data.attaches["fwd"] else: data.forwarded = [] msg = Message(self.api, data) if await self.check_event(data.user_id, data.chat_id, data.attaches): msg.is_event = True await self.process_message(msg) async def longpoll_processor(self): await self.init_long_polling() session = aiohttp.ClientSession(loop=self.loop) while True: try: self.longpoll_request = session.get(self.server, params=self.values) resp = await self.longpoll_request except aiohttp.ClientOSError: session = aiohttp.ClientSession(loop=self.loop) except (asyncio.TimeoutError, aiohttp.ServerDisconnectedError): self.logger.warning( "Long polling server doesn't respond. Changing server") await self.init_long_polling() continue try: events = json.loads(await resp.text()) except ValueError: continue failed = events.get('failed') if failed: err_num = int(failed) if err_num == 1: # 1 - update timestamp self.values['ts'] = events['ts'] elif err_num in (2, 3): # 2, 3 - new data for long polling await self.init_long_polling(err_num) continue self.values['ts'] = events['ts'] for event in events['updates']: asyncio.ensure_future(self.process_longpoll_event(event)) async def callback_processor(self, request): try: data = await request.json() except (UnicodeDecodeError, json.decoder.JSONDecodeError): return web.Response(text="ok") data_type = data["type"] if data_type == "confirmation": return web.Response(text=self.settings.CONF_CODE) obj = data["object"] if "user_id" in obj: obj['user_id'] = int(obj['user_id']) if data_type == 'message_new': data = MessageEventData.from_message_body(obj) msg = Message(self.api, data) await self.process_message(msg) else: evnt = CallbackEvent(self.api, data_type, obj) await self.process_event(evnt) return web.Response(text="ok") def longpoll_run(self, custom_process=False): self.main_task = Task(self.longpoll_processor()) if custom_process: return self.main_task self.logger.info("Started to process messages") try: self.loop.run_until_complete(self.main_task) except (KeyboardInterrupt, SystemExit): self.stop() self.logger.info("Stopped to process messages") except asyncio.CancelledError: pass def callback_run(self, custom_process=False): host = getenv('IP', '0.0.0.0') port = int(getenv('PORT', 8000)) self.logger.info("Started to process messages") try: server_generator, handler, app = self.loop.run_until_complete( self.init_app(host, port, self.loop)) server = self.loop.run_until_complete(server_generator) except OSError: self.logger.error("Address already in use: " + str(host) + ":" + str(port)) return self.main_task = Future() if custom_process: return self.main_task print("======== Running on http://{}:{} ========\n" " (Press CTRL+C to quit)".format( *server.sockets[0].getsockname())) def stop_server(): server.close() if not self.loop.is_running(): return self.loop.run_until_complete(server.wait_closed()) self.loop.run_until_complete(app.shutdown()) self.loop.run_until_complete(handler.shutdown(10)) self.loop.run_until_complete(app.cleanup()) try: self.loop.run_until_complete(self.main_task) except KeyboardInterrupt: self.stop() stop_server() self.loop.close() except asyncio.CancelledError: pass finally: stop_server() self.logger.info("Stopped to process messages") async def init_app(self, host, port, loop): app = web.Application() app.router.add_post('/', self.callback_processor) handler = app.make_handler() server_generator = loop.create_server(handler, host, port) return server_generator, handler, app def stop_bot(self, full=False): try: self.main_task.cancel() except: pass if full: self.stop() self.loop.stop() self.logger.info("Attempting to turn bot off") async def process_message(self, msg): asyncio.ensure_future(self.handler.process(msg), loop=self.loop) async def check_event(self, user_id, chat_id, attaches): if chat_id != 0 and "source_act" in attaches: photo = attaches.get("attach1_type") + attaches.get( "attach1") if "attach1" in attaches else None evnt = ChatChangeEvent(self.api, user_id, chat_id, attaches.get("source_act"), int(attaches.get("source_mid", 0)), attaches.get("source_text"), attaches.get("source_old_text"), photo, int(attaches.get("from", 0))) await self.process_event(evnt) return True return False async def process_event(self, evnt): asyncio.ensure_future(self.handler.process_event(evnt), loop=self.loop) def do(self, coroutine): if asyncio.iscoroutine(coroutine): return self.loop.run_until_complete(coroutine) return False @staticmethod def silent(func): try: func() except: pass def stop(self): self.handler.stop() self.api.stop() self.silent(self.main_task.cancel) self.logger.removeHandler(self.logger_file) self.logger_file.close()