nonebot-plugin-htmlrender 0.5.0__tar.gz → 0.6.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.
Files changed (62) hide show
  1. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/.github/workflows/publish.yml +6 -1
  2. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/.gitignore +3 -0
  3. nonebot_plugin_htmlrender-0.6.0/.pre-commit-config.yaml +16 -0
  4. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/PKG-INFO +37 -22
  5. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/README.md +34 -20
  6. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/__init__.py +12 -10
  7. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/utils.py +6 -5
  8. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/__init__.py +55 -0
  9. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/browser.py +169 -0
  10. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/config.py +80 -0
  11. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/consts.py +38 -0
  12. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/data_source.py +24 -24
  13. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/install.py +230 -0
  14. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/process.py +165 -0
  15. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/signal.py +138 -0
  16. nonebot_plugin_htmlrender-0.6.0/nonebot_plugin_htmlrender/utils.py +148 -0
  17. nonebot_plugin_htmlrender-0.6.0/pyproject.toml +105 -0
  18. nonebot_plugin_htmlrender-0.6.0/tests/test_browser.py +296 -0
  19. nonebot_plugin_htmlrender-0.6.0/tests/test_config_custom_validation.py +120 -0
  20. nonebot_plugin_htmlrender-0.6.0/tests/test_deprecated_decorator.py +61 -0
  21. nonebot_plugin_htmlrender-0.6.0/tests/test_htmlrender.py +139 -0
  22. nonebot_plugin_htmlrender-0.6.0/tests/test_install.py +131 -0
  23. nonebot_plugin_htmlrender-0.6.0/tests/test_process.py +185 -0
  24. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/uv.lock +128 -1
  25. nonebot_plugin_htmlrender-0.5.0/.flake8 +0 -3
  26. nonebot_plugin_htmlrender-0.5.0/nonebot_plugin_htmlrender/__init__.py +0 -60
  27. nonebot_plugin_htmlrender-0.5.0/nonebot_plugin_htmlrender/browser.py +0 -356
  28. nonebot_plugin_htmlrender-0.5.0/nonebot_plugin_htmlrender/config.py +0 -17
  29. nonebot_plugin_htmlrender-0.5.0/pyproject.toml +0 -40
  30. nonebot_plugin_htmlrender-0.5.0/tests/test_htmlrender.py +0 -86
  31. nonebot_plugin_htmlrender-0.5.0/tests/utils.py +0 -10
  32. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/.github/workflows/codecov.yml +0 -0
  33. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/.python-version +0 -0
  34. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/LICENSE +0 -0
  35. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/docs/example.md +0 -0
  36. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/docs/html2pic.png +0 -0
  37. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/docs/md2pic.png +0 -0
  38. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/docs/template2pic.png +0 -0
  39. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/docs/text2pic.png +0 -0
  40. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/.env.dev +0 -0
  41. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/bot.py +0 -0
  42. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/html2pic.html +0 -0
  43. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/templates/markdown.css +0 -0
  44. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/templates/mystyle.css +0 -0
  45. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/templates/progress.html.jinja2 +0 -0
  46. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/example/plugins/render/templates/text.html +0 -0
  47. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/github-markdown-light.css +0 -0
  48. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/katex/katex.min.b64_fonts.css +0 -0
  49. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/katex/katex.min.js +0 -0
  50. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/katex/mathtex-script-type.min.js +0 -0
  51. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/katex/mhchem.min.js +0 -0
  52. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/markdown.html +0 -0
  53. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/pygments-default.css +0 -0
  54. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/text.css +0 -0
  55. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/nonebot_plugin_htmlrender/templates/text.html +0 -0
  56. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/__init__.py +0 -0
  57. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/conftest.py +0 -0
  58. {nonebot_plugin_htmlrender-0.5.0/tests → nonebot_plugin_htmlrender-0.6.0/tests/resources}/test_template_filter.png +0 -0
  59. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/templates/markdown.css +0 -0
  60. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/templates/mystyle.css +0 -0
  61. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/templates/progress.html.jinja2 +0 -0
  62. {nonebot_plugin_htmlrender-0.5.0 → nonebot_plugin_htmlrender-0.6.0}/tests/templates/text.html +0 -0
