mirror of
https://github.com/suyiiyii/nonebot-bison.git
synced 2025-06-02 09:26:12 +08:00
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
403 lines
10 KiB
Python
403 lines
10 KiB
Python
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
|
||
"""文件大小,KiB(1024)"""
|
||
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
|
||
"""文件大小,KiB(1024)"""
|
||
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)
|