升级弹幕协议版本

This commit is contained in:
John Smith 2021-12-18 20:26:51 +08:00
parent 7ac4966c92
commit cfda838e01
4 changed files with 86 additions and 31 deletions

View File

@ -6,10 +6,10 @@ import json
import logging import logging
import ssl as ssl_ import ssl as ssl_
import struct import struct
import zlib
from typing import * from typing import *
import aiohttp import aiohttp
import brotli
from . import handlers from . import handlers
@ -423,9 +423,8 @@ class BLiveClient:
auth_params = { auth_params = {
'uid': self._uid, 'uid': self._uid,
'roomid': self._room_id, 'roomid': self._room_id,
'protover': 2, 'protover': 3,
'platform': 'web', 'platform': 'web',
'clientver': '1.14.3',
'type': 2 'type': 2
} }
if self._host_server_token is not None: if self._host_server_token is not None:
@ -531,9 +530,9 @@ class BLiveClient:
""" """
if header.operation == Operation.SEND_MSG_REPLY: if header.operation == Operation.SEND_MSG_REPLY:
# 业务消息 # 业务消息
if header.ver == ProtoVer.DEFLATE: if header.ver == ProtoVer.BROTLI:
# 压缩过的先解压,为了避免阻塞网络线程,放在其他线程执行 # 压缩过的先解压,为了避免阻塞网络线程,放在其他线程执行
body = await self._loop.run_in_executor(None, zlib.decompress, body) body = await self._loop.run_in_executor(None, brotli.decompress, body)
await self._parse_ws_message(body) await self._parse_ws_message(body)
elif header.ver == ProtoVer.NORMAL: elif header.ver == ProtoVer.NORMAL:
# 没压缩过的直接反序列化因为有万恶的GIL这里不能并行避免阻塞 # 没压缩过的直接反序列化因为有万恶的GIL这里不能并行避免阻塞

View File

