surrender to the async devil and use blackmagic to initialize asyncronously an object

This commit is contained in:
histausse 2021-10-03 16:36:36 +02:00
parent ee81cf93af
commit e368d661ac
Signed by: histausse
GPG key ID: 67486F107F62E9E9
3 changed files with 62 additions and 9 deletions

View file

@ -0,0 +1,17 @@
"""
Some utilities to help with async stuff.
"""
class Aobject(object):
"""
Inheriting this class allows you to define an async __init__.
So you can create objects by doing something like `await MyClass(params)`
Copied from https://newbedev.com/how-to-set-class-attribute-with-await-in-init
"""
async def __new__(cls, *a, **kw):
instance = super().__new__(cls)
await instance.__init__(*a, **kw)
return instance
async def __init__(self):
pass

View file

@ -10,13 +10,14 @@ from typing import (
NoReturn, NoReturn,
Union Union
) )
from .async_utils import Aobject
from .utils import ( from .utils import (
Room, Room,
RoomAlias, RoomAlias,
RoomId RoomId
) )
class Client: class Client(Aobject):
""" """
Connect to the matrix server and handle interactions with the Connect to the matrix server and handle interactions with the
server. server.
@ -24,6 +25,8 @@ class Client:
allowed_rooms: dict of the rooms where the bot is allowed to connect, indexed allowed_rooms: dict of the rooms where the bot is allowed to connect, indexed
by id (the name starting with '!'). If set to None, the bot connect to by id (the name starting with '!'). If set to None, the bot connect to
all room where it is invited. all room where it is invited.
/!\ The client is initialized asyncronously: `client = await Client(...)`
""" """
__client: nio.AsyncClient __client: nio.AsyncClient
@ -31,7 +34,7 @@ class Client:
__rooms_by_id: dict[RoomId, Room] __rooms_by_id: dict[RoomId, Room]
allowed_rooms: Optional[dict[RoomId, Room]] allowed_rooms: Optional[dict[RoomId, Room]]
def __init__( async def __init__(
self, self,
username: str, username: str,
homeserver: str, homeserver: str,
@ -47,23 +50,22 @@ class Client:
(given by room id (expl: '!xxx:matrix.org') of room alias (expl: (given by room id (expl: '!xxx:matrix.org') of room alias (expl:
'#xxx:matrix.org')) '#xxx:matrix.org'))
""" """
loop = asyncio.get_event_loop()
self.__client = nio.AsyncClient( self.__client = nio.AsyncClient(
homeserver, homeserver,
username username
) )
self.__rooms_by_aliases = {} self.__rooms_by_aliases = {}
self.__rooms_by_id = {} self.__rooms_by_id = {}
resp = loop.run_until_complete(self.__client.login(password)) resp = await self.__client.login(password)
if isinstance(resp, nio.responses.LoginError): if isinstance(resp, nio.responses.LoginError):
raise RuntimeError(f"Fail to connect: {resp.message}") raise RuntimeError(f"Fail to connect: {resp.message}")
# Where is the async map when you need it? # TODO: Where is the async map when you need it?
self.allowed_rooms = None self.allowed_rooms = None
if allowed_rooms_names: if allowed_rooms_names:
self.allowed_rooms = {} self.allowed_rooms = {}
for room_name in allowed_rooms_names: for room_name in allowed_rooms_names:
room = loop.run_until_complete(self.resolve_room(room_name)) room = await self.resolve_room(room_name)
self.allowed_rooms[room.id] = room # room uniqueness is handled by self.resolve_room self.allowed_rooms[room.id] = room # room uniqueness is handled by self.resolve_room
@ -107,3 +109,30 @@ class Client:
self.__rooms_by_aliases[room_name] = room self.__rooms_by_aliases[room_name] = room
return room return room
async def sync(
self,
sync_delta:int=30
)->NoReturn:
"""
Sync with the server every sync_delta seconds.
sync_delta: the time in sec between each sync.
"""
while True:
sync_resp = await self.__client.sync(sync_delta*1000)
if isinstance(sync_resp, nio.responses.SyncError):
print(f"Error while syncronizing: {sync_resp.message}") # TODO: use proper logging
continue
print(sync_resp)
async def run(
self,
sync_delta:int=30
)->NoReturn:
"""
Run the bot: sync with the server and execute callbacks.
"""
await asyncio.gather(
self.sync(sync_delta=sync_delta)
)

View file

@ -2,18 +2,25 @@
Not really tests, because I don't know how to testt without a whole matrix server. Not really tests, because I don't know how to testt without a whole matrix server.
""" """
import asyncio
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
from matrix_bot.client import Client from matrix_bot.client import Client
from getpass import getpass from getpass import getpass
if __name__ == "__main__": async def main():
load_dotenv() load_dotenv()
client = Client( client = await Client(
os.environ["MUSER"], os.environ["MUSER"],
os.environ["HOMESERVER"], os.environ["HOMESERVER"],
os.environ["PASSWD"], os.environ["PASSWD"],
os.environ["ROOMS"].split(",") os.environ["ROOMS"].split(",")
) )
print(client.allowed_rooms) # print(client.allowed_rooms)
await client.run()
if __name__ == "__main__":
asyncio.run(main())