@@ -43,8 +43,13 @@ jobs:
43
43
  - name: Build Package
44
44
  run: uv build
45
45
 
46
+ - uses: actions/upload-artifact@v4
47
+ with:
48
+ name: artifact
49
+ path: dist/ # or path/to/artifact
50
+
46
51
  - name: pypi-publish
47
- uses: pypa/gh-action-pypi-publish@v1.11.0
52
+ uses: pypa/gh-action-pypi-publish@v1.12.3
48
53
 
49
54
  # - name: Publish Package to PyPI
50
55
  # run: uv publish
@@ -127,3 +127,6 @@ dmypy.json
127
127
  # Pyre type checker
128
128
  .pyre/
129
129
  .pdm-python
130
+
131
+ # pycharm project settings
132
+ .idea/
@@ -0,0 +1,16 @@
1
+ default_install_hook_types: [pre-commit]
2
+ ci:
3
+ autofix_commit_msg: ":rotating_light: auto fix by pre-commit hooks"
4
+ autofix_prs: true
5
+ autoupdate_branch: main
6
+ autoupdate_schedule: monthly
7
+ autoupdate_commit_msg: ":arrow_up: auto update by pre-commit hooks"
8
+ repos:
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.8.2
11
+ hooks:
12
+ - id: ruff
13
+ args: [--fix]
14
+ stages: [pre-commit]
15
+ - id: ruff-format
16
+ stages: [pre-commit]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: nonebot-plugin-htmlrender
3
- Version: 0.5.0
3
+ Version: 0.6.0
4
4
  Summary: 通过浏览器渲染图片
5
5
  Project-URL: Homepage, https://github.com/kexue-z/nonebot-plugin-htmlrender
6
6
  Project-URL: Bug Tracker, https://github.com/kexue-z/nonebot-plugin-htmlrender/issues
@@ -26,6 +26,7 @@ License: MIT License
26
26
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
27
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  SOFTWARE.
29
+ License-File: LICENSE
29
30
  Requires-Python: <4.0,>=3.9
30
31
  Requires-Dist: aiofiles>=0.8.0
31
32
  Requires-Dist: jinja2>=3.0.3
@@ -55,6 +56,7 @@ Description-Content-Type: text/markdown
55
56
 
56
57
  ```py
57
58
  from nonebot import require
59
+
58
60
  require("nonebot_plugin_htmlrender")
59
61
  # 注意顺序,先require再 from ... import ...
60
62
  # 注意顺序,先require再 from ... import ...
@@ -72,32 +74,44 @@ from nonebot_plugin_htmlrender import (
72
74
 
73
75
  # 配置
74
76
 
77
+ ## .env 配置项说明
78
+
75
79
  ```ini
76
- # 默认情况 可不写
80
+ # Playwright 浏览器引擎类型
81
+ # 可不填,默认为 "chromium"
77
82
  htmlrender_browser = "chromium"
78
- # 使用 firefox
79
- htmlrender_browser = "firefox"
80
83
 
81
- # 下载 playwright 代理地址 可不写
84
+ # Playwright 浏览器下载地址
85
+ # 可选,用于自定义浏览器下载源
82
86
  htmlrender_download_host = ""
83
87
 
84
- # 浏览器自定代理地址 可不写
85
- htmlrender_proxy_host = "http://127.0.0.1:7890"
88
+ # Playwright 浏览器下载代理
89
+ # 可选,用于配置下载浏览器时的代理
90
+ htmlrender_download_proxy = ""
91
+
92
+ # Playwright 浏览器代理地址
93
+ # 可选,用于配置浏览器访问时的代理
94
+ # 示例: htmlrender_proxy_host = "http://127.0.0.1:7890"
95
+
96
+ # Playwright 浏览器代理绕过地址
97
+ # 可选,指定不使用代理的地址
98
+ htmlrender_proxy_host_bypass = ""
86
99
 
