nonebot-plugin-group-rename 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.
- nonebot_plugin_group_rename-0.1.0/LICENSE +23 -0
- nonebot_plugin_group_rename-0.1.0/PKG-INFO +167 -0
- nonebot_plugin_group_rename-0.1.0/README.md +141 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename/__init__.py +202 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename/config.py +37 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename/logic.py +73 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename/state.py +64 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename.egg-info/PKG-INFO +167 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename.egg-info/SOURCES.txt +16 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename.egg-info/dependency_links.txt +1 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename.egg-info/requires.txt +4 -0
- nonebot_plugin_group_rename-0.1.0/nonebot_plugin_group_rename.egg-info/top_level.txt +1 -0
- nonebot_plugin_group_rename-0.1.0/pyproject.toml +36 -0
- nonebot_plugin_group_rename-0.1.0/setup.cfg +4 -0
- nonebot_plugin_group_rename-0.1.0/tests/test_handler.py +165 -0
- nonebot_plugin_group_rename-0.1.0/tests/test_logic.py +92 -0
- nonebot_plugin_group_rename-0.1.0/tests/test_permissions.py +83 -0
- nonebot_plugin_group_rename-0.1.0/tests/test_state.py +42 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Felis
|
|
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.
|
|
22
|
+
|
|
23
|
+
\n
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nonebot-plugin-group-rename
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: 适用于 PJSK、BanG Dream! 等五位房间号固车群的 NoneBot2 改群名插件,按群启用后通过群消息自动更新或清空群名车牌
|
|
5
|
+
Author-email: Felis <3397152010@qq.com>
|
|
6
|
+
Maintainer-email: Felis <3397152010@qq.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/Felis2026/nonebot-plugin-group-rename
|
|
9
|
+
Project-URL: Maintainer, https://github.com/Felis2026
|
|
10
|
+
Keywords: nonebot2,onebot,group-rename,group-name
|
|
11
|
+
Classifier: Framework :: AsyncIO
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: nonebot2<3.0.0,>=2.4.4
|
|
22
|
+
Requires-Dist: nonebot-adapter-onebot<3.0.0,>=2.4.0
|
|
23
|
+
Requires-Dist: nonebot-plugin-localstore<1.0.0,>=0.7.4
|
|
24
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
<div align="center">
|
|
28
|
+
|
|
29
|
+
<img src="assets/group-rename-cover.jpg" width="180" alt="改群名插件头图">
|
|
30
|
+
|
|
31
|
+
<h1>🏷️ 改群名 · Group Rename</h1>
|
|
32
|
+
|
|
33
|
+
<p><strong>面向 PJSK、BanG Dream! 等使用五位房间号的固车、冲榜群的 NoneBot 插件</strong></p>
|
|
34
|
+
|
|
35
|
+
<p>
|
|
36
|
+
<img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python >= 3.10">
|
|
37
|
+
<img src="https://img.shields.io/badge/NoneBot-2.4.4%2B-black" alt="NoneBot >= 2.4.4">
|
|
38
|
+
<img src="https://img.shields.io/badge/Adapter-OneBot%20V11-orange" alt="OneBot V11">
|
|
39
|
+
<img src="https://img.shields.io/badge/Version-0.1.0-ff69b4" alt="Version 0.1.0">
|
|
40
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<p>
|
|
44
|
+
<a href="#-适用范围">适用范围</a> ·
|
|
45
|
+
<a href="#-主要功能">主要功能</a> ·
|
|
46
|
+
<a href="#-快速开始">快速开始</a> ·
|
|
47
|
+
<a href="#️-指令与效果">指令与效果</a> ·
|
|
48
|
+
<a href="#️-配置">配置</a>
|
|
49
|
+
</p>
|
|
50
|
+
</div>
|
|
51
|
+
|
|
52
|
+
> 本项目参考了 [luban652 的 AstrBot 群名前缀插件](https://github.com/luban652/astrbot_plugin_group_prefix_manager)的功能需求
|
|
53
|
+
|
|
54
|
+
## 🎯 适用范围
|
|
55
|
+
|
|
56
|
+
适合 **PJSK**、**BanG Dream!** 等使用**五位数字房间号**的固车、冲榜群。
|
|
57
|
+
群管理员开启后,在群内发送房间号,Bot 会移除群名中的旧号码,并把新号码放在群名开头;
|
|
58
|
+
发送 `清空` 可移除号码。插件依据群消息中的数字工作,不区分具体游戏。
|
|
59
|
+
|
|
60
|
+
## ✨ 主要功能
|
|
61
|
+
|
|
62
|
+
| 功能 | 行为 |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| 车牌改名 | 群内发送独立的连续五位数字(车牌),将其放在群名开头 |
|
|
65
|
+
| 清空车牌 | 发送 `清空`,移除群名中的连续五位及以上数字 |
|
|
66
|
+
| 按群开关 | 默认关闭,由**群管理员及以上**在目标群内开启或关闭 |
|
|
67
|
+
| 混合消息 | 数字或“清空”可与 @、图片、回复同条发送;数字不能被非文本消息段隔开 |
|
|
68
|
+
| 防止覆盖 | 同一群的改名串行处理,并有默认 1 秒操作冷却 |
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 🚀 快速开始
|
|
73
|
+
|
|
74
|
+
环境要求:
|
|
75
|
+
|
|
76
|
+
- Python `>= 3.10`
|
|
77
|
+
- NoneBot `>= 2.4.4`
|
|
78
|
+
- OneBot V11 适配器
|
|
79
|
+
- 机器人账号具备修改目标群名称的权限
|
|
80
|
+
|
|
81
|
+
将仓库放到 Bot 设备上,在该 Bot 的 Python 环境中运行:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install -e /path/to/nonebot-plugin-group-rename
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
上架 NoneBot 商店后,可在 Bot 项目中使用 NB-CLI 安装:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
nb plugin install nonebot-plugin-group-rename
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
如果手动管理插件加载,请在 NoneBot 初始化后加载:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
nonebot.load_plugin("nonebot_plugin_group_rename")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
确认 Bot 已加载 OneBot V11 适配器,并在 `COMMAND_START` 中包含 `/`。随后由群管理员、群主或 SUPERUSER 在目标群发送 `/group_rename on` 或 `/改群名 on`。首次安装时所有群均关闭。
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## ⌨️ 指令与效果
|
|
105
|
+
|
|
106
|
+
### 群开关
|
|
107
|
+
|
|
108
|
+
| 指令 | 权限 | 作用 |
|
|
109
|
+
| --- | --- | --- |
|
|
110
|
+
| `/group_rename on` 或 `/改群名 on` | 群管理员、群主或 SUPERUSER | 开启当前群 |
|
|
111
|
+
| `/group_rename off` 或 `/改群名 off` | 群管理员、群主或 SUPERUSER | 关闭当前群 |
|
|
112
|
+
| `/group_rename status` 或 `/改群名 status` | 群管理员、群主或 SUPERUSER | 查看当前群状态 |
|
|
113
|
+
| `12345` | 默认群成员;开启管理员限制后仅上述管理员 | 将 `12345` 设置为群名车牌 |
|
|
114
|
+
| `清空` | 默认群成员;开启管理员限制后仅上述管理员 | 从群名移除旧车牌 |
|
|
115
|
+
|
|
116
|
+
### 改名前后举例
|
|
117
|
+
|
|
118
|
+
| 原群名 | 群消息 | 修改后 | 原因 |
|
|
119
|
+
| --- | --- | --- | --- |
|
|
120
|
+
| `冲榜群` | `12345` | `12345 冲榜群` | 新增车牌 |
|
|
121
|
+
| `12345 冲榜群` | `67890` | `67890 冲榜群` | 替换旧车牌 |
|
|
122
|
+
| `1234567冲榜群` | `67890` | `67890 冲榜群` | 连续五位及以上数字整体移除 |
|
|
123
|
+
| `12345 冲榜群` | `清空` | `冲榜群` | 清除车牌 |
|
|
124
|
+
| `12345` | `67890` | `67890` | 原群名只剩车牌时仅替换 |
|
|
125
|
+
| `12345` | `清空` | `12345` | 清空会得到空群名,因此保留原名并提示先手动设置群名 |
|
|
126
|
+
|
|
127
|
+
发送 `@某人 12345`、`[回复]12345` 或 `12345[图片]` 仍可触发。`12[图片]345`、`123456`、`文字 12345` 和同一条消息里的两段非空文本不会触发。被 `group_rename_ignore_patterns` 忽略的数字也不会触发。
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## ⚙️ 配置
|
|
133
|
+
|
|
134
|
+
不写配置文件即可使用默认参数,群开关仍默认关闭。
|
|
135
|
+
配置文件由 `nonebot-plugin-localstore` 管理。
|
|
136
|
+
|
|
137
|
+
| JSON 参数 | 默认值 | 说明 |
|
|
138
|
+
| --- | --- | --- |
|
|
139
|
+
| `group_rename_max_length` | `30` | 目标群名的最长字符数,最小为 `5` |
|
|
140
|
+
| `group_rename_enable_notify` | `true` | 冷却、读取或修改群名失败、仅剩车牌无法清空时发送提示 |
|
|
141
|
+
| `group_rename_admin_only` | `false` | 开启后仅允许群管理员、群主或 SUPERUSER 通过车牌消息触发改名 |
|
|
142
|
+
| `group_rename_ignore_patterns` | `[]` | 忽略的五位数字,使用 JSON 数组 |
|
|
143
|
+
| `group_rename_cooldown_seconds` | `1` | 每群改名尝试的冷却秒数 |
|
|
144
|
+
|
|
145
|
+
例如,在 `config.json` 中只填写需要覆盖的参数:
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"group_rename_admin_only": true,
|
|
150
|
+
"group_rename_ignore_patterns": ["12345", "99999"]
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
群开关状态另存在 `nonebot-plugin-localstore` 管理的数据目录下的 `groups.json`,不与参数配置混用;换设备时复制该状态文件,或在目标群重新执行 `/group_rename on`。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 📋 版本记录
|
|
159
|
+
|
|
160
|
+
### 0.1.0
|
|
161
|
+
|
|
162
|
+
- 支持 OneBot V11 群内发送连续五位数字更新群名,发送“清空”移除旧号码;@、图片和回复可与号码同条发送。
|
|
163
|
+
- 群开关默认关闭,由群管理员及以上在目标群管理;开关状态单独保存。
|
|
164
|
+
- 同群改名串行处理,默认冷却 1 秒;号码与原群名之间保留空格,清空后群名为空时保留原名。
|
|
165
|
+
- 使用独立 JSON 文件配置插件参数。
|
|
166
|
+
|
|
167
|
+
本项目代码采用 [MIT License](LICENSE)。
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="assets/group-rename-cover.jpg" width="180" alt="改群名插件头图">
|
|
4
|
+
|
|
5
|
+
<h1>🏷️ 改群名 · Group Rename</h1>
|
|
6
|
+
|
|
7
|
+
<p><strong>面向 PJSK、BanG Dream! 等使用五位房间号的固车、冲榜群的 NoneBot 插件</strong></p>
|
|
8
|
+
|
|
9
|
+
<p>
|
|
10
|
+
<img src="https://img.shields.io/badge/Python-3.10%2B-blue" alt="Python >= 3.10">
|
|
11
|
+
<img src="https://img.shields.io/badge/NoneBot-2.4.4%2B-black" alt="NoneBot >= 2.4.4">
|
|
12
|
+
<img src="https://img.shields.io/badge/Adapter-OneBot%20V11-orange" alt="OneBot V11">
|
|
13
|
+
<img src="https://img.shields.io/badge/Version-0.1.0-ff69b4" alt="Version 0.1.0">
|
|
14
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT License"></a>
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
<p>
|
|
18
|
+
<a href="#-适用范围">适用范围</a> ·
|
|
19
|
+
<a href="#-主要功能">主要功能</a> ·
|
|
20
|
+
<a href="#-快速开始">快速开始</a> ·
|
|
21
|
+
<a href="#️-指令与效果">指令与效果</a> ·
|
|
22
|
+
<a href="#️-配置">配置</a>
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
> 本项目参考了 [luban652 的 AstrBot 群名前缀插件](https://github.com/luban652/astrbot_plugin_group_prefix_manager)的功能需求
|
|
27
|
+
|
|
28
|
+
## 🎯 适用范围
|
|
29
|
+
|
|
30
|
+
适合 **PJSK**、**BanG Dream!** 等使用**五位数字房间号**的固车、冲榜群。
|
|
31
|
+
群管理员开启后,在群内发送房间号,Bot 会移除群名中的旧号码,并把新号码放在群名开头;
|
|
32
|
+
发送 `清空` 可移除号码。插件依据群消息中的数字工作,不区分具体游戏。
|
|
33
|
+
|
|
34
|
+
## ✨ 主要功能
|
|
35
|
+
|
|
36
|
+
| 功能 | 行为 |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| 车牌改名 | 群内发送独立的连续五位数字(车牌),将其放在群名开头 |
|
|
39
|
+
| 清空车牌 | 发送 `清空`,移除群名中的连续五位及以上数字 |
|
|
40
|
+
| 按群开关 | 默认关闭,由**群管理员及以上**在目标群内开启或关闭 |
|
|
41
|
+
| 混合消息 | 数字或“清空”可与 @、图片、回复同条发送;数字不能被非文本消息段隔开 |
|
|
42
|
+
| 防止覆盖 | 同一群的改名串行处理,并有默认 1 秒操作冷却 |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🚀 快速开始
|
|
47
|
+
|
|
48
|
+
环境要求:
|
|
49
|
+
|
|
50
|
+
- Python `>= 3.10`
|
|
51
|
+
- NoneBot `>= 2.4.4`
|
|
52
|
+
- OneBot V11 适配器
|
|
53
|
+
- 机器人账号具备修改目标群名称的权限
|
|
54
|
+
|
|
55
|
+
将仓库放到 Bot 设备上,在该 Bot 的 Python 环境中运行:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install -e /path/to/nonebot-plugin-group-rename
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
上架 NoneBot 商店后,可在 Bot 项目中使用 NB-CLI 安装:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
nb plugin install nonebot-plugin-group-rename
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
如果手动管理插件加载,请在 NoneBot 初始化后加载:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
nonebot.load_plugin("nonebot_plugin_group_rename")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
确认 Bot 已加载 OneBot V11 适配器,并在 `COMMAND_START` 中包含 `/`。随后由群管理员、群主或 SUPERUSER 在目标群发送 `/group_rename on` 或 `/改群名 on`。首次安装时所有群均关闭。
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## ⌨️ 指令与效果
|
|
79
|
+
|
|
80
|
+
### 群开关
|
|
81
|
+
|
|
82
|
+
| 指令 | 权限 | 作用 |
|
|
83
|
+
| --- | --- | --- |
|
|
84
|
+
| `/group_rename on` 或 `/改群名 on` | 群管理员、群主或 SUPERUSER | 开启当前群 |
|
|
85
|
+
| `/group_rename off` 或 `/改群名 off` | 群管理员、群主或 SUPERUSER | 关闭当前群 |
|
|
86
|
+
| `/group_rename status` 或 `/改群名 status` | 群管理员、群主或 SUPERUSER | 查看当前群状态 |
|
|
87
|
+
| `12345` | 默认群成员;开启管理员限制后仅上述管理员 | 将 `12345` 设置为群名车牌 |
|
|
88
|
+
| `清空` | 默认群成员;开启管理员限制后仅上述管理员 | 从群名移除旧车牌 |
|
|
89
|
+
|
|
90
|
+
### 改名前后举例
|
|
91
|
+
|
|
92
|
+
| 原群名 | 群消息 | 修改后 | 原因 |
|
|
93
|
+
| --- | --- | --- | --- |
|
|
94
|
+
| `冲榜群` | `12345` | `12345 冲榜群` | 新增车牌 |
|
|
95
|
+
| `12345 冲榜群` | `67890` | `67890 冲榜群` | 替换旧车牌 |
|
|
96
|
+
| `1234567冲榜群` | `67890` | `67890 冲榜群` | 连续五位及以上数字整体移除 |
|
|
97
|
+
| `12345 冲榜群` | `清空` | `冲榜群` | 清除车牌 |
|
|
98
|
+
| `12345` | `67890` | `67890` | 原群名只剩车牌时仅替换 |
|
|
99
|
+
| `12345` | `清空` | `12345` | 清空会得到空群名,因此保留原名并提示先手动设置群名 |
|
|
100
|
+
|
|
101
|
+
发送 `@某人 12345`、`[回复]12345` 或 `12345[图片]` 仍可触发。`12[图片]345`、`123456`、`文字 12345` 和同一条消息里的两段非空文本不会触发。被 `group_rename_ignore_patterns` 忽略的数字也不会触发。
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## ⚙️ 配置
|
|
107
|
+
|
|
108
|
+
不写配置文件即可使用默认参数,群开关仍默认关闭。
|
|
109
|
+
配置文件由 `nonebot-plugin-localstore` 管理。
|
|
110
|
+
|
|
111
|
+
| JSON 参数 | 默认值 | 说明 |
|
|
112
|
+
| --- | --- | --- |
|
|
113
|
+
| `group_rename_max_length` | `30` | 目标群名的最长字符数,最小为 `5` |
|
|
114
|
+
| `group_rename_enable_notify` | `true` | 冷却、读取或修改群名失败、仅剩车牌无法清空时发送提示 |
|
|
115
|
+
| `group_rename_admin_only` | `false` | 开启后仅允许群管理员、群主或 SUPERUSER 通过车牌消息触发改名 |
|
|
116
|
+
| `group_rename_ignore_patterns` | `[]` | 忽略的五位数字,使用 JSON 数组 |
|
|
117
|
+
| `group_rename_cooldown_seconds` | `1` | 每群改名尝试的冷却秒数 |
|
|
118
|
+
|
|
119
|
+
例如,在 `config.json` 中只填写需要覆盖的参数:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"group_rename_admin_only": true,
|
|
124
|
+
"group_rename_ignore_patterns": ["12345", "99999"]
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
群开关状态另存在 `nonebot-plugin-localstore` 管理的数据目录下的 `groups.json`,不与参数配置混用;换设备时复制该状态文件,或在目标群重新执行 `/group_rename on`。
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## 📋 版本记录
|
|
133
|
+
|
|
134
|
+
### 0.1.0
|
|
135
|
+
|
|
136
|
+
- 支持 OneBot V11 群内发送连续五位数字更新群名,发送“清空”移除旧号码;@、图片和回复可与号码同条发送。
|
|
137
|
+
- 群开关默认关闭,由群管理员及以上在目标群管理;开关状态单独保存。
|
|
138
|
+
- 同群改名串行处理,默认冷却 1 秒;号码与原群名之间保留空格,清空后群名为空时保留原名。
|
|
139
|
+
- 使用独立 JSON 文件配置插件参数。
|
|
140
|
+
|
|
141
|
+
本项目代码采用 [MIT License](LICENSE)。
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""通过 OneBot V11 群消息中的车牌改群名。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
|
|
8
|
+
from nonebot import get_driver, logger, on_command, on_message, require
|
|
9
|
+
from nonebot.adapters import Event
|
|
10
|
+
from nonebot.adapters.onebot.v11 import ActionFailed, Bot, GroupMessageEvent
|
|
11
|
+
from nonebot.params import CommandArg
|
|
12
|
+
from nonebot.permission import Permission
|
|
13
|
+
from nonebot.plugin import PluginMetadata
|
|
14
|
+
|
|
15
|
+
# 持久化目录由 localstore 决定,避免依赖 Bot 的启动工作目录。
|
|
16
|
+
require("nonebot_plugin_localstore")
|
|
17
|
+
import nonebot_plugin_localstore as localstore
|
|
18
|
+
|
|
19
|
+
from .config import Config, load_config
|
|
20
|
+
from .logic import GroupCooldown, build_group_name, message_trigger
|
|
21
|
+
from .state import GroupState
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
__plugin_meta__ = PluginMetadata(
|
|
25
|
+
name="改群名",
|
|
26
|
+
description="适用于 PJSK、BanG Dream! 等使用五位房间号固车、冲榜群的改群名插件。按群启用后,发送独立的连续五位数字可更新群名开头的车牌,发送“清空”可移除旧车牌。",
|
|
27
|
+
usage="群管理员、群主或 SUPERUSER 在目标群发送 /group_rename on 或 /改群名 on 开启;使用 off 关闭、status 查看状态。开启后,在群内发送五位房间号或“清空”即可操作群名。",
|
|
28
|
+
type="application",
|
|
29
|
+
homepage="https://github.com/Felis2026/nonebot-plugin-group-rename",
|
|
30
|
+
config=Config,
|
|
31
|
+
supported_adapters={"~onebot.v11"},
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
config = load_config(localstore.get_config_file("nonebot_plugin_group_rename", "config.json"))
|
|
35
|
+
# 显式指定插件名,使数据目录不依赖当前模块的调用上下文。
|
|
36
|
+
state = GroupState(localstore.get_data_file("nonebot_plugin_group_rename", "groups.json"))
|
|
37
|
+
cooldown = GroupCooldown(config.group_rename_cooldown_seconds)
|
|
38
|
+
group_locks: dict[str, asyncio.Lock] = {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _group_lock(group_id: str) -> asyncio.Lock:
|
|
42
|
+
"""按群复用锁,让改名和群开关操作按到达顺序执行。"""
|
|
43
|
+
# 同一事件循环内创建锁的过程不含 await,不会为同群创建两把锁。
|
|
44
|
+
if group_id not in group_locks:
|
|
45
|
+
group_locks[group_id] = asyncio.Lock()
|
|
46
|
+
return group_locks[group_id]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _is_group_admin_or_superuser(event: Event) -> bool:
|
|
50
|
+
"""允许当前群管理员、群主及 NoneBot 超级用户管理本群。"""
|
|
51
|
+
if not isinstance(event, GroupMessageEvent):
|
|
52
|
+
return False
|
|
53
|
+
# OneBot V11 的群内角色只有 owner/admin/member;缺失角色时不放行。
|
|
54
|
+
return (
|
|
55
|
+
event.sender.role in {"admin", "owner"}
|
|
56
|
+
or event.get_user_id() in get_driver().config.superusers
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _message_trigger(event: GroupMessageEvent) -> str | None:
|
|
61
|
+
"""从 OneBot 消息段读取车牌,不跨 @、图片或回复拼接数字。"""
|
|
62
|
+
segments = (
|
|
63
|
+
(segment.type, str(segment.data.get("text", "")))
|
|
64
|
+
for segment in event.get_message()
|
|
65
|
+
)
|
|
66
|
+
return message_trigger(segments, config.group_rename_ignore_patterns)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _is_rename_message(event: Event) -> bool:
|
|
70
|
+
"""只让已启用群的有效车牌消息进入改名处理。"""
|
|
71
|
+
if not isinstance(event, GroupMessageEvent):
|
|
72
|
+
return False
|
|
73
|
+
if not state.is_enabled(str(event.group_id)):
|
|
74
|
+
return False
|
|
75
|
+
if config.group_rename_admin_only and not _is_group_admin_or_superuser(event):
|
|
76
|
+
return False
|
|
77
|
+
return _message_trigger(event) is not None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _is_permission_failure(exc: ActionFailed) -> bool:
|
|
81
|
+
"""只根据 OneBot 返回的权限字样识别权限失败,不猜测 retcode 的平台含义。"""
|
|
82
|
+
# 不同 OneBot 实现的错误码并不统一,文字字段更适合做保守识别。
|
|
83
|
+
details = " ".join(str(exc.info.get(key, "")) for key in ("wording", "message", "msg"))
|
|
84
|
+
details = details.casefold()
|
|
85
|
+
return any(
|
|
86
|
+
marker in details
|
|
87
|
+
for marker in ("权限", "管理员", "无权", "permission", "not admin", "forbidden")
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
# ================================ 群开关命令 ================================ #
|
|
92
|
+
|
|
93
|
+
group_rename_command = on_command(
|
|
94
|
+
"group_rename",
|
|
95
|
+
aliases={"改群名"},
|
|
96
|
+
permission=Permission(_is_group_admin_or_superuser),
|
|
97
|
+
priority=1,
|
|
98
|
+
block=False,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@group_rename_command.handle()
|
|
103
|
+
async def handle_group_rename_command(event: GroupMessageEvent, args=CommandArg()) -> None:
|
|
104
|
+
"""群管理员、群主或超级用户管理当前群的插件开关。"""
|
|
105
|
+
action = args.extract_plain_text().strip().lower()
|
|
106
|
+
group_id = str(event.group_id)
|
|
107
|
+
if action == "status":
|
|
108
|
+
enabled = state.is_enabled(group_id)
|
|
109
|
+
await group_rename_command.send("改群名:已开启" if enabled else "改群名:已关闭")
|
|
110
|
+
return
|
|
111
|
+
if action not in {"on", "off"}:
|
|
112
|
+
await group_rename_command.send("用法:/group_rename 或 /改群名,后接 on|off|status")
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
# 关闭须等待当前群已开始的改名完成;排队中的改名随后会重新检查开关。
|
|
116
|
+
async with _group_lock(group_id):
|
|
117
|
+
try:
|
|
118
|
+
changed = await state.set_enabled(group_id, action == "on")
|
|
119
|
+
except OSError as exc:
|
|
120
|
+
logger.error(f"保存群 {group_id} 的改群名开关失败: {exc}")
|
|
121
|
+
await group_rename_command.send("改群名开关保存失败")
|
|
122
|
+
return
|
|
123
|
+
status = "已开启" if action == "on" else "已关闭"
|
|
124
|
+
await group_rename_command.send(f"改群名:{status}" if changed else f"改群名:已经{status[1:]}")
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ================================ 群名修改 ================================ #
|
|
128
|
+
|
|
129
|
+
rename_matcher = on_message(rule=_is_rename_message, priority=1, block=False)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@rename_matcher.handle()
|
|
133
|
+
async def handle_rename(bot: Bot, event: GroupMessageEvent) -> None:
|
|
134
|
+
"""按群串行读取当前群名,在冷却许可时提交改名。"""
|
|
135
|
+
trigger = _message_trigger(event)
|
|
136
|
+
if trigger is None:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
group_id = str(event.group_id)
|
|
140
|
+
async with _group_lock(group_id):
|
|
141
|
+
# 匹配到执行之间可能关闭群开关或改动权限,必须再次检查。
|
|
142
|
+
if not state.is_enabled(group_id):
|
|
143
|
+
return
|
|
144
|
+
if config.group_rename_admin_only and not _is_group_admin_or_superuser(event):
|
|
145
|
+
return
|
|
146
|
+
if not cooldown.allow(group_id):
|
|
147
|
+
if config.group_rename_enable_notify:
|
|
148
|
+
await rename_matcher.send("⏳改名太频繁,请稍后再试")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# ================================ 读取当前群名 ================================ #
|
|
152
|
+
# 禁用适配器缓存,连续改名以服务端当前群名为准。
|
|
153
|
+
try:
|
|
154
|
+
info = await bot.get_group_info(group_id=event.group_id, no_cache=True)
|
|
155
|
+
except ActionFailed as exc:
|
|
156
|
+
logger.error(f"读取群 {group_id} 名称被 OneBot 拒绝: {exc!r}")
|
|
157
|
+
if config.group_rename_enable_notify:
|
|
158
|
+
await rename_matcher.send("❌读取群名失败:OneBot 接口返回错误")
|
|
159
|
+
return
|
|
160
|
+
except Exception as exc:
|
|
161
|
+
logger.error(f"读取群 {group_id} 名称异常: {exc!r}")
|
|
162
|
+
if config.group_rename_enable_notify:
|
|
163
|
+
await rename_matcher.send("❌读取群名失败:请求异常,请稍后重试")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
if not isinstance(info, Mapping) or not isinstance(info.get("group_name"), str):
|
|
167
|
+
logger.error(f"群 {group_id} 的信息缺少可用群名")
|
|
168
|
+
if config.group_rename_enable_notify:
|
|
169
|
+
await rename_matcher.send("❌读取群名失败:接口未返回有效群名")
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
# ================================ 构造并提交群名 ================================ #
|
|
173
|
+
current_name = info["group_name"]
|
|
174
|
+
try:
|
|
175
|
+
target_name = build_group_name(
|
|
176
|
+
current_name,
|
|
177
|
+
None if trigger == "clear" else trigger,
|
|
178
|
+
config.group_rename_max_length,
|
|
179
|
+
)
|
|
180
|
+
# OneBot 的 set_group_name 不能提交空群名;仅有车牌时保留原名并提示。
|
|
181
|
+
if not target_name:
|
|
182
|
+
cooldown.cancel_noop(group_id)
|
|
183
|
+
if config.group_rename_enable_notify:
|
|
184
|
+
await rename_matcher.send("群名只剩车牌,无法清空;请先手动设置群名")
|
|
185
|
+
return
|
|
186
|
+
if target_name == current_name:
|
|
187
|
+
cooldown.cancel_noop(group_id)
|
|
188
|
+
return
|
|
189
|
+
await bot.set_group_name(group_id=event.group_id, group_name=target_name)
|
|
190
|
+
except ActionFailed as exc:
|
|
191
|
+
logger.error(f"修改群 {group_id} 名称被 OneBot 拒绝: {exc!r}")
|
|
192
|
+
if config.group_rename_enable_notify:
|
|
193
|
+
message = (
|
|
194
|
+
"❌改群名失败:Bot 缺少修改群名的权限"
|
|
195
|
+
if _is_permission_failure(exc)
|
|
196
|
+
else "❌改群名失败:OneBot 拒绝了改名请求,请检查 Bot 权限或服务端日志"
|
|
197
|
+
)
|
|
198
|
+
await rename_matcher.send(message)
|
|
199
|
+
except Exception as exc:
|
|
200
|
+
logger.error(f"修改群 {group_id} 名称异常: {exc!r}")
|
|
201
|
+
if config.group_rename_enable_notify:
|
|
202
|
+
await rename_matcher.send("❌改群名失败:请求异常,请稍后重试")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""独立插件的 JSON 配置项与加载逻辑。"""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from nonebot import logger
|
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Config(BaseModel):
|
|
10
|
+
"""校验改群名参数;使用独立文件,避免依赖宿主 Bot 的配置格式。"""
|
|
11
|
+
|
|
12
|
+
model_config = ConfigDict(extra="forbid")
|
|
13
|
+
|
|
14
|
+
# 至少容纳完整五位车牌,防止长度配置把有效车牌截断成无效群名前缀。
|
|
15
|
+
group_rename_max_length: int = Field(default=30, ge=5)
|
|
16
|
+
group_rename_enable_notify: bool = True
|
|
17
|
+
group_rename_admin_only: bool = False
|
|
18
|
+
group_rename_ignore_patterns: list[str] = Field(default_factory=list)
|
|
19
|
+
group_rename_cooldown_seconds: float = Field(default=1, ge=0)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# ================================ JSON 配置加载 ================================ #
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def load_config(path: Path) -> Config:
|
|
26
|
+
"""从 localstore 配置文件加载参数;无文件时使用默认值,有错时阻止插件加载。"""
|
|
27
|
+
if not path.exists():
|
|
28
|
+
logger.info(f"改群名使用默认配置;可在 {path} 创建 JSON 文件自定义参数")
|
|
29
|
+
return Config()
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
config = Config.model_validate_json(path.read_text(encoding="utf-8"))
|
|
33
|
+
except (OSError, ValidationError) as exc:
|
|
34
|
+
raise RuntimeError(f"读取改群名配置文件 {path} 失败: {exc}") from exc
|
|
35
|
+
|
|
36
|
+
logger.info(f"改群名已读取配置文件: {path}")
|
|
37
|
+
return config
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""消息识别、群名构造和按群冷却。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import time
|
|
7
|
+
from collections.abc import Iterable
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# 车牌必须是连续五位;旧群名中的五位及以上数字按整段删除。
|
|
11
|
+
DIGIT_PATTERN = re.compile(r"^\d{5}$")
|
|
12
|
+
OLD_NUMBER_PATTERN = re.compile(r"\d{5,}")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# ================================ 消息识别 ================================ #
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def classify_message(message: str, ignored: list[str]) -> str | None:
|
|
19
|
+
"""返回五位车牌或清空标记;其他文本不触发改名。"""
|
|
20
|
+
message = message.strip()
|
|
21
|
+
if message == "清空":
|
|
22
|
+
return "clear"
|
|
23
|
+
if DIGIT_PATTERN.fullmatch(message) and message not in ignored:
|
|
24
|
+
return message
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def message_trigger(segments: Iterable[tuple[str, str]], ignored: list[str]) -> str | None:
|
|
29
|
+
"""仅识别单段连续文本,允许前后存在 @、图片或回复。"""
|
|
30
|
+
runs: list[str] = []
|
|
31
|
+
current: list[str] = []
|
|
32
|
+
for segment_type, content in segments:
|
|
33
|
+
if segment_type == "text":
|
|
34
|
+
current.append(content)
|
|
35
|
+
elif current:
|
|
36
|
+
runs.append("".join(current).strip())
|
|
37
|
+
current.clear()
|
|
38
|
+
if current:
|
|
39
|
+
runs.append("".join(current).strip())
|
|
40
|
+
|
|
41
|
+
# 非文本段夹在两段数字中间时,不能跨段拼成车牌。
|
|
42
|
+
nonempty_runs = [run for run in runs if run]
|
|
43
|
+
return classify_message(nonempty_runs[0], ignored) if len(nonempty_runs) == 1 else None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ================================ 群名构造 ================================ #
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build_group_name(current_name: str, prefix: str | None, max_length: int) -> str:
|
|
50
|
+
"""清理旧数字,新增车牌时与原群名之间留一个空格。"""
|
|
51
|
+
pure_name = OLD_NUMBER_PATTERN.sub("", current_name).strip()
|
|
52
|
+
target = pure_name if prefix is None else f"{prefix} {pure_name}" if pure_name else prefix
|
|
53
|
+
return target[:max_length].rstrip() if len(target) > max_length else target
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class GroupCooldown:
|
|
57
|
+
"""按群记录操作尝试;失败请求也进入冷却。"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, seconds: float = 1):
|
|
60
|
+
self.seconds = seconds
|
|
61
|
+
self.last_by_group: dict[str, float] = {}
|
|
62
|
+
|
|
63
|
+
def allow(self, group_id: str) -> bool:
|
|
64
|
+
"""检查冷却并登记本次操作;由调用者在同群锁内调用。"""
|
|
65
|
+
now = time.monotonic()
|
|
66
|
+
if now - self.last_by_group.get(group_id, float("-inf")) < self.seconds:
|
|
67
|
+
return False
|
|
68
|
+
self.last_by_group[group_id] = now
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
def cancel_noop(self, group_id: str) -> None:
|
|
72
|
+
"""当前群无需提交改名时撤销本次登记;调用者须持有同群锁。"""
|
|
73
|
+
self.last_by_group.pop(group_id, None)
|