Document matrix_client.py
This commit is contained in:
parent
05a331382e
commit
c6938903b8
2
TODO.md
2
TODO.md
|
@ -47,6 +47,8 @@
|
||||||
|
|
||||||
## Issues
|
## Issues
|
||||||
|
|
||||||
|
- last event obj
|
||||||
|
- load_past raise
|
||||||
- Room pane slightly overlaps chat at small width
|
- Room pane slightly overlaps chat at small width
|
||||||
- invisible uploaded mxc images?
|
- invisible uploaded mxc images?
|
||||||
- first undecryptable message
|
- first undecryptable message
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
"""Matrix client and related classes."""
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import html
|
import html
|
||||||
import io
|
import io
|
||||||
|
@ -41,23 +43,40 @@ CryptDict = Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
class UploadReturn(NamedTuple):
|
class UploadReturn(NamedTuple):
|
||||||
|
"""Details for an uploaded file."""
|
||||||
|
|
||||||
mxc: str
|
mxc: str
|
||||||
mime: str
|
mime: str
|
||||||
decryption_dict: Dict[str, Any]
|
decryption_dict: Dict[str, Any]
|
||||||
|
|
||||||
|
|
||||||
class MatrixImageInfo(NamedTuple):
|
class MatrixImageInfo(NamedTuple):
|
||||||
w: int
|
"""Image informations to be passed for Matrix file events."""
|
||||||
h: int
|
|
||||||
mimetype: str
|
width: int
|
||||||
size: int
|
height: int
|
||||||
|
mime: str
|
||||||
|
size: int
|
||||||
|
|
||||||
|
def as_dict(self) -> Dict[str, Union[int, str]]:
|
||||||
|
"""Return a dict ready to be included in a Matrix file events."""
|
||||||
|
|
||||||
|
return {
|
||||||
|
"w": self.width,
|
||||||
|
"h": self.height,
|
||||||
|
"mimetype": self.mime,
|
||||||
|
"size": self.size,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MatrixClient(nio.AsyncClient):
|
class MatrixClient(nio.AsyncClient):
|
||||||
|
"""A client for an account to interact with a matrix homeserver."""
|
||||||
|
|
||||||
user_id_regex = re.compile(r"^@.+:.+")
|
user_id_regex = re.compile(r"^@.+:.+")
|
||||||
room_id_or_alias_regex = re.compile(r"^[#!].+:.+")
|
room_id_or_alias_regex = re.compile(r"^[#!].+:.+")
|
||||||
http_s_url = re.compile(r"^https?://")
|
http_s_url = re.compile(r"^https?://")
|
||||||
|
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
backend,
|
backend,
|
||||||
user: str,
|
user: str,
|
||||||
|
@ -112,12 +131,16 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def default_device_name(self) -> str:
|
def default_device_name(self) -> str:
|
||||||
|
"""Device name to set at login if the user hasn't set a custom one."""
|
||||||
|
|
||||||
os_ = f" on {platform.system()}".rstrip()
|
os_ = f" on {platform.system()}".rstrip()
|
||||||
os_ = f"{os_} {platform.release()}".rstrip() if os_ != " on" else ""
|
os_ = f"{os_} {platform.release()}".rstrip() if os_ != " on" else ""
|
||||||
return f"{__display_name__}{os_}"
|
return f"{__display_name__}{os_}"
|
||||||
|
|
||||||
|
|
||||||
async def login(self, password: str, device_name: str = "") -> None:
|
async def login(self, password: str, device_name: str = "") -> None:
|
||||||
|
"""Login to the server using the account's password."""
|
||||||
|
|
||||||
response = await super().login(
|
response = await super().login(
|
||||||
password, device_name or self.default_device_name,
|
password, device_name or self.default_device_name,
|
||||||
)
|
)
|
||||||
|
@ -125,17 +148,21 @@ class MatrixClient(nio.AsyncClient):
|
||||||
if isinstance(response, nio.LoginError):
|
if isinstance(response, nio.LoginError):
|
||||||
raise MatrixError.from_nio(response)
|
raise MatrixError.from_nio(response)
|
||||||
|
|
||||||
asyncio.ensure_future(self.start())
|
asyncio.ensure_future(self._start())
|
||||||
|
|
||||||
|
|
||||||
async def resume(self, user_id: str, token: str, device_id: str) -> None:
|
async def resume(self, user_id: str, token: str, device_id: str) -> None:
|
||||||
|
"""Login to the server using an existing access token."""
|
||||||
|
|
||||||
response = nio.LoginResponse(user_id, device_id, token)
|
response = nio.LoginResponse(user_id, device_id, token)
|
||||||
await self.receive_response(response)
|
await self.receive_response(response)
|
||||||
|
|
||||||
asyncio.ensure_future(self.start())
|
asyncio.ensure_future(self._start())
|
||||||
|
|
||||||
|
|
||||||
async def logout(self) -> None:
|
async def logout(self) -> None:
|
||||||
|
"""Logout from the server. This will delete the device."""
|
||||||
|
|
||||||
for task in (self.profile_task, self.load_rooms_task, self.sync_task):
|
for task in (self.profile_task, self.load_rooms_task, self.sync_task):
|
||||||
if task:
|
if task:
|
||||||
task.cancel()
|
task.cancel()
|
||||||
|
@ -148,14 +175,20 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def syncing(self) -> bool:
|
def syncing(self) -> bool:
|
||||||
|
"""Return whether this client is currently syncing with the server."""
|
||||||
|
|
||||||
if not self.sync_task:
|
if not self.sync_task:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return not self.sync_task.done()
|
return not self.sync_task.done()
|
||||||
|
|
||||||
|
|
||||||
async def start(self) -> None:
|
async def _start(self) -> None:
|
||||||
|
"""Fetch our user profile and enter the server sync loop."""
|
||||||
|
|
||||||
def on_profile_response(future) -> None:
|
def on_profile_response(future) -> None:
|
||||||
|
"""Update our model `Account` with the received profile details."""
|
||||||
|
|
||||||
exception = future.exception()
|
exception = future.exception()
|
||||||
|
|
||||||
if exception:
|
if exception:
|
||||||
|
@ -192,10 +225,14 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all_rooms(self) -> Dict[str, nio.MatrixRoom]:
|
def all_rooms(self) -> Dict[str, nio.MatrixRoom]:
|
||||||
|
"""Return dict containing both our joined and invited rooms."""
|
||||||
|
|
||||||
return {**self.invited_rooms, **self.rooms}
|
return {**self.invited_rooms, **self.rooms}
|
||||||
|
|
||||||
|
|
||||||
async def send_text(self, room_id: str, text: str) -> None:
|
async def send_text(self, room_id: str, text: str) -> None:
|
||||||
|
"""Send a markdown `m.text` or `m.notice` (with `/me`) message ."""
|
||||||
|
|
||||||
escape = False
|
escape = False
|
||||||
if text.startswith("//") or text.startswith(r"\/"):
|
if text.startswith("//") or text.startswith(r"\/"):
|
||||||
escape = True
|
escape = True
|
||||||
|
@ -228,6 +265,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
async def send_file(self, room_id: str, path: Union[Path, str]) -> None:
|
||||||
|
"""Send a `m.file`, `m.image`, `m.audio` or `m.video` message."""
|
||||||
|
|
||||||
item_uuid = uuid4()
|
item_uuid = uuid4()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -240,6 +279,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def _send_file(
|
async def _send_file(
|
||||||
self, item_uuid: UUID, room_id: str, path: Union[Path, str],
|
self, item_uuid: UUID, room_id: str, path: Union[Path, str],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Monitorably upload a file + thumbnail and send the built event."""
|
||||||
|
|
||||||
|
# TODO: this function is WAY TOO COMPLEX, and most of it should be
|
||||||
|
# refactored into nio.
|
||||||
|
|
||||||
from .media_cache import Media, Thumbnail
|
from .media_cache import Media, Thumbnail
|
||||||
|
|
||||||
transaction_id = uuid4()
|
transaction_id = uuid4()
|
||||||
|
@ -361,7 +405,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
else:
|
else:
|
||||||
content["info"]["thumbnail_url"] = thumb_url
|
content["info"]["thumbnail_url"] = thumb_url
|
||||||
|
|
||||||
content["info"]["thumbnail_info"] = thumb_info._asdict()
|
content["info"]["thumbnail_info"] = thumb_info.as_dict()
|
||||||
|
|
||||||
elif kind == "audio":
|
elif kind == "audio":
|
||||||
event_type = \
|
event_type = \
|
||||||
|
@ -422,9 +466,27 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def _local_echo(
|
async def _local_echo(
|
||||||
self, room_id: str, transaction_id: UUID,
|
self,
|
||||||
event_type: Type[nio.Event], **event_fields,
|
room_id: str,
|
||||||
|
transaction_id: UUID,
|
||||||
|
event_type: Type[nio.Event],
|
||||||
|
**event_fields,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Register a local model `Event` while waiting for the server.
|
||||||
|
|
||||||
|
When the user sends a message, we want to show instant feedback in
|
||||||
|
the UI timeline without waiting for the servers to receive our message
|
||||||
|
and retransmit it to us.
|
||||||
|
|
||||||
|
The event will be locally echoed for all our accounts that are members
|
||||||
|
of the `room_id` room.
|
||||||
|
This allows sending messages from other accounts within the same
|
||||||
|
composer without having to go to another page in the UI,
|
||||||
|
and getting direct feedback for these accounts in the timeline.
|
||||||
|
|
||||||
|
When we do get the real event retransmited by the server, it will
|
||||||
|
replace the local one we registered.
|
||||||
|
"""
|
||||||
|
|
||||||
our_info = self.models[Member, self.user_id, room_id][self.user_id]
|
our_info = self.models[Member, self.user_id, room_id][self.user_id]
|
||||||
|
|
||||||
|
@ -453,6 +515,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def _send_message(self, room_id: str, content: dict) -> None:
|
async def _send_message(self, room_id: str, content: dict) -> None:
|
||||||
|
"""Send a message event with `content` dict to a room."""
|
||||||
|
|
||||||
async with self.backend.send_locks[room_id]:
|
async with self.backend.send_locks[room_id]:
|
||||||
response = await self.room_send(
|
response = await self.room_send(
|
||||||
|
@ -467,6 +530,14 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def load_past_events(self, room_id: str) -> bool:
|
async def load_past_events(self, room_id: str) -> bool:
|
||||||
|
"""Ask the server for 100 previous events of the room.
|
||||||
|
|
||||||
|
Events from before the client was started will be requested and
|
||||||
|
registered into our models.
|
||||||
|
|
||||||
|
Returns whether there are any messages left to load.
|
||||||
|
"""
|
||||||
|
|
||||||
if room_id in self.fully_loaded_rooms or \
|
if room_id in self.fully_loaded_rooms or \
|
||||||
room_id in self.invited_rooms or \
|
room_id in self.invited_rooms or \
|
||||||
room_id in self.cleared_events_rooms:
|
room_id in self.cleared_events_rooms:
|
||||||
|
@ -507,6 +578,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def load_rooms_without_visible_events(self) -> None:
|
async def load_rooms_without_visible_events(self) -> None:
|
||||||
|
"""Call `_load_room_without_visible_events` for all joined rooms."""
|
||||||
|
|
||||||
for room_id in self.models[Room, self.user_id]:
|
for room_id in self.models[Room, self.user_id]:
|
||||||
asyncio.ensure_future(
|
asyncio.ensure_future(
|
||||||
self._load_room_without_visible_events(room_id),
|
self._load_room_without_visible_events(room_id),
|
||||||
|
@ -514,6 +587,20 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def _load_room_without_visible_events(self, room_id: str) -> None:
|
async def _load_room_without_visible_events(self, room_id: str) -> None:
|
||||||
|
"""Request past events for rooms without any suitable event to show.
|
||||||
|
|
||||||
|
Some events are currently not supported, or processed but not
|
||||||
|
shown in the UI timeline/room "last event" subtitle, e.g.
|
||||||
|
the "x changed their name/avatar" events.
|
||||||
|
|
||||||
|
It could happen that all the initial events received in the initial
|
||||||
|
sync for a room are such events,
|
||||||
|
and thus we'd have nothing to show in the room.
|
||||||
|
|
||||||
|
This method tries to load past events until we have at least one
|
||||||
|
to show or there is nothing left to load.
|
||||||
|
"""
|
||||||
|
|
||||||
events = self.models[Event, self.user_id, room_id]
|
events = self.models[Event, self.user_id, room_id]
|
||||||
more = True
|
more = True
|
||||||
|
|
||||||
|
@ -522,6 +609,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def new_direct_chat(self, invite: str, encrypt: bool = False) -> str:
|
async def new_direct_chat(self, invite: str, encrypt: bool = False) -> str:
|
||||||
|
"""Create a room and invite a single user in it for a direct chat."""
|
||||||
|
|
||||||
if invite == self.user_id:
|
if invite == self.user_id:
|
||||||
raise InvalidUserInContext(invite)
|
raise InvalidUserInContext(invite)
|
||||||
|
|
||||||
|
@ -553,6 +642,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
encrypt: bool = False,
|
encrypt: bool = False,
|
||||||
federate: bool = True,
|
federate: bool = True,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
"""Create a new matrix room with the purpose of being a group chat."""
|
||||||
|
|
||||||
response = await super().room_create(
|
response = await super().room_create(
|
||||||
name = name or None,
|
name = name or None,
|
||||||
|
@ -571,6 +661,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
return response.room_id
|
return response.room_id
|
||||||
|
|
||||||
async def room_join(self, alias_or_id_or_url: str) -> str:
|
async def room_join(self, alias_or_id_or_url: str) -> str:
|
||||||
|
"""Join an existing matrix room."""
|
||||||
|
|
||||||
string = alias_or_id_or_url.strip()
|
string = alias_or_id_or_url.strip()
|
||||||
|
|
||||||
if self.http_s_url.match(string):
|
if self.http_s_url.match(string):
|
||||||
|
@ -593,6 +685,12 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def room_forget(self, room_id: str) -> None:
|
async def room_forget(self, room_id: str) -> None:
|
||||||
|
"""Leave a joined room (or decline an invite) and forget its history.
|
||||||
|
|
||||||
|
If all the members of a room leave and forget it, that room
|
||||||
|
will be marked as suitable for destruction by the server.
|
||||||
|
"""
|
||||||
|
|
||||||
await super().room_leave(room_id)
|
await super().room_leave(room_id)
|
||||||
await super().room_forget(room_id)
|
await super().room_forget(room_id)
|
||||||
self.models[Room, self.user_id].pop(room_id, None)
|
self.models[Room, self.user_id].pop(room_id, None)
|
||||||
|
@ -603,6 +701,13 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def room_mass_invite(
|
async def room_mass_invite(
|
||||||
self, room_id: str, *user_ids: str,
|
self, room_id: str, *user_ids: str,
|
||||||
) -> Tuple[List[str], List[Tuple[str, Exception]]]:
|
) -> Tuple[List[str], List[Tuple[str, Exception]]]:
|
||||||
|
"""Invite users to a room in parallel.
|
||||||
|
|
||||||
|
Returns a tuple with:
|
||||||
|
|
||||||
|
- A list of users we successfully invited
|
||||||
|
- A list of `(user_id, Exception)` tuples for those failed to invite.
|
||||||
|
"""
|
||||||
|
|
||||||
user_ids = tuple(
|
user_ids = tuple(
|
||||||
uid for uid in user_ids
|
uid for uid in user_ids
|
||||||
|
@ -640,6 +745,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def generate_thumbnail(
|
async def generate_thumbnail(
|
||||||
self, data: UploadData, is_svg: bool = False,
|
self, data: UploadData, is_svg: bool = False,
|
||||||
) -> Tuple[bytes, MatrixImageInfo]:
|
) -> Tuple[bytes, MatrixImageInfo]:
|
||||||
|
"""Create a thumbnail from an image, return the bytes and info."""
|
||||||
|
|
||||||
png_modes = ("1", "L", "P", "RGBA")
|
png_modes = ("1", "L", "P", "RGBA")
|
||||||
|
|
||||||
|
@ -689,6 +795,7 @@ class MatrixClient(nio.AsyncClient):
|
||||||
encrypt: bool = False,
|
encrypt: bool = False,
|
||||||
monitor: Optional[nio.TransferMonitor] = None,
|
monitor: Optional[nio.TransferMonitor] = None,
|
||||||
) -> UploadReturn:
|
) -> UploadReturn:
|
||||||
|
"""Upload a file to the matrix homeserver."""
|
||||||
|
|
||||||
mime = mime or await utils.guess_mime(data_provider(0, 0))
|
mime = mime or await utils.guess_mime(data_provider(0, 0))
|
||||||
|
|
||||||
|
@ -707,6 +814,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def set_avatar_from_file(self, path: Union[Path, str]) -> None:
|
async def set_avatar_from_file(self, path: Union[Path, str]) -> None:
|
||||||
|
"""Upload an image to the homeserver and set it as our avatar."""
|
||||||
|
|
||||||
mime = await utils.guess_mime(path)
|
mime = await utils.guess_mime(path)
|
||||||
|
|
||||||
if mime.split("/")[0] != "image":
|
if mime.split("/")[0] != "image":
|
||||||
|
@ -717,11 +826,15 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def import_keys(self, infile: str, passphrase: str) -> None:
|
async def import_keys(self, infile: str, passphrase: str) -> None:
|
||||||
|
"""Import decryption keys from a file, then retry decrypting events."""
|
||||||
|
|
||||||
await super().import_keys(infile, passphrase)
|
await super().import_keys(infile, passphrase)
|
||||||
await self.retry_decrypting_events()
|
await self.retry_decrypting_events()
|
||||||
|
|
||||||
|
|
||||||
async def export_keys(self, outfile: str, passphrase: str) -> None:
|
async def export_keys(self, outfile: str, passphrase: str) -> None:
|
||||||
|
"""Export our decryption keys to a file."""
|
||||||
|
|
||||||
path = Path(outfile)
|
path = Path(outfile)
|
||||||
path.parent.mkdir(parents=True, exist_ok=True)
|
path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
@ -733,6 +846,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def retry_decrypting_events(self) -> None:
|
async def retry_decrypting_events(self) -> None:
|
||||||
|
"""Retry decrypting room `Event`s in our model we failed to decrypt."""
|
||||||
|
|
||||||
for sync_id, model in self.models.items():
|
for sync_id, model in self.models.items():
|
||||||
if not (isinstance(sync_id, tuple) and
|
if not (isinstance(sync_id, tuple) and
|
||||||
sync_id[0:2] == (Event, self.user_id)):
|
sync_id[0:2] == (Event, self.user_id)):
|
||||||
|
@ -759,6 +874,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def clear_events(self, room_id: str) -> None:
|
async def clear_events(self, room_id: str) -> None:
|
||||||
|
"""Remove every `Event` of a room we registred in our model.
|
||||||
|
|
||||||
|
The events will be gone from the UI, until the client is restarted.
|
||||||
|
"""
|
||||||
|
|
||||||
self.cleared_events_rooms.add(room_id)
|
self.cleared_events_rooms.add(room_id)
|
||||||
model = self.models[Event, self.user_id, room_id]
|
model = self.models[Event, self.user_id, room_id]
|
||||||
if model:
|
if model:
|
||||||
|
@ -769,6 +889,8 @@ class MatrixClient(nio.AsyncClient):
|
||||||
# Functions to register data into models
|
# Functions to register data into models
|
||||||
|
|
||||||
async def event_is_past(self, ev: Union[nio.Event, Event]) -> bool:
|
async def event_is_past(self, ev: Union[nio.Event, Event]) -> bool:
|
||||||
|
"""Return whether an event was created before this client started."""
|
||||||
|
|
||||||
if not self.first_sync_date:
|
if not self.first_sync_date:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -780,6 +902,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
|
|
||||||
|
|
||||||
async def set_room_last_event(self, room_id: str, item: Event) -> None:
|
async def set_room_last_event(self, room_id: str, item: Event) -> None:
|
||||||
|
"""Set the `last_event` for a `Room` using data in our `Event` model.
|
||||||
|
|
||||||
|
The `last_event` is notably displayed in the UI room subtitles.
|
||||||
|
"""
|
||||||
|
|
||||||
model = self.models[Room, self.user_id]
|
model = self.models[Room, self.user_id]
|
||||||
room = model[room_id]
|
room = model[room_id]
|
||||||
|
|
||||||
|
@ -813,8 +940,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
model.sync_now()
|
model.sync_now()
|
||||||
|
|
||||||
|
|
||||||
async def register_nio_room(self, room: nio.MatrixRoom, left: bool = False,
|
async def register_nio_room(
|
||||||
) -> None:
|
self, room: nio.MatrixRoom, left: bool = False,
|
||||||
|
) -> None:
|
||||||
|
"""Register a `nio.MatrixRoom` as a `Room` object in our model."""
|
||||||
|
|
||||||
# Add room
|
# Add room
|
||||||
try:
|
try:
|
||||||
last_ev = self.models[Room, self.user_id][room.room_id].last_event
|
last_ev = self.models[Room, self.user_id][room.room_id].last_event
|
||||||
|
@ -880,16 +1010,26 @@ class MatrixClient(nio.AsyncClient):
|
||||||
self.models[Member, self.user_id, room.room_id].update(new_dict)
|
self.models[Member, self.user_id, room.room_id].update(new_dict)
|
||||||
|
|
||||||
|
|
||||||
async def get_member_name_avatar(self, room_id: str, user_id: str,
|
async def get_member_name_avatar(
|
||||||
) -> Tuple[str, str]:
|
self, room_id: str, user_id: str,
|
||||||
|
) -> Tuple[str, str]:
|
||||||
|
"""Return a room member's display name and avatar.
|
||||||
|
|
||||||
|
If the member isn't found in the room (e.g. they left), their
|
||||||
|
profile is retrieved using `MatrixClient.backend.get_profile()`.
|
||||||
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
item = self.models[Member, self.user_id, room_id][user_id]
|
item = self.models[Member, self.user_id, room_id][user_id]
|
||||||
|
|
||||||
except KeyError: # e.g. user is not anymore in the room
|
except KeyError: # e.g. user is not anymore in the room
|
||||||
try:
|
try:
|
||||||
info = await self.backend.get_profile(user_id)
|
info = await self.backend.get_profile(user_id)
|
||||||
return (info.displayname or "", info.avatar_url or "")
|
return (info.displayname or "", info.avatar_url or "")
|
||||||
|
|
||||||
except MatrixError:
|
except MatrixError:
|
||||||
return ("", "")
|
return ("", "")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return (item.display_name, item.avatar_url)
|
return (item.display_name, item.avatar_url)
|
||||||
|
|
||||||
|
@ -897,6 +1037,11 @@ class MatrixClient(nio.AsyncClient):
|
||||||
async def register_nio_event(
|
async def register_nio_event(
|
||||||
self, room: nio.MatrixRoom, ev: nio.Event, **fields,
|
self, room: nio.MatrixRoom, ev: nio.Event, **fields,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Register a `nio.Event` as a `Event` object in our model.
|
||||||
|
|
||||||
|
`MatrixClient.register_nio_room` is called for the passed `room`
|
||||||
|
if neccessary before.
|
||||||
|
"""
|
||||||
|
|
||||||
await self.register_nio_room(room)
|
await self.register_nio_room(room)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user