forked from platypush/platypush
Implemented last Matrix integration features.
- Added presence, typing and seen receipt events. - Added set display_name and avatar methods.
This commit is contained in:
parent
e479ca7e3e
commit
c417d2f692
3 changed files with 172 additions and 24 deletions
|
@ -217,3 +217,34 @@ class MatrixRoomTopicChangedEvent(MatrixEvent):
|
|||
:param topic: New room topic.
|
||||
"""
|
||||
super().__init__(*args, topic=topic, **kwargs)
|
||||
|
||||
|
||||
class MatrixRoomTypingStartEvent(MatrixEvent):
|
||||
"""
|
||||
Event triggered when a user in a room starts typing.
|
||||
"""
|
||||
|
||||
|
||||
class MatrixRoomTypingStopEvent(MatrixEvent):
|
||||
"""
|
||||
Event triggered when a user in a room stops typing.
|
||||
"""
|
||||
|
||||
|
||||
class MatrixRoomSeenReceiptEvent(MatrixEvent):
|
||||
"""
|
||||
Event triggered when the last message seen by a user in a room is updated.
|
||||
"""
|
||||
|
||||
|
||||
class MatrixUserPresenceEvent(MatrixEvent):
|
||||
"""
|
||||
Event triggered when a user comes online or goes offline.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, is_active: bool, last_active: datetime | None, **kwargs):
|
||||
"""
|
||||
:param is_active: True if the user is currently online.
|
||||
:param topic: When the user was last active.
|
||||
"""
|
||||
super().__init__(*args, is_active=is_active, last_active=last_active, **kwargs)
|
||||
|
|
|
@ -66,6 +66,8 @@ from nio.client.async_client import client_session
|
|||
from nio.client.base_client import logged_in
|
||||
from nio.crypto import decrypt_attachment
|
||||
from nio.crypto.device import OlmDevice
|
||||
from nio.events.ephemeral import ReceiptEvent, TypingNoticeEvent
|
||||
from nio.events.presence import PresenceEvent
|
||||
from nio.exceptions import OlmUnverifiedDeviceError
|
||||
from nio.responses import DownloadResponse, RoomMessagesResponse
|
||||
|
||||
|
@ -86,8 +88,12 @@ from platypush.message.event.matrix import (
|
|||
MatrixRoomInviteEvent,
|
||||
MatrixRoomJoinEvent,
|
||||
MatrixRoomLeaveEvent,
|
||||
MatrixRoomSeenReceiptEvent,
|
||||
MatrixRoomTopicChangedEvent,
|
||||
MatrixRoomTypingStartEvent,
|
||||
MatrixRoomTypingStopEvent,
|
||||
MatrixSyncEvent,
|
||||
MatrixUserPresenceEvent,
|
||||
)
|
||||
|
||||
from platypush.plugins import AsyncRunnablePlugin, action
|
||||
|
@ -165,6 +171,7 @@ class MatrixClient(AsyncClient):
|
|||
self._autotrust_users_whitelist = autotrust_users_whitelist or set()
|
||||
self._first_sync_performed = asyncio.Event()
|
||||
self._last_batches_by_room = {}
|
||||
self._typing_users_by_room = {}
|
||||
|
||||
self._encrypted_attachments_keystore_path = os.path.join(
|
||||
store_path, 'attachment_keys.json'
|
||||
|
@ -417,6 +424,9 @@ class MatrixClient(AsyncClient):
|
|||
self.add_to_device_callback(self._on_key_verification_key, KeyVerificationKey) # type: ignore
|
||||
self.add_to_device_callback(self._on_key_verification_mac, KeyVerificationMac) # type: ignore
|
||||
self.add_to_device_callback(self._on_key_verification_accept, KeyVerificationAccept) # type: ignore
|
||||
self.add_ephemeral_callback(self._on_typing, TypingNoticeEvent) # type: ignore
|
||||
self.add_ephemeral_callback(self._on_receipt, ReceiptEvent) # type: ignore
|
||||
self.add_presence_callback(self._on_presence, PresenceEvent) # type: ignore
|
||||
|
||||
if self._autojoin_on_invite:
|
||||
self.add_event_callback(self._autojoin_room_callback, InviteEvent) # type: ignore
|
||||
|
@ -498,9 +508,9 @@ class MatrixClient(AsyncClient):
|
|||
return response
|
||||
|
||||
async def _event_base_args(
|
||||
self, room: MatrixRoom, event: Event | None = None
|
||||
self, room: MatrixRoom | None, event: Event | None = None
|
||||
) -> dict:
|
||||
sender_id = event.sender if event else None
|
||||
sender_id = getattr(event, 'sender', None)
|
||||
sender = (
|
||||
await self.get_profile(sender_id) if sender_id else None # type: ignore
|
||||
)
|
||||
|
@ -510,9 +520,15 @@ class MatrixClient(AsyncClient):
|
|||
'sender_id': sender_id,
|
||||
'sender_display_name': sender.displayname if sender else None,
|
||||
'sender_avatar_url': sender.avatar_url if sender else None,
|
||||
**(
|
||||
{
|
||||
'room_id': room.room_id,
|
||||
'room_name': room.name,
|
||||
'room_topic': room.topic,
|
||||
}
|
||||
if room
|
||||
else {}
|
||||
),
|
||||
'server_timestamp': (
|
||||
datetime.datetime.fromtimestamp(event.server_timestamp / 1000)
|
||||
if event and getattr(event, 'server_timestamp', None)
|
||||
|
@ -738,9 +754,65 @@ class MatrixClient(AsyncClient):
|
|||
await self.room_leave(room.room_id)
|
||||
await self.join(event.replacement_room)
|
||||
|
||||
async def _on_typing(self, room: MatrixRoom, event: TypingNoticeEvent):
|
||||
users = set(event.users)
|
||||
typing_users = self._typing_users_by_room.get(room.room_id, set())
|
||||
start_typing_users = users.difference(typing_users)
|
||||
stop_typing_users = typing_users.difference(users)
|
||||
|
||||
for user in start_typing_users:
|
||||
event.sender = user # type: ignore
|
||||
get_bus().post(
|
||||
MatrixRoomTypingStartEvent(
|
||||
**(await self._event_base_args(room, event)), # type: ignore
|
||||
sender=user,
|
||||
)
|
||||
)
|
||||
|
||||
for user in stop_typing_users:
|
||||
event.sender = user # type: ignore
|
||||
get_bus().post(
|
||||
MatrixRoomTypingStopEvent(
|
||||
**(await self._event_base_args(room, event)), # type: ignore
|
||||
)
|
||||
)
|
||||
|
||||
self._typing_users_by_room[room.room_id] = users
|
||||
|
||||
async def _on_receipt(self, room: MatrixRoom, event: ReceiptEvent):
|
||||
if self._first_sync_performed.is_set():
|
||||
for receipt in event.receipts:
|
||||
event.sender = receipt.user_id # type: ignore
|
||||
get_bus().post(
|
||||
MatrixRoomSeenReceiptEvent(
|
||||
**(await self._event_base_args(room, event)), # type: ignore
|
||||
)
|
||||
)
|
||||
|
||||
async def _on_presence(self, event: PresenceEvent):
|
||||
if self._first_sync_performed.is_set():
|
||||
last_active = (
|
||||
(
|
||||
datetime.datetime.now()
|
||||
- datetime.timedelta(seconds=event.last_active_ago / 1000)
|
||||
)
|
||||
if event.last_active_ago
|
||||
else None
|
||||
)
|
||||
|
||||
event.sender = event.user_id # type: ignore
|
||||
get_bus().post(
|
||||
MatrixUserPresenceEvent(
|
||||
**(await self._event_base_args(None, event)), # type: ignore
|
||||
is_active=event.currently_active or False,
|
||||
last_active=last_active,
|
||||
)
|
||||
)
|
||||
|
||||
async def _on_unknown_encrypted_event(
|
||||
self, room: MatrixRoom, event: UnknownEncryptedEvent | MegolmEvent
|
||||
):
|
||||
if self._first_sync_performed.is_set():
|
||||
body = getattr(event, 'ciphertext', '')
|
||||
get_bus().post(
|
||||
MatrixEncryptedMessageEvent(
|
||||
|
@ -875,6 +947,14 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
|||
* :class:`platypush.message.event.matrix.MatrixEncryptedMessageEvent`:
|
||||
when a message is received but the client doesn't have the E2E keys
|
||||
to decrypt it, or encryption has not been enabled.
|
||||
* :class:`platypush.message.event.matrix.MatrixRoomTypingStartEvent`:
|
||||
when a user in a room starts typing.
|
||||
* :class:`platypush.message.event.matrix.MatrixRoomTypingStopEvent`:
|
||||
when a user in a room stops typing.
|
||||
* :class:`platypush.message.event.matrix.MatrixRoomSeenReceiptEvent`:
|
||||
when the last message seen by a user in a room is updated.
|
||||
* :class:`platypush.message.event.matrix.MatrixUserPresenceEvent`:
|
||||
when a user comes online or goes offline.
|
||||
|
||||
"""
|
||||
|
||||
|
@ -1001,6 +1081,8 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
|||
pass
|
||||
except Exception as e:
|
||||
self.logger.exception(e)
|
||||
self.logger.info('Waiting 10 seconds before reconnecting')
|
||||
await asyncio.sleep(10)
|
||||
finally:
|
||||
try:
|
||||
await self.client.close()
|
||||
|
@ -1185,7 +1267,7 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
|||
first returned message will be the oldest and messages will be
|
||||
returned in ascending order.
|
||||
:param limit: Maximum number of messages to be returned (default: 10).
|
||||
# :return: .. schema:: matrix.MatrixMessagesResponseSchema
|
||||
:return: .. schema:: matrix.MatrixMessagesResponseSchema
|
||||
"""
|
||||
response = self._loop_execute(
|
||||
self.client.room_messages(
|
||||
|
@ -1570,5 +1652,23 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
|||
"""
|
||||
self._loop_execute(self.client.room_forget(room_id))
|
||||
|
||||
@action
|
||||
def set_display_name(self, display_name: str):
|
||||
"""
|
||||
Set/change the display name for the current user.
|
||||
|
||||
:param display_name: New display name.
|
||||
"""
|
||||
self._loop_execute(self.client.set_displayname(display_name))
|
||||
|
||||
@action
|
||||
def set_avatar(self, url: str):
|
||||
"""
|
||||
Set/change the avatar URL for the current user.
|
||||
|
||||
:param url: New avatar URL. It must be a valid ``mxc://`` link.
|
||||
"""
|
||||
self._loop_execute(self.client.set_avatar(url))
|
||||
|
||||
|
||||
# vim:sw=4:ts=4:et:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
manifest:
|
||||
events:
|
||||
platypush.message.event.matrix.MatrixMessageEvent: when a message is received.
|
||||
platypush.message.event.matrix.MatrixMessageEvent: when a message is
|
||||
received.
|
||||
platypush.message.event.matrix.MatrixMessageImageEvent: when a message
|
||||
containing an image is received.
|
||||
platypush.message.event.matrix.MatrixMessageAudioEvent: when a message
|
||||
|
@ -11,17 +12,33 @@ manifest:
|
|||
containing a generic file is received.
|
||||
platypush.message.event.matrix.MatrixSyncEvent: when the startup
|
||||
synchronization has been completed and the plugin is ready to use.
|
||||
platypush.message.event.matrix.MatrixRoomCreatedEvent: when a room is created.
|
||||
platypush.message.event.matrix.MatrixRoomJoinEvent: when a user joins a room.
|
||||
platypush.message.event.matrix.MatrixRoomLeaveEvent: when a user leaves a room.
|
||||
platypush.message.event.matrix.MatrixRoomInviteEvent: when the user is invited to a room.
|
||||
platypush.message.event.matrix.MatrixRoomTopicChangedEvent: when the topic/title of a room changes.
|
||||
platypush.message.event.matrix.MatrixCallInviteEvent: when the user is invited to a call.
|
||||
platypush.message.event.matrix.MatrixCallAnswerEvent: when a called user wishes to pick the call.
|
||||
platypush.message.event.matrix.MatrixCallHangupEvent: when a called user exits the call.
|
||||
platypush.message.event.matrix.MatrixEncryptedMessageEvent: |
|
||||
when a message is received but the client doesn't
|
||||
have the E2E keys to decrypt it, or encryption has not been enabled.
|
||||
platypush.message.event.matrix.MatrixRoomCreatedEvent: when a room is
|
||||
created.
|
||||
platypush.message.event.matrix.MatrixRoomJoinEvent: when a user joins a
|
||||
room.
|
||||
platypush.message.event.matrix.MatrixRoomLeaveEvent: when a user leaves a
|
||||
room.
|
||||
platypush.message.event.matrix.MatrixRoomInviteEvent: when the user is
|
||||
invited to a room.
|
||||
platypush.message.event.matrix.MatrixRoomTopicChangedEvent: when the
|
||||
topic/title of a room changes.
|
||||
platypush.message.event.matrix.MatrixCallInviteEvent: when the user is
|
||||
invited to a call.
|
||||
platypush.message.event.matrix.MatrixCallAnswerEvent: when a called user
|
||||
wishes to pick the call.
|
||||
platypush.message.event.matrix.MatrixCallHangupEvent: when a called user
|
||||
exits the call.
|
||||
platypush.message.event.matrix.MatrixEncryptedMessageEvent: when a message
|
||||
is received but the client doesn't have the E2E keys to decrypt it, or
|
||||
encryption has not been enabled.
|
||||
platypush.message.event.matrix.MatrixRoomTypingStartEvent: when a user in a
|
||||
room starts typing.
|
||||
platypush.message.event.matrix.MatrixRoomTypingStopEvent: when a user in a
|
||||
room stops typing.
|
||||
platypush.message.event.matrix.MatrixRoomSeenReceiptEvent: when the last
|
||||
message seen by a user in a room is updated.
|
||||
platypush.message.event.matrix.MatrixUserPresenceEvent: when a user comes
|
||||
online or goes offline.
|
||||
apt:
|
||||
- libolm-devel
|
||||
pacman:
|
||||
|
|
Loading…
Reference in a new issue