forked from platypush/platypush
Merge pull request 'Matrix Integration' (#217) from matrix-integration into master
Reviewed-on: platypush/platypush#217 Closes: #2
This commit is contained in:
commit
f4360dc0e0
15 changed files with 2623 additions and 171 deletions
|
@ -138,15 +138,12 @@ latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#
|
#
|
||||||
# 'papersize': 'letterpaper',
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#
|
#
|
||||||
# 'pointsize': '10pt',
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#
|
#
|
||||||
# 'preamble': '',
|
# 'preamble': '',
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
# Latex figure (float) alignment
|
||||||
#
|
#
|
||||||
# 'figure_align': 'htbp',
|
# 'figure_align': 'htbp',
|
||||||
|
@ -156,8 +153,7 @@ latex_elements = {
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(master_doc, 'platypush.tex', 'platypush Documentation',
|
(master_doc, 'platypush.tex', 'platypush Documentation', 'BlackLight', 'manual'),
|
||||||
'BlackLight', 'manual'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -165,10 +161,7 @@ latex_documents = [
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [(master_doc, 'platypush', 'platypush Documentation', [author], 1)]
|
||||||
(master_doc, 'platypush', 'platypush Documentation',
|
|
||||||
[author], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output ----------------------------------------------
|
# -- Options for Texinfo output ----------------------------------------------
|
||||||
|
@ -177,9 +170,15 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(master_doc, 'platypush', 'platypush Documentation',
|
(
|
||||||
author, 'platypush', 'One line description of project.',
|
master_doc,
|
||||||
'Miscellaneous'),
|
'platypush',
|
||||||
|
'platypush Documentation',
|
||||||
|
author,
|
||||||
|
'platypush',
|
||||||
|
'One line description of project.',
|
||||||
|
'Miscellaneous',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -199,7 +198,8 @@ autodoc_default_options = {
|
||||||
'inherited-members': True,
|
'inherited-members': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
autodoc_mock_imports = [
|
||||||
|
'googlesamples.assistant.grpc.audio_helpers',
|
||||||
'google.assistant.embedded',
|
'google.assistant.embedded',
|
||||||
'google.assistant.library',
|
'google.assistant.library',
|
||||||
'google.assistant.library.event',
|
'google.assistant.library.event',
|
||||||
|
@ -291,7 +291,10 @@ autodoc_mock_imports = ['googlesamples.assistant.grpc.audio_helpers',
|
||||||
'irc.connection',
|
'irc.connection',
|
||||||
'irc.events',
|
'irc.events',
|
||||||
'defusedxml',
|
'defusedxml',
|
||||||
]
|
'nio',
|
||||||
|
'aiofiles',
|
||||||
|
'aiofiles.os',
|
||||||
|
]
|
||||||
|
|
||||||
sys.path.insert(0, os.path.abspath('../..'))
|
sys.path.insert(0, os.path.abspath('../..'))
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ Events
|
||||||
platypush/events/linode.rst
|
platypush/events/linode.rst
|
||||||
platypush/events/log.http.rst
|
platypush/events/log.http.rst
|
||||||
platypush/events/mail.rst
|
platypush/events/mail.rst
|
||||||
|
platypush/events/matrix.rst
|
||||||
platypush/events/media.rst
|
platypush/events/media.rst
|
||||||
platypush/events/midi.rst
|
platypush/events/midi.rst
|
||||||
platypush/events/mqtt.rst
|
platypush/events/mqtt.rst
|
||||||
|
@ -72,6 +73,7 @@ Events
|
||||||
platypush/events/weather.rst
|
platypush/events/weather.rst
|
||||||
platypush/events/web.rst
|
platypush/events/web.rst
|
||||||
platypush/events/web.widget.rst
|
platypush/events/web.widget.rst
|
||||||
|
platypush/events/websocket.rst
|
||||||
platypush/events/wiimote.rst
|
platypush/events/wiimote.rst
|
||||||
platypush/events/zeroborg.rst
|
platypush/events/zeroborg.rst
|
||||||
platypush/events/zeroconf.rst
|
platypush/events/zeroconf.rst
|
||||||
|
|
5
docs/source/platypush/events/matrix.rst
Normal file
5
docs/source/platypush/events/matrix.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``matrix``
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. automodule:: platypush.message.event.matrix
|
||||||
|
:members:
|
5
docs/source/platypush/events/websocket.rst
Normal file
5
docs/source/platypush/events/websocket.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``websocket``
|
||||||
|
=============
|
||||||
|
|
||||||
|
.. automodule:: platypush.message.event.websocket
|
||||||
|
:members:
|
5
docs/source/platypush/plugins/matrix.rst
Normal file
5
docs/source/platypush/plugins/matrix.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
``matrix``
|
||||||
|
==========
|
||||||
|
|
||||||
|
.. automodule:: platypush.plugins.matrix
|
||||||
|
:members:
|
|
@ -75,6 +75,7 @@ Plugins
|
||||||
platypush/plugins/mail.smtp.rst
|
platypush/plugins/mail.smtp.rst
|
||||||
platypush/plugins/mailgun.rst
|
platypush/plugins/mailgun.rst
|
||||||
platypush/plugins/mastodon.rst
|
platypush/plugins/mastodon.rst
|
||||||
|
platypush/plugins/matrix.rst
|
||||||
platypush/plugins/media.chromecast.rst
|
platypush/plugins/media.chromecast.rst
|
||||||
platypush/plugins/media.gstreamer.rst
|
platypush/plugins/media.gstreamer.rst
|
||||||
platypush/plugins/media.jellyfin.rst
|
platypush/plugins/media.jellyfin.rst
|
||||||
|
|
|
@ -213,11 +213,10 @@ class Config:
|
||||||
config['scripts_dir'] = os.path.abspath(
|
config['scripts_dir'] = os.path.abspath(
|
||||||
os.path.expanduser(file_config[section])
|
os.path.expanduser(file_config[section])
|
||||||
)
|
)
|
||||||
elif (
|
else:
|
||||||
'disabled' not in file_config[section]
|
section_config = file_config.get(section, {}) or {}
|
||||||
or file_config[section]['disabled'] is False
|
if not section_config.get('disabled'):
|
||||||
):
|
config[section] = section_config
|
||||||
config[section] = file_config[section]
|
|
||||||
|
|
||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
250
platypush/message/event/matrix.py
Normal file
250
platypush/message/event/matrix.py
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Dict, Any
|
||||||
|
|
||||||
|
from platypush.message.event import Event
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixEvent(Event):
|
||||||
|
"""
|
||||||
|
Base matrix event.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
server_url: str,
|
||||||
|
sender_id: str | None = None,
|
||||||
|
sender_display_name: str | None = None,
|
||||||
|
sender_avatar_url: str | None = None,
|
||||||
|
room_id: str | None = None,
|
||||||
|
room_name: str | None = None,
|
||||||
|
room_topic: str | None = None,
|
||||||
|
server_timestamp: datetime | None = None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
:param server_url: Base server URL.
|
||||||
|
:param sender_id: The event's sender ID.
|
||||||
|
:param sender_display_name: The event's sender display name.
|
||||||
|
:param sender_avatar_url: The event's sender avatar URL.
|
||||||
|
:param room_id: Event room ID.
|
||||||
|
:param room_name: The name of the room associated to the event.
|
||||||
|
:param room_topic: The topic of the room associated to the event.
|
||||||
|
:param server_timestamp: The server timestamp of the event.
|
||||||
|
"""
|
||||||
|
evt_args: Dict[str, Any] = {
|
||||||
|
'server_url': server_url,
|
||||||
|
}
|
||||||
|
|
||||||
|
if sender_id:
|
||||||
|
evt_args['sender_id'] = sender_id
|
||||||
|
if sender_display_name:
|
||||||
|
evt_args['sender_display_name'] = sender_display_name
|
||||||
|
if sender_avatar_url:
|
||||||
|
evt_args['sender_avatar_url'] = sender_avatar_url
|
||||||
|
if room_id:
|
||||||
|
evt_args['room_id'] = room_id
|
||||||
|
if room_name:
|
||||||
|
evt_args['room_name'] = room_name
|
||||||
|
if room_topic:
|
||||||
|
evt_args['room_topic'] = room_topic
|
||||||
|
if server_timestamp:
|
||||||
|
evt_args['server_timestamp'] = server_timestamp
|
||||||
|
|
||||||
|
super().__init__(*args, **evt_args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixSyncEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when the startup synchronization has been completed and the
|
||||||
|
plugin is ready to use.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message is received on a subscribed room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
*args,
|
||||||
|
body: str = '',
|
||||||
|
url: str | None = None,
|
||||||
|
thumbnail_url: str | None = None,
|
||||||
|
mimetype: str | None = None,
|
||||||
|
formatted_body: str | None = None,
|
||||||
|
format: str | None = None,
|
||||||
|
**kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
:param body: The body of the message.
|
||||||
|
:param url: The URL of the media file, if the message includes media.
|
||||||
|
:param thumbnail_url: The URL of the thumbnail, if the message includes media.
|
||||||
|
:param mimetype: The MIME type of the media file, if the message includes media.
|
||||||
|
:param formatted_body: The formatted body, if ``format`` is specified.
|
||||||
|
:param format: The format of the message (e.g. ``html`` or ``markdown``).
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
*args,
|
||||||
|
body=body,
|
||||||
|
url=url,
|
||||||
|
thumbnail_url=thumbnail_url,
|
||||||
|
mimetype=mimetype,
|
||||||
|
formatted_body=formatted_body,
|
||||||
|
format=format,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageImageEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message containing an image is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageFileEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message containing a generic file is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageAudioEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message containing an audio file is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMessageVideoEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message containing a video file is received.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixReactionEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a user submits a reaction to an event.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, in_response_to_event_id: str, **kwargs):
|
||||||
|
"""
|
||||||
|
:param in_response_to_event_id: The ID of the URL related to the reaction.
|
||||||
|
"""
|
||||||
|
super().__init__(
|
||||||
|
*args, in_response_to_event_id=in_response_to_event_id, **kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixEncryptedMessageEvent(MatrixMessageEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a message is received but the client doesn't
|
||||||
|
have the E2E keys to decrypt it, or encryption has not been enabled.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixCallEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Base class for Matrix call events.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, *args, call_id: str, version: int, sdp: str | None = None, **kwargs
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
:param call_id: The unique ID of the call.
|
||||||
|
:param version: An increasing integer representing the version of the call.
|
||||||
|
:param sdp: SDP text of the session description.
|
||||||
|
"""
|
||||||
|
super().__init__(*args, call_id=call_id, version=version, sdp=sdp, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixCallInviteEvent(MatrixCallEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when the user is invited to a call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, invite_validity: float | None = None, **kwargs):
|
||||||
|
"""
|
||||||
|
:param invite_validity: For how long the invite will be valid, in seconds.
|
||||||
|
:param sdp: SDP text of the session description.
|
||||||
|
"""
|
||||||
|
super().__init__(*args, invite_validity=invite_validity, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixCallAnswerEvent(MatrixCallEvent):
|
||||||
|
"""
|
||||||
|
Event triggered by the callee when they wish to answer the call.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixCallHangupEvent(MatrixCallEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a participant in the call exists.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomCreatedEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a room is created.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomJoinEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a user joins a room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomLeaveEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when a user leaves a room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomInviteEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when the user is invited to a room.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomTopicChangedEvent(MatrixEvent):
|
||||||
|
"""
|
||||||
|
Event triggered when the topic/title of a room changes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, *args, topic: str, **kwargs):
|
||||||
|
"""
|
||||||
|
: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)
|
|
@ -12,17 +12,30 @@ from platypush.config import Config
|
||||||
from platypush.context import get_plugin
|
from platypush.context import get_plugin
|
||||||
from platypush.message import Message
|
from platypush.message import Message
|
||||||
from platypush.message.response import Response
|
from platypush.message.response import Response
|
||||||
from platypush.utils import get_hash, get_module_and_method_from_action, get_redis_queue_name_by_message, \
|
from platypush.utils import (
|
||||||
is_functional_procedure
|
get_hash,
|
||||||
|
get_module_and_method_from_action,
|
||||||
|
get_redis_queue_name_by_message,
|
||||||
|
is_functional_procedure,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger('platypush')
|
logger = logging.getLogger('platypush')
|
||||||
|
|
||||||
|
|
||||||
class Request(Message):
|
class Request(Message):
|
||||||
""" Request message class """
|
"""Request message class"""
|
||||||
|
|
||||||
def __init__(self, target, action, origin=None, id=None, backend=None,
|
def __init__(
|
||||||
args=None, token=None, timestamp=None):
|
self,
|
||||||
|
target,
|
||||||
|
action,
|
||||||
|
origin=None,
|
||||||
|
id=None,
|
||||||
|
backend=None,
|
||||||
|
args=None,
|
||||||
|
token=None,
|
||||||
|
timestamp=None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Params:
|
Params:
|
||||||
target -- Target node [Str]
|
target -- Target node [Str]
|
||||||
|
@ -48,9 +61,13 @@ class Request(Message):
|
||||||
@classmethod
|
@classmethod
|
||||||
def build(cls, msg):
|
def build(cls, msg):
|
||||||
msg = super().parse(msg)
|
msg = super().parse(msg)
|
||||||
args = {'target': msg.get('target', Config.get('device_id')), 'action': msg['action'],
|
args = {
|
||||||
'args': msg.get('args', {}), 'id': msg['id'] if 'id' in msg else cls._generate_id(),
|
'target': msg.get('target', Config.get('device_id')),
|
||||||
'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time()}
|
'action': msg['action'],
|
||||||
|
'args': msg.get('args', {}),
|
||||||
|
'id': msg['id'] if 'id' in msg else cls._generate_id(),
|
||||||
|
'timestamp': msg['_timestamp'] if '_timestamp' in msg else time.time(),
|
||||||
|
}
|
||||||
|
|
||||||
if 'origin' in msg:
|
if 'origin' in msg:
|
||||||
args['origin'] = msg['origin']
|
args['origin'] = msg['origin']
|
||||||
|
@ -61,7 +78,7 @@ class Request(Message):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _generate_id():
|
def _generate_id():
|
||||||
_id = ''
|
_id = ''
|
||||||
for i in range(0, 16):
|
for _ in range(0, 16):
|
||||||
_id += '%.2x' % random.randint(0, 255)
|
_id += '%.2x' % random.randint(0, 255)
|
||||||
return _id
|
return _id
|
||||||
|
|
||||||
|
@ -84,9 +101,14 @@ class Request(Message):
|
||||||
|
|
||||||
return proc_config(*args, **kwargs)
|
return proc_config(*args, **kwargs)
|
||||||
|
|
||||||
proc = Procedure.build(name=proc_name, requests=proc_config['actions'],
|
proc = Procedure.build(
|
||||||
_async=proc_config['_async'], args=self.args,
|
name=proc_name,
|
||||||
backend=self.backend, id=self.id)
|
requests=proc_config['actions'],
|
||||||
|
_async=proc_config['_async'],
|
||||||
|
args=self.args,
|
||||||
|
backend=self.backend,
|
||||||
|
id=self.id,
|
||||||
|
)
|
||||||
|
|
||||||
return proc.execute(*args, **kwargs)
|
return proc.execute(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -112,7 +134,7 @@ class Request(Message):
|
||||||
|
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
value = self.expand_value_from_context(value, **context)
|
value = self.expand_value_from_context(value, **context)
|
||||||
elif isinstance(value, dict) or isinstance(value, list):
|
elif isinstance(value, (dict, list)):
|
||||||
self._expand_context(event_args=value, **context)
|
self._expand_context(event_args=value, **context)
|
||||||
|
|
||||||
event_args[key] = value
|
event_args[key] = value
|
||||||
|
@ -132,7 +154,11 @@ class Request(Message):
|
||||||
try:
|
try:
|
||||||
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
exec('{}="{}"'.format(k, re.sub(r'(^|[^\\])"', '\1\\"', v)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.debug('Could not set context variable {}={}: {}'.format(k, v, str(e)))
|
logger.debug(
|
||||||
|
'Could not set context variable {}={}: {}'.format(
|
||||||
|
k, v, str(e)
|
||||||
|
)
|
||||||
|
)
|
||||||
logger.debug('Context: {}'.format(context))
|
logger.debug('Context: {}'.format(context))
|
||||||
|
|
||||||
parsed_value = ''
|
parsed_value = ''
|
||||||
|
@ -152,7 +178,7 @@ class Request(Message):
|
||||||
|
|
||||||
if callable(context_value):
|
if callable(context_value):
|
||||||
context_value = context_value()
|
context_value = context_value()
|
||||||
if isinstance(context_value, range) or isinstance(context_value, tuple):
|
if isinstance(context_value, (range, tuple)):
|
||||||
context_value = [*context_value]
|
context_value = [*context_value]
|
||||||
if isinstance(context_value, datetime.date):
|
if isinstance(context_value, datetime.date):
|
||||||
context_value = context_value.isoformat()
|
context_value = context_value.isoformat()
|
||||||
|
@ -162,7 +188,7 @@ class Request(Message):
|
||||||
|
|
||||||
parsed_value += prefix + (
|
parsed_value += prefix + (
|
||||||
json.dumps(context_value)
|
json.dumps(context_value)
|
||||||
if isinstance(context_value, list) or isinstance(context_value, dict)
|
if isinstance(context_value, (list, dict))
|
||||||
else str(context_value)
|
else str(context_value)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
@ -205,6 +231,9 @@ class Request(Message):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def _thread_func(_n_tries, errors=None):
|
def _thread_func(_n_tries, errors=None):
|
||||||
|
from platypush.context import get_bus
|
||||||
|
from platypush.plugins import RunnablePlugin
|
||||||
|
|
||||||
response = None
|
response = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -221,11 +250,15 @@ class Request(Message):
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
action = self.expand_value_from_context(self.action, **context)
|
action = self.expand_value_from_context(self.action, **context)
|
||||||
(module_name, method_name) = get_module_and_method_from_action(action)
|
(module_name, method_name) = get_module_and_method_from_action(
|
||||||
|
action
|
||||||
|
)
|
||||||
plugin = get_plugin(module_name)
|
plugin = get_plugin(module_name)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.exception(e)
|
logger.exception(e)
|
||||||
msg = 'Uncaught pre-processing exception from action [{}]: {}'.format(self.action, str(e))
|
msg = 'Uncaught pre-processing exception from action [{}]: {}'.format(
|
||||||
|
self.action, str(e)
|
||||||
|
)
|
||||||
logger.warning(msg)
|
logger.warning(msg)
|
||||||
response = Response(output=None, errors=[msg])
|
response = Response(output=None, errors=[msg])
|
||||||
self._send_response(response)
|
self._send_response(response)
|
||||||
|
@ -243,24 +276,36 @@ class Request(Message):
|
||||||
response = plugin.run(method_name, args)
|
response = plugin.run(method_name, args)
|
||||||
|
|
||||||
if not response:
|
if not response:
|
||||||
logger.warning('Received null response from action {}'.format(action))
|
logger.warning(
|
||||||
|
'Received null response from action {}'.format(action)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if response.is_error():
|
if response.is_error():
|
||||||
logger.warning(('Response processed with errors from ' +
|
logger.warning(
|
||||||
'action {}: {}').format(
|
(
|
||||||
action, str(response)))
|
'Response processed with errors from ' + 'action {}: {}'
|
||||||
|
).format(action, str(response))
|
||||||
|
)
|
||||||
elif not response.disable_logging:
|
elif not response.disable_logging:
|
||||||
logger.info('Processed response from action {}: {}'.
|
logger.info(
|
||||||
format(action, str(response)))
|
'Processed response from action {}: {}'.format(
|
||||||
|
action, str(response)
|
||||||
|
)
|
||||||
|
)
|
||||||
except (AssertionError, TimeoutError) as e:
|
except (AssertionError, TimeoutError) as e:
|
||||||
plugin.logger.exception(e)
|
logger.warning(
|
||||||
logger.warning('{} from action [{}]: {}'.format(type(e), action, str(e)))
|
'%s from action [%s]: %s', e.__class__.__name__, action, str(e)
|
||||||
|
)
|
||||||
response = Response(output=None, errors=[str(e)])
|
response = Response(output=None, errors=[str(e)])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# Retry mechanism
|
# Retry mechanism
|
||||||
plugin.logger.exception(e)
|
plugin.logger.exception(e)
|
||||||
logger.warning(('Uncaught exception while processing response ' +
|
logger.warning(
|
||||||
'from action [{}]: {}').format(action, str(e)))
|
(
|
||||||
|
'Uncaught exception while processing response '
|
||||||
|
+ 'from action [{}]: {}'
|
||||||
|
).format(action, str(e))
|
||||||
|
)
|
||||||
|
|
||||||
errors = errors or []
|
errors = errors or []
|
||||||
if str(e) not in errors:
|
if str(e) not in errors:
|
||||||
|
@ -269,16 +314,20 @@ class Request(Message):
|
||||||
response = Response(output=None, errors=errors)
|
response = Response(output=None, errors=errors)
|
||||||
if _n_tries - 1 > 0:
|
if _n_tries - 1 > 0:
|
||||||
logger.info('Reloading plugin {} and retrying'.format(module_name))
|
logger.info('Reloading plugin {} and retrying'.format(module_name))
|
||||||
get_plugin(module_name, reload=True)
|
plugin = get_plugin(module_name, reload=True)
|
||||||
response = _thread_func(_n_tries=_n_tries-1, errors=errors)
|
if isinstance(plugin, RunnablePlugin):
|
||||||
|
plugin.bus = get_bus()
|
||||||
|
plugin.start()
|
||||||
|
|
||||||
|
response = _thread_func(_n_tries=_n_tries - 1, errors=errors)
|
||||||
finally:
|
finally:
|
||||||
self._send_response(response)
|
self._send_response(response)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
token_hash = Config.get('token_hash')
|
stored_token_hash = Config.get('token_hash')
|
||||||
|
token = getattr(self, 'token', '')
|
||||||
if token_hash:
|
if stored_token_hash and get_hash(token) != stored_token_hash:
|
||||||
if self.token is None or get_hash(self.token) != token_hash:
|
|
||||||
raise PermissionError()
|
raise PermissionError()
|
||||||
|
|
||||||
if _async:
|
if _async:
|
||||||
|
@ -292,7 +341,8 @@ class Request(Message):
|
||||||
the message into a UTF-8 JSON string
|
the message into a UTF-8 JSON string
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return json.dumps({
|
return json.dumps(
|
||||||
|
{
|
||||||
'type': 'request',
|
'type': 'request',
|
||||||
'target': self.target,
|
'target': self.target,
|
||||||
'action': self.action,
|
'action': self.action,
|
||||||
|
@ -301,6 +351,8 @@ class Request(Message):
|
||||||
'id': self.id if hasattr(self, 'id') else None,
|
'id': self.id if hasattr(self, 'id') else None,
|
||||||
'token': self.token if hasattr(self, 'token') else None,
|
'token': self.token if hasattr(self, 'token') else None,
|
||||||
'_timestamp': self.timestamp,
|
'_timestamp': self.timestamp,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# vim:sw=4:ts=4:et:
|
# vim:sw=4:ts=4:et:
|
||||||
|
|
|
@ -21,12 +21,12 @@ def action(f):
|
||||||
result = f(*args, **kwargs)
|
result = f(*args, **kwargs)
|
||||||
|
|
||||||
if result and isinstance(result, Response):
|
if result and isinstance(result, Response):
|
||||||
result.errors = result.errors \
|
result.errors = (
|
||||||
if isinstance(result.errors, list) else [result.errors]
|
result.errors if isinstance(result.errors, list) else [result.errors]
|
||||||
|
)
|
||||||
response = result
|
response = result
|
||||||
elif isinstance(result, tuple) and len(result) == 2:
|
elif isinstance(result, tuple) and len(result) == 2:
|
||||||
response.errors = result[1] \
|
response.errors = result[1] if isinstance(result[1], list) else [result[1]]
|
||||||
if isinstance(result[1], list) else [result[1]]
|
|
||||||
|
|
||||||
if len(response.errors) == 1 and response.errors[0] is None:
|
if len(response.errors) == 1 and response.errors[0] is None:
|
||||||
response.errors = []
|
response.errors = []
|
||||||
|
@ -42,11 +42,13 @@ def action(f):
|
||||||
|
|
||||||
|
|
||||||
class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-to-init]
|
class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-to-init]
|
||||||
""" Base plugin class """
|
"""Base plugin class"""
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.logger = logging.getLogger('platypush:plugin:' + get_plugin_name_by_class(self.__class__))
|
self.logger = logging.getLogger(
|
||||||
|
'platypush:plugin:' + get_plugin_name_by_class(self.__class__)
|
||||||
|
)
|
||||||
if 'logging' in kwargs:
|
if 'logging' in kwargs:
|
||||||
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
|
self.logger.setLevel(getattr(logging, kwargs['logging'].upper()))
|
||||||
|
|
||||||
|
@ -55,8 +57,9 @@ class Plugin(EventGenerator, ExtensionWithManifest): # lgtm [py/missing-call-t
|
||||||
)
|
)
|
||||||
|
|
||||||
def run(self, method, *args, **kwargs):
|
def run(self, method, *args, **kwargs):
|
||||||
assert method in self.registered_actions, '{} is not a registered action on {}'.\
|
assert (
|
||||||
format(method, self.__class__.__name__)
|
method in self.registered_actions
|
||||||
|
), '{} is not a registered action on {}'.format(method, self.__class__.__name__)
|
||||||
return getattr(self, method)(*args, **kwargs)
|
return getattr(self, method)(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@ -64,6 +67,7 @@ class RunnablePlugin(Plugin):
|
||||||
"""
|
"""
|
||||||
Class for runnable plugins - i.e. plugins that have a start/stop method and can be started.
|
Class for runnable plugins - i.e. plugins that have a start/stop method and can be started.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, poll_interval: Optional[float] = None, **kwargs):
|
def __init__(self, poll_interval: Optional[float] = None, **kwargs):
|
||||||
"""
|
"""
|
||||||
:param poll_interval: How often the :meth:`.loop` function should be execute (default: None, no pause/interval).
|
:param poll_interval: How often the :meth:`.loop` function should be execute (default: None, no pause/interval).
|
||||||
|
@ -80,6 +84,9 @@ class RunnablePlugin(Plugin):
|
||||||
def should_stop(self):
|
def should_stop(self):
|
||||||
return self._should_stop.is_set()
|
return self._should_stop.is_set()
|
||||||
|
|
||||||
|
def wait_stop(self, timeout=None):
|
||||||
|
return self._should_stop.wait(timeout=timeout)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
set_thread_name(self.__class__.__name__)
|
set_thread_name(self.__class__.__name__)
|
||||||
self._thread = threading.Thread(target=self._runner)
|
self._thread = threading.Thread(target=self._runner)
|
||||||
|
|
1674
platypush/plugins/matrix/__init__.py
Normal file
1674
platypush/plugins/matrix/__init__.py
Normal file
File diff suppressed because it is too large
Load diff
50
platypush/plugins/matrix/manifest.yaml
Normal file
50
platypush/plugins/matrix/manifest.yaml
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
manifest:
|
||||||
|
events:
|
||||||
|
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
|
||||||
|
containing an audio file is received.
|
||||||
|
platypush.message.event.matrix.MatrixMessageVideoEvent: when a message
|
||||||
|
containing a video file is received.
|
||||||
|
platypush.message.event.matrix.MatrixMessageFileEvent: when a message
|
||||||
|
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.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:
|
||||||
|
- libolm
|
||||||
|
pip:
|
||||||
|
- matrix-nio[e2e]
|
||||||
|
- async_lru
|
||||||
|
package: platypush.plugins.matrix
|
||||||
|
type: plugin
|
|
@ -21,7 +21,17 @@ class StrippedString(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
return value.strip()
|
return value.strip()
|
||||||
|
|
||||||
|
|
||||||
class DateTime(fields.Function): # lgtm [py/missing-call-to-init]
|
class Function(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
|
def _get_attr(self, obj, attr: str, _recursive=True):
|
||||||
|
if self.attribute and _recursive:
|
||||||
|
return self._get_attr(obj, self.attribute, False)
|
||||||
|
if hasattr(obj, attr):
|
||||||
|
return getattr(obj, attr)
|
||||||
|
elif hasattr(obj, 'get'):
|
||||||
|
return obj.get(attr)
|
||||||
|
|
||||||
|
|
||||||
|
class DateTime(Function): # lgtm [py/missing-call-to-init]
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.metadata = {
|
self.metadata = {
|
||||||
|
@ -30,7 +40,7 @@ class DateTime(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
}
|
}
|
||||||
|
|
||||||
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
|
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
|
||||||
value = normalize_datetime(obj.get(attr))
|
value = normalize_datetime(self._get_attr(obj, attr))
|
||||||
if value:
|
if value:
|
||||||
return value.isoformat()
|
return value.isoformat()
|
||||||
|
|
||||||
|
@ -38,7 +48,7 @@ class DateTime(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
return normalize_datetime(value)
|
return normalize_datetime(value)
|
||||||
|
|
||||||
|
|
||||||
class Date(fields.Function): # lgtm [py/missing-call-to-init]
|
class Date(Function): # lgtm [py/missing-call-to-init]
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.metadata = {
|
self.metadata = {
|
||||||
|
@ -47,7 +57,7 @@ class Date(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
}
|
}
|
||||||
|
|
||||||
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
|
def _serialize(self, value, attr, obj, **kwargs) -> Optional[str]:
|
||||||
value = normalize_datetime(obj.get(attr))
|
value = normalize_datetime(self._get_attr(obj, attr))
|
||||||
if value:
|
if value:
|
||||||
return date(value.year, value.month, value.day).isoformat()
|
return date(value.year, value.month, value.day).isoformat()
|
||||||
|
|
||||||
|
@ -56,10 +66,12 @@ class Date(fields.Function): # lgtm [py/missing-call-to-init]
|
||||||
return date.fromtimestamp(dt.timestamp())
|
return date.fromtimestamp(dt.timestamp())
|
||||||
|
|
||||||
|
|
||||||
def normalize_datetime(dt: Union[str, date, datetime]) -> Optional[Union[date, datetime]]:
|
def normalize_datetime(
|
||||||
|
dt: Optional[Union[str, date, datetime]]
|
||||||
|
) -> Optional[Union[date, datetime]]:
|
||||||
if not dt:
|
if not dt:
|
||||||
return
|
return
|
||||||
if isinstance(dt, datetime) or isinstance(dt, date):
|
if isinstance(dt, (datetime, date)):
|
||||||
return dt
|
return dt
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
385
platypush/schemas/matrix.py
Normal file
385
platypush/schemas/matrix.py
Normal file
|
@ -0,0 +1,385 @@
|
||||||
|
from marshmallow import fields
|
||||||
|
from marshmallow.schema import Schema
|
||||||
|
|
||||||
|
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):
|
||||||
|
event_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Event ID',
|
||||||
|
'example': '$24KT_aQz6sSKaZH8oTCibRTl62qywDgQXMpz5epXsW5',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomIdSchema(Schema):
|
||||||
|
room_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Room ID',
|
||||||
|
'example': '!aBcDeFgHiJkMnO:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixProfileSchema(Schema):
|
||||||
|
user_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'User ID',
|
||||||
|
'example': '@myuser:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
display_name = fields.String(
|
||||||
|
attribute='displayname',
|
||||||
|
metadata={
|
||||||
|
'description': 'User display name',
|
||||||
|
'example': 'Foo Bar',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
avatar_url = fields.URL(
|
||||||
|
metadata={
|
||||||
|
'description': 'User avatar URL',
|
||||||
|
'example': 'mxc://matrix.example.org/AbCdEfG0123456789',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMemberSchema(MatrixProfileSchema):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['display_name'].attribute = 'display_name'
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixRoomSchema(Schema):
|
||||||
|
room_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'Room ID',
|
||||||
|
'example': '!aBcDeFgHiJkMnO:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
name = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Room name',
|
||||||
|
'example': 'My Room',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
display_name = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Room display name',
|
||||||
|
'example': 'My Room',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
topic = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Room topic',
|
||||||
|
'example': 'My Room Topic',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
avatar_url = fields.URL(
|
||||||
|
attribute='room_avatar_url',
|
||||||
|
metadata={
|
||||||
|
'description': 'Room avatar URL',
|
||||||
|
'example': 'mxc://matrix.example.org/AbCdEfG0123456789',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
owner_id = fields.String(
|
||||||
|
attribute='own_user_id',
|
||||||
|
metadata={
|
||||||
|
'description': 'Owner user ID',
|
||||||
|
'example': '@myuser:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
encrypted = fields.Bool()
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixDeviceSchema(Schema):
|
||||||
|
device_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
attribute='id',
|
||||||
|
metadata={
|
||||||
|
'description': 'ABCDEFG',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
user_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
metadata={
|
||||||
|
'description': 'User ID associated to the device',
|
||||||
|
'example': '@myuser:matrix.example.org',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
display_name = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Display name of the device',
|
||||||
|
'example': 'Element Android',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
blacklisted = fields.Boolean()
|
||||||
|
deleted = fields.Boolean(default=False)
|
||||||
|
ignored = fields.Boolean()
|
||||||
|
verified = fields.Boolean()
|
||||||
|
|
||||||
|
keys = fields.Dict(
|
||||||
|
metadata={
|
||||||
|
'description': 'Encryption keys supported by the device',
|
||||||
|
'example': {
|
||||||
|
'curve25519': 'BtlB0vaQmtYFsvOYkmxyzw9qP5yGjuAyRh4gXh3q',
|
||||||
|
'ed25519': 'atohIK2FeVlYoY8xxpZ1bhDbveD+HA2DswNFqUxP',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixMyDeviceSchema(Schema):
|
||||||
|
device_id = fields.String(
|
||||||
|
required=True,
|
||||||
|
attribute='id',
|
||||||
|
metadata={
|
||||||
|
'description': 'ABCDEFG',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
display_name = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Device display name',
|
||||||
|
'example': 'My Device',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
last_seen_ip = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Last IP associated to this device',
|
||||||
|
'example': '1.2.3.4',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
last_seen_date = DateTime(
|
||||||
|
metadata={
|
||||||
|
'description': 'The last time that the device was reported online',
|
||||||
|
'example': '2022-07-23T17:20:01.254223',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class MatrixDownloadedFileSchema(Schema):
|
||||||
|
url = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Matrix URL of the original resource',
|
||||||
|
'example': 'mxc://matrix.example.org/YhQycHvFOvtiDDbEeWWtEhXx',
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
path = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Local path where the file has been saved',
|
||||||
|
'example': '/home/user/Downloads/image.png',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
content_type = fields.String(
|
||||||
|
metadata={
|
||||||
|
'description': 'Content type of the downloaded file',
|
||||||
|
'example': 'image/png',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
size = fields.Int(
|
||||||
|
metadata={
|
||||||
|
'description': 'Length in bytes of the output file',
|
||||||
|
'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',
|
||||||
|
},
|
||||||
|
)
|
2
setup.py
2
setup.py
|
@ -268,5 +268,7 @@ setup(
|
||||||
'ngrok': ['pyngrok'],
|
'ngrok': ['pyngrok'],
|
||||||
# Support for IRC integration
|
# Support for IRC integration
|
||||||
'irc': ['irc'],
|
'irc': ['irc'],
|
||||||
|
# Support for the Matrix integration
|
||||||
|
'matrix': ['matrix-nio'],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue