butterbot-python 3.1.0.dev1__py3-none-any.whl
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.
- butterbot/__init__.py +15 -0
- butterbot/app/__init__.py +41 -0
- butterbot/app/bot_app.py +374 -0
- butterbot/app/config.py +156 -0
- butterbot/app/source_manager.py +367 -0
- butterbot/core/__init__.py +46 -0
- butterbot/core/api/__init__.py +9 -0
- butterbot/core/api/base_api.py +32 -0
- butterbot/core/context/README.md +1 -0
- butterbot/core/context/__init__.py +9 -0
- butterbot/core/context/api_registry.py +109 -0
- butterbot/core/context/app_context.py +51 -0
- butterbot/core/context/config_provider.py +14 -0
- butterbot/core/data/__init__.py +15 -0
- butterbot/core/data/base_data.py +29 -0
- butterbot/core/data/base_model.py +197 -0
- butterbot/core/event/README.md +1 -0
- butterbot/core/event/__init__.py +10 -0
- butterbot/core/event/event.py +27 -0
- butterbot/core/event/event_bus.py +276 -0
- butterbot/core/event/subscriber.py +130 -0
- butterbot/core/exceptions.py +90 -0
- butterbot/core/filter/__init__.py +11 -0
- butterbot/core/filter/base_filter.py +74 -0
- butterbot/core/source/README.md +1 -0
- butterbot/core/source/__init__.py +9 -0
- butterbot/core/source/base_source.py +134 -0
- butterbot/core/types/__init__.py +9 -0
- butterbot/core/types/base_type.py +74 -0
- butterbot/sources/__init__.py +1 -0
- butterbot/sources/bilibili/README.md +10 -0
- butterbot/sources/bilibili/__init__.py +20 -0
- butterbot/sources/bilibili/api/__init__.py +5 -0
- butterbot/sources/bilibili/api/bili_api.py +123 -0
- butterbot/sources/bilibili/data/__init__.py +52 -0
- butterbot/sources/bilibili/data/danmaku_gift_data.py +135 -0
- butterbot/sources/bilibili/data/danmaku_guard_data.py +54 -0
- butterbot/sources/bilibili/data/danmaku_msg_data.py +71 -0
- butterbot/sources/bilibili/data/dto/__init__.py +59 -0
- butterbot/sources/bilibili/data/dto/danmaku_gift_dto.py +193 -0
- butterbot/sources/bilibili/data/dto/danmaku_guard_buy_dto.py +54 -0
- butterbot/sources/bilibili/data/dto/danmaku_msg_dto.py +123 -0
- butterbot/sources/bilibili/data/dto/dynamic_dto.py +276 -0
- butterbot/sources/bilibili/data/dto/live_room_dto.py +169 -0
- butterbot/sources/bilibili/data/dto/video_part_dto.py +18 -0
- butterbot/sources/bilibili/data/dynamic_data.py +362 -0
- butterbot/sources/bilibili/data/live_room_data.py +162 -0
- butterbot/sources/bilibili/data/video_part.py +46 -0
- butterbot/sources/bilibili/source/__init__.py +15 -0
- butterbot/sources/bilibili/source/base_polling_source.py +130 -0
- butterbot/sources/bilibili/source/bili_danmaku_source.py +230 -0
- butterbot/sources/bilibili/source/bili_dynamic_source.py +135 -0
- butterbot/sources/bilibili/source/bili_live_source.py +137 -0
- butterbot/sources/bilibili/types/__init__.py +11 -0
- butterbot/sources/bilibili/types/bili_type.py +32 -0
- butterbot/sources/napcat/README.md +10 -0
- butterbot/sources/napcat/__init__.py +20 -0
- butterbot/sources/napcat/api/__init__.py +3 -0
- butterbot/sources/napcat/api/napcat_api.py +316 -0
- butterbot/sources/napcat/data/__init__.py +179 -0
- butterbot/sources/napcat/data/event_data.py +441 -0
- butterbot/sources/napcat/data/segment_data.py +432 -0
- butterbot/sources/napcat/events.py +91 -0
- butterbot/sources/napcat/filters/__init__.py +19 -0
- butterbot/sources/napcat/filters/filters.py +202 -0
- butterbot/sources/napcat/source/__init__.py +5 -0
- butterbot/sources/napcat/source/napcat_source.py +58 -0
- butterbot/sources/napcat/types/__init__.py +5 -0
- butterbot/sources/napcat/types/napcat_type.py +61 -0
- butterbot/utils/README.md +15 -0
- butterbot/utils/__init__.py +11 -0
- butterbot/utils/data_pair.py +27 -0
- butterbot/utils/logging_config.py +521 -0
- butterbot/utils/terminal.py +308 -0
- butterbot/utils/websocket.py +1270 -0
- butterbot_python-3.1.0.dev1.dist-info/METADATA +769 -0
- butterbot_python-3.1.0.dev1.dist-info/RECORD +79 -0
- butterbot_python-3.1.0.dev1.dist-info/WHEEL +4 -0
- butterbot_python-3.1.0.dev1.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from butterbot.core.data import BaseDataMixin
|
|
5
|
+
|
|
6
|
+
from .dto import (
|
|
7
|
+
ArticleDto,
|
|
8
|
+
AuthorDto,
|
|
9
|
+
DynamicDTO,
|
|
10
|
+
LiveRcmdDto,
|
|
11
|
+
MusicDto,
|
|
12
|
+
StatDto,
|
|
13
|
+
VideoDto,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_max_id(date: dict) -> int | None:
|
|
18
|
+
"""获取最新动态的索引ID
|
|
19
|
+
Args:
|
|
20
|
+
date (dict): 原始动态数据
|
|
21
|
+
Returns:
|
|
22
|
+
max_id (int): 索引id,如果数据不存在则返回None
|
|
23
|
+
|
|
24
|
+
"""
|
|
25
|
+
# 数据校验:检查 items 键是否存在
|
|
26
|
+
if not date or "items" not in date:
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
list_data = date["items"]
|
|
30
|
+
|
|
31
|
+
# 数据校验:检查 items 是否为空
|
|
32
|
+
if not list_data:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
max_timestamp: int = 0
|
|
36
|
+
max_id: int | None = None
|
|
37
|
+
|
|
38
|
+
for index, item in enumerate(list_data):
|
|
39
|
+
# 数据校验:检查 item 是否为字典
|
|
40
|
+
if not isinstance(item, dict):
|
|
41
|
+
continue
|
|
42
|
+
|
|
43
|
+
# 数据校验:检查嵌套的 key 是否存在
|
|
44
|
+
modules = item.get("modules")
|
|
45
|
+
if not modules or not isinstance(modules, dict):
|
|
46
|
+
continue
|
|
47
|
+
|
|
48
|
+
module_author = modules.get("module_author")
|
|
49
|
+
if not module_author or not isinstance(module_author, dict):
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
pub_ts = module_author.get("pub_ts")
|
|
53
|
+
if pub_ts is None:
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
timestamp = int(pub_ts)
|
|
58
|
+
if timestamp > max_timestamp:
|
|
59
|
+
max_timestamp = timestamp
|
|
60
|
+
max_id = index
|
|
61
|
+
except (ValueError, TypeError):
|
|
62
|
+
# 如果转换失败,跳过该项
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
return max_id
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True)
|
|
69
|
+
class AuthorData(BaseDataMixin):
|
|
70
|
+
"""
|
|
71
|
+
作者信息数据
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
uid: int # UP主UID
|
|
75
|
+
name: str # UP主昵称
|
|
76
|
+
face: str # UP主头像URL
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_dto(cls, author: AuthorDto) -> "AuthorData":
|
|
80
|
+
"""从AuthorDto构造AuthorData实例"""
|
|
81
|
+
return cls(
|
|
82
|
+
uid=author.uid,
|
|
83
|
+
name=author.name,
|
|
84
|
+
face=author.face,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def jump_url(self) -> str:
|
|
89
|
+
"""作者空间跳转链接"""
|
|
90
|
+
return "https://space.bilibili.com/%s" % self.uid
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass(frozen=True)
|
|
94
|
+
class StatData(BaseDataMixin):
|
|
95
|
+
"""
|
|
96
|
+
动态统计信息数据
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
comment_count: int # 评论数
|
|
100
|
+
like_count: int # 点赞数
|
|
101
|
+
forward_count: int # 转发数
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_dto(cls, stat: StatDto) -> "StatData":
|
|
105
|
+
"""从StatDto构造StatData实例"""
|
|
106
|
+
return cls(
|
|
107
|
+
comment_count=stat.comment_count,
|
|
108
|
+
like_count=stat.like_count,
|
|
109
|
+
forward_count=stat.forward_count,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass(frozen=True)
|
|
114
|
+
class VideoData(BaseDataMixin):
|
|
115
|
+
"""
|
|
116
|
+
视频信息数据
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
av_id: str # 视频AV号
|
|
120
|
+
bv_id: str # 视频BV号
|
|
121
|
+
title: str # 视频标题
|
|
122
|
+
cover: str # 视频封面
|
|
123
|
+
desc: str # 视频简介
|
|
124
|
+
duration_text: str # 视频时长
|
|
125
|
+
dynamic_text: str # 动态文本
|
|
126
|
+
play_count: str # 播放数
|
|
127
|
+
danmaku_count: str # 弹幕数
|
|
128
|
+
|
|
129
|
+
@classmethod
|
|
130
|
+
def from_dto(cls, video: VideoDto) -> "VideoData":
|
|
131
|
+
"""从VideoDto构造VideoData实例"""
|
|
132
|
+
return cls(
|
|
133
|
+
av_id=video.av_id,
|
|
134
|
+
bv_id=video.bv_id,
|
|
135
|
+
title=video.title,
|
|
136
|
+
cover=video.cover,
|
|
137
|
+
desc=video.desc,
|
|
138
|
+
duration_text=video.duration_text,
|
|
139
|
+
dynamic_text=video.dynamic_text,
|
|
140
|
+
play_count=video.play_count,
|
|
141
|
+
danmaku_count=video.danmaku_count,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def jump_url(self) -> str:
|
|
146
|
+
"""视频跳转链接"""
|
|
147
|
+
return "https://www.bilibili.com/video/%s/" % self.bv_id
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass(frozen=True)
|
|
151
|
+
class MusicData(BaseDataMixin):
|
|
152
|
+
"""
|
|
153
|
+
音乐信息数据
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
music_id: str # 音乐ID
|
|
157
|
+
title: str # 音乐标题
|
|
158
|
+
cover: str # 音乐封面
|
|
159
|
+
label: str # 音乐标签(作者/歌手)
|
|
160
|
+
dynamic_text: str # 动态文本
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
def from_dto(cls, music: MusicDto) -> "MusicData":
|
|
164
|
+
"""从MusicDto构造MusicData实例"""
|
|
165
|
+
return cls(
|
|
166
|
+
music_id=music.music_id,
|
|
167
|
+
title=music.title,
|
|
168
|
+
cover=music.cover,
|
|
169
|
+
label=music.label,
|
|
170
|
+
dynamic_text=music.dynamic_text,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def jump_url(self) -> str:
|
|
175
|
+
"""音乐跳转链接"""
|
|
176
|
+
return "https://www.bilibili.com/audio/au%s" % self.music_id
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass(frozen=True)
|
|
180
|
+
class ArticleData(BaseDataMixin):
|
|
181
|
+
"""
|
|
182
|
+
专栏信息数据
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
title: str # 专栏标题
|
|
186
|
+
summary: str # 专栏摘要
|
|
187
|
+
has_more: bool # 是否有更多内容
|
|
188
|
+
article_id: int # 专栏ID
|
|
189
|
+
|
|
190
|
+
@classmethod
|
|
191
|
+
def from_dto(cls, article: ArticleDto) -> "ArticleData":
|
|
192
|
+
"""从ArticleDto构造ArticleData实例"""
|
|
193
|
+
return cls(
|
|
194
|
+
title=article.title,
|
|
195
|
+
summary=article.summary,
|
|
196
|
+
has_more=article.has_more,
|
|
197
|
+
article_id=article.id,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def jump_url(self) -> str:
|
|
202
|
+
"""专栏跳转链接"""
|
|
203
|
+
return "https://www.bilibili.com/opus/%s" % self.article_id
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
@dataclass(frozen=True)
|
|
207
|
+
class LiveRcmdData(BaseDataMixin):
|
|
208
|
+
"""
|
|
209
|
+
直播推荐信息数据
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
room_id: int # 直播间ID
|
|
213
|
+
live_status: int # 直播状态 1:直播中
|
|
214
|
+
title: str # 直播间标题
|
|
215
|
+
cover: str # 直播间封面
|
|
216
|
+
online: int # 在线人数
|
|
217
|
+
area_id: int # 直播分区ID
|
|
218
|
+
area_name: str # 直播分区
|
|
219
|
+
parent_area_id: int # 直播父分区ID
|
|
220
|
+
parent_area_name: str # 直播父分区
|
|
221
|
+
live_start_time: int # 开播时间戳
|
|
222
|
+
watched_num: int | None # 观看人数
|
|
223
|
+
switch: bool | None # 观看榜开关
|
|
224
|
+
text_small: str | None # 小文本
|
|
225
|
+
text_large: str | None # 大文本
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def from_dto(cls, live_rcmd: LiveRcmdDto) -> "LiveRcmdData":
|
|
229
|
+
"""从LiveRcmdDto构造LiveRcmdData实例"""
|
|
230
|
+
return cls(
|
|
231
|
+
room_id=live_rcmd.room_id,
|
|
232
|
+
live_status=live_rcmd.live_status,
|
|
233
|
+
title=live_rcmd.title,
|
|
234
|
+
cover=live_rcmd.cover,
|
|
235
|
+
online=live_rcmd.online,
|
|
236
|
+
area_id=live_rcmd.area_id,
|
|
237
|
+
area_name=live_rcmd.area_name,
|
|
238
|
+
parent_area_id=live_rcmd.parent_area_id,
|
|
239
|
+
parent_area_name=live_rcmd.parent_area_name,
|
|
240
|
+
live_start_time=live_rcmd.live_start_time,
|
|
241
|
+
watched_num=live_rcmd.watched_num,
|
|
242
|
+
switch=live_rcmd.switch,
|
|
243
|
+
text_small=live_rcmd.text_small,
|
|
244
|
+
text_large=live_rcmd.text_large,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def jump_url(self) -> str:
|
|
249
|
+
"""直播间跳转链接"""
|
|
250
|
+
return "https://live.bilibili.com/%s" % self.room_id
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@dataclass(frozen=True)
|
|
254
|
+
class DynamicData(BaseDataMixin):
|
|
255
|
+
"""
|
|
256
|
+
动态数据
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
dynamic_id: str # 动态ID
|
|
260
|
+
dynamic_type: str # 动态类型
|
|
261
|
+
visible: bool # 动态显示状态(false时被折叠)
|
|
262
|
+
pub_time: str # 发布时间
|
|
263
|
+
pub_ts: int # 发布时间戳
|
|
264
|
+
author: AuthorData # 作者信息
|
|
265
|
+
stat: StatData | None # 统计信息
|
|
266
|
+
tag: str | None # 标签(如置顶)
|
|
267
|
+
text: str | None # 文字内容
|
|
268
|
+
pics_url: list[str] | None # 图片列表
|
|
269
|
+
video: VideoData | None # 视频信息
|
|
270
|
+
music: MusicData | None # 音乐信息
|
|
271
|
+
article: ArticleData | None # 专栏信息
|
|
272
|
+
live_rcmd: LiveRcmdData | None # 直播推荐信息
|
|
273
|
+
forward_orig: Optional["DynamicData"] # 转发的原动态
|
|
274
|
+
|
|
275
|
+
@classmethod
|
|
276
|
+
def from_dto(cls, dto: DynamicDTO) -> "DynamicData":
|
|
277
|
+
"""从DTO对象构造DynamicData实例
|
|
278
|
+
|
|
279
|
+
Args:
|
|
280
|
+
dto: DynamicDTO对象
|
|
281
|
+
|
|
282
|
+
Returns:
|
|
283
|
+
DynamicData实例
|
|
284
|
+
"""
|
|
285
|
+
# 构造作者数据
|
|
286
|
+
author_data = AuthorData.from_dto(dto.author)
|
|
287
|
+
|
|
288
|
+
# 构造统计数据
|
|
289
|
+
stat_data = StatData.from_dto(dto.stat) if dto.stat else None
|
|
290
|
+
|
|
291
|
+
# 构造视频数据
|
|
292
|
+
video_data = VideoData.from_dto(dto.video) if dto.video else None
|
|
293
|
+
|
|
294
|
+
# 构造音乐数据
|
|
295
|
+
music_data = MusicData.from_dto(dto.music) if dto.music else None
|
|
296
|
+
|
|
297
|
+
# 构造专栏数据
|
|
298
|
+
article_data = ArticleData.from_dto(dto.article) if dto.article else None
|
|
299
|
+
|
|
300
|
+
# 构造直播推荐数据
|
|
301
|
+
live_rcmd_data = LiveRcmdData.from_dto(dto.live_rcmd) if dto.live_rcmd else None
|
|
302
|
+
|
|
303
|
+
# 递归构造转发动态数据
|
|
304
|
+
forward_orig_data = cls.from_dto(dto.forward_orig) if dto.forward_orig else None
|
|
305
|
+
|
|
306
|
+
return cls(
|
|
307
|
+
dynamic_id=dto.dynamic_id,
|
|
308
|
+
dynamic_type=dto.dynamic_type,
|
|
309
|
+
visible=dto.visible,
|
|
310
|
+
pub_time=dto.pub_time,
|
|
311
|
+
pub_ts=dto.pub_ts,
|
|
312
|
+
author=author_data,
|
|
313
|
+
stat=stat_data,
|
|
314
|
+
tag=dto.tag,
|
|
315
|
+
text=dto.text,
|
|
316
|
+
pics_url=list(dto.pics_url) if dto.pics_url else None,
|
|
317
|
+
video=video_data,
|
|
318
|
+
music=music_data,
|
|
319
|
+
article=article_data,
|
|
320
|
+
live_rcmd=live_rcmd_data,
|
|
321
|
+
forward_orig=forward_orig_data,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def jump_url(self) -> str:
|
|
326
|
+
"""动态跳转链接"""
|
|
327
|
+
return "https://t.bilibili.com/%s" % self.dynamic_id
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def is_live(self) -> bool:
|
|
331
|
+
"""判断动态是否为直播推荐类型"""
|
|
332
|
+
return self.dynamic_type == "DYNAMIC_TYPE_LIVE_RCMD"
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def is_video(self) -> bool:
|
|
336
|
+
"""判断动态是否为视频类型"""
|
|
337
|
+
return self.dynamic_type == "DYNAMIC_TYPE_AV"
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def is_music(self) -> bool:
|
|
341
|
+
"""判断动态是否为音乐类型"""
|
|
342
|
+
return self.dynamic_type == "DYNAMIC_TYPE_MUSIC"
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def is_article(self) -> bool:
|
|
346
|
+
"""判断动态是否为专栏类型"""
|
|
347
|
+
return self.dynamic_type == "DYNAMIC_TYPE_ARTICLE"
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def is_word(self) -> bool:
|
|
351
|
+
"""判断动态是否为纯文字类型"""
|
|
352
|
+
return self.dynamic_type == "DYNAMIC_TYPE_WORD"
|
|
353
|
+
|
|
354
|
+
@property
|
|
355
|
+
def is_draw(self) -> bool:
|
|
356
|
+
"""判断动态是否为图文类型"""
|
|
357
|
+
return self.dynamic_type == "DYNAMIC_TYPE_DRAW"
|
|
358
|
+
|
|
359
|
+
@property
|
|
360
|
+
def is_forward(self) -> bool:
|
|
361
|
+
"""判断动态是否为转发类型"""
|
|
362
|
+
return self.dynamic_type == "DYNAMIC_TYPE_FORWARD"
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from butterbot.core.data import BaseDataMixin
|
|
4
|
+
|
|
5
|
+
from .dto import (
|
|
6
|
+
AnchorInfoDto,
|
|
7
|
+
LiveRoomDTO,
|
|
8
|
+
NoticeBoardDto,
|
|
9
|
+
RoomInfoDto,
|
|
10
|
+
WatchedShowDto,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class RoomInfoData(BaseDataMixin):
|
|
16
|
+
"""直播间信息数据"""
|
|
17
|
+
|
|
18
|
+
uid: int # 用户uid
|
|
19
|
+
room_id: int # 房间号
|
|
20
|
+
title: str # 直播间标题
|
|
21
|
+
cover_url: str # 直播间封面url
|
|
22
|
+
background_url: str # 直播间背景图url
|
|
23
|
+
description: str # 主播简介
|
|
24
|
+
tags: list[str] # 直播间标签列表
|
|
25
|
+
live_status: int # 直播状态 0:未开播 1:直播中 2:轮播中
|
|
26
|
+
live_start_time: int # 直播开始时间戳
|
|
27
|
+
parent_area_name: str # 直播间父分区
|
|
28
|
+
parent_area_id: int # 直播间父分区ID
|
|
29
|
+
area_name: str # 直播间子分区
|
|
30
|
+
area_id: int # 直播间子分区ID
|
|
31
|
+
keyframe_url: str # 直播间关键帧url
|
|
32
|
+
online: int # 直播间当前在线人数
|
|
33
|
+
|
|
34
|
+
@classmethod
|
|
35
|
+
def from_dto(cls, room_info: RoomInfoDto) -> "RoomInfoData":
|
|
36
|
+
"""从RoomInfoDto构造RoomInfoData实例"""
|
|
37
|
+
return cls(
|
|
38
|
+
uid=room_info.uid,
|
|
39
|
+
room_id=room_info.room_id,
|
|
40
|
+
title=room_info.title,
|
|
41
|
+
cover_url=room_info.cover_url,
|
|
42
|
+
background_url=room_info.background_url,
|
|
43
|
+
description=room_info.description,
|
|
44
|
+
tags=list(room_info.tags),
|
|
45
|
+
live_status=room_info.live_status,
|
|
46
|
+
live_start_time=room_info.live_start_time,
|
|
47
|
+
parent_area_name=room_info.parent_area_name,
|
|
48
|
+
parent_area_id=room_info.parent_area_id,
|
|
49
|
+
area_name=room_info.area_name,
|
|
50
|
+
area_id=room_info.area_id,
|
|
51
|
+
keyframe_url=room_info.keyframe_url,
|
|
52
|
+
online=room_info.online,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def jump_url(self) -> str:
|
|
57
|
+
"""直播间跳转链接"""
|
|
58
|
+
return "https://live.bilibili.com/%s" % self.room_id
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True)
|
|
62
|
+
class AnchorInfoData(BaseDataMixin):
|
|
63
|
+
"""主播信息数据"""
|
|
64
|
+
|
|
65
|
+
name: str # 主播昵称
|
|
66
|
+
face_url: str # 主播头像url
|
|
67
|
+
gender: str # 主播性别
|
|
68
|
+
official_info: str # 主播官方信息(认证信息)
|
|
69
|
+
fanclub_name: str # 粉丝牌名称
|
|
70
|
+
fanclub_num: int # 粉丝团人数
|
|
71
|
+
live_level: int # 主播等级
|
|
72
|
+
live_score: int # 直播分数
|
|
73
|
+
live_upgrade_score: int # 升级所需分数
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def from_dto(cls, anchor_info: AnchorInfoDto) -> "AnchorInfoData":
|
|
77
|
+
"""从AnchorInfoDto构造AnchorInfoData实例"""
|
|
78
|
+
return cls(
|
|
79
|
+
name=anchor_info.name,
|
|
80
|
+
face_url=anchor_info.face_url,
|
|
81
|
+
gender=anchor_info.gender,
|
|
82
|
+
official_info=anchor_info.official_info,
|
|
83
|
+
fanclub_name=anchor_info.fanclub_name,
|
|
84
|
+
fanclub_num=anchor_info.fanclub_num,
|
|
85
|
+
live_level=anchor_info.live_level,
|
|
86
|
+
live_score=anchor_info.live_score,
|
|
87
|
+
live_upgrade_score=anchor_info.live_upgrade_score,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class WatchedShowData(BaseDataMixin):
|
|
93
|
+
"""观看榜信息数据"""
|
|
94
|
+
|
|
95
|
+
switch: bool # 观看榜开关
|
|
96
|
+
num: int # 观看人数/人气值
|
|
97
|
+
text_small: str # 小文本
|
|
98
|
+
text_large: str # 大文本
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_dto(cls, watched_show: WatchedShowDto) -> "WatchedShowData":
|
|
102
|
+
"""从WatchedShowDto构造WatchedShowData实例"""
|
|
103
|
+
return cls(
|
|
104
|
+
switch=watched_show.switch,
|
|
105
|
+
num=watched_show.num,
|
|
106
|
+
text_small=watched_show.text_small,
|
|
107
|
+
text_large=watched_show.text_large,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@dataclass(frozen=True)
|
|
112
|
+
class NoticeBoardData(BaseDataMixin):
|
|
113
|
+
"""公告栏信息数据"""
|
|
114
|
+
|
|
115
|
+
content: str # 公告内容
|
|
116
|
+
ctime: str # 公告发布时间
|
|
117
|
+
|
|
118
|
+
@classmethod
|
|
119
|
+
def from_dto(cls, notice_board: NoticeBoardDto) -> "NoticeBoardData":
|
|
120
|
+
"""从NoticeBoardDto构造NoticeBoardData实例"""
|
|
121
|
+
return cls(content=notice_board.content, ctime=notice_board.ctime)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass(frozen=True)
|
|
125
|
+
class LiveRoomData(BaseDataMixin):
|
|
126
|
+
"""直播间数据"""
|
|
127
|
+
|
|
128
|
+
room_info: RoomInfoData # 直播间信息
|
|
129
|
+
anchor_info: AnchorInfoData # 主播信息
|
|
130
|
+
watched_show: WatchedShowData # 观看榜信息
|
|
131
|
+
notice_board: NoticeBoardData | None # 公告栏信息
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def from_dto(cls, dto: LiveRoomDTO) -> "LiveRoomData":
|
|
135
|
+
"""从DTO对象构造LiveRoomData实例
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
dto: LiveRoomDTO对象
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
LiveRoomData实例
|
|
142
|
+
"""
|
|
143
|
+
# 构造直播间信息数据
|
|
144
|
+
room_info_data = RoomInfoData.from_dto(dto.room_info)
|
|
145
|
+
|
|
146
|
+
# 构造主播信息数据
|
|
147
|
+
anchor_info_data = AnchorInfoData.from_dto(dto.anchor_info)
|
|
148
|
+
|
|
149
|
+
# 构造观看榜信息数据
|
|
150
|
+
watched_show_data = WatchedShowData.from_dto(dto.watched_show)
|
|
151
|
+
|
|
152
|
+
# 构造公告栏信息数据
|
|
153
|
+
notice_board_data = (
|
|
154
|
+
NoticeBoardData.from_dto(dto.notice_board) if dto.notice_board else None
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
return cls(
|
|
158
|
+
room_info=room_info_data,
|
|
159
|
+
anchor_info=anchor_info_data,
|
|
160
|
+
watched_show=watched_show_data,
|
|
161
|
+
notice_board=notice_board_data,
|
|
162
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from butterbot.core.data import BaseDataMixin
|
|
4
|
+
|
|
5
|
+
from .dto import (
|
|
6
|
+
VideoPartDto,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class VideoPartData(BaseDataMixin):
|
|
12
|
+
"""
|
|
13
|
+
视频字幕数据
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
start_timestamp: int # 开始时间戳
|
|
17
|
+
end_timestamp: int # 结束时间戳
|
|
18
|
+
content: str # 字幕内容
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def from_dto_list(cls, dto_list: list[VideoPartDto]) -> list["VideoPartData"]:
|
|
22
|
+
"""从DTO对象列表构造VideoPartData实例列表
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dto_list: VideoPartDto对象列表
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
VideoPartData实例列表
|
|
29
|
+
"""
|
|
30
|
+
return [cls.from_dto(dto) for dto in dto_list]
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def from_dto(cls, dto: VideoPartDto) -> "VideoPartData":
|
|
34
|
+
"""从DTO对象构造VideoPartData实例
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
dto: VideoPartDto对象
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
VideoPartData实例
|
|
41
|
+
"""
|
|
42
|
+
return cls(
|
|
43
|
+
start_timestamp=dto.start_timestamp,
|
|
44
|
+
end_timestamp=dto.end_timestamp,
|
|
45
|
+
content=dto.content,
|
|
46
|
+
)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .bili_danmaku_source import (
|
|
2
|
+
BiliDanmakuSource,
|
|
3
|
+
)
|
|
4
|
+
from .bili_dynamic_source import (
|
|
5
|
+
BiliDynamicSource,
|
|
6
|
+
)
|
|
7
|
+
from .bili_live_source import (
|
|
8
|
+
BiliLiveSource,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BiliDanmakuSource",
|
|
13
|
+
"BiliDynamicSource",
|
|
14
|
+
"BiliLiveSource",
|
|
15
|
+
]
|