87
- # 浏览器 channel 支持以下
88
- # "chrome", "chrome-beta", "chrome-dev", "chrome-canary",
89
- # "msedge", "msedge-beta", "msedge-dev", "msedge-canary"
90
- # 手动编辑可以直接使用系统自带浏览器而不用重新下载 chromium
91
- # 可不写
100
+ # Playwright 浏览器通道
101
+ # 可选,支持以下值:
102
+ # - Chrome: "chrome", "chrome-beta", "chrome-dev", "chrome-canary"
103
+ # - Edge: "msedge", "msedge-beta", "msedge-dev", "msedge-canary"
104
+ # 配置后可直接使用系统浏览器,无需下载 Chromium
92
105
  htmlrender_browser_channel = ""
93
106
 
94
- # 浏览器执行文件路径 可不写
107
+ # Playwright 浏览器可执行文件路径
108
+ # 可选,用于指定浏览器程序位置
95
109
  htmlrender_browser_executable_path = ""
96
110
 
97
- # 使用 cdp 连接现有浏览器
98
- # 使用方法,自行启动浏览,在启动浏览器时添加参数 --remote-debugging-port=1234
99
- # 可不写
100
- htmlrender_connect_over_cdp = "http://127.0.0.1:1234"
111
+ # CDP 远程调试地址
112
+ # 可选,用于连接已运行的浏览器实例
113
+ # 使用时需要在启动浏览器时添加参数 --remote-debugging-port=端口号
114
+ htmlrender_connect_over_cdp = "http://127.0.0.1:9222"
101
115
  ```
102
116
 
103
117
  ## markdown 转 图片
@@ -106,8 +120,8 @@ htmlrender_connect_over_cdp = "http://127.0.0.1:1234"
106
120
  - 支持绝大部分 md 语法
107
121
  - 代码高亮
108
122
  - latex 数学公式 (感谢@[MeetWq](https://github.com/MeetWq))
109
- - 使用 `$$...$$` 来输入独立公式
110
- - 使用 `$...$` 来输入行内公式
123
+ - 使用 `$$...$$` 来输入独立公式
124
+ - 使用 `$...$` 来输入行内公式
111
125
  - 图片需要使用外部连接并使用`html`格式 否则文末会超出截图范围
112
126
  - 图片可使用 md 语法 路径可为 `绝对路径`(建议), 或 `相对于template_path` 的路径
113
127
 
@@ -165,10 +179,11 @@ LC_ALL zh_CN.UTF-8
165
179
  ### CentOS 使用 `yum`
166
180
 
167
181
  - ~~小心 CentOS~~
168
- - 参考[CentOS Dockerfile](https://github.com/kumaraditya303/playwright-centos/blob/master/Dockerfile)
182
+ -
183
+ 参考[CentOS Dockerfile](https://github.com/kumaraditya303/playwright-centos/blob/master/Dockerfile)
169
184
  - 添加中文字体库
170
185
  - ~~最佳解决办法~~
171
- - 使用 Docker 然后用 Python 镜像 按照上面 Ubuntu 的写 `dockerfile`
186
+ - 使用 Docker 然后用 Python 镜像 按照上面 Ubuntu 的写 `dockerfile`
172
187
 
173
188
  下面这个依赖运行一下 也许就可以用了
174
189
 
@@ -16,6 +16,7 @@
16
16
 
17
17
  ```py
18
18
  from nonebot import require
19
+
19
20
  require("nonebot_plugin_htmlrender")
20
21
  # 注意顺序,先require再 from ... import ...
21
22
  # 注意顺序,先require再 from ... import ...
