From 75d1683b38e65855eb769f38cb06ca31cefaa606 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Sun, 3 Oct 2021 18:02:30 +0200 Subject: [PATCH] remember the sync token to avoid reloading the whole history --- .gitignore | 3 +++ requirements.txt | 1 + setup.cfg | 3 ++- src/matrix_bot/client.py | 46 ++++++++++++++++++++++++++++++++++------ 4 files changed, 45 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 29dba5a..e4a7426 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,6 @@ dmypy.json # Cython debug symbols cython_debug/ + +# Project specific files: +.sync_token diff --git a/requirements.txt b/requirements.txt index 468ba73..111d322 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ matrix-nio==0.18.7 +aiopath==0.5.12 diff --git a/setup.cfg b/setup.cfg index a52660f..2b02687 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,7 +11,8 @@ packages = matrix_bot python_requires = >=3.9.2 package_dir = =src install_requires = - matrix-nio>=0.18.7 + matrix-nio>=0.18.7 + aiopath>=0.5.12 [options.extras_require] testing = diff --git a/src/matrix_bot/client.py b/src/matrix_bot/client.py index 693802b..ae7bc77 100644 --- a/src/matrix_bot/client.py +++ b/src/matrix_bot/client.py @@ -5,6 +5,7 @@ Connect to the matrix server and handle interactions with the server. import asyncio import nio +from aiopath import AsyncPath from typing import ( Optional, NoReturn, @@ -32,6 +33,9 @@ class Client(Aobject): __client: nio.AsyncClient __rooms_by_aliases: dict[RoomAlias, Room] __rooms_by_id: dict[RoomId, Room] + __sync_token_file: AsyncPath + __sync_token:Optional[str] + __sync_token_queue:asyncio.Queue[str] allowed_rooms: Optional[dict[RoomId, Room]] async def __init__( @@ -39,7 +43,8 @@ class Client(Aobject): username: str, homeserver: str, password: str, - allowed_rooms_names: Optional[list[Union[RoomAlias, RoomId]]]=None + allowed_rooms_names: Optional[list[Union[RoomAlias, RoomId]]]=None, + sync_token_file:str="./.sync_token", ): """ Initialize the Client. @@ -49,6 +54,9 @@ class Client(Aobject): allowed_rooms: the list of the rooms where the bot is allowed to connect (given by room id (expl: '!xxx:matrix.org') of room alias (expl: '#xxx:matrix.org')) + sync_token_file: the file where is stored the last sync token received from + the sync responses. This token avoid reloadind all the history of the + bot each time we start it. """ self.__client = nio.AsyncClient( homeserver, @@ -56,6 +64,13 @@ class Client(Aobject): ) self.__rooms_by_aliases = {} self.__rooms_by_id = {} + self.__sync_token_file = AsyncPath(sync_token_file) + if (await self.__sync_token_file.is_file()): + async with self.__sync_token_file.open() as file: + self.__sync_token = (await file.read()).strip() + else: + self.__sync_token = None + self.__sync_token_queue = asyncio.Queue() resp = await self.__client.login(password) if isinstance(resp, nio.responses.LoginError): raise RuntimeError(f"Fail to connect: {resp.message}") @@ -109,29 +124,46 @@ class Client(Aobject): self.__rooms_by_aliases[room_name] = room return room - async def sync( + async def __save_sync_tocken(self)->NoReturn: + """ + Save the sync token. + """ + while True: + token = await self.__sync_token_queue.get() + async with self.__sync_token_file.open(mode='w') as file: + await file.write(token) + self.__sync_token_queue.task_done() + + async def __sync( self, - sync_delta:int=30 + sync_timeout:int=30 )->NoReturn: """ Sync with the server every sync_delta seconds. - sync_delta: the time in sec between each sync. + sync_timeout: the max time in sec between each sync. """ while True: - sync_resp = await self.__client.sync(sync_delta*1000) + sync_resp = await self.__client.sync( + sync_delta*1000, + since=self.__sync_token + ) if isinstance(sync_resp, nio.responses.SyncError): print(f"Error while syncronizing: {sync_resp.message}") # TODO: use proper logging continue + self.__sync_token = sync_resp.next_batch + await self.__sync_token_queue.put(self.__sync_token) async def run( self, - sync_delta:int=30 + sync_timeout:int=30 )->NoReturn: """ Run the bot: sync with the server and execute callbacks. + sync_timeout: the max time in sec between each sync. """ await asyncio.gather( - self.sync(sync_delta=sync_delta) + self.__sync(sync_delta=sync_delta), + self.__save_sync_tocken() )