Azide 74a3a0fab0
🐛 B站转发动态补充 DeletedItem 类型解析 (#659)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2024-12-12 11:24:42 +08:00

403 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from typing import Any, Literal, TypeVar, TypeAlias
from pydantic import BaseModel
from nonebot.compat import PYDANTIC_V2, ConfigDict
from nonebot_bison.compat import model_rebuild
TBaseModel = TypeVar("TBaseModel", bound=type[BaseModel])
# 不能当成装饰器用
# 当装饰器用时global namespace 中还没有被装饰的类,会报错
def model_rebuild_recurse(cls: TBaseModel) -> TBaseModel:
"""Recursively rebuild all BaseModel subclasses in the class."""
if not PYDANTIC_V2:
from inspect import isclass, getmembers
for _, sub_cls in getmembers(cls, lambda x: isclass(x) and issubclass(x, BaseModel)):
model_rebuild_recurse(sub_cls)
model_rebuild(cls)
return cls
class Base(BaseModel):
if PYDANTIC_V2:
model_config = ConfigDict(from_attributes=True)
else:
class Config:
orm_mode = True
class APIBase(Base):
"""Bilibili API返回的基础数据"""
code: int
message: str
class UserAPI(APIBase):
class Card(Base):
name: str
class Data(Base):
card: "UserAPI.Card"
data: Data | None = None
DynamicType = Literal[
"DYNAMIC_TYPE_ARTICLE",
"DYNAMIC_TYPE_AV",
"DYNAMIC_TYPE_WORD",
"DYNAMIC_TYPE_DRAW",
"DYNAMIC_TYPE_FORWARD",
"DYNAMIC_TYPE_LIVE",
"DYNAMIC_TYPE_LIVE_RCMD",
"DYNAMIC_TYPE_PGC",
"DYNAMIC_TYPE_PGC_UNION",
"DYNAMIC_TYPE_NONE", # 已删除的动态,一般只会出现在转发动态的源动态被删除
"DYNAMIC_TYPE_COMMON_SQUARE",
"DYNAMIC_TYPE_COMMON_VERTICAL",
"DYNAMIC_TYPE_COURSES_SEASON",
]
# 参考 https://github.com/Yun-Shan/bilibili-dynamic
class PostAPI(APIBase):
class Basic(Base):
rid_str: str
"""可能含义是referrer id表示引用的对象的ID
已知专栏动态时该ID与专栏ID一致视频动态时与av号一致
"""
class Modules(Base):
class Author(Base):
face: str
mid: int
name: str
jump_url: str
pub_ts: int
type: Literal["AUTHOR_TYPE_NORMAL", "AUTHOR_TYPE_PGC"]
"""作者类型一般情况下都是NORMAL番剧推送是PGC"""
class Additional(Base):
type: str
"""用户发视频时同步发布的动态带图片: ADDITIONAL_TYPE_UGC
显示相关游戏: ADDITIONAL_TYPE_COMMON
显示预约: ADDITIONAL_TYPE_RESERVE
显示投票: ADDITIONAL_TYPE_VOTE
显示包月充电专属抽奖: ADDITIONAL_TYPE_UPOWER_LOTTERY
显示赛事时(暂时只看到回放的,理论上直播时应该也是这个): ADDITIONAL_TYPE_MATCH
"""
class Desc(Base):
rich_text_nodes: list[dict[str, Any]]
"""描述的富文本节点,组成动态的各种内容
一个可能通用的结构:
```json
[
{
"jump_url": "//search.bilibili.com/all?keyword=鸣潮公测定档",
"orig_text": "#鸣潮公测定档#",
"text": "#鸣潮公测定档#",
"type": "RICH_TEXT_NODE_TYPE_TOPIC"
},
//...
]
```
"""
text: str
"""描述的纯文本内容"""
class Dynamic(Base):
additional: "PostAPI.Modules.Additional | None" = None
desc: "PostAPI.Modules.Desc | None" = None
"""动态描述,可能为空"""
major: "Major | None" = None
"""主要内容,可能为空"""
module_author: "PostAPI.Modules.Author"
module_dynamic: "PostAPI.Modules.Dynamic"
class Topic(Base):
id: int
name: str
jump_url: str
class Item(Base):
basic: "PostAPI.Basic"
id_str: str
modules: "PostAPI.Modules"
orig: "PostAPI.Item | PostAPI.DeletedItem | None" = None
topic: "PostAPI.Topic | None" = None
type: DynamicType
class DeletedItem(Base):
basic: "PostAPI.Basic"
id_str: None
modules: "PostAPI.Modules"
type: Literal["DYNAMIC_TYPE_NONE"]
def to_item(self) -> "PostAPI.Item":
return PostAPI.Item(
basic=self.basic,
id_str="",
modules=self.modules,
type=self.type,
)
class Data(Base):
items: "list[PostAPI.Item | PostAPI.DeletedItem] | None" = None
data: "PostAPI.Data | None" = None
class VideoMajor(Base):
class Archive(Base):
aid: str
bvid: str
title: str
desc: str
"""视频简介,太长的话会被截断"""
cover: str
jump_url: str
type: Literal["MAJOR_TYPE_ARCHIVE"]
archive: "VideoMajor.Archive"
class LiveRecommendMajor(Base):
class LiveRecommand(Base):
content: str
"""直播卡片的内容值为JSON文本可以解析为LiveRecommendMajor.Content"""
class Content(Base):
type: int
"""直播类型"""
live_play_info: "LiveRecommendMajor.LivePlayInfo"
class LivePlayInfo(Base):
uid: int
"""用户UID不是直播间号"""
room_type: int
"""房间类型"""
room_paid_type: int
"""付费类型"""
play_type: int
"""播放类型?"""
live_status: int
"""直播状态"""
live_screen_type: int
"""直播画面类型?"""
room_id: int
"""直播间号"""
cover: str
"""直播封面"""
title: str
online: int
"""开播时长?"""
parent_area_id: int
"""主分区ID"""
parent_area_name: str
"""主分区名称"""
area_id: int
"""分区ID"""
area_name: str
"""分区名称"""
live_start_time: int
"""开播时间戳"""
link: str
"""跳转链接,相对协议(即//开头而不是https://开头)"""
live_id: str
"""直播ID不知道有什么用"""
watched_show: "LiveRecommendMajor.WatchedShow"
class WatchedShow(Base):
num: int
"""观看人数"""
text_small: str
"""观看人数的文本描述: 例如 1.2万"""
text_large: str
"""观看人数的文本描述: 例如 1.2万人看过"""
switch: bool
"""未知"""
icon: str
"""观看文本前的图标"""
icon_web: str
"""观看文本前的图标(网页版)"""
icon_location: str
"""图标位置?"""
type: Literal["MAJOR_TYPE_LIVE_RCMD"]
live_rcmd: "LiveRecommendMajor.LiveRecommand"
class LiveMajor(Base):
class Live(Base):
id: int
"""直播间号"""
title: str
live_state: int
"""直播状态1为直播中0为未开播"""
cover: str
desc_first: str
"""直播信息的第一部分,用来显示分区"""
desc_second: str
jump_url: str
"""跳转链接,目前用的是相对协议(即//开头而不是https://开头)"""
type: Literal["MAJOR_TYPE_LIVE"]
live: "LiveMajor.Live"
class ArticleMajor(Base):
class Article(Base):
id: int
"""专栏CID"""
title: str
desc: str
"""专栏简介"""
covers: list[str]
"""专栏封面,一般是一张图片"""
jump_url: str
type: Literal["MAJOR_TYPE_ARTICLE"]
article: "ArticleMajor.Article"
class DrawMajor(Base):
class Item(Base):
width: int
height: int
size: float
"""文件大小KiB1024"""
src: str
"""图片链接"""
class Draw(Base):
id: int
items: "list[DrawMajor.Item]"
type: Literal["MAJOR_TYPE_DRAW"]
draw: "DrawMajor.Draw"
class PGCMajor(Base):
"""番剧推送"""
class PGC(Base):
title: str
cover: str
jump_url: str
"""通常https://www.bilibili.com/bangumi/play/ep{epid}"""
epid: int
season_id: int
type: Literal["MAJOR_TYPE_PGC"]
pgc: "PGCMajor.PGC"
class OPUSMajor(Base):
"""通用图文内容"""
class Summary(Base):
rich_text_nodes: list[dict[str, Any]]
"""描述的富文本节点,组成动态的各种内容"""
text: str
class Pic(Base):
width: int
height: int
size: int
"""文件大小KiB1024"""
url: str
"""图片链接"""
class Opus(Base):
jump_url: str
title: str
summary: "OPUSMajor.Summary"
pics: "list[OPUSMajor.Pic]"
type: Literal["MAJOR_TYPE_OPUS"]
opus: "OPUSMajor.Opus"
class CommonMajor(Base):
"""还是通用图文内容
主要跟特殊官方功能有关系,例如专属活动页、会员购、漫画、赛事中心、游戏中心、小黑屋、工房集市、装扮等
"""
class Common(Base):
cover: str
"""卡片左侧图片的URL"""
title: str
desc: str
"""内容"""
jump_url: str
type: Literal["MAJOR_TYPE_COMMON"]
common: "CommonMajor.Common"
class CoursesMajor(Base):
"""课程推送"""
class Courses(Base):
title: str
sub_title: str
"""副标题,一般是课程的简介"""
desc: str
"""课时信息"""
cover: str
jump_url: str
id: int
"""课程ID"""
type: Literal["MAJOR_TYPE_COURSES"]
courses: "CoursesMajor.Courses"
class DeletedMajor(Base):
class None_(Base):
tips: str
type: Literal["MAJOR_TYPE_NONE"]
none: "DeletedMajor.None_"
class UnknownMajor(Base):
type: str
Major = (
VideoMajor
| LiveRecommendMajor
| LiveMajor
| ArticleMajor
| DrawMajor
| PGCMajor
| OPUSMajor
| CommonMajor
| CoursesMajor
| DeletedMajor
| UnknownMajor
)
DynRawPost: TypeAlias = PostAPI.Item
model_rebuild_recurse(VideoMajor)
model_rebuild_recurse(LiveRecommendMajor)
model_rebuild_recurse(LiveMajor)
model_rebuild_recurse(ArticleMajor)
model_rebuild_recurse(DrawMajor)
model_rebuild_recurse(PGCMajor)
model_rebuild_recurse(OPUSMajor)
model_rebuild_recurse(CommonMajor)
model_rebuild_recurse(CoursesMajor)
model_rebuild_recurse(UserAPI)
model_rebuild_recurse(PostAPI)