nonebot-adapter-matrix 0.2.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.
- nonebot_adapter_matrix-0.2.0/PKG-INFO +271 -0
- nonebot_adapter_matrix-0.2.0/README.md +255 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/__init__.py +50 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/adapter.py +823 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/__init__.py +119 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/client.py +2 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/client.pyi +212 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/handle.py +629 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/model.py +276 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/types.py +125 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/utils.py +20 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/api/validation.py +217 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/bot.py +169 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/config.py +47 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/event.py +205 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/exception.py +89 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/message.py +448 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/oauth.py +444 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/serialization.py +159 -0
- nonebot_adapter_matrix-0.2.0/nonebot/adapters/matrix/utils.py +74 -0
- nonebot_adapter_matrix-0.2.0/pyproject.toml +176 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nonebot-adapter-matrix
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Matrix adapter for nonebot2
|
|
5
|
+
Keywords: nonebot,matrix,bot
|
|
6
|
+
Author: CMHopeSunshine, yanyongyu, scdhh
|
|
7
|
+
Author-email: CMHopeSunshine <277073121@qq.com>, yanyongyu <yyy@nonebot.dev>, scdhh <wallfjjd@gmail.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Requires-Dist: nonebot2>=2.4.4
|
|
10
|
+
Requires-Dist: requests-oauth2client>=1.8.0
|
|
11
|
+
Requires-Python: >=3.10, <4.0.0
|
|
12
|
+
Project-URL: homepage, https://github.com/nonebot/adapter-matrix
|
|
13
|
+
Project-URL: repository, https://github.com/nonebot/adapter-matrix
|
|
14
|
+
Project-URL: documentation, https://github.com/nonebot/adapter-matrix
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
<p align="center">
|
|
18
|
+
<a href="https://nonebot.dev/"><img src="assets/logo.svg" width="200" height="200" alt="nonebot-adapter-discord"></a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
<div align="center">
|
|
22
|
+
|
|
23
|
+
# NoneBot-Adapter-Matrix
|
|
24
|
+
|
|
25
|
+
_✨ Matrix Client-Server 协议适配 ✨_
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install nonebot-adapter-matrix
|
|
33
|
+
```
|
|
34
|
+
开发版本可从当前仓库构建安装:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install git+https://github.com/elysia-best/adapter-matrix.git@master
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 配置
|
|
41
|
+
|
|
42
|
+
Matrix adapter 使用 Client-Server API 与 homeserver 通信,需要可发起 HTTP 请求的 NoneBot ForwardDriver。
|
|
43
|
+
|
|
44
|
+
```dotenv
|
|
45
|
+
DRIVER=~httpx
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 三种启动模式
|
|
49
|
+
|
|
50
|
+
适配器支持三种 token 管理模式:
|
|
51
|
+
|
|
52
|
+
#### 1. 静态 token 模式(兼容模式)
|
|
53
|
+
|
|
54
|
+
适合已有 access token 的场景。不会自动获取或刷新 token。
|
|
55
|
+
|
|
56
|
+
```dotenv
|
|
57
|
+
MATRIX_BOTS='[
|
|
58
|
+
{
|
|
59
|
+
"homeserver": "https://matrix.example.org",
|
|
60
|
+
"access_token": "YOUR_ACCESS_TOKEN",
|
|
61
|
+
"user_id": "@bot:example.org"
|
|
62
|
+
}
|
|
63
|
+
]'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
注意:此模式下如果没有额外提供登录凭据或 OAuth2 配置,协议上无法自动获取首个 refresh token。access token 过期后需要手动更新配置。
|
|
67
|
+
|
|
68
|
+
#### 2. 传统 Matrix 登录模式
|
|
69
|
+
|
|
70
|
+
提供登录凭据,适配器启动时自动调用 `/login` 获取 token 对,并在需要时自动 refresh。
|
|
71
|
+
|
|
72
|
+
```dotenv
|
|
73
|
+
MATRIX_BOTS='[
|
|
74
|
+
{
|
|
75
|
+
"homeserver": "https://matrix.example.org",
|
|
76
|
+
"access_token": "",
|
|
77
|
+
"login_user": "@bot:example.org",
|
|
78
|
+
"login_password": "your-password",
|
|
79
|
+
"device_id": "BOTDEVICE",
|
|
80
|
+
"set_presence": "online"
|
|
81
|
+
}
|
|
82
|
+
]'
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- `login_user`:Matrix 用户 ID,用于 `/login` 请求。
|
|
86
|
+
- `login_password`:Matrix 账户密码。
|
|
87
|
+
- `login_initial_device_display_name`:可选;初始设备显示名称。
|
|
88
|
+
- 登录成功后会自动设置 `session_type: "legacy_login"`,后续 refresh 走 `/_matrix/client/v3/refresh`。
|
|
89
|
+
- 若 refresh 返回 `soft_logout: true`,适配器会自动用密码重新登录。
|
|
90
|
+
|
|
91
|
+
#### 3. OAuth2 模式
|
|
92
|
+
|
|
93
|
+
通过 OAuth2 Authorization Code + PKCE 登录,适合支持 Matrix next-gen auth / OIDC 的 homeserver。
|
|
94
|
+
|
|
95
|
+
最小配置:
|
|
96
|
+
|
|
97
|
+
```dotenv
|
|
98
|
+
MATRIX_BOTS='[
|
|
99
|
+
{
|
|
100
|
+
"homeserver": "https://matrix.example.org",
|
|
101
|
+
"access_token": "",
|
|
102
|
+
"oauth_enabled": true,
|
|
103
|
+
"oauth_server_url": "https://account.matrix.org",
|
|
104
|
+
"oauth_open_browser": true
|
|
105
|
+
}
|
|
106
|
+
]'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
也可以手动指定已注册的 client:
|
|
110
|
+
|
|
111
|
+
```dotenv
|
|
112
|
+
MATRIX_BOTS='[
|
|
113
|
+
{
|
|
114
|
+
"homeserver": "https://matrix.example.org",
|
|
115
|
+
"access_token": "",
|
|
116
|
+
"oauth_enabled": true,
|
|
117
|
+
"oauth_server_url": "https://account.matrix.org",
|
|
118
|
+
"oauth_client_id": "your-client-id",
|
|
119
|
+
"oauth_redirect_uri": "https://your-app.example/callback"
|
|
120
|
+
}
|
|
121
|
+
]'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- `oauth_enabled`:启用 OAuth2 登录流程。
|
|
125
|
+
- `oauth_server_url`:可选;直接指定 OAuth2/OIDC server 根地址,例如 `https://account.matrix.org`。若未提供,会优先尝试 Matrix `/_matrix/client/v1/auth_metadata` 自动发现。
|
|
126
|
+
- `oauth_metadata_url`:可选;直接指定 metadata 文档地址,优先级高于 `oauth_server_url`。
|
|
127
|
+
- `oauth_client_id`:可选;若未提供且 server 暴露 `registration_endpoint`,适配器会自动注册一个 OAuth2 client 并持久化保存。
|
|
128
|
+
- `oauth_client_uri`:可选;动态注册时写入 client metadata。某些 Matrix Authentication Service(例如 matrix.org)要求该字段存在;未配置时默认使用 `homeserver` 作为回退值。需要注意这里需要填写有效的域名
|
|
129
|
+
- `oauth_redirect_uri`:可选;若不提供,则默认使用 loopback 回调并自动选择一个可用随机端口。若提供 localhost / `127.0.0.1` 回调地址,则必须显式写出端口,且注册、授权、换 token 全程使用该 URI 原样值;若想用自动随机端口,请直接省略 `oauth_redirect_uri`。若提供外部回调地址,则需要手动复制授权结果中的 `code` 或完整回调 URL 到终端。
|
|
130
|
+
- `oauth_scope`:可选;默认会构造符合 MSC2967 的 scope,并自动补上 `urn:matrix:org.matrix.msc2967.client:device:{DEVICE_ID}`;若你自定义 scope,适配器仍会补 device scope。
|
|
131
|
+
- `oauth_device_id`:可选;指定请求的 Matrix device ID。未提供时会自动生成一个 12 位的大写字母数字 ID。
|
|
132
|
+
- `oauth_open_browser`:是否自动打开浏览器访问授权 URL,默认 `false`。
|
|
133
|
+
- `oauth_callback_timeout`:等待回调的超时时间(秒),默认 `300`。
|
|
134
|
+
|
|
135
|
+
OAuth2 登录流程:
|
|
136
|
+
1. 适配器按如下顺序发现元数据:`oauth_metadata_url` → `/_matrix/client/v1/auth_metadata`(及其 unstable MSC2965 端点)→ `oauth_server_url` 兜底。
|
|
137
|
+
2. 校验 server 支持 `response_type=code`、`response_mode=fragment` 和 PKCE `S256`。
|
|
138
|
+
3. 若未配置 `oauth_client_id`,则通过 metadata 返回的 `registration_endpoint` 自动注册 client。
|
|
139
|
+
4. 生成 device ID、`state`、`code_verifier`/`code_challenge`,并构造符合 MSC2967 的 scope。
|
|
140
|
+
5. 输出授权 URL(若 `oauth_open_browser: true` 则自动打开浏览器)。
|
|
141
|
+
6. 获取 authorization code 并交换首个 access/refresh token。
|
|
142
|
+
7. 后续 refresh 走 metadata 返回的 OAuth2 token endpoint(`grant_type=refresh_token`)。
|
|
143
|
+
|
|
144
|
+
### 通用字段
|
|
145
|
+
|
|
146
|
+
```dotenv
|
|
147
|
+
MATRIX_BOTS='[
|
|
148
|
+
{
|
|
149
|
+
"homeserver": "https://matrix.example.org",
|
|
150
|
+
"access_token": "YOUR_ACCESS_TOKEN",
|
|
151
|
+
"refresh_token": "OPTIONAL_REFRESH_TOKEN",
|
|
152
|
+
"access_token_expires_at_ms": 1760000000000,
|
|
153
|
+
"refresh_before_expiry_ms": 60000,
|
|
154
|
+
"user_id": "@bot:example.org",
|
|
155
|
+
"device_id": "BOTDEVICE",
|
|
156
|
+
"sync_filter": {"room": {"timeline": {"limit": 50}}},
|
|
157
|
+
"set_presence": "online"
|
|
158
|
+
}
|
|
159
|
+
]'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
- `homeserver`:Matrix homeserver 根地址(必填)。
|
|
163
|
+
- `access_token`:当前使用的 Matrix access token(使用登录模式时可留空)。
|
|
164
|
+
- `refresh_token`:通常由登录流程获得,不要求手动填写。持久化到 `MATRIX_TOKEN_STORE_PATH` 后会自动加载。
|
|
165
|
+
- `access_token_expires_at_ms`:当前 access token 的绝对过期时间戳,单位毫秒。
|
|
166
|
+
- `refresh_before_expiry_ms`:距离过期多久前主动 refresh,默认 `60000`(1 分钟)。
|
|
167
|
+
- `user_id`:启动时通过 `/account/whoami` 校验 token 所属用户。
|
|
168
|
+
- `device_id`:记录当前 token 对应的 Matrix 设备 ID。
|
|
169
|
+
- `sync_filter`:传给 `/sync` 的 filter id 或 filter JSON。
|
|
170
|
+
- `set_presence`:`online`、`offline` 或 `unavailable`。
|
|
171
|
+
|
|
172
|
+
### 其他配置
|
|
173
|
+
|
|
174
|
+
```dotenv
|
|
175
|
+
MATRIX_API_TIMEOUT=30.0
|
|
176
|
+
MATRIX_SYNC_TIMEOUT=30000
|
|
177
|
+
MATRIX_RETRY_INTERVAL=3.0
|
|
178
|
+
MATRIX_HANDLE_SELF_MESSAGE=false
|
|
179
|
+
MATRIX_HANDLE_OLD_EVENTS=false
|
|
180
|
+
MATRIX_PROXY='http://127.0.0.1:7890'
|
|
181
|
+
MATRIX_TOKEN_STORE_PATH='.data/matrix-tokens.json'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- `MATRIX_API_TIMEOUT`:普通 API 请求超时时间,单位秒。
|
|
185
|
+
- `MATRIX_SYNC_TIMEOUT`:`/sync` long-poll 超时时间,单位毫秒。
|
|
186
|
+
- `MATRIX_RETRY_INTERVAL`:网络错误后的重试间隔,单位秒。
|
|
187
|
+
- `MATRIX_HANDLE_SELF_MESSAGE`:是否处理机器人自己发送的消息。
|
|
188
|
+
- `MATRIX_HANDLE_OLD_EVENTS`:是否处理早于本次启动时间的旧事件,默认丢弃旧事件。
|
|
189
|
+
- `MATRIX_PROXY`:可选 HTTP 代理。
|
|
190
|
+
- `MATRIX_TOKEN_STORE_PATH`:可选 token 状态文件路径;配置后会将 token 持久化到该文件,重启时自动加载。
|
|
191
|
+
|
|
192
|
+
### Refresh Token 行为
|
|
193
|
+
|
|
194
|
+
- 启动时若配置了 `MATRIX_TOKEN_STORE_PATH`,会优先读取状态文件中的最新 token 和 `session_type`。
|
|
195
|
+
- access token 接近 `access_token_expires_at_ms` 时,会在下一次 `/sync` 前主动 refresh。
|
|
196
|
+
- 若 homeserver 提前使当前 token 失效并返回 `M_UNKNOWN_TOKEN`,adapter 会尝试使用 refresh token 恢复。
|
|
197
|
+
- **refresh 失败语义**:
|
|
198
|
+
- 网络错误或 5xx:保留旧 refresh token,稍后重试。
|
|
199
|
+
- 4xx 且带 `soft_logout: true`:若配置了 `login_password`,自动重新登录。
|
|
200
|
+
- 4xx 无 `soft_logout`:视为会话失效,等待重试。
|
|
201
|
+
- refresh 成功后更新内存中的 `access_token`、`refresh_token`、`access_token_expires_at_ms`,并写回状态文件。
|
|
202
|
+
- adapter 不会回写 `.env` 或其他部署配置;`/sync` 的 `next_batch` 仍然不会持久化。
|
|
203
|
+
|
|
204
|
+
## 插件示例
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from nonebot import on_command
|
|
208
|
+
from nonebot.params import CommandArg
|
|
209
|
+
|
|
210
|
+
from nonebot.adapters.matrix import Bot, Message, MessageEvent, MessageSegment
|
|
211
|
+
|
|
212
|
+
matcher = on_command("echo")
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@matcher.handle()
|
|
216
|
+
async def handle_echo(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):
|
|
217
|
+
text = msg.extract_plain_text()
|
|
218
|
+
if text == "mention":
|
|
219
|
+
await matcher.finish(MessageSegment.mention_user(event.get_user_id()))
|
|
220
|
+
if text == "notice":
|
|
221
|
+
await matcher.finish(MessageSegment.notice("这是一条 Matrix notice"))
|
|
222
|
+
await bot.send(event, MessageSegment.text(text or "hello matrix"))
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### 发送媒体
|
|
226
|
+
|
|
227
|
+
Matrix 媒体需要先上传到 media repository,消息正文再引用返回的 `mxc://` URI;`MessageSegment.image/file/audio/video` 在传入 bytes 时会自动执行这个流程。
|
|
228
|
+
|
|
229
|
+
```python
|
|
230
|
+
from pathlib import Path
|
|
231
|
+
|
|
232
|
+
from nonebot import on_command
|
|
233
|
+
from nonebot.adapters.matrix import Bot, MessageEvent, MessageSegment
|
|
234
|
+
|
|
235
|
+
matcher = on_command("image")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@matcher.handle()
|
|
239
|
+
async def handle_img_send():
|
|
240
|
+
cur_dir = os.path.dirname(__file__)
|
|
241
|
+
# Read img from current directory
|
|
242
|
+
content = Path(os.path.join(cur_dir, "./assets/test.jpg")).read_bytes()
|
|
243
|
+
await send_img.finish(
|
|
244
|
+
MessageSegment.image(
|
|
245
|
+
content,
|
|
246
|
+
filename="test.jpg",
|
|
247
|
+
content_type="image/jpg"
|
|
248
|
+
)
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 常用 Matrix 操作
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
await bot.react(event.room_id, event.event_id, "👍")
|
|
257
|
+
await bot.set_typing_state(event.room_id, typing=True, timeout=5000)
|
|
258
|
+
await bot.mark_read(event.room_id, event.event_id)
|
|
259
|
+
await bot.redact(event.room_id, event.event_id, reason="handled")
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
## 当前范围
|
|
263
|
+
|
|
264
|
+
当前实现面向 Matrix Client-Server bot 场景:
|
|
265
|
+
|
|
266
|
+
- 通过 `/account/whoami` 校验身份。
|
|
267
|
+
- 通过 `/sync` long-poll 接收 room timeline、state、typing、receipt 等事件。
|
|
268
|
+
- 支持发送 `m.room.message`、上传媒体、reaction、redaction、typing 和 receipt。
|
|
269
|
+
- 不包含端到端加密房间支持。
|
|
270
|
+
- 不包含 Matrix Application Service API。
|
|
271
|
+
- 不持久化 `/sync` 的 `next_batch`;进程内重连会复用内存状态,跨进程重启默认丢弃早于本次启动时间的旧事件。
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://nonebot.dev/"><img src="assets/logo.svg" width="200" height="200" alt="nonebot-adapter-discord"></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
|
|
7
|
+
# NoneBot-Adapter-Matrix
|
|
8
|
+
|
|
9
|
+
_✨ Matrix Client-Server 协议适配 ✨_
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
## 安装
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install nonebot-adapter-matrix
|
|
17
|
+
```
|
|
18
|
+
开发版本可从当前仓库构建安装:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install git+https://github.com/elysia-best/adapter-matrix.git@master
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 配置
|
|
25
|
+
|
|
26
|
+
Matrix adapter 使用 Client-Server API 与 homeserver 通信,需要可发起 HTTP 请求的 NoneBot ForwardDriver。
|
|
27
|
+
|
|
28
|
+
```dotenv
|
|
29
|
+
DRIVER=~httpx
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 三种启动模式
|
|
33
|
+
|
|
34
|
+
适配器支持三种 token 管理模式:
|
|
35
|
+
|
|
36
|
+
#### 1. 静态 token 模式(兼容模式)
|
|
37
|
+
|
|
38
|
+
适合已有 access token 的场景。不会自动获取或刷新 token。
|
|
39
|
+
|
|
40
|
+
```dotenv
|
|
41
|
+
MATRIX_BOTS='[
|
|
42
|
+
{
|
|
43
|
+
"homeserver": "https://matrix.example.org",
|
|
44
|
+
"access_token": "YOUR_ACCESS_TOKEN",
|
|
45
|
+
"user_id": "@bot:example.org"
|
|
46
|
+
}
|
|
47
|
+
]'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
注意:此模式下如果没有额外提供登录凭据或 OAuth2 配置,协议上无法自动获取首个 refresh token。access token 过期后需要手动更新配置。
|
|
51
|
+
|
|
52
|
+
#### 2. 传统 Matrix 登录模式
|
|
53
|
+
|
|
54
|
+
提供登录凭据,适配器启动时自动调用 `/login` 获取 token 对,并在需要时自动 refresh。
|
|
55
|
+
|
|
56
|
+
```dotenv
|
|
57
|
+
MATRIX_BOTS='[
|
|
58
|
+
{
|
|
59
|
+
"homeserver": "https://matrix.example.org",
|
|
60
|
+
"access_token": "",
|
|
61
|
+
"login_user": "@bot:example.org",
|
|
62
|
+
"login_password": "your-password",
|
|
63
|
+
"device_id": "BOTDEVICE",
|
|
64
|
+
"set_presence": "online"
|
|
65
|
+
}
|
|
66
|
+
]'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- `login_user`:Matrix 用户 ID,用于 `/login` 请求。
|
|
70
|
+
- `login_password`:Matrix 账户密码。
|
|
71
|
+
- `login_initial_device_display_name`:可选;初始设备显示名称。
|
|
72
|
+
- 登录成功后会自动设置 `session_type: "legacy_login"`,后续 refresh 走 `/_matrix/client/v3/refresh`。
|
|
73
|
+
- 若 refresh 返回 `soft_logout: true`,适配器会自动用密码重新登录。
|
|
74
|
+
|
|
75
|
+
#### 3. OAuth2 模式
|
|
76
|
+
|
|
77
|
+
通过 OAuth2 Authorization Code + PKCE 登录,适合支持 Matrix next-gen auth / OIDC 的 homeserver。
|
|
78
|
+
|
|
79
|
+
最小配置:
|
|
80
|
+
|
|
81
|
+
```dotenv
|
|
82
|
+
MATRIX_BOTS='[
|
|
83
|
+
{
|
|
84
|
+
"homeserver": "https://matrix.example.org",
|
|
85
|
+
"access_token": "",
|
|
86
|
+
"oauth_enabled": true,
|
|
87
|
+
"oauth_server_url": "https://account.matrix.org",
|
|
88
|
+
"oauth_open_browser": true
|
|
89
|
+
}
|
|
90
|
+
]'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
也可以手动指定已注册的 client:
|
|
94
|
+
|
|
95
|
+
```dotenv
|
|
96
|
+
MATRIX_BOTS='[
|
|
97
|
+
{
|
|
98
|
+
"homeserver": "https://matrix.example.org",
|
|
99
|
+
"access_token": "",
|
|
100
|
+
"oauth_enabled": true,
|
|
101
|
+
"oauth_server_url": "https://account.matrix.org",
|
|
102
|
+
"oauth_client_id": "your-client-id",
|
|
103
|
+
"oauth_redirect_uri": "https://your-app.example/callback"
|
|
104
|
+
}
|
|
105
|
+
]'
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- `oauth_enabled`:启用 OAuth2 登录流程。
|
|
109
|
+
- `oauth_server_url`:可选;直接指定 OAuth2/OIDC server 根地址,例如 `https://account.matrix.org`。若未提供,会优先尝试 Matrix `/_matrix/client/v1/auth_metadata` 自动发现。
|
|
110
|
+
- `oauth_metadata_url`:可选;直接指定 metadata 文档地址,优先级高于 `oauth_server_url`。
|
|
111
|
+
- `oauth_client_id`:可选;若未提供且 server 暴露 `registration_endpoint`,适配器会自动注册一个 OAuth2 client 并持久化保存。
|
|
112
|
+
- `oauth_client_uri`:可选;动态注册时写入 client metadata。某些 Matrix Authentication Service(例如 matrix.org)要求该字段存在;未配置时默认使用 `homeserver` 作为回退值。需要注意这里需要填写有效的域名
|
|
113
|
+
- `oauth_redirect_uri`:可选;若不提供,则默认使用 loopback 回调并自动选择一个可用随机端口。若提供 localhost / `127.0.0.1` 回调地址,则必须显式写出端口,且注册、授权、换 token 全程使用该 URI 原样值;若想用自动随机端口,请直接省略 `oauth_redirect_uri`。若提供外部回调地址,则需要手动复制授权结果中的 `code` 或完整回调 URL 到终端。
|
|
114
|
+
- `oauth_scope`:可选;默认会构造符合 MSC2967 的 scope,并自动补上 `urn:matrix:org.matrix.msc2967.client:device:{DEVICE_ID}`;若你自定义 scope,适配器仍会补 device scope。
|
|
115
|
+
- `oauth_device_id`:可选;指定请求的 Matrix device ID。未提供时会自动生成一个 12 位的大写字母数字 ID。
|
|
116
|
+
- `oauth_open_browser`:是否自动打开浏览器访问授权 URL,默认 `false`。
|
|
117
|
+
- `oauth_callback_timeout`:等待回调的超时时间(秒),默认 `300`。
|
|
118
|
+
|
|
119
|
+
OAuth2 登录流程:
|
|
120
|
+
1. 适配器按如下顺序发现元数据:`oauth_metadata_url` → `/_matrix/client/v1/auth_metadata`(及其 unstable MSC2965 端点)→ `oauth_server_url` 兜底。
|
|
121
|
+
2. 校验 server 支持 `response_type=code`、`response_mode=fragment` 和 PKCE `S256`。
|
|
122
|
+
3. 若未配置 `oauth_client_id`,则通过 metadata 返回的 `registration_endpoint` 自动注册 client。
|
|
123
|
+
4. 生成 device ID、`state`、`code_verifier`/`code_challenge`,并构造符合 MSC2967 的 scope。
|
|
124
|
+
5. 输出授权 URL(若 `oauth_open_browser: true` 则自动打开浏览器)。
|
|
125
|
+
6. 获取 authorization code 并交换首个 access/refresh token。
|
|
126
|
+
7. 后续 refresh 走 metadata 返回的 OAuth2 token endpoint(`grant_type=refresh_token`)。
|
|
127
|
+
|
|
128
|
+
### 通用字段
|
|
129
|
+
|
|
130
|
+
```dotenv
|
|
131
|
+
MATRIX_BOTS='[
|
|
132
|
+
{
|
|
133
|
+
"homeserver": "https://matrix.example.org",
|
|
134
|
+
"access_token": "YOUR_ACCESS_TOKEN",
|
|
135
|
+
"refresh_token": "OPTIONAL_REFRESH_TOKEN",
|
|
136
|
+
"access_token_expires_at_ms": 1760000000000,
|
|
137
|
+
"refresh_before_expiry_ms": 60000,
|
|
138
|
+
"user_id": "@bot:example.org",
|
|
139
|
+
"device_id": "BOTDEVICE",
|
|
140
|
+
"sync_filter": {"room": {"timeline": {"limit": 50}}},
|
|
141
|
+
"set_presence": "online"
|
|
142
|
+
}
|
|
143
|
+
]'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `homeserver`:Matrix homeserver 根地址(必填)。
|
|
147
|
+
- `access_token`:当前使用的 Matrix access token(使用登录模式时可留空)。
|
|
148
|
+
- `refresh_token`:通常由登录流程获得,不要求手动填写。持久化到 `MATRIX_TOKEN_STORE_PATH` 后会自动加载。
|
|
149
|
+
- `access_token_expires_at_ms`:当前 access token 的绝对过期时间戳,单位毫秒。
|
|
150
|
+
- `refresh_before_expiry_ms`:距离过期多久前主动 refresh,默认 `60000`(1 分钟)。
|
|
151
|
+
- `user_id`:启动时通过 `/account/whoami` 校验 token 所属用户。
|
|
152
|
+
- `device_id`:记录当前 token 对应的 Matrix 设备 ID。
|
|
153
|
+
- `sync_filter`:传给 `/sync` 的 filter id 或 filter JSON。
|
|
154
|
+
- `set_presence`:`online`、`offline` 或 `unavailable`。
|
|
155
|
+
|
|
156
|
+
### 其他配置
|
|
157
|
+
|
|
158
|
+
```dotenv
|
|
159
|
+
MATRIX_API_TIMEOUT=30.0
|
|
160
|
+
MATRIX_SYNC_TIMEOUT=30000
|
|
161
|
+
MATRIX_RETRY_INTERVAL=3.0
|
|
162
|
+
MATRIX_HANDLE_SELF_MESSAGE=false
|
|
163
|
+
MATRIX_HANDLE_OLD_EVENTS=false
|
|
164
|
+
MATRIX_PROXY='http://127.0.0.1:7890'
|
|
165
|
+
MATRIX_TOKEN_STORE_PATH='.data/matrix-tokens.json'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
- `MATRIX_API_TIMEOUT`:普通 API 请求超时时间,单位秒。
|
|
169
|
+
- `MATRIX_SYNC_TIMEOUT`:`/sync` long-poll 超时时间,单位毫秒。
|
|
170
|
+
- `MATRIX_RETRY_INTERVAL`:网络错误后的重试间隔,单位秒。
|
|
171
|
+
- `MATRIX_HANDLE_SELF_MESSAGE`:是否处理机器人自己发送的消息。
|
|
172
|
+
- `MATRIX_HANDLE_OLD_EVENTS`:是否处理早于本次启动时间的旧事件,默认丢弃旧事件。
|
|
173
|
+
- `MATRIX_PROXY`:可选 HTTP 代理。
|
|
174
|
+
- `MATRIX_TOKEN_STORE_PATH`:可选 token 状态文件路径;配置后会将 token 持久化到该文件,重启时自动加载。
|
|
175
|
+
|
|
176
|
+
### Refresh Token 行为
|
|
177
|
+
|
|
178
|
+
- 启动时若配置了 `MATRIX_TOKEN_STORE_PATH`,会优先读取状态文件中的最新 token 和 `session_type`。
|
|
179
|
+
- access token 接近 `access_token_expires_at_ms` 时,会在下一次 `/sync` 前主动 refresh。
|
|
180
|
+
- 若 homeserver 提前使当前 token 失效并返回 `M_UNKNOWN_TOKEN`,adapter 会尝试使用 refresh token 恢复。
|
|
181
|
+
- **refresh 失败语义**:
|
|
182
|
+
- 网络错误或 5xx:保留旧 refresh token,稍后重试。
|
|
183
|
+
- 4xx 且带 `soft_logout: true`:若配置了 `login_password`,自动重新登录。
|
|
184
|
+
- 4xx 无 `soft_logout`:视为会话失效,等待重试。
|
|
185
|
+
- refresh 成功后更新内存中的 `access_token`、`refresh_token`、`access_token_expires_at_ms`,并写回状态文件。
|
|
186
|
+
- adapter 不会回写 `.env` 或其他部署配置;`/sync` 的 `next_batch` 仍然不会持久化。
|
|
187
|
+
|
|
188
|
+
## 插件示例
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from nonebot import on_command
|
|
192
|
+
from nonebot.params import CommandArg
|
|
193
|
+
|
|
194
|
+
from nonebot.adapters.matrix import Bot, Message, MessageEvent, MessageSegment
|
|
195
|
+
|
|
196
|
+
matcher = on_command("echo")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@matcher.handle()
|
|
200
|
+
async def handle_echo(bot: Bot, event: MessageEvent, msg: Message = CommandArg()):
|
|
201
|
+
text = msg.extract_plain_text()
|
|
202
|
+
if text == "mention":
|
|
203
|
+
await matcher.finish(MessageSegment.mention_user(event.get_user_id()))
|
|
204
|
+
if text == "notice":
|
|
205
|
+
await matcher.finish(MessageSegment.notice("这是一条 Matrix notice"))
|
|
206
|
+
await bot.send(event, MessageSegment.text(text or "hello matrix"))
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 发送媒体
|
|
210
|
+
|
|
211
|
+
Matrix 媒体需要先上传到 media repository,消息正文再引用返回的 `mxc://` URI;`MessageSegment.image/file/audio/video` 在传入 bytes 时会自动执行这个流程。
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from pathlib import Path
|
|
215
|
+
|
|
216
|
+
from nonebot import on_command
|
|
217
|
+
from nonebot.adapters.matrix import Bot, MessageEvent, MessageSegment
|
|
218
|
+
|
|
219
|
+
matcher = on_command("image")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
@matcher.handle()
|
|
223
|
+
async def handle_img_send():
|
|
224
|
+
cur_dir = os.path.dirname(__file__)
|
|
225
|
+
# Read img from current directory
|
|
226
|
+
content = Path(os.path.join(cur_dir, "./assets/test.jpg")).read_bytes()
|
|
227
|
+
await send_img.finish(
|
|
228
|
+
MessageSegment.image(
|
|
229
|
+
content,
|
|
230
|
+
filename="test.jpg",
|
|
231
|
+
content_type="image/jpg"
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### 常用 Matrix 操作
|
|
238
|
+
|
|
239
|
+
```python
|
|
240
|
+
await bot.react(event.room_id, event.event_id, "👍")
|
|
241
|
+
await bot.set_typing_state(event.room_id, typing=True, timeout=5000)
|
|
242
|
+
await bot.mark_read(event.room_id, event.event_id)
|
|
243
|
+
await bot.redact(event.room_id, event.event_id, reason="handled")
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## 当前范围
|
|
247
|
+
|
|
248
|
+
当前实现面向 Matrix Client-Server bot 场景:
|
|
249
|
+
|
|
250
|
+
- 通过 `/account/whoami` 校验身份。
|
|
251
|
+
- 通过 `/sync` long-poll 接收 room timeline、state、typing、receipt 等事件。
|
|
252
|
+
- 支持发送 `m.room.message`、上传媒体、reaction、redaction、typing 和 receipt。
|
|
253
|
+
- 不包含端到端加密房间支持。
|
|
254
|
+
- 不包含 Matrix Application Service API。
|
|
255
|
+
- 不持久化 `/sync` 的 `next_batch`;进程内重连会复用内存状态,跨进程重启默认丢弃早于本次启动时间的旧事件。
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from .adapter import Adapter
|
|
2
|
+
from .api import UNSET, is_not_unset, is_unset
|
|
3
|
+
from .bot import Bot
|
|
4
|
+
from .event import (
|
|
5
|
+
Event,
|
|
6
|
+
EventType,
|
|
7
|
+
InviteEvent,
|
|
8
|
+
LeaveEvent,
|
|
9
|
+
MessageEvent,
|
|
10
|
+
MetaEvent,
|
|
11
|
+
NoticeEvent,
|
|
12
|
+
ReactionEvent,
|
|
13
|
+
ReceiptEvent,
|
|
14
|
+
RedactionEvent,
|
|
15
|
+
RoomMemberEvent,
|
|
16
|
+
RoomMessageEvent,
|
|
17
|
+
SyncMetaEvent,
|
|
18
|
+
TypingEvent,
|
|
19
|
+
UnknownRoomEvent,
|
|
20
|
+
event_classes,
|
|
21
|
+
)
|
|
22
|
+
from .message import Message, MessageSegment
|
|
23
|
+
from .utils import log
|
|
24
|
+
|
|
25
|
+
__all__ = (
|
|
26
|
+
"UNSET",
|
|
27
|
+
"Adapter",
|
|
28
|
+
"Bot",
|
|
29
|
+
"Event",
|
|
30
|
+
"EventType",
|
|
31
|
+
"InviteEvent",
|
|
32
|
+
"LeaveEvent",
|
|
33
|
+
"Message",
|
|
34
|
+
"MessageEvent",
|
|
35
|
+
"MessageSegment",
|
|
36
|
+
"MetaEvent",
|
|
37
|
+
"NoticeEvent",
|
|
38
|
+
"ReactionEvent",
|
|
39
|
+
"ReceiptEvent",
|
|
40
|
+
"RedactionEvent",
|
|
41
|
+
"RoomMemberEvent",
|
|
42
|
+
"RoomMessageEvent",
|
|
43
|
+
"SyncMetaEvent",
|
|
44
|
+
"TypingEvent",
|
|
45
|
+
"UnknownRoomEvent",
|
|
46
|
+
"event_classes",
|
|
47
|
+
"is_not_unset",
|
|
48
|
+
"is_unset",
|
|
49
|
+
"log",
|
|
50
|
+
)
|