@@ -33,32 +34,44 @@ from nonebot_plugin_htmlrender import (
33
34
 
34
35
  # 配置
35
36
 
37
+ ## .env 配置项说明
38
+
36
39
  ```ini
37
- # 默认情况 可不写
40
+ # Playwright 浏览器引擎类型
41
+ # 可不填,默认为 "chromium"
38
42
  htmlrender_browser = "chromium"
39
- # 使用 firefox
40
- htmlrender_browser = "firefox"
41
43
 
42
- # 下载 playwright 代理地址 可不写
44
+ # Playwright 浏览器下载地址
45
+ # 可选,用于自定义浏览器下载源
43
46
  htmlrender_download_host = ""
44
47
 
45
- # 浏览器自定代理地址 可不写
46
- htmlrender_proxy_host = "http://127.0.0.1:7890"
48
+ # Playwright 浏览器下载代理
49
+ # 可选,用于配置下载浏览器时的代理
50
+ htmlrender_download_proxy = ""
51
+
52
+ # Playwright 浏览器代理地址
53
+ # 可选,用于配置浏览器访问时的代理
54
+ # 示例: htmlrender_proxy_host = "http://127.0.0.1:7890"
55
+
56
+ # Playwright 浏览器代理绕过地址
57
+ # 可选,指定不使用代理的地址
58
+ htmlrender_proxy_host_bypass = ""
47
59
 
48
- # 浏览器 channel 支持以下
49
- # "chrome", "chrome-beta", "chrome-dev", "chrome-canary",
50
- # "msedge", "msedge-beta", "msedge-dev", "msedge-canary"
51
- # 手动编辑可以直接使用系统自带浏览器而不用重新下载 chromium
52
- # 可不写
60
+ # Playwright 浏览器通道
61
+ # 可选,支持以下值:
62
+ # - Chrome: "chrome", "chrome-beta", "chrome-dev", "chrome-canary"
63
+ # - Edge: "msedge", "msedge-beta", "msedge-dev", "msedge-canary"
64
+ # 配置后可直接使用系统浏览器,无需下载 Chromium
53
65
  htmlrender_browser_channel = ""
54
66
 
55
- # 浏览器执行文件路径 可不写
67
+ # Playwright 浏览器可执行文件路径
68
+ # 可选,用于指定浏览器程序位置
56
69
  htmlrender_browser_executable_path = ""
57
70
 
58
- # 使用 cdp 连接现有浏览器
59
- # 使用方法,自行启动浏览,在启动浏览器时添加参数 --remote-debugging-port=1234
60
- # 可不写
61
- htmlrender_connect_over_cdp = "http://127.0.0.1:1234"
71
+ # CDP 远程调试地址
72
+ # 可选,用于连接已运行的浏览器实例
73
+ # 使用时需要在启动浏览器时添加参数 --remote-debugging-port=端口号
74
+ htmlrender_connect_over_cdp = "http://127.0.0.1:9222"
62
75
  ```
63
76
 
64
77
  ## markdown 转 图片
@@ -67,8 +80,8 @@ htmlrender_connect_over_cdp = "http://127.0.0.1:1234"
67
80
  - 支持绝大部分 md 语法
68
81
  - 代码高亮
69
82
  - latex 数学公式 (感谢@[MeetWq](https://github.com/MeetWq))
70
- - 使用 `$$...$$` 来输入独立公式
71
- - 使用 `$...$` 来输入行内公式
83
+ - 使用 `$$...$$` 来输入独立公式
84
+ - 使用 `$...$` 来输入行内公式
72
85
  - 图片需要使用外部连接并使用`html`格式 否则文末会超出截图范围
73
86
  - 图片可使用 md 语法 路径可为 `绝对路径`(建议), 或 `相对于template_path` 的路径
74
87
 
@@ -126,10 +139,11 @@ LC_ALL zh_CN.UTF-8
126
139
  ### CentOS 使用 `yum`
127
140
 
128
141
  - ~~小心 CentOS~~
129
- - 参考[CentOS Dockerfile](https://github.com/kumaraditya303/playwright-centos/blob/master/Dockerfile)
142
+ -
143
+ 参考[CentOS Dockerfile](https://github.com/kumaraditya303/playwright-centos/blob/master/Dockerfile)
130
144
  - 添加中文字体库
131
145
  - ~~最佳解决办法~~
132
- - 使用 Docker 然后用 Python 镜像 按照上面 Ubuntu 的写 `dockerfile`
146
+ - 使用 Docker 然后用 Python 镜像 按照上面 Ubuntu 的写 `dockerfile`
133
147
 
134
148
  下面这个依赖运行一下 也许就可以用了
135
149
 
@@ -1,22 +1,24 @@
1
1
  from nonebot import require
2
+
2
3
  require("nonebot_plugin_htmlrender")
3
4
  # 注意顺序,先require再 from ... import ...
4
5
  # 注意顺序,先require再 from ... import ...
5
6
  # 注意顺序,先require再 from ... import ...
6
- from nonebot_plugin_htmlrender import (
7
- text_to_pic,
8
- md_to_pic,
9
- template_to_pic,
10
- get_new_page,
11
- )
7
+ import io
8
+
12
9
  # 注意顺序,先require再 from ... import ...
13
10
  # 注意顺序,先require再 from ... import ...
14
11
  # 注意顺序,先require再 from ... import ...
15
-
16
12
  from nonebot import on_command
17
13
  from nonebot.adapters.onebot.v11 import Bot, MessageEvent, MessageSegment
18
14
  from PIL import Image
19
- import io
15
+
16
+ from nonebot_plugin_htmlrender import (
17
+ get_new_page,
18
+ md_to_pic,
19
+ template_to_pic,
20
+ text_to_pic,
21
+ )
20
22
 
21
23
  from .utils import count_to_color
22
24
 
@@ -109,7 +111,7 @@ async def _():
109
111
  pages={
110
112
  "viewport": {"width": 600, "height": 300},
111
113
  "base_url": f"file://{template_path}",
112
- }
114
+ },
113
115
  )
114
116
 
115
117
  a = Image.open(io.BytesIO(pic))
@@ -124,7 +126,7 @@ md2pic = on_command("md2pic")
124
126
 
125
127
  @md2pic.handle()
126
128
  async def _md2pic(bot: Bot, event: MessageEvent):
127
- from pathlib import Path
129
+ # from pathlib import Path
128
130
 
129
131
  # 如果是直接获取消息内容 需要 unescape
130
132
  from nonebot.adapters.onebot.v11 import unescape
@@ -1,12 +1,13 @@
1
1
  from nonebot.log import logger
2
2
 
3
+
3
4
  def count_to_color(count: str) -> str:
4
- logger.debug(f"Filtering count: {count}")
5
- if count == '1':
5
+ logger.debug(f"Filtering count: {count}")
6
+ if count == "1":
6
7
  return "#facc15"
7
- elif count == '2':
8
+ elif count == "2":
8
9
  return "#f87171"
9
- elif count == '3':
10
+ elif count == "3":
10
11
  return "#c084fc"
11
12
  else:
12
- return "#60a5fa"
13
+ return "#60a5fa"
@@ -0,0 +1,55 @@
1
+ import nonebot
2
+ from nonebot.log import logger
3
+ from nonebot.plugin import PluginMetadata
4
+
5
+ from nonebot_plugin_htmlrender.browser import get_browser as get_browser
6
+ from nonebot_plugin_htmlrender.browser import get_new_page as get_new_page
7
+ from nonebot_plugin_htmlrender.browser import init_browser as init_browser
8
+ from nonebot_plugin_htmlrender.browser import shutdown_browser as shutdown_browser
9
+ from nonebot_plugin_htmlrender.browser import start_browser as start_browser
10
+ from nonebot_plugin_htmlrender.config import Config
11
+ from nonebot_plugin_htmlrender.data_source import capture_element as capture_element
12
+ from nonebot_plugin_htmlrender.data_source import html_to_pic as html_to_pic
13
+ from nonebot_plugin_htmlrender.data_source import md_to_pic as md_to_pic
14
+ from nonebot_plugin_htmlrender.data_source import template_to_html as template_to_html
15
+ from nonebot_plugin_htmlrender.data_source import template_to_pic as template_to_pic
16
+ from nonebot_plugin_htmlrender.data_source import text_to_pic as text_to_pic
17
+
18
+ __plugin_meta__ = PluginMetadata(
19
+ name="nonebot-plugin-htmlrender",
20
+ description="通过浏览器渲染图片",
21
+ usage="",
22
+ type="library",
23
+ config=Config,
24
+ homepage="https://github.com/kexue-z/nonebot-plugin-htmlrender",
25
+ extra={},
26
+ )
27
+
28
+ driver = nonebot.get_driver()
29
+
30
+
31
+ @driver.on_startup
32
+ async def init(**kwargs):
33
+ logger.info("htmlrender启动中...")
34
+ await init_browser(**kwargs)
35
+ logger.info("htmlrender已启动.")
36
+
37
+
38
+ @driver.on_shutdown
39
+ async def shutdown():
40
+ logger.info("htmlrender关闭中...")
41
+ await shutdown_browser()
42
+ logger.info("htmlrender已关闭.")
43
+
44
+
45
+ __all__ = [
46
+ "capture_element",
47
+ "get_new_page",
48
+ "html_to_pic",
49
+ "md_to_pic",
50
+ "shutdown_browser",
51
+ "start_browser",
52
+ "template_to_html",
53
+ "template_to_pic",
54
+ "text_to_pic",
55
+ ]
@@ -0,0 +1,169 @@
1
+ from collections.abc import AsyncIterator
2
+ from contextlib import asynccontextmanager
3
+ from typing import Optional
4
+
5
+ from nonebot.log import logger
6
+ from playwright.async_api import (
7
+ Browser,
8
+ BrowserType,
9
+ Error,
10
+ Page,
11
+ Playwright,
12
+ async_playwright,
13
+ )
14
+
15
+ from nonebot_plugin_htmlrender.config import BrowserEngineType, plugin_config
16
+ from nonebot_plugin_htmlrender.install import install_browser
17
+ from nonebot_plugin_htmlrender.utils import proxy_settings, suppress_and_log
18
+
19
+ _browser: Optional[Browser] = None
20
+ _playwright: Optional[Playwright] = None
21
+
22
+
23
+ async def _launch(browser_type: BrowserEngineType, **kwargs) -> Browser:
24
+ """
25
+ 启动浏览器实例。
26
+
27
+ Args:
28
+ browser_type (BrowserEngineType): 浏览器类型。
29
+ **kwargs: 传递给`playwright.launch`的关键字参数。
30
+
31
+ Returns:
32
+ Browser: 启动的浏览器实例。
33
+ """
34
+ _browser_cls: BrowserType = getattr(_playwright, browser_type)
35
+ logger.opt(colors=True).debug(
36
+ f"<cyan>{browser_type.capitalize()}</cyan> launching with kwargs: {kwargs}"
37
+ )
38
+ logger.opt(colors=True).debug(
39
+ f"Looking for Browser in path: <blue>{_browser_cls.executable_path}</blue>"
40
+ )
41
+ return await _browser_cls.launch(**kwargs)
42
+
43
+
44
+ async def init_browser(**kwargs) -> Browser:
45
+ """
46
+ 初始化浏览器实例。
47
+
48
+ Args:
49
+ **kwargs: 传递给`playwright.launch`的关键字参数。
50
+
51
+ Returns:
52
+ Browser: 浏览器实例。
53
+
54
+ Raises:
55
+ RuntimeError: 如果浏览器无法启动或安装失败。
56
+ """
57
+ try:
58
+ return await start_browser(**kwargs)
59
+ except (Error, Exception) as e:
60
+ if "Executable doesn't exist" not in str(e):
61
+ raise RuntimeError("无法启动浏览器实例") from e
62
+ try:
63
+ if await install_browser():
64
+ return await start_browser(**kwargs)
65
+ else:
66
+ raise RuntimeError("无法启动浏览器实例且尝试安装失败") from e
67
+ except Exception as e:
68
+ raise RuntimeError("无法启动浏览器实例") from e
69
+
70
+
71
+ @asynccontextmanager
72
+ async def get_new_page(device_scale_factor: float = 2, **kwargs) -> AsyncIterator[Page]:
73
+ """
74
+ 获取一个新的页面的上下文管理器。
75
+
76
+ Args:
77
+ device_scale_factor (float): 设备缩放因子。
78
+ **kwargs: 传递给`browser.new_context`的关键字参数。
79
+
80
+ Yields:
81
+ Page: 页面对象。
82
+ """
83
+ ctx = await get_browser()
84
+ page = await ctx.new_page(device_scale_factor=device_scale_factor, **kwargs)
85
+ try:
86
+ yield page
87
+ finally:
88
+ await page.close()
89
+
90
+
91
+ async def get_browser(**kwargs) -> Browser:
92
+ """
93
+ 获取浏览器实例。
94
+
95
+ Args:
96
+ **kwargs: 传递给`playwright.launch`的关键字参数。
97
+
98
+ Returns:
99
+ Browser: 浏览器实例。
100
+ """
101
+ if _browser and _browser.is_connected():
102
+ return _browser
103
+
104
+ return await init_browser(**kwargs)
105
+
106
+
107
+ async def _connect_via_cdp(**kwargs) -> Browser:
108
+ """
109
+ 通过 CDP 连接 Chromium 浏览器。
110
+
111
+ Args:
112
+ **kwargs: 传递给`chromium.connect_over_cdp`的关键字参数。
113
+
114
+ Returns:
115
+ Browser: 通过 CDP 连接的浏览器实例。
116
+
117
+ Raises:
118
+ RuntimeError: 如果 Playwright 未初始化。
119
+ """
120
+ kwargs["endpoint_url"] = plugin_config.htmlrender_connect_over_cdp
121
+ logger.info(
122
+ f"正在使用 CDP 连接 Chromium({plugin_config.htmlrender_connect_over_cdp})"
123
+ )
124
+ if _playwright is not None:
125
+ return await _playwright.chromium.connect_over_cdp(**kwargs)
126
+ else:
127
+ raise RuntimeError("Playwright 未初始化")
128
+
129
+
130
+ async def start_browser(**kwargs) -> Browser:
131
+ """
132
+ 启动 Playwright 浏览器实例。
133
+
134
+ Args:
135
+ **kwargs: 传递给`playwright.launch`的关键字参数。
136
+
137
+ Returns:
138
+ Browser: 启动的浏览器实例。
139
+ """
140
+ global _browser, _playwright
141
+ _playwright = await async_playwright().start()
142
+
143
+ if (
144
+ plugin_config.htmlrender_browser == "chromium"
145
+ and plugin_config.htmlrender_connect_over_cdp
146
+ ):
147
+ return await _connect_via_cdp(**kwargs)
148
+
149
+ if plugin_config.htmlrender_browser_channel:
150
+ kwargs["channel"] = plugin_config.htmlrender_browser_channel
151
+
152
+ if plugin_config.htmlrender_proxy_host:
153
+ kwargs["proxy"] = proxy_settings(plugin_config.htmlrender_proxy_host)
154
+
155
+ if plugin_config.htmlrender_browser_executable_path:
156
+ kwargs["executable_path"] = plugin_config.htmlrender_browser_executable_path
157
+
158
+ _browser = await _launch(plugin_config.htmlrender_browser, **kwargs)
159
+ return _browser
160
+
161
+
162
+ async def shutdown_browser() -> None:
163
+ """关闭浏览器和 Playwright 实例。"""
164
+ if _browser:
165
+ with suppress_and_log():
166
+ await _browser.close()
167
+ if _playwright:
168
+ with suppress_and_log():
169
+ await _playwright.stop()
@@ -0,0 +1,80 @@
1
+ from collections.abc import Generator
2
+ from typing import Any, Callable, ClassVar, Optional
3
+
4
+ from nonebot import get_driver, get_plugin_config
5
+ from nonebot.compat import custom_validation
6
+ from pydantic import BaseModel, Field
7
+
8
+ from nonebot_plugin_htmlrender.consts import BROWSER_CHANNEL_TYPES, BROWSER_ENGINE_TYPES
9
+
10
+
11
+ @custom_validation
12
+ class BrowserEngineType(str):
13
+ """Playwright浏览器引擎类型,支持的值由 BROWSER_ENGINE_TYPES 定义。"""
14
+
15
+ ALLOWED_VALUES: ClassVar = BROWSER_ENGINE_TYPES
16
+
17
+ @classmethod
18
+ def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
19
+ yield cls.validate
20
+
21
+ @classmethod
22
+ def validate(cls, value: Any) -> str:
23
+ if value not in cls.ALLOWED_VALUES:
24
+ raise ValueError(
25
+ f"Invalid browser type: {value!r}, must be one of {cls.ALLOWED_VALUES}"
26
+ )
27
+ return value
28
+
29
+
30
+ @custom_validation
31
+ class BrowserChannelType(str):
32
+ """Playwright浏览器通道类型,支持的值由 BROWSER_CHANNEL_TYPES 定义。"""
33
+
34
+ ALLOWED_VALUES: ClassVar = BROWSER_CHANNEL_TYPES
35
+
36
+ @classmethod
37
+ def __get_validators__(cls) -> Generator[Callable[..., Any], None, None]:
38
+ yield cls.validate
39
+
40
+ @classmethod
41
+ def validate(cls, value: Any) -> str:
42
+ if value not in cls.ALLOWED_VALUES:
43
+ raise ValueError(
44
+ f"Invalid channel: {value!r}, must be one of {cls.ALLOWED_VALUES}"
45
+ )
46
+ return value
47
+
48
+
49
+ class Config(BaseModel):
50
+ """插件配置类。"""
51
+
52
+ htmlrender_browser: BrowserEngineType = Field(
53
+ default="chromium",
54
+ description="Playwright浏览器引擎类型,默认值为 'chromium'。",
55
+ )
56
+ htmlrender_download_host: Optional[str] = Field(
57
+ default=None, description="下载Playwright浏览器时的主机地址。"
58
+ )
59
+ htmlrender_download_proxy: Optional[str] = Field(
60
+ default=None, description="下载Playwright浏览器时的代理设置。"
61
+ )
62
+ htmlrender_proxy_host: Optional[str] = Field(
63
+ default=None, description="Playwright浏览器使用的代理主机地址。"
64
+ )
65
+ htmlrender_proxy_host_bypass: Optional[str] = Field(
66
+ default=None, description="Playwright浏览器代理的绕过地址。"
67
+ )
68
+ htmlrender_browser_channel: Optional[BrowserChannelType] = Field(
69
+ default=None, description="Playwright浏览器通道类型。"
70
+ )
71
+ htmlrender_browser_executable_path: Optional[str] = Field(
72
+ default=None, description="Playwright浏览器可执行文件的路径。"
73
+ )
74
+ htmlrender_connect_over_cdp: Optional[str] = Field(
75
+ default=None, description="通过 CDP 连接Playwright浏览器的端点地址。"
76
+ )
77
+
78
+
79
+ global_config = get_driver().config
80
+ plugin_config = get_plugin_config(Config)
@@ -0,0 +1,38 @@
1
+ from dataclasses import dataclass
2
+ import os
3
+ import sys
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class MirrorSource:
8
+ name: str
9
+ url: str
10
+ priority: int
11
+
12
+
13
+ # consts
14
+ PLUGINS_GROUP = "nonebot_plugin_htmlrender"
15
+ SCRIPTS_GROUP = "nonebot_plugin_htmlrender_scripts"
16
+ REQUIRES_PYTHON = (3, 9)
17
+ # SHELL = os.getenv("SHELL", "")
18
+ WINDOWS = sys.platform.startswith("win") or (sys.platform == "cli" and os.name == "nt")
19
+ # MINGW = sysconfig.get_platform().startswith("mingw")
20
+ # MACOS = sys.platform == "darwin"
21
+ MIRRORS = [
22
+ MirrorSource("官方", "https://playwright.azureedge.net", 1),
23
+ MirrorSource("淘宝", "https://registry.npmmirror.com/-/binary/playwright", 2),
24
+ ]
25
+ BROWSER_ENGINE_TYPES = ["chromium", "firefox", "webkit"]
26
+ BROWSER_CHANNEL_TYPES = [
27
+ "chromium",
28
+ "chrome",
29
+ "chrome-beta",
30
+ "chrome-dev",
31
+ "chrome-canary",
32
+ "msedge",
33
+ "msedge-beta",
34
+ "msedge-dev",
35
+ "msedge-canary",
36
+ "firefox",
37
+ "webkit",
38
+ ]