nonebot-plugin-parser 2.2.0__tar.gz → 2.2.2__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_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/PKG-INFO +18 -19
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/README.md +16 -18
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/pyproject.toml +6 -3
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/constants.py +12 -6
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/exception.py +12 -6
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/helper.py +64 -23
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/matchers/__init__.py +5 -5
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/__init__.py +18 -1
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/acfun.py +2 -2
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/base.py +32 -6
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +29 -27
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/douyin/__init__.py +5 -2
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/weibo.py +99 -10
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/base.py +4 -2
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/common.py +57 -50
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/__init__.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/config.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/download/__init__.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/download/task.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/matchers/filter.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/matchers/rule.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/data.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/douyin/slides.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/douyin/video.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/kuaishou.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/nga.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/tiktok.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/twitter.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/xiaohongshu.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/parsers/youtube.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/__init__.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/default.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW-1.ttf +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/media_button.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/templates/weibo.html.jinja +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/renders/weibo.py +0 -0
- {nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nonebot-plugin-parser
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: NoneBot2 链接分享解析 Alconna 版, 通用媒体卡片渲染(PIL 实现), 支持 B站/抖音/快手/微博/小红书/youtube/tiktok/twitter/acfun/nga
|
|
5
5
|
Keywords: acfun,bilibili,douyin,kuaishou,nga,nonebot,nonebot2,tiktok,twitter,video,weibo,xiaohongshu,youtube
|
|
6
6
|
Author: fllesser
|
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
18
|
Classifier: Topic :: Communications :: Chat
|
|
18
19
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
19
20
|
Classifier: Topic :: Multimedia :: Video
|
|
@@ -22,7 +23,7 @@ Requires-Dist: beautifulsoup4>=4.12.0,<5.0.0
|
|
|
22
23
|
Requires-Dist: bilibili-api-python>=17.4.0,<18.0.0
|
|
23
24
|
Requires-Dist: curl-cffi>=0.13.0,<1.0.0
|
|
24
25
|
Requires-Dist: httpx>=0.27.2,<1.0.0
|
|
25
|
-
Requires-Dist: msgspec>=0.
|
|
26
|
+
Requires-Dist: msgspec>=0.20.0,<1.0.0
|
|
26
27
|
Requires-Dist: nonebot-plugin-alconna>=0.59.4
|
|
27
28
|
Requires-Dist: nonebot-plugin-apscheduler>=0.5.0,<1.0.0
|
|
28
29
|
Requires-Dist: nonebot-plugin-localstore>=0.7.4,<1.0.0
|
|
@@ -68,19 +69,19 @@ Description-Content-Type: text/markdown
|
|
|
68
69
|
|
|
69
70
|
## 📖 介绍
|
|
70
71
|
|
|
71
|
-
| 平台 | 触发的消息形态
|
|
72
|
-
| ------- |
|
|
73
|
-
| B站 | BV
|
|
74
|
-
| 抖音 | 链接(分享链接,兼容电脑端链接)
|
|
75
|
-
| 微博 | 链接(博文,视频,show)
|
|
76
|
-
| 小红书 | 链接(含短链)/卡片
|
|
77
|
-
| 快手 | 链接(包含标准链接和短链)
|
|
78
|
-
| acfun | 链接
|
|
79
|
-
| youtube | 链接(含短链)
|
|
80
|
-
| tiktok | 链接
|
|
81
|
-
| twitter | 链接
|
|
72
|
+
| 平台 | 触发的消息形态 | 视频 | 图集 | 音频 |
|
|
73
|
+
| ------- | ------------------------------- | ---- | ---- | ---- |
|
|
74
|
+
| B站 | av号/BV号/链接/短链/卡片/小程序 | ✅ | ✅ | ✅ |
|
|
75
|
+
| 抖音 | 链接(分享链接,兼容电脑端链接) | ✅ | ✅ | ❌️ |
|
|
76
|
+
| 微博 | 链接(博文,视频,show, 文章) | ✅ | ✅ | ❌️ |
|
|
77
|
+
| 小红书 | 链接(含短链)/卡片 | ✅ | ✅ | ❌️ |
|
|
78
|
+
| 快手 | 链接(包含标准链接和短链) | ✅ | ✅ | ❌️ |
|
|
79
|
+
| acfun | 链接 | ✅ | ❌️ | ❌️ |
|
|
80
|
+
| youtube | 链接(含短链) | ✅ | ❌️ | ✅ |
|
|
81
|
+
| tiktok | 链接 | ✅ | ❌️ | ❌️ |
|
|
82
|
+
| twitter | 链接 | ✅ | ✅ | ❌️ |
|
|
82
83
|
|
|
83
|
-
支持的链接,可参考 [测试链接](https://github.com/fllesser/nonebot-plugin-parser/blob/master/
|
|
84
|
+
支持的链接,可参考 [测试链接](https://github.com/fllesser/nonebot-plugin-parser/blob/master/tests/others/test_urls.md)
|
|
84
85
|
|
|
85
86
|
## 🎨 效果图
|
|
86
87
|
插件默认启用 PIL 实现的通用媒体卡片渲染,效果图如下
|
|
@@ -289,9 +290,7 @@ from httpx import AsyncClient
|
|
|
289
290
|
from nonebot import require
|
|
290
291
|
|
|
291
292
|
require("nonebot_plugin_parser")
|
|
292
|
-
from nonebot_plugin_parser.parsers import BaseParser,
|
|
293
|
-
from nonebot_plugin_parser.parsers.base import Platform, handle
|
|
294
|
-
|
|
293
|
+
from nonebot_plugin_parser.parsers import BaseParser, Platform, handle
|
|
295
294
|
|
|
296
295
|
class ExampleParser(BaseParser):
|
|
297
296
|
"""示例视频网站解析器"""
|
|
@@ -299,7 +298,7 @@ class ExampleParser(BaseParser):
|
|
|
299
298
|
platform: ClassVar[Platform] = Platform(name="example", display_name="示例网站")
|
|
300
299
|
|
|
301
300
|
@handle("ex.short", r"ex\.short/\w+)")
|
|
302
|
-
async def _parse_short_link(self, searched:
|
|
301
|
+
async def _parse_short_link(self, searched: Match[str]):
|
|
303
302
|
"""解析短链"""
|
|
304
303
|
url = f"https://{searched.group(0)}"
|
|
305
304
|
# 重定向再解析,请确保重定向链接的 handle 存在
|
|
@@ -308,7 +307,7 @@ class ExampleParser(BaseParser):
|
|
|
308
307
|
|
|
309
308
|
@handle("example.com", r"example\.com/video/(?P<video_id>\w+)")
|
|
310
309
|
@handle("exam.ple", r"exam\.ple/(?P<video_id>\w+)")
|
|
311
|
-
async def _parse(self, searched: Match[str])
|
|
310
|
+
async def _parse(self, searched: Match[str]):
|
|
312
311
|
# 1. 提取视频 ID
|
|
313
312
|
video_id = searched.group("video_id")
|
|
314
313
|
|
|
@@ -22,19 +22,19 @@
|
|
|
22
22
|
|
|
23
23
|
## 📖 介绍
|
|
24
24
|
|
|
25
|
-
| 平台 | 触发的消息形态
|
|
26
|
-
| ------- |
|
|
27
|
-
| B站 | BV
|
|
28
|
-
| 抖音 | 链接(分享链接,兼容电脑端链接)
|
|
29
|
-
| 微博 | 链接(博文,视频,show)
|
|
30
|
-
| 小红书 | 链接(含短链)/卡片
|
|
31
|
-
| 快手 | 链接(包含标准链接和短链)
|
|
32
|
-
| acfun | 链接
|
|
33
|
-
| youtube | 链接(含短链)
|
|
34
|
-
| tiktok | 链接
|
|
35
|
-
| twitter | 链接
|
|
36
|
-
|
|
37
|
-
支持的链接,可参考 [测试链接](https://github.com/fllesser/nonebot-plugin-parser/blob/master/
|
|
25
|
+
| 平台 | 触发的消息形态 | 视频 | 图集 | 音频 |
|
|
26
|
+
| ------- | ------------------------------- | ---- | ---- | ---- |
|
|
27
|
+
| B站 | av号/BV号/链接/短链/卡片/小程序 | ✅ | ✅ | ✅ |
|
|
28
|
+
| 抖音 | 链接(分享链接,兼容电脑端链接) | ✅ | ✅ | ❌️ |
|
|
29
|
+
| 微博 | 链接(博文,视频,show, 文章) | ✅ | ✅ | ❌️ |
|
|
30
|
+
| 小红书 | 链接(含短链)/卡片 | ✅ | ✅ | ❌️ |
|
|
31
|
+
| 快手 | 链接(包含标准链接和短链) | ✅ | ✅ | ❌️ |
|
|
32
|
+
| acfun | 链接 | ✅ | ❌️ | ❌️ |
|
|
33
|
+
| youtube | 链接(含短链) | ✅ | ❌️ | ✅ |
|
|
34
|
+
| tiktok | 链接 | ✅ | ❌️ | ❌️ |
|
|
35
|
+
| twitter | 链接 | ✅ | ✅ | ❌️ |
|
|
36
|
+
|
|
37
|
+
支持的链接,可参考 [测试链接](https://github.com/fllesser/nonebot-plugin-parser/blob/master/tests/others/test_urls.md)
|
|
38
38
|
|
|
39
39
|
## 🎨 效果图
|
|
40
40
|
插件默认启用 PIL 实现的通用媒体卡片渲染,效果图如下
|
|
@@ -243,9 +243,7 @@ from httpx import AsyncClient
|
|
|
243
243
|
from nonebot import require
|
|
244
244
|
|
|
245
245
|
require("nonebot_plugin_parser")
|
|
246
|
-
from nonebot_plugin_parser.parsers import BaseParser,
|
|
247
|
-
from nonebot_plugin_parser.parsers.base import Platform, handle
|
|
248
|
-
|
|
246
|
+
from nonebot_plugin_parser.parsers import BaseParser, Platform, handle
|
|
249
247
|
|
|
250
248
|
class ExampleParser(BaseParser):
|
|
251
249
|
"""示例视频网站解析器"""
|
|
@@ -253,7 +251,7 @@ class ExampleParser(BaseParser):
|
|
|
253
251
|
platform: ClassVar[Platform] = Platform(name="example", display_name="示例网站")
|
|
254
252
|
|
|
255
253
|
@handle("ex.short", r"ex\.short/\w+)")
|
|
256
|
-
async def _parse_short_link(self, searched:
|
|
254
|
+
async def _parse_short_link(self, searched: Match[str]):
|
|
257
255
|
"""解析短链"""
|
|
258
256
|
url = f"https://{searched.group(0)}"
|
|
259
257
|
# 重定向再解析,请确保重定向链接的 handle 存在
|
|
@@ -262,7 +260,7 @@ class ExampleParser(BaseParser):
|
|
|
262
260
|
|
|
263
261
|
@handle("example.com", r"example\.com/video/(?P<video_id>\w+)")
|
|
264
262
|
@handle("exam.ple", r"exam\.ple/(?P<video_id>\w+)")
|
|
265
|
-
async def _parse(self, searched: Match[str])
|
|
263
|
+
async def _parse(self, searched: Match[str]):
|
|
266
264
|
# 1. 提取视频 ID
|
|
267
265
|
video_id = searched.group("video_id")
|
|
268
266
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
#:tombi schema.strict = false
|
|
2
|
+
|
|
1
3
|
[project]
|
|
2
4
|
name = "nonebot-plugin-parser"
|
|
3
|
-
version = "2.2.
|
|
5
|
+
version = "2.2.2"
|
|
4
6
|
description = "NoneBot2 链接分享解析 Alconna 版, 通用媒体卡片渲染(PIL 实现), 支持 B站/抖音/快手/微博/小红书/youtube/tiktok/twitter/acfun/nga"
|
|
5
7
|
readme = "README.md"
|
|
6
8
|
requires-python = ">=3.10"
|
|
@@ -30,6 +32,7 @@ classifiers = [
|
|
|
30
32
|
"Programming Language :: Python :: 3.10",
|
|
31
33
|
"Programming Language :: Python :: 3.11",
|
|
32
34
|
"Programming Language :: Python :: 3.12",
|
|
35
|
+
"Programming Language :: Python :: 3.13",
|
|
33
36
|
"Topic :: Communications :: Chat",
|
|
34
37
|
"Topic :: Internet :: WWW/HTTP",
|
|
35
38
|
"Topic :: Multimedia :: Video",
|
|
@@ -40,7 +43,7 @@ dependencies = [
|
|
|
40
43
|
"bilibili-api-python>=17.4.0,<18.0.0",
|
|
41
44
|
"curl_cffi>=0.13.0,<1.0.0",
|
|
42
45
|
"httpx>=0.27.2,<1.0.0",
|
|
43
|
-
"msgspec>=0.
|
|
46
|
+
"msgspec>=0.20.0,<1.0.0",
|
|
44
47
|
"nonebot-plugin-alconna>=0.59.4",
|
|
45
48
|
"nonebot-plugin-apscheduler>=0.5.0,<1.0.0",
|
|
46
49
|
"nonebot-plugin-localstore>=0.7.4,<1.0.0",
|
|
@@ -86,7 +89,7 @@ requires = ["uv_build>=0.9.0,<0.10.0"]
|
|
|
86
89
|
build-backend = "uv_build"
|
|
87
90
|
|
|
88
91
|
[tool.bumpversion]
|
|
89
|
-
current_version = "2.2.
|
|
92
|
+
current_version = "2.2.2"
|
|
90
93
|
commit = true
|
|
91
94
|
message = "release: bump vesion from {current_version} to {new_version}"
|
|
92
95
|
tag = true
|
{nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/constants.py
RENAMED
|
@@ -4,18 +4,24 @@ from typing import Final
|
|
|
4
4
|
from httpx import Timeout
|
|
5
5
|
|
|
6
6
|
COMMON_HEADER: Final[dict[str, str]] = {
|
|
7
|
-
"User-Agent":
|
|
8
|
-
|
|
7
|
+
"User-Agent": (
|
|
8
|
+
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
9
|
+
"Chrome/55.0.2883.87 UBrowser/6.2.4098.3 Safari/537.36"
|
|
10
|
+
)
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
IOS_HEADER: Final[dict[str, str]] = {
|
|
12
|
-
"User-Agent":
|
|
13
|
-
|
|
14
|
+
"User-Agent": (
|
|
15
|
+
"Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) "
|
|
16
|
+
"Version/16.6 Mobile/15E148 Safari/604.1 Edg/132.0.0.0"
|
|
17
|
+
)
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
ANDROID_HEADER: Final[dict[str, str]] = {
|
|
17
|
-
"User-Agent":
|
|
18
|
-
|
|
21
|
+
"User-Agent": (
|
|
22
|
+
"Mozilla/5.0 (Linux; Android 15; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
23
|
+
"Chrome/132.0.0.0 Mobile Safari/537.36 Edg/132.0.0.0"
|
|
24
|
+
)
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
COMMON_TIMEOUT: Final[Timeout] = Timeout(connect=15.0, read=20.0, write=10.0, pool=10.0)
|
{nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/exception.py
RENAMED
|
@@ -2,39 +2,45 @@ class ParseException(Exception):
|
|
|
2
2
|
"""异常基类"""
|
|
3
3
|
|
|
4
4
|
def __init__(self, message: str):
|
|
5
|
+
super().__init__(message)
|
|
5
6
|
self.message = message
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
class TipException(ParseException):
|
|
10
|
+
"""提示异常"""
|
|
11
|
+
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
8
15
|
class DownloadException(ParseException):
|
|
9
16
|
"""下载异常"""
|
|
10
17
|
|
|
11
18
|
def __init__(self, message: str | None = None):
|
|
12
|
-
|
|
19
|
+
super().__init__(message or "媒体下载失败")
|
|
13
20
|
|
|
14
21
|
|
|
15
22
|
class DownloadLimitException(DownloadException):
|
|
16
23
|
"""下载超过限制异常"""
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
raise NotImplementedError
|
|
25
|
+
pass
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
class SizeLimitException(DownloadLimitException):
|
|
23
29
|
"""下载大小超过限制异常"""
|
|
24
30
|
|
|
25
31
|
def __init__(self):
|
|
26
|
-
|
|
32
|
+
super().__init__("媒体大小超过配置限制,取消下载")
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
class DurationLimitException(DownloadLimitException):
|
|
30
36
|
"""下载时长超过限制异常"""
|
|
31
37
|
|
|
32
38
|
def __init__(self):
|
|
33
|
-
|
|
39
|
+
super().__init__("媒体时长超过配置限制,取消下载")
|
|
34
40
|
|
|
35
41
|
|
|
36
42
|
class ZeroSizeException(DownloadException):
|
|
37
43
|
"""下载大小为 0 异常"""
|
|
38
44
|
|
|
39
45
|
def __init__(self):
|
|
40
|
-
|
|
46
|
+
super().__init__("媒体大小为 0, 取消下载")
|
{nonebot_plugin_parser-2.2.0 → nonebot_plugin_parser-2.2.2}/src/nonebot_plugin_parser/helper.py
RENAMED
|
@@ -1,25 +1,38 @@
|
|
|
1
|
-
from collections.abc import Sequence
|
|
1
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
2
2
|
from functools import wraps
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Literal
|
|
4
|
+
from typing import Any, ClassVar, Literal
|
|
5
5
|
|
|
6
6
|
from nonebot import logger
|
|
7
7
|
from nonebot.adapters import Event
|
|
8
|
-
from nonebot.
|
|
9
|
-
from
|
|
10
|
-
from nonebot_plugin_alconna import
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
from nonebot.matcher import current_bot, current_event
|
|
9
|
+
from nonebot_plugin_alconna import SupportAdapter, uniseg
|
|
10
|
+
from nonebot_plugin_alconna.uniseg import (
|
|
11
|
+
CustomNode,
|
|
12
|
+
File,
|
|
13
|
+
Image,
|
|
14
|
+
Reference,
|
|
15
|
+
Segment,
|
|
16
|
+
Text,
|
|
17
|
+
UniMessage,
|
|
18
|
+
Video,
|
|
19
|
+
Voice,
|
|
20
|
+
)
|
|
13
21
|
|
|
14
22
|
from .config import pconfig
|
|
15
23
|
|
|
24
|
+
# from .exception import TipException
|
|
25
|
+
|
|
16
26
|
ForwardNodeInner = str | Segment | UniMessage
|
|
17
27
|
"""转发消息节点内部允许的类型"""
|
|
18
28
|
|
|
19
29
|
|
|
20
30
|
class UniHelper:
|
|
21
31
|
@staticmethod
|
|
22
|
-
def construct_forward_message(
|
|
32
|
+
def construct_forward_message(
|
|
33
|
+
segments: Sequence[ForwardNodeInner],
|
|
34
|
+
user_id: str | None = None,
|
|
35
|
+
) -> Reference:
|
|
23
36
|
"""构造转发消息
|
|
24
37
|
|
|
25
38
|
Args:
|
|
@@ -45,7 +58,10 @@ class UniHelper:
|
|
|
45
58
|
return Reference(nodes=nodes)
|
|
46
59
|
|
|
47
60
|
@staticmethod
|
|
48
|
-
def img_seg(
|
|
61
|
+
def img_seg(
|
|
62
|
+
img_path: Path | None = None,
|
|
63
|
+
raw: bytes | None = None,
|
|
64
|
+
) -> Image:
|
|
49
65
|
"""获取图片 Seg
|
|
50
66
|
|
|
51
67
|
Args:
|
|
@@ -102,7 +118,10 @@ class UniHelper:
|
|
|
102
118
|
return Video(path=video_path)
|
|
103
119
|
|
|
104
120
|
@staticmethod
|
|
105
|
-
def file_seg(
|
|
121
|
+
def file_seg(
|
|
122
|
+
file: Path,
|
|
123
|
+
display_name: str | None = None,
|
|
124
|
+
) -> File:
|
|
106
125
|
"""获取文件 Seg
|
|
107
126
|
|
|
108
127
|
Args:
|
|
@@ -121,43 +140,65 @@ class UniHelper:
|
|
|
121
140
|
else:
|
|
122
141
|
return File(path=file, name=display_name)
|
|
123
142
|
|
|
124
|
-
|
|
143
|
+
EMOJI_MAP: ClassVar[dict[str, tuple[str, str]]] = {
|
|
144
|
+
"fail": ("10060", "❌"),
|
|
145
|
+
"resolving": ("424", "👀"),
|
|
146
|
+
"done": ("144", "🎉"),
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
125
150
|
async def message_reaction(
|
|
151
|
+
cls,
|
|
126
152
|
event: Event,
|
|
127
153
|
status: Literal["fail", "resolving", "done"],
|
|
128
154
|
) -> None:
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
155
|
+
"""发送消息回应
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
event (Event): 事件对象
|
|
159
|
+
status (Literal["fail", "resolving", "done"]): 状态
|
|
160
|
+
"""
|
|
134
161
|
message_id = uniseg.get_message_id(event)
|
|
135
162
|
target = uniseg.get_target(event)
|
|
136
163
|
|
|
137
164
|
if target.adapter in (SupportAdapter.onebot11, SupportAdapter.qq):
|
|
138
|
-
emoji =
|
|
165
|
+
emoji = cls.EMOJI_MAP[status][0]
|
|
139
166
|
else:
|
|
140
|
-
emoji =
|
|
167
|
+
emoji = cls.EMOJI_MAP[status][1]
|
|
141
168
|
|
|
142
169
|
try:
|
|
143
170
|
await uniseg.message_reaction(emoji, message_id=message_id)
|
|
144
171
|
except Exception:
|
|
145
172
|
logger.warning(f"reaction {emoji} to {message_id} failed, maybe not support")
|
|
146
173
|
|
|
147
|
-
@
|
|
148
|
-
def
|
|
174
|
+
@classmethod
|
|
175
|
+
def with_reaction(cls, func: Callable[..., Awaitable[Any]]):
|
|
176
|
+
"""自动回应装饰器
|
|
177
|
+
|
|
178
|
+
自动处理消息响应状态,并捕获 TipException 发送提示消息
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
func: 被装饰的函数
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
装饰后的函数
|
|
185
|
+
"""
|
|
186
|
+
|
|
149
187
|
@wraps(func)
|
|
150
188
|
async def wrapper(*args, **kwargs):
|
|
151
189
|
event = current_event.get()
|
|
152
|
-
await
|
|
190
|
+
await cls.message_reaction(event, "resolving")
|
|
153
191
|
|
|
154
192
|
try:
|
|
155
193
|
result = await func(*args, **kwargs)
|
|
194
|
+
# except TipException as e:
|
|
195
|
+
# await UniMessage.text(e.message).send()
|
|
196
|
+
# raise
|
|
156
197
|
except Exception:
|
|
157
|
-
await
|
|
198
|
+
await cls.message_reaction(event, "fail")
|
|
158
199
|
raise
|
|
159
200
|
|
|
160
|
-
await
|
|
201
|
+
await cls.message_reaction(event, "done")
|
|
161
202
|
return result
|
|
162
203
|
|
|
163
204
|
return wrapper
|
|
@@ -45,7 +45,7 @@ def clear_result_cache():
|
|
|
45
45
|
_RESULT_CACHE.clear()
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
@UniHelper.
|
|
48
|
+
@UniHelper.with_reaction
|
|
49
49
|
async def parser_handler(
|
|
50
50
|
sr: SearchResult = Searched(),
|
|
51
51
|
):
|
|
@@ -67,7 +67,7 @@ async def parser_handler(
|
|
|
67
67
|
async for message in renderer.render_messages(result):
|
|
68
68
|
await message.send()
|
|
69
69
|
|
|
70
|
-
# 4.
|
|
70
|
+
# 4. 缓存解析结果
|
|
71
71
|
_RESULT_CACHE[cache_key] = result
|
|
72
72
|
|
|
73
73
|
|
|
@@ -84,7 +84,7 @@ from ..parsers import BilibiliParser
|
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
@on_command("bm", priority=3, block=True).handle()
|
|
87
|
-
@UniHelper.
|
|
87
|
+
@UniHelper.with_reaction
|
|
88
88
|
async def _(message: Message = CommandArg()):
|
|
89
89
|
text = message.extract_plain_text()
|
|
90
90
|
matched = re.search(r"(BV[A-Za-z0-9]{10})(\s\d{1,3})?", text)
|
|
@@ -116,13 +116,13 @@ if YTDLP_DOWNLOADER is not None:
|
|
|
116
116
|
from ..parsers import YouTubeParser
|
|
117
117
|
|
|
118
118
|
@on_command("ym", priority=3, block=True).handle()
|
|
119
|
-
@UniHelper.
|
|
119
|
+
@UniHelper.with_reaction
|
|
120
120
|
async def _(message: Message = CommandArg()):
|
|
121
121
|
text = message.extract_plain_text()
|
|
122
122
|
ytb_parser = cast(YouTubeParser, KEYWORD_PARSER_MAP["youtu.be"])
|
|
123
123
|
_, matched = ytb_parser.search_url(text)
|
|
124
124
|
if not matched:
|
|
125
|
-
await UniMessage("
|
|
125
|
+
await UniMessage("请发送正确的油管链接").finish()
|
|
126
126
|
|
|
127
127
|
url = matched.group(0)
|
|
128
128
|
|
|
@@ -3,7 +3,6 @@ from ..download import YTDLP_DOWNLOADER
|
|
|
3
3
|
from .acfun import AcfunParser as AcfunParser
|
|
4
4
|
from .base import BaseParser as BaseParser
|
|
5
5
|
from .bilibili import BilibiliParser as BilibiliParser
|
|
6
|
-
from .data import ParseResult as ParseResult
|
|
7
6
|
from .douyin import DouyinParser as DouyinParser
|
|
8
7
|
from .kuaishou import KuaiShouParser as KuaiShouParser
|
|
9
8
|
from .nga import NGAParser as NGAParser
|
|
@@ -15,6 +14,24 @@ if YTDLP_DOWNLOADER is not None:
|
|
|
15
14
|
from .tiktok import TikTokParser as TikTokParser
|
|
16
15
|
from .youtube import YouTubeParser as YouTubeParser
|
|
17
16
|
|
|
17
|
+
from .base import handle as handle
|
|
18
|
+
from .data import AudioContent as AudioContent
|
|
19
|
+
from .data import Author
|
|
20
|
+
from .data import DynamicContent as DynamicContent
|
|
21
|
+
from .data import GraphicsContent as GraphicsContent
|
|
22
|
+
from .data import ImageContent as ImageContent
|
|
23
|
+
from .data import ParseResult as ParseResult
|
|
24
|
+
from .data import Platform as Platform
|
|
25
|
+
from .data import VideoContent as VideoContent
|
|
26
|
+
|
|
18
27
|
__all__ = [
|
|
28
|
+
"AudioContent",
|
|
29
|
+
"Author",
|
|
30
|
+
"DynamicContent",
|
|
31
|
+
"GraphicsContent",
|
|
32
|
+
"ImageContent",
|
|
19
33
|
"ParseResult",
|
|
34
|
+
"Platform",
|
|
35
|
+
"VideoContent",
|
|
36
|
+
"handle",
|
|
20
37
|
]
|
|
@@ -119,8 +119,8 @@ class AcfunParser(BaseParser):
|
|
|
119
119
|
break
|
|
120
120
|
except HTTPError:
|
|
121
121
|
await safe_unlink(video_file)
|
|
122
|
-
logger.exception("
|
|
123
|
-
raise DownloadException("
|
|
122
|
+
logger.exception("视频下载失败")
|
|
123
|
+
raise DownloadException("视频下载失败")
|
|
124
124
|
return video_file
|
|
125
125
|
|
|
126
126
|
async def _parse_m3u8(self, m3u8_url: str):
|
|
@@ -5,7 +5,7 @@ from asyncio import Task
|
|
|
5
5
|
from collections.abc import Callable, Coroutine
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from re import Match, Pattern, compile
|
|
8
|
-
from typing import Any, ClassVar, TypeVar, cast
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, cast
|
|
9
9
|
from typing_extensions import Unpack
|
|
10
10
|
|
|
11
11
|
from ..config import pconfig as pconfig
|
|
@@ -16,6 +16,7 @@ from ..exception import DownloadException as DownloadException
|
|
|
16
16
|
from ..exception import DurationLimitException as DurationLimitException
|
|
17
17
|
from ..exception import ParseException as ParseException
|
|
18
18
|
from ..exception import SizeLimitException as SizeLimitException
|
|
19
|
+
from ..exception import TipException as TipException
|
|
19
20
|
from ..exception import ZeroSizeException as ZeroSizeException
|
|
20
21
|
from .data import ParseResult, ParseResultKwargs, Platform
|
|
21
22
|
|
|
@@ -55,8 +56,9 @@ class BaseParser:
|
|
|
55
56
|
platform: ClassVar[Platform]
|
|
56
57
|
""" 平台信息(包含名称和显示名称) """
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
if TYPE_CHECKING:
|
|
60
|
+
_key_patterns: ClassVar[KeyPatterns]
|
|
61
|
+
_handlers: ClassVar[dict[str, HandlerFunc]]
|
|
60
62
|
|
|
61
63
|
def __init__(self):
|
|
62
64
|
self.headers = COMMON_HEADER.copy()
|
|
@@ -106,7 +108,11 @@ class BaseParser:
|
|
|
106
108
|
"""
|
|
107
109
|
return await self._handlers[keyword](self, searched)
|
|
108
110
|
|
|
109
|
-
async def parse_with_redirect(
|
|
111
|
+
async def parse_with_redirect(
|
|
112
|
+
self,
|
|
113
|
+
url: str,
|
|
114
|
+
headers: dict[str, str] | None = None,
|
|
115
|
+
) -> ParseResult:
|
|
110
116
|
"""先重定向再解析"""
|
|
111
117
|
redirect_url = await self.get_redirect_url(url, headers=headers or self.headers)
|
|
112
118
|
|
|
@@ -124,7 +130,7 @@ class BaseParser:
|
|
|
124
130
|
continue
|
|
125
131
|
if searched := pattern.search(url):
|
|
126
132
|
return keyword, searched
|
|
127
|
-
raise
|
|
133
|
+
raise ParseException(f"无法匹配 {url}")
|
|
128
134
|
|
|
129
135
|
@classmethod
|
|
130
136
|
def result(cls, **kwargs: Unpack[ParseResultKwargs]) -> ParseResult:
|
|
@@ -136,7 +142,7 @@ class BaseParser:
|
|
|
136
142
|
url: str,
|
|
137
143
|
headers: dict[str, str] | None = None,
|
|
138
144
|
) -> str:
|
|
139
|
-
"""获取重定向后的URL"""
|
|
145
|
+
"""获取重定向后的 URL, 单次重定向"""
|
|
140
146
|
from httpx import AsyncClient
|
|
141
147
|
|
|
142
148
|
headers = headers or COMMON_HEADER.copy()
|
|
@@ -151,6 +157,26 @@ class BaseParser:
|
|
|
151
157
|
response.raise_for_status()
|
|
152
158
|
return response.headers.get("Location", url)
|
|
153
159
|
|
|
160
|
+
@staticmethod
|
|
161
|
+
async def get_final_url(
|
|
162
|
+
url: str,
|
|
163
|
+
headers: dict[str, str] | None = None,
|
|
164
|
+
) -> str:
|
|
165
|
+
"""获取重定向后的 URL, 允许多次重定向"""
|
|
166
|
+
from httpx import AsyncClient
|
|
167
|
+
|
|
168
|
+
headers = headers or COMMON_HEADER.copy()
|
|
169
|
+
async with AsyncClient(
|
|
170
|
+
headers=headers,
|
|
171
|
+
verify=False,
|
|
172
|
+
follow_redirects=True,
|
|
173
|
+
timeout=COMMON_TIMEOUT,
|
|
174
|
+
) as client:
|
|
175
|
+
response = await client.get(url)
|
|
176
|
+
if response.status_code >= 400:
|
|
177
|
+
response.raise_for_status()
|
|
178
|
+
return str(response.url)
|
|
179
|
+
|
|
154
180
|
def create_author(
|
|
155
181
|
self,
|
|
156
182
|
name: str,
|