nonebot-plugin-picmenu-next 0.1.0.dev1__tar.gz → 0.1.0.dev2__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_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/PKG-INFO +4 -2
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/__init__.py +2 -6
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/__main__.py +26 -28
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/config.py +6 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/__init__.py +31 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/collect.py +106 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/models.py +191 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/pinyin.py +86 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/ft_parser.py +215 -0
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/__init__.py +1 -1
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/__init__.py +10 -42
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/base.html.jinja +11 -4
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/css/base.css +96 -3
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/css/dark.css +2 -1
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/css/index.css +0 -1
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/detail.html.jinja +16 -18
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/index.html.jinja +1 -1
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/js/base.js +7 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/js/tsconfig.json +8 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/code-colorful.css +69 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/code-github-dark.css +81 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/katex.min.css +1 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/katex.min.js +1 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/pw_utils.py +82 -0
- nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/utils.py +34 -0
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/pyproject.toml +4 -2
- nonebot_plugin_picmenu_next-0.1.0.dev1/nonebot_plugin_picmenu_next/data_source.py +0 -341
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/LICENSE +0 -0
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/README.md +0 -0
- {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/templates/default/res/css/detail.css +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: nonebot-plugin-picmenu-next
|
|
3
|
-
Version: 0.1.0.
|
|
3
|
+
Version: 0.1.0.dev2
|
|
4
4
|
Summary: Template plugin project
|
|
5
5
|
Author-Email: LgCookie <lgc2333@126.com>
|
|
6
6
|
License: MIT
|
|
@@ -8,10 +8,12 @@ Project-URL: homepage, https://github.com/lgc-NB2Dev/nonebot-plugin-picmenu-next
|
|
|
8
8
|
Requires-Python: <4.0,>=3.9
|
|
9
9
|
Requires-Dist: nonebot2>=2.4.1
|
|
10
10
|
Requires-Dist: nonebot-plugin-alconna>=0.54.2
|
|
11
|
-
Requires-Dist: cookit[jinja,loguru,nonebot-alconna,nonebot-localstore,pw,pyd]>=0.11.
|
|
11
|
+
Requires-Dist: cookit[jinja,loguru,nonebot-alconna,nonebot-localstore,pw,pyd]>=0.11.5
|
|
12
12
|
Requires-Dist: pypinyin>=0.54.0
|
|
13
13
|
Requires-Dist: jieba>=0.42.1
|
|
14
14
|
Requires-Dist: thefuzz>=0.22.1
|
|
15
|
+
Requires-Dist: markdown-it-py[plugins]>=3.0.0
|
|
16
|
+
Requires-Dist: pygments>=2.19.1
|
|
15
17
|
Description-Content-Type: text/markdown
|
|
16
18
|
|
|
17
19
|
<!-- markdownlint-disable MD031 MD033 MD036 MD041 -->
|
|
@@ -4,7 +4,6 @@ from nonebot import get_driver
|
|
|
4
4
|
from nonebot.plugin import PluginMetadata, inherit_supported_adapters, require
|
|
5
5
|
|
|
6
6
|
require("nonebot_plugin_alconna")
|
|
7
|
-
require("nonebot_plugin_waiter")
|
|
8
7
|
require("nonebot_plugin_htmlrender")
|
|
9
8
|
|
|
10
9
|
from . import __main__ as __main__
|
|
@@ -12,7 +11,7 @@ from .config import ConfigModel
|
|
|
12
11
|
from .data_source import refresh_infos
|
|
13
12
|
from .templates import load_builtin_templates
|
|
14
13
|
|
|
15
|
-
__version__ = "0.1.0.
|
|
14
|
+
__version__ = "0.1.0.dev2"
|
|
16
15
|
__plugin_meta__ = PluginMetadata(
|
|
17
16
|
name="PicMenu Next",
|
|
18
17
|
description="新一代的图片帮助插件",
|
|
@@ -20,10 +19,7 @@ __plugin_meta__ = PluginMetadata(
|
|
|
20
19
|
type="application",
|
|
21
20
|
homepage="https://github.com/lgc-NB2Dev/nonebot-plugin-picmenu-next",
|
|
22
21
|
config=ConfigModel,
|
|
23
|
-
supported_adapters=inherit_supported_adapters(
|
|
24
|
-
"nonebot_plugin_alconna",
|
|
25
|
-
"nonebot_plugin_waiter",
|
|
26
|
-
),
|
|
22
|
+
supported_adapters=inherit_supported_adapters("nonebot_plugin_alconna"),
|
|
27
23
|
extra={"License": "MIT", "Author": "LgCookie"},
|
|
28
24
|
)
|
|
29
25
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
|
-
from contextlib import suppress
|
|
3
2
|
from typing import Optional, TypeVar
|
|
4
3
|
|
|
5
4
|
from arclet.alconna import Alconna, Arg, Args, CommandMeta, Option, store_true
|
|
@@ -8,12 +7,8 @@ from nonebot_plugin_alconna import Query, on_alconna
|
|
|
8
7
|
from nonebot_plugin_alconna.uniseg import UniMessage
|
|
9
8
|
from thefuzz import process
|
|
10
9
|
|
|
11
|
-
from .data_source import
|
|
12
|
-
|
|
13
|
-
PMNPluginInfo,
|
|
14
|
-
get_resolved_infos,
|
|
15
|
-
transform_to_pinyin,
|
|
16
|
-
)
|
|
10
|
+
from .data_source import get_resolved_infos
|
|
11
|
+
from .data_source.models import PinyinChunkSequence, PMDataItem, PMNPluginInfo
|
|
17
12
|
from .templates import detail_templates, func_detail_templates, index_templates
|
|
18
13
|
|
|
19
14
|
alc = Alconna(
|
|
@@ -49,21 +44,24 @@ def get_name_similarities(
|
|
|
49
44
|
raw_weight: float = 0.6,
|
|
50
45
|
pinyin_weight: float = 0.4,
|
|
51
46
|
) -> list[float]:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
47
|
+
raw_scores = [x[1] for x in process.extractWithoutOrder(query, choices)]
|
|
48
|
+
pinyin_scores = [
|
|
49
|
+
x[1] for x in process.extractWithoutOrder(query_pinyin, choices_pinyin)
|
|
50
|
+
]
|
|
51
|
+
similarities = [
|
|
52
|
+
raw_weight * raw + pinyin_weight * pinyin
|
|
53
|
+
for raw, pinyin in zip(raw_scores, pinyin_scores)
|
|
54
|
+
]
|
|
60
55
|
logger.opt(lazy=True).debug(
|
|
61
|
-
"Query: {}, similarities
|
|
62
|
-
lambda: query,
|
|
63
|
-
lambda: "
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
"Query: {}, similarities:\n{}",
|
|
57
|
+
lambda: f"{query} ({query_pinyin})",
|
|
58
|
+
lambda: ";\n".join(
|
|
59
|
+
(
|
|
60
|
+
f"{choices[i]} ({choices_pinyin[i]})"
|
|
61
|
+
f": ({raw} * {raw_weight}) + ({pin} * {pinyin_weight}) = {sim}"
|
|
62
|
+
)
|
|
63
|
+
for i, (raw, pin, sim) in sorted(
|
|
64
|
+
enumerate(zip(raw_scores, pinyin_scores, similarities)),
|
|
67
65
|
key=lambda x: x[1],
|
|
68
66
|
reverse=True,
|
|
69
67
|
)
|
|
@@ -96,12 +94,12 @@ async def query_plugin(
|
|
|
96
94
|
choices: list[str] = []
|
|
97
95
|
choices_pinyin: list[str] = []
|
|
98
96
|
for info in infos:
|
|
99
|
-
choices.append(info.
|
|
100
|
-
choices_pinyin.append(info.name_pinyin.
|
|
97
|
+
choices.append(info.casefold_name)
|
|
98
|
+
choices_pinyin.append(info.name_pinyin.casefold_str)
|
|
101
99
|
|
|
102
100
|
similarities = get_name_similarities(
|
|
103
101
|
query.casefold(),
|
|
104
|
-
|
|
102
|
+
PinyinChunkSequence.from_raw(query).casefold_str,
|
|
105
103
|
choices,
|
|
106
104
|
choices_pinyin,
|
|
107
105
|
)
|
|
@@ -122,12 +120,12 @@ async def query_func_detail(
|
|
|
122
120
|
choices: list[str] = []
|
|
123
121
|
choices_pinyin: list[str] = []
|
|
124
122
|
for data in pm_data:
|
|
125
|
-
choices.append(data.
|
|
126
|
-
choices_pinyin.append(data.func_pinyin.
|
|
123
|
+
choices.append(data.casefold_func)
|
|
124
|
+
choices_pinyin.append(data.func_pinyin.casefold_str)
|
|
127
125
|
|
|
128
126
|
similarities = get_name_similarities(
|
|
129
127
|
query.casefold(),
|
|
130
|
-
|
|
128
|
+
PinyinChunkSequence.from_raw(query).casefold_str,
|
|
131
129
|
choices,
|
|
132
130
|
choices_pinyin,
|
|
133
131
|
)
|
|
@@ -145,7 +143,7 @@ async def _(
|
|
|
145
143
|
):
|
|
146
144
|
infos = await get_resolved_infos()
|
|
147
145
|
if not q_show_hidden.result:
|
|
148
|
-
infos = [x for x in infos if not x.
|
|
146
|
+
infos = [x for x in infos if not x.pmn.hidden]
|
|
149
147
|
|
|
150
148
|
if not q_plugin.result:
|
|
151
149
|
m = await index_templates.get()(infos)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
from cookit.loguru import warning_suppress as _warning_suppress
|
|
4
|
+
from nonebot import get_loaded_plugins as _get_loaded_plugins
|
|
5
|
+
|
|
6
|
+
from .collect import collect_plugin_infos as _collect_plugin_infos
|
|
7
|
+
from .models import (
|
|
8
|
+
PMNPluginInfo as _PMNPluginInfo,
|
|
9
|
+
PMNPluginInfoRaw as _PMNPluginInfoRaw,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
_infos: list[_PMNPluginInfoRaw] = []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_infos() -> list[_PMNPluginInfoRaw]:
|
|
16
|
+
return _infos
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def refresh_infos() -> list[_PMNPluginInfoRaw]:
|
|
20
|
+
global _infos
|
|
21
|
+
_infos = await _collect_plugin_infos(_get_loaded_plugins())
|
|
22
|
+
return _infos
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
async def get_resolved_infos() -> list[_PMNPluginInfo]:
|
|
26
|
+
async def _resolve(p: _PMNPluginInfoRaw):
|
|
27
|
+
with _warning_suppress(f"Failed to resolve plugin info of {p.name}"):
|
|
28
|
+
return await _PMNPluginInfo.resolve(p)
|
|
29
|
+
|
|
30
|
+
r = await asyncio.gather(*(_resolve(x) for x in _infos))
|
|
31
|
+
return [v for v in r if v]
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Iterable
|
|
3
|
+
from importlib.metadata import Distribution, distribution
|
|
4
|
+
from typing import Literal, Optional, Union
|
|
5
|
+
|
|
6
|
+
from cookit.loguru import warning_suppress
|
|
7
|
+
from cookit.pyd import type_validate_python
|
|
8
|
+
from nonebot import logger
|
|
9
|
+
from nonebot.plugin import Plugin
|
|
10
|
+
|
|
11
|
+
from ..utils import normalize_plugin_name
|
|
12
|
+
from .models import PMNDataRaw, PMNPluginExtra, PMNPluginInfoRaw
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def normalize_metadata_user(info: str, allow_multi: bool = False) -> str:
|
|
16
|
+
infos = info.split(",")
|
|
17
|
+
if not allow_multi:
|
|
18
|
+
infos = infos[:1]
|
|
19
|
+
return " & ".join(x.split("<")[0].strip().strip("'\"") for x in infos)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def get_info_from_plugin(plugin: Plugin) -> PMNPluginInfoRaw:
|
|
23
|
+
meta = plugin.metadata
|
|
24
|
+
extra: Optional[PMNPluginExtra] = None
|
|
25
|
+
if meta:
|
|
26
|
+
with warning_suppress(f"Failed to parse plugin metadata of {plugin.id_}"):
|
|
27
|
+
extra = type_validate_python(PMNPluginExtra, meta.extra)
|
|
28
|
+
|
|
29
|
+
name = normalize_plugin_name(meta.name if meta else plugin.id_)
|
|
30
|
+
|
|
31
|
+
_dist: Union[Distribution, None, Literal[False]] = False
|
|
32
|
+
|
|
33
|
+
def get_dist() -> Optional[Distribution]:
|
|
34
|
+
nonlocal _dist
|
|
35
|
+
if _dist is not False:
|
|
36
|
+
return _dist
|
|
37
|
+
|
|
38
|
+
_dist = None
|
|
39
|
+
module = plugin.module_name
|
|
40
|
+
while True:
|
|
41
|
+
with warning_suppress(
|
|
42
|
+
f"Failed to get info of package {module}",
|
|
43
|
+
level="DEBUG",
|
|
44
|
+
debug_stack=False,
|
|
45
|
+
):
|
|
46
|
+
_dist = distribution(module)
|
|
47
|
+
return _dist
|
|
48
|
+
if "." not in module:
|
|
49
|
+
return None
|
|
50
|
+
module = module.rsplit(".", 1)[0]
|
|
51
|
+
|
|
52
|
+
ver = extra.version if extra else None
|
|
53
|
+
if not ver:
|
|
54
|
+
ver = getattr(plugin, "__version__", None)
|
|
55
|
+
if not ver and (dist := get_dist()):
|
|
56
|
+
ver = dist.version
|
|
57
|
+
|
|
58
|
+
author = (
|
|
59
|
+
(" & ".join(extra.author) if isinstance(extra.author, list) else extra.author)
|
|
60
|
+
if extra
|
|
61
|
+
else None
|
|
62
|
+
)
|
|
63
|
+
if not author and (dist := get_dist()):
|
|
64
|
+
if author := dist.metadata.get("Author") or dist.metadata.get("Maintainer"):
|
|
65
|
+
author = normalize_metadata_user(author)
|
|
66
|
+
elif author := dist.metadata.get("Author-Email") or dist.metadata.get(
|
|
67
|
+
"Maintainer-Email",
|
|
68
|
+
):
|
|
69
|
+
author = normalize_metadata_user(author, allow_multi=True)
|
|
70
|
+
|
|
71
|
+
description = (
|
|
72
|
+
meta.description
|
|
73
|
+
if meta
|
|
74
|
+
else (dist.metadata.get("Summary") if (dist := get_dist()) else None)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
pmn = (extra.pmn if extra else None) or PMNDataRaw()
|
|
78
|
+
if ("hidden" not in pmn.model_fields_set) and meta and meta.type == "library":
|
|
79
|
+
pmn = PMNDataRaw(hidden=True)
|
|
80
|
+
|
|
81
|
+
logger.debug(f"Completed to get info of plugin {plugin.id_}")
|
|
82
|
+
obj = PMNPluginInfoRaw(
|
|
83
|
+
name=name,
|
|
84
|
+
author=author,
|
|
85
|
+
version=ver,
|
|
86
|
+
description=description,
|
|
87
|
+
usage=meta.usage if meta else None,
|
|
88
|
+
pm_data=extra.menu_data if extra else None,
|
|
89
|
+
pmn=pmn,
|
|
90
|
+
)
|
|
91
|
+
obj.plugin = plugin
|
|
92
|
+
return obj
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def collect_plugin_infos(plugins: Iterable[Plugin]):
|
|
96
|
+
async def _get(p: Plugin):
|
|
97
|
+
with warning_suppress(f"Failed to get plugin info of {p.id_}"):
|
|
98
|
+
return await get_info_from_plugin(p)
|
|
99
|
+
|
|
100
|
+
infos = await asyncio.gather(
|
|
101
|
+
*(_get(plugin) for plugin in plugins),
|
|
102
|
+
)
|
|
103
|
+
infos = [x for x in infos if x]
|
|
104
|
+
logger.success(f"Collected {len(infos)} plugin infos")
|
|
105
|
+
infos.sort(key=lambda x: x.name_pinyin)
|
|
106
|
+
return infos
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from collections.abc import Awaitable
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from typing import Any, Optional, Union
|
|
5
|
+
from typing_extensions import Self
|
|
6
|
+
from weakref import ref
|
|
7
|
+
|
|
8
|
+
from cookit.loguru import warning_suppress
|
|
9
|
+
from cookit.pyd import model_validator, type_dump_python
|
|
10
|
+
from nonebot.plugin import Plugin
|
|
11
|
+
from pydantic import BaseModel, Field, PrivateAttr
|
|
12
|
+
|
|
13
|
+
from ..utils import call_entrypoint
|
|
14
|
+
from .pinyin import PinyinChunkSequence
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
async def resolve_func_hidden(plugin: Plugin, entrypoint: str) -> bool:
|
|
18
|
+
"""should only be called from event handler,
|
|
19
|
+
so hidden func can get current bot, event, etc."""
|
|
20
|
+
|
|
21
|
+
with warning_suppress(
|
|
22
|
+
f"Failed to resole hidden status `{entrypoint}` from plugin {plugin.id_}",
|
|
23
|
+
):
|
|
24
|
+
return bool(await call_entrypoint(plugin, entrypoint))
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PMDataItemRaw(BaseModel):
|
|
29
|
+
func: str
|
|
30
|
+
trigger_method: str
|
|
31
|
+
trigger_condition: str
|
|
32
|
+
brief_des: str
|
|
33
|
+
detail_des: str
|
|
34
|
+
|
|
35
|
+
# extension properties
|
|
36
|
+
hidden: Union[bool, str] = Field(default=False, alias="pmn_hidden")
|
|
37
|
+
template: Optional[str] = Field(default=None, alias="pmn_template")
|
|
38
|
+
|
|
39
|
+
@cached_property
|
|
40
|
+
def casefold_func(self) -> str:
|
|
41
|
+
return self.func.casefold()
|
|
42
|
+
|
|
43
|
+
@cached_property
|
|
44
|
+
def func_pinyin(self) -> PinyinChunkSequence:
|
|
45
|
+
return PinyinChunkSequence.from_raw(self.func)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class PMDataItem(PMDataItemRaw):
|
|
49
|
+
hidden: bool = Field(default=False, alias="pmn_hidden") # pyright: ignore[reportIncompatibleVariableOverride]
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
async def resolve(cls, plugin: Plugin, data: PMDataItemRaw) -> Self:
|
|
53
|
+
data_dict: dict = type_dump_python(data, exclude_unset=True)
|
|
54
|
+
if isinstance((hidden := data_dict.get("pmn_hidden")), str):
|
|
55
|
+
data_dict["pmn_hidden"] = await resolve_func_hidden(plugin, hidden)
|
|
56
|
+
ins = cls(**data_dict)
|
|
57
|
+
ins.casefold_func = data.casefold_func
|
|
58
|
+
ins.func_pinyin = data.func_pinyin
|
|
59
|
+
return ins
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class PMNDataRaw(BaseModel):
|
|
63
|
+
hidden: Union[bool, str] = False
|
|
64
|
+
markdown: bool = False
|
|
65
|
+
template: Optional[str] = None
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class PMNData(PMNDataRaw):
|
|
69
|
+
hidden: bool = False # pyright: ignore[reportIncompatibleVariableOverride]
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
async def resolve(cls, plugin: Plugin, data: PMNDataRaw) -> Self:
|
|
73
|
+
data_dict: dict = type_dump_python(data, exclude_unset=True)
|
|
74
|
+
if isinstance((hidden := data_dict.get("hidden")), str):
|
|
75
|
+
data_dict["hidden"] = await resolve_func_hidden(plugin, hidden)
|
|
76
|
+
return cls(**data_dict)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PMNPluginExtra(BaseModel):
|
|
80
|
+
author: Union[str, list[str], None] = None
|
|
81
|
+
version: Optional[str] = None
|
|
82
|
+
menu_data: Optional[list[PMDataItemRaw]] = None
|
|
83
|
+
pmn: Optional[PMNDataRaw] = None
|
|
84
|
+
|
|
85
|
+
@model_validator(mode="before")
|
|
86
|
+
def normalize_input(cls, values: Any): # noqa: N805
|
|
87
|
+
if isinstance(values, PMNPluginExtra):
|
|
88
|
+
values = type_dump_python(values, exclude_unset=True)
|
|
89
|
+
if not isinstance(values, dict):
|
|
90
|
+
raise TypeError(f"Expected dict, got {type(values)}")
|
|
91
|
+
should_normalize_keys = {x for x in values if x.lower() in {"author"}}
|
|
92
|
+
for key in should_normalize_keys:
|
|
93
|
+
value = values[key]
|
|
94
|
+
del values[key]
|
|
95
|
+
values[key.lower()] = value
|
|
96
|
+
return values
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class PMNPluginInfoRaw(BaseModel):
|
|
100
|
+
name: str
|
|
101
|
+
author: Optional[str] = None
|
|
102
|
+
version: Optional[str] = None
|
|
103
|
+
description: Optional[str] = None
|
|
104
|
+
usage: Optional[str] = None
|
|
105
|
+
pm_data: Optional[list[PMDataItemRaw]] = None
|
|
106
|
+
pmn: PMNDataRaw = PMNDataRaw()
|
|
107
|
+
|
|
108
|
+
_plugin_ref: Optional[ref[Plugin]] = PrivateAttr(None)
|
|
109
|
+
_resolved_pm_data: Optional[list[PMDataItem]] = PrivateAttr(None)
|
|
110
|
+
|
|
111
|
+
@cached_property
|
|
112
|
+
def casefold_name(self) -> str:
|
|
113
|
+
return self.name.casefold()
|
|
114
|
+
|
|
115
|
+
@cached_property
|
|
116
|
+
def name_pinyin(self) -> PinyinChunkSequence:
|
|
117
|
+
return PinyinChunkSequence.from_raw(self.name)
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def plugin(self) -> Optional[Plugin]:
|
|
121
|
+
if self._plugin_ref:
|
|
122
|
+
return self._plugin_ref()
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
@plugin.setter
|
|
126
|
+
def plugin(self, plugin: Plugin):
|
|
127
|
+
self._plugin_ref = ref(plugin)
|
|
128
|
+
|
|
129
|
+
@cached_property
|
|
130
|
+
def subtitle(self) -> str:
|
|
131
|
+
return " | ".join(
|
|
132
|
+
x
|
|
133
|
+
for x in (
|
|
134
|
+
f"By {self.author}" if self.author else None,
|
|
135
|
+
f"v{self.version}" if self.version else None,
|
|
136
|
+
)
|
|
137
|
+
if x
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
async def resolve_pm_data(self, plugin: Plugin):
|
|
141
|
+
if self._resolved_pm_data is not None:
|
|
142
|
+
return self._resolved_pm_data
|
|
143
|
+
if not self.pm_data:
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
async def _ts(x: PMDataItemRaw):
|
|
147
|
+
with warning_suppress(
|
|
148
|
+
f"Failed to resolve plugin menu item `{x.func}` of {plugin.id_}",
|
|
149
|
+
):
|
|
150
|
+
return await PMDataItem.resolve(plugin, x)
|
|
151
|
+
|
|
152
|
+
self._resolved_pm_data = [
|
|
153
|
+
x for x in await asyncio.gather(*(_ts(x) for x in self.pm_data)) if x
|
|
154
|
+
]
|
|
155
|
+
return self._resolved_pm_data
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class PMNPluginInfo(PMNPluginInfoRaw):
|
|
159
|
+
pmn: PMNData = PMNData() # pyright: ignore[reportIncompatibleVariableOverride]
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
async def resolve(
|
|
163
|
+
cls,
|
|
164
|
+
data: PMNPluginInfoRaw,
|
|
165
|
+
plugin: Optional[Plugin] = None,
|
|
166
|
+
) -> Self:
|
|
167
|
+
if (not plugin) and (not (plugin := data.plugin)):
|
|
168
|
+
raise ValueError("Plugin ref is expired, please manually set")
|
|
169
|
+
|
|
170
|
+
data_dict: dict = type_dump_python(data, exclude_unset=True)
|
|
171
|
+
tasks: list[Awaitable] = []
|
|
172
|
+
|
|
173
|
+
if pmn := data_dict.get("pmn"):
|
|
174
|
+
|
|
175
|
+
async def _t():
|
|
176
|
+
v = None
|
|
177
|
+
with warning_suppress(
|
|
178
|
+
f"Failed to resolve PicMenu Next data of {plugin.id_}",
|
|
179
|
+
):
|
|
180
|
+
v = await PMNData.resolve(plugin, pmn)
|
|
181
|
+
data_dict["pmn"] = v
|
|
182
|
+
|
|
183
|
+
tasks.append(_t())
|
|
184
|
+
|
|
185
|
+
await asyncio.gather(*tasks)
|
|
186
|
+
|
|
187
|
+
ins = cls(**data_dict)
|
|
188
|
+
ins.casefold_name = data.casefold_name
|
|
189
|
+
ins.name_pinyin = data.name_pinyin
|
|
190
|
+
ins.plugin = plugin
|
|
191
|
+
return ins
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from collections.abc import Iterable, Iterator, Sequence
|
|
2
|
+
from functools import cached_property
|
|
3
|
+
from typing import NamedTuple, Optional, Union, overload
|
|
4
|
+
from typing_extensions import Self, override
|
|
5
|
+
|
|
6
|
+
import jieba
|
|
7
|
+
from pypinyin import Style, pinyin
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _NotCHNStr(str):
|
|
11
|
+
__slots__ = ()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PinyinChunk(NamedTuple):
|
|
15
|
+
is_pinyin: bool
|
|
16
|
+
text: str
|
|
17
|
+
tone: int = 0
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def from_pinyin_res(cls, text: str) -> Self:
|
|
21
|
+
is_pinyin = not isinstance(text, _NotCHNStr)
|
|
22
|
+
tone = 0
|
|
23
|
+
if is_pinyin:
|
|
24
|
+
tone = int(text[-1])
|
|
25
|
+
text = text[:-1]
|
|
26
|
+
return cls(is_pinyin=is_pinyin, text=text, tone=tone)
|
|
27
|
+
|
|
28
|
+
@cached_property
|
|
29
|
+
def casefold_str(self) -> str:
|
|
30
|
+
return self.text.casefold()
|
|
31
|
+
|
|
32
|
+
def __str__(self):
|
|
33
|
+
return f"{self.text}{self.tone}" if self.is_pinyin else self.text
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PinyinChunkSequence(Sequence[PinyinChunk]):
|
|
37
|
+
def __init__(self, iterable: Optional[Iterable[PinyinChunk]] = None):
|
|
38
|
+
self.chunks: tuple[PinyinChunk, ...] = tuple(iterable) if iterable else ()
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_raw(cls, text: str) -> Self:
|
|
42
|
+
transformed = pinyin(
|
|
43
|
+
[x.strip() for x in jieba.lcut(text)],
|
|
44
|
+
style=Style.TONE3,
|
|
45
|
+
errors=lambda x: _NotCHNStr(x),
|
|
46
|
+
neutral_tone_with_five=True,
|
|
47
|
+
)
|
|
48
|
+
return cls(PinyinChunk.from_pinyin_res(x[0]) for x in transformed)
|
|
49
|
+
|
|
50
|
+
@cached_property
|
|
51
|
+
def casefold_str(self) -> str:
|
|
52
|
+
return str(self).casefold()
|
|
53
|
+
|
|
54
|
+
def __str__(self):
|
|
55
|
+
return " ".join(str(x) for x in self)
|
|
56
|
+
|
|
57
|
+
def __lt__(self, other: "PinyinChunkSequence"):
|
|
58
|
+
return self.chunks.__lt__(other.chunks)
|
|
59
|
+
|
|
60
|
+
def __gt__(self, other: "PinyinChunkSequence"):
|
|
61
|
+
return self.chunks.__gt__(other.chunks)
|
|
62
|
+
|
|
63
|
+
def __eq__(self, other: object):
|
|
64
|
+
return (
|
|
65
|
+
self.chunks.__eq__(other.chunks)
|
|
66
|
+
if isinstance(other, PinyinChunkSequence)
|
|
67
|
+
else NotImplemented
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@override
|
|
71
|
+
def __iter__(self) -> Iterator[PinyinChunk]:
|
|
72
|
+
return self.chunks.__iter__()
|
|
73
|
+
|
|
74
|
+
@override
|
|
75
|
+
def __len__(self) -> int:
|
|
76
|
+
return self.chunks.__len__()
|
|
77
|
+
|
|
78
|
+
@overload
|
|
79
|
+
def __getitem__(self, index: int) -> PinyinChunk: ...
|
|
80
|
+
@overload
|
|
81
|
+
def __getitem__(self, index: slice) -> Self: ...
|
|
82
|
+
@override
|
|
83
|
+
def __getitem__(self, index: Union[int, slice]):
|
|
84
|
+
if isinstance(index, slice):
|
|
85
|
+
return type(self)(self.chunks[index])
|
|
86
|
+
return self.chunks[index]
|