mirror of
https://github.com/xfgryujk/blivedm.git
synced 2024-12-26 12:50:13 +08:00
添加开放平台消息模型
This commit is contained in:
parent
ff766d68db
commit
74b9cdc100
@ -1,6 +1,6 @@
|
|||||||
# blivedm
|
# blivedm
|
||||||
|
|
||||||
Python获取bilibili直播弹幕的库,使用WebSocket协议
|
Python获取bilibili直播弹幕的库,使用WebSocket协议,支持web端和B站直播开放平台两种接口
|
||||||
|
|
||||||
[协议解释](https://blog.csdn.net/xfgryujk/article/details/80306776)(有点过时了,总体是没错的)
|
[协议解释](https://blog.csdn.net/xfgryujk/article/details/80306776)(有点过时了,总体是没错的)
|
||||||
|
|
||||||
@ -15,4 +15,4 @@ Python获取bilibili直播弹幕的库,使用WebSocket协议
|
|||||||
pip install -r requirements.txt
|
pip install -r requirements.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
3. 例程看[sample.py](./sample.py)
|
3. 例程看[sample.py](./sample.py)和[open_live_sample.py](./open_live_sample.py)
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from .models import *
|
|
||||||
from .handlers import *
|
from .handlers import *
|
||||||
from .clients import *
|
from .clients import *
|
||||||
|
@ -117,6 +117,13 @@ class BLiveClient(ws_base.WebSocketClientBase):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
async def _init_uid(self):
|
async def _init_uid(self):
|
||||||
|
cookies = self._session.cookie_jar.filter_cookies(yarl.URL(UID_INIT_URL))
|
||||||
|
sessdata_cookie = cookies.get('SESSDATA', None)
|
||||||
|
if sessdata_cookie is None or sessdata_cookie.value == '':
|
||||||
|
# cookie都没有,不用请求了
|
||||||
|
self._uid = 0
|
||||||
|
return True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with self._session.get(
|
async with self._session.get(
|
||||||
UID_INIT_URL,
|
UID_INIT_URL,
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
from typing import *
|
from typing import *
|
||||||
|
|
||||||
from .clients import ws_base
|
from .clients import ws_base
|
||||||
from . import models
|
from .models import web as web_models, open_live as open_models
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'HandlerInterface',
|
'HandlerInterface',
|
||||||
@ -12,7 +12,7 @@ __all__ = (
|
|||||||
|
|
||||||
logger = logging.getLogger('blivedm')
|
logger = logging.getLogger('blivedm')
|
||||||
|
|
||||||
IGNORED_CMDS = (
|
logged_unknown_cmds = {
|
||||||
'COMBO_SEND',
|
'COMBO_SEND',
|
||||||
'ENTRY_EFFECT',
|
'ENTRY_EFFECT',
|
||||||
'HOT_RANK_CHANGED',
|
'HOT_RANK_CHANGED',
|
||||||
@ -36,10 +36,7 @@ IGNORED_CMDS = (
|
|||||||
'STOP_LIVE_ROOM_LIST',
|
'STOP_LIVE_ROOM_LIST',
|
||||||
'SUPER_CHAT_MESSAGE_JPN',
|
'SUPER_CHAT_MESSAGE_JPN',
|
||||||
'WIDGET_BANNER',
|
'WIDGET_BANNER',
|
||||||
)
|
}
|
||||||
"""常见可忽略的cmd"""
|
|
||||||
|
|
||||||
logged_unknown_cmds = set()
|
|
||||||
"""已打日志的未知cmd"""
|
"""已打日志的未知cmd"""
|
||||||
|
|
||||||
|
|
||||||
@ -54,28 +51,22 @@ class HandlerInterface:
|
|||||||
# TODO 加个异常停止的回调
|
# TODO 加个异常停止的回调
|
||||||
|
|
||||||
|
|
||||||
|
def _make_msg_callback(method_name, message_cls):
|
||||||
|
def callback(self: 'BaseHandler', client: ws_base.WebSocketClientBase, command: dict):
|
||||||
|
method = getattr(self, method_name)
|
||||||
|
return method(client, message_cls.from_command(command['data']))
|
||||||
|
return callback
|
||||||
|
|
||||||
|
|
||||||
class BaseHandler(HandlerInterface):
|
class BaseHandler(HandlerInterface):
|
||||||
"""
|
"""
|
||||||
一个简单的消息处理器实现,带消息分发和消息类型转换。继承并重写_on_xxx方法即可实现自己的处理器
|
一个简单的消息处理器实现,带消息分发和消息类型转换。继承并重写_on_xxx方法即可实现自己的处理器
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __heartbeat_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
|
||||||
return self._on_heartbeat(client, models.HeartbeatMessage.from_command(command['data']))
|
|
||||||
|
|
||||||
def __danmu_msg_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
def __danmu_msg_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
||||||
return self._on_danmaku(client, models.DanmakuMessage.from_command(command['info'], command.get('dm_v2', '')))
|
return self._on_danmaku(
|
||||||
|
client, web_models.DanmakuMessage.from_command(command['info'], command.get('dm_v2', ''))
|
||||||
def __send_gift_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
)
|
||||||
return self._on_gift(client, models.GiftMessage.from_command(command['data']))
|
|
||||||
|
|
||||||
def __guard_buy_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
|
||||||
return self._on_buy_guard(client, models.GuardBuyMessage.from_command(command['data']))
|
|
||||||
|
|
||||||
def __super_chat_message_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
|
||||||
return self._on_super_chat(client, models.SuperChatMessage.from_command(command['data']))
|
|
||||||
|
|
||||||
def __super_chat_message_delete_callback(self, client: ws_base.WebSocketClientBase, command: dict):
|
|
||||||
return self._on_super_chat_delete(client, models.SuperChatDeleteMessage.from_command(command['data']))
|
|
||||||
|
|
||||||
_CMD_CALLBACK_DICT: Dict[
|
_CMD_CALLBACK_DICT: Dict[
|
||||||
str,
|
str,
|
||||||
@ -85,24 +76,39 @@ class BaseHandler(HandlerInterface):
|
|||||||
]]
|
]]
|
||||||
] = {
|
] = {
|
||||||
# 收到心跳包,这是blivedm自造的消息,原本的心跳包格式不一样
|
# 收到心跳包,这是blivedm自造的消息,原本的心跳包格式不一样
|
||||||
'_HEARTBEAT': __heartbeat_callback,
|
'_HEARTBEAT': _make_msg_callback('_on_heartbeat', web_models.HeartbeatMessage),
|
||||||
# 收到弹幕
|
# 收到弹幕
|
||||||
# go-common\app\service\live\live-dm\service\v1\send.go
|
# go-common\app\service\live\live-dm\service\v1\send.go
|
||||||
'DANMU_MSG': __danmu_msg_callback,
|
'DANMU_MSG': __danmu_msg_callback,
|
||||||
# 有人送礼
|
# 有人送礼
|
||||||
'SEND_GIFT': __send_gift_callback,
|
'SEND_GIFT': _make_msg_callback('_on_gift', web_models.GiftMessage),
|
||||||
# 有人上舰
|
# 有人上舰
|
||||||
'GUARD_BUY': __guard_buy_callback,
|
'GUARD_BUY': _make_msg_callback('_on_buy_guard', web_models.GuardBuyMessage),
|
||||||
# 醒目留言
|
# 醒目留言
|
||||||
'SUPER_CHAT_MESSAGE': __super_chat_message_callback,
|
'SUPER_CHAT_MESSAGE': _make_msg_callback('_on_super_chat', web_models.SuperChatMessage),
|
||||||
# 删除醒目留言
|
# 删除醒目留言
|
||||||
'SUPER_CHAT_MESSAGE_DELETE': __super_chat_message_delete_callback,
|
'SUPER_CHAT_MESSAGE_DELETE': _make_msg_callback('_on_super_chat_delete', web_models.SuperChatDeleteMessage),
|
||||||
|
|
||||||
|
#
|
||||||
|
# 开放平台消息
|
||||||
|
#
|
||||||
|
|
||||||
|
# 收到弹幕
|
||||||
|
'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),
|
||||||
|
# 删除醒目留言
|
||||||
|
'LIVE_OPEN_PLATFORM_SUPER_CHAT_DEL': _make_msg_callback(
|
||||||
|
'_on_open_live_super_chat_delete', open_models.SuperChatDeleteMessage
|
||||||
|
),
|
||||||
|
# 点赞
|
||||||
|
'LIVE_OPEN_PLATFORM_LIKE': _make_msg_callback('_on_open_live_like', open_models.LikeMessage),
|
||||||
}
|
}
|
||||||
"""cmd -> 处理回调"""
|
"""cmd -> 处理回调"""
|
||||||
# 忽略其他常见cmd
|
|
||||||
for cmd in IGNORED_CMDS:
|
|
||||||
_CMD_CALLBACK_DICT[cmd] = None
|
|
||||||
del cmd
|
|
||||||
|
|
||||||
async def handle(self, client: ws_base.WebSocketClientBase, command: dict):
|
async def handle(self, client: ws_base.WebSocketClientBase, command: dict):
|
||||||
cmd = command.get('cmd', '')
|
cmd = command.get('cmd', '')
|
||||||
@ -121,32 +127,72 @@ class BaseHandler(HandlerInterface):
|
|||||||
if callback is not None:
|
if callback is not None:
|
||||||
await callback(self, client, command)
|
await callback(self, client, command)
|
||||||
|
|
||||||
async def _on_heartbeat(self, client: ws_base.WebSocketClientBase, message: models.HeartbeatMessage):
|
async def _on_heartbeat(self, client: ws_base.WebSocketClientBase, message: web_models.HeartbeatMessage):
|
||||||
"""
|
"""
|
||||||
收到心跳包(人气值)
|
收到心跳包
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _on_danmaku(self, client: ws_base.WebSocketClientBase, message: models.DanmakuMessage):
|
async def _on_danmaku(self, client: ws_base.WebSocketClientBase, message: web_models.DanmakuMessage):
|
||||||
"""
|
"""
|
||||||
收到弹幕
|
收到弹幕
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _on_gift(self, client: ws_base.WebSocketClientBase, message: models.GiftMessage):
|
async def _on_gift(self, client: ws_base.WebSocketClientBase, message: web_models.GiftMessage):
|
||||||
"""
|
"""
|
||||||
收到礼物
|
收到礼物
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _on_buy_guard(self, client: ws_base.WebSocketClientBase, message: models.GuardBuyMessage):
|
async def _on_buy_guard(self, client: ws_base.WebSocketClientBase, message: web_models.GuardBuyMessage):
|
||||||
"""
|
"""
|
||||||
有人上舰
|
有人上舰
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _on_super_chat(self, client: ws_base.WebSocketClientBase, message: models.SuperChatMessage):
|
async def _on_super_chat(self, client: ws_base.WebSocketClientBase, message: web_models.SuperChatMessage):
|
||||||
"""
|
"""
|
||||||
醒目留言
|
醒目留言
|
||||||
"""
|
"""
|
||||||
|
|
||||||
async def _on_super_chat_delete(self, client: ws_base.WebSocketClientBase, message: models.SuperChatDeleteMessage):
|
async def _on_super_chat_delete(
|
||||||
|
self, client: ws_base.WebSocketClientBase, message: web_models.SuperChatDeleteMessage
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
删除醒目留言
|
删除醒目留言
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
#
|
||||||
|
# 开放平台消息
|
||||||
|
#
|
||||||
|
|
||||||
|
async def _on_open_live_danmaku(self, client: ws_base.WebSocketClientBase, message: open_models.DanmakuMessage):
|
||||||
|
"""
|
||||||
|
收到弹幕
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _on_open_live_gift(self, client: ws_base.WebSocketClientBase, message: open_models.GiftMessage):
|
||||||
|
"""
|
||||||
|
收到礼物
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _on_open_live_buy_guard(self, client: ws_base.WebSocketClientBase, message: open_models.GuardBuyMessage):
|
||||||
|
"""
|
||||||
|
有人上舰
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _on_open_live_super_chat(
|
||||||
|
self, client: ws_base.WebSocketClientBase, message: open_models.SuperChatMessage
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
醒目留言
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _on_open_live_super_chat_delete(
|
||||||
|
self, client: ws_base.WebSocketClientBase, message: open_models.SuperChatDeleteMessage
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
删除醒目留言
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _on_open_live_like(self, client: ws_base.WebSocketClientBase, message: open_models.LikeMessage):
|
||||||
|
"""
|
||||||
|
点赞
|
||||||
|
"""
|
||||||
|
@ -1,401 +1 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import base64
|
|
||||||
import binascii
|
|
||||||
import dataclasses
|
|
||||||
import json
|
|
||||||
from typing import *
|
|
||||||
|
|
||||||
from . import pb
|
|
||||||
|
|
||||||
__all__ = (
|
|
||||||
'HeartbeatMessage',
|
|
||||||
'DanmakuMessage',
|
|
||||||
'GiftMessage',
|
|
||||||
'GuardBuyMessage',
|
|
||||||
'SuperChatMessage',
|
|
||||||
'SuperChatDeleteMessage',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class HeartbeatMessage:
|
|
||||||
"""
|
|
||||||
心跳消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
popularity: int = 0
|
|
||||||
"""人气值,已废弃"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, data: dict):
|
|
||||||
return cls(
|
|
||||||
popularity=data['popularity'],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class DanmakuMessage:
|
|
||||||
"""
|
|
||||||
弹幕消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
mode: int = 0
|
|
||||||
"""弹幕显示模式(滚动、顶部、底部)"""
|
|
||||||
font_size: int = 0
|
|
||||||
"""字体尺寸"""
|
|
||||||
color: int = 0
|
|
||||||
"""颜色"""
|
|
||||||
timestamp: int = 0
|
|
||||||
"""时间戳(毫秒)"""
|
|
||||||
rnd: int = 0
|
|
||||||
"""随机数,前端叫作弹幕ID,可能是去重用的"""
|
|
||||||
uid_crc32: str = ''
|
|
||||||
"""用户ID文本的CRC32"""
|
|
||||||
msg_type: int = 0
|
|
||||||
"""是否礼物弹幕(节奏风暴)"""
|
|
||||||
bubble: int = 0
|
|
||||||
"""右侧评论栏气泡"""
|
|
||||||
dm_type: int = 0
|
|
||||||
"""弹幕类型,0文本,1表情,2语音"""
|
|
||||||
emoticon_options: Union[dict, str] = ''
|
|
||||||
"""表情参数"""
|
|
||||||
voice_config: Union[dict, str] = ''
|
|
||||||
"""语音参数"""
|
|
||||||
mode_info: dict = dataclasses.field(default_factory=dict)
|
|
||||||
"""一些附加参数"""
|
|
||||||
|
|
||||||
msg: str = ''
|
|
||||||
"""弹幕内容"""
|
|
||||||
|
|
||||||
uid: int = 0
|
|
||||||
"""用户ID"""
|
|
||||||
uname: str = ''
|
|
||||||
"""用户名"""
|
|
||||||
face: str = ''
|
|
||||||
"""用户头像URL"""
|
|
||||||
admin: int = 0
|
|
||||||
"""是否房管"""
|
|
||||||
vip: int = 0
|
|
||||||
"""是否月费老爷"""
|
|
||||||
svip: int = 0
|
|
||||||
"""是否年费老爷"""
|
|
||||||
urank: int = 0
|
|
||||||
"""用户身份,用来判断是否正式会员,猜测非正式会员为5000,正式会员为10000"""
|
|
||||||
mobile_verify: int = 0
|
|
||||||
"""是否绑定手机"""
|
|
||||||
uname_color: str = ''
|
|
||||||
"""用户名颜色"""
|
|
||||||
|
|
||||||
medal_level: str = ''
|
|
||||||
"""勋章等级"""
|
|
||||||
medal_name: str = ''
|
|
||||||
"""勋章名"""
|
|
||||||
runame: str = ''
|
|
||||||
"""勋章房间主播名"""
|
|
||||||
medal_room_id: int = 0
|
|
||||||
"""勋章房间ID"""
|
|
||||||
mcolor: int = 0
|
|
||||||
"""勋章颜色"""
|
|
||||||
special_medal: str = ''
|
|
||||||
"""特殊勋章"""
|
|
||||||
|
|
||||||
user_level: int = 0
|
|
||||||
"""用户等级"""
|
|
||||||
ulevel_color: int = 0
|
|
||||||
"""用户等级颜色"""
|
|
||||||
ulevel_rank: str = ''
|
|
||||||
"""用户等级排名,>50000时为'>50000'"""
|
|
||||||
|
|
||||||
old_title: str = ''
|
|
||||||
"""旧头衔"""
|
|
||||||
title: str = ''
|
|
||||||
"""头衔"""
|
|
||||||
|
|
||||||
privilege_type: int = 0
|
|
||||||
"""舰队类型,0非舰队,1总督,2提督,3舰长"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, info: list, dm_v2=''):
|
|
||||||
proto: Optional[pb.SimpleDm] = None
|
|
||||||
if dm_v2 != '':
|
|
||||||
try:
|
|
||||||
proto = pb.SimpleDm.loads(base64.b64decode(dm_v2))
|
|
||||||
except (binascii.Error, KeyError, TypeError, ValueError):
|
|
||||||
pass
|
|
||||||
if proto is not None:
|
|
||||||
face = proto.user.face
|
|
||||||
else:
|
|
||||||
face = ''
|
|
||||||
|
|
||||||
if len(info[3]) != 0:
|
|
||||||
medal_level = info[3][0]
|
|
||||||
medal_name = info[3][1]
|
|
||||||
runame = info[3][2]
|
|
||||||
room_id = info[3][3]
|
|
||||||
mcolor = info[3][4]
|
|
||||||
special_medal = info[3][5]
|
|
||||||
else:
|
|
||||||
medal_level = 0
|
|
||||||
medal_name = ''
|
|
||||||
runame = ''
|
|
||||||
room_id = 0
|
|
||||||
mcolor = 0
|
|
||||||
special_medal = 0
|
|
||||||
|
|
||||||
return cls(
|
|
||||||
mode=info[0][1],
|
|
||||||
font_size=info[0][2],
|
|
||||||
color=info[0][3],
|
|
||||||
timestamp=info[0][4],
|
|
||||||
rnd=info[0][5],
|
|
||||||
uid_crc32=info[0][7],
|
|
||||||
msg_type=info[0][9],
|
|
||||||
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],
|
|
||||||
|
|
||||||
uid=info[2][0],
|
|
||||||
uname=info[2][1],
|
|
||||||
face=face,
|
|
||||||
admin=info[2][2],
|
|
||||||
vip=info[2][3],
|
|
||||||
svip=info[2][4],
|
|
||||||
urank=info[2][5],
|
|
||||||
mobile_verify=info[2][6],
|
|
||||||
uname_color=info[2][7],
|
|
||||||
|
|
||||||
medal_level=medal_level,
|
|
||||||
medal_name=medal_name,
|
|
||||||
runame=runame,
|
|
||||||
medal_room_id=room_id,
|
|
||||||
mcolor=mcolor,
|
|
||||||
special_medal=special_medal,
|
|
||||||
|
|
||||||
user_level=info[4][0],
|
|
||||||
ulevel_color=info[4][2],
|
|
||||||
ulevel_rank=info[4][3],
|
|
||||||
|
|
||||||
old_title=info[5][0],
|
|
||||||
title=info[5][1],
|
|
||||||
|
|
||||||
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 {}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class GiftMessage:
|
|
||||||
"""
|
|
||||||
礼物消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
gift_name: str = ''
|
|
||||||
"""礼物名"""
|
|
||||||
num: int = 0
|
|
||||||
"""数量"""
|
|
||||||
uname: str = ''
|
|
||||||
"""用户名"""
|
|
||||||
face: str = ''
|
|
||||||
"""用户头像URL"""
|
|
||||||
guard_level: int = 0
|
|
||||||
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
|
||||||
uid: int = 0
|
|
||||||
"""用户ID"""
|
|
||||||
timestamp: int = 0
|
|
||||||
"""时间戳"""
|
|
||||||
gift_id: int = 0
|
|
||||||
"""礼物ID"""
|
|
||||||
gift_type: int = 0
|
|
||||||
"""礼物类型(未知)"""
|
|
||||||
action: str = ''
|
|
||||||
"""目前遇到的有'喂食'、'赠送'"""
|
|
||||||
price: int = 0
|
|
||||||
"""礼物单价瓜子数"""
|
|
||||||
rnd: str = ''
|
|
||||||
"""随机数,可能是去重用的。有时是时间戳+去重ID,有时是UUID"""
|
|
||||||
coin_type: str = ''
|
|
||||||
"""瓜子类型,'silver'或'gold',1000金瓜子 = 1元"""
|
|
||||||
total_coin: int = 0
|
|
||||||
"""总瓜子数"""
|
|
||||||
tid: str = ''
|
|
||||||
"""可能是事务ID,有时和rnd相同"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, data: dict):
|
|
||||||
return cls(
|
|
||||||
gift_name=data['giftName'],
|
|
||||||
num=data['num'],
|
|
||||||
uname=data['uname'],
|
|
||||||
face=data['face'],
|
|
||||||
guard_level=data['guard_level'],
|
|
||||||
uid=data['uid'],
|
|
||||||
timestamp=data['timestamp'],
|
|
||||||
gift_id=data['giftId'],
|
|
||||||
gift_type=data['giftType'],
|
|
||||||
action=data['action'],
|
|
||||||
price=data['price'],
|
|
||||||
rnd=data['rnd'],
|
|
||||||
coin_type=data['coin_type'],
|
|
||||||
total_coin=data['total_coin'],
|
|
||||||
tid=data['tid'],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class GuardBuyMessage:
|
|
||||||
"""
|
|
||||||
上舰消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
uid: int = 0
|
|
||||||
"""用户ID"""
|
|
||||||
username: str = ''
|
|
||||||
"""用户名"""
|
|
||||||
guard_level: int = 0
|
|
||||||
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
|
||||||
num: int = 0
|
|
||||||
"""数量"""
|
|
||||||
price: int = 0
|
|
||||||
"""单价金瓜子数"""
|
|
||||||
gift_id: int = 0
|
|
||||||
"""礼物ID"""
|
|
||||||
gift_name: str = ''
|
|
||||||
"""礼物名"""
|
|
||||||
start_time: int = 0
|
|
||||||
"""开始时间戳,和结束时间戳相同"""
|
|
||||||
end_time: int = 0
|
|
||||||
"""结束时间戳,和开始时间戳相同"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, data: dict):
|
|
||||||
return cls(
|
|
||||||
uid=data['uid'],
|
|
||||||
username=data['username'],
|
|
||||||
guard_level=data['guard_level'],
|
|
||||||
num=data['num'],
|
|
||||||
price=data['price'],
|
|
||||||
gift_id=data['gift_id'],
|
|
||||||
gift_name=data['gift_name'],
|
|
||||||
start_time=data['start_time'],
|
|
||||||
end_time=data['end_time'],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class SuperChatMessage:
|
|
||||||
"""
|
|
||||||
醒目留言消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
price: int = 0
|
|
||||||
"""价格(人民币)"""
|
|
||||||
message: str = ''
|
|
||||||
"""消息"""
|
|
||||||
message_trans: str = ''
|
|
||||||
"""消息日文翻译(目前只出现在SUPER_CHAT_MESSAGE_JPN)"""
|
|
||||||
start_time: int = 0
|
|
||||||
"""开始时间戳"""
|
|
||||||
end_time: int = 0
|
|
||||||
"""结束时间戳"""
|
|
||||||
time: int = 0
|
|
||||||
"""剩余时间(约等于 结束时间戳 - 开始时间戳)"""
|
|
||||||
id: int = 0
|
|
||||||
"""醒目留言ID,删除时用"""
|
|
||||||
gift_id: int = 0
|
|
||||||
"""礼物ID"""
|
|
||||||
gift_name: str = ''
|
|
||||||
"""礼物名"""
|
|
||||||
uid: int = 0
|
|
||||||
"""用户ID"""
|
|
||||||
uname: str = ''
|
|
||||||
"""用户名"""
|
|
||||||
face: str = ''
|
|
||||||
"""用户头像URL"""
|
|
||||||
guard_level: int = 0
|
|
||||||
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
|
||||||
user_level: int = 0
|
|
||||||
"""用户等级"""
|
|
||||||
background_bottom_color: str = ''
|
|
||||||
"""底部背景色,'#rrggbb'"""
|
|
||||||
background_color: str = ''
|
|
||||||
"""背景色,'#rrggbb'"""
|
|
||||||
background_icon: str = ''
|
|
||||||
"""背景图标"""
|
|
||||||
background_image: str = ''
|
|
||||||
"""背景图URL"""
|
|
||||||
background_price_color: str = ''
|
|
||||||
"""背景价格颜色,'#rrggbb'"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, data: dict):
|
|
||||||
return cls(
|
|
||||||
price=data['price'],
|
|
||||||
message=data['message'],
|
|
||||||
message_trans=data['message_trans'],
|
|
||||||
start_time=data['start_time'],
|
|
||||||
end_time=data['end_time'],
|
|
||||||
time=data['time'],
|
|
||||||
id=data['id'],
|
|
||||||
gift_id=data['gift']['gift_id'],
|
|
||||||
gift_name=data['gift']['gift_name'],
|
|
||||||
uid=data['uid'],
|
|
||||||
uname=data['user_info']['uname'],
|
|
||||||
face=data['user_info']['face'],
|
|
||||||
guard_level=data['user_info']['guard_level'],
|
|
||||||
user_level=data['user_info']['user_level'],
|
|
||||||
background_bottom_color=data['background_bottom_color'],
|
|
||||||
background_color=data['background_color'],
|
|
||||||
background_icon=data['background_icon'],
|
|
||||||
background_image=data['background_image'],
|
|
||||||
background_price_color=data['background_price_color'],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass
|
|
||||||
class SuperChatDeleteMessage:
|
|
||||||
"""
|
|
||||||
删除醒目留言消息
|
|
||||||
"""
|
|
||||||
|
|
||||||
ids: List[int] = dataclasses.field(default_factory=list)
|
|
||||||
"""醒目留言ID数组"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_command(cls, data: dict):
|
|
||||||
return cls(
|
|
||||||
ids=data['ids'],
|
|
||||||
)
|
|
||||||
|
371
blivedm/models/open_live.py
Normal file
371
blivedm/models/open_live.py
Normal file
@ -0,0 +1,371 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import dataclasses
|
||||||
|
from typing import *
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'DanmakuMessage',
|
||||||
|
'GiftMessage',
|
||||||
|
'GuardBuyMessage',
|
||||||
|
'SuperChatMessage',
|
||||||
|
'SuperChatDeleteMessage',
|
||||||
|
'LikeMessage',
|
||||||
|
)
|
||||||
|
|
||||||
|
# 注释都是复制自官方文档的,看不懂的话问B站
|
||||||
|
# https://open-live.bilibili.com/document/f9ce25be-312e-1f4a-85fd-fef21f1637f8
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DanmakuMessage:
|
||||||
|
"""
|
||||||
|
弹幕消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
uname: str = ''
|
||||||
|
"""用户昵称"""
|
||||||
|
uid: int = 0
|
||||||
|
"""用户UID"""
|
||||||
|
uface: str = ''
|
||||||
|
"""用户头像"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""弹幕发送时间秒级时间戳"""
|
||||||
|
room_id: int = 0
|
||||||
|
"""弹幕接收的直播间"""
|
||||||
|
msg: str = ''
|
||||||
|
"""弹幕内容"""
|
||||||
|
msg_id: str = ''
|
||||||
|
"""消息唯一id"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""对应房间大航海等级"""
|
||||||
|
fans_medal_wearing_status: bool = False
|
||||||
|
"""该房间粉丝勋章佩戴情况"""
|
||||||
|
fans_medal_name: str = ''
|
||||||
|
"""粉丝勋章名"""
|
||||||
|
fans_medal_level: int = 0
|
||||||
|
"""对应房间勋章信息"""
|
||||||
|
emoji_img_url: str = ''
|
||||||
|
"""表情包图片地址"""
|
||||||
|
dm_type: int = 0
|
||||||
|
"""弹幕类型 0:普通弹幕 1:表情包弹幕"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
uname=data['uname'],
|
||||||
|
uid=data['uid'],
|
||||||
|
uface=data['uface'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
room_id=data['room_id'],
|
||||||
|
msg=data['msg'],
|
||||||
|
msg_id=data['msg_id'],
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
fans_medal_wearing_status=data['fans_medal_wearing_status'],
|
||||||
|
fans_medal_name=data['fans_medal_name'],
|
||||||
|
fans_medal_level=data['fans_medal_level'],
|
||||||
|
emoji_img_url=data['emoji_img_url'],
|
||||||
|
dm_type=data['dm_type'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class AnchorInfo:
|
||||||
|
"""
|
||||||
|
主播信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
uid: int = 0
|
||||||
|
"""收礼主播uid"""
|
||||||
|
uname: str = ''
|
||||||
|
"""收礼主播昵称"""
|
||||||
|
uface: str = ''
|
||||||
|
"""收礼主播头像"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
uid=data['uid'],
|
||||||
|
uname=data['uname'],
|
||||||
|
uface=data['uface'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class ComboInfo:
|
||||||
|
"""
|
||||||
|
连击信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
combo_base_num: int = 0
|
||||||
|
"""每次连击赠送的道具数量"""
|
||||||
|
combo_count: int = 0
|
||||||
|
"""连击次数"""
|
||||||
|
combo_id: str = ''
|
||||||
|
"""连击id"""
|
||||||
|
combo_timeout: int = 0
|
||||||
|
"""连击有效期秒"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
combo_base_num=data['combo_base_num'],
|
||||||
|
combo_count=data['combo_count'],
|
||||||
|
combo_id=data['combo_id'],
|
||||||
|
combo_timeout=data['combo_timeout'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GiftMessage:
|
||||||
|
"""
|
||||||
|
礼物消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
room_id: int = 0
|
||||||
|
"""房间号"""
|
||||||
|
uid: int = 0
|
||||||
|
"""送礼用户UID"""
|
||||||
|
uname: str = ''
|
||||||
|
"""送礼用户昵称"""
|
||||||
|
uface: str = ''
|
||||||
|
"""送礼用户头像"""
|
||||||
|
gift_id: int = 0
|
||||||
|
"""道具id(盲盒:爆出道具id)"""
|
||||||
|
gift_name: str = ''
|
||||||
|
"""道具名(盲盒:爆出道具名)"""
|
||||||
|
gift_num: int = 0
|
||||||
|
"""赠送道具数量"""
|
||||||
|
price: int = 0
|
||||||
|
"""支付金额(1000 = 1元 = 10电池),盲盒:爆出道具的价值"""
|
||||||
|
paid: bool = False
|
||||||
|
"""是否是付费道具"""
|
||||||
|
fans_medal_level: int = 0
|
||||||
|
"""实际送礼人的勋章信息"""
|
||||||
|
fans_medal_name: str = ''
|
||||||
|
"""粉丝勋章名"""
|
||||||
|
fans_medal_wearing_status: bool = False
|
||||||
|
"""该房间粉丝勋章佩戴情况"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""大航海等级"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""收礼时间秒级时间戳"""
|
||||||
|
anchor_info: AnchorInfo = dataclasses.field(default_factory=AnchorInfo)
|
||||||
|
"""主播信息"""
|
||||||
|
msg_id: str = ''
|
||||||
|
"""消息唯一id"""
|
||||||
|
gift_icon: str = ''
|
||||||
|
"""道具icon"""
|
||||||
|
combo_gift: bool = False
|
||||||
|
"""是否是combo道具"""
|
||||||
|
combo_info: ComboInfo = dataclasses.field(default_factory=ComboInfo)
|
||||||
|
"""连击信息"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
room_id=data['room_id'],
|
||||||
|
uid=data['uid'],
|
||||||
|
uname=data['uname'],
|
||||||
|
uface=data['uface'],
|
||||||
|
gift_id=data['gift_id'],
|
||||||
|
gift_name=data['gift_name'],
|
||||||
|
gift_num=data['gift_num'],
|
||||||
|
price=data['price'],
|
||||||
|
paid=data['paid'],
|
||||||
|
fans_medal_level=data['fans_medal_level'],
|
||||||
|
fans_medal_name=data['fans_medal_name'],
|
||||||
|
fans_medal_wearing_status=data['fans_medal_wearing_status'],
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
anchor_info=AnchorInfo.from_dict(data['anchor_info']),
|
||||||
|
msg_id=data['msg_id'],
|
||||||
|
gift_icon=data['gift_icon'],
|
||||||
|
combo_gift=data['combo_gift'],
|
||||||
|
combo_info=ComboInfo.from_dict(data['combo_info']),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class UserInfo:
|
||||||
|
"""
|
||||||
|
用户信息
|
||||||
|
"""
|
||||||
|
|
||||||
|
uid: int = 0
|
||||||
|
"""用户uid"""
|
||||||
|
uname: str = ''
|
||||||
|
"""用户昵称"""
|
||||||
|
uface: str = ''
|
||||||
|
"""用户头像"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_dict(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
uid=data['uid'],
|
||||||
|
uname=data['uname'],
|
||||||
|
uface=data['uface'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GuardBuyMessage:
|
||||||
|
"""
|
||||||
|
上舰消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
user_info: UserInfo = dataclasses.field(default_factory=UserInfo)
|
||||||
|
"""用户信息"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""大航海等级"""
|
||||||
|
guard_num: int = 0
|
||||||
|
"""大航海数量"""
|
||||||
|
guard_unit: str = ''
|
||||||
|
"""大航海单位"""
|
||||||
|
fans_medal_level: int = 0
|
||||||
|
"""粉丝勋章等级"""
|
||||||
|
fans_medal_name: str = ''
|
||||||
|
"""粉丝勋章名"""
|
||||||
|
fans_medal_wearing_status: bool = False
|
||||||
|
"""该房间粉丝勋章佩戴情况"""
|
||||||
|
room_id: int = 0
|
||||||
|
"""房间号"""
|
||||||
|
msg_id: str = ''
|
||||||
|
"""消息唯一id"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""上舰时间秒级时间戳"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
user_info=UserInfo.from_dict(data['user_info']),
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
guard_num=data['guard_num'],
|
||||||
|
guard_unit=data['guard_unit'],
|
||||||
|
fans_medal_level=data['fans_medal_level'],
|
||||||
|
fans_medal_name=data['fans_medal_name'],
|
||||||
|
fans_medal_wearing_status=data['fans_medal_wearing_status'],
|
||||||
|
room_id=data['room_id'],
|
||||||
|
msg_id=data['msg_id'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SuperChatMessage:
|
||||||
|
"""
|
||||||
|
醒目留言消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
room_id: int = 0
|
||||||
|
"""直播间id"""
|
||||||
|
uid: int = 0
|
||||||
|
"""购买用户UID"""
|
||||||
|
uname: str = ''
|
||||||
|
"""购买的用户昵称"""
|
||||||
|
uface: str = ''
|
||||||
|
"""购买用户头像"""
|
||||||
|
message_id: int = 0
|
||||||
|
"""留言id(风控场景下撤回留言需要)"""
|
||||||
|
message: str = ''
|
||||||
|
"""留言内容"""
|
||||||
|
rmb: int = 0
|
||||||
|
"""支付金额(元)"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""赠送时间秒级"""
|
||||||
|
start_time: int = 0
|
||||||
|
"""生效开始时间"""
|
||||||
|
end_time: int = 0
|
||||||
|
"""生效结束时间"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""对应房间大航海等级"""
|
||||||
|
fans_medal_level: int = 0
|
||||||
|
"""对应房间勋章信息"""
|
||||||
|
fans_medal_name: str = ''
|
||||||
|
"""对应房间勋章名字"""
|
||||||
|
fans_medal_wearing_status: bool = False
|
||||||
|
"""该房间粉丝勋章佩戴情况"""
|
||||||
|
msg_id: str = ''
|
||||||
|
"""消息唯一id"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
room_id=data['room_id'],
|
||||||
|
uid=data['uid'],
|
||||||
|
uname=data['uname'],
|
||||||
|
uface=data['uface'],
|
||||||
|
message_id=data['message_id'],
|
||||||
|
message=data['message'],
|
||||||
|
rmb=data['rmb'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
start_time=data['start_time'],
|
||||||
|
end_time=data['end_time'],
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
fans_medal_level=data['fans_medal_level'],
|
||||||
|
fans_medal_name=data['fans_medal_name'],
|
||||||
|
fans_medal_wearing_status=data['fans_medal_wearing_status'],
|
||||||
|
msg_id=data['msg_id'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SuperChatDeleteMessage:
|
||||||
|
"""
|
||||||
|
删除醒目留言消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
room_id: int = 0
|
||||||
|
"""直播间id"""
|
||||||
|
message_ids: List[int] = dataclasses.field(default_factory=list)
|
||||||
|
"""留言id"""
|
||||||
|
msg_id: str = ''
|
||||||
|
"""消息唯一id"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
room_id=data['room_id'],
|
||||||
|
message_ids=data['message_ids'],
|
||||||
|
msg_id=data['msg_id'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class LikeMessage:
|
||||||
|
"""
|
||||||
|
点赞消息
|
||||||
|
|
||||||
|
请注意:用户端每分钟触发若干次的情况下只会推送一次该消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
uname: str = ''
|
||||||
|
"""用户昵称"""
|
||||||
|
uid: int = 0
|
||||||
|
"""用户UID"""
|
||||||
|
uface: str = ''
|
||||||
|
"""用户头像"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""时间秒级时间戳"""
|
||||||
|
room_id: int = 0
|
||||||
|
"""发生的直播间"""
|
||||||
|
like_text: str = ''
|
||||||
|
"""点赞文案(“xxx点赞了”)"""
|
||||||
|
fans_medal_wearing_status: bool = False
|
||||||
|
"""该房间粉丝勋章佩戴情况"""
|
||||||
|
fans_medal_name: str = ''
|
||||||
|
"""粉丝勋章名"""
|
||||||
|
fans_medal_level: int = 0
|
||||||
|
"""对应房间勋章信息"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
uname=data['uname'],
|
||||||
|
uid=data['uid'],
|
||||||
|
uface=data['uface'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
room_id=data['room_id'],
|
||||||
|
like_text=data['like_text'],
|
||||||
|
fans_medal_wearing_status=data['fans_medal_wearing_status'],
|
||||||
|
fans_medal_name=data['fans_medal_name'],
|
||||||
|
fans_medal_level=data['fans_medal_level'],
|
||||||
|
)
|
401
blivedm/models/web.py
Normal file
401
blivedm/models/web.py
Normal file
@ -0,0 +1,401 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import base64
|
||||||
|
import binascii
|
||||||
|
import dataclasses
|
||||||
|
import json
|
||||||
|
from typing import *
|
||||||
|
|
||||||
|
from . import pb
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
'HeartbeatMessage',
|
||||||
|
'DanmakuMessage',
|
||||||
|
'GiftMessage',
|
||||||
|
'GuardBuyMessage',
|
||||||
|
'SuperChatMessage',
|
||||||
|
'SuperChatDeleteMessage',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class HeartbeatMessage:
|
||||||
|
"""
|
||||||
|
心跳消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
popularity: int = 0
|
||||||
|
"""人气值,已废弃"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
popularity=data['popularity'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class DanmakuMessage:
|
||||||
|
"""
|
||||||
|
弹幕消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
mode: int = 0
|
||||||
|
"""弹幕显示模式(滚动、顶部、底部)"""
|
||||||
|
font_size: int = 0
|
||||||
|
"""字体尺寸"""
|
||||||
|
color: int = 0
|
||||||
|
"""颜色"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""时间戳(毫秒)"""
|
||||||
|
rnd: int = 0
|
||||||
|
"""随机数,前端叫作弹幕ID,可能是去重用的"""
|
||||||
|
uid_crc32: str = ''
|
||||||
|
"""用户ID文本的CRC32"""
|
||||||
|
msg_type: int = 0
|
||||||
|
"""是否礼物弹幕(节奏风暴)"""
|
||||||
|
bubble: int = 0
|
||||||
|
"""右侧评论栏气泡"""
|
||||||
|
dm_type: int = 0
|
||||||
|
"""弹幕类型,0文本,1表情,2语音"""
|
||||||
|
emoticon_options: Union[dict, str] = ''
|
||||||
|
"""表情参数"""
|
||||||
|
voice_config: Union[dict, str] = ''
|
||||||
|
"""语音参数"""
|
||||||
|
mode_info: dict = dataclasses.field(default_factory=dict)
|
||||||
|
"""一些附加参数"""
|
||||||
|
|
||||||
|
msg: str = ''
|
||||||
|
"""弹幕内容"""
|
||||||
|
|
||||||
|
uid: int = 0
|
||||||
|
"""用户ID"""
|
||||||
|
uname: str = ''
|
||||||
|
"""用户名"""
|
||||||
|
face: str = ''
|
||||||
|
"""用户头像URL"""
|
||||||
|
admin: int = 0
|
||||||
|
"""是否房管"""
|
||||||
|
vip: int = 0
|
||||||
|
"""是否月费老爷"""
|
||||||
|
svip: int = 0
|
||||||
|
"""是否年费老爷"""
|
||||||
|
urank: int = 0
|
||||||
|
"""用户身份,用来判断是否正式会员,猜测非正式会员为5000,正式会员为10000"""
|
||||||
|
mobile_verify: int = 0
|
||||||
|
"""是否绑定手机"""
|
||||||
|
uname_color: str = ''
|
||||||
|
"""用户名颜色"""
|
||||||
|
|
||||||
|
medal_level: str = ''
|
||||||
|
"""勋章等级"""
|
||||||
|
medal_name: str = ''
|
||||||
|
"""勋章名"""
|
||||||
|
runame: str = ''
|
||||||
|
"""勋章房间主播名"""
|
||||||
|
medal_room_id: int = 0
|
||||||
|
"""勋章房间ID"""
|
||||||
|
mcolor: int = 0
|
||||||
|
"""勋章颜色"""
|
||||||
|
special_medal: str = ''
|
||||||
|
"""特殊勋章"""
|
||||||
|
|
||||||
|
user_level: int = 0
|
||||||
|
"""用户等级"""
|
||||||
|
ulevel_color: int = 0
|
||||||
|
"""用户等级颜色"""
|
||||||
|
ulevel_rank: str = ''
|
||||||
|
"""用户等级排名,>50000时为'>50000'"""
|
||||||
|
|
||||||
|
old_title: str = ''
|
||||||
|
"""旧头衔"""
|
||||||
|
title: str = ''
|
||||||
|
"""头衔"""
|
||||||
|
|
||||||
|
privilege_type: int = 0
|
||||||
|
"""舰队类型,0非舰队,1总督,2提督,3舰长"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, info: list, dm_v2=''):
|
||||||
|
proto: Optional[pb.SimpleDm] = None
|
||||||
|
if dm_v2 != '':
|
||||||
|
try:
|
||||||
|
proto = pb.SimpleDm.loads(base64.b64decode(dm_v2))
|
||||||
|
except (binascii.Error, KeyError, TypeError, ValueError):
|
||||||
|
pass
|
||||||
|
if proto is not None:
|
||||||
|
face = proto.user.face
|
||||||
|
else:
|
||||||
|
face = ''
|
||||||
|
|
||||||
|
if len(info[3]) != 0:
|
||||||
|
medal_level = info[3][0]
|
||||||
|
medal_name = info[3][1]
|
||||||
|
runame = info[3][2]
|
||||||
|
room_id = info[3][3]
|
||||||
|
mcolor = info[3][4]
|
||||||
|
special_medal = info[3][5]
|
||||||
|
else:
|
||||||
|
medal_level = 0
|
||||||
|
medal_name = ''
|
||||||
|
runame = ''
|
||||||
|
room_id = 0
|
||||||
|
mcolor = 0
|
||||||
|
special_medal = 0
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
mode=info[0][1],
|
||||||
|
font_size=info[0][2],
|
||||||
|
color=info[0][3],
|
||||||
|
timestamp=info[0][4],
|
||||||
|
rnd=info[0][5],
|
||||||
|
uid_crc32=info[0][7],
|
||||||
|
msg_type=info[0][9],
|
||||||
|
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],
|
||||||
|
|
||||||
|
uid=info[2][0],
|
||||||
|
uname=info[2][1],
|
||||||
|
face=face,
|
||||||
|
admin=info[2][2],
|
||||||
|
vip=info[2][3],
|
||||||
|
svip=info[2][4],
|
||||||
|
urank=info[2][5],
|
||||||
|
mobile_verify=info[2][6],
|
||||||
|
uname_color=info[2][7],
|
||||||
|
|
||||||
|
medal_level=medal_level,
|
||||||
|
medal_name=medal_name,
|
||||||
|
runame=runame,
|
||||||
|
medal_room_id=room_id,
|
||||||
|
mcolor=mcolor,
|
||||||
|
special_medal=special_medal,
|
||||||
|
|
||||||
|
user_level=info[4][0],
|
||||||
|
ulevel_color=info[4][2],
|
||||||
|
ulevel_rank=info[4][3],
|
||||||
|
|
||||||
|
old_title=info[5][0],
|
||||||
|
title=info[5][1],
|
||||||
|
|
||||||
|
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 {}
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GiftMessage:
|
||||||
|
"""
|
||||||
|
礼物消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
gift_name: str = ''
|
||||||
|
"""礼物名"""
|
||||||
|
num: int = 0
|
||||||
|
"""数量"""
|
||||||
|
uname: str = ''
|
||||||
|
"""用户名"""
|
||||||
|
face: str = ''
|
||||||
|
"""用户头像URL"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
||||||
|
uid: int = 0
|
||||||
|
"""用户ID"""
|
||||||
|
timestamp: int = 0
|
||||||
|
"""时间戳"""
|
||||||
|
gift_id: int = 0
|
||||||
|
"""礼物ID"""
|
||||||
|
gift_type: int = 0
|
||||||
|
"""礼物类型(未知)"""
|
||||||
|
action: str = ''
|
||||||
|
"""目前遇到的有'喂食'、'赠送'"""
|
||||||
|
price: int = 0
|
||||||
|
"""礼物单价瓜子数"""
|
||||||
|
rnd: str = ''
|
||||||
|
"""随机数,可能是去重用的。有时是时间戳+去重ID,有时是UUID"""
|
||||||
|
coin_type: str = ''
|
||||||
|
"""瓜子类型,'silver'或'gold',1000金瓜子 = 1元"""
|
||||||
|
total_coin: int = 0
|
||||||
|
"""总瓜子数"""
|
||||||
|
tid: str = ''
|
||||||
|
"""可能是事务ID,有时和rnd相同"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
gift_name=data['giftName'],
|
||||||
|
num=data['num'],
|
||||||
|
uname=data['uname'],
|
||||||
|
face=data['face'],
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
uid=data['uid'],
|
||||||
|
timestamp=data['timestamp'],
|
||||||
|
gift_id=data['giftId'],
|
||||||
|
gift_type=data['giftType'],
|
||||||
|
action=data['action'],
|
||||||
|
price=data['price'],
|
||||||
|
rnd=data['rnd'],
|
||||||
|
coin_type=data['coin_type'],
|
||||||
|
total_coin=data['total_coin'],
|
||||||
|
tid=data['tid'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class GuardBuyMessage:
|
||||||
|
"""
|
||||||
|
上舰消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
uid: int = 0
|
||||||
|
"""用户ID"""
|
||||||
|
username: str = ''
|
||||||
|
"""用户名"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
||||||
|
num: int = 0
|
||||||
|
"""数量"""
|
||||||
|
price: int = 0
|
||||||
|
"""单价金瓜子数"""
|
||||||
|
gift_id: int = 0
|
||||||
|
"""礼物ID"""
|
||||||
|
gift_name: str = ''
|
||||||
|
"""礼物名"""
|
||||||
|
start_time: int = 0
|
||||||
|
"""开始时间戳,和结束时间戳相同"""
|
||||||
|
end_time: int = 0
|
||||||
|
"""结束时间戳,和开始时间戳相同"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
uid=data['uid'],
|
||||||
|
username=data['username'],
|
||||||
|
guard_level=data['guard_level'],
|
||||||
|
num=data['num'],
|
||||||
|
price=data['price'],
|
||||||
|
gift_id=data['gift_id'],
|
||||||
|
gift_name=data['gift_name'],
|
||||||
|
start_time=data['start_time'],
|
||||||
|
end_time=data['end_time'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SuperChatMessage:
|
||||||
|
"""
|
||||||
|
醒目留言消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
price: int = 0
|
||||||
|
"""价格(人民币)"""
|
||||||
|
message: str = ''
|
||||||
|
"""消息"""
|
||||||
|
message_trans: str = ''
|
||||||
|
"""消息日文翻译(目前只出现在SUPER_CHAT_MESSAGE_JPN)"""
|
||||||
|
start_time: int = 0
|
||||||
|
"""开始时间戳"""
|
||||||
|
end_time: int = 0
|
||||||
|
"""结束时间戳"""
|
||||||
|
time: int = 0
|
||||||
|
"""剩余时间(约等于 结束时间戳 - 开始时间戳)"""
|
||||||
|
id: int = 0
|
||||||
|
"""醒目留言ID,删除时用"""
|
||||||
|
gift_id: int = 0
|
||||||
|
"""礼物ID"""
|
||||||
|
gift_name: str = ''
|
||||||
|
"""礼物名"""
|
||||||
|
uid: int = 0
|
||||||
|
"""用户ID"""
|
||||||
|
uname: str = ''
|
||||||
|
"""用户名"""
|
||||||
|
face: str = ''
|
||||||
|
"""用户头像URL"""
|
||||||
|
guard_level: int = 0
|
||||||
|
"""舰队等级,0非舰队,1总督,2提督,3舰长"""
|
||||||
|
user_level: int = 0
|
||||||
|
"""用户等级"""
|
||||||
|
background_bottom_color: str = ''
|
||||||
|
"""底部背景色,'#rrggbb'"""
|
||||||
|
background_color: str = ''
|
||||||
|
"""背景色,'#rrggbb'"""
|
||||||
|
background_icon: str = ''
|
||||||
|
"""背景图标"""
|
||||||
|
background_image: str = ''
|
||||||
|
"""背景图URL"""
|
||||||
|
background_price_color: str = ''
|
||||||
|
"""背景价格颜色,'#rrggbb'"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
price=data['price'],
|
||||||
|
message=data['message'],
|
||||||
|
message_trans=data['message_trans'],
|
||||||
|
start_time=data['start_time'],
|
||||||
|
end_time=data['end_time'],
|
||||||
|
time=data['time'],
|
||||||
|
id=data['id'],
|
||||||
|
gift_id=data['gift']['gift_id'],
|
||||||
|
gift_name=data['gift']['gift_name'],
|
||||||
|
uid=data['uid'],
|
||||||
|
uname=data['user_info']['uname'],
|
||||||
|
face=data['user_info']['face'],
|
||||||
|
guard_level=data['user_info']['guard_level'],
|
||||||
|
user_level=data['user_info']['user_level'],
|
||||||
|
background_bottom_color=data['background_bottom_color'],
|
||||||
|
background_color=data['background_color'],
|
||||||
|
background_icon=data['background_icon'],
|
||||||
|
background_image=data['background_image'],
|
||||||
|
background_price_color=data['background_price_color'],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class SuperChatDeleteMessage:
|
||||||
|
"""
|
||||||
|
删除醒目留言消息
|
||||||
|
"""
|
||||||
|
|
||||||
|
ids: List[int] = dataclasses.field(default_factory=list)
|
||||||
|
"""醒目留言ID数组"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_command(cls, data: dict):
|
||||||
|
return cls(
|
||||||
|
ids=data['ids'],
|
||||||
|
)
|
@ -2,10 +2,15 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import blivedm
|
import blivedm
|
||||||
|
import blivedm.models.open_live as open_models
|
||||||
|
import blivedm.models.web as web_models
|
||||||
|
|
||||||
|
# 在开放平台申请的开发者密钥
|
||||||
ACCESS_KEY = ''
|
ACCESS_KEY = ''
|
||||||
ACCESS_SECRET = ''
|
ACCESS_SECRET = ''
|
||||||
|
# 在开放平台创建的项目ID
|
||||||
APP_ID = 0
|
APP_ID = 0
|
||||||
|
# 主播身份码
|
||||||
ROOM_OWNER_AUTH_CODE = ''
|
ROOM_OWNER_AUTH_CODE = ''
|
||||||
|
|
||||||
|
|
||||||
@ -29,17 +34,41 @@ async def run_single_client():
|
|||||||
client.start()
|
client.start()
|
||||||
try:
|
try:
|
||||||
# 演示70秒后停止
|
# 演示70秒后停止
|
||||||
await asyncio.sleep(70)
|
# await asyncio.sleep(70)
|
||||||
client.stop()
|
# client.stop()
|
||||||
|
|
||||||
await client.join()
|
await client.join()
|
||||||
finally:
|
finally:
|
||||||
await client.stop_and_close()
|
await client.stop_and_close()
|
||||||
|
|
||||||
|
|
||||||
class MyHandler(blivedm.HandlerInterface):
|
class MyHandler(blivedm.BaseHandler):
|
||||||
async def handle(self, client: blivedm.OpenLiveClient, command: dict):
|
async def _on_heartbeat(self, client: blivedm.BLiveClient, message: web_models.HeartbeatMessage):
|
||||||
print(command)
|
print(f'[{client.room_id}] 心跳')
|
||||||
|
|
||||||
|
async def _on_open_live_danmaku(self, client: blivedm.OpenLiveClient, message: open_models.DanmakuMessage):
|
||||||
|
print(f'[{message.room_id}] {message.uname}:{message.msg}')
|
||||||
|
|
||||||
|
async def _on_open_live_gift(self, client: blivedm.OpenLiveClient, message: open_models.GiftMessage):
|
||||||
|
coin_type = '金瓜子' if message.paid else '银瓜子'
|
||||||
|
print(f'[{message.room_id}] {message.uname} 赠送{message.gift_name}x{message.gift_num}'
|
||||||
|
f' ({coin_type}x{message.price})')
|
||||||
|
|
||||||
|
async def _on_open_live_buy_guard(self, client: blivedm.OpenLiveClient, message: open_models.GuardBuyMessage):
|
||||||
|
print(f'[{message.room_id}] {message.user_info.uname} 购买 大航海等级={message.guard_level}')
|
||||||
|
|
||||||
|
async def _on_open_live_super_chat(
|
||||||
|
self, client: blivedm.OpenLiveClient, message: open_models.SuperChatMessage
|
||||||
|
):
|
||||||
|
print(f'[{message.room_id}] 醒目留言 ¥{message.rmb} {message.uname}:{message.message}')
|
||||||
|
|
||||||
|
async def _on_open_live_super_chat_delete(
|
||||||
|
self, client: blivedm.OpenLiveClient, message: open_models.SuperChatDeleteMessage
|
||||||
|
):
|
||||||
|
print(f'[{message.room_id}] 删除醒目留言 message_ids={message.message_ids}')
|
||||||
|
|
||||||
|
async def _on_open_live_like(self, client: blivedm.OpenLiveClient, message: open_models.LikeMessage):
|
||||||
|
print(f'[{message.room_id}] {message.uname} 点赞')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
24
sample.py
24
sample.py
@ -2,10 +2,12 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import http.cookies
|
import http.cookies
|
||||||
import random
|
import random
|
||||||
|
from typing import *
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
import blivedm
|
import blivedm
|
||||||
|
import blivedm.models.web as web_models
|
||||||
|
|
||||||
# 直播间ID的取值看直播间URL
|
# 直播间ID的取值看直播间URL
|
||||||
TEST_ROOM_IDS = [
|
TEST_ROOM_IDS = [
|
||||||
@ -16,14 +18,16 @@ TEST_ROOM_IDS = [
|
|||||||
23105590,
|
23105590,
|
||||||
]
|
]
|
||||||
|
|
||||||
session = None
|
session: Optional[aiohttp.ClientSession] = None
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
init_session()
|
init_session()
|
||||||
|
try:
|
||||||
await run_single_client()
|
await run_single_client()
|
||||||
await run_multi_clients()
|
await run_multi_clients()
|
||||||
|
finally:
|
||||||
|
await session.close()
|
||||||
|
|
||||||
|
|
||||||
def init_session():
|
def init_session():
|
||||||
@ -88,20 +92,20 @@ class MyHandler(blivedm.BaseHandler):
|
|||||||
# f" uname={command['data']['uname']}")
|
# f" uname={command['data']['uname']}")
|
||||||
# _CMD_CALLBACK_DICT['INTERACT_WORD'] = __interact_word_callback # noqa
|
# _CMD_CALLBACK_DICT['INTERACT_WORD'] = __interact_word_callback # noqa
|
||||||
|
|
||||||
async def _on_heartbeat(self, client: blivedm.BLiveClient, message: blivedm.HeartbeatMessage):
|
async def _on_heartbeat(self, client: blivedm.BLiveClient, message: web_models.HeartbeatMessage):
|
||||||
print(f'[{client.room_id}] 当前人气值:{message.popularity}')
|
print(f'[{client.room_id}] 心跳')
|
||||||
|
|
||||||
async def _on_danmaku(self, client: blivedm.BLiveClient, message: blivedm.DanmakuMessage):
|
async def _on_danmaku(self, client: blivedm.BLiveClient, message: web_models.DanmakuMessage):
|
||||||
print(f'[{client.room_id}] {message.uname}:{message.msg}')
|
print(f'[{client.room_id}] {message.uname}:{message.msg}')
|
||||||
|
|
||||||
async def _on_gift(self, client: blivedm.BLiveClient, message: blivedm.GiftMessage):
|
async def _on_gift(self, client: blivedm.BLiveClient, message: web_models.GiftMessage):
|
||||||
print(f'[{client.room_id}] {message.uname} 赠送{message.gift_name}x{message.num}'
|
print(f'[{client.room_id}] {message.uname} 赠送{message.gift_name}x{message.num}'
|
||||||
f' ({message.coin_type}瓜子x{message.total_coin})')
|
f' ({message.coin_type}瓜子x{message.total_coin})')
|
||||||
|
|
||||||
async def _on_buy_guard(self, client: blivedm.BLiveClient, message: blivedm.GuardBuyMessage):
|
async def _on_buy_guard(self, client: blivedm.BLiveClient, message: web_models.GuardBuyMessage):
|
||||||
print(f'[{client.room_id}] {message.username} 购买{message.gift_name}')
|
print(f'[{client.room_id}] {message.username} 购买{message.gift_name}')
|
||||||
|
|
||||||
async def _on_super_chat(self, client: blivedm.BLiveClient, message: blivedm.SuperChatMessage):
|
async def _on_super_chat(self, client: blivedm.BLiveClient, message: web_models.SuperChatMessage):
|
||||||
print(f'[{client.room_id}] 醒目留言 ¥{message.price} {message.uname}:{message.message}')
|
print(f'[{client.room_id}] 醒目留言 ¥{message.price} {message.uname}:{message.message}')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user