forked from platypush/platypush
Completing the Matrix plugin integration
Newly implemented actions: - `get_messages` - `get_room_members` - `update_device` - `delete_devices` - `room_alias_to_id` - `add_room_alias` - `delete_room_alias` - `kick` - `ban` - `unban` - `forget`
This commit is contained in:
parent
0e3cabc5f6
commit
e479ca7e3e
2 changed files with 392 additions and 5 deletions
|
@ -41,6 +41,7 @@ from nio import (
|
||||||
RoomGetEventError,
|
RoomGetEventError,
|
||||||
RoomGetStateResponse,
|
RoomGetStateResponse,
|
||||||
RoomMemberEvent,
|
RoomMemberEvent,
|
||||||
|
RoomMessage,
|
||||||
RoomMessageAudio,
|
RoomMessageAudio,
|
||||||
RoomMessageFile,
|
RoomMessageFile,
|
||||||
RoomMessageFormatted,
|
RoomMessageFormatted,
|
||||||
|
@ -51,6 +52,7 @@ from nio import (
|
||||||
RoomTopicEvent,
|
RoomTopicEvent,
|
||||||
RoomUpgradeEvent,
|
RoomUpgradeEvent,
|
||||||
StickerEvent,
|
StickerEvent,
|
||||||
|
SyncResponse,
|
||||||
ToDeviceError,
|
ToDeviceError,
|
||||||
UnknownEncryptedEvent,
|
UnknownEncryptedEvent,
|
||||||
UnknownEvent,
|
UnknownEvent,
|
||||||
|
@ -58,13 +60,14 @@ from nio import (
|
||||||
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
import aiofiles.os
|
import aiofiles.os
|
||||||
from nio.api import RoomVisibility
|
from nio.api import MessageDirection, RoomVisibility
|
||||||
|
|
||||||
from nio.client.async_client import client_session
|
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 import decrypt_attachment
|
||||||
from nio.crypto.device import OlmDevice
|
from nio.crypto.device import OlmDevice
|
||||||
from nio.exceptions import OlmUnverifiedDeviceError
|
from nio.exceptions import OlmUnverifiedDeviceError
|
||||||
from nio.responses import DownloadResponse
|
from nio.responses import DownloadResponse, RoomMessagesResponse
|
||||||
|
|
||||||
from platypush.config import Config
|
from platypush.config import Config
|
||||||
from platypush.context import get_bus
|
from platypush.context import get_bus
|
||||||
|
@ -92,6 +95,8 @@ from platypush.schemas.matrix import (
|
||||||
MatrixDeviceSchema,
|
MatrixDeviceSchema,
|
||||||
MatrixDownloadedFileSchema,
|
MatrixDownloadedFileSchema,
|
||||||
MatrixEventIdSchema,
|
MatrixEventIdSchema,
|
||||||
|
MatrixMemberSchema,
|
||||||
|
MatrixMessagesResponseSchema,
|
||||||
MatrixMyDeviceSchema,
|
MatrixMyDeviceSchema,
|
||||||
MatrixProfileSchema,
|
MatrixProfileSchema,
|
||||||
MatrixRoomIdSchema,
|
MatrixRoomIdSchema,
|
||||||
|
@ -159,6 +164,7 @@ class MatrixClient(AsyncClient):
|
||||||
self._autotrust_rooms_whitelist = autotrust_rooms_whitelist or set()
|
self._autotrust_rooms_whitelist = autotrust_rooms_whitelist or set()
|
||||||
self._autotrust_users_whitelist = autotrust_users_whitelist or set()
|
self._autotrust_users_whitelist = autotrust_users_whitelist or set()
|
||||||
self._first_sync_performed = asyncio.Event()
|
self._first_sync_performed = asyncio.Event()
|
||||||
|
self._last_batches_by_room = {}
|
||||||
|
|
||||||
self._encrypted_attachments_keystore_path = os.path.join(
|
self._encrypted_attachments_keystore_path = os.path.join(
|
||||||
store_path, 'attachment_keys.json'
|
store_path, 'attachment_keys.json'
|
||||||
|
@ -177,6 +183,7 @@ class MatrixClient(AsyncClient):
|
||||||
pathlib.Path(self._encrypted_attachments_keystore_path).touch(
|
pathlib.Path(self._encrypted_attachments_keystore_path).touch(
|
||||||
mode=0o600, exist_ok=True
|
mode=0o600, exist_ok=True
|
||||||
)
|
)
|
||||||
|
|
||||||
self._encrypted_attachments_keystore = {
|
self._encrypted_attachments_keystore = {
|
||||||
tuple(key.split('|')): data for key, data in keystore.items()
|
tuple(key.split('|')): data for key, data in keystore.items()
|
||||||
}
|
}
|
||||||
|
@ -276,6 +283,37 @@ class MatrixClient(AsyncClient):
|
||||||
self.logger.info('State synchronized')
|
self.logger.info('State synchronized')
|
||||||
return login_res
|
return login_res
|
||||||
|
|
||||||
|
@logged_in
|
||||||
|
async def sync(self, *args, **kwargs) -> SyncResponse:
|
||||||
|
response = await super().sync(*args, **kwargs)
|
||||||
|
assert isinstance(response, SyncResponse), str(response)
|
||||||
|
self._last_batches_by_room.update(
|
||||||
|
{
|
||||||
|
room_id: {
|
||||||
|
'prev_batch': room.timeline.prev_batch,
|
||||||
|
'next_batch': response.next_batch,
|
||||||
|
}
|
||||||
|
for room_id, room in response.rooms.join.items()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
@logged_in
|
||||||
|
async def room_messages(
|
||||||
|
self, room_id: str, start: str | None = None, *args, **kwargs
|
||||||
|
) -> RoomMessagesResponse:
|
||||||
|
if not start:
|
||||||
|
start = self._last_batches_by_room.get(room_id, {}).get('prev_batch')
|
||||||
|
assert start, (
|
||||||
|
f'No sync batches were found for room {room_id} and no start'
|
||||||
|
'batch has been provided'
|
||||||
|
)
|
||||||
|
|
||||||
|
response = await super().room_messages(room_id, start, *args, **kwargs)
|
||||||
|
assert isinstance(response, RoomMessagesResponse), str(response)
|
||||||
|
return response
|
||||||
|
|
||||||
def _sync_devices_trust(self):
|
def _sync_devices_trust(self):
|
||||||
all_devices = self.get_devices()
|
all_devices = self.get_devices()
|
||||||
devices_to_trust: Dict[str, OlmDevice] = {}
|
devices_to_trust: Dict[str, OlmDevice] = {}
|
||||||
|
@ -425,6 +463,7 @@ class MatrixClient(AsyncClient):
|
||||||
), f'Could not retrieve profile for room {room_id}: {ret.message}'
|
), f'Could not retrieve profile for room {room_id}: {ret.message}'
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@client_session
|
||||||
async def download(
|
async def download(
|
||||||
self,
|
self,
|
||||||
server_name: str,
|
server_name: str,
|
||||||
|
@ -1125,6 +1164,44 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
setattr(room, k, v)
|
setattr(room, k, v)
|
||||||
return MatrixRoomSchema().dump(room)
|
return MatrixRoomSchema().dump(room)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def get_messages(
|
||||||
|
self,
|
||||||
|
room_id: str,
|
||||||
|
start: str | None = None,
|
||||||
|
end: str | None = None,
|
||||||
|
backwards: bool = True,
|
||||||
|
limit: int = 10,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Retrieve a list of messages from a room.
|
||||||
|
|
||||||
|
:param room_id: Room ID.
|
||||||
|
:param start: Start retrieving messages from this batch ID (default:
|
||||||
|
latest batch returned from a call to ``sync``).
|
||||||
|
:param end: Retrieving messages until this batch ID.
|
||||||
|
:param backwards: Set to True if you want to retrieve messages starting
|
||||||
|
from the most recent, in descending order (default). Otherwise, the
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
response = self._loop_execute(
|
||||||
|
self.client.room_messages(
|
||||||
|
room_id,
|
||||||
|
start=start,
|
||||||
|
end=end,
|
||||||
|
limit=limit,
|
||||||
|
direction=(
|
||||||
|
MessageDirection.back if backwards else MessageDirection.front
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
response.chunk = [m for m in response.chunk if isinstance(m, RoomMessage)]
|
||||||
|
return MatrixMessagesResponseSchema().dump(response)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_my_devices(self):
|
def get_my_devices(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1144,6 +1221,44 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
"""
|
"""
|
||||||
return MatrixDeviceSchema().dump(self._get_device(device_id))
|
return MatrixDeviceSchema().dump(self._get_device(device_id))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def update_device(self, device_id: str, display_name: str | None = None):
|
||||||
|
"""
|
||||||
|
Update information about a user's device.
|
||||||
|
|
||||||
|
:param display_name: New display name.
|
||||||
|
:return: .. schema:: matrix.MatrixDeviceSchema
|
||||||
|
"""
|
||||||
|
content = {}
|
||||||
|
if display_name:
|
||||||
|
content['display_name'] = display_name
|
||||||
|
|
||||||
|
self._loop_execute(self.client.update_device(device_id, content))
|
||||||
|
return MatrixDeviceSchema().dump(self._get_device(device_id))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def delete_devices(
|
||||||
|
self,
|
||||||
|
devices: Sequence[str],
|
||||||
|
username: str | None = None,
|
||||||
|
password: str | None = None,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Delete a list of devices from the user's authorized list and invalidate
|
||||||
|
their access tokens.
|
||||||
|
|
||||||
|
:param devices: List of devices that should be deleted.
|
||||||
|
:param username: Username, if the server requires authentication upon
|
||||||
|
device deletion.
|
||||||
|
:param password: User password, if the server requires authentication
|
||||||
|
upon device deletion.
|
||||||
|
"""
|
||||||
|
auth = {}
|
||||||
|
if username and password:
|
||||||
|
auth = {'type': 'm.login.password', 'user': username, 'password': password}
|
||||||
|
|
||||||
|
self._loop_execute(self.client.delete_devices([*devices], auth=auth))
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def get_joined_rooms(self):
|
def get_joined_rooms(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1154,6 +1269,48 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
response = self._loop_execute(self.client.joined_rooms())
|
response = self._loop_execute(self.client.joined_rooms())
|
||||||
return [self.get_room(room_id).output for room_id in response.rooms] # type: ignore
|
return [self.get_room(room_id).output for room_id in response.rooms] # type: ignore
|
||||||
|
|
||||||
|
@action
|
||||||
|
def get_room_members(self, room_id: str):
|
||||||
|
"""
|
||||||
|
Retrieve the list of users joined into a room.
|
||||||
|
|
||||||
|
:param room_id: The room ID.
|
||||||
|
:return: .. schema:: matrix.MatrixMemberSchema(many=True)
|
||||||
|
"""
|
||||||
|
response = self._loop_execute(self.client.joined_members(room_id))
|
||||||
|
return MatrixMemberSchema().dump(response.members, many=True)
|
||||||
|
|
||||||
|
@action
|
||||||
|
def room_alias_to_id(self, alias: str) -> str:
|
||||||
|
"""
|
||||||
|
Convert a room alias (in the format ``#alias:matrix.example.org``) to a
|
||||||
|
room ID (in the format ``!aBcDeFgHiJkMnO:matrix.example.org').
|
||||||
|
|
||||||
|
:param alias: The room alias.
|
||||||
|
:return: The room ID, as a string.
|
||||||
|
"""
|
||||||
|
response = self._loop_execute(self.client.room_resolve_alias(alias))
|
||||||
|
return response.room_id
|
||||||
|
|
||||||
|
@action
|
||||||
|
def add_room_alias(self, room_id: str, alias: str):
|
||||||
|
"""
|
||||||
|
Add an alias to a room.
|
||||||
|
|
||||||
|
:param room_id: An existing room ID.
|
||||||
|
:param alias: The room alias.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_put_alias(alias, room_id))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def delete_room_alias(self, alias: str):
|
||||||
|
"""
|
||||||
|
Delete a room alias.
|
||||||
|
|
||||||
|
:param alias: The room alias.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_delete_alias(alias))
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def upload_keys(self):
|
def upload_keys(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1342,7 +1499,7 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
return MatrixRoomIdSchema().dump(rs)
|
return MatrixRoomIdSchema().dump(rs)
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def invite_to_room(self, room_id: str, user_id: str):
|
def invite(self, room_id: str, user_id: str):
|
||||||
"""
|
"""
|
||||||
Invite a user to a room.
|
Invite a user to a room.
|
||||||
|
|
||||||
|
@ -1352,7 +1509,39 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
self._loop_execute(self.client.room_invite(room_id, user_id))
|
self._loop_execute(self.client.room_invite(room_id, user_id))
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def join_room(self, room_id: str):
|
def kick(self, room_id: str, user_id: str, reason: str | None = None):
|
||||||
|
"""
|
||||||
|
Kick a user out of a room.
|
||||||
|
|
||||||
|
:param room_id: Room ID.
|
||||||
|
:param user_id: User ID.
|
||||||
|
:param reason: Optional reason.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_kick(room_id, user_id, reason))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def ban(self, room_id: str, user_id: str, reason: str | None = None):
|
||||||
|
"""
|
||||||
|
Ban a user from a room.
|
||||||
|
|
||||||
|
:param room_id: Room ID.
|
||||||
|
:param user_id: User ID.
|
||||||
|
:param reason: Optional reason.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_ban(room_id, user_id, reason))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def unban(self, room_id: str, user_id: str):
|
||||||
|
"""
|
||||||
|
Remove a user ban from a room.
|
||||||
|
|
||||||
|
:param room_id: Room ID.
|
||||||
|
:param user_id: User ID.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_unban(room_id, user_id))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def join(self, room_id: str):
|
||||||
"""
|
"""
|
||||||
Join a room.
|
Join a room.
|
||||||
|
|
||||||
|
@ -1361,7 +1550,7 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
self._loop_execute(self.client.join(room_id))
|
self._loop_execute(self.client.join(room_id))
|
||||||
|
|
||||||
@action
|
@action
|
||||||
def leave_room(self, room_id: str):
|
def leave(self, room_id: str):
|
||||||
"""
|
"""
|
||||||
Leave a joined room.
|
Leave a joined room.
|
||||||
|
|
||||||
|
@ -1369,5 +1558,17 @@ class MatrixPlugin(AsyncRunnablePlugin):
|
||||||
"""
|
"""
|
||||||
self._loop_execute(self.client.room_leave(room_id))
|
self._loop_execute(self.client.room_leave(room_id))
|
||||||
|
|
||||||
|
@action
|
||||||
|
def forget(self, room_id: str):
|
||||||
|
"""
|
||||||
|
Leave a joined room and forget its data as well as all the messages.
|
||||||
|
|
||||||
|
If all the users leave a room, that room will be marked for deletion by
|
||||||
|
the homeserver.
|
||||||
|
|
||||||
|
:param room_id: Room ID.
|
||||||
|
"""
|
||||||
|
self._loop_execute(self.client.room_forget(room_id))
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -4,6 +4,14 @@ from marshmallow.schema import Schema
|
||||||
from platypush.schemas import DateTime
|
from platypush.schemas import DateTime
|
||||||
|
|
||||||
|
|
||||||
|
class MillisecondsTimestamp(DateTime):
|
||||||
|
def _get_attr(self, *args, **kwargs):
|
||||||
|
value = super()._get_attr(*args, **kwargs)
|
||||||
|
if isinstance(value, int):
|
||||||
|
value = float(value / 1000)
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class MatrixEventIdSchema(Schema):
|
class MatrixEventIdSchema(Schema):
|
||||||
event_id = fields.String(
|
event_id = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
|
@ -49,6 +57,12 @@ class MatrixProfileSchema(Schema):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMemberSchema(MatrixProfileSchema):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['display_name'].attribute = 'display_name'
|
||||||
|
|
||||||
|
|
||||||
class MatrixRoomSchema(Schema):
|
class MatrixRoomSchema(Schema):
|
||||||
room_id = fields.String(
|
room_id = fields.String(
|
||||||
required=True,
|
required=True,
|
||||||
|
@ -197,3 +211,175 @@ class MatrixDownloadedFileSchema(Schema):
|
||||||
'example': 1024,
|
'example': 1024,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageSchema(Schema):
|
||||||
|
event_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Event ID associated to this message',
|
||||||
|
'example': '$2eOQ5ueafANj91GnPCRkRUOOjM7dI5kFDOlfMNCD2ly',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
room_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'The ID of the room containing the message',
|
||||||
|
'example': '!aBcDeFgHiJkMnO:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
attribute='sender',
|
||||||
|
metadata={
|
||||||
|
'description': 'ID of the user who sent the message',
|
||||||
|
'example': '@myuser:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
body = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Message body',
|
||||||
|
'example': 'Hello world!',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
format = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Message format',
|
||||||
|
'example': 'markdown',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
formatted_body = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Formatted body',
|
||||||
|
'example': '**Hello world!**',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
url = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'mxc:// URL if this message contains an attachment',
|
||||||
|
'example': 'mxc://matrix.example.org/oarGdlpvcwppARPjzNlmlXkD',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
content_type = fields.String(
|
||||||
|
attribute='mimetype',
|
||||||
|
metadata={
|
||||||
|
'description': 'If the message contains an attachment, this field '
|
||||||
|
'will contain its MIME type',
|
||||||
|
'example': 'image/jpeg',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
transaction_id = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Set if this message a unique transaction_id associated',
|
||||||
|
'example': 'mQ8hZR6Dx8I8YDMwONYmBkf7lTgJSMV/ZPqosDNM',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
decrypted = fields.Bool(
|
||||||
|
metadata={
|
||||||
|
'description': 'True if the message was encrypted and has been '
|
||||||
|
'successfully decrypted',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
verified = fields.Bool(
|
||||||
|
metadata={
|
||||||
|
'description': 'True if this is an encrypted message coming from a '
|
||||||
|
'verified source'
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
hashes = fields.Dict(
|
||||||
|
metadata={
|
||||||
|
'description': 'If the message has been decrypted, this field '
|
||||||
|
'contains a mapping of its hashes',
|
||||||
|
'example': {'sha256': 'yoQLQwcURq6/bJp1xQ/uhn9Z2xeA27KhMhPd/mfT8tR'},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
iv = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'If the message has been decrypted, this field '
|
||||||
|
'contains the encryption initial value',
|
||||||
|
'example': 'NqJMMdijlLvAAAAAAAAAAA',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
key = fields.Dict(
|
||||||
|
metadata={
|
||||||
|
'description': 'If the message has been decrypted, this field '
|
||||||
|
'contains the encryption/decryption key',
|
||||||
|
'example': {
|
||||||
|
'alg': 'A256CTR',
|
||||||
|
'ext': True,
|
||||||
|
'k': 'u6jjAyNvJoBHE55P5ZfvX49m3oSt9s_L4PSQdprRSJI',
|
||||||
|
'key_ops': ['encrypt', 'decrypt'],
|
||||||
|
'kty': 'oct',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
timestamp = MillisecondsTimestamp(
|
||||||
|
required=True,
|
||||||
|
attribute='server_timestamp',
|
||||||
|
metadata={
|
||||||
|
'description': 'When the event was registered on the server',
|
||||||
|
'example': '2022-07-23T17:20:01.254223',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessagesResponseSchema(Schema):
|
||||||
|
messages = fields.Nested(
|
||||||
|
MatrixMessageSchema(),
|
||||||
|
many=True,
|
||||||
|
required=True,
|
||||||
|
attribute='chunk',
|
||||||
|
)
|
||||||
|
|
||||||
|
start = fields.String(
|
||||||
|
required=True,
|
||||||
|
nullable=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Pointer to the first message. It can be used as a '
|
||||||
|
'``start``/``end`` for another ``get_messages`` query.',
|
||||||
|
'example': 's10226_143893_619_3648_5951_5_555_7501_0',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
end = fields.String(
|
||||||
|
required=True,
|
||||||
|
nullable=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Pointer to the last message. It can be used as a '
|
||||||
|
'``start``/``end`` for another ``get_messages`` query.',
|
||||||
|
'example': 't2-10202_143892_626_3663_5949_6_558_7501_0',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
start_time = MillisecondsTimestamp(
|
||||||
|
required=True,
|
||||||
|
nullable=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'The oldest timestamp of the returned messages',
|
||||||
|
'example': '2022-07-23T16:20:01.254223',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
end_time = MillisecondsTimestamp(
|
||||||
|
required=True,
|
||||||
|
nullable=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'The newest timestamp of the returned messages',
|
||||||
|
'example': '2022-07-23T18:20:01.254223',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue