快手直播间WebSocket的Protobuf协议分析
苗锦洲
河南省
1
20
570
0
使用Python将JSON转化为Proto文件,自动处理嵌套的枚举类和import
友情提示:此篇文章大约需要阅读 60分17秒
1. 访问快手直播间,点进WebSocket调用栈的第一个栈
2. 找到JSON数据
3. 将Message全部单独提取出来
4. 创建 `proto`文件夹,运行 `main.py`
5. 运行结果![image.png](https://api.ordinaryroad.tech/upms/file/download/ordinaryroad-blog/2024-01-07/22f4b1cb54d94fedb16fabec5a9c2351.png)
```python
import json
from jinja2 import Template
ignore_imports = ['AssistantType', 'CompressionType', 'StyleType', 'DisplayType']
proto_template = """syntax = "proto3";
{{ imports }}
{{ options }}
{{ protobuf }}
"""
template = Template(proto_template)
def json_to_proto(json_data, is_nested=False):
proto_definitions = []
for message_name, data in json_data.items():
options = ['option java_package = "tech.ordinaryroad.live.chat.client.kuaishou.protobuf";']
# import,去重
imports = []
protobuf = ""
if "fields" in data:
fields = data["fields"]
message_def = f"message {message_name} " + "{\n"
upper_filed_types = []
for field_name, field_data in fields.items():
field_type = field_data["type"]
field_id = field_data["id"]
message_def += f" {field_type} {field_name} = {field_id};\n"
if field_type[0].isupper():
if field_type not in upper_filed_types:
upper_filed_types.append(field_type)
# 处理嵌套
if "nested" in data:
fields = data["nested"]
message_def += "\n"
message_def += json_to_proto(fields, True)[0]
message_def += "\n}"
for upper_filed_type in upper_filed_types:
if upper_filed_type not in ignore_imports:
imports.append(f"import \"{upper_filed_type}.proto\";")
protobuf = message_def
elif "values" in data:
enum_values = data["values"]
enum_def = f"enum {message_name} " + "{\n"
for enum_value, enum_value_id in enum_values.items():
enum_def += f" {enum_value} = {enum_value_id};\n"
enum_def += "}"
protobuf = enum_def
if is_nested:
proto_definitions.append(protobuf)
else:
proto_definitions.append([
message_name + ".proto",
template.render(protobuf=protobuf,
imports="\n".join(imports),
options="\n".join(options)
)])
return proto_definitions
# 示例JSON数据
json_data = {
"SimpleUserInfo": {
"fields": {
"principalId": {
"type": "string",
"id": 1
},
"userName": {
"type": "string",
"id": 2
},
"headUrl": {
"type": "string",
"id": 3
}
}
},
"LiveAudienceState": {
"fields": {
"isFromFansTop": {
"type": "bool",
"id": 1
},
"isKoi": {
"type": "bool",
"id": 2
},
"assistantType": {
"type": "AssistantType",
"id": 3
},
"fansGroupIntimacyLevel": {
"type": "uint32",
"id": 4
},
"nameplate": {
"type": "GzoneNameplate",
"id": 5
},
"liveFansGroupState": {
"type": "LiveFansGroupState",
"id": 6
},
"wealthGrade": {
"type": "uint32",
"id": 7
},
"badgeKey": {
"type": "string",
"id": 8
}
},
"nested": {
"AssistantType": {
"values": {
"UNKNOWN_ASSISTANT_TYPE": 0,
"SUPER": 1,
"JUNIOR": 2
}
}
}
},
"GzoneNameplate": {
"fields": {
"id": {
"type": "int64",
"id": 1
},
"name": {
"type": "string",
"id": 2
},
"urls": {
"rule": "repeated",
"type": "PicUrl",
"id": 3
}
}
},
"LiveFansGroupState": {
"fields": {
"intimacyLevel": {
"type": "uint32",
"id": 1
},
"enterRoomSpecialEffect": {
"type": "uint32",
"id": 2
}
}
},
"CSWebEnterRoom": {
"fields": {
"token": {
"type": "string",
"id": 1
},
"liveStreamId": {
"type": "string",
"id": 2
},
"reconnectCount": {
"type": "uint32",
"id": 3
},
"lastErrorCode": {
"type": "uint32",
"id": 4
},
"expTag": {
"type": "string",
"id": 5
},
"attach": {
"type": "string",
"id": 6
},
"pageId": {
"type": "string",
"id": 7
}
}
},
"SCWebEnterRoomAck": {
"fields": {
"minReconnectMs": {
"type": "uint64",
"id": 1
},
"maxReconnectMs": {
"type": "uint64",
"id": 2
},
"heartbeatIntervalMs": {
"type": "uint64",
"id": 3
}
}
},
"CSWebHeartbeat": {
"fields": {
"timestamp": {
"type": "uint64",
"id": 1
}
}
},
"SCWebHeartbeatAck": {
"fields": {
"timestamp": {
"type": "uint64",
"id": 1
},
"clientTimestamp": {
"type": "uint64",
"id": 2
}
}
},
"SCWebError": {
"fields": {
"code": {
"type": "uint32",
"id": 1
},
"msg": {
"type": "string",
"id": 2
},
"subCode": {
"type": "uint32",
"id": 3
}
}
},
"CSWebError": {
"fields": {
"code": {
"type": "uint32",
"id": 1
},
"msg": {
"type": "string",
"id": 2
}
}
},
"WebUserPauseType": {
"values": {
"UNKNOWN_USER_PAUSE_TYPE": 0,
"BACKGROUND": 1
}
},
"CSWebUserPause": {
"fields": {
"time": {
"type": "uint64",
"id": 1
},
"pauseType": {
"type": "WebUserPauseType",
"id": 2
}
}
},
"CSWebUserExit": {
"fields": {
"time": {
"type": "uint64",
"id": 1
}
}
},
"WebPauseType": {
"values": {
"UNKNOWN_PAUSE_TYPE": 0,
"TELEPHONE": 1,
"SHARE": 2
}
},
"SCWebAuthorPause": {
"fields": {
"time": {
"type": "uint64",
"id": 1
},
"pauseType": {
"type": "WebPauseType",
"id": 2
}
}
},
"SCWebAuthorResume": {
"fields": {
"time": {
"type": "uint64",
"id": 1
}
}
},
"SCWebPipStarted": {
"fields": {
"time": {
"type": "uint64",
"id": 1
}
}
},
"SCWebPipEnded": {
"fields": {
"time": {
"type": "uint64",
"id": 1
}
}
},
"SCWebFeedPush": {
"fields": {
"displayWatchingCount": {
"type": "string",
"id": 1
},
"displayLikeCount": {
"type": "string",
"id": 2
},
"pendingLikeCount": {
"type": "uint64",
"id": 3
},
"pushInterval": {
"type": "uint64",
"id": 4
},
"commentFeeds": {
"rule": "repeated",
"type": "WebCommentFeed",
"id": 5
},
"commentCursor": {
"type": "string",
"id": 6
},
"comboCommentFeed": {
"rule": "repeated",
"type": "WebComboCommentFeed",
"id": 7
},
"likeFeeds": {
"rule": "repeated",
"type": "WebLikeFeed",
"id": 8
},
"giftFeeds": {
"rule": "repeated",
"type": "WebGiftFeed",
"id": 9
},
"giftCursor": {
"type": "string",
"id": 10
},
"systemNoticeFeeds": {
"rule": "repeated",
"type": "WebSystemNoticeFeed",
"id": 11
},
"shareFeeds": {
"rule": "repeated",
"type": "WebShareFeed",
"id": 12
}
}
},
"WebLikeFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"user": {
"type": "SimpleUserInfo",
"id": 2
},
"sortRank": {
"type": "uint64",
"id": 3
},
"deviceHash": {
"type": "string",
"id": 4
}
}
},
"WebCommentFeedShowType": {
"values": {
"FEED_SHOW_UNKNOWN": 0,
"FEED_SHOW_NORMAL": 1,
"FEED_HIDDEN": 2
}
},
"WebCommentFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"user": {
"type": "SimpleUserInfo",
"id": 2
},
"content": {
"type": "string",
"id": 3
},
"deviceHash": {
"type": "string",
"id": 4
},
"sortRank": {
"type": "uint64",
"id": 5
},
"color": {
"type": "string",
"id": 6
},
"showType": {
"type": "WebCommentFeedShowType",
"id": 7
},
"senderState": {
"type": "LiveAudienceState",
"id": 8
}
}
},
"WebComboCommentFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"content": {
"type": "string",
"id": 2
},
"comboCount": {
"type": "uint32",
"id": 3
}
}
},
"WebSystemNoticeFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"user": {
"type": "SimpleUserInfo",
"id": 2
},
"time": {
"type": "uint64",
"id": 3
},
"content": {
"type": "string",
"id": 4
},
"displayDuration": {
"type": "uint64",
"id": 5
},
"sortRank": {
"type": "uint64",
"id": 6
},
"displayType": {
"type": "DisplayType",
"id": 7
}
},
"nested": {
"DisplayType": {
"values": {
"UNKNOWN_DISPLAY_TYPE": 0,
"COMMENT": 1,
"ALERT": 2,
"TOAST": 3
}
}
}
},
"WebGiftFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"user": {
"type": "SimpleUserInfo",
"id": 2
},
"time": {
"type": "uint64",
"id": 3
},
"giftId": {
"type": "uint32",
"id": 4
},
"sortRank": {
"type": "uint64",
"id": 5
},
"mergeKey": {
"type": "string",
"id": 6
},
"batchSize": {
"type": "uint32",
"id": 7
},
"comboCount": {
"type": "uint32",
"id": 8
},
"rank": {
"type": "uint32",
"id": 9
},
"expireDuration": {
"type": "uint64",
"id": 10
},
"clientTimestamp": {
"type": "uint64",
"id": 11
},
"slotDisplayDuration": {
"type": "uint64",
"id": 12
},
"starLevel": {
"type": "uint32",
"id": 13
},
"styleType": {
"type": "StyleType",
"id": 14
},
"liveAssistantType": {
"type": "WebLiveAssistantType",
"id": 15
},
"deviceHash": {
"type": "string",
"id": 16
},
"danmakuDisplay": {
"type": "bool",
"id": 17
}
},
"nested": {
"StyleType": {
"values": {
"UNKNOWN_STYLE": 0,
"BATCH_STAR_0": 1,
"BATCH_STAR_1": 2,
"BATCH_STAR_2": 3,
"BATCH_STAR_3": 4,
"BATCH_STAR_4": 5,
"BATCH_STAR_5": 6,
"BATCH_STAR_6": 7
}
}
}
},
"SCWebRefreshWallet": {
"fields": {}
},
"SCWebCurrentRedPackFeed": {
"fields": {
"redPack": {
"rule": "repeated",
"type": "WebRedPackInfo",
"id": 1
}
}
},
"WebRedPackCoverType": {
"values": {
"UNKNOWN_COVER_TYPE": 0,
"NORMAL_COVER": 1,
"PRETTY_COVER": 2
}
},
"WebRedPackInfo": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"author": {
"type": "SimpleUserInfo",
"id": 2
},
"balance": {
"type": "uint64",
"id": 3
},
"openTime": {
"type": "uint64",
"id": 4
},
"currentTime": {
"type": "uint64",
"id": 5
},
"grabToken": {
"type": "string",
"id": 6
},
"needSendRequest": {
"type": "bool",
"id": 7
},
"requestDelayMillis": {
"type": "uint64",
"id": 8
},
"luckiestDelayMillis": {
"type": "uint64",
"id": 9
},
"coverType": {
"type": "WebRedPackCoverType",
"id": 10
}
}
},
"WebLiveAssistantType": {
"values": {
"UNKNOWN_ASSISTANT_TYPE": 0,
"SUPER": 1,
"JUNIOR": 2
}
},
"WebWatchingUserInfo": {
"fields": {
"user": {
"type": "SimpleUserInfo",
"id": 1
},
"offline": {
"type": "bool",
"id": 2
},
"tuhao": {
"type": "bool",
"id": 3
},
"liveAssistantType": {
"type": "WebLiveAssistantType",
"id": 4
},
"displayKsCoin": {
"type": "string",
"id": 5
}
}
},
"SCWebLiveWatchingUsers": {
"fields": {
"watchingUser": {
"rule": "repeated",
"type": "WebWatchingUserInfo",
"id": 1
},
"displayWatchingCount": {
"type": "string",
"id": 2
},
"pendingDuration": {
"type": "uint64",
"id": 3
}
}
},
"WebShareFeed": {
"fields": {
"id": {
"type": "string",
"id": 1
},
"user": {
"type": "SimpleUserInfo",
"id": 2
},
"time": {
"type": "uint64",
"id": 3
},
"thirdPartyPlatform": {
"type": "uint32",
"id": 4
},
"sortRank": {
"type": "uint64",
"id": 5
},
"liveAssistantType": {
"type": "WebLiveAssistantType",
"id": 6
},
"deviceHash": {
"type": "string",
"id": 7
}
}
},
"SCWebSuspectedViolation": {
"fields": {
"suspectedViolation": {
"type": "bool",
"id": 1
}
}
},
"SCWebGuessOpened": {
"fields": {
"time": {
"type": "uint64",
"id": 1
},
"guessId": {
"type": "string",
"id": 2
},
"submitDeadline": {
"type": "uint64",
"id": 3
},
"displayMaxDelayMillis": {
"type": "uint64",
"id": 4
}
}
},
"SCWebGuessClosed": {
"fields": {
"time": {
"type": "uint64",
"id": 1
},
"guessId": {
"type": "string",
"id": 2
},
"displayMaxDelayMillis": {
"type": "uint64",
"id": 3
}
}
},
"SCWebRideChanged": {
"fields": {
"rideId": {
"type": "string",
"id": 1
},
"requestMaxDelayMillis": {
"type": "uint32",
"id": 2
}
}
},
"SCWebBetChanged": {
"fields": {
"maxDelayMillis": {
"type": "uint64",
"id": 1
}
}
},
"SCWebBetClosed": {
"fields": {
"maxDelayMillis": {
"type": "uint64",
"id": 1
}
}
},
"ConfigSwitchType": {
"values": {
"UNKNOWN": 0,
"HIDE_BARRAGE": 1,
"HIDE_SPECIAL_EFFECT": 2
}
},
"ConfigSwitchItem": {
"fields": {
"configSwitchType": {
"type": "ConfigSwitchType",
"id": 1
},
"value": {
"type": "bool",
"id": 2
}
}
},
"SCWebLiveSpecialAccountConfigState": {
"fields": {
"configSwitchItem": {
"rule": "repeated",
"type": "ConfigSwitchItem",
"id": 1
},
"timestamp": {
"type": "uint64",
"id": 2
}
}
},
"LiveCdnNodeView": {
"fields": {
"cdn": {
"type": "string",
"id": 1
},
"url": {
"type": "string",
"id": 2
},
"freeTraffic": {
"type": "bool",
"id": 3
}
}
},
"AuditAudienceMask": {
"fields": {
"iconCdnNodeView": {
"rule": "repeated",
"type": "LiveCdnNodeView",
"id": 1
},
"title": {
"type": "string",
"id": 2
},
"detail": {
"type": "string",
"id": 3
}
}
},
"SCLiveWarningMaskStatusChangedAudience": {
"fields": {
"displayMask": {
"type": "bool",
"id": 1
},
"warningMask": {
"type": "AuditAudienceMask",
"id": 2
}
}
},
"SocketMessage": {
"fields": {
"payloadType": {
"type": "PayloadType",
"id": 1
},
"compressionType": {
"type": "CompressionType",
"id": 2
},
"payload": {
"type": "bytes",
"id": 3
}
},
"nested": {
"CompressionType": {
"values": {
"UNKNOWN": 0,
"NONE": 1,
"GZIP": 2,
"AES": 3
}
}
}
},
"PayloadType": {
"values": {
"UNKNOWN": 0,
"CS_HEARTBEAT": 1,
"CS_ERROR": 3,
"CS_PING": 4,
"PS_HOST_INFO": 51,
"SC_HEARTBEAT_ACK": 101,
"SC_ECHO": 102,
"SC_ERROR": 103,
"SC_PING_ACK": 104,
"SC_INFO": 105,
"CS_ENTER_ROOM": 200,
"CS_USER_PAUSE": 201,
"CS_USER_EXIT": 202,
"CS_AUTHOR_PUSH_TRAFFIC_ZERO": 203,
"CS_HORSE_RACING": 204,
"CS_RACE_LOSE": 205,
"CS_VOIP_SIGNAL": 206,
"SC_ENTER_ROOM_ACK": 300,
"SC_AUTHOR_PAUSE": 301,
"SC_AUTHOR_RESUME": 302,
"SC_AUTHOR_PUSH_TRAFFIC_ZERO": 303,
"SC_AUTHOR_HEARTBEAT_MISS": 304,
"SC_PIP_STARTED": 305,
"SC_PIP_ENDED": 306,
"SC_HORSE_RACING_ACK": 307,
"SC_VOIP_SIGNAL": 308,
"SC_FEED_PUSH": 310,
"SC_ASSISTANT_STATUS": 311,
"SC_REFRESH_WALLET": 312,
"SC_LIVE_CHAT_CALL": 320,
"SC_LIVE_CHAT_CALL_ACCEPTED": 321,
"SC_LIVE_CHAT_CALL_REJECTED": 322,
"SC_LIVE_CHAT_READY": 323,
"SC_LIVE_CHAT_GUEST_END": 324,
"SC_LIVE_CHAT_ENDED": 325,
"SC_RENDERING_MAGIC_FACE_DISABLE": 326,
"SC_RENDERING_MAGIC_FACE_ENABLE": 327,
"SC_RED_PACK_FEED": 330,
"SC_LIVE_WATCHING_LIST": 340,
"SC_LIVE_QUIZ_QUESTION_ASKED": 350,
"SC_LIVE_QUIZ_QUESTION_REVIEWED": 351,
"SC_LIVE_QUIZ_SYNC": 352,
"SC_LIVE_QUIZ_ENDED": 353,
"SC_LIVE_QUIZ_WINNERS": 354,
"SC_SUSPECTED_VIOLATION": 355,
"SC_SHOP_OPENED": 360,
"SC_SHOP_CLOSED": 361,
"SC_GUESS_OPENED": 370,
"SC_GUESS_CLOSED": 371,
"SC_PK_INVITATION": 380,
"SC_PK_STATISTIC": 381,
"SC_RIDDLE_OPENED": 390,
"SC_RIDDLE_CLOESED": 391,
"SC_RIDE_CHANGED": 412,
"SC_BET_CHANGED": 441,
"SC_BET_CLOSED": 442,
"SC_LIVE_SPECIAL_ACCOUNT_CONFIG_STATE": 645,
"SC_LIVE_WARNING_MASK_STATUS_CHANGED_AUDIENCE": 758
}
},
"CSHeartbeat": {
"fields": {
"timestamp": {
"type": "uint64",
"id": 1
}
}
},
"SCHeartbeatAck": {
"fields": {
"timestamp": {
"type": "uint64",
"id": 1
},
"clientTimestamp": {
"type": "uint64",
"id": 2
}
}
},
"SCError": {
"fields": {
"code": {
"type": "uint32",
"id": 1
},
"msg": {
"type": "string",
"id": 2
},
"subCode": {
"type": "uint32",
"id": 3
}
}
},
"SCInfo": {
"fields": {
"code": {
"type": "uint32",
"id": 1
},
"msg": {
"type": "string",
"id": 2
}
}
},
"CSError": {
"fields": {
"code": {
"type": "uint32",
"id": 1
}
}
},
"CSPing": {
"fields": {
"echoData": {
"type": "string",
"id": 1
},
"clientId": {
"type": "ClientId",
"id": 2
},
"deviceId": {
"type": "string",
"id": 3
},
"appVer": {
"type": "string",
"id": 4
}
}
},
"SCPingAck": {
"fields": {
"echoData": {
"type": "string",
"id": 1
}
}
},
"SCEcho": {
"fields": {
"content": {
"type": "string",
"id": 1
}
}
},
"PSHostInfo": {
"fields": {
"ip": {
"type": "string",
"id": 1
},
"port": {
"type": "int32",
"id": 2
}
}
},
"PicUrl": {
"fields": {
"cdn": {
"type": "string",
"id": 1
},
"url": {
"type": "string",
"id": 2
},
"urlPattern": {
"type": "string",
"id": 3
},
"ip": {
"type": "string",
"id": 4
}
}
},
"UserInfo": {
"fields": {
"userId": {
"type": "uint64",
"id": 1
},
"userName": {
"type": "string",
"id": 2
},
"userGender": {
"type": "string",
"id": 3
},
"userText": {
"type": "string",
"id": 4
},
"headUrls": {
"rule": "repeated",
"type": "PicUrl",
"id": 5
},
"verified": {
"type": "bool",
"id": 6
},
"sUserId": {
"type": "string",
"id": 7
},
"httpsHeadUrls": {
"rule": "repeated",
"type": "PicUrl",
"id": 8
},
"kwaiId": {
"type": "string",
"id": 9
}
}
},
"ClientId": {
"values": {
"NONE": 0,
"IPHONE": 1,
"ANDROID": 2,
"WEB": 3,
"PC": 6,
"IPHONE_LIVE_MATE": 8,
"ANDROID_LIVE_MATE": 9
}
}
}
# 将JSON转换为protobuf定义并打印结果
proto_definitions = json_to_proto(json_data)
for proto_definition in proto_definitions:
file_name = proto_definition[0]
file_content = proto_definition[1]
with open(f"./proto/{file_name}", "w") as file:
file.write(file_content)
print(proto_definitions)
```
1
评论
已自动恢复阅读位置、日/夜间模式参数