| 
					
				 | 
			
			
				@@ -0,0 +1,690 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+"""Provide a simple Bot object, mirroring Telegram API methods. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+camelCase methods mirror API directly, while snake_case ones act as middlewares 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    someway. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Standard library modules 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import asyncio 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import logging 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Third party modules 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import aiohttp 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from aiohttp import web 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Project modules 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from utilities import get_secure_key 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+# Do not log aiohttp `INFO` and `DEBUG` levels 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+logging.getLogger('aiohttp').setLevel(logging.WARNING) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TelegramError(Exception): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Telegram API exceptions class.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, error_code=0, description=None, ok=False): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get an error response and return corresponding Exception.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._code = error_code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if description is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._description = 'Generic error' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._description = description 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        super().__init__(self.description) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def code(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Telegram error code.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._code 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def description(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Human-readable description of error.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return f"Error {self.code}: {self._description}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TelegramBot(object): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Provide python method having the same signature as Telegram API methods. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    All mirrored methods are camelCase. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    loop = asyncio.get_event_loop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    app = web.Application(loop=loop) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    sessions_timeouts = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        'getUpdates': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            timeout=35, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            close=False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        'sendMessage': dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            timeout=20, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            close=False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, token): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Set bot token and store HTTP sessions.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._token = token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.sessions = dict() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def token(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Telegram API bot token.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @staticmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def check_telegram_api_json(response): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Take a json Telegram response, check it and return its content. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Example of well-formed json Telegram responses: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "ok": False, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "error_code": 401, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "description": "Unauthorized" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "ok": True, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "result": ... 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert 'ok' in response, ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            "All Telegram API responses have an `ok` field." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not response['ok']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            raise TelegramError(**response) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return response['result'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @staticmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def clean_parameters(parameters, exclude=[]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Remove key-value couples from `parameters` dict. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Remove the couple having `self` as key, keys in `exclude` list, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            and couples with empty value. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Adapt files parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        exclude.append('self') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data = aiohttp.FormData() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for key, value in parameters.items(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (key in exclude or value is None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif type(value) is int: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data.add_field(key, str(value)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif key in ['photo', 'audio', 'document']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data.add_field(key, value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data.add_field(key, value) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def get_session(self, api_method): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """According to API method, return proper session and information. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Return a tuple (session, session_must_be_closed) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        session : aiohttp.ClientSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Client session with proper timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        session_must_be_closed : bool 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            True if session must be closed after being used once 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls = self.__class__ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if api_method in cls.sessions_timeouts: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if api_method not in self.sessions: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self.sessions[api_method] = aiohttp.ClientSession( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    loop=cls.loop, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    timeout=aiohttp.ClientTimeout( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        total=cls.sessions_timeouts[api_method]['timeout'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            session = self.sessions[api_method] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            session_must_be_closed = cls.sessions_timeouts[api_method]['close'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            session = aiohttp.ClientSession( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                loop=cls.loop, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                timeout=aiohttp.ClientTimeout(total=None) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            session_must_be_closed = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return session, session_must_be_closed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def api_request(self, method, parameters={}, exclude=[]): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return the result of a Telegram bot API request, or an Exception. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Opened sessions will be used more than one time (if appropriate) and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            will be closed on `Bot.app.cleanup`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Result may be a Telegram API json response, None, or Exception. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        response_object = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        session, session_must_be_closed = self.get_session(method) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        parameters = self.clean_parameters(parameters, exclude=exclude) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            async with session.post( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "https://api.telegram.org/bot" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"{self.token}/{method}", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data=parameters 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) as response: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    response_object = self.check_telegram_api_json( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        await response.json()  # Telegram returns json objects 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                except TelegramError as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logging.error(f"{e}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return e 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                except Exception as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    logging.error(f"{e}", exc_info=True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    return e 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except asyncio.TimeoutError as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.info(f"{e}: {method} API call timed out") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        finally: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if session_must_be_closed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await session.close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return response_object 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def getMe(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get basic information about the bot in form of a User object. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Useful to test `self.token`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#getme for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'getMe', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def getUpdates(self, offset, timeout, limit, allowed_updates): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get a list of updates starting from `offset`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        If there are no updates, keep the request hanging until `timeout`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        If there are more than `limit` updates, retrieve them in packs of 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            `limit`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Allowed update types (empty list to allow all). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#getupdates for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            method='getUpdates', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def setWebhook(self, url=None, certificate=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         max_connections=None, allowed_updates=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Set or remove a webhook. Telegram will post to `url` new updates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#setwebhook for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if url is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            url = self.webhook_url 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if allowed_updates is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            allowed_updates = self.allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if max_connections is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            max_connections = self.max_connections 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if certificate is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            certificate = self.certificate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if type(certificate) is str: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                certificate = open(certificate, 'r') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            except FileNotFoundError as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.error(f"{e}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                certificate = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        certificate = dict( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            file=certificate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'setWebhook', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def deleteWebhook(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Remove webhook integration and switch back to getUpdate. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#deletewebhook for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'deleteWebhook', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def getWebhookInfo(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get current webhook status. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#getwebhookinfo for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'getWebhookInfo', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendMessage(self, chat_id, text, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          disable_web_page_preview=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send a text message. On success, return it. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#sendmessage for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'sendMessage', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def forwardMessage(self, chat_id, from_chat_id, message_id, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                             disable_notification=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Forward a message. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#forwardmessage for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'forwardMessage', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendPhoto(self, chat_id, photo, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        caption=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send a photo from file_id, HTTP url or file. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#sendphoto for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'sendPhoto', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendAudio(self, chat_id, audio, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        caption=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        duration=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        performer=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        title=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        thumb=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send an audio file from file_id, HTTP url or file. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#sendaudio for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'sendAudio', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendDocument(self, chat_id, document, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           thumb=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           caption=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send a document from file_id, HTTP url or file. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#senddocument for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'sendDocument', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendVideo(self, chat_id, video, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        duration=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        width=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        height=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        thumb=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        caption=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        supports_streaming=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send a video from file_id, HTTP url or file. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#sendvideo for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'sendVideo', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def sendAnimation(self, chat_id, animation, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            duration=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            width=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            height=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            thumb=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            caption=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            parse_mode=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            disable_notification=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            reply_to_message_id=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            reply_markup=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Send animation files (GIF or H.264/MPEG-4 AVC video without sound). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#sendanimation for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'method_name', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def method_name( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self, chat_id, reply_to_message_id=None, reply_markup=None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """method_name. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        See https://core.telegram.org/bots/api#method_name for details. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return await self.api_request( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            'method_name', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            parameters=locals() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class Bot(TelegramBot): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """Simple Bot object, providing methods corresponding to Telegram bot API. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    Multiple Bot() instances may be run together, along with a aiohttp web app. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    bots = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    runner = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    local_host = 'localhost' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    port = 3000 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    final_state = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self, token, hostname='', certificate=None, max_connections=40, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        allowed_updates=[] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Init a bot instance. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        token : str 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Telegram bot API token. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        hostname : str 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Domain (or public IP address) for webhooks. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        certificate : str 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Path to domain certificate. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        max_connections : int (1 - 100) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Maximum number of HTTPS connections allowed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        allowed_updates : List(str) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Allowed update types (empty list to allow all). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.__class__.bots.append(self) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        super().__init__(token) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._offset = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._hostname = hostname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._certificate = certificate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._max_connections = max_connections 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._allowed_updates = allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._session_token = get_secure_key(length=10) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._name = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._telegram_id = None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def hostname(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Hostname for the webhook URL. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        It must be a public domain or IP address. Port may be specified. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        A custom webhook url, including bot token and a random token, will be 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        generated for Telegram to post new updates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._hostname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def webhook_url(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """URL where Telegram servers should post new updates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        It must be a public domain name or IP address. Port may be specified. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not self.hostname: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            f"{self.hostname}/webhook/{self.token}_{self.session_token}/" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def webhook_local_address(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Local address where Telegram updates are routed by revers proxy.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            f"/webhook/{self.token}_{self.session_token}/" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def certificate(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Public certificate for `webhook_url`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        May be self-signed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._certificate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def max_connections(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Maximum number of simultaneous HTTPS connections allowed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Telegram will open as many connections as possible to boost bot’s 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            throughput, lower values limit the load on bot‘s server. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._max_connections 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def allowed_updates(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """List of update types to be retrieved. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Empty list to allow all updates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def name(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Bot name.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._name 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def telegram_id(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Telegram id of this bot.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._telegram_id 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def session_token(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return a token generated with the current instantiation.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._session_token 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @property 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def offset(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Return last update id. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Useful to ignore repeated updates and restore original update order. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return self._offset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def webhook_feeder(self, request): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Handle incoming HTTP `request`s. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Get data, feed webhook and return and OK message. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update = await request.json() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        asyncio.ensure_future( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.route_update(update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return web.Response( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            body='OK'.encode('utf-8') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def get_me(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get bot information. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Restart bots if bot can't be got. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            me = await self.getMe() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if isinstance(me, Exception): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                raise me 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif me is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                raise Exception('getMe returned None') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._name = me["username"] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self._telegram_id = me['id'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except Exception as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.error( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Information about bot with token {self.token} could not " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"be got. Restarting in 5 minutes...\n\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Error information:\n{e}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            await asyncio.sleep(5*60) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.__class__.stop( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                65, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Information about bot with token {self.token} could not " 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                "be got. Restarting..." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def setup(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Make bot ask for updates and handle responses.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if not self.webhook_url: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            asyncio.ensure_future(self.get_updates()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            asyncio.ensure_future(self.set_webhook()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            self.__class__.app.router.add_route( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                'POST', self.webhook_local_address, self.webhook_feeder 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def close_sessions(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Close open sessions.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for session_name, session in self.sessions.items(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            await session.close() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def set_webhook(self, url=None, certificate=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          max_connections=None, allowed_updates=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Set a webhook if token is valid.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Return if token is invalid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await self.get_me() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self.name is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        webhook_was_set = await self.setWebhook( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            url=url, certificate=certificate, max_connections=max_connections, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            allowed_updates=allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        )  # `setWebhook` API method returns `True` on success 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        webhook_information = await self.getWebhookInfo() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if webhook_was_set: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.info( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Webhook was set correctly.\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Webhook information: {webhook_information}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.error( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Failed to set webhook!\n" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                f"Webhook information: {webhook_information}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def get_updates(self, timeout=30, limit=100, allowed_updates=None, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          error_cooldown=10): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Get updates using long polling. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        timeout : int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Timeout set for Telegram servers. Make sure that connection timeout 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            is greater than `timeout`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        limit : int (1 - 100) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Max number of updates to be retrieved. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        allowed_updates : List(str) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            List of update types to be retrieved. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            Empty list to allow all updates. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            None to fallback to class default. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Return if token is invalid 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await self.get_me() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if self.name is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        # Set custom list of allowed updates or fallback to class default list 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if allowed_updates is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            allowed_updates = self.allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await self.deleteWebhook()  # Remove eventually active webhook 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        update = None  # Do not update offset if no update is received 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        while True: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            updates = await self.getUpdates( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                offset=self._offset, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                timeout=timeout, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                limit=limit, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                allowed_updates=allowed_updates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if updates is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            elif isinstance(updates, TelegramError): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.error( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    f"Waiting {error_cooldown} seconds before trying again..." 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                await asyncio.sleep(error_cooldown) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for update in updates: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                asyncio.ensure_future(self.route_update(update)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if update is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                self._offset = update['update_id'] + 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def route_update(self, update): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Pass `update` to proper method. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Work in progress: at the moment the update gets simply printed. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        print(update) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await self.sendMessage( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            chat_id=update['message']['chat']['id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            text="Ciaone!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with open('rrr.txt', 'r') as _file: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            await self.sendDocument( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                chat_id=update['message']['chat']['id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                document=_file, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                caption="Prova!" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def start_app(cls): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Start running `aiohttp.web.Application`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        It will route webhook-received updates and other custom paths. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert cls.local_host is not None, "Invalid local host" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        assert cls.port is not None, "Invalid port" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.runner = web.AppRunner(cls.app) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await cls.runner.setup() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.server = web.TCPSite(cls.runner, cls.local_host, cls.port) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await cls.server.start() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logging.info(f"App running at http://{cls.local_host}:{cls.port}") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    async def stop_app(cls): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Close bot sessions and cleanup.""" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for bot in cls.bots: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            await bot.close_sessions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        await cls.runner.cleanup() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def stop(cls, message, final_state=0): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Log a final `message`, stop loop and set exiting `code`. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        All bots and the web app will be terminated gracefully. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        The final state may be retrieved to get information about what stopped 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            the bots. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logging.info(message) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.final_state = final_state 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        cls.loop.stop() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @classmethod 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def run(cls, local_host=None, port=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """Run aiohttp web app and all Bot instances. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        Each bot will receive updates via long polling or webhook according to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            its initialization parameters. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        A single aiohttp.web.Application instance will be run (cls.app) on 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            local_host:port and it may serve custom-defined routes as well. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        """ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if local_host is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cls.local_host = local_host 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if port is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cls.port = port 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for bot in cls.bots: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            bot.setup() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        asyncio.ensure_future(cls.start_app()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cls.loop.run_forever() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except KeyboardInterrupt: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.info("Stopped by KeyboardInterrupt") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except Exception as e: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.error(f"{e}", exc_info=True) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        finally: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            cls.loop.run_until_complete(cls.stop_app()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return cls.final_state 
			 |