Skip to content

Commit 150e58e

Browse files
committed
up
1 parent 575bb85 commit 150e58e

File tree

7 files changed

+220
-26
lines changed

7 files changed

+220
-26
lines changed

README.md

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,49 @@ plugins = [
168168

169169
在 NoneBot2 项目的 `.env` 文件中添加下表中的配置
170170

171-
| 配置项 | 必填 | 默认值 | 说明 |
172-
| :-----------------------------: | :--: | :-------: | :----------------------------: |
173-
| `PMN_INDEX_TEMPLATE` || `default` | 首页展示模板的名称 |
174-
| `PMN_DETAIL_TEMPLATE` || `default` | 插件详情模板的名称 |
175-
| `PMN_FUNC_DETAIL_TEMPLATE` || `default` | 插件功能详情模板的名称 |
176-
| `PMN_ONLY_SUPERUSER_SEE_HIDDEN` || `False` | 是否仅超级用户可以查看隐藏内容 |
171+
| 配置项 | 必填 | 默认值 | 说明 |
172+
| :-----------------------------------: | :--: | :-------: | :----------------------------: |
173+
| **本体配置** | | | |
174+
| `PMN_INDEX_TEMPLATE` || `default` | 首页展示模板的名称 |
175+
| `PMN_DETAIL_TEMPLATE` || `default` | 插件详情模板的名称 |
176+
| `PMN_FUNC_DETAIL_TEMPLATE` || `default` | 插件功能详情模板的名称 |
177+
| `PMN_ONLY_SUPERUSER_SEE_HIDDEN` || `False` | 是否仅超级用户可以查看隐藏内容 |
178+
| **默认模板配置** | | | |
179+
| `PMN_DEFAULT_DARK` || `False` | 是否使用暗色模式 |
180+
| `PMN_DEFAULT_ENABLE_BUILTIN_CODE_CSS` || `True` | 是否启用内置代码着色 CSS |
181+
| `PMN_DEFAULT_ADDITIONAL_CSS` || `[]` | 要附加的 CSS 路径列表 |
182+
| `PMN_DEFAULT_ADDITIONAL_JS` || `[]` | 要附加的 JS 路径列表 |
177183

178184
## 🎉 使用
179185

180186
发送 `帮助` 指令试试吧!
181187

188+
### 外部菜单加载说明
189+
190+
本插件兼容原 PicMenu 的外部菜单路径及格式,并在其基础上做了些许扩展
191+
192+
本插件会读取以下目录中的所有 `json` / `yml(yaml)` / `toml` 文件并作为外部菜单配置加载
193+
194+
- 插件 localstore 路径下的 `external_infos` 文件夹
195+
- 原 PicMenu 的 `menu_config/menus` 文件夹
196+
197+
插件会将其文件名作为 `插件 ID` (如为顶层级插件,通常为插件包名) 来判断是否覆盖已存在的插件的菜单信息
198+
仅被配置文件定义的顶层属性会被覆盖
199+
200+
配置文件定义 Schema 请查看 [defs/ExternalPluginInfo.json](./defs/ExternalPluginInfo.json)
201+
182202
## 🔧 开发
183203

204+
### 插件开发者对接
205+
206+
文档待补充
207+
208+
### Mixin
209+
210+
文档待补充
211+
212+
### 菜单模板
213+
184214
文档待补充
185215

186216
## 📞 联系
@@ -204,6 +234,10 @@ Telegram:[@lgc2333](https://t.me/lgc2333)
204234

205235
## 📝 更新日志
206236

237+
### 0.1.6
238+
239+
- 尝试修复与 Pydantic V1 的兼容性
240+
207241
### 0.1.5
208242

209243
- 修复上个版本中的 Bug

defs/ExternalPluginInfo.json

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
"$defs": {
3+
"PMDataItem": {
4+
"properties": {
5+
"func": { "title": "Func", "type": "string" },
6+
"trigger_method": { "title": "Trigger Method", "type": "string" },
7+
"trigger_condition": { "title": "Trigger Condition", "type": "string" },
8+
"brief_des": { "title": "Brief Des", "type": "string" },
9+
"detail_des": { "title": "Detail Des", "type": "string" },
10+
"pmn_hidden": { "default": false, "title": "Pmn Hidden", "type": "boolean" },
11+
"pmn_template": {
12+
"anyOf": [{ "type": "string" }, { "type": "null" }],
13+
"default": null,
14+
"title": "Pmn Template"
15+
}
16+
},
17+
"required": [
18+
"func",
19+
"trigger_method",
20+
"trigger_condition",
21+
"brief_des",
22+
"detail_des"
23+
],
24+
"title": "PMDataItem",
25+
"type": "object"
26+
},
27+
"PMNData": {
28+
"properties": {
29+
"hidden": { "default": false, "title": "Hidden", "type": "boolean" },
30+
"hidden_mixin": {
31+
"anyOf": [{ "type": "string" }, { "type": "null" }],
32+
"default": null,
33+
"title": "Hidden Mixin"
34+
},
35+
"func_hidden_mixin": {
36+
"anyOf": [{ "type": "string" }, { "type": "null" }],
37+
"default": null,
38+
"title": "Func Hidden Mixin"
39+
},
40+
"markdown": { "default": false, "title": "Markdown", "type": "boolean" },
41+
"template": {
42+
"anyOf": [{ "type": "string" }, { "type": "null" }],
43+
"default": null,
44+
"title": "Template"
45+
}
46+
},
47+
"title": "PMNData",
48+
"type": "object"
49+
}
50+
},
51+
"properties": {
52+
"name": {
53+
"anyOf": [{ "type": "string" }, { "type": "null" }],
54+
"default": null,
55+
"title": "Name"
56+
},
57+
"author": {
58+
"anyOf": [{ "type": "string" }, { "type": "null" }],
59+
"default": null,
60+
"title": "Author"
61+
},
62+
"version": {
63+
"anyOf": [{ "type": "string" }, { "type": "null" }],
64+
"default": null,
65+
"title": "Version"
66+
},
67+
"description": {
68+
"anyOf": [{ "type": "string" }, { "type": "null" }],
69+
"default": null,
70+
"title": "Description"
71+
},
72+
"usage": {
73+
"anyOf": [{ "type": "string" }, { "type": "null" }],
74+
"default": null,
75+
"title": "Usage"
76+
},
77+
"funcs": {
78+
"anyOf": [
79+
{ "items": { "$ref": "#/$defs/PMDataItem" }, "type": "array" },
80+
{ "type": "null" }
81+
],
82+
"default": null,
83+
"title": "Funcs"
84+
},
85+
"pmn": {
86+
"$ref": "#/$defs/PMNData",
87+
"default": {
88+
"hidden": false,
89+
"hidden_mixin": null,
90+
"func_hidden_mixin": null,
91+
"markdown": false,
92+
"template": null
93+
}
94+
}
95+
},
96+
"title": "ExternalPluginInfo",
97+
"type": "object"
98+
}

nonebot_plugin_picmenu_next/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from .data_source import refresh_infos
1313
from .templates import load_builtin_templates
1414

15-
__version__ = "0.1.5.post1"
15+
__version__ = "0.1.6"
1616
__plugin_meta__ = PluginMetadata(
1717
name="PicMenu Next",
1818
description="新一代的图片帮助插件",

nonebot_plugin_picmenu_next/data_source/collect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def _load_file(path: Path) -> ExternalPluginInfo:
148148
import tomllib
149149
except ImportError:
150150
try:
151-
import tomli as tomllib
151+
import tomli as tomllib # pyright: ignore[reportMissingImports]
152152
except ImportError as e:
153153
raise ImportError(
154154
"Missing dependency for parsing toml files, please install using"

nonebot_plugin_picmenu_next/data_source/models.py

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,23 @@
1111
)
1212
from nonebot import get_plugin
1313
from nonebot.plugin import Plugin
14-
from pydantic import BaseModel, Field
14+
from pydantic import BaseModel, ConfigDict, Field
1515

16+
from ..utils import normalize_plugin_name
1617
from .pinyin import PinyinChunkSequence
1718

1819
T = TypeVar("T")
1920

2021

2122
if PYDANTIC_V2:
23+
compat_model_config: ConfigDict = {}
2224
CompatModel = BaseModel
2325
else:
24-
CompatModel = get_model_with_config(
25-
{
26-
"arbitrary_types_allowed": True,
27-
"keep_untouched": (cached_property,),
28-
},
29-
)
26+
compat_model_config: ConfigDict = {
27+
"arbitrary_types_allowed": True,
28+
"keep_untouched": (cached_property,),
29+
}
30+
CompatModel = get_model_with_config(compat_model_config)
3031

3132

3233
class PMDataItem(CompatModel):
@@ -77,6 +78,25 @@ def normalize_input(cls, values: Any): # noqa: N805
7778
return values
7879

7980

81+
class OptionalPMNPluginInfo(CompatModel):
82+
name: str | None = None
83+
plugin_id: str | None = None
84+
author: str | None = None
85+
version: str | None = None
86+
description: str | None = None
87+
usage: str | None = None
88+
pm_data: list[PMDataItem] | None = None
89+
pmn: PMNData = PMNData()
90+
91+
def to_required(self, name: str | None = None):
92+
if name is None and self.name is None:
93+
raise ValueError("`name` is required for PMNPluginInfo")
94+
data = {k: getattr(self, k) for k in model_fields_set(self)}
95+
if name:
96+
data["name"] = name
97+
return PMNPluginInfo(**data)
98+
99+
80100
class PMNPluginInfo(CompatModel):
81101
name: str
82102
plugin_id: str | None = None
@@ -112,22 +132,37 @@ def plugin(self) -> Plugin | None:
112132

113133

114134
class ExternalPluginInfo(CompatModel):
115-
name: str
135+
name: str | None = None
116136
author: str | None = None
117137
version: str | None = None
118138
description: str | None = None
119139
usage: str | None = None
120140
funcs: list[PMDataItem] | None = None
121141
pmn: PMNData = PMNData()
122142

123-
def to_plugin_info(self, plugin_id: str | None = None):
143+
def to_optional_plugin_info(self, plugin_id: str | None = None):
124144
key_name_map = {"funcs": "pm_data"}
125145
data = {
126146
key_name_map.get(k, k): getattr(self, k) for k in model_fields_set(self)
127147
}
128148
if plugin_id:
129149
data["plugin_id"] = plugin_id
130-
return PMNPluginInfo(**data)
150+
return OptionalPMNPluginInfo(**data)
151+
152+
def to_plugin_info(self, plugin_id: str | None = None, name: str | None = None):
153+
if name is None:
154+
if self.name is not None:
155+
name = self.name
156+
elif plugin_id:
157+
name = normalize_plugin_name(plugin_id)
158+
if name is None:
159+
raise ValueError(
160+
"`name` is required for PMNPluginInfo"
161+
", please set `name` to this model instance or pass it in"
162+
", or pass `plugin_id` to generate one",
163+
)
164+
info = self.to_optional_plugin_info(plugin_id)
165+
return info.to_required(name=name)
131166

132167
def merge_to(
133168
self,
@@ -137,7 +172,7 @@ def merge_to(
137172
):
138173
if copy:
139174
other = model_copy(other)
140-
this = self.to_plugin_info(plugin_id)
175+
this = self.to_optional_plugin_info(plugin_id)
141176
for k in model_fields_set(this):
142177
setattr(other, k, getattr(this, k))
143178
return other

nonebot_plugin_picmenu_next/templates/default/__init__.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,36 @@
66
from cookit import DebugFileWriter
77
from cookit.pw import RouterGroup, make_real_path_router, screenshot_html
88
from cookit.pw.loguru import log_router_err
9-
from cookit.pyd import model_with_alias_generator
9+
from cookit.pyd.compat import get_model_with_config
1010
from nonebot import get_plugin_config
1111
from nonebot_plugin_alconna.uniseg import UniMessage
1212
from nonebot_plugin_htmlrender import get_new_page
13-
from pydantic import BaseModel, Field
13+
from pydantic import Field
1414

1515
from ...config import version
16-
from ...data_source.models import PMDataItem, PMNPluginInfo
16+
from ...data_source.models import PMDataItem, PMNPluginInfo, compat_model_config
1717
from .. import detail_templates, func_detail_templates, index_templates
1818
from ..pw_utils import ROUTE_BASE_URL, base_routers, register_filters
1919

2020
if TYPE_CHECKING:
2121
from yarl import URL
2222

2323

24-
@model_with_alias_generator(lambda x: f"pmn_default_{x}")
25-
class TemplateConfigModel(BaseModel):
24+
AliasCompatModel = get_model_with_config(
25+
{
26+
"alias_generator": lambda x: f"pmn_default_{x}",
27+
**compat_model_config,
28+
},
29+
)
30+
31+
32+
class TemplateConfigModel(AliasCompatModel):
2633
command_start: set[str] = Field(alias="command_start")
2734

2835
dark: bool = False
2936
enable_builtin_code_css: bool = True
30-
additional_css: list[str] = []
31-
additional_js: list[str] = []
37+
additional_css: list[str] = Field(default_factory=list)
38+
additional_js: list[str] = Field(default_factory=list)
3239

3340
@cached_property
3441
def pfx(self) -> str:

scripts/gen_defs.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# ruff: noqa: INP001, E402
2+
3+
import json
4+
from pathlib import Path
5+
6+
import nonebot
7+
8+
nonebot.init(localstore_use_cwd=True)
9+
10+
nonebot.require("nonebot_plugin_picmenu_next")
11+
12+
from nonebot_plugin_picmenu_next.data_source import models
13+
14+
(Path(__file__).parent.parent / "defs" / "ExternalPluginInfo.json").write_text(
15+
json.dumps(
16+
models.ExternalPluginInfo.model_json_schema(),
17+
ensure_ascii=False,
18+
),
19+
"u8",
20+
)

0 commit comments

Comments
 (0)