2023-11-04 16:52:07 +08:00
|
|
|
|
# -*- coding: utf-8 -*-
|
2023-11-05 16:29:11 +08:00
|
|
|
|
import dataclasses
|
2023-11-04 16:52:07 +08:00
|
|
|
|
import enum
|
2023-11-05 16:29:11 +08:00
|
|
|
|
from typing import *
|
|
|
|
|
|
|
|
|
|
__all__ = (
|
|
|
|
|
'RoomKeyType',
|
|
|
|
|
'RoomKey',
|
|
|
|
|
'Command',
|
2023-11-08 00:21:17 +08:00
|
|
|
|
'ExtraData',
|
|
|
|
|
'AddRoomMsg',
|
|
|
|
|
'RoomInitMsg',
|
|
|
|
|
'DelRoomMsg',
|
|
|
|
|
'OpenPluginAdminUiMsg',
|
2023-11-05 16:29:11 +08:00
|
|
|
|
'AuthorType',
|
|
|
|
|
'GuardLevel',
|
|
|
|
|
'ContentType',
|
|
|
|
|
'AddTextMsg',
|
|
|
|
|
'AddGiftMsg',
|
|
|
|
|
'AddMemberMsg',
|
|
|
|
|
'AddSuperChatMsg',
|
|
|
|
|
'DelSuperChatMsg',
|
|
|
|
|
'UpdateTranslationMsg',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RoomKeyType(enum.IntEnum):
|
|
|
|
|
ROOM_ID = 1
|
|
|
|
|
AUTH_CODE = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RoomKey(NamedTuple):
|
|
|
|
|
"""用来标识一个房间"""
|
|
|
|
|
type: RoomKeyType
|
|
|
|
|
value: Union[int, str]
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
|
res = str(self.value)
|
|
|
|
|
if self.type == RoomKeyType.AUTH_CODE:
|
|
|
|
|
# 身份码要脱敏
|
|
|
|
|
res = '***' + res[-3:]
|
|
|
|
|
return res
|
|
|
|
|
__repr__ = __str__
|
2023-11-04 16:52:07 +08:00
|
|
|
|
|
2023-11-08 00:21:17 +08:00
|
|
|
|
@classmethod
|
|
|
|
|
def from_dict(cls, data: dict):
|
|
|
|
|
type_ = RoomKeyType(data['type'])
|
|
|
|
|
value = data['value']
|
|
|
|
|
if type_ == RoomKeyType.ROOM_ID:
|
|
|
|
|
if not isinstance(value, int):
|
|
|
|
|
raise TypeError(f'Type of value is {type(value)}, value={value}')
|
|
|
|
|
elif type_ == RoomKeyType.AUTH_CODE:
|
|
|
|
|
if not isinstance(value, str):
|
|
|
|
|
raise TypeError(f'Type of value is {type(value)}, value={value}')
|
|
|
|
|
return cls(type=type_, value=value)
|
|
|
|
|
|
|
|
|
|
def to_dict(self):
|
|
|
|
|
return {'type': self.type, 'value': self.value}
|
|
|
|
|
|
2023-11-04 16:52:07 +08:00
|
|
|
|
|
|
|
|
|
class Command(enum.IntEnum):
|
|
|
|
|
HEARTBEAT = 0
|
2023-11-05 16:29:11 +08:00
|
|
|
|
BLC_INIT = 1
|
2023-11-05 21:42:43 +08:00
|
|
|
|
ADD_ROOM = 2
|
|
|
|
|
ROOM_INIT = 3
|
|
|
|
|
DEL_ROOM = 4
|
2023-11-08 00:21:17 +08:00
|
|
|
|
OPEN_PLUGIN_ADMIN_UI = 5
|
|
|
|
|
|
|
|
|
|
# 从插件发送到blivechat的请求
|
|
|
|
|
LOG_REQ = 30
|
|
|
|
|
ADD_TEXT_REQ = 31
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
2023-11-08 00:21:17 +08:00
|
|
|
|
# 房间内消息
|
|
|
|
|
ADD_TEXT = 50
|
|
|
|
|
ADD_GIFT = 51
|
|
|
|
|
ADD_MEMBER = 52
|
|
|
|
|
ADD_SUPER_CHAT = 53
|
|
|
|
|
DEL_SUPER_CHAT = 54
|
|
|
|
|
UPDATE_TRANSLATION = 55
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
|
|
|
|
|
2023-11-05 21:42:43 +08:00
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class ExtraData:
|
|
|
|
|
"""一些消息共用的附加信息"""
|
|
|
|
|
|
|
|
|
|
room_id: Optional[int] = None
|
|
|
|
|
"""房间ID"""
|
|
|
|
|
room_key: Optional[RoomKey] = None
|
|
|
|
|
"""blivechat用来标识一个房间的key"""
|
|
|
|
|
is_from_plugin: bool = False
|
2023-11-08 00:21:17 +08:00
|
|
|
|
"""
|
|
|
|
|
消息是插件生成的
|
|
|
|
|
|
|
|
|
|
如果你的插件既要监听消息,又要生成消息,注意判断这个以避免死循环
|
|
|
|
|
"""
|
2023-11-05 21:42:43 +08:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_dict(cls, data: dict):
|
|
|
|
|
room_key_dict = data.get('roomKey', None)
|
|
|
|
|
if room_key_dict is not None:
|
2023-11-08 00:21:17 +08:00
|
|
|
|
room_key = RoomKey.from_dict(room_key_dict)
|
2023-11-05 21:42:43 +08:00
|
|
|
|
else:
|
|
|
|
|
room_key = None
|
|
|
|
|
|
|
|
|
|
return cls(
|
|
|
|
|
room_id=data.get('roomId', None),
|
|
|
|
|
room_key=room_key,
|
|
|
|
|
is_from_plugin=data.get('isFromPlugin', False),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
2023-11-08 00:21:17 +08:00
|
|
|
|
class _EmptyMsg:
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, _data: dict):
|
|
|
|
|
return cls()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class AddRoomMsg(_EmptyMsg):
|
2023-11-05 21:42:43 +08:00
|
|
|
|
"""
|
|
|
|
|
添加房间消息。房间信息在extra里
|
|
|
|
|
|
|
|
|
|
此时room_id是None,因为还没有初始化
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class RoomInitMsg:
|
|
|
|
|
"""
|
|
|
|
|
房间初始化消息。房间信息在extra里
|
|
|
|
|
|
|
|
|
|
一个房间创建后可能被多次初始化,处理时注意去重
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
is_success: bool = False
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: dict):
|
|
|
|
|
return cls(
|
|
|
|
|
is_success=data['isSuccess'],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
2023-11-08 00:21:17 +08:00
|
|
|
|
class DelRoomMsg(_EmptyMsg):
|
2023-11-05 21:42:43 +08:00
|
|
|
|
"""
|
|
|
|
|
删除房间消息。房间信息在extra里
|
|
|
|
|
|
|
|
|
|
注意此时room_id可能是None
|
|
|
|
|
"""
|
|
|
|
|
|
2023-11-08 00:21:17 +08:00
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class OpenPluginAdminUiMsg(_EmptyMsg):
|
|
|
|
|
"""用户请求打开当前插件的管理界面消息"""
|
2023-11-05 21:42:43 +08:00
|
|
|
|
|
|
|
|
|
|
2023-11-05 16:29:11 +08:00
|
|
|
|
class AuthorType(enum.IntEnum):
|
|
|
|
|
NORMAL = 0
|
|
|
|
|
GUARD = 1
|
|
|
|
|
"""舰队"""
|
|
|
|
|
ADMIN = 2
|
|
|
|
|
"""房管"""
|
|
|
|
|
ROOM_OWNER = 3
|
|
|
|
|
"""主播"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class GuardLevel(enum.IntEnum):
|
|
|
|
|
"""舰队等级"""
|
|
|
|
|
|
|
|
|
|
NONE = 0
|
|
|
|
|
LV3 = 1
|
|
|
|
|
"""总督"""
|
|
|
|
|
LV2 = 2
|
|
|
|
|
"""提督"""
|
|
|
|
|
LV1 = 3
|
|
|
|
|
"""舰长"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ContentType(enum.IntEnum):
|
|
|
|
|
TEXT = 0
|
|
|
|
|
EMOTICON = 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class AddTextMsg:
|
|
|
|
|
"""弹幕消息"""
|
|
|
|
|
|
|
|
|
|
avatar_url: str = ''
|
|
|
|
|
"""用户头像URL"""
|
|
|
|
|
timestamp: int = 0
|
|
|
|
|
"""时间戳(秒)"""
|
|
|
|
|
author_name: str = ''
|
|
|
|
|
"""用户名"""
|
|
|
|
|
author_type: int = AuthorType.NORMAL.value
|
|
|
|
|
"""用户类型,见AuthorType"""
|
|
|
|
|
content: str = ''
|
|
|
|
|
"""弹幕内容"""
|
|
|
|
|
privilege_type: int = GuardLevel.NONE.value
|
|
|
|
|
"""舰队等级,见GuardLevel"""
|
|
|
|
|
is_gift_danmaku: bool = False
|
|
|
|
|
"""是否礼物弹幕"""
|
|
|
|
|
author_level: int = 1
|
|
|
|
|
"""用户等级"""
|
|
|
|
|
is_newbie: bool = False
|
|
|
|
|
"""是否正式会员"""
|
|
|
|
|
is_mobile_verified: bool = True
|
|
|
|
|
"""是否绑定手机"""
|
|
|
|
|
medal_level: int = 0
|
|
|
|
|
"""勋章等级,如果没戴当前房间勋章则为0"""
|
|
|
|
|
id: str = ''
|
|
|
|
|
"""消息ID"""
|
|
|
|
|
translation: str = ''
|
|
|
|
|
"""弹幕内容翻译"""
|
|
|
|
|
content_type: int = ContentType.TEXT.value
|
|
|
|
|
"""内容类型,见ContentType"""
|
|
|
|
|
content_type_params: Union[dict, list] = dataclasses.field(default_factory=dict)
|
|
|
|
|
"""跟内容类型相关的参数"""
|
2024-01-15 01:15:31 +08:00
|
|
|
|
uid: int = 0
|
|
|
|
|
"""用户ID"""
|
|
|
|
|
medal_name: str = ''
|
|
|
|
|
"""勋章名"""
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: list):
|
|
|
|
|
content_type = data[13]
|
|
|
|
|
content_type_params = data[14]
|
|
|
|
|
if content_type == ContentType.EMOTICON:
|
|
|
|
|
content_type_params = {'url': content_type_params[0]}
|
|
|
|
|
|
|
|
|
|
return cls(
|
|
|
|
|
avatar_url=data[0],
|
|
|
|
|
timestamp=data[1],
|
|
|
|
|
author_name=data[2],
|
|
|
|
|
author_type=data[3],
|
|
|
|
|
content=data[4],
|
|
|
|
|
privilege_type=data[5],
|
|
|
|
|
is_gift_danmaku=bool(data[6]),
|
|
|
|
|
author_level=data[7],
|
|
|
|
|
is_newbie=bool(data[8]),
|
|
|
|
|
is_mobile_verified=bool(data[9]),
|
|
|
|
|
medal_level=data[10],
|
|
|
|
|
id=data[11],
|
|
|
|
|
translation=data[12],
|
|
|
|
|
content_type=content_type,
|
|
|
|
|
content_type_params=content_type_params,
|
2024-01-15 01:15:31 +08:00
|
|
|
|
uid=data[16],
|
|
|
|
|
medal_name=data[17],
|
2023-11-05 16:29:11 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class AddGiftMsg:
|
|
|
|
|
"""礼物消息"""
|
|
|
|
|
|
|
|
|
|
id: str = ''
|
|
|
|
|
"""消息ID"""
|
|
|
|
|
avatar_url: str = ''
|
|
|
|
|
"""用户头像URL"""
|
|
|
|
|
timestamp: int = 0
|
|
|
|
|
"""时间戳(秒)"""
|
|
|
|
|
author_name: str = ''
|
|
|
|
|
"""用户名"""
|
|
|
|
|
total_coin: int = 0
|
2024-01-15 01:15:31 +08:00
|
|
|
|
"""总价付费瓜子数,1000金瓜子 = 1元"""
|
|
|
|
|
total_free_coin: int = 0
|
|
|
|
|
"""总价免费瓜子数"""
|
2023-11-05 16:29:11 +08:00
|
|
|
|
gift_name: str = ''
|
|
|
|
|
"""礼物名"""
|
|
|
|
|
num: int = 0
|
|
|
|
|
"""数量"""
|
2024-01-15 01:15:31 +08:00
|
|
|
|
gift_id: int = 0
|
|
|
|
|
"""礼物ID"""
|
|
|
|
|
gift_icon_url: str = ''
|
|
|
|
|
"""礼物图标URL"""
|
|
|
|
|
uid: int = 0
|
|
|
|
|
"""用户ID"""
|
|
|
|
|
privilege_type: int = GuardLevel.NONE.value
|
|
|
|
|
"""舰队等级,见GuardLevel"""
|
|
|
|
|
medal_level: int = 0
|
|
|
|
|
"""勋章等级,如果没戴当前房间勋章则为0"""
|
|
|
|
|
medal_name: str = ''
|
|
|
|
|
"""勋章名"""
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: dict):
|
|
|
|
|
return cls(
|
|
|
|
|
id=data['id'],
|
|
|
|
|
avatar_url=data['avatarUrl'],
|
|
|
|
|
timestamp=data['timestamp'],
|
|
|
|
|
author_name=data['authorName'],
|
|
|
|
|
total_coin=data['totalCoin'],
|
2024-01-15 01:15:31 +08:00
|
|
|
|
total_free_coin=data['totalFreeCoin'],
|
2023-11-05 16:29:11 +08:00
|
|
|
|
gift_name=data['giftName'],
|
|
|
|
|
num=data['num'],
|
2024-01-15 01:15:31 +08:00
|
|
|
|
gift_id=data['giftId'],
|
|
|
|
|
gift_icon_url=data['giftIconUrl'],
|
|
|
|
|
uid=data['uid'],
|
|
|
|
|
privilege_type=data['privilegeType'],
|
|
|
|
|
medal_level=data['medalLevel'],
|
|
|
|
|
medal_name=data['medalName'],
|
2023-11-05 16:29:11 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class AddMemberMsg:
|
|
|
|
|
"""上舰消息"""
|
|
|
|
|
|
|
|
|
|
id: str = ''
|
|
|
|
|
"""消息ID"""
|
|
|
|
|
avatar_url: str = ''
|
|
|
|
|
"""用户头像URL"""
|
|
|
|
|
timestamp: int = 0
|
|
|
|
|
"""时间戳(秒)"""
|
|
|
|
|
author_name: str = ''
|
|
|
|
|
"""用户名"""
|
|
|
|
|
privilege_type: int = GuardLevel.NONE.value
|
|
|
|
|
"""舰队等级,见GuardLevel"""
|
2024-01-15 01:15:31 +08:00
|
|
|
|
num: int = 0
|
|
|
|
|
"""数量"""
|
|
|
|
|
unit: str = ''
|
|
|
|
|
"""单位(月)"""
|
|
|
|
|
uid: int = 0
|
|
|
|
|
"""用户ID"""
|
|
|
|
|
medal_level: int = 0
|
|
|
|
|
"""勋章等级,如果没戴当前房间勋章则为0"""
|
|
|
|
|
medal_name: str = ''
|
|
|
|
|
"""勋章名"""
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: dict):
|
|
|
|
|
return cls(
|
|
|
|
|
id=data['id'],
|
|
|
|
|
avatar_url=data['avatarUrl'],
|
|
|
|
|
timestamp=data['timestamp'],
|
|
|
|
|
author_name=data['authorName'],
|
|
|
|
|
privilege_type=data['privilegeType'],
|
2024-01-15 01:15:31 +08:00
|
|
|
|
num=data['num'],
|
|
|
|
|
unit=data['unit'],
|
|
|
|
|
uid=data['uid'],
|
|
|
|
|
medal_level=data['medalLevel'],
|
|
|
|
|
medal_name=data['medalName'],
|
2023-11-05 16:29:11 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class AddSuperChatMsg:
|
|
|
|
|
"""醒目留言消息"""
|
|
|
|
|
|
|
|
|
|
id: str = ''
|
|
|
|
|
"""消息ID"""
|
|
|
|
|
avatar_url: str = ''
|
|
|
|
|
"""用户头像URL"""
|
|
|
|
|
timestamp: int = 0
|
|
|
|
|
"""时间戳(秒)"""
|
|
|
|
|
author_name: str = ''
|
|
|
|
|
"""用户名"""
|
|
|
|
|
price: int = 0
|
|
|
|
|
"""价格(元)"""
|
|
|
|
|
content: str = ''
|
|
|
|
|
"""内容"""
|
|
|
|
|
translation: str = ''
|
|
|
|
|
"""内容翻译"""
|
2024-01-15 01:15:31 +08:00
|
|
|
|
uid: int = 0
|
|
|
|
|
"""用户ID"""
|
|
|
|
|
privilege_type: int = GuardLevel.NONE.value
|
|
|
|
|
"""舰队等级,见GuardLevel"""
|
|
|
|
|
medal_level: int = 0
|
|
|
|
|
"""勋章等级,如果没戴当前房间勋章则为0"""
|
|
|
|
|
medal_name: str = ''
|
|
|
|
|
"""勋章名"""
|
2023-11-05 16:29:11 +08:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: dict):
|
|
|
|
|
return cls(
|
|
|
|
|
id=data['id'],
|
|
|
|
|
avatar_url=data['avatarUrl'],
|
|
|
|
|
timestamp=data['timestamp'],
|
|
|
|
|
author_name=data['authorName'],
|
|
|
|
|
price=data['price'],
|
|
|
|
|
content=data['content'],
|
|
|
|
|
translation=data['translation'],
|
2024-01-15 01:15:31 +08:00
|
|
|
|
uid=data['uid'],
|
|
|
|
|
privilege_type=data['privilegeType'],
|
|
|
|
|
medal_level=data['medalLevel'],
|
|
|
|
|
medal_name=data['medalName'],
|
2023-11-05 16:29:11 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class DelSuperChatMsg:
|
|
|
|
|
"""删除醒目留言消息"""
|
|
|
|
|
|
|
|
|
|
ids: List[str] = dataclasses.field(default_factory=list)
|
|
|
|
|
"""醒目留言ID数组"""
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: dict):
|
|
|
|
|
return cls(
|
|
|
|
|
ids=data['ids'],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclasses.dataclass
|
|
|
|
|
class UpdateTranslationMsg:
|
|
|
|
|
"""更新内容翻译消息"""
|
|
|
|
|
|
|
|
|
|
id: str = ''
|
|
|
|
|
"""消息ID"""
|
|
|
|
|
translation: str = ''
|
|
|
|
|
"""内容翻译"""
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def from_command(cls, data: list):
|
|
|
|
|
return cls(
|
|
|
|
|
id=data[0],
|
|
|
|
|
translation=data[1],
|
|
|
|
|
)
|