| 7 | import network, socket, ssl, time, uasyncio as asyncio, json |
| 8 | |
| 9 | class TelegramBot: |
| 10 | def __init__(self,token,callback): |
| 11 | self.token = token |
| 12 | self.callback = callback |
| 13 | self.rbuf = bytearray(4096) |
| 14 | self.rbuf_mv = memoryview(self.rbuf) |
| 15 | self.rbuf_used = 0 |
| 16 | self.active = True # So we can stop the task with .stop() |
| 17 | self.debug = False |
| 18 | self.missed_write = None # Failed write payload. This is useful |
| 19 | # in order to retransfer after reconnection. |
| 20 | |
| 21 | # Array of outgoing messages. Each entry is a hash with |
| 22 | # chat_id and text fields. |
| 23 | self.outgoing = [] |
| 24 | self.pending = False # Pending HTTP request, waiting for reply. |
| 25 | self.reconnect = True # We need to reconnect the socket, either for |
| 26 | # the first time or after errors. |
| 27 | self.offset = 0 # Next message ID offset. |
| 28 | self.watchdog_timeout_ms = 60000 # 60 seconds max idle time. |
| 29 | |
| 30 | # Stop the task handling the bot. This should be called before |
| 31 | # destroying the object, in order to also terminate the task. |
| 32 | def stop(self): |
| 33 | self.active = False |
| 34 | |
| 35 | # Main telegram bot loop. |
| 36 | # Sould be executed asynchronously, like with: |
| 37 | # asyncio.create_task(bot.run()) |
| 38 | async def run(self): |
| 39 | while self.active: |
| 40 | if self.reconnect: |
| 41 | if self.debug: print("[telegram] Reconnecting socket.") |
| 42 | # Reconnection (or first connection) |
| 43 | try: |
| 44 | addr = socket.getaddrinfo("api.telegram.org", 443, socket.AF_INET) |
| 45 | addr = addr[0][-1] |
| 46 | self.socket = socket.socket(socket.AF_INET) |
| 47 | self.socket.connect(addr) |
| 48 | self.socket.setblocking(False) |
| 49 | self.ssl = ssl.wrap_socket(self.socket) |
| 50 | self.reconnect = False |
| 51 | self.pending = False |
| 52 | except: |
| 53 | self.reconnect = True |
| 54 | |
| 55 | self.send_api_requests() |
| 56 | self.read_api_response() |
| 57 | |
| 58 | # Watchdog: if the connection is idle for a too long |
| 59 | # time, force a reconnection. |
| 60 | if self.pending and time.ticks_diff(time.ticks_ms(),self.pending_since) > self.watchdog_timeout_ms: |
| 61 | self.reconnect = True |
| 62 | print("[telegram] *** SOCKET WATCHDOG EXPIRED ***") |
| 63 | |
| 64 | # If there are outgoing messages pending, wait less |
| 65 | # to do I/O again. |
| 66 | sleep_time = 0.1 if len(self.outgoing) > 0 else 1.0 |