@ -14,26 +14,29 @@ logger = logging.getLogger('blivedm')
# 常见可忽略的cmd # 常见可忽略的cmd
IGNORED_CMDS = ( IGNORED_CMDS = (
'INTERACT_WORD',
'ROOM_BANNER',
'ROOM_REAL_TIME_MESSAGE_UPDATE',
'NOTICE_MSG',
'COMBO_SEND', 'COMBO_SEND',
'COMBO_END',
'ENTRY_EFFECT', 'ENTRY_EFFECT',
'WELCOME_GUARD', 'HOT_RANK_CHANGED',
'WELCOME', 'HOT_RANK_CHANGED_V2',
'ROOM_RANK', 'INTERACT_WORD',
'ACTIVITY_BANNER_UPDATE_V2',
'PANEL',
'SUPER_CHAT_MESSAGE_JPN',
'USER_TOAST_MSG',
'ROOM_BLOCK_MSG',
'LIVE', 'LIVE',
'LIVE_INTERACTIVE_GAME',
'NOTICE_MSG',
'ONLINE_RANK_COUNT',
'ONLINE_RANK_TOP3',
'ONLINE_RANK_V2',
'PK_BATTLE_END',
'PK_BATTLE_FINAL_PROCESS',
'PK_BATTLE_PROCESS',
'PK_BATTLE_PROCESS_NEW',
'PK_BATTLE_SETTLE',
'PK_BATTLE_SETTLE_USER',
'PK_BATTLE_SETTLE_V2',
'PREPARING', 'PREPARING',
'room_admin_entrance', 'ROOM_REAL_TIME_MESSAGE_UPDATE',
'ROOM_ADMINS', 'STOP_LIVE_ROOM_LIST',
'ROOM_CHANGE', 'SUPER_CHAT_MESSAGE_JPN',
'WIDGET_BANNER',
) )
# 已打日志的未知cmd # 已打日志的未知cmd

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import json
from typing import * from typing import *
__all__ = ( __all__ = (
@ -39,10 +40,14 @@ class DanmakuMessage:
:param font_size: 字体尺寸 :param font_size: 字体尺寸
:param color: 颜色 :param color: 颜色
:param timestamp: 时间戳毫秒 :param timestamp: 时间戳毫秒
:param rnd: 随机数可能是去重用的 :param rnd: 随机数前端叫作弹幕ID可能是去重用的
:param uid_crc32: 用户ID文本的CRC32 :param uid_crc32: 用户ID文本的CRC32
:param msg_type: 是否礼物弹幕节奏风暴 :param msg_type: 是否礼物弹幕节奏风暴
:param bubble: 右侧评论栏气泡 :param bubble: 右侧评论栏气泡
:param dm_type: 弹幕类型0文本1表情2语音
:param emoticon_options: 表情参数
:param voice_config: 语音参数
:param mode_info: 一些附加参数
:param msg: 弹幕内容 :param msg: 弹幕内容
@ -82,6 +87,10 @@ class DanmakuMessage:
uid_crc32: str = None, uid_crc32: str = None,
msg_type: int = None, msg_type: int = None,
bubble: int = None, bubble: int = None,
dm_type: int = None,
emoticon_options: Union[dict, str] = None,
voice_config: Union[dict, str] = None,
mode_info: dict = None,
msg: str = None, msg: str = None,
@ -118,6 +127,10 @@ class DanmakuMessage:
self.uid_crc32: str = uid_crc32 self.uid_crc32: str = uid_crc32
self.msg_type: int = msg_type self.msg_type: int = msg_type
self.bubble: int = bubble self.bubble: int = bubble
self.dm_type: int = dm_type
self.emoticon_options: Union[dict, str] = emoticon_options
self.voice_config: Union[dict, str] = voice_config
self.mode_info: dict = mode_info
self.msg: str = msg self.msg: str = msg
@ -172,6 +185,10 @@ class DanmakuMessage:
uid_crc32=info[0][7], uid_crc32=info[0][7],
msg_type=info[0][9], msg_type=info[0][9],
bubble=info[0][10], bubble=info[0][10],
dm_type=info[0][12],
emoticon_options=info[0][13],
voice_config=info[0][14],
mode_info=info[0][15],
msg=info[1], msg=info[1],
@ -201,6 +218,37 @@ class DanmakuMessage:
privilege_type=info[7], privilege_type=info[7],
) )
@property
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
try:
return json.loads(self.emoticon_options)
except (json.JSONDecodeError, TypeError):
return {}
@property
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
try:
return json.loads(self.voice_config)
except (json.JSONDecodeError, TypeError):
return {}
class GiftMessage: class GiftMessage:
""" """
@ -217,9 +265,10 @@ class GiftMessage:
:param gift_type: 礼物类型未知 :param gift_type: 礼物类型未知
:param action: 目前遇到的有'喂食''赠送' :param action: 目前遇到的有'喂食''赠送'
:param price: 礼物单价瓜子数 :param price: 礼物单价瓜子数
:param rnd: 随机数可能是去重用的 :param rnd: 随机数可能是去重用的有时是时间戳+去重ID有时是UUID
:param coin_type: 瓜子类型'silver''gold' :param coin_type: 瓜子类型'silver''gold'1000金瓜子 = 1
:param total_coin: 总瓜子数 :param total_coin: 总瓜子数
:param tid: 可能是事务ID有时和rnd相同
""" """
def __init__( def __init__(
@ -238,6 +287,7 @@ class GiftMessage:
rnd: str = None, rnd: str = None,
coin_type: str = None, coin_type: str = None,
total_coin: int = None, total_coin: int = None,
tid: str = None,
): ):
self.gift_name = gift_name self.gift_name = gift_name
self.num = num self.num = num
@ -253,6 +303,7 @@ class GiftMessage:
self.rnd = rnd self.rnd = rnd
self.coin_type = coin_type self.coin_type = coin_type
self.total_coin = total_coin self.total_coin = total_coin
self.tid = tid
@classmethod @classmethod
def from_command(cls, data: dict): def from_command(cls, data: dict):
@ -271,6 +322,7 @@ class GiftMessage:
rnd=data['rnd'], rnd=data['rnd'],
coin_type=data['coin_type'], coin_type=data['coin_type'],
total_coin=data['total_coin'], total_coin=data['total_coin'],
tid=data['tid'],
) )
@ -285,8 +337,8 @@ class GuardBuyMessage:
:param price: 单价金瓜子数 :param price: 单价金瓜子数
:param gift_id: 礼物ID :param gift_id: 礼物ID
:param gift_name: 礼物名 :param gift_name: 礼物名
:param start_time: 开始时间戳和结束时间戳一样 :param start_time: 开始时间戳和结束时间戳相同
:param end_time: 结束时间戳和开始时间戳一样 :param end_time: 结束时间戳和开始时间戳相同
""" """
def __init__( def __init__(
@ -332,10 +384,10 @@ class SuperChatMessage:
:param price: 价格人民币 :param price: 价格人民币
:param message: 消息 :param message: 消息
:param message_jpn: 消息日文翻译目前只出现在SUPER_CHAT_MESSAGE_JPN :param message_trans: 消息日文翻译目前只出现在SUPER_CHAT_MESSAGE_JPN
:param start_time: 开始时间戳 :param start_time: 开始时间戳
:param end_time: 结束时间戳 :param end_time: 结束时间戳
:param time: 持续时间结束时间戳 - 开始时间戳 :param time: 剩余时间约等于 结束时间戳 - 开始时间戳
:param id_: str醒目留言ID删除时用 :param id_: str醒目留言ID删除时用
:param gift_id: 礼物ID :param gift_id: 礼物ID
:param gift_name: 礼物名 :param gift_name: 礼物名
@ -355,7 +407,7 @@ class SuperChatMessage:
self, self,
price: int = None, price: int = None,
message: str = None, message: str = None,
message_jpn: str = None, message_trans: str = None,
start_time: int = None, start_time: int = None,
end_time: int = None, end_time: int = None,
time: int = None, time: int = None,
@ -375,7 +427,7 @@ class SuperChatMessage:
): ):
self.price: int = price self.price: int = price
self.message: str = message self.message: str = message
self.message_jpn: str = message_jpn self.message_trans: str = message_trans
self.start_time: int = start_time self.start_time: int = start_time
self.end_time: int = end_time self.end_time: int = end_time
self.time: int = time self.time: int = time
@ -398,7 +450,7 @@ class SuperChatMessage:
return cls( return cls(
price=data['price'], price=data['price'],
message=data['message'], message=data['message'],
message_jpn=data['message_trans'], message_trans=data['message_trans'],
start_time=data['start_time'], start_time=data['start_time'],
end_time=data['end_time'], end_time=data['end_time'],
time=data['time'], time=data['time'],

View File

@ -1 +1,2 @@
aiohttp==3.7.4 aiohttp==3.7.4
Brotli==1.0.9