ErisPulse-MatrixAdapter 4.2.0__tar.gz → 4.4.0__tar.gz
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.
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0/ErisPulse_MatrixAdapter.egg-info}/PKG-INFO +107 -12
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/SOURCES.txt +5 -1
- erispulse_matrixadapter-4.4.0/ErisPulse_MatrixAdapter.egg-info/requires.txt +1 -0
- erispulse_matrixadapter-4.4.0/LICENSE +21 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/Converter.py +52 -36
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/Core.py +389 -26
- erispulse_matrixadapter-4.4.0/MatrixAdapter/NioSession.py +278 -0
- erispulse_matrixadapter-4.2.0/README.md → erispulse_matrixadapter-4.4.0/PKG-INFO +113 -9
- erispulse_matrixadapter-4.2.0/ErisPulse_MatrixAdapter.egg-info/PKG-INFO → erispulse_matrixadapter-4.4.0/README.md +79 -18
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/pyproject.toml +8 -4
- erispulse_matrixadapter-4.4.0/test/test_e2ee.py +418 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/dependency_links.txt +0 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/entry_points.txt +0 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/ErisPulse_MatrixAdapter.egg-info/top_level.txt +0 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/MatrixAdapter/__init__.py +0 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/setup.cfg +0 -0
- {erispulse_matrixadapter-4.2.0 → erispulse_matrixadapter-4.4.0}/test/test.py +0 -0
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse-MatrixAdapter
|
|
3
|
-
Version: 4.
|
|
4
|
-
Summary: ErisPulse
|
|
3
|
+
Version: 4.4.0
|
|
4
|
+
Summary: ErisPulse Matrix protocol adapter with native end-to-end encryption support
|
|
5
5
|
Author-email: wsu2059 <wsu2059@qq.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 ErisPulse
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
6
28
|
Project-URL: homepage, https://github.com/ErisPulse/ErisPulse-MatrixAdapter
|
|
7
|
-
Requires-Python: >=3.
|
|
29
|
+
Requires-Python: >=3.10
|
|
8
30
|
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Requires-Dist: matrix-nio[e2e]>=0.26.0
|
|
33
|
+
Dynamic: license-file
|
|
9
34
|
|
|
10
35
|
<div align="center">
|
|
11
36
|
|
|
@@ -153,6 +178,41 @@ enabled = true
|
|
|
153
178
|
|
|
154
179
|
---
|
|
155
180
|
|
|
181
|
+
### End-to-End Encryption (E2EE)
|
|
182
|
+
|
|
183
|
+
The adapter natively supports end-to-end encryption for encrypted rooms (based on [matrix-nio](https://github.com/matrix-nio/matrix-nio), powered by [vodozemac](https://pypi.org/project/vodozemac/)): messages and media in encrypted rooms are automatically decrypted on receive and encrypted on send — no changes required in your modules.
|
|
184
|
+
|
|
185
|
+
```toml
|
|
186
|
+
[Matrix_Adapter.accounts.default]
|
|
187
|
+
homeserver = "https://matrix.org"
|
|
188
|
+
user_id = "@bot:matrix.org"
|
|
189
|
+
password = "YOUR_PASSWORD"
|
|
190
|
+
encryption_enabled = true # Enable E2EE (default false)
|
|
191
|
+
trust_all_devices = true # Auto-trust unverified devices (default true)
|
|
192
|
+
# device_id = "XXXXXXXXXX" # Optional: specify manually only if the server doesn't report one
|
|
193
|
+
# store_path = "data/matrix/default" # Optional: crypto session store directory
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Encryption fields (per account):**
|
|
197
|
+
- `encryption_enabled`: Enable E2EE, defaults to `false` (existing plaintext deployments are unaffected)
|
|
198
|
+
- `trust_all_devices`: Automatically trust unverified devices, defaults to `true` (common bot practice). Set to `false` for strict mode — unverified devices will not receive room keys for new messages
|
|
199
|
+
- `device_id`: Device ID, auto-resolved in most cases (login response → `/whoami` → persisted). Manual specification is only needed on servers that don't report it
|
|
200
|
+
- `store_path`: Crypto session store directory, defaults to `data/matrix/<account_name>`
|
|
201
|
+
|
|
202
|
+
**How it works:**
|
|
203
|
+
- The `device_id` is resolved on login and persisted (in `store_path/device.json`); password re-login reuses the same device to avoid device pile-up
|
|
204
|
+
- Sync runs through the E2EE session: device key upload, one-time key replenishment, to-device messages and Megolm decryption are handled automatically
|
|
205
|
+
- Sending to an encrypted room automatically shares the Megolm session and encrypts the payload; media uploads in encrypted rooms are encrypted end-to-end (`m.file.encrypted`)
|
|
206
|
+
- Modules can call `event.is_encrypted()` to check whether a message came from an encrypted room; encrypted media segments carry `matrix_encrypted_file` (JWK), and ciphertext can be decrypted with `MatrixAdapter.decrypt_media(bytes, key, iv, sha256)`
|
|
207
|
+
|
|
208
|
+
**Important notes:**
|
|
209
|
+
1. Requires Python >= 3.10 and `matrix-nio[e2e] >= 0.26` (installed automatically as a dependency)
|
|
210
|
+
2. Cross-signing and server-side key backups are not supported; historical messages cannot be decrypted if the local store directory (`store_path`) is deleted — do not delete it
|
|
211
|
+
3. Interactive SAS (emoji) verification is planned for a future release; the current trust model is "trust all devices" by default
|
|
212
|
+
4. Events that fail to decryption are skipped with a warning logged
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
156
216
|
### Matrix-Specific Features
|
|
157
217
|
|
|
158
218
|
See the [Matrix platform features documentation](platform-features.md) for platform-specific capabilities, including the decentralized architecture, room concepts, Long Polling sync, MXC URIs, HTML rich text, reactions, message editing, and extension field descriptions.
|
|
@@ -221,6 +281,7 @@ async def handle_message(event):
|
|
|
221
281
|
sender = event.get_matrix_sender() # Get the sender ID
|
|
222
282
|
is_edited = event.is_edited() # Whether the message is an edit
|
|
223
283
|
is_notice = event.is_notice() # Whether it is an m.notice type
|
|
284
|
+
is_encrypted = event.is_encrypted() # Whether it came from an E2EE room
|
|
224
285
|
```
|
|
225
286
|
|
|
226
287
|
### Notes
|
|
@@ -234,6 +295,7 @@ async def handle_message(event):
|
|
|
234
295
|
7. Auto-accepting room invites is supported (can be disabled via the `auto_accept_invites` config).
|
|
235
296
|
8. HTML-formatted messages are supported via the `.Html()` method.
|
|
236
297
|
9. Message replies (`.Reply()`) and user mentions (`.At()`, `.AtAll()`) are supported.
|
|
298
|
+
10. With end-to-end encryption enabled, do not delete the `store_path` directory (see the "End-to-End Encryption" section).
|
|
237
299
|
|
|
238
300
|
---
|
|
239
301
|
|
|
@@ -368,6 +430,41 @@ enabled = true
|
|
|
368
430
|
|
|
369
431
|
---
|
|
370
432
|
|
|
433
|
+
## 端到端加密(E2EE)
|
|
434
|
+
|
|
435
|
+
适配器原生支持加密房间的端到端加密(基于 [matrix-nio](https://github.com/matrix-nio/matrix-nio),底层使用 [vodozemac](https://pypi.org/project/vodozemac/)):加密房间的消息与媒体在接收时自动解密、发送时自动加密,模块无需任何改动。
|
|
436
|
+
|
|
437
|
+
```toml
|
|
438
|
+
[Matrix_Adapter.accounts.default]
|
|
439
|
+
homeserver = "https://matrix.org"
|
|
440
|
+
user_id = "@bot:matrix.org"
|
|
441
|
+
password = "YOUR_PASSWORD"
|
|
442
|
+
encryption_enabled = true # 启用端到端加密(默认 false)
|
|
443
|
+
trust_all_devices = true # 自动信任未验证设备(默认 true)
|
|
444
|
+
# device_id = "XXXXXXXXXX" # 可选:仅在服务器不返回时手动指定
|
|
445
|
+
# store_path = "data/matrix/default" # 可选:加密会话存储目录
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
**加密相关配置(每个账户):**
|
|
449
|
+
- `encryption_enabled`:启用端到端加密,默认 `false`(存量明文部署不受影响)
|
|
450
|
+
- `trust_all_devices`:自动信任未验证设备,默认 `true`(bot 场景惯例);设为 `false` 进入严格模式——未验证设备将无法解密新消息
|
|
451
|
+
- `device_id`:设备 ID,多数情况自动获取(登录响应 → `/whoami` → 本地持久化),仅服务器不返回时需手动指定
|
|
452
|
+
- `store_path`:加密会话存储目录,默认 `data/matrix/<账户名>`
|
|
453
|
+
|
|
454
|
+
**工作原理:**
|
|
455
|
+
- 登录时解析并持久化 `device_id`(`store_path/device.json`),密码重登会复用同一设备,避免设备堆积
|
|
456
|
+
- 同步走 E2EE 会话:设备密钥上传、一次性密钥补充、to-device 消息、Megolm 解密全部自动完成
|
|
457
|
+
- 向加密房间发送消息时自动分享 Megolm 会话并加密载荷;加密房间的媒体上传为端到端加密(`m.file.encrypted`)
|
|
458
|
+
- 模块可通过 `event.is_encrypted()` 判断消息是否来自加密房间;加密媒体段携带 `matrix_encrypted_file`(JWK),可配合 `MatrixAdapter.decrypt_media(bytes, key, iv, sha256)` 解密密文
|
|
459
|
+
|
|
460
|
+
**注意事项:**
|
|
461
|
+
1. 需要 Python >= 3.10 与 `matrix-nio[e2e] >= 0.26`(已作为依赖自动安装)
|
|
462
|
+
2. 不支持交叉签名与服务器端密钥备份;本地会话目录(`store_path`)删除后历史消息将无法解密,请勿删除
|
|
463
|
+
3. 交互式 SAS(emoji)验证计划在后续版本提供,当前默认信任策略为"自动信任所有设备"
|
|
464
|
+
4. 解密失败的事件会被跳过并记录警告日志
|
|
465
|
+
|
|
466
|
+
---
|
|
467
|
+
|
|
371
468
|
## Matrix平台特有功能
|
|
372
469
|
|
|
373
470
|
请参考 [Matrix平台特性文档](platform-features.md) 了解Matrix平台的特有功能,包括去中心化架构、房间概念、Long Polling同步、MXC URI、HTML富文本、表情回应、消息编辑、扩展字段说明等内容。
|
|
@@ -436,19 +533,17 @@ async def handle_message(event):
|
|
|
436
533
|
sender = event.get_matrix_sender() # 获取发送者ID
|
|
437
534
|
is_edited = event.is_edited() # 是否为编辑消息
|
|
438
535
|
is_notice = event.is_notice() # 是否为 m.notice 类型
|
|
536
|
+
is_encrypted = event.is_encrypted() # 是否来自端到端加密房间
|
|
439
537
|
```
|
|
440
538
|
|
|
441
539
|
## 注意事项:
|
|
442
540
|
|
|
443
|
-
1.
|
|
444
|
-
2. Matrix
|
|
445
|
-
3.
|
|
446
|
-
4.
|
|
447
|
-
5.
|
|
448
|
-
6.
|
|
449
|
-
7. 支持自动接受房间邀请(可通过 `auto_accept_invites` 配置关闭)
|
|
450
|
-
8. 支持发送 HTML 格式消息,使用 `.Html()` 方法
|
|
451
|
-
9. 支持消息回复(`.Reply()`)和用户提及(`.At()`、`.AtAll()`)
|
|
541
|
+
1. Matrix 是去中心化协议,用户ID格式为 `@user:server.domain`,房间ID格式为 `!room_id:server.domain`
|
|
542
|
+
2. Matrix 不区分群聊和私聊,所有会话都是"房间"。适配器通过 DM 账户数据自动识别私聊
|
|
543
|
+
3. 适配器使用 Long Polling(`/sync` API)获取事件,而非 WebSocket
|
|
544
|
+
4. 媒体文件通过 `mxc://` URI 引用,适配器支持自动上传和下载
|
|
545
|
+
5. 支持自动接受房间邀请(可通过 `auto_accept_invites` 配置关闭)
|
|
546
|
+
6. 启用端到端加密后,请勿删除 `store_path` 目录(详见「端到端加密」章节)
|
|
452
547
|
|
|
453
548
|
---
|
|
454
549
|
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
+
LICENSE
|
|
1
2
|
README.md
|
|
2
3
|
pyproject.toml
|
|
3
4
|
ErisPulse_MatrixAdapter.egg-info/PKG-INFO
|
|
4
5
|
ErisPulse_MatrixAdapter.egg-info/SOURCES.txt
|
|
5
6
|
ErisPulse_MatrixAdapter.egg-info/dependency_links.txt
|
|
6
7
|
ErisPulse_MatrixAdapter.egg-info/entry_points.txt
|
|
8
|
+
ErisPulse_MatrixAdapter.egg-info/requires.txt
|
|
7
9
|
ErisPulse_MatrixAdapter.egg-info/top_level.txt
|
|
8
10
|
MatrixAdapter/Converter.py
|
|
9
11
|
MatrixAdapter/Core.py
|
|
12
|
+
MatrixAdapter/NioSession.py
|
|
10
13
|
MatrixAdapter/__init__.py
|
|
11
|
-
test/test.py
|
|
14
|
+
test/test.py
|
|
15
|
+
test/test_e2ee.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
matrix-nio[e2e]>=0.26.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ErisPulse
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -186,51 +186,67 @@ class MatrixConverter(BaseConverter):
|
|
|
186
186
|
|
|
187
187
|
elif msgtype == "m.image":
|
|
188
188
|
url = content.get("url", "")
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
189
|
+
enc_file = content.get("file") if not url else None
|
|
190
|
+
data = {
|
|
191
|
+
"filename": body,
|
|
192
|
+
"info": content.get("info", {}),
|
|
193
|
+
}
|
|
194
|
+
if enc_file:
|
|
195
|
+
data["url"] = enc_file.get("url", "")
|
|
196
|
+
data["matrix_encrypted_file"] = enc_file
|
|
197
|
+
data["encrypted"] = True
|
|
198
|
+
else:
|
|
199
|
+
data["url"] = url
|
|
200
|
+
data["matrix_mxc"] = url
|
|
201
|
+
segments.append({"type": "image", "data": data})
|
|
198
202
|
|
|
199
203
|
elif msgtype == "m.audio":
|
|
200
204
|
url = content.get("url", "")
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
205
|
+
enc_file = content.get("file") if not url else None
|
|
206
|
+
data = {
|
|
207
|
+
"filename": body,
|
|
208
|
+
"info": content.get("info", {}),
|
|
209
|
+
}
|
|
210
|
+
if enc_file:
|
|
211
|
+
data["url"] = enc_file.get("url", "")
|
|
212
|
+
data["matrix_encrypted_file"] = enc_file
|
|
213
|
+
data["encrypted"] = True
|
|
214
|
+
else:
|
|
215
|
+
data["url"] = url
|
|
216
|
+
data["matrix_mxc"] = url
|
|
217
|
+
segments.append({"type": "voice", "data": data})
|
|
210
218
|
|
|
211
219
|
elif msgtype == "m.video":
|
|
212
220
|
url = content.get("url", "")
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
221
|
+
enc_file = content.get("file") if not url else None
|
|
222
|
+
data = {
|
|
223
|
+
"filename": body,
|
|
224
|
+
"info": content.get("info", {}),
|
|
225
|
+
}
|
|
226
|
+
if enc_file:
|
|
227
|
+
data["url"] = enc_file.get("url", "")
|
|
228
|
+
data["matrix_encrypted_file"] = enc_file
|
|
229
|
+
data["encrypted"] = True
|
|
230
|
+
else:
|
|
231
|
+
data["url"] = url
|
|
232
|
+
data["matrix_mxc"] = url
|
|
233
|
+
segments.append({"type": "video", "data": data})
|
|
222
234
|
|
|
223
235
|
elif msgtype == "m.file":
|
|
224
236
|
url = content.get("url", "")
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
"
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
237
|
+
enc_file = content.get("file") if not url else None
|
|
238
|
+
data = {
|
|
239
|
+
"filename": body,
|
|
240
|
+
"info": content.get("info", {}),
|
|
241
|
+
}
|
|
242
|
+
if enc_file:
|
|
243
|
+
data["url"] = enc_file.get("url", "")
|
|
244
|
+
data["matrix_encrypted_file"] = enc_file
|
|
245
|
+
data["encrypted"] = True
|
|
246
|
+
else:
|
|
247
|
+
data["url"] = url
|
|
248
|
+
data["matrix_mxc"] = url
|
|
249
|
+
segments.append({"type": "file", "data": data})
|
|
234
250
|
|
|
235
251
|
elif msgtype == "m.location":
|
|
236
252
|
geo_uri = content.get("geo_uri", "")
|