From 8f6d770e1850fc58c6bb5621e37b611514b72541 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 11 Mar 2024 21:08:15 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=8E=A5=E5=8F=A3=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * uid改成open_id * 上舰消息添加价格字段 --- README.md | 4 ++-- blivedm/models/open_live.py | 35 ++++++++++++++++++++++++++++------- sample.py | 2 +- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4c6fb3d..6d9ba12 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Python获取bilibili直播弹幕的库,使用WebSocket协议,支持web端和B站直播开放平台两种接口 -[协议解释](https://blog.csdn.net/xfgryujk/article/details/80306776)(有点过时了,总体是没错的) +[协议解释](https://open-live.bilibili.com/document/657d8e34-f926-a133-16c0-300c1afc6e6b) 基于本库开发的一个应用:[blivechat](https://github.com/xfgryujk/blivechat) @@ -15,4 +15,4 @@ Python获取bilibili直播弹幕的库,使用WebSocket协议,支持web端和 pip install -r requirements.txt ``` -3. 例程看[sample.py](./sample.py)和[open_live_sample.py](./open_live_sample.py) +3. web端例程在[sample.py](./sample.py),B站直播开放平台例程在[open_live_sample.py](./open_live_sample.py) diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index c3cd855..2282a0c 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -24,7 +24,9 @@ class DanmakuMessage: uname: str = '' """用户昵称""" uid: int = 0 - """用户UID""" + """用户UID(即将废弃)""" + open_id: str = '' + """用户唯一标识(2024-03-11后上线)""" uface: str = '' """用户头像""" timestamp: int = 0 @@ -53,6 +55,7 @@ class DanmakuMessage: return cls( uname=data['uname'], uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uface=data['uface'], timestamp=data['timestamp'], room_id=data['room_id'], @@ -75,6 +78,8 @@ class AnchorInfo: uid: int = 0 """收礼主播uid""" + open_id: str = '' + """收礼主播唯一标识(2024-03-11后上线)""" uname: str = '' """收礼主播昵称""" uface: str = '' @@ -84,6 +89,7 @@ class AnchorInfo: def from_dict(cls, data: dict): return cls( uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uname=data['uname'], uface=data['uface'], ) @@ -123,7 +129,9 @@ class GiftMessage: room_id: int = 0 """房间号""" uid: int = 0 - """送礼用户UID""" + """送礼用户UID(即将废弃)""" + open_id: str = '' + """用户唯一标识(2024-03-11后上线)""" uname: str = '' """送礼用户昵称""" uface: str = '' @@ -135,7 +143,7 @@ class GiftMessage: gift_num: int = 0 """赠送道具数量""" price: int = 0 - """(礼物单价)支付金额(1000 = 1元 = 10电池),盲盒:爆出道具的价值""" # 这个B 站文档又不写清楚是单价还是总价 + """礼物爆出单价,(1000 = 1元 = 10电池),盲盒:爆出道具的价值""" paid: bool = False """是否是付费道具""" fans_medal_level: int = 0 @@ -170,6 +178,7 @@ class GiftMessage: return cls( room_id=data['room_id'], uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uname=data['uname'], uface=data['uface'], gift_id=data['gift_id'], @@ -197,7 +206,9 @@ class UserInfo: """ uid: int = 0 - """用户uid""" + """用户UID(即将废弃)""" + open_id: str = '' + """用户唯一标识(2024-03-11后上线)""" uname: str = '' """用户昵称""" uface: str = '' @@ -207,6 +218,7 @@ class UserInfo: def from_dict(cls, data: dict): return cls( uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uname=data['uname'], uface=data['uface'], ) @@ -226,6 +238,8 @@ class GuardBuyMessage: """大航海数量""" guard_unit: str = '' """大航海单位""" + price: int = 0 + """大航海金瓜子""" fans_medal_level: int = 0 """粉丝勋章等级""" fans_medal_name: str = '' @@ -246,6 +260,7 @@ class GuardBuyMessage: guard_level=data['guard_level'], guard_num=data['guard_num'], guard_unit=data['guard_unit'], + price=data['price'], fans_medal_level=data['fans_medal_level'], fans_medal_name=data['fans_medal_name'], fans_medal_wearing_status=data['fans_medal_wearing_status'], @@ -264,7 +279,9 @@ class SuperChatMessage: room_id: int = 0 """直播间id""" uid: int = 0 - """购买用户UID""" + """用户UID(即将废弃)""" + open_id: str = '' + """用户唯一标识(2024-03-11后上线)""" uname: str = '' """购买的用户昵称""" uface: str = '' @@ -297,6 +314,7 @@ class SuperChatMessage: return cls( room_id=data['room_id'], uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uname=data['uname'], uface=data['uface'], message_id=data['message_id'], @@ -346,7 +364,9 @@ class LikeMessage: uname: str = '' """用户昵称""" uid: int = 0 - """用户UID""" + """用户UID(即将废弃)""" + open_id: str = '' + """用户唯一标识(2024-03-11后上线)""" uface: str = '' """用户头像""" timestamp: int = 0 @@ -355,7 +375,7 @@ class LikeMessage: """发生的直播间""" like_text: str = '' """点赞文案(“xxx点赞了”)""" - like_count: int = 0 # 官方文档把这个字段名打错了,这个B文档真是一点都靠不住 + like_count: int = 0 """对单个用户最近2秒的点赞次数聚合""" fans_medal_wearing_status: bool = False """该房间粉丝勋章佩戴情况""" @@ -372,6 +392,7 @@ class LikeMessage: return cls( uname=data['uname'], uid=data['uid'], + open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 uface=data['uface'], timestamp=data['timestamp'], room_id=data['room_id'], diff --git a/sample.py b/sample.py index d54ef15..976e434 100644 --- a/sample.py +++ b/sample.py @@ -18,7 +18,7 @@ TEST_ROOM_IDS = [ 23105590, ] -# 这里填一个已登录账号的cookie。不填cookie也可以连接,但是收到弹幕的用户名会打码,UID会变成0 +# 这里填一个已登录账号的cookie的SESSDATA字段的值。不填也可以连接,但是收到弹幕的用户名会打码,UID会变成0 SESSDATA = '' session: Optional[aiohttp.ClientSession] = None From 60ce01927a3d17ecc069d8f5f8ed62ffc0075e31 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 17 Mar 2024 18:39:28 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E5=BC=80=E6=94=BE=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E6=B7=BB=E5=8A=A0=E4=B8=BB=E6=92=AD?= =?UTF-8?q?Open=20ID=E3=80=81=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AAweb?= =?UTF-8?q?=E7=AB=AF=E8=A7=A3=E6=9E=90=E5=BC=B9=E5=B9=95=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/clients/open_live.py | 10 ++++++++++ blivedm/models/open_live.py | 12 ++++++------ blivedm/models/web.py | 11 +++++++++-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/blivedm/clients/open_live.py b/blivedm/clients/open_live.py index 0b00a2f..b94a299 100644 --- a/blivedm/clients/open_live.py +++ b/blivedm/clients/open_live.py @@ -60,6 +60,8 @@ class OpenLiveClient(ws_base.WebSocketClientBase): # 在调用init_room后初始化的字段 self._room_owner_uid: Optional[int] = None """主播用户ID""" + self._room_owner_open_id: Optional[str] = None + """主播Open ID""" self._host_server_url_list: Optional[List[str]] = [] """弹幕服务器URL列表""" self._auth_body: Optional[str] = None @@ -78,6 +80,13 @@ class OpenLiveClient(ws_base.WebSocketClientBase): """ return self._room_owner_uid + @property + def room_owner_open_id(self) -> Optional[str]: + """ + 主播Open ID,调用init_room后初始化 + """ + return self._room_owner_open_id + @property def room_owner_auth_code(self): """ @@ -181,6 +190,7 @@ class OpenLiveClient(ws_base.WebSocketClientBase): anchor_info = data['anchor_info'] self._room_id = anchor_info['room_id'] self._room_owner_uid = anchor_info['uid'] + self._room_owner_open_id = anchor_info['open_id'] return True async def _end_game(self): diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index 2282a0c..7954989 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -55,7 +55,7 @@ class DanmakuMessage: return cls( uname=data['uname'], uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uface=data['uface'], timestamp=data['timestamp'], room_id=data['room_id'], @@ -89,7 +89,7 @@ class AnchorInfo: def from_dict(cls, data: dict): return cls( uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uname=data['uname'], uface=data['uface'], ) @@ -178,7 +178,7 @@ class GiftMessage: return cls( room_id=data['room_id'], uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uname=data['uname'], uface=data['uface'], gift_id=data['gift_id'], @@ -218,7 +218,7 @@ class UserInfo: def from_dict(cls, data: dict): return cls( uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uname=data['uname'], uface=data['uface'], ) @@ -314,7 +314,7 @@ class SuperChatMessage: return cls( room_id=data['room_id'], uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uname=data['uname'], uface=data['uface'], message_id=data['message_id'], @@ -392,7 +392,7 @@ class LikeMessage: return cls( uname=data['uname'], uid=data['uid'], - open_id=data.get('open_id', ''), # 还没全量上线,先用get保险 + open_id=data['open_id'], uface=data['uface'], timestamp=data['timestamp'], room_id=data['room_id'], diff --git a/blivedm/models/web.py b/blivedm/models/web.py index 2025695..1473f49 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -125,6 +125,13 @@ class DanmakuMessage: mcolor = 0 special_medal = 0 + if len(info[5]) != 0: + old_title = info[5][0] + title = info[5][1] + else: + old_title = '' + title = '' + return cls( mode=info[0][1], font_size=info[0][2], @@ -161,8 +168,8 @@ class DanmakuMessage: ulevel_color=info[4][2], ulevel_rank=info[4][3], - old_title=info[5][0], - title=info[5][1], + old_title=old_title, + title=title, privilege_type=info[7], ) From 359e46ae0c3c7af6b5ad118951c66531bd61ef82 Mon Sep 17 00:00:00 2001 From: John Smith Date: Tue, 16 Apr 2024 20:36:04 +0800 Subject: [PATCH 03/16] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=A8=A1=E5=9E=8B=E7=9A=84UID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/models/open_live.py | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index 7954989..09bd248 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -23,10 +23,8 @@ class DanmakuMessage: uname: str = '' """用户昵称""" - uid: int = 0 - """用户UID(即将废弃)""" open_id: str = '' - """用户唯一标识(2024-03-11后上线)""" + """用户唯一标识""" uface: str = '' """用户头像""" timestamp: int = 0 @@ -54,7 +52,6 @@ class DanmakuMessage: def from_command(cls, data: dict): return cls( uname=data['uname'], - uid=data['uid'], open_id=data['open_id'], uface=data['uface'], timestamp=data['timestamp'], @@ -79,7 +76,7 @@ class AnchorInfo: uid: int = 0 """收礼主播uid""" open_id: str = '' - """收礼主播唯一标识(2024-03-11后上线)""" + """收礼主播唯一标识""" uname: str = '' """收礼主播昵称""" uface: str = '' @@ -128,10 +125,8 @@ class GiftMessage: room_id: int = 0 """房间号""" - uid: int = 0 - """送礼用户UID(即将废弃)""" open_id: str = '' - """用户唯一标识(2024-03-11后上线)""" + """用户唯一标识""" uname: str = '' """送礼用户昵称""" uface: str = '' @@ -177,7 +172,6 @@ class GiftMessage: return cls( room_id=data['room_id'], - uid=data['uid'], open_id=data['open_id'], uname=data['uname'], uface=data['uface'], @@ -205,10 +199,8 @@ class UserInfo: 用户信息 """ - uid: int = 0 - """用户UID(即将废弃)""" open_id: str = '' - """用户唯一标识(2024-03-11后上线)""" + """用户唯一标识""" uname: str = '' """用户昵称""" uface: str = '' @@ -217,7 +209,6 @@ class UserInfo: @classmethod def from_dict(cls, data: dict): return cls( - uid=data['uid'], open_id=data['open_id'], uname=data['uname'], uface=data['uface'], @@ -278,10 +269,8 @@ class SuperChatMessage: room_id: int = 0 """直播间id""" - uid: int = 0 - """用户UID(即将废弃)""" open_id: str = '' - """用户唯一标识(2024-03-11后上线)""" + """用户唯一标识""" uname: str = '' """购买的用户昵称""" uface: str = '' @@ -313,7 +302,6 @@ class SuperChatMessage: def from_command(cls, data: dict): return cls( room_id=data['room_id'], - uid=data['uid'], open_id=data['open_id'], uname=data['uname'], uface=data['uface'], @@ -363,10 +351,8 @@ class LikeMessage: uname: str = '' """用户昵称""" - uid: int = 0 - """用户UID(即将废弃)""" open_id: str = '' - """用户唯一标识(2024-03-11后上线)""" + """用户唯一标识""" uface: str = '' """用户头像""" timestamp: int = 0 @@ -391,7 +377,6 @@ class LikeMessage: def from_command(cls, data: dict): return cls( uname=data['uname'], - uid=data['uid'], open_id=data['open_id'], uface=data['uface'], timestamp=data['timestamp'], From defcc7ad58066d053a6653d43d3c31078c655c1b Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 6 Jun 2024 23:14:36 +0800 Subject: [PATCH 04/16] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dweb=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96buvid=E5=A4=B1=E8=B4=A5=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit close #54 --- blivedm/clients/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm/clients/web.py b/blivedm/clients/web.py index e7a6d78..a12c297 100644 --- a/blivedm/clients/web.py +++ b/blivedm/clients/web.py @@ -16,7 +16,7 @@ __all__ = ( logger = logging.getLogger('blivedm') UID_INIT_URL = 'https://api.bilibili.com/x/web-interface/nav' -BUVID_INIT_URL = 'https://data.bilibili.com/v/' +BUVID_INIT_URL = 'https://www.bilibili.com/' ROOM_INIT_URL = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom' DANMAKU_SERVER_CONF_URL = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo' DEFAULT_DANMAKU_SERVER_LIST = [ From 7f400e9c61d449a0e4212b47f45f0d1e4215157c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=AF=E5=87=9B?= <1348292515@qq.com> Date: Mon, 7 Oct 2024 16:33:19 +0800 Subject: [PATCH 05/16] build: PEP 621 packaging (#60) --- .gitignore | 3 +++ blivedm/__init__.py | 2 ++ pyproject.toml | 46 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index 101adc4..96fbcac 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,6 @@ ENV/ .idea/ +.pdm-python +pdm.lock +pdm.toml diff --git a/blivedm/__init__.py b/blivedm/__init__.py index e3e5b60..9248087 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,3 +1,5 @@ # -*- coding: utf-8 -*- +__version__ = '1.0.0' + from .handlers import * from .clients import * diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8cecd98 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,46 @@ +[build-system] +requires = ["pdm-backend"] +build-backend = "pdm.backend" + +[project] +name = "blivedm" +dynamic = ["version"] +description = "Python获取bilibili直播弹幕的库,使用WebSocket协议" +readme = "README.md" +keywords = ["bilibili", "bilibili-live", "danmaku"] +requires-python = ">=3.8" +authors = [ + {name = "xfgryujk", email = "xfgryujk@126.com"}, +] +license = {file = "LICENSE"} +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Framework :: AsyncIO", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Software Development :: Libraries", +] +dependencies = [ + "aiohttp~=3.9.0", + "Brotli~=1.1.0", + "yarl~=1.9.3", +] + +[project.urls] +Homepage = "https://github.com/xfgryujk/blivedm" +Repository = "https://github.com/xfgryujk/blivedm" +Issues = "https://github.com/xfgryujk/blivedm/issues" + +[tool.pdm] +version = {source = "file", path = "blivedm/__init__.py"} +distribution = true From aafe70efe368d6f837de4da6ba377c1af6f29bf7 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sat, 7 Dec 2024 21:02:08 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/__init__.py | 2 +- blivedm/handlers.py | 21 +++++++ blivedm/models/open_live.py | 116 +++++++++++++++++++++++++++++++++++- open_live_sample.py | 9 +++ 4 files changed, 144 insertions(+), 4 deletions(-) diff --git a/blivedm/__init__.py b/blivedm/__init__.py index 9248087..a1c5a5b 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '1.0.0' +__version__ = '1.1.0-dev' from .handlers import * from .clients import * diff --git a/blivedm/handlers.py b/blivedm/handlers.py index 76caca6..5cdbe90 100644 --- a/blivedm/handlers.py +++ b/blivedm/handlers.py @@ -108,6 +108,12 @@ class BaseHandler(HandlerInterface): ), # 点赞 'LIVE_OPEN_PLATFORM_LIKE': _make_msg_callback('_on_open_live_like', open_models.LikeMessage), + # 进入房间 + 'LIVE_OPEN_PLATFORM_LIVE_ROOM_ENTER': _make_msg_callback('_on_open_live_enter_room', open_models.RoomEnterMessage), + # 开始直播 + 'LIVE_OPEN_PLATFORM_LIVE_START': _make_msg_callback('_on_open_live_start_live', open_models.LiveStartMessage), + # 结束直播 + 'LIVE_OPEN_PLATFORM_LIVE_END': _make_msg_callback('_on_open_live_end_live', open_models.LiveEndMessage), } """cmd -> 处理回调""" @@ -197,3 +203,18 @@ class BaseHandler(HandlerInterface): """ 点赞 """ + + def _on_open_live_enter_room(self, client: ws_base.WebSocketClientBase, message: open_models.RoomEnterMessage): + """ + 进入房间 + """ + + def _on_open_live_start_live(self, client: ws_base.WebSocketClientBase, message: open_models.LiveStartMessage): + """ + 开始直播 + """ + + def _on_open_live_end_live(self, client: ws_base.WebSocketClientBase, message: open_models.LiveEndMessage): + """ + 结束直播 + """ diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index 09bd248..5d5c40b 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -138,7 +138,20 @@ class GiftMessage: gift_num: int = 0 """赠送道具数量""" price: int = 0 - """礼物爆出单价,(1000 = 1元 = 10电池),盲盒:爆出道具的价值""" + """ + 礼物爆出单价,(1000 = 1元 = 10电池),盲盒:爆出道具的价值 + + 注意: + + - 免费礼物这个字段也可能不是0,而是银瓜子数 + - 有些打折礼物这里不是实际支付的价值,实际价值应该用 `r_price` + """ + r_price: int = 0 + """ + 实际价值(1000 = 1元 = 10电池),盲盒:爆出道具的价值 + + 注意:免费礼物这个字段也可能不是0 + """ paid: bool = False """是否是付费道具""" fans_medal_level: int = 0 @@ -179,6 +192,7 @@ class GiftMessage: gift_name=data['gift_name'], gift_num=data['gift_num'], price=data['price'], + r_price=data['r_price'], paid=data['paid'], fans_medal_level=data['fans_medal_level'], fans_medal_name=data['fans_medal_name'], @@ -228,7 +242,7 @@ class GuardBuyMessage: guard_num: int = 0 """大航海数量""" guard_unit: str = '' - """大航海单位""" + """大航海单位(正常单位为“月”,如为其他内容,无视`guard_num`以本字段内容为准,例如`*3天`)""" price: int = 0 """大航海金瓜子""" fans_medal_level: int = 0 @@ -346,7 +360,10 @@ class LikeMessage: """ 点赞消息 - 请注意:用户端每分钟触发若干次的情况下只会推送一次该消息 + 请注意: + + - 只有房间处于开播中,才会触发点赞事件 + - 对单一用户最近2秒聚合发送一次点赞次数 """ uname: str = '' @@ -388,3 +405,96 @@ class LikeMessage: fans_medal_level=data['fans_medal_level'], msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是参考JSON里面有 ) + + +@dataclasses.dataclass +class RoomEnterMessage: + """ + 进入房间消息 + """ + + room_id: int = 0 + """直播间id""" + uface: str = '' + """用户头像""" + uname: str = '' + """用户昵称""" + open_id: str = '' + """用户唯一标识""" + timestamp: int = 0 + """发生的时间戳""" + msg_id: str = '' # 官方文档表格里没列出这个字段,但是实际上有 + """消息唯一id""" + + @classmethod + def from_command(cls, data: dict): + return cls( + room_id=data['room_id'], + uface=data['uface'], + uname=data['uname'], + open_id=data['open_id'], + timestamp=data['timestamp'], + msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是实际上有 + ) + + +@dataclasses.dataclass +class LiveStartMessage: + """ + 开始直播消息 + """ + + room_id: int = 0 + """直播间id""" + open_id: str = '' + """用户唯一标识""" + timestamp: int = 0 + """发生的时间戳""" + area_id: int = 0 + """开播二级分区ID""" + title: str = '' + """开播时刻,直播间的标题""" + msg_id: str = '' # 官方文档表格里没列出这个字段,但是实际上有 + """消息唯一id""" + + @classmethod + def from_command(cls, data: dict): + return cls( + room_id=data['room_id'], + open_id=data['open_id'], + timestamp=data['timestamp'], + area_id=int(data['area_id']), # 官方文档说是int64,实际上发的是str,之后会修复成int64 + title=data['title'], + msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是实际上有 + ) + + +@dataclasses.dataclass +class LiveEndMessage: + """ + 结束直播消息 + """ + + room_id: int = 0 + """直播间id""" + open_id: str = '' + """用户唯一标识""" + timestamp: int = 0 + """发生的时间戳""" + area_id: int = 0 + """开播二级分区ID""" + title: str = '' + """开播时刻,直播间的标题""" + msg_id: str = '' # 官方文档表格里没列出这个字段,但是实际上有 + """消息唯一id""" + + @classmethod + def from_command(cls, data: dict): + return cls( + room_id=data['room_id'], + open_id=data['open_id'], + timestamp=data['timestamp'], + area_id=int(data['area_id']), # 官方文档说是int64,实际上发的是str,之后会修复成int64 + title=data['title'], + msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是实际上有 + ) diff --git a/open_live_sample.py b/open_live_sample.py index 6e47410..ace5b22 100644 --- a/open_live_sample.py +++ b/open_live_sample.py @@ -71,6 +71,15 @@ class MyHandler(blivedm.BaseHandler): def _on_open_live_like(self, client: blivedm.OpenLiveClient, message: open_models.LikeMessage): print(f'[{message.room_id}] {message.uname} 点赞') + def _on_open_live_enter_room(self, client: blivedm.OpenLiveClient, message: open_models.RoomEnterMessage): + print(f'[{message.room_id}] {message.uname} 进入房间') + + def _on_open_live_start_live(self, client: blivedm.OpenLiveClient, message: open_models.LiveStartMessage): + print(f'[{message.room_id}] 开始直播') + + def _on_open_live_end_live(self, client: blivedm.OpenLiveClient, message: open_models.LiveEndMessage): + print(f'[{message.room_id}] 结束直播') + if __name__ == '__main__': asyncio.run(main()) From f9b6dc2f353f574dc7ae757be63ccacf6c93d06f Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 8 Dec 2024 18:10:12 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=E6=8E=A5=E5=85=A5=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=81=9C=E6=AD=A2?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E7=9A=84=E6=B6=88=E6=81=AF=EF=BC=8C=E5=8F=8A?= =?UTF-8?q?=E6=97=B6=E6=96=AD=E5=BC=80=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/clients/open_live.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/blivedm/clients/open_live.py b/blivedm/clients/open_live.py index b94a299..b85686a 100644 --- a/blivedm/clients/open_live.py +++ b/blivedm/clients/open_live.py @@ -291,3 +291,16 @@ class OpenLiveClient(ws_base.WebSocketClientBase): 发送认证包 """ await self._websocket.send_bytes(self._make_packet(self._auth_body, ws_base.Operation.AUTH)) + + def _handle_command(self, command: dict): + cmd = command.get('cmd', '') + if cmd == 'LIVE_OPEN_PLATFORM_INTERACTION_END' and command['data']['game_id'] == self._game_id: + # 服务器主动停止推送,可能是心跳超时,需要重新开启项目 + logger.warning('room=%d game end by server, game_id=%s', self._room_id, self._game_id) + + self._need_init_room = True + if self._websocket is not None and not self._websocket.closed: + asyncio.create_task(self._websocket.close()) + return + + super()._handle_command(command) From 8fd7cb467a7e0d0e6ff122e6cd5bc70423756c22 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 22 Dec 2024 23:39:16 +0800 Subject: [PATCH 08/16] =?UTF-8?q?web=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=E6=88=BF=E9=97=B4=E6=B6=88=E6=81=AF=E3=80=81?= =?UTF-8?q?=E5=8F=A6=E4=B8=80=E4=B8=AA=E4=B8=8A=E8=88=B0=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/handlers.py | 97 +++++++++++++++++-------------------------- blivedm/models/web.py | 85 ++++++++++++++++++++++++++++++++++++- sample.py | 20 +++++---- 3 files changed, 133 insertions(+), 69 deletions(-) diff --git a/blivedm/handlers.py b/blivedm/handlers.py index 5cdbe90..531f2b9 100644 --- a/blivedm/handlers.py +++ b/blivedm/handlers.py @@ -17,7 +17,6 @@ logged_unknown_cmds = { 'ENTRY_EFFECT', 'HOT_RANK_CHANGED', 'HOT_RANK_CHANGED_V2', - 'INTERACT_WORD', 'LIVE', 'LIVE_INTERACTIVE_GAME', 'NOTICE_MSG', @@ -35,6 +34,7 @@ logged_unknown_cmds = { 'ROOM_REAL_TIME_MESSAGE_UPDATE', 'STOP_LIVE_ROOM_LIST', 'SUPER_CHAT_MESSAGE_JPN', + 'USER_TOAST_MSG', 'WIDGET_BANNER', } """已打日志的未知cmd""" @@ -75,30 +75,36 @@ class BaseHandler(HandlerInterface): ['BaseHandler', ws_base.WebSocketClientBase, dict], Any ]] - ] = { + ] + """cmd -> 处理回调""" + _CMD_CALLBACK_DICT = { # 收到心跳包,这是blivedm自造的消息,原本的心跳包格式不一样 '_HEARTBEAT': _make_msg_callback('_on_heartbeat', web_models.HeartbeatMessage), - # 收到弹幕 + # 弹幕 # go-common\app\service\live\live-dm\service\v1\send.go 'DANMU_MSG': __danmu_msg_callback, - # 有人送礼 + # 礼物 'SEND_GIFT': _make_msg_callback('_on_gift', web_models.GiftMessage), - # 有人上舰 + # 上舰 'GUARD_BUY': _make_msg_callback('_on_buy_guard', web_models.GuardBuyMessage), + # 另一个上舰消息 + 'USER_TOAST_MSG_V2': _make_msg_callback('_on_user_toast_v2', web_models.UserToastV2Message), # 醒目留言 'SUPER_CHAT_MESSAGE': _make_msg_callback('_on_super_chat', web_models.SuperChatMessage), # 删除醒目留言 'SUPER_CHAT_MESSAGE_DELETE': _make_msg_callback('_on_super_chat_delete', web_models.SuperChatDeleteMessage), + # 进入房间、关注主播等互动消息 + 'INTERACT_WORD': _make_msg_callback('_on_interact_word', web_models.InteractWordMessage), # # 开放平台消息 # - # 收到弹幕 + # 弹幕 'LIVE_OPEN_PLATFORM_DM': _make_msg_callback('_on_open_live_danmaku', open_models.DanmakuMessage), - # 有人送礼 + # 礼物 'LIVE_OPEN_PLATFORM_SEND_GIFT': _make_msg_callback('_on_open_live_gift', open_models.GiftMessage), - # 有人上舰 + # 上舰 'LIVE_OPEN_PLATFORM_GUARD': _make_msg_callback('_on_open_live_buy_guard', open_models.GuardBuyMessage), # 醒目留言 'LIVE_OPEN_PLATFORM_SUPER_CHAT': _make_msg_callback('_on_open_live_super_chat', open_models.SuperChatMessage), @@ -115,7 +121,6 @@ class BaseHandler(HandlerInterface): # 结束直播 'LIVE_OPEN_PLATFORM_LIVE_END': _make_msg_callback('_on_open_live_end_live', open_models.LiveEndMessage), } - """cmd -> 处理回调""" def handle(self, client: ws_base.WebSocketClientBase, command: dict): cmd = command.get('cmd', '') @@ -135,86 +140,58 @@ class BaseHandler(HandlerInterface): callback(self, client, command) def _on_heartbeat(self, client: ws_base.WebSocketClientBase, message: web_models.HeartbeatMessage): - """ - 收到心跳包 - """ + """收到心跳包""" def _on_danmaku(self, client: ws_base.WebSocketClientBase, message: web_models.DanmakuMessage): - """ - 收到弹幕 - """ + """弹幕""" def _on_gift(self, client: ws_base.WebSocketClientBase, message: web_models.GiftMessage): - """ - 收到礼物 - """ + """礼物""" def _on_buy_guard(self, client: ws_base.WebSocketClientBase, message: web_models.GuardBuyMessage): - """ - 有人上舰 - """ + """上舰""" + + def _on_user_toast_v2(self, client: ws_base.WebSocketClientBase, message: web_models.UserToastV2Message): + """另一个上舰消息""" def _on_super_chat(self, client: ws_base.WebSocketClientBase, message: web_models.SuperChatMessage): - """ - 醒目留言 - """ + """醒目留言""" - def _on_super_chat_delete( - self, client: ws_base.WebSocketClientBase, message: web_models.SuperChatDeleteMessage - ): - """ - 删除醒目留言 - """ + def _on_super_chat_delete(self, client: ws_base.WebSocketClientBase, message: web_models.SuperChatDeleteMessage): + """删除醒目留言""" + + def _on_interact_word(self, client: ws_base.WebSocketClientBase, message: web_models.InteractWordMessage): + """进入房间、关注主播等互动消息""" # # 开放平台消息 # def _on_open_live_danmaku(self, client: ws_base.WebSocketClientBase, message: open_models.DanmakuMessage): - """ - 收到弹幕 - """ + """弹幕""" def _on_open_live_gift(self, client: ws_base.WebSocketClientBase, message: open_models.GiftMessage): - """ - 收到礼物 - """ + """礼物""" def _on_open_live_buy_guard(self, client: ws_base.WebSocketClientBase, message: open_models.GuardBuyMessage): - """ - 有人上舰 - """ + """上舰""" - def _on_open_live_super_chat( - self, client: ws_base.WebSocketClientBase, message: open_models.SuperChatMessage - ): - """ - 醒目留言 - """ + def _on_open_live_super_chat(self, client: ws_base.WebSocketClientBase, message: open_models.SuperChatMessage): + """醒目留言""" def _on_open_live_super_chat_delete( self, client: ws_base.WebSocketClientBase, message: open_models.SuperChatDeleteMessage ): - """ - 删除醒目留言 - """ + """删除醒目留言""" def _on_open_live_like(self, client: ws_base.WebSocketClientBase, message: open_models.LikeMessage): - """ - 点赞 - """ + """点赞""" def _on_open_live_enter_room(self, client: ws_base.WebSocketClientBase, message: open_models.RoomEnterMessage): - """ - 进入房间 - """ + """进入房间""" def _on_open_live_start_live(self, client: ws_base.WebSocketClientBase, message: open_models.LiveStartMessage): - """ - 开始直播 - """ + """开始直播""" def _on_open_live_end_live(self, client: ws_base.WebSocketClientBase, message: open_models.LiveEndMessage): - """ - 结束直播 - """ + """结束直播""" diff --git a/blivedm/models/web.py b/blivedm/models/web.py index 1473f49..9aab924 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -276,7 +276,7 @@ class GuardBuyMessage: """用户名""" guard_level: int = 0 """舰队等级,0非舰队,1总督,2提督,3舰长""" - num: int = 0 + num: int = 0 # 可以理解为礼物数量? """数量""" price: int = 0 """单价金瓜子数""" @@ -304,6 +304,57 @@ class GuardBuyMessage: ) +@dataclasses.dataclass +class UserToastV2Message: + """ + 另一个上舰消息,包含的数据更多 + """ + + uid: int = 0 + """用户ID""" + username: str = '' + """用户名""" + guard_level: int = 0 + """舰队等级,0非舰队,1总督,2提督,3舰长""" + num: int = 0 # 可以理解为礼物数量? + """数量""" + price: int = 0 + """单价金瓜子数""" + unit: str = '' + """单位,根据开放平台的文档,正常单位为“月”,如为其他内容,无视`guard_num`以本字段内容为准,例如`*3天`""" + gift_id: int = 0 + """礼物ID""" + start_time: int = 0 + """开始时间戳,和结束时间戳相同""" + end_time: int = 0 + """结束时间戳,和开始时间戳相同""" + source: int = 0 + """猜测0是自己买的,2是别人送的,这个只影响是否播动画""" + toast_msg: str = '' + """提示信息("<%XXX%> 在主播XXX的直播间续费了舰长,今天是TA陪伴主播的第XXX天")""" + + @classmethod + def from_command(cls, data: dict): + sender_info = data['sender_uinfo'] + guard_info = data['guard_info'] + pay_info = data['pay_info'] + gift_info = data['gift_info'] + option = data['option'] + return cls( + uid=sender_info['uid'], + username=sender_info['base']['name'], + guard_level=guard_info['guard_level'], + num=pay_info['num'], + price=pay_info['price'], + unit=pay_info['unit'], + gift_id=gift_info['gift_id'], + start_time=guard_info['start_time'], + end_time=guard_info['end_time'], + source=option['source'], + toast_msg=data['toast_msg'], + ) + + @dataclasses.dataclass class SuperChatMessage: """ @@ -315,7 +366,7 @@ class SuperChatMessage: message: str = '' """消息""" message_trans: str = '' - """消息日文翻译(目前只出现在SUPER_CHAT_MESSAGE_JPN)""" + """消息日文翻译""" start_time: int = 0 """开始时间戳""" end_time: int = 0 @@ -388,3 +439,33 @@ class SuperChatDeleteMessage: return cls( ids=data['ids'], ) + + +@dataclasses.dataclass +class InteractWordMessage: + """ + 进入房间、关注主播等互动消息 + """ + + uid: int = 0 + """用户ID""" + username: str = '' + """用户名""" + face: str = '' + """用户头像URL""" + timestamp: int = 0 + """时间戳""" + msg_type: int = 0 + """`{1: '进入', 2: '关注了', 3: '分享了', 4: '特别关注了', 5: '互粉了', 6: '为主播点赞了'}`""" + + @classmethod + def from_command(cls, data: dict): + user_info = data['uinfo'] + user_base_info = user_info['base'] + return cls( + uid=user_info['uid'], + username=user_base_info['name'], + face=user_base_info['face'], + timestamp=data['timestamp'], + msg_type=data['msg_type'], + ) diff --git a/sample.py b/sample.py index 976e434..b2fe1c9 100644 --- a/sample.py +++ b/sample.py @@ -87,11 +87,10 @@ class MyHandler(blivedm.BaseHandler): # # 演示如何添加自定义回调 # _CMD_CALLBACK_DICT = blivedm.BaseHandler._CMD_CALLBACK_DICT.copy() # - # # 入场消息回调 - # def __interact_word_callback(self, client: blivedm.BLiveClient, command: dict): - # print(f"[{client.room_id}] INTERACT_WORD: self_type={type(self).__name__}, room_id={client.room_id}," - # f" uname={command['data']['uname']}") - # _CMD_CALLBACK_DICT['INTERACT_WORD'] = __interact_word_callback # noqa + # # 看过数消息回调 + # def __watched_change_callback(self, client: blivedm.BLiveClient, command: dict): + # print(f'[{client.room_id}] WATCHED_CHANGE: {command}') + # _CMD_CALLBACK_DICT['WATCHED_CHANGE'] = __watched_change_callback # noqa def _on_heartbeat(self, client: blivedm.BLiveClient, message: web_models.HeartbeatMessage): print(f'[{client.room_id}] 心跳') @@ -103,12 +102,19 @@ class MyHandler(blivedm.BaseHandler): print(f'[{client.room_id}] {message.uname} 赠送{message.gift_name}x{message.num}' f' ({message.coin_type}瓜子x{message.total_coin})') - def _on_buy_guard(self, client: blivedm.BLiveClient, message: web_models.GuardBuyMessage): - print(f'[{client.room_id}] {message.username} 购买{message.gift_name}') + # def _on_buy_guard(self, client: blivedm.BLiveClient, message: web_models.GuardBuyMessage): + # print(f'[{client.room_id}] {message.username} 上舰,guard_level={message.guard_level}') + + def _on_user_toast_v2(self, client: blivedm.BLiveClient, message: web_models.UserToastV2Message): + print(f'[{client.room_id}] {message.username} 上舰,guard_level={message.guard_level}') def _on_super_chat(self, client: blivedm.BLiveClient, message: web_models.SuperChatMessage): print(f'[{client.room_id}] 醒目留言 ¥{message.price} {message.uname}:{message.message}') + # def _on_interact_word(self, client: blivedm.BLiveClient, message: web_models.InteractWordMessage): + # if message.msg_type == 1: + # print(f'[{client.room_id}] {message.username} 进入房间') + if __name__ == '__main__': asyncio.run(main()) From 68eae6b6aa1ddde660d317fbccf82cd7edbecd98 Mon Sep 17 00:00:00 2001 From: John Smith Date: Mon, 23 Dec 2024 00:12:50 +0800 Subject: [PATCH 09/16] =?UTF-8?q?web=E6=8E=A5=E5=8F=A3=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=B7=BB=E5=8A=A0=E5=A4=B4=E5=83=8F=E5=92=8C?= =?UTF-8?q?extra?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/models/web.py | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/blivedm/models/web.py b/blivedm/models/web.py index 9aab924..c5b3622 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -67,6 +67,8 @@ class DanmakuMessage: """用户ID""" uname: str = '' """用户名""" + face: str = '' + """用户头像URL""" admin: int = 0 """是否房管""" vip: int = 0 @@ -110,6 +112,12 @@ class DanmakuMessage: @classmethod def from_command(cls, info: list): + mode_info = info[0][15] + try: + face = mode_info['user']['base']['face'] + except KeyError: + face = '' + if len(info[3]) != 0: medal_level = info[3][0] medal_name = info[3][1] @@ -144,12 +152,13 @@ class DanmakuMessage: dm_type=info[0][12], emoticon_options=info[0][13], voice_config=info[0][14], - mode_info=info[0][15], + mode_info=mode_info, msg=info[1], uid=info[2][0], uname=info[2][1], + face=face, admin=info[2][2], vip=info[2][3], svip=info[2][4], @@ -178,8 +187,11 @@ class DanmakuMessage: def emoticon_options_dict(self) -> dict: """ 示例: + + ``` {'bulge_display': 0, 'emoticon_unique': 'official_13', 'height': 60, 'in_player_area': 1, 'is_dynamic': 1, 'url': 'https://i0.hdslb.com/bfs/live/a98e35996545509188fe4d24bd1a56518ea5af48.png', 'width': 183} + ``` """ if isinstance(self.emoticon_options, dict): return self.emoticon_options @@ -192,11 +204,14 @@ class DanmakuMessage: def voice_config_dict(self) -> dict: """ 示例: + + ``` {'voice_url': 'https%3A%2F%2Fboss.hdslb.com%2Flive-dm-voice%2Fb5b26e48b556915cbf3312a59d3bb2561627725945.wav %3FX-Amz-Algorithm%3DAWS4-HMAC-SHA256%26X-Amz-Credential%3D2663ba902868f12f%252F20210731%252Fshjd%252Fs3%25 2Faws4_request%26X-Amz-Date%3D20210731T100545Z%26X-Amz-Expires%3D600000%26X-Amz-SignedHeaders%3Dhost%26 X-Amz-Signature%3D114e7cb5ac91c72e231c26d8ca211e53914722f36309b861a6409ffb20f07ab8', 'file_format': 'wav', 'text': '汤,下午好。', 'file_duration': 1} + ``` """ if isinstance(self.voice_config, dict): return self.voice_config @@ -205,6 +220,30 @@ class DanmakuMessage: except (json.JSONDecodeError, TypeError): return {} + @property + def extra_dict(self) -> dict: + """ + 示例: + + ``` + {'send_from_me': False, 'mode': 0, 'color': 14893055, 'dm_type': 0, 'font_size': 25, 'player_mode': 4, + 'show_player_type': 0, 'content': '确实', 'user_hash': '2904574201', 'emoticon_unique': '', 'bulge_display': 0, + 'recommend_score': 5, 'main_state_dm_color': '', 'objective_state_dm_color': '', 'direction': 0, + 'pk_direction': 0, 'quartet_direction': 0, 'anniversary_crowd': 0, 'yeah_space_type': '', 'yeah_space_url': '', + 'jump_to_url': '', 'space_type': '', 'space_url': '', 'animation': {}, 'emots': None, 'is_audited': False, + 'id_str': '6fa9959ab8feabcd1b337aa5066768334027', 'icon': None, 'show_reply': True, 'reply_mid': 0, + 'reply_uname': '', 'reply_uname_color': '', 'reply_is_mystery': False, 'reply_type_enum': 0, 'hit_combo': 0, + 'esports_jump_url': ''} + ``` + """ + try: + extra = self.mode_info['extra'] + if isinstance(extra, dict): + return extra + return json.loads(extra) + except (KeyError, json.JSONDecodeError, TypeError): + return {} + @dataclasses.dataclass class GiftMessage: From 41ee24c08c091edd3859b17cc56c54547b60caed Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 25 Dec 2024 23:02:57 +0800 Subject: [PATCH 10/16] =?UTF-8?q?web=E6=8E=A5=E5=8F=A3=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E5=BC=B9=E5=B9=95=E8=A7=A3=E6=9E=90=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/models/web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm/models/web.py b/blivedm/models/web.py index c5b3622..f103992 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -115,7 +115,7 @@ class DanmakuMessage: mode_info = info[0][15] try: face = mode_info['user']['base']['face'] - except KeyError: + except (TypeError, KeyError): face = '' if len(info[3]) != 0: From 0685fe1cfd06157db74fb203f17eb499748d3cd4 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 12 Jan 2025 19:23:17 +0800 Subject: [PATCH 11/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/models/open_live.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index 5d5c40b..4566b9d 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -450,8 +450,8 @@ class LiveStartMessage: """用户唯一标识""" timestamp: int = 0 """发生的时间戳""" - area_id: int = 0 - """开播二级分区ID""" + area_name: str = '' + """开播二级分区名""" title: str = '' """开播时刻,直播间的标题""" msg_id: str = '' # 官方文档表格里没列出这个字段,但是实际上有 @@ -463,7 +463,7 @@ class LiveStartMessage: room_id=data['room_id'], open_id=data['open_id'], timestamp=data['timestamp'], - area_id=int(data['area_id']), # 官方文档说是int64,实际上发的是str,之后会修复成int64 + area_name=data['area_name'], title=data['title'], msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是实际上有 ) @@ -481,8 +481,8 @@ class LiveEndMessage: """用户唯一标识""" timestamp: int = 0 """发生的时间戳""" - area_id: int = 0 - """开播二级分区ID""" + area_name: str = '' + """开播二级分区名""" title: str = '' """开播时刻,直播间的标题""" msg_id: str = '' # 官方文档表格里没列出这个字段,但是实际上有 @@ -494,7 +494,7 @@ class LiveEndMessage: room_id=data['room_id'], open_id=data['open_id'], timestamp=data['timestamp'], - area_id=int(data['area_id']), # 官方文档说是int64,实际上发的是str,之后会修复成int64 + area_name=data['area_name'], title=data['title'], msg_id=data.get('msg_id', ''), # 官方文档表格里没列出这个字段,但是实际上有 ) From 100ed8fdf970bb105a69df52d63ffd49a64b7cf6 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 19 Jan 2025 16:25:25 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/models/open_live.py | 12 ++++++++++++ blivedm/models/web.py | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/blivedm/models/open_live.py b/blivedm/models/open_live.py index 4566b9d..637e5c4 100644 --- a/blivedm/models/open_live.py +++ b/blivedm/models/open_live.py @@ -47,6 +47,14 @@ class DanmakuMessage: """表情包图片地址""" dm_type: int = 0 """弹幕类型 0:普通弹幕 1:表情包弹幕""" + glory_level: int = 0 + """直播荣耀等级""" + reply_open_id: str = '' + """被at用户唯一标识""" + reply_uname: str = '' + """被at的用户昵称""" + is_admin: int = 0 + """发送弹幕的用户是否是房管,取值范围0或1,取值为1时是房管""" @classmethod def from_command(cls, data: dict): @@ -64,6 +72,10 @@ class DanmakuMessage: fans_medal_level=data['fans_medal_level'], emoji_img_url=data['emoji_img_url'], dm_type=data['dm_type'], + glory_level=data['glory_level'], + reply_open_id=data['reply_open_id'], + reply_uname=data['reply_uname'], + is_admin=data['is_admin'], ) diff --git a/blivedm/models/web.py b/blivedm/models/web.py index f103992..5b64116 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -110,6 +110,9 @@ class DanmakuMessage: privilege_type: int = 0 """舰队类型,0非舰队,1总督,2提督,3舰长""" + wealth_level: int = 0 + """荣耀等级""" + @classmethod def from_command(cls, info: list): mode_info = info[0][15] @@ -181,6 +184,8 @@ class DanmakuMessage: title=title, privilege_type=info[7], + + wealth_level=info[16][0], ) @property From b204c17cc2e20370ea4f08f5e9c07db72a1671cd Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 29 Jan 2025 22:08:20 +0800 Subject: [PATCH 13/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B71.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm/__init__.py b/blivedm/__init__.py index a1c5a5b..ff588e6 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.0-dev' +__version__ = '1.1.0' from .handlers import * from .clients import * From d9ee5422d41653ddf7b280fe54e22653044081d4 Mon Sep 17 00:00:00 2001 From: John Smith Date: Sun, 9 Mar 2025 20:38:46 +0800 Subject: [PATCH 14/16] =?UTF-8?q?web=E6=B6=88=E6=81=AF=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=A4=BC=E7=89=A9=E5=9B=BE=E6=A0=87=E3=80=81?= =?UTF-8?q?=E5=8B=8B=E7=AB=A0=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/__init__.py | 2 +- blivedm/models/web.py | 59 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/blivedm/__init__.py b/blivedm/__init__.py index ff588e6..1009858 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.0' +__version__ = '1.1.1-dev' from .handlers import * from .clients import * diff --git a/blivedm/models/web.py b/blivedm/models/web.py index 5b64116..a36f834 100644 --- a/blivedm/models/web.py +++ b/blivedm/models/web.py @@ -82,7 +82,7 @@ class DanmakuMessage: uname_color: str = '' """用户名颜色""" - medal_level: str = '' + medal_level: int = 0 """勋章等级""" medal_name: str = '' """勋章名""" @@ -125,14 +125,14 @@ class DanmakuMessage: medal_level = info[3][0] medal_name = info[3][1] runame = info[3][2] - room_id = info[3][3] + medal_room_id = info[3][3] mcolor = info[3][4] special_medal = info[3][5] else: medal_level = 0 medal_name = '' runame = '' - room_id = 0 + medal_room_id = 0 mcolor = 0 special_medal = 0 @@ -172,7 +172,7 @@ class DanmakuMessage: medal_level=medal_level, medal_name=medal_name, runame=runame, - medal_room_id=room_id, + medal_room_id=medal_room_id, mcolor=mcolor, special_medal=special_medal, @@ -274,6 +274,8 @@ class GiftMessage: """礼物ID""" gift_type: int = 0 """礼物类型(未知)""" + gift_img_basic: str = '' + """图标URL""" action: str = '' """目前遇到的有'喂食'、'赠送'""" price: int = 0 @@ -286,9 +288,29 @@ class GiftMessage: """总瓜子数""" tid: str = '' """可能是事务ID,有时和rnd相同""" + medal_level: int = 0 + """勋章等级""" + medal_name: str = '' + """勋章名""" + medal_room_id: int = 0 + """勋章房间ID,未登录时是0""" + medal_ruid: int = 0 + """勋章主播ID""" @classmethod def from_command(cls, data: dict): + medal_info = data.get('medal_info', None) + if medal_info is not None: + medal_level = medal_info['medal_level'] + medal_name = medal_info['medal_name'] + medal_room_id = medal_info['anchor_roomid'] + medal_ruid = medal_info['target_id'] + else: + medal_level = 0 + medal_name = '' + medal_room_id = 0 + medal_ruid = 0 + return cls( gift_name=data['giftName'], num=data['num'], @@ -299,12 +321,17 @@ class GiftMessage: timestamp=data['timestamp'], gift_id=data['giftId'], gift_type=data['giftType'], + gift_img_basic=data['gift_info']['img_basic'], action=data['action'], price=data['price'], rnd=data['rnd'], coin_type=data['coin_type'], total_coin=data['total_coin'], tid=data['tid'], + medal_level=medal_level, + medal_name=medal_name, + medal_room_id=medal_room_id, + medal_ruid=medal_ruid, ) @@ -443,9 +470,29 @@ class SuperChatMessage: """背景图URL""" background_price_color: str = '' """背景价格颜色,'#rrggbb'""" + medal_level: int = 0 + """勋章等级""" + medal_name: str = '' + """勋章名""" + medal_room_id: int = 0 + """勋章房间ID""" + medal_ruid: int = 0 + """勋章主播ID""" @classmethod def from_command(cls, data: dict): + medal_info = data.get('medal_info', None) + if medal_info is not None: + medal_level = medal_info['medal_level'] + medal_name = medal_info['medal_name'] + medal_room_id = medal_info['anchor_roomid'] + medal_ruid = medal_info['target_id'] + else: + medal_level = 0 + medal_name = '' + medal_room_id = 0 + medal_ruid = 0 + return cls( price=data['price'], message=data['message'], @@ -466,6 +513,10 @@ class SuperChatMessage: background_icon=data['background_icon'], background_image=data['background_image'], background_price_color=data['background_price_color'], + medal_level=medal_level, + medal_name=medal_name, + medal_room_id=medal_room_id, + medal_ruid=medal_ruid, ) From 30fcd4bf6275d0921a6e38d53f95d3f85cae570f Mon Sep 17 00:00:00 2001 From: John Smith Date: Wed, 12 Mar 2025 21:48:52 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B71.1.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blivedm/__init__.py b/blivedm/__init__.py index 1009858..06ebc2c 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.1-dev' +__version__ = '1.1.1' from .handlers import * from .clients import * From a45ee8f6774064978ba062621475cb78d6e27df8 Mon Sep 17 00:00:00 2001 From: John Smith Date: Thu, 3 Apr 2025 19:48:26 +0800 Subject: [PATCH 16/16] =?UTF-8?q?web=E6=8E=A5=E5=8F=A3=E6=8D=A2=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E5=88=9D=E5=A7=8B=E5=8C=96=E6=88=BF=E9=97=B4=E7=9A=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blivedm/__init__.py | 2 +- blivedm/clients/web.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/blivedm/__init__.py b/blivedm/__init__.py index 06ebc2c..3dab646 100644 --- a/blivedm/__init__.py +++ b/blivedm/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -__version__ = '1.1.1' +__version__ = '1.1.2' from .handlers import * from .clients import * diff --git a/blivedm/clients/web.py b/blivedm/clients/web.py index a12c297..1beb3bb 100644 --- a/blivedm/clients/web.py +++ b/blivedm/clients/web.py @@ -17,7 +17,7 @@ logger = logging.getLogger('blivedm') UID_INIT_URL = 'https://api.bilibili.com/x/web-interface/nav' BUVID_INIT_URL = 'https://www.bilibili.com/' -ROOM_INIT_URL = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom' +ROOM_INIT_URL = 'https://api.live.bilibili.com/room/v1/Room/get_info' DANMAKU_SERVER_CONF_URL = 'https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo' DEFAULT_DANMAKU_SERVER_LIST = [ {'host': 'broadcastlv.chat.bilibili.com', 'port': 2243, 'wss_port': 443, 'ws_port': 2244} @@ -194,9 +194,8 @@ class BLiveClient(ws_base.WebSocketClientBase): return True def _parse_room_init(self, data): - room_info = data['room_info'] - self._room_id = room_info['room_id'] - self._room_owner_uid = room_info['uid'] + self._room_id = data['room_id'] + self._room_owner_uid = data['uid'] return True async def _init_host_server(self):