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.
Files changed (90) hide show
  1. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/PKG-INFO +4 -2
  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
  3. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/__main__.py +26 -28
  4. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/nonebot_plugin_picmenu_next/config.py +6 -0
  5. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/__init__.py +31 -0
  6. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/collect.py +106 -0
  7. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/models.py +191 -0
  8. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/data_source/pinyin.py +86 -0
  9. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/ft_parser.py +215 -0
  10. {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
  11. {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
  12. {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
  13. {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
  14. {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
  15. {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
  16. {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
  17. {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
  18. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/js/base.js +7 -0
  19. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/js/tsconfig.json +8 -0
  20. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/code-colorful.css +69 -0
  21. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/code-github-dark.css +81 -0
  22. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.ttf +0 -0
  23. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.woff +0 -0
  24. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  25. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  26. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  27. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  28. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  29. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  30. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  31. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  32. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  33. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  34. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  35. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  36. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  37. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.ttf +0 -0
  38. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.woff +0 -0
  39. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Bold.woff2 +0 -0
  40. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  41. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  42. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  43. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.ttf +0 -0
  44. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.woff +0 -0
  45. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Italic.woff2 +0 -0
  46. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.ttf +0 -0
  47. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.woff +0 -0
  48. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Main-Regular.woff2 +0 -0
  49. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  50. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  51. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  52. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.ttf +0 -0
  53. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.woff +0 -0
  54. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Math-Italic.woff2 +0 -0
  55. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  56. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  57. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  58. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  59. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  60. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  61. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  62. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  63. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  64. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.ttf +0 -0
  65. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.woff +0 -0
  66. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Script-Regular.woff2 +0 -0
  67. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.ttf +0 -0
  68. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.woff +0 -0
  69. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  70. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.ttf +0 -0
  71. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.woff +0 -0
  72. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  73. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.ttf +0 -0
  74. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.woff +0 -0
  75. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  76. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.ttf +0 -0
  77. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.woff +0 -0
  78. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  79. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  80. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  81. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  82. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/katex.min.css +1 -0
  83. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/default/res/third-party/katex.min.js +1 -0
  84. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/templates/pw_utils.py +82 -0
  85. nonebot_plugin_picmenu_next-0.1.0.dev2/nonebot_plugin_picmenu_next/utils.py +34 -0
  86. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/pyproject.toml +4 -2
  87. nonebot_plugin_picmenu_next-0.1.0.dev1/nonebot_plugin_picmenu_next/data_source.py +0 -341
  88. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/LICENSE +0 -0
  89. {nonebot_plugin_picmenu_next-0.1.0.dev1 → nonebot_plugin_picmenu_next-0.1.0.dev2}/README.md +0 -0
  90. {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.dev1
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.2
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.dev1"
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
- PMDataItem,
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
- similarities: list[float] = []
53
- raw_iter = process.extractWithoutOrder(query, choices)
54
- pinyin_iter = process.extractWithoutOrder(query_pinyin, choices_pinyin)
55
- with suppress(StopIteration):
56
- while True:
57
- raw_score = next(raw_iter)[1]
58
- pinyin_score = next(pinyin_iter)[1]
59
- similarities.append(raw_weight * raw_score + pinyin_weight * pinyin_score)
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: "; ".join(
64
- f"{choices[i]}: {s}"
65
- for i, s in sorted(
66
- enumerate(similarities),
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.name.casefold())
100
- choices_pinyin.append(info.name_pinyin.casefold())
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
- transform_to_pinyin(query).casefold(),
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.func.casefold())
126
- choices_pinyin.append(data.func_pinyin.casefold())
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
- transform_to_pinyin(query).casefold(),
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.pmn_v.hidden]
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)
@@ -11,3 +11,9 @@ class ConfigModel(BaseModel):
11
11
 
12
12
 
13
13
  config: ConfigModel = get_plugin_config(ConfigModel)
14
+
15
+
16
+ def version():
17
+ from . import __version__
18
+
19
+ return __version__
@@ -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]