matrix-python 1.4.1a0__py3-none-any.whl → 1.4.4a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matrix/__init__.py +2 -0
- matrix/_version.py +2 -2
- matrix/bot.py +93 -28
- matrix/errors.py +4 -0
- matrix/extension.py +14 -1
- matrix/message.py +61 -22
- matrix/protocols.py +1 -1
- matrix/room.py +53 -19
- matrix/types.py +6 -0
- {matrix_python-1.4.1a0.dist-info → matrix_python-1.4.4a0.dist-info}/METADATA +18 -9
- matrix_python-1.4.4a0.dist-info/RECORD +24 -0
- matrix_python-1.4.1a0.dist-info/RECORD +0 -24
- {matrix_python-1.4.1a0.dist-info → matrix_python-1.4.4a0.dist-info}/WHEEL +0 -0
- {matrix_python-1.4.1a0.dist-info → matrix_python-1.4.4a0.dist-info}/top_level.txt +0 -0
matrix/__init__.py
CHANGED
|
@@ -15,6 +15,7 @@ from .command import Command
|
|
|
15
15
|
from .help import HelpCommand
|
|
16
16
|
from .checks import cooldown
|
|
17
17
|
from .room import Room
|
|
18
|
+
from .message import Message
|
|
18
19
|
from .extension import Extension
|
|
19
20
|
|
|
20
21
|
__all__ = [
|
|
@@ -27,5 +28,6 @@ __all__ = [
|
|
|
27
28
|
"HelpCommand",
|
|
28
29
|
"cooldown",
|
|
29
30
|
"Room",
|
|
31
|
+
"Message",
|
|
30
32
|
"Extension",
|
|
31
33
|
]
|
matrix/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.4.
|
|
22
|
-
__version_tuple__ = version_tuple = (1, 4,
|
|
21
|
+
__version__ = version = '1.4.4a0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 4, 4, 'a0')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
matrix/bot.py
CHANGED
|
@@ -3,7 +3,7 @@ import inspect
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
|
-
from typing import
|
|
6
|
+
from typing import Optional, Any
|
|
7
7
|
|
|
8
8
|
from nio import AsyncClient, Event, MatrixRoom
|
|
9
9
|
|
|
@@ -15,7 +15,12 @@ from .extension import Extension
|
|
|
15
15
|
from .registry import Registry
|
|
16
16
|
from .help import HelpCommand, DefaultHelpCommand
|
|
17
17
|
from .scheduler import Scheduler
|
|
18
|
-
from .errors import
|
|
18
|
+
from .errors import (
|
|
19
|
+
AlreadyRegisteredError,
|
|
20
|
+
CommandNotFoundError,
|
|
21
|
+
CheckError,
|
|
22
|
+
RoomNotFoundError,
|
|
23
|
+
)
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
class Bot(Registry):
|
|
@@ -28,29 +33,36 @@ class Bot(Registry):
|
|
|
28
33
|
"""
|
|
29
34
|
|
|
30
35
|
def __init__(
|
|
31
|
-
self,
|
|
36
|
+
self,
|
|
37
|
+
*,
|
|
38
|
+
help_: Optional[HelpCommand] = None,
|
|
32
39
|
) -> None:
|
|
33
|
-
|
|
34
|
-
self.config = config
|
|
35
|
-
elif isinstance(config, str):
|
|
36
|
-
self.config = Config(config_path=config)
|
|
37
|
-
else:
|
|
38
|
-
raise TypeError("config must be a Config instance or a config file path")
|
|
40
|
+
super().__init__(self.__class__.__name__)
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
self._config: Config | None = None
|
|
43
|
+
self._client: AsyncClient | None = None
|
|
44
|
+
self._synced: asyncio.Event = asyncio.Event()
|
|
45
|
+
self._help: HelpCommand | None = help_
|
|
41
46
|
|
|
42
|
-
self.client: AsyncClient = AsyncClient(self.config.homeserver)
|
|
43
47
|
self.extensions: dict[str, Extension] = {}
|
|
44
48
|
self.scheduler: Scheduler = Scheduler()
|
|
45
49
|
self.log: logging.Logger = logging.getLogger(__name__)
|
|
50
|
+
self.start_at: float | None = None
|
|
46
51
|
|
|
47
|
-
|
|
52
|
+
@property
|
|
53
|
+
def client(self) -> AsyncClient:
|
|
54
|
+
assert self._client is not None, "Bot has not been started."
|
|
55
|
+
return self._client
|
|
48
56
|
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
@property
|
|
58
|
+
def config(self) -> Config:
|
|
59
|
+
assert self._config is not None, "Bot has not been started."
|
|
60
|
+
return self._config
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
62
|
+
@property
|
|
63
|
+
def help(self) -> HelpCommand:
|
|
64
|
+
assert self._help is not None, "Bot has not been started."
|
|
65
|
+
return self._help
|
|
54
66
|
|
|
55
67
|
def _auto_register_events(self) -> None:
|
|
56
68
|
for attr in dir(self):
|
|
@@ -70,10 +82,23 @@ class Bot(Registry):
|
|
|
70
82
|
except ValueError:
|
|
71
83
|
continue
|
|
72
84
|
|
|
73
|
-
def get_room(self, room_id: str) -> Room:
|
|
74
|
-
"""Retrieve a Room instance
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
def get_room(self, room_id: str) -> Room | None:
|
|
86
|
+
"""Retrieve a `Room` instance by its Matrix room ID.
|
|
87
|
+
|
|
88
|
+
Returns the `Room` object corresponding to `room_id` if it exists in
|
|
89
|
+
the client's known rooms. Returns `None` if the room cannot be found.
|
|
90
|
+
|
|
91
|
+
## Example
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
room = bot.get_room("!abc123:matrix.org")
|
|
95
|
+
if room:
|
|
96
|
+
print(room.name)
|
|
97
|
+
```
|
|
98
|
+
"""
|
|
99
|
+
if matrix_room := self.client.rooms.get(room_id):
|
|
100
|
+
return Room(matrix_room=matrix_room, client=self.client)
|
|
101
|
+
return None
|
|
77
102
|
|
|
78
103
|
def load_extension(self, extension: Extension) -> None:
|
|
79
104
|
self.log.debug(f"Loading extension: '{extension.name}'")
|
|
@@ -192,7 +217,26 @@ class Bot(Registry):
|
|
|
192
217
|
|
|
193
218
|
# ENTRYPOINT
|
|
194
219
|
|
|
195
|
-
def
|
|
220
|
+
def _load_config(self, config: Config | str) -> None:
|
|
221
|
+
if self._config is not None:
|
|
222
|
+
raise RuntimeError("Config is already loaded.")
|
|
223
|
+
|
|
224
|
+
if isinstance(config, str):
|
|
225
|
+
config = Config(config_path=config)
|
|
226
|
+
elif not isinstance(config, Config):
|
|
227
|
+
raise TypeError("config must be a Config instance or a config file path")
|
|
228
|
+
|
|
229
|
+
self._config = config
|
|
230
|
+
self._client = AsyncClient(config.homeserver)
|
|
231
|
+
self._help = self._help or DefaultHelpCommand()
|
|
232
|
+
|
|
233
|
+
self.prefix = config.prefix
|
|
234
|
+
self.register_command(self.help)
|
|
235
|
+
|
|
236
|
+
self.client.add_event_callback(self._on_matrix_event, Event)
|
|
237
|
+
self._auto_register_events()
|
|
238
|
+
|
|
239
|
+
def start(self, *, config: Config | str) -> None:
|
|
196
240
|
"""
|
|
197
241
|
Synchronous entry point for running the bot.
|
|
198
242
|
|
|
@@ -201,6 +245,9 @@ class Bot(Registry):
|
|
|
201
245
|
:func:`asyncio.run`, and ensures the client is closed gracefully
|
|
202
246
|
on interruption.
|
|
203
247
|
"""
|
|
248
|
+
if config is not None:
|
|
249
|
+
self._load_config(config)
|
|
250
|
+
|
|
204
251
|
try:
|
|
205
252
|
asyncio.run(self.run())
|
|
206
253
|
except KeyboardInterrupt:
|
|
@@ -228,17 +275,26 @@ class Bot(Registry):
|
|
|
228
275
|
login_resp = await self.client.login(self.config.password)
|
|
229
276
|
self.log.info("logged in: %s", login_resp)
|
|
230
277
|
|
|
231
|
-
self.
|
|
278
|
+
sync_task = asyncio.create_task(self.client.sync_forever(timeout=30_000))
|
|
232
279
|
|
|
280
|
+
await self._wait_until_synced()
|
|
233
281
|
await self._on_ready()
|
|
234
|
-
|
|
282
|
+
|
|
283
|
+
self.scheduler.start()
|
|
284
|
+
await sync_task
|
|
285
|
+
|
|
286
|
+
async def _wait_until_synced(self) -> None:
|
|
287
|
+
await self._synced.wait()
|
|
235
288
|
|
|
236
289
|
# MATRIX EVENTS
|
|
237
290
|
|
|
238
|
-
async def on_message(self, room:
|
|
291
|
+
async def on_message(self, room: Room, event: Event) -> None:
|
|
239
292
|
await self._process_commands(room, event)
|
|
240
293
|
|
|
241
|
-
async def _on_matrix_event(self,
|
|
294
|
+
async def _on_matrix_event(self, matrix_room: MatrixRoom, event: Event) -> None:
|
|
295
|
+
if not self._synced.is_set():
|
|
296
|
+
self._synced.set()
|
|
297
|
+
|
|
242
298
|
# ignore bot events
|
|
243
299
|
if event.sender == self.client.user:
|
|
244
300
|
return
|
|
@@ -248,6 +304,11 @@ class Bot(Registry):
|
|
|
248
304
|
return
|
|
249
305
|
|
|
250
306
|
try:
|
|
307
|
+
room = self.get_room(matrix_room.room_id)
|
|
308
|
+
|
|
309
|
+
if not room:
|
|
310
|
+
raise RoomNotFoundError(f"Room '{matrix_room.room_id}' not found.")
|
|
311
|
+
|
|
251
312
|
await self._dispatch_matrix_event(room, event)
|
|
252
313
|
except Exception as error:
|
|
253
314
|
await self._on_error(error)
|
|
@@ -257,14 +318,14 @@ class Bot(Registry):
|
|
|
257
318
|
for handler in self._hook_handlers.get(event_name, []):
|
|
258
319
|
await handler(*args, **kwargs)
|
|
259
320
|
|
|
260
|
-
async def _dispatch_matrix_event(self, room:
|
|
321
|
+
async def _dispatch_matrix_event(self, room: Room, event: Event) -> None:
|
|
261
322
|
"""Fire all listeners registered for a named matrix event."""
|
|
262
323
|
for event_type, funcs in self._event_handlers.items():
|
|
263
324
|
if isinstance(event, event_type):
|
|
264
325
|
for func in funcs:
|
|
265
326
|
await func(room, event)
|
|
266
327
|
|
|
267
|
-
async def _process_commands(self, room:
|
|
328
|
+
async def _process_commands(self, room: Room, event: Event) -> None:
|
|
268
329
|
"""Parse and execute commands"""
|
|
269
330
|
ctx = await self._build_context(room, event)
|
|
270
331
|
|
|
@@ -276,8 +337,12 @@ class Bot(Registry):
|
|
|
276
337
|
await self._on_command(ctx)
|
|
277
338
|
await ctx.command(ctx)
|
|
278
339
|
|
|
279
|
-
async def _build_context(self, matrix_room:
|
|
340
|
+
async def _build_context(self, matrix_room: Room, event: Event) -> Context:
|
|
280
341
|
room = self.get_room(matrix_room.room_id)
|
|
342
|
+
|
|
343
|
+
if not room:
|
|
344
|
+
raise RoomNotFoundError(f"Room '{matrix_room.room_id}' not found.")
|
|
345
|
+
|
|
281
346
|
ctx = Context(bot=self, room=room, event=event)
|
|
282
347
|
prefix = self.prefix or self.config.prefix
|
|
283
348
|
|
matrix/errors.py
CHANGED
matrix/extension.py
CHANGED
|
@@ -17,7 +17,20 @@ class Extension(Registry):
|
|
|
17
17
|
self._on_load: Optional[Callable] = None
|
|
18
18
|
self._on_unload: Optional[Callable] = None
|
|
19
19
|
|
|
20
|
-
def get_room(self, room_id: str) -> Room:
|
|
20
|
+
def get_room(self, room_id: str) -> Room | None:
|
|
21
|
+
"""Retrieve a `Room` instance by its Matrix room ID.
|
|
22
|
+
|
|
23
|
+
Returns the `Room` object corresponding to `room_id` if it exists in
|
|
24
|
+
the client's known rooms. Returns `None` if the room cannot be found.
|
|
25
|
+
|
|
26
|
+
## Example
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
room = extension.get_room("!abc123:matrix.org")
|
|
30
|
+
if room:
|
|
31
|
+
print(room.name)
|
|
32
|
+
```
|
|
33
|
+
"""
|
|
21
34
|
if self.bot is None:
|
|
22
35
|
raise RuntimeError("Extension is not loaded")
|
|
23
36
|
return self.bot.get_room(room_id)
|
matrix/message.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING
|
|
2
|
-
|
|
1
|
+
from typing import TYPE_CHECKING, Self
|
|
2
|
+
|
|
3
|
+
from nio import AsyncClient, Event
|
|
4
|
+
|
|
5
|
+
from matrix.types import Reaction
|
|
3
6
|
from matrix.content import ReactionContent, EditContent
|
|
4
7
|
from matrix.errors import MatrixError
|
|
5
8
|
|
|
@@ -10,28 +13,35 @@ if TYPE_CHECKING:
|
|
|
10
13
|
class Message:
|
|
11
14
|
"""Represents a Matrix message with methods to interact with it."""
|
|
12
15
|
|
|
13
|
-
def __init__(
|
|
14
|
-
self, *, room: "Room", event_id: str, body: str | None, client: AsyncClient
|
|
15
|
-
) -> None:
|
|
16
|
+
def __init__(self, *, room: "Room", event: Event, client: AsyncClient) -> None:
|
|
16
17
|
self._room = room
|
|
17
|
-
self.
|
|
18
|
-
self._body = body
|
|
18
|
+
self._matrix_event: Event = event
|
|
19
19
|
self._client = client
|
|
20
20
|
|
|
21
|
+
self._body = getattr(self._matrix_event, "body", None)
|
|
22
|
+
|
|
23
|
+
def __repr__(self) -> str:
|
|
24
|
+
return f"<Message id={self.event_id!r} body={self.body!r}>"
|
|
25
|
+
|
|
21
26
|
@property
|
|
22
27
|
def room(self) -> "Room":
|
|
23
28
|
"""The room this message was sent in."""
|
|
24
29
|
return self._room
|
|
25
30
|
|
|
26
31
|
@property
|
|
27
|
-
def
|
|
28
|
-
"""The event
|
|
29
|
-
return self.
|
|
32
|
+
def event(self) -> Event:
|
|
33
|
+
"""The matrix event of this message"""
|
|
34
|
+
return self._matrix_event
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def client(self) -> AsyncClient:
|
|
38
|
+
"""The Matrix client."""
|
|
39
|
+
return self._client
|
|
30
40
|
|
|
31
41
|
@property
|
|
32
42
|
def event_id(self) -> str:
|
|
33
|
-
"""The event ID of this message
|
|
34
|
-
return self.
|
|
43
|
+
"""The event ID of this message."""
|
|
44
|
+
return str(self._matrix_event.event_id)
|
|
35
45
|
|
|
36
46
|
@property
|
|
37
47
|
def body(self) -> str | None:
|
|
@@ -39,9 +49,41 @@ class Message:
|
|
|
39
49
|
return self._body
|
|
40
50
|
|
|
41
51
|
@property
|
|
42
|
-
def
|
|
43
|
-
"""The
|
|
44
|
-
return self.
|
|
52
|
+
def key(self) -> str | None:
|
|
53
|
+
"""The key of this message."""
|
|
54
|
+
return getattr(self._matrix_event, "key", None)
|
|
55
|
+
|
|
56
|
+
async def fetch_reactions(self) -> list[Reaction]:
|
|
57
|
+
"""Fetch all reactions for this message.
|
|
58
|
+
|
|
59
|
+
Returns a dict mapping emoji to a list of sender IDs who reacted with it.
|
|
60
|
+
|
|
61
|
+
## Example
|
|
62
|
+
```python
|
|
63
|
+
@bot.command()
|
|
64
|
+
async def reactions(ctx: Context):
|
|
65
|
+
reactions = await ctx.message.fetch_reactions()
|
|
66
|
+
|
|
67
|
+
for emoji, senders in reactions.items():
|
|
68
|
+
await ctx.reply(f"{emoji}: {len(senders)} reaction(s)")
|
|
69
|
+
```
|
|
70
|
+
"""
|
|
71
|
+
raw: dict[str, list[str]] = {}
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
async for event in self.client.room_get_event_relations(
|
|
75
|
+
room_id=self.room.room_id,
|
|
76
|
+
event_id=self.event_id,
|
|
77
|
+
):
|
|
78
|
+
emoji = getattr(event, "key", None)
|
|
79
|
+
sender = getattr(event, "sender", None)
|
|
80
|
+
|
|
81
|
+
if emoji and sender:
|
|
82
|
+
raw.setdefault(emoji, []).append(sender)
|
|
83
|
+
except Exception as e:
|
|
84
|
+
raise MatrixError(f"Failed to fetch reactions: {e}")
|
|
85
|
+
|
|
86
|
+
return [Reaction(key=emoji, senders=senders) for emoji, senders in raw.items()]
|
|
45
87
|
|
|
46
88
|
async def reply(self, body: str) -> "Message":
|
|
47
89
|
"""Reply to this message.
|
|
@@ -57,7 +99,7 @@ class Message:
|
|
|
57
99
|
```
|
|
58
100
|
"""
|
|
59
101
|
try:
|
|
60
|
-
return await self.room.send_text(content=body, reply_to=self.
|
|
102
|
+
return await self.room.send_text(content=body, reply_to=self.event_id)
|
|
61
103
|
except Exception as e:
|
|
62
104
|
raise MatrixError(f"Failed to send reply: {e}")
|
|
63
105
|
|
|
@@ -72,7 +114,7 @@ class Message:
|
|
|
72
114
|
await msg.react("👍")
|
|
73
115
|
```
|
|
74
116
|
"""
|
|
75
|
-
content = ReactionContent(event_id=self.
|
|
117
|
+
content = ReactionContent(event_id=self.event_id, emoji=emoji)
|
|
76
118
|
|
|
77
119
|
try:
|
|
78
120
|
await self.client.room_send(
|
|
@@ -95,7 +137,7 @@ class Message:
|
|
|
95
137
|
await msg.edit("Hello world!")
|
|
96
138
|
```
|
|
97
139
|
"""
|
|
98
|
-
content = EditContent(new_body, original_event_id=self.
|
|
140
|
+
content = EditContent(new_body, original_event_id=self.event_id)
|
|
99
141
|
|
|
100
142
|
try:
|
|
101
143
|
await self.client.room_send(
|
|
@@ -122,10 +164,7 @@ class Message:
|
|
|
122
164
|
try:
|
|
123
165
|
await self.client.room_redact(
|
|
124
166
|
room_id=self.room.room_id,
|
|
125
|
-
event_id=self.
|
|
167
|
+
event_id=self.event_id,
|
|
126
168
|
)
|
|
127
169
|
except Exception as e:
|
|
128
170
|
raise MatrixError(f"Failed to delete message: {e}")
|
|
129
|
-
|
|
130
|
-
def __repr__(self) -> str:
|
|
131
|
-
return f"<Message id={self.id!r} body={self.body!r}>"
|
matrix/protocols.py
CHANGED
matrix/room.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from nio import AsyncClient, MatrixRoom
|
|
3
|
+
from nio import AsyncClient, MatrixRoom, Event
|
|
4
4
|
|
|
5
5
|
from matrix.errors import MatrixError
|
|
6
6
|
from matrix.message import Message
|
|
@@ -25,6 +25,22 @@ class Room:
|
|
|
25
25
|
self._matrix_room: MatrixRoom = matrix_room
|
|
26
26
|
self._client: AsyncClient = client
|
|
27
27
|
|
|
28
|
+
def __getattr__(self, name: str) -> Any:
|
|
29
|
+
"""
|
|
30
|
+
Fallback to MatrixRoom for attributes not explicitly defined.
|
|
31
|
+
|
|
32
|
+
This allows access to any MatrixRoom attribute not wrapped by this class.
|
|
33
|
+
See matrix-nio's MatrixRoom documentation for available attributes.
|
|
34
|
+
|
|
35
|
+
https://matrix-nio.readthedocs.io/en/latest/nio.html#nio.rooms.MatrixRoom
|
|
36
|
+
"""
|
|
37
|
+
try:
|
|
38
|
+
return getattr(self._matrix_room, name)
|
|
39
|
+
except AttributeError:
|
|
40
|
+
raise AttributeError(
|
|
41
|
+
f"'{type(self).__name__}' object has no attribute '{name}'"
|
|
42
|
+
) from None
|
|
43
|
+
|
|
28
44
|
@property
|
|
29
45
|
def matrix_room(self) -> MatrixRoom:
|
|
30
46
|
"""Access to underlying MatrixRoom object."""
|
|
@@ -65,22 +81,6 @@ class Room:
|
|
|
65
81
|
"""Whether the room is encrypted."""
|
|
66
82
|
return self._matrix_room.encrypted # type: ignore[no-any-return]
|
|
67
83
|
|
|
68
|
-
def __getattr__(self, name: str) -> Any:
|
|
69
|
-
"""
|
|
70
|
-
Fallback to MatrixRoom for attributes not explicitly defined.
|
|
71
|
-
|
|
72
|
-
This allows access to any MatrixRoom attribute not wrapped by this class.
|
|
73
|
-
See matrix-nio's MatrixRoom documentation for available attributes.
|
|
74
|
-
|
|
75
|
-
https://matrix-nio.readthedocs.io/en/latest/nio.html#nio.rooms.MatrixRoom
|
|
76
|
-
"""
|
|
77
|
-
try:
|
|
78
|
-
return getattr(self._matrix_room, name)
|
|
79
|
-
except AttributeError:
|
|
80
|
-
raise AttributeError(
|
|
81
|
-
f"'{type(self).__name__}' object has no attribute '{name}'"
|
|
82
|
-
) from None
|
|
83
|
-
|
|
84
84
|
async def send(
|
|
85
85
|
self,
|
|
86
86
|
content: str | None = None,
|
|
@@ -319,16 +319,50 @@ class Room:
|
|
|
319
319
|
message_type="m.room.message",
|
|
320
320
|
content=payload.build(),
|
|
321
321
|
)
|
|
322
|
+
event = await self.fetch_event(resp.event_id)
|
|
322
323
|
|
|
323
324
|
return Message(
|
|
324
325
|
room=self,
|
|
325
|
-
|
|
326
|
-
body=getattr(payload, "body", None),
|
|
326
|
+
event=event,
|
|
327
327
|
client=self.client,
|
|
328
328
|
)
|
|
329
329
|
except Exception as e:
|
|
330
330
|
raise MatrixError(f"Failed to send message: {e}")
|
|
331
331
|
|
|
332
|
+
async def fetch_event(self, event_id: str) -> Event:
|
|
333
|
+
"""Fetch a Matrix event by its ID.
|
|
334
|
+
|
|
335
|
+
## Example
|
|
336
|
+
```python
|
|
337
|
+
event = await room.fetch_event("$event_id:matrix.org")
|
|
338
|
+
print(event.sender)
|
|
339
|
+
```
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
response = await self.client.room_get_event(
|
|
343
|
+
room_id=self.room_id,
|
|
344
|
+
event_id=event_id,
|
|
345
|
+
)
|
|
346
|
+
return response.event
|
|
347
|
+
except Exception as e:
|
|
348
|
+
raise MatrixError(f"Failed to get event: {e}")
|
|
349
|
+
|
|
350
|
+
async def fetch_message(self, event_id: str) -> Message:
|
|
351
|
+
"""Fetch a Message by its event ID.
|
|
352
|
+
|
|
353
|
+
## Example
|
|
354
|
+
```python
|
|
355
|
+
message = await room.fetch_message("$event_id:matrix.org")
|
|
356
|
+
message.reply("hello world")
|
|
357
|
+
```
|
|
358
|
+
"""
|
|
359
|
+
event = await self.fetch_event(event_id)
|
|
360
|
+
return Message(
|
|
361
|
+
room=self,
|
|
362
|
+
event=event,
|
|
363
|
+
client=self.client,
|
|
364
|
+
)
|
|
365
|
+
|
|
332
366
|
async def invite_user(self, user_id: str) -> None:
|
|
333
367
|
"""Invite a user to the room.
|
|
334
368
|
|
matrix/types.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: matrix-python
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.4a0
|
|
4
4
|
Summary: An easy-to-use Matrix bot framework designed for quick development and minimal setup
|
|
5
5
|
Author: Simon Roy, Chris Dedman Rollet
|
|
6
6
|
Maintainer-email: Code Society Lab <admin@codesociety.xyz>
|
|
@@ -718,6 +718,7 @@ decorator-based API similar to popular event-driven frameworks, allowing
|
|
|
718
718
|
developers to focus on behavior rather than boilerplate.
|
|
719
719
|
|
|
720
720
|
#### Key Features
|
|
721
|
+
|
|
721
722
|
- Minimal setup, easy to extend
|
|
722
723
|
- Event-driven API using async/await
|
|
723
724
|
- Clean command registration
|
|
@@ -727,6 +728,7 @@ developers to focus on behavior rather than boilerplate.
|
|
|
727
728
|
# Quickstart
|
|
728
729
|
|
|
729
730
|
**Requirements**
|
|
731
|
+
|
|
730
732
|
- Python 3.10+
|
|
731
733
|
|
|
732
734
|
```
|
|
@@ -734,38 +736,45 @@ pip install matrix-python
|
|
|
734
736
|
```
|
|
735
737
|
|
|
736
738
|
If you plan on contributing to matrix.py, we recommend to install the development libraries:
|
|
739
|
+
|
|
737
740
|
```
|
|
738
741
|
pip install -e .[dev]
|
|
739
742
|
```
|
|
740
743
|
|
|
741
|
-
*Note*: It is recommended to use
|
|
742
|
-
|
|
744
|
+
*Note*: It is recommended to use
|
|
745
|
+
a [virtual environment](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/)
|
|
746
|
+
when installing python packages.
|
|
743
747
|
|
|
744
748
|
```python
|
|
745
749
|
from matrix import Bot, Context
|
|
746
750
|
|
|
747
|
-
bot = Bot(
|
|
751
|
+
bot = Bot()
|
|
748
752
|
|
|
749
753
|
|
|
750
754
|
@bot.command("ping")
|
|
751
755
|
async def ping(ctx: Context):
|
|
752
|
-
await ctx.
|
|
756
|
+
await ctx.reply("Pong!")
|
|
753
757
|
|
|
754
758
|
|
|
755
|
-
bot.start()
|
|
759
|
+
bot.start(config="config.yml")
|
|
756
760
|
```
|
|
757
761
|
|
|
758
762
|
[Documentation](https://github.com/Code-Society-Lab/matrixpy/wiki) - [Examples](https://github.com/Code-Society-Lab/matrixpy/tree/main/examples)
|
|
759
763
|
|
|
760
764
|
# Contributing
|
|
761
|
-
|
|
765
|
+
|
|
766
|
+
We welcome everyone to contribute!
|
|
762
767
|
|
|
763
768
|
Whether it's fixing bugs, suggesting features, or improving the docs - every bit helps.
|
|
769
|
+
|
|
764
770
|
- Submit an issue
|
|
765
771
|
- Open a pull request
|
|
766
|
-
- Or just hop into our [Matrix](https://matrix.to/#/%23codesociety:matrix.org)
|
|
772
|
+
- Or just hop into our [Matrix](https://matrix.to/#/%23codesociety:matrix.org)
|
|
773
|
+
or [Discord](https://discord.gg/code-society-823178343943897088) server and say hi!
|
|
767
774
|
|
|
768
|
-
If you intend to contribute, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) first. Additionally, **every
|
|
775
|
+
If you intend to contribute, please read the [CONTRIBUTING.md](./CONTRIBUTING.md) first. Additionally, **every
|
|
776
|
+
contributor** is expected to follow the [code of conduct](./CODE_OF_CONDUCT.md).
|
|
769
777
|
|
|
770
778
|
# License
|
|
779
|
+
|
|
771
780
|
matrix.py is released under [GPL-3.0](https://opensource.org/license/gpl-3-0)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
matrix/__init__.py,sha256=g8yEFjELnnwlvOKns-Ug6LgOezkjAFZ-Opt7esbBHKg,728
|
|
2
|
+
matrix/_version.py,sha256=tcfrwnvkUDHHz8JoKK522ystGqqHOEnmR8F4E8SoWsI,528
|
|
3
|
+
matrix/bot.py,sha256=4gwL2vawLFEREwZD4cEydv6XSXm_SP6pztLD8uwB86M,11944
|
|
4
|
+
matrix/checks.py,sha256=F_7432_OcFO-im4fRAj62MUsyv1mXywT4OsGC_7xbBQ,486
|
|
5
|
+
matrix/command.py,sha256=GrP3WsT07sKehGX7PHfnT7gRX22d99877VPd0X2ViEw,10514
|
|
6
|
+
matrix/config.py,sha256=wPLfcHGpSapkBqbZIuI7zBdxh52OHTD8cQS0WqQkMeU,2388
|
|
7
|
+
matrix/content.py,sha256=z5_E2rTvHsODE52OiDkhDHNQAryx5NLhyHjBb65Xe-U,3853
|
|
8
|
+
matrix/context.py,sha256=-CbxY-LtK9-jgHERhvJH73B3SpO-Uk5ty0j1TMKfzuI,4032
|
|
9
|
+
matrix/errors.py,sha256=HKGb5NUeFuZvieXgpLlVSmUxK4jpA0ODuiPQqQlbQTE,1676
|
|
10
|
+
matrix/extension.py,sha256=RbCx58CdRXF8kGUgS-ec1aZdd-K5hQedhCCQ0-YR4Vg,2272
|
|
11
|
+
matrix/group.py,sha256=TRIX7PE3lcB2ZWu3xY2W2OAmE_a8-i2zHNBYnX5uj28,3691
|
|
12
|
+
matrix/message.py,sha256=w6pu86goylxdrX5fgXPUMB_tW0bOFIk6tKy6qkXTjl4,5136
|
|
13
|
+
matrix/protocols.py,sha256=nFb4tLanwtrKWoIhZ96xMwXPjD3RF5ITca_yXtakXC4,166
|
|
14
|
+
matrix/registry.py,sha256=jxXg_f_pVl6nuwLdVOIjWJ9Yl9hDJSLwoChnnr3ztNs,12753
|
|
15
|
+
matrix/room.py,sha256=PBuMWQo8mKy2d2XIeMbBlVBTnnqZjOPPGpKLp4K1AVM,14038
|
|
16
|
+
matrix/scheduler.py,sha256=EXsL9i8IDXhcpdW8lti0BR5XcIgkmud4iwOPaqcE9Gw,1727
|
|
17
|
+
matrix/types.py,sha256=UFjC7p8RAf7piEPvp2X3NuWdqBwkM9Yc3He7KWb9icc,384
|
|
18
|
+
matrix/help/__init__.py,sha256=1u7V7T_-VgYDeQCTXsc4y8Fo-8gJhOqYJq2U3cUjMWg,168
|
|
19
|
+
matrix/help/help_command.py,sha256=xCLmKklw74LEMjbUfgQR9eaPMFvi3sPtDw2n2pnAnVQ,12800
|
|
20
|
+
matrix/help/pagination.py,sha256=sJk0wC46sFHf7xl7WsGRAUc4FC7b9hPqmwQDmvcjwgM,2717
|
|
21
|
+
matrix_python-1.4.4a0.dist-info/METADATA,sha256=zswqUmzf_FyaRISg-p2Rqxqocwb9vwHT7Ldr51d4K5A,44541
|
|
22
|
+
matrix_python-1.4.4a0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
23
|
+
matrix_python-1.4.4a0.dist-info/top_level.txt,sha256=BvHVM9c7-5SLzg-1OCRpHKgqAubWhRN1e38e6coHs-g,7
|
|
24
|
+
matrix_python-1.4.4a0.dist-info/RECORD,,
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
matrix/__init__.py,sha256=Wo5-bisd2CLwH5p48d2AwqaCeYtGi78GWeMkvJRuaTk,684
|
|
2
|
-
matrix/_version.py,sha256=hLbk0OM_vBZ8bjp25JufFvnWCZ7E5n03qPCWDS3CuPc,528
|
|
3
|
-
matrix/bot.py,sha256=FqM4uRGFk7lEgJXgmOFDUdmuEb-Oa8CYzJh1X7rjebk,10258
|
|
4
|
-
matrix/checks.py,sha256=F_7432_OcFO-im4fRAj62MUsyv1mXywT4OsGC_7xbBQ,486
|
|
5
|
-
matrix/command.py,sha256=GrP3WsT07sKehGX7PHfnT7gRX22d99877VPd0X2ViEw,10514
|
|
6
|
-
matrix/config.py,sha256=wPLfcHGpSapkBqbZIuI7zBdxh52OHTD8cQS0WqQkMeU,2388
|
|
7
|
-
matrix/content.py,sha256=z5_E2rTvHsODE52OiDkhDHNQAryx5NLhyHjBb65Xe-U,3853
|
|
8
|
-
matrix/context.py,sha256=-CbxY-LtK9-jgHERhvJH73B3SpO-Uk5ty0j1TMKfzuI,4032
|
|
9
|
-
matrix/errors.py,sha256=_9WtZ2D0RxYCQTwItImUEjfD1qjvkQWPn5ZG3VUsUn4,1627
|
|
10
|
-
matrix/extension.py,sha256=7zBTlqVC-9F8RY60Loo65YgdVoJa5QDFeiz8nHjuebo,1883
|
|
11
|
-
matrix/group.py,sha256=TRIX7PE3lcB2ZWu3xY2W2OAmE_a8-i2zHNBYnX5uj28,3691
|
|
12
|
-
matrix/message.py,sha256=7L9M354VM0KWmzL0032-Nq_2wBrSvbqy7KixMXocP-Q,3752
|
|
13
|
-
matrix/protocols.py,sha256=UC7F8T2pPffBMf_QiANXtga50rznSfUsJ7pxpKhQGPQ,159
|
|
14
|
-
matrix/registry.py,sha256=jxXg_f_pVl6nuwLdVOIjWJ9Yl9hDJSLwoChnnr3ztNs,12753
|
|
15
|
-
matrix/room.py,sha256=uNcUrqHhHK98fCxnVZUwLk1nR-4No0Wlx-RG-J9_lQA,13051
|
|
16
|
-
matrix/scheduler.py,sha256=EXsL9i8IDXhcpdW8lti0BR5XcIgkmud4iwOPaqcE9Gw,1727
|
|
17
|
-
matrix/types.py,sha256=FHnSYmf5yB99kR9ZCeXLAqNcbWkt0zG75s_TD2pIp2Q,319
|
|
18
|
-
matrix/help/__init__.py,sha256=1u7V7T_-VgYDeQCTXsc4y8Fo-8gJhOqYJq2U3cUjMWg,168
|
|
19
|
-
matrix/help/help_command.py,sha256=xCLmKklw74LEMjbUfgQR9eaPMFvi3sPtDw2n2pnAnVQ,12800
|
|
20
|
-
matrix/help/pagination.py,sha256=sJk0wC46sFHf7xl7WsGRAUc4FC7b9hPqmwQDmvcjwgM,2717
|
|
21
|
-
matrix_python-1.4.1a0.dist-info/METADATA,sha256=DOdiTiUth4zsbzKq_PtFR-L6CYFaCdtbkSq5VVED0Uw,44534
|
|
22
|
-
matrix_python-1.4.1a0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
23
|
-
matrix_python-1.4.1a0.dist-info/top_level.txt,sha256=BvHVM9c7-5SLzg-1OCRpHKgqAubWhRN1e38e6coHs-g,7
|
|
24
|
-
matrix_python-1.4.1a0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|