nonebot-plugin-random 0.0.8__tar.gz → 0.1.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.
Files changed (33) hide show
  1. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/LICENSE +21 -21
  2. nonebot_plugin_random-0.1.0/PKG-INFO +280 -0
  3. nonebot_plugin_random-0.1.0/README.md +242 -0
  4. nonebot_plugin_random-0.1.0/nonebot-plugin-random/__init__.py +28 -0
  5. nonebot_plugin_random-0.1.0/nonebot-plugin-random/cooldown.py +65 -0
  6. nonebot_plugin_random-0.1.0/nonebot-plugin-random/data_source.py +248 -0
  7. nonebot_plugin_random-0.1.0/nonebot-plugin-random/depends.py +26 -0
  8. nonebot_plugin_random-0.1.0/nonebot-plugin-random/download.py +44 -0
  9. nonebot_plugin_random-0.1.0/nonebot-plugin-random/management.py +153 -0
  10. nonebot_plugin_random-0.1.0/nonebot-plugin-random/matchers.py +541 -0
  11. nonebot_plugin_random-0.1.0/nonebot-plugin-random/models.py +169 -0
  12. nonebot_plugin_random-0.1.0/nonebot-plugin-random/registry.py +472 -0
  13. nonebot_plugin_random-0.1.0/nonebot-plugin-random/render.py +38 -0
  14. nonebot_plugin_random-0.1.0/nonebot-plugin-random/switches.py +283 -0
  15. nonebot_plugin_random-0.1.0/nonebot-plugin-random/templates/pool_manager.html +99 -0
  16. nonebot_plugin_random-0.1.0/nonebot-plugin-random/utils.py +62 -0
  17. nonebot_plugin_random-0.1.0/nonebot_plugin_random.egg-info/PKG-INFO +280 -0
  18. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/nonebot_plugin_random.egg-info/SOURCES.txt +7 -0
  19. nonebot_plugin_random-0.1.0/nonebot_plugin_random.egg-info/requires.txt +9 -0
  20. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/setup.py +38 -26
  21. nonebot-plugin-random-0.0.8/PKG-INFO +0 -226
  22. nonebot-plugin-random-0.0.8/README.md +0 -211
  23. nonebot-plugin-random-0.0.8/nonebot-plugin-random/__init__.py +0 -14
  24. nonebot-plugin-random-0.0.8/nonebot-plugin-random/depends.py +0 -10
  25. nonebot-plugin-random-0.0.8/nonebot-plugin-random/download.py +0 -15
  26. nonebot-plugin-random-0.0.8/nonebot-plugin-random/matchers.py +0 -308
  27. nonebot-plugin-random-0.0.8/nonebot-plugin-random/models.py +0 -52
  28. nonebot-plugin-random-0.0.8/nonebot-plugin-random/utils.py +0 -19
  29. nonebot-plugin-random-0.0.8/nonebot_plugin_random.egg-info/PKG-INFO +0 -226
  30. nonebot-plugin-random-0.0.8/nonebot_plugin_random.egg-info/requires.txt +0 -3
  31. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/nonebot_plugin_random.egg-info/dependency_links.txt +0 -0
  32. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/nonebot_plugin_random.egg-info/top_level.txt +0 -0
  33. {nonebot-plugin-random-0.0.8 → nonebot_plugin_random-0.1.0}/setup.cfg +0 -0
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2022 jcjrobert
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.
1
+ MIT License
2
+
3
+ Copyright (c) 2022 jcjrobert
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.
@@ -0,0 +1,280 @@
1
+ Metadata-Version: 2.4
2
+ Name: nonebot-plugin-random
3
+ Version: 0.1.0
4
+ Summary: Nonebot2 通用抽图/语音插件
5
+ Home-page: https://github.com/jcjrobert/nonebot-plugin-random
6
+ Author: jcjrobert
7
+ Author-email: jcjrobbie@gmail.com
8
+ License: MIT License
9
+ Keywords: pip,nonebot2,nonebot,random,抽图
10
+ Platform: any
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Requires-Python: >=3.9.5
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: nonebot2>=2.3.1
17
+ Requires-Dist: nonebot-adapter-onebot>=2.4.3
18
+ Requires-Dist: httpx>=0.27.0
19
+ Requires-Dist: nonebot-plugin-htmlrender>=0.4.0
20
+ Requires-Dist: nonebot-plugin-alconna>=0.51.4
21
+ Requires-Dist: nonebot-plugin-waiter>=0.6.2
22
+ Requires-Dist: Pillow>=10.3.0
23
+ Requires-Dist: Jinja2>=3.1.4
24
+ Requires-Dist: pydantic>=2.7.4
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: keywords
32
+ Dynamic: license
33
+ Dynamic: license-file
34
+ Dynamic: platform
35
+ Dynamic: requires-dist
36
+ Dynamic: requires-python
37
+ Dynamic: summary
38
+
39
+ <div align="center">
40
+ <a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
41
+ <br>
42
+ <p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
43
+ </div>
44
+
45
+ <div align="center">
46
+
47
+ # nonebot-plugin-random
48
+
49
+ _✨ Nonebot2 通用抽图/语音插件 ✨_
50
+
51
+ <a href="./LICENSE">
52
+ <img src="https://img.shields.io/github/license/jcjrobert/nonebot-plugin-random.svg" alt="license">
53
+ </a>
54
+ <a href="https://pypi.python.org/pypi/nonebot-plugin-random">
55
+ <img src="https://img.shields.io/pypi/v/nonebot-plugin-random.svg" alt="pypi">
56
+ </a>
57
+ <img src="https://img.shields.io/badge/python-3.9.5+-blue.svg" alt="python">
58
+
59
+ </div>
60
+
61
+ ## 📖 介绍
62
+
63
+ `nonebot-plugin-random` 是面向 NoneBot2 和 OneBot V11 的通用随机素材插件。只需在 `data/random` 中建立资源池并放入素材,即可提供文本、图片、语音或视频随机抽取功能。
64
+
65
+ 0.1.0 新增动态资源池管理、网页截图面板、群级与全局开关、群级冷却、跨群管理、资源池归档恢复和严格配置校验。资源池 Matcher 可以在运行期间创建、重建或注销,无需每次修改后重启机器人。
66
+
67
+ ## 💿 安装
68
+
69
+ 插件要求 Python 3.9.5 或更高版本。
70
+
71
+ 使用 nb-cli:
72
+
73
+ ```shell
74
+ nb plugin install nonebot-plugin-random
75
+ ```
76
+
77
+ 使用 pip:
78
+
79
+ ```shell
80
+ pip install nonebot-plugin-random
81
+ ```
82
+
83
+ 然后在 NoneBot2 配置中加载插件,或在入口文件中调用:
84
+
85
+ ```python
86
+ nonebot.load_plugin("nonebot-plugin-random")
87
+ ```
88
+
89
+ 管理面板由 `nonebot-plugin-htmlrender` 渲染。首次部署时请按照该插件的说明准备 Playwright 浏览器运行环境;浏览器不可用时,随机池列表会回退为文本消息。
90
+
91
+ ## 🎉 快速开始
92
+
93
+ 机器人启动时会创建并扫描运行目录下的 `data/random/`。每个不以 `.` 或 `_` 开头的子目录都是一个资源池,例如:
94
+
95
+ ```text
96
+ data/
97
+ ├─ random/
98
+ │ ├─ capoo/
99
+ │ │ ├─ config.json
100
+ │ │ ├─ happy.png
101
+ │ │ └─ nested/
102
+ │ │ └─ sleepy.webp
103
+ │ └─ quote/
104
+ │ ├─ config.json
105
+ │ └─ hello.txt
106
+ └─ random_archive/
107
+ ```
108
+
109
+ 若 `capoo` 没有 `config.json`,插件会使用默认配置并注册 `随机capoo` 命令。已有的 `data/random/<资源池>` 目录可以继续使用,但新版配置采用严格校验;包含未知字段、错误类型或非法值的资源池会记录错误并跳过。
110
+
111
+ ## ⚙️ 资源池配置
112
+
113
+ `config.json` 必须是 UTF-8 编码的 JSON 对象。
114
+
115
+ ```json
116
+ {
117
+ "display_name": "Capoo",
118
+ "draw_output": "image",
119
+ "draw_mode": "direct",
120
+ "cooldown_seconds": 10,
121
+ "message_type": "command",
122
+ "message": ["随机capoo"],
123
+ "insert_message": ["添加随机capoo"],
124
+ "delete_message": ["删除随机capoo"],
125
+ "modify_admin_only": true,
126
+ "is_tome": false,
127
+ "output_prefix": "",
128
+ "output_suffix": "",
129
+ "is_at_sender": false
130
+ }
131
+ ```
132
+
133
+ | 配置项 | 默认值 | 说明 |
134
+ |---|---|---|
135
+ | `display_name` | 当前目录名 | 展示名称,最多 30 个字符 |
136
+ | `draw_output` | `image` | 输出类型:`text`、`image`、`record` 或 `video` |
137
+ | `draw_mode` | `direct` | `direct` 从资源池全部子目录递归抽取;`indirect` 先等概率选择一个一级子目录,再从其中抽取 |
138
+ | `cooldown_seconds` | `10` | 每个群独立计算的非负整数冷却秒数 |
139
+ | `message_type` | `command` | 匹配类型:`command`、`keyword` 或 `regex` |
140
+ | `message` | `随机<资源池ID>` | 触发条件列表;正则模式必须为 `[表达式, 展示文本]` |
141
+ | `insert_message` | 根据 `message` 生成 | 图片资源池的添加命令列表 |
142
+ | `delete_message` | 根据 `message` 生成 | 图片资源池的删除命令列表 |
143
+ | `modify_admin_only` | `true` | 是否只允许群主、群管理员和超级管理员增删图片 |
144
+ | `is_tome` | `false` | 触发抽取时是否必须 @机器人 |
145
+ | `output_prefix` | 空字符串 | 文本或图片输出前缀 |
146
+ | `output_suffix` | 空字符串 | 文本或图片输出后缀 |
147
+ | `is_at_sender` | `false` | 发送文本或图片时是否 @触发者 |
148
+
149
+ `output_prefix` 和 `output_suffix` 支持以下占位符:
150
+
151
+ | 占位符 | 内容 |
152
+ |---|---|
153
+ | `{filename}` | 素材文件名,包含扩展名 |
154
+ | `{filestem}` | 素材文件名,不包含扩展名 |
155
+
156
+ 资源池 ID 来自目录名,只允许小写字母、数字、下划线和连字符,长度为 1~32 个字符,并且必须以字母或数字开头。
157
+
158
+ ## 📦 支持的素材
159
+
160
+ | 输出类型 | 文件扩展名 |
161
+ |---|---|
162
+ | `text` | `.txt`,按 UTF-8 读取 |
163
+ | `image` | `.gif`、`.png`、`.jpg`、`.jpeg`、`.webp` |
164
+ | `record` | `.mp3`、`.wav`、`.ogg`、`.flac` |
165
+ | `video` | `.mp4`、`.avi`、`.flv`、`.wmv`、`.mov`、`.mpg`、`.mpeg` |
166
+
167
+ 命令模式下可以在抽取命令后附加文件名前缀,插件会从文件名以该前缀开头的候选素材中随机选择。
168
+
169
+ 图片资源池支持在群聊中发送或回复图片进行添加、删除。添加时会验证图片内容,只接受 PNG、JPEG、GIF 和 WebP;删除会移除内容哈希相同的所有图片。
170
+
171
+ ## 🎛️ 管理命令
172
+
173
+ ### 查看与群内开关
174
+
175
+ | 命令 | 权限 | 说明 |
176
+ |---|---|---|
177
+ | `随机池列表` | 群成员 | 查看当前群可见的资源池截图面板 |
178
+ | `开启随机池 <资源池ID...>` | 群主、群管理员或超级管理员 | 在当前群显式开启资源池,可覆盖全局禁用 |
179
+ | `关闭随机池 <资源池ID...>` | 群主、群管理员或超级管理员 | 在当前群显式关闭资源池 |
180
+
181
+ ### 跨群与全局管理
182
+
183
+ | 命令 | 权限 | 说明 |
184
+ |---|---|---|
185
+ | `随机池列表 <群号>` | 超级管理员 | 查看指定群的完整资源池状态 |
186
+ | `开启群随机池 <群号> <资源池ID...>` | 超级管理员 | 为指定群开启资源池 |
187
+ | `关闭群随机池 <群号> <资源池ID...>` | 超级管理员 | 为指定群关闭资源池 |
188
+ | `全局启用随机池 <资源池ID...>` | 超级管理员 | 恢复资源池的全局默认开启状态 |
189
+ | `全局禁用随机池 <资源池ID...>` | 超级管理员 | 默认对所有群禁用资源池;群显式开启仍可覆盖 |
190
+
191
+ 群级和全局开关保存在 `data/random/group_switches.json`,写入过程使用临时文件替换,避免部分写入。
192
+
193
+ ### 动态资源池管理
194
+
195
+ | 命令 | 权限 | 说明 |
196
+ |---|---|---|
197
+ | `新增随机池` | 超级管理员 | 通过交互式会话创建配置并立即注册 Matcher |
198
+ | `编辑随机池 <资源池ID>` | 超级管理员 | 修改配置并立即重建 Matcher |
199
+ | `删除随机池 <资源池ID>` | 超级管理员 | 确认后归档资源池并注销 Matcher |
200
+ | `随机池归档列表` | 超级管理员 | 查看可恢复的归档 ID |
201
+ | `恢复随机池 <归档ID>` | 超级管理员 | 恢复资源池并立即注册 Matcher |
202
+
203
+ 删除操作不会直接销毁素材,而是将整个资源池移动到 `data/random_archive/<时间戳>--<资源池ID>`。
204
+
205
+ ## 🔐 权限与状态优先级
206
+
207
+ 资源池状态按以下顺序解析:
208
+
209
+ 1. 群显式开启;
210
+ 2. 群显式关闭;
211
+ 3. 全局默认禁用;
212
+ 4. 默认开启。
213
+
214
+ 群级冷却按“群号 + 资源池 ID”分别计算,不同群互不影响。抽取发送失败时会释放本次冷却占用。
215
+
216
+ ## 📝 更新日志
217
+
218
+ ### 0.1.0
219
+
220
+ - 重构为动态资源池注册与管理架构。
221
+ - 新增网页截图管理面板及文本降级响应。
222
+ - 新增群级开关、全局默认状态和超级管理员跨群管理。
223
+ - 新增群级冷却、文本抽取、直接/分层抽取模式。
224
+ - 新增运行时创建、编辑、归档与恢复资源池。
225
+ - 使用 Pydantic v2 严格校验配置,并增强图片内容与文件名安全校验。
226
+ - 将图片增删权限默认值调整为仅管理员。
227
+
228
+ <details>
229
+ <summary>历史版本(展开/收起)</summary>
230
+
231
+ ### 0.0.9
232
+
233
+ - 添加删除图片默认所有人可以添加,要仅管理员需要单独设置
234
+ - 支持视频抽取
235
+
236
+ ### 0.0.8
237
+
238
+ - 支持动态删除图片(仅command)
239
+
240
+ ### 0.0.7
241
+
242
+ - 规定读取config.json文件必须为UTF-8编码
243
+ - 输出前后缀支持文件名
244
+ - 添加图片仅管理员可以操作
245
+
246
+ ### 0.0.6
247
+
248
+ - 支持动态添加图片(仅command)
249
+
250
+ ### 0.0.5
251
+
252
+ - 支持根据文件名定向抽取文件(仅command)
253
+
254
+ ### 0.0.4
255
+
256
+ - 去除draw_mode,现在可以抽取该文件夹下符合格式的全部文件
257
+ - 代码优化,分离config
258
+
259
+ ### 0.0.3
260
+
261
+ - 支持正则命令匹配
262
+
263
+ ### 0.0.2
264
+
265
+ - 修复未配置"message"时不能正常使用随机命令的bug
266
+ - 支持输出前后缀配置和at发送者
267
+
268
+ ### 0.0.1
269
+
270
+ - 插件初次发布
271
+
272
+ </details>
273
+
274
+ ## 💡 特别感谢
275
+
276
+ - [noneplugin/nonebot-plugin-petpet](https://github.com/noneplugin/nonebot-plugin-petpet) Nonebot2 插件,用于制作摸头等头像相关表情包
277
+
278
+ ## 📄 开源许可
279
+
280
+ 本项目使用 [MIT License](./LICENSE) 开源。
@@ -0,0 +1,242 @@
1
+ <div align="center">
2
+ <a href="https://v2.nonebot.dev/store"><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/nbp_logo.png" width="180" height="180" alt="NoneBotPluginLogo"></a>
3
+ <br>
4
+ <p><img src="https://github.com/A-kirami/nonebot-plugin-template/blob/resources/NoneBotPlugin.svg" width="240" alt="NoneBotPluginText"></p>
5
+ </div>
6
+
7
+ <div align="center">
8
+
9
+ # nonebot-plugin-random
10
+
11
+ _✨ Nonebot2 通用抽图/语音插件 ✨_
12
+
13
+ <a href="./LICENSE">
14
+ <img src="https://img.shields.io/github/license/jcjrobert/nonebot-plugin-random.svg" alt="license">
15
+ </a>
16
+ <a href="https://pypi.python.org/pypi/nonebot-plugin-random">
17
+ <img src="https://img.shields.io/pypi/v/nonebot-plugin-random.svg" alt="pypi">
18
+ </a>
19
+ <img src="https://img.shields.io/badge/python-3.9.5+-blue.svg" alt="python">
20
+
21
+ </div>
22
+
23
+ ## 📖 介绍
24
+
25
+ `nonebot-plugin-random` 是面向 NoneBot2 和 OneBot V11 的通用随机素材插件。只需在 `data/random` 中建立资源池并放入素材,即可提供文本、图片、语音或视频随机抽取功能。
26
+
27
+ 0.1.0 新增动态资源池管理、网页截图面板、群级与全局开关、群级冷却、跨群管理、资源池归档恢复和严格配置校验。资源池 Matcher 可以在运行期间创建、重建或注销,无需每次修改后重启机器人。
28
+
29
+ ## 💿 安装
30
+
31
+ 插件要求 Python 3.9.5 或更高版本。
32
+
33
+ 使用 nb-cli:
34
+
35
+ ```shell
36
+ nb plugin install nonebot-plugin-random
37
+ ```
38
+
39
+ 使用 pip:
40
+
41
+ ```shell
42
+ pip install nonebot-plugin-random
43
+ ```
44
+
45
+ 然后在 NoneBot2 配置中加载插件,或在入口文件中调用:
46
+
47
+ ```python
48
+ nonebot.load_plugin("nonebot-plugin-random")
49
+ ```
50
+
51
+ 管理面板由 `nonebot-plugin-htmlrender` 渲染。首次部署时请按照该插件的说明准备 Playwright 浏览器运行环境;浏览器不可用时,随机池列表会回退为文本消息。
52
+
53
+ ## 🎉 快速开始
54
+
55
+ 机器人启动时会创建并扫描运行目录下的 `data/random/`。每个不以 `.` 或 `_` 开头的子目录都是一个资源池,例如:
56
+
57
+ ```text
58
+ data/
59
+ ├─ random/
60
+ │ ├─ capoo/
61
+ │ │ ├─ config.json
62
+ │ │ ├─ happy.png
63
+ │ │ └─ nested/
64
+ │ │ └─ sleepy.webp
65
+ │ └─ quote/
66
+ │ ├─ config.json
67
+ │ └─ hello.txt
68
+ └─ random_archive/
69
+ ```
70
+
71
+ 若 `capoo` 没有 `config.json`,插件会使用默认配置并注册 `随机capoo` 命令。已有的 `data/random/<资源池>` 目录可以继续使用,但新版配置采用严格校验;包含未知字段、错误类型或非法值的资源池会记录错误并跳过。
72
+
73
+ ## ⚙️ 资源池配置
74
+
75
+ `config.json` 必须是 UTF-8 编码的 JSON 对象。
76
+
77
+ ```json
78
+ {
79
+ "display_name": "Capoo",
80
+ "draw_output": "image",
81
+ "draw_mode": "direct",
82
+ "cooldown_seconds": 10,
83
+ "message_type": "command",
84
+ "message": ["随机capoo"],
85
+ "insert_message": ["添加随机capoo"],
86
+ "delete_message": ["删除随机capoo"],
87
+ "modify_admin_only": true,
88
+ "is_tome": false,
89
+ "output_prefix": "",
90
+ "output_suffix": "",
91
+ "is_at_sender": false
92
+ }
93
+ ```
94
+
95
+ | 配置项 | 默认值 | 说明 |
96
+ |---|---|---|
97
+ | `display_name` | 当前目录名 | 展示名称,最多 30 个字符 |
98
+ | `draw_output` | `image` | 输出类型:`text`、`image`、`record` 或 `video` |
99
+ | `draw_mode` | `direct` | `direct` 从资源池全部子目录递归抽取;`indirect` 先等概率选择一个一级子目录,再从其中抽取 |
100
+ | `cooldown_seconds` | `10` | 每个群独立计算的非负整数冷却秒数 |
101
+ | `message_type` | `command` | 匹配类型:`command`、`keyword` 或 `regex` |
102
+ | `message` | `随机<资源池ID>` | 触发条件列表;正则模式必须为 `[表达式, 展示文本]` |
103
+ | `insert_message` | 根据 `message` 生成 | 图片资源池的添加命令列表 |
104
+ | `delete_message` | 根据 `message` 生成 | 图片资源池的删除命令列表 |
105
+ | `modify_admin_only` | `true` | 是否只允许群主、群管理员和超级管理员增删图片 |
106
+ | `is_tome` | `false` | 触发抽取时是否必须 @机器人 |
107
+ | `output_prefix` | 空字符串 | 文本或图片输出前缀 |
108
+ | `output_suffix` | 空字符串 | 文本或图片输出后缀 |
109
+ | `is_at_sender` | `false` | 发送文本或图片时是否 @触发者 |
110
+
111
+ `output_prefix` 和 `output_suffix` 支持以下占位符:
112
+
113
+ | 占位符 | 内容 |
114
+ |---|---|
115
+ | `{filename}` | 素材文件名,包含扩展名 |
116
+ | `{filestem}` | 素材文件名,不包含扩展名 |
117
+
118
+ 资源池 ID 来自目录名,只允许小写字母、数字、下划线和连字符,长度为 1~32 个字符,并且必须以字母或数字开头。
119
+
120
+ ## 📦 支持的素材
121
+
122
+ | 输出类型 | 文件扩展名 |
123
+ |---|---|
124
+ | `text` | `.txt`,按 UTF-8 读取 |
125
+ | `image` | `.gif`、`.png`、`.jpg`、`.jpeg`、`.webp` |
126
+ | `record` | `.mp3`、`.wav`、`.ogg`、`.flac` |
127
+ | `video` | `.mp4`、`.avi`、`.flv`、`.wmv`、`.mov`、`.mpg`、`.mpeg` |
128
+
129
+ 命令模式下可以在抽取命令后附加文件名前缀,插件会从文件名以该前缀开头的候选素材中随机选择。
130
+
131
+ 图片资源池支持在群聊中发送或回复图片进行添加、删除。添加时会验证图片内容,只接受 PNG、JPEG、GIF 和 WebP;删除会移除内容哈希相同的所有图片。
132
+
133
+ ## 🎛️ 管理命令
134
+
135
+ ### 查看与群内开关
136
+
137
+ | 命令 | 权限 | 说明 |
138
+ |---|---|---|
139
+ | `随机池列表` | 群成员 | 查看当前群可见的资源池截图面板 |
140
+ | `开启随机池 <资源池ID...>` | 群主、群管理员或超级管理员 | 在当前群显式开启资源池,可覆盖全局禁用 |
141
+ | `关闭随机池 <资源池ID...>` | 群主、群管理员或超级管理员 | 在当前群显式关闭资源池 |
142
+
143
+ ### 跨群与全局管理
144
+
145
+ | 命令 | 权限 | 说明 |
146
+ |---|---|---|
147
+ | `随机池列表 <群号>` | 超级管理员 | 查看指定群的完整资源池状态 |
148
+ | `开启群随机池 <群号> <资源池ID...>` | 超级管理员 | 为指定群开启资源池 |
149
+ | `关闭群随机池 <群号> <资源池ID...>` | 超级管理员 | 为指定群关闭资源池 |
150
+ | `全局启用随机池 <资源池ID...>` | 超级管理员 | 恢复资源池的全局默认开启状态 |
151
+ | `全局禁用随机池 <资源池ID...>` | 超级管理员 | 默认对所有群禁用资源池;群显式开启仍可覆盖 |
152
+
153
+ 群级和全局开关保存在 `data/random/group_switches.json`,写入过程使用临时文件替换,避免部分写入。
154
+
155
+ ### 动态资源池管理
156
+
157
+ | 命令 | 权限 | 说明 |
158
+ |---|---|---|
159
+ | `新增随机池` | 超级管理员 | 通过交互式会话创建配置并立即注册 Matcher |
160
+ | `编辑随机池 <资源池ID>` | 超级管理员 | 修改配置并立即重建 Matcher |
161
+ | `删除随机池 <资源池ID>` | 超级管理员 | 确认后归档资源池并注销 Matcher |
162
+ | `随机池归档列表` | 超级管理员 | 查看可恢复的归档 ID |
163
+ | `恢复随机池 <归档ID>` | 超级管理员 | 恢复资源池并立即注册 Matcher |
164
+
165
+ 删除操作不会直接销毁素材,而是将整个资源池移动到 `data/random_archive/<时间戳>--<资源池ID>`。
166
+
167
+ ## 🔐 权限与状态优先级
168
+
169
+ 资源池状态按以下顺序解析:
170
+
171
+ 1. 群显式开启;
172
+ 2. 群显式关闭;
173
+ 3. 全局默认禁用;
174
+ 4. 默认开启。
175
+
176
+ 群级冷却按“群号 + 资源池 ID”分别计算,不同群互不影响。抽取发送失败时会释放本次冷却占用。
177
+
178
+ ## 📝 更新日志
179
+
180
+ ### 0.1.0
181
+
182
+ - 重构为动态资源池注册与管理架构。
183
+ - 新增网页截图管理面板及文本降级响应。
184
+ - 新增群级开关、全局默认状态和超级管理员跨群管理。
185
+ - 新增群级冷却、文本抽取、直接/分层抽取模式。
186
+ - 新增运行时创建、编辑、归档与恢复资源池。
187
+ - 使用 Pydantic v2 严格校验配置,并增强图片内容与文件名安全校验。
188
+ - 将图片增删权限默认值调整为仅管理员。
189
+
190
+ <details>
191
+ <summary>历史版本(展开/收起)</summary>
192
+
193
+ ### 0.0.9
194
+
195
+ - 添加删除图片默认所有人可以添加,要仅管理员需要单独设置
196
+ - 支持视频抽取
197
+
198
+ ### 0.0.8
199
+
200
+ - 支持动态删除图片(仅command)
201
+
202
+ ### 0.0.7
203
+
204
+ - 规定读取config.json文件必须为UTF-8编码
205
+ - 输出前后缀支持文件名
206
+ - 添加图片仅管理员可以操作
207
+
208
+ ### 0.0.6
209
+
210
+ - 支持动态添加图片(仅command)
211
+
212
+ ### 0.0.5
213
+
214
+ - 支持根据文件名定向抽取文件(仅command)
215
+
216
+ ### 0.0.4
217
+
218
+ - 去除draw_mode,现在可以抽取该文件夹下符合格式的全部文件
219
+ - 代码优化,分离config
220
+
221
+ ### 0.0.3
222
+
223
+ - 支持正则命令匹配
224
+
225
+ ### 0.0.2
226
+
227
+ - 修复未配置"message"时不能正常使用随机命令的bug
228
+ - 支持输出前后缀配置和at发送者
229
+
230
+ ### 0.0.1
231
+
232
+ - 插件初次发布
233
+
234
+ </details>
235
+
236
+ ## 💡 特别感谢
237
+
238
+ - [noneplugin/nonebot-plugin-petpet](https://github.com/noneplugin/nonebot-plugin-petpet) Nonebot2 插件,用于制作摸头等头像相关表情包
239
+
240
+ ## 📄 开源许可
241
+
242
+ 本项目使用 [MIT License](./LICENSE) 开源。
@@ -0,0 +1,28 @@
1
+ from nonebot import require
2
+ from nonebot.plugin import PluginMetadata
3
+
4
+
5
+ require("nonebot_plugin_alconna")
6
+ require("nonebot_plugin_waiter")
7
+
8
+ from .registry import pool_registry # noqa: E402
9
+
10
+
11
+ pool_registry.load_initial()
12
+ COMMANDS = pool_registry.metadata_usage()
13
+
14
+ __version__ = "0.1.0"
15
+ __plugin_meta__ = PluginMetadata(
16
+ name="随机抽图/语音",
17
+ description="Nonebot2 通用抽图/语音插件",
18
+ usage="\n".join(COMMANDS),
19
+ extra={
20
+ "version": __version__,
21
+ "license": "MIT",
22
+ "author": "jcjrobert <jcjrobbie@gmail.com>",
23
+ },
24
+ )
25
+
26
+ pool_registry.bind_metadata(__plugin_meta__)
27
+
28
+ from . import matchers as _matchers # noqa: E402,F401
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import time
5
+ from dataclasses import dataclass
6
+ from typing import Dict, Optional, Tuple
7
+
8
+
9
+ CooldownKey = Tuple[str, str]
10
+
11
+
12
+ @dataclass(frozen=True)
13
+ class CooldownLease:
14
+ key: Optional[CooldownKey]
15
+ token: int
16
+
17
+
18
+ class GroupCooldownManager:
19
+ def __init__(self) -> None:
20
+ self._entries: Dict[CooldownKey, Tuple[float, int]] = {}
21
+ self._lock = asyncio.Lock()
22
+ self._token = 0
23
+
24
+ async def acquire(
25
+ self,
26
+ pool_id: str,
27
+ group_id: str,
28
+ cooldown_seconds: int,
29
+ ) -> Optional[CooldownLease]:
30
+ if cooldown_seconds <= 0:
31
+ return CooldownLease(key=None, token=0)
32
+
33
+ now = time.monotonic()
34
+ key = (pool_id, group_id)
35
+ async with self._lock:
36
+ self._prune(now)
37
+ current = self._entries.get(key)
38
+ if current and current[0] > now:
39
+ return None
40
+ self._token += 1
41
+ token = self._token
42
+ self._entries[key] = (now + cooldown_seconds, token)
43
+ return CooldownLease(key=key, token=token)
44
+
45
+ async def release(self, lease: Optional[CooldownLease]) -> None:
46
+ if lease is None or lease.key is None:
47
+ return
48
+ async with self._lock:
49
+ current = self._entries.get(lease.key)
50
+ if current and current[1] == lease.token:
51
+ self._entries.pop(lease.key, None)
52
+
53
+ async def clear_pool(self, pool_id: str) -> None:
54
+ async with self._lock:
55
+ keys = [key for key in self._entries if key[0] == pool_id]
56
+ for key in keys:
57
+ self._entries.pop(key, None)
58
+
59
+ def _prune(self, now: float) -> None:
60
+ expired = [key for key, value in self._entries.items() if value[0] <= now]
61
+ for key in expired:
62
+ self._entries.pop(key, None)
63
+
64
+
65
+ group_cooldowns = GroupCooldownManager()