nonebot-plugin-parser 2.4.1__tar.gz → 2.4.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.
Files changed (66) hide show
  1. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/PKG-INFO +1 -1
  2. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/pyproject.toml +2 -2
  3. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/base.py +12 -0
  4. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/twitter.py +82 -25
  5. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/README.md +0 -0
  6. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/__init__.py +0 -0
  7. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/config.py +0 -0
  8. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/constants.py +0 -0
  9. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/download/__init__.py +0 -0
  10. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/download/task.py +0 -0
  11. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
  12. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/exception.py +0 -0
  13. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/helper.py +0 -0
  14. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/matchers/__init__.py +0 -0
  15. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/matchers/filter.py +0 -0
  16. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/matchers/rule.py +0 -0
  17. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/__init__.py +0 -0
  18. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/acfun/__init__.py +0 -0
  19. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/acfun/video.py +0 -0
  20. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +0 -0
  21. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
  22. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
  23. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
  24. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
  25. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
  26. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
  27. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
  28. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
  29. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/data.py +0 -0
  30. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/douyin/__init__.py +0 -0
  31. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/douyin/slides.py +0 -0
  32. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/douyin/video.py +0 -0
  33. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/kuaishou/__init__.py +0 -0
  34. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/kuaishou/states.py +0 -0
  35. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/nga.py +0 -0
  36. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/tiktok.py +0 -0
  37. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/weibo/__init__.py +0 -0
  38. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/weibo/article.py +0 -0
  39. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/weibo/common.py +0 -0
  40. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/weibo/show.py +0 -0
  41. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/__init__.py +0 -0
  42. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/common.py +0 -0
  43. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/discovery.py +0 -0
  44. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/xiaohongshu/explore.py +0 -0
  45. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/youtube/__init__.py +0 -0
  46. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/parsers/youtube/meta.py +0 -0
  47. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/__init__.py +0 -0
  48. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/base.py +0 -0
  49. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/common.py +0 -0
  50. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/default.py +0 -0
  51. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/htmlrender.py +0 -0
  52. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW.ttf +0 -0
  53. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/avatar.png +0 -0
  54. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
  55. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
  56. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
  57. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/play.png +0 -0
  58. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
  59. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
  60. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
  61. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
  62. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
  63. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/templates/card.html.jinja +0 -0
  64. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/templates/weibo.html.jinja +0 -0
  65. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.2}/src/nonebot_plugin_parser/renders/weibo.py +0 -0
  66. {nonebot_plugin_parser-2.4.1 → nonebot_plugin_parser-2.4.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.4.1
3
+ Version: 2.4.2
4
4
  Summary: NoneBot2 链接分享解析 Alconna 版, 现支持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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nonebot-plugin-parser"
3
- version = "2.4.1"
3
+ version = "2.4.2"
4
4
  description = "NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -119,7 +119,7 @@ nonebug = { git = "https://github.com/nonebot/nonebug" }
119
119
  [tool.bumpversion]
120
120
  tag = true
121
121
  commit = true
122
- current_version = "2.4.1"
122
+ current_version = "2.4.2"
123
123
  message = "release: bump vesion from {current_version} to {new_version}"
124
124
 
125
125
  [[tool.bumpversion.files]]
@@ -199,6 +199,18 @@ class BaseParser:
199
199
  contents.append(ImageContent(task))
200
200
  return contents
201
201
 
202
+ def create_image_content(
203
+ self,
204
+ url_or_task: str | Task[Path],
205
+ ):
206
+ """创建图片内容"""
207
+ from .data import ImageContent
208
+
209
+ if isinstance(url_or_task, str):
210
+ url_or_task = DOWNLOADER.download_img(url_or_task, ext_headers=self.headers)
211
+
212
+ return ImageContent(url_or_task)
213
+
202
214
  def create_dynamic_contents(
203
215
  self,
204
216
  dynamic_urls: list[str],
@@ -3,31 +3,82 @@ from typing import Any, ClassVar
3
3
  from itertools import chain
4
4
 
5
5
  from httpx import AsyncClient
6
+ from msgspec import Struct, field
7
+ from msgspec.json import Decoder
6
8
 
7
9
  from .base import BaseParser, PlatformEnum, handle
8
- from .data import Platform, ParseResult
10
+ from .data import Platform, ParseResult, MediaContent
9
11
  from ..exception import ParseException
10
12
 
11
13
 
14
+ class MediaElement(Struct):
15
+ type: str
16
+ """媒体类型 video/image/gif"""
17
+ url: str
18
+ altText: str | None = None
19
+ thumbnail_url: str | None = None
20
+ duration_millis: int | None = None
21
+
22
+
23
+ class VxTwitterResponse(Struct):
24
+ article: str | None
25
+ date_epoch: int
26
+ fetched_on: int
27
+ likes: int
28
+ text: str
29
+ user_name: str
30
+ user_screen_name: str
31
+ user_profile_image_url: str
32
+ qrt: "VxTwitterResponse | None" = None
33
+ qrtURL: str | None = None
34
+ media_extended: list[MediaElement] = field(default_factory=list)
35
+
36
+
37
+ decoder = Decoder(VxTwitterResponse)
38
+
39
+
12
40
  class TwitterParser(BaseParser):
13
41
  platform: ClassVar[Platform] = Platform(name=PlatformEnum.TWITTER, display_name="小蓝鸟")
14
42
 
15
- async def _req_xdown_api(self, url: str) -> dict[str, Any]:
16
- headers = {
17
- "Accept": "application/json, text/plain, */*",
18
- "Content-Type": "application/x-www-form-urlencoded",
19
- "Origin": "https://xdown.app",
20
- "Referer": "https://xdown.app/",
21
- **self.headers,
22
- }
23
- data = {"q": url, "lang": "zh-cn"}
24
- async with AsyncClient(headers=headers, timeout=self.timeout) as client:
25
- url = "https://xdown.app/api/ajaxSearch"
26
- response = await client.post(url, data=data)
27
- return response.json()
28
-
29
43
  @handle("x.com", r"x.com/[0-9-a-zA-Z_]{1,20}/status/([0-9]+)")
30
44
  async def _parse(self, searched: re.Match[str]) -> ParseResult:
45
+ url = f"https://{searched.group(0)}"
46
+ return await self.parse_by_vxapi(url)
47
+
48
+ async def parse_by_vxapi(self, url: str):
49
+ """使用 vxtwitter API 解析 Twitter 链接"""
50
+
51
+ api_url = url.replace("x.com", "api.vxtwitter.com")
52
+ async with AsyncClient(headers=self.headers, timeout=self.timeout) as client:
53
+ response = await client.get(api_url)
54
+ response.raise_for_status()
55
+
56
+ data = decoder.decode(response.content)
57
+ return self._collect_result(data)
58
+
59
+ def _collect_result(self, data: VxTwitterResponse) -> ParseResult:
60
+ author = self.create_author(data.user_screen_name, data.user_profile_image_url)
61
+
62
+ contents: list[MediaContent] = []
63
+
64
+ for media in data.media_extended:
65
+ if media.type in ("video", "gif"):
66
+ contents.append(self.create_video_content(media.url, media.thumbnail_url))
67
+ elif media.type == "image":
68
+ contents.append(self.create_image_content(media.url))
69
+
70
+ repost = self._collect_result(data.qrt) if data.qrt else None
71
+
72
+ return self.result(
73
+ author=author,
74
+ title=data.article,
75
+ text=data.text,
76
+ timestamp=data.date_epoch,
77
+ contents=contents,
78
+ repost=repost,
79
+ )
80
+
81
+ async def _parse_old(self, searched: re.Match[str]) -> ParseResult:
31
82
  # 从匹配对象中获取原始URL
32
83
  url = f"https://{searched.group(0)}"
33
84
 
@@ -40,9 +91,23 @@ class TwitterParser(BaseParser):
40
91
  if html_content is None:
41
92
  raise ParseException("解析失败, 数据为空")
42
93
 
43
- return self.parse_twitter_html(html_content)
94
+ return self._parse_twitter_html(html_content)
95
+
96
+ async def _req_xdown_api(self, url: str) -> dict[str, Any]:
97
+ headers = {
98
+ "Accept": "application/json, text/plain, */*",
99
+ "Content-Type": "application/x-www-form-urlencoded",
100
+ "Origin": "https://xdown.app",
101
+ "Referer": "https://xdown.app/",
102
+ **self.headers,
103
+ }
104
+ data = {"q": url, "lang": "zh-cn"}
105
+ async with AsyncClient(headers=headers, timeout=self.timeout) as client:
106
+ url = "https://xdown.app/api/ajaxSearch"
107
+ response = await client.post(url, data=data)
108
+ return response.json()
44
109
 
45
- def parse_twitter_html(self, html_content: str) -> ParseResult:
110
+ def _parse_twitter_html(self, html_content: str) -> ParseResult:
46
111
  """解析 Twitter HTML 内容"""
47
112
  from bs4 import Tag, BeautifulSoup
48
113
 
@@ -106,11 +171,3 @@ class TwitterParser(BaseParser):
106
171
  author=self.create_author("无用户名"),
107
172
  contents=contents,
108
173
  )
109
- # # 4. 提取Twitter ID
110
- # twitter_id_input = soup.find("input", {"id": "TwitterId"})
111
- # if (
112
- # twitter_id_input
113
- # and isinstance(twitter_id_input, Tag)
114
- # and (value := twitter_id_input.get("value"))
115
- # and isinstance(value, str)
116
- # ):