Merge pull request #2891 from sufan721/feat-add-role-module

- Auto-scan roles/*.json on startup and merge into built-in roles
This commit is contained in:
zhayujie
2026-06-13 18:23:57 +08:00
committed by GitHub
2 changed files with 73 additions and 9 deletions

View File

@@ -4,18 +4,40 @@
- `$角色/$role <角色名>` - 让AI扮演该角色角色名支持模糊匹配。
- `$停止扮演` - 停止角色扮演。
添加自定义角色请在`roles/roles.json`中添加。
## 目录结构
```
plugins/role/
├── README.md
├── __init__.py
├── role.py # 插件主逻辑
├── roles.json # 内置角色库(所有默认角色)
├── schema/
│ └── role_template.json # 角色模板,新增角色时复制此文件
└── roles/ # 可选:放自定义角色文件,自动加载
└── 我的角色.json # 同title会覆盖内置角色不同则追加
```
## 添加自定义角色
1. 新建roles目录
2. 复制 下面模板 到 `roles/` 目录并重命名(如 `我的角色.json`)。
3. 编辑文件内容,填写角色信息。
4. 重启Bot即可生效插件会自动扫描 `roles/` 目录下的所有 `.json` 文件。
> **覆盖规则**:如果自定义角色的 `title` 与内置角色同名,则覆盖内置角色;否则作为新角色追加。无需修改任何映射文件。
(大部分prompt来自https://github.com/rockbenben/ChatGPT-Shortcut/blob/main/src/data/users.tsx)
以下为例子:
以下为角色文件内容例子:
```json
{
"title": "写作助理",
"description": "As a writing improvement assistant, your task is to improve the spelling, grammar, clarity, concision, and overall readability of the text I provided, while breaking down long sentences, reducing repetition, and providing suggestions for improvement. Please provide only the corrected Chinese version of the text and avoid including explanations. Please treat every message I send later as text content.",
"descn": "作为一名中文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。请把我之后的每一条消息都当作文本内容。",
"description": "As a writing improvement assistant...",
"descn": "作为一名中文写作改进助理...",
"wrapper": "内容是:\n\"%s\"",
"remark": "最常使用的角色,用于优化文本的语法、清晰度和简洁度,提高可读性。"
"remark": "最常使用的角色,用于优化文本的语法、清晰度和简洁度,提高可读性。",
"tags": ["favorite", "write"]
}
```
@@ -24,3 +46,16 @@
- `descn`: 使用`$角色`触发时使用中文prompt。
- `wrapper`: 用于包装用户消息,可起到强调作用,避免回复离题。
- `remark`: 简短描述该角色,在打印帮助文档时显示。
- `tags`: 角色分类标签,可用`$角色类型 <标签>`查看。
```json
{
"title": "",
"description": "",
"descn": "",
"wrapper": "",
"remark": "",
"tags": []
}
```

View File

@@ -57,6 +57,14 @@ class Role(Plugin):
logger.warning(f"[Role] unknown tag {tag} ")
self.tags[tag] = (tag, [])
self.tags[tag][1].append(role)
# 扫描 roles/ 目录,加载可选的扩展/覆盖角色文件
roles_dir = os.path.join(curdir, "roles")
if os.path.isdir(roles_dir):
for fname in sorted(os.listdir(roles_dir)):
if fname.endswith(".json"):
self._load_role_file(os.path.join(roles_dir, fname))
for tag in list(self.tags.keys()):
if len(self.tags[tag][1]) == 0:
logger.debug(f"[Role] no role found for tag {tag} ")
@@ -74,6 +82,27 @@ class Role(Plugin):
logger.warn("[Role] init failed, ignore or see https://github.com/zhayujie/chatgpt-on-wechat/tree/master/plugins/role .")
raise e
def _load_role_file(self, filepath):
"""从roles/目录加载单个角色文件同title则覆盖内置角色否则追加"""
try:
with open(filepath, "r", encoding="utf-8") as f:
role = json.load(f)
title_lower = role["title"].lower()
if title_lower in self.roles:
old_tags = self.roles[title_lower].get("tags", [])
for tag in old_tags:
if tag in self.tags:
self.tags[tag][1] = [r for r in self.tags[tag][1] if r["title"].lower() != title_lower]
logger.info(f"[Role] overridden: {role['title']}")
self.roles[title_lower] = role
for tag in role.get("tags", []):
if tag not in self.tags:
self.tags[tag] = (tag, [])
self.tags[tag][1].append(role)
logger.debug(f"[Role] loaded: {role['title']} from {os.path.basename(filepath)}")
except Exception as e:
logger.warn(f"[Role] failed to load {filepath}: {e}")
def get_role(self, name, find_closest=True, min_sim=0.35):
name = name.lower()
found_role = None