nonebot-plugin-parser 2.4.2__tar.gz → 2.4.3__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.2 → nonebot_plugin_parser-2.4.3}/PKG-INFO +4 -4
  2. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/pyproject.toml +8 -8
  3. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/matchers/rule.py +9 -8
  4. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/douyin/slides.py +3 -2
  5. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/douyin/video.py +3 -2
  6. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/kuaishou/states.py +3 -2
  7. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/nga.py +24 -12
  8. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/twitter.py +16 -7
  9. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/weibo/article.py +3 -2
  10. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/weibo/common.py +3 -2
  11. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/youtube/meta.py +3 -2
  12. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/README.md +0 -0
  13. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/__init__.py +0 -0
  14. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/config.py +0 -0
  15. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/constants.py +0 -0
  16. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/download/__init__.py +0 -0
  17. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/download/task.py +0 -0
  18. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/download/ytdlp.py +0 -0
  19. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/exception.py +0 -0
  20. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/helper.py +0 -0
  21. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/matchers/__init__.py +0 -0
  22. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/matchers/filter.py +0 -0
  23. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/__init__.py +0 -0
  24. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/acfun/__init__.py +0 -0
  25. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/acfun/video.py +0 -0
  26. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/base.py +0 -0
  27. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/__init__.py +0 -0
  28. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/article.py +0 -0
  29. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/common.py +0 -0
  30. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/dynamic.py +0 -0
  31. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/favlist.py +0 -0
  32. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/live.py +0 -0
  33. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/opus.py +0 -0
  34. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/bilibili/video.py +0 -0
  35. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/cookie.py +0 -0
  36. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/data.py +0 -0
  37. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/douyin/__init__.py +0 -0
  38. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/kuaishou/__init__.py +0 -0
  39. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/tiktok.py +0 -0
  40. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/weibo/__init__.py +0 -0
  41. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/weibo/show.py +0 -0
  42. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/xiaohongshu/__init__.py +0 -0
  43. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/xiaohongshu/common.py +0 -0
  44. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/xiaohongshu/discovery.py +0 -0
  45. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/xiaohongshu/explore.py +0 -0
  46. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/parsers/youtube/__init__.py +0 -0
  47. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/__init__.py +0 -0
  48. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/base.py +0 -0
  49. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/common.py +0 -0
  50. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/default.py +0 -0
  51. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/htmlrender.py +0 -0
  52. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/HYSongYunLangHeiW.ttf +0 -0
  53. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/avatar.png +0 -0
  54. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/bilibili.png +0 -0
  55. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/douyin.png +0 -0
  56. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/kuaishou.png +0 -0
  57. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/play.png +0 -0
  58. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/tiktok.png +0 -0
  59. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/twitter.png +0 -0
  60. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/weibo.png +0 -0
  61. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/xiaohongshu.png +0 -0
  62. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/resources/youtube.png +0 -0
  63. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/templates/card.html.jinja +0 -0
  64. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/templates/weibo.html.jinja +0 -0
  65. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/src/nonebot_plugin_parser/renders/weibo.py +0 -0
  66. {nonebot_plugin_parser-2.4.2 → nonebot_plugin_parser-2.4.3}/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.2
3
+ Version: 2.4.3
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
@@ -25,7 +25,7 @@ Requires-Dist: pillow>=11.0.0
25
25
  Requires-Dist: aiofiles>=25.1.0
26
26
  Requires-Dist: httpx>=0.27.2,<1.0.0
27
27
  Requires-Dist: msgspec>=0.20.0,<1.0.0
28
- Requires-Dist: apilmoji[rich]>=0.3.0,<1.0.0
28
+ Requires-Dist: apilmoji[rich]>=0.3.1,<1.0.0
29
29
  Requires-Dist: beautifulsoup4>=4.12.0,<5.0.0
30
30
  Requires-Dist: curl-cffi>=0.13.0,!=0.14.0,<1.0.0
31
31
  Requires-Dist: bilibili-api-python>=17.4.1,<18.0.0
@@ -35,12 +35,12 @@ Requires-Dist: nonebot-plugin-localstore>=0.7.4,<1.0.0
35
35
  Requires-Dist: nonebot-plugin-uninfo>=0.10.1,<1.0.0
36
36
  Requires-Dist: nonebot-plugin-htmlkit>=0.1.0rc4 ; extra == 'all'
37
37
  Requires-Dist: nonebot-plugin-htmlrender>=0.6.7 ; extra == 'all'
38
- Requires-Dist: yt-dlp[default]>=2026.2.4 ; extra == 'all'
38
+ Requires-Dist: yt-dlp[default]>=2026.2.21 ; extra == 'all'
39
39
  Requires-Dist: emosvg>=0.1.6 ; extra == 'all'
40
40
  Requires-Dist: emosvg>=0.1.6 ; extra == 'emosvg'
41
41
  Requires-Dist: nonebot-plugin-htmlkit>=0.1.0rc4 ; extra == 'htmlkit'
42
42
  Requires-Dist: nonebot-plugin-htmlrender>=0.6.7 ; extra == 'htmlrender'
43
- Requires-Dist: yt-dlp[default]>=2025.2.4 ; extra == 'ytdlp'
43
+ Requires-Dist: yt-dlp[default]>=2025.2.21 ; extra == 'ytdlp'
44
44
  Requires-Python: >=3.10
45
45
  Project-URL: IssueTracker, https://github.com/fllesser/nonebot-plugin-parser/issues
46
46
  Project-URL: Release, https://github.com/fllesser/nonebot-plugin-parser/releases
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nonebot-plugin-parser"
3
- version = "2.4.2"
3
+ version = "2.4.3"
4
4
  description = "NoneBot2 链接分享解析 Alconna 版, 现支持B站|抖音|快手|微博|小红书|YouTube|TikTok|Twitter|AcFun|NGA"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -45,7 +45,7 @@ dependencies = [
45
45
  "aiofiles>=25.1.0",
46
46
  "httpx>=0.27.2,<1.0.0",
47
47
  "msgspec>=0.20.0,<1.0.0",
48
- "apilmoji[rich]>=0.3.0,<1.0.0",
48
+ "apilmoji[rich]>=0.3.1,<1.0.0",
49
49
  "beautifulsoup4>=4.12.0,<5.0.0",
50
50
  "curl_cffi>=0.13.0,<1.0.0,!=0.14.0",
51
51
  "bilibili-api-python>=17.4.1,<18.0.0",
@@ -64,12 +64,12 @@ Repository = "https://github.com/fllesser/nonebot-plugin-parser"
64
64
  [project.optional-dependencies]
65
65
  htmlkit = ["nonebot-plugin-htmlkit>=0.1.0rc4"]
66
66
  htmlrender = ["nonebot-plugin-htmlrender>=0.6.7"]
67
- ytdlp = ["yt-dlp[default]>=2025.2.4"]
67
+ ytdlp = ["yt-dlp[default]>=2025.2.21"]
68
68
  emosvg = ["emosvg>=0.1.6"]
69
69
  all = [
70
70
  "nonebot-plugin-htmlkit>=0.1.0rc4",
71
71
  "nonebot-plugin-htmlrender>=0.6.7",
72
- "yt-dlp[default]>=2026.2.4",
72
+ "yt-dlp[default]>=2026.2.21",
73
73
  "emosvg>=0.1.6",
74
74
  ]
75
75
 
@@ -85,12 +85,12 @@ extras = [
85
85
  "emosvg>=0.1.6",
86
86
  "nonebot-plugin-htmlkit>=0.1.0rc4",
87
87
  "nonebot-plugin-htmlrender>=0.6.7",
88
- "yt-dlp[default]>=2026.2.4",
89
- "types-yt-dlp>=2026.2.4.20260206",
88
+ "yt-dlp[default]>=2026.2.21",
89
+ "types-yt-dlp>=2026.2.21.20260223",
90
90
  ]
91
91
  test = [
92
92
  "nonebug>=0.4.3,<1.0.0",
93
- "poethepoet>=0.40.0",
93
+ "poethepoet>=0.42.0",
94
94
  "pytest-asyncio>=1.3.0,<1.4.0",
95
95
  "pytest-cov>=7.0.0",
96
96
  "pytest-xdist>=3.8.0,<4.0.0",
@@ -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.2"
122
+ current_version = "2.4.3"
123
123
  message = "release: bump vesion from {current_version} to {new_version}"
124
124
 
125
125
  [[tool.bumpversion.files]]
@@ -1,8 +1,9 @@
1
1
  import re
2
2
  from typing import Literal
3
3
 
4
- import msgspec
4
+ from msgspec import Struct, DecodeError
5
5
  from nonebot import logger
6
+ from msgspec.json import Decoder
6
7
  from nonebot.rule import Rule
7
8
  from nonebot.params import Depends
8
9
  from nonebot.typing import T_State
@@ -20,29 +21,29 @@ PSR_SEARCHED_KEY: Literal["psr-searched"] = "psr-searched"
20
21
 
21
22
 
22
23
  # 定义 JSON 卡片的数据结构
23
- class MetaDetail(msgspec.Struct):
24
+ class MetaDetail(Struct):
24
25
  qqdocurl: str | None = None
25
26
 
26
27
 
27
- class MetaNews(msgspec.Struct):
28
+ class MetaNews(Struct):
28
29
  jumpUrl: str | None = None
29
30
 
30
31
 
31
- class MetaMusic(msgspec.Struct):
32
+ class MetaMusic(Struct):
32
33
  jumpUrl: str | None = None
33
34
 
34
35
 
35
- class Meta(msgspec.Struct):
36
+ class Meta(Struct):
36
37
  detail_1: MetaDetail | None = None
37
38
  news: MetaNews | None = None
38
39
  music: MetaMusic | None = None
39
40
 
40
41
 
41
- class RawData(msgspec.Struct):
42
+ class RawData(Struct):
42
43
  meta: Meta | None = None
43
44
 
44
45
 
45
- raw_decoder = msgspec.json.Decoder(RawData)
46
+ raw_decoder = Decoder(RawData)
46
47
 
47
48
 
48
49
  class SearchResult:
@@ -81,7 +82,7 @@ def _extract_url(hyper: Hyper) -> str | None:
81
82
 
82
83
  try:
83
84
  raw = raw_decoder.decode(raw_str)
84
- except msgspec.DecodeError:
85
+ except DecodeError:
85
86
  logger.exception(f"json 卡片解析失败: {raw_str}")
86
87
  return None
87
88
 
@@ -1,6 +1,7 @@
1
1
  from random import choice
2
2
 
3
- from msgspec import Struct, json, field
3
+ from msgspec import Struct, field
4
+ from msgspec.json import Decoder
4
5
 
5
6
 
6
7
  class PlayAddr(Struct):
@@ -59,4 +60,4 @@ class SlidesInfo(Struct):
59
60
  aweme_details: list[SlidesData] = field(default_factory=list)
60
61
 
61
62
 
62
- decoder = json.Decoder(SlidesInfo)
63
+ decoder = Decoder(SlidesInfo)
@@ -1,7 +1,8 @@
1
1
  from random import choice
2
2
  from typing import Any
3
3
 
4
- from msgspec import Struct, json, field
4
+ from msgspec import Struct, field
5
+ from msgspec.json import Decoder
5
6
 
6
7
  from ..base import ParseException
7
8
 
@@ -95,4 +96,4 @@ class RouterData(Struct):
95
96
  raise ParseException("can't find video_(id)/page or note_(id)/page in router data")
96
97
 
97
98
 
98
- decoder = json.Decoder(RouterData)
99
+ decoder = Decoder(RouterData)
@@ -1,6 +1,7 @@
1
1
  from random import choice
2
2
 
3
- from msgspec import Struct, json, field
3
+ from msgspec import Struct, field
4
+ from msgspec.json import Decoder
4
5
 
5
6
 
6
7
  class CdnUrl(Struct):
@@ -59,4 +60,4 @@ class TusjohData(Struct):
59
60
  photo: Photo | None = None
60
61
 
61
62
 
62
- decoder = json.Decoder(dict[str, TusjohData])
63
+ decoder = Decoder(dict[str, TusjohData])
@@ -27,12 +27,15 @@ class NGAParser(BaseParser):
27
27
  "Upgrade-Insecure-Requests": "1",
28
28
  }
29
29
  self.headers.update(extra_headers)
30
- self.base_img_url = "https://img.nga.178.com/attachments"
31
30
 
32
31
  @staticmethod
33
32
  def build_url_by_tid(tid: str | int) -> str:
34
33
  return f"https://nga.178.com/read.php?tid={tid}"
35
34
 
35
+ @staticmethod
36
+ def build_img_url(path: str) -> str:
37
+ return "https://img.nga.178.com/attachments" + path
38
+
36
39
  # ("ngabbs.com", r"https?://ngabbs\.com/read\.php\?tid=(?P<tid>\d+)(?:[&#A-Za-z\d=_-]+)?"),
37
40
  # ("nga.178.com", r"https?://nga\.178\.com/read\.php\?tid=(?P<tid>\d+)(?:[&#A-Za-z\d=_-]+)?"),
38
41
  # ("bbs.nga.cn", r"https?://bbs\.nga\.cn/read\.php\?tid=(?P<tid>\d+)(?:[&#A-Za-z\d=_-]+)?"),
@@ -117,20 +120,29 @@ class NGAParser(BaseParser):
117
120
  if content_tag and isinstance(content_tag, Tag):
118
121
  text = content_tag.get_text("\n", strip=True)
119
122
  lines = text.split("\n")
120
- temp_text = ""
123
+ text_buffer: list[str] = []
124
+
121
125
  for line in lines:
122
- if line.startswith("[img]"):
126
+ if "[" in line:
123
127
  # [img]./mon_202602/10/-lmuf1Q1aw-hzwpZ2dT3cSl4-bs.webp[/img]
124
- img_url = self.base_img_url + line[6:-6]
125
- contents.append(self.create_graphics_content(img_url, text=temp_text))
126
- temp_text = ""
127
- # 去除其他标签, 仅保留文本
128
- elif "[" in line:
129
- if clean_line := re.sub(r"\[[^\]]*?\]", "", line).strip():
130
- temp_text += clean_line + "\n"
128
+ if paths := re.findall(r"\[img\]\.(.*?)\[\/img\]", line):
129
+ for path in paths:
130
+ contents.append(
131
+ self.create_graphics_content(
132
+ self.build_img_url(path),
133
+ text="\n".join(text_buffer).strip(),
134
+ )
135
+ )
136
+ text_buffer.clear()
137
+ else:
138
+ # 去除其他标签, 仅保留文本
139
+ if clean_line := re.sub(r"\[[^\]]*?\]", "", line).strip():
140
+ text_buffer.append(clean_line)
131
141
  else:
132
- temp_text += line + "\n"
133
- text = temp_text.strip()
142
+ text_buffer.append(line)
143
+
144
+ # 处理剩余的文本
145
+ text = "\n".join(text_buffer).strip()
134
146
 
135
147
  return self.result(
136
148
  title=title,
@@ -1,5 +1,5 @@
1
1
  import re
2
- from typing import Any, ClassVar
2
+ from typing import Any, Literal, ClassVar
3
3
  from itertools import chain
4
4
 
5
5
  from httpx import AsyncClient
@@ -12,16 +12,21 @@ from ..exception import ParseException
12
12
 
13
13
 
14
14
  class MediaElement(Struct):
15
- type: str
16
- """媒体类型 video/image/gif"""
15
+ type: Literal["video", "image", "gif"]
17
16
  url: str
18
17
  altText: str | None = None
19
18
  thumbnail_url: str | None = None
20
19
  duration_millis: int | None = None
21
20
 
22
21
 
22
+ class Article(Struct):
23
+ image: str | None = None
24
+ preview_text: str | None = None
25
+ title: str | None = None
26
+
27
+
23
28
  class VxTwitterResponse(Struct):
24
- article: str | None
29
+ article: str | Article | None
25
30
  date_epoch: int
26
31
  fetched_on: int
27
32
  likes: int
@@ -33,6 +38,10 @@ class VxTwitterResponse(Struct):
33
38
  qrtURL: str | None = None
34
39
  media_extended: list[MediaElement] = field(default_factory=list)
35
40
 
41
+ @property
42
+ def name(self) -> str:
43
+ return f"{self.user_name} @{self.user_screen_name}"
44
+
36
45
 
37
46
  decoder = Decoder(VxTwitterResponse)
38
47
 
@@ -57,10 +66,10 @@ class TwitterParser(BaseParser):
57
66
  return self._collect_result(data)
58
67
 
59
68
  def _collect_result(self, data: VxTwitterResponse) -> ParseResult:
60
- author = self.create_author(data.user_screen_name, data.user_profile_image_url)
69
+ author = self.create_author(data.user_name, data.user_profile_image_url)
70
+ title = data.article.title if isinstance(data.article, Article) else data.article
61
71
 
62
72
  contents: list[MediaContent] = []
63
-
64
73
  for media in data.media_extended:
65
74
  if media.type in ("video", "gif"):
66
75
  contents.append(self.create_video_content(media.url, media.thumbnail_url))
@@ -71,7 +80,7 @@ class TwitterParser(BaseParser):
71
80
 
72
81
  return self.result(
73
82
  author=author,
74
- title=data.article,
83
+ title=title,
75
84
  text=data.text,
76
85
  timestamp=data.date_epoch,
77
86
  contents=contents,
@@ -1,4 +1,5 @@
1
- from msgspec import Struct, json
1
+ from msgspec import Struct
2
+ from msgspec.json import Decoder
2
3
 
3
4
 
4
5
  class UserInfo(Struct):
@@ -20,4 +21,4 @@ class Detail(Struct):
20
21
  data: Data
21
22
 
22
23
 
23
- decoder = json.Decoder(Detail)
24
+ decoder = Decoder(Detail)
@@ -1,6 +1,7 @@
1
1
  from re import sub
2
2
 
3
- from msgspec import Struct, json
3
+ from msgspec import Struct
4
+ from msgspec.json import Decoder
4
5
 
5
6
 
6
7
  class LargeInPic(Struct):
@@ -107,4 +108,4 @@ class WeiboResponse(Struct):
107
108
  data: WeiboData
108
109
 
109
110
 
110
- decoder = json.Decoder(WeiboResponse)
111
+ decoder = Decoder(WeiboResponse)
@@ -1,4 +1,5 @@
1
- from msgspec import Struct, json
1
+ from msgspec import Struct
2
+ from msgspec.json import Decoder
2
3
 
3
4
 
4
5
  class Thumbnail(Struct):
@@ -40,4 +41,4 @@ class BrowseResponse(Struct):
40
41
  return self.metadata.channelMetadataRenderer.description
41
42
 
42
43
 
43
- decoder = json.Decoder(BrowseResponse)
44
+ decoder = Decoder(BrowseResponse)