2026-07-06 12:43:40 +08:00

1211 lines
44 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package roomapi
import (
"strconv"
"hyapp/pkg/imgroup"
"hyapp/pkg/tencentrtc"
roomv1 "hyapp.local/api/proto/room/v1"
walletv1 "hyapp.local/api/proto/wallet/v1"
)
const joinRoomContributionRankLimit = 10
type roomListData struct {
Rooms []roomListItemData `json:"rooms"`
NextCursor string `json:"next_cursor,omitempty"`
Countries []roomListCountryData `json:"countries,omitempty"`
}
type roomListCountryData struct {
CountryID int64 `json:"country_id,omitempty"`
CountryCode string `json:"country_code"`
CountryName string `json:"country_name,omitempty"`
CountryDisplayName string `json:"country_display_name,omitempty"`
CountryFlag string `json:"country_flag,omitempty"`
SortOrder int32 `json:"sort_order,omitempty"`
}
type myRoomData struct {
HasRoom bool `json:"has_room"`
Room *roomListItemData `json:"room,omitempty"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomListItemData struct {
RoomID string `json:"room_id"`
IMGroupID string `json:"im_group_id"`
OwnerUserID string `json:"owner_user_id,omitempty"`
Title string `json:"title,omitempty"`
CoverURL string `json:"cover_url,omitempty"`
Mode string `json:"mode,omitempty"`
Status string `json:"status,omitempty"`
Locked bool `json:"locked"`
Heat int64 `json:"heat"`
OnlineCount int32 `json:"online_count"`
SeatCount int32 `json:"seat_count"`
OccupiedSeatCount int32 `json:"occupied_seat_count"`
VisibleRegionID int64 `json:"visible_region_id,omitempty"`
AppCode string `json:"app_code,omitempty"`
RoomShortID string `json:"room_short_id,omitempty"`
CountryCode string `json:"country_code,omitempty"`
CountryFlag string `json:"country_flag,omitempty"`
}
type createRoomData struct {
Result roomCommandResultData `json:"result"`
Room roomInitialData `json:"room"`
Seats []roomSeatData `json:"seats"`
IM roomIMData `json:"im"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type joinRoomData struct {
Result roomCommandResultData `json:"result"`
Room roomInitialData `json:"room"`
Viewer roomViewerData `json:"viewer"`
Seats []roomSeatData `json:"seats"`
ContributionRank []roomRankItemData `json:"contribution_rank"`
Profiles []roomDisplayProfileData `json:"profiles"`
IM roomIMData `json:"im"`
RTC roomRTCData `json:"rtc"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type joinRoomLiteData struct {
Result roomCommandResultData `json:"result"`
Room roomInitialData `json:"room"`
Viewer roomViewerData `json:"viewer"`
Seats []roomSeatData `json:"seats"`
Profiles []roomBasicProfileData `json:"profiles"`
IM roomIMData `json:"im"`
RTC roomRTCData `json:"rtc"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomDetailData struct {
Room roomInitialData `json:"room"`
Viewer roomViewerData `json:"viewer"`
IsFollowed bool `json:"is_followed"`
Seats []roomSeatData `json:"seats"`
RankTop []roomRankItemData `json:"rank_top"`
OnlineCount int32 `json:"online_count"`
Permissions roomPermissionsData `json:"permissions"`
Profiles []roomDisplayProfileData `json:"profiles,omitempty"`
IM roomIMData `json:"im"`
RTC roomRTCData `json:"rtc"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomOnlineUsersData struct {
Items []roomOnlineUserData `json:"items"`
Total int64 `json:"total"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomOnlineUserData struct {
UserID string `json:"user_id"`
Role string `json:"role,omitempty"`
IsOwner bool `json:"is_owner"`
RoomRole string `json:"room_role,omitempty"`
GiftValue int64 `json:"gift_value"`
JoinedAtMS int64 `json:"joined_at_ms,omitempty"`
LastSeenAtMS int64 `json:"last_seen_at_ms,omitempty"`
Profile *roomDisplayProfileData `json:"profile,omitempty"`
}
type roomBannedUsersData struct {
RoomID string `json:"room_id"`
Items []roomBannedUserData `json:"items"`
Total int64 `json:"total"`
Page int32 `json:"page"`
PageSize int32 `json:"page_size"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomBannedUserData struct {
UserID string `json:"user_id"`
DisplayUserID string `json:"display_user_id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
Country string `json:"country"`
CountryName string `json:"country_name"`
CountryDisplayName string `json:"country_display_name"`
CountryFlag string `json:"country_flag"`
ActorUserID string `json:"actor_user_id"`
CreatedAtMS int64 `json:"created_at_ms"`
UnbanAtMS int64 `json:"unban_at_ms"`
RemainingMS int64 `json:"remaining_ms"`
Permanent bool `json:"permanent"`
}
type roomContributionRankData struct {
RoomID string `json:"room_id"`
Period string `json:"period"`
StartAtMS int64 `json:"start_at_ms"`
EndAtMS int64 `json:"end_at_ms"`
Total int64 `json:"total"`
ContributionRank []roomContributionRankItemData `json:"contribution_rank"`
Profiles []roomDisplayProfileData `json:"profiles"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomContributionRankItemData struct {
Rank int64 `json:"rank"`
UserID string `json:"user_id"`
Score int64 `json:"score"`
GiftValue int64 `json:"gift_value"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
Profile *roomDisplayProfileData `json:"profile,omitempty"`
}
type setRoomAdminData struct {
Result roomCommandResultData `json:"result"`
TargetUserID string `json:"target_user_id"`
RoomRole string `json:"room_role"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomPermissionsData struct {
IsOwner bool `json:"is_owner"`
IsAdmin bool `json:"is_admin"`
IsMuted bool `json:"is_muted"`
CanSpeak bool `json:"can_speak"`
CanSendGift bool `json:"can_send_gift"`
CanMicUp bool `json:"can_mic_up"`
CanManageRoom bool `json:"can_manage_room"`
CanLeaveRoom bool `json:"can_leave_room"`
CanCloseRoom bool `json:"can_close_room"`
CanMuteMic bool `json:"can_mute_mic"`
CanOpenGiftBox bool `json:"can_open_gift_box"`
}
type roomDisplayProfileData struct {
UserID string `json:"user_id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
DisplayUserID string `json:"display_user_id"`
PrettyID string `json:"pretty_id,omitempty"`
PrettyDisplayUserID string `json:"pretty_display_user_id,omitempty"`
Gender string `json:"gender"`
Age int32 `json:"age"`
Country string `json:"country"`
CountryName string `json:"country_name"`
CountryDisplayName string `json:"country_display_name"`
CountryFlag string `json:"country_flag"`
VIP map[string]any `json:"vip"`
Level map[string]any `json:"level"`
LevelBadges map[string]any `json:"level_badges"`
Badges []map[string]any `json:"badges"`
AvatarFrame map[string]any `json:"avatar_frame"`
ProfileCard map[string]any `json:"profile_card"`
Vehicle map[string]any `json:"vehicle"`
MicSeatAnimation map[string]any `json:"mic_seat_animation"`
Charm int64 `json:"charm"`
}
type roomBasicProfileData struct {
UserID string `json:"user_id"`
Username string `json:"username"`
Avatar string `json:"avatar"`
DisplayUserID string `json:"display_user_id"`
}
type roomGiftPanelData struct {
CoinBalance int64 `json:"coin_balance"`
Recipients []roomGiftRecipientData `json:"recipients"`
Tabs []roomGiftTabData `json:"tabs"`
Gifts []giftConfigData `json:"gifts"`
QuantityPresets []int32 `json:"quantity_presets"`
}
type roomRocketData struct {
Enabled bool `json:"enabled"`
Levels []roomRocketLevelData `json:"levels"`
State roomRocketStateData `json:"state"`
BroadcastScope string `json:"broadcast_scope"`
LaunchDelayMS int64 `json:"launch_delay_ms"`
BroadcastDelayMS int64 `json:"broadcast_delay_ms"`
RewardStackPolicy string `json:"reward_stack_policy"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomRocketLevelData struct {
Level int32 `json:"level"`
FuelThreshold int64 `json:"fuel_threshold"`
CoverURL string `json:"cover_url,omitempty"`
AnimationURL string `json:"animation_url,omitempty"`
LaunchAnimationURL string `json:"launch_animation_url,omitempty"`
LaunchedImageURL string `json:"launched_image_url,omitempty"`
InRoomRewards []roomRocketRewardData `json:"in_room_rewards"`
Top1Rewards []roomRocketRewardData `json:"top1_rewards"`
IgniterRewards []roomRocketRewardData `json:"igniter_rewards"`
}
type roomRocketRewardData struct {
RewardItemID string `json:"reward_item_id"`
ResourceGroupID int64 `json:"resource_group_id"`
ResourceGroup *roomRocketResourceGroupData `json:"resource_group,omitempty"`
Weight int64 `json:"weight,omitempty"`
DisplayName string `json:"display_name,omitempty"`
IconURL string `json:"icon_url,omitempty"`
}
type roomRocketStateData struct {
CurrentLevel int32 `json:"current_level"`
CurrentFuel int64 `json:"current_fuel"`
FuelThreshold int64 `json:"fuel_threshold"`
Status string `json:"status"`
IgnitedAtMS int64 `json:"ignited_at_ms,omitempty"`
LaunchAtMS int64 `json:"launch_at_ms,omitempty"`
LaunchedAtMS int64 `json:"launched_at_ms,omitempty"`
ResetAtMS int64 `json:"reset_at_ms"`
Top1UserID string `json:"top1_user_id,omitempty"`
IgniterUserID string `json:"igniter_user_id,omitempty"`
RocketID string `json:"rocket_id,omitempty"`
ConfigVersion int64 `json:"config_version,omitempty"`
LastRewards []roomRocketRewardGrantData `json:"last_rewards,omitempty"`
PendingLaunches []roomRocketPendingLaunchData `json:"pending_launches,omitempty"`
}
type roomRocketPendingLaunchData struct {
RocketID string `json:"rocket_id"`
Level int32 `json:"level"`
FuelThreshold int64 `json:"fuel_threshold"`
IgnitedAtMS int64 `json:"ignited_at_ms"`
LaunchAtMS int64 `json:"launch_at_ms"`
ResetAtMS int64 `json:"reset_at_ms"`
Top1UserID string `json:"top1_user_id,omitempty"`
IgniterUserID string `json:"igniter_user_id,omitempty"`
ConfigVersion int64 `json:"config_version,omitempty"`
CoverURL string `json:"cover_url,omitempty"`
}
type roomRocketRewardGrantData struct {
RewardRole string `json:"reward_role"`
UserID string `json:"user_id"`
RewardItemID string `json:"reward_item_id"`
ResourceGroupID int64 `json:"resource_group_id"`
ResourceGroup *roomRocketResourceGroupData `json:"resource_group,omitempty"`
DisplayName string `json:"display_name,omitempty"`
IconURL string `json:"icon_url,omitempty"`
GrantID string `json:"grant_id,omitempty"`
Status string `json:"status,omitempty"`
}
type roomRocketResourceData struct {
ResourceID int64 `json:"resource_id"`
ResourceCode string `json:"resource_code"`
ResourceType string `json:"resource_type"`
Name string `json:"name"`
Status string `json:"status"`
Grantable bool `json:"grantable"`
GrantStrategy string `json:"grant_strategy"`
WalletAssetType string `json:"wallet_asset_type,omitempty"`
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
UsageScopes []string `json:"usage_scopes"`
AssetURL string `json:"asset_url"`
PreviewURL string `json:"preview_url"`
AnimationURL string `json:"animation_url"`
MetadataJSON string `json:"metadata_json"`
SortOrder int32 `json:"sort_order"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type roomRocketResourceGroupItemData struct {
GroupItemID int64 `json:"group_item_id"`
ItemType string `json:"item_type"`
ResourceID int64 `json:"resource_id"`
Resource *roomRocketResourceData `json:"resource,omitempty"`
WalletAssetType string `json:"wallet_asset_type,omitempty"`
WalletAssetAmount int64 `json:"wallet_asset_amount,omitempty"`
Quantity int64 `json:"quantity"`
DurationMS int64 `json:"duration_ms"`
SortOrder int32 `json:"sort_order"`
}
type roomRocketResourceGroupData struct {
GroupID int64 `json:"group_id"`
GroupCode string `json:"group_code"`
Name string `json:"name"`
Status string `json:"status"`
Description string `json:"description"`
SortOrder int32 `json:"sort_order"`
Items []roomRocketResourceGroupItemData `json:"items"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type roomGiftRecipientData struct {
TargetType string `json:"target_type"`
UserID string `json:"user_id,omitempty"`
SeatNo int32 `json:"seat_no,omitempty"`
Label string `json:"label,omitempty"`
Profile *roomDisplayProfileData `json:"profile,omitempty"`
}
type roomGiftTabData struct {
Key string `json:"key"`
GiftTypeCode string `json:"gift_type_code,omitempty"`
Label string `json:"label,omitempty"`
Order int32 `json:"order"`
}
type updateRoomProfileData struct {
Result roomCommandResultData `json:"result"`
Room roomInitialData `json:"room"`
Seats []roomSeatData `json:"seats"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomBackgroundData struct {
BackgroundID int64 `json:"background_id"`
RoomID string `json:"room_id"`
ImageURL string `json:"image_url"`
CreatedByUserID string `json:"created_by_user_id,omitempty"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type saveRoomBackgroundData struct {
Background roomBackgroundData `json:"background"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type listRoomBackgroundsData struct {
Backgrounds []roomBackgroundData `json:"backgrounds"`
SelectedBackgroundURL string `json:"selected_background_url,omitempty"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type setRoomBackgroundData struct {
Result roomCommandResultData `json:"result"`
Room roomInitialData `json:"room"`
Background roomBackgroundData `json:"background"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomSnapshotData struct {
Room roomInitialData `json:"room"`
Seats []roomSeatData `json:"seats"`
ContributionRank []roomRankItemData `json:"contribution_rank"`
IsFollowed bool `json:"is_followed"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomFollowData struct {
RoomID string `json:"room_id"`
IsFollowed bool `json:"is_followed"`
FollowedAtMS int64 `json:"followed_at_ms,omitempty"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomCommandResultData struct {
Applied bool `json:"applied"`
RoomVersion int64 `json:"room_version"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomInitialData struct {
RoomID string `json:"room_id"`
IMGroupID string `json:"im_group_id"`
RoomShortID string `json:"room_short_id,omitempty"`
Title string `json:"title,omitempty"`
CoverURL string `json:"cover_url,omitempty"`
BackgroundURL string `json:"background_url,omitempty"`
Description string `json:"description,omitempty"`
OwnerUserID string `json:"owner_user_id,omitempty"`
Mode string `json:"mode,omitempty"`
Status string `json:"status,omitempty"`
ChatEnabled bool `json:"chat_enabled"`
Locked bool `json:"locked"`
Heat int64 `json:"heat"`
OnlineCount int32 `json:"online_count"`
SeatCount int32 `json:"seat_count"`
OccupiedSeatCount int32 `json:"occupied_seat_count"`
VisibleRegionID int64 `json:"visible_region_id,omitempty"`
Version int64 `json:"version"`
}
type roomViewerData struct {
UserID string `json:"user_id"`
Role string `json:"role,omitempty"`
JoinedAtMS int64 `json:"joined_at_ms,omitempty"`
LastSeenAtMS int64 `json:"last_seen_at_ms,omitempty"`
}
type roomSeatData struct {
SeatNo int32 `json:"seat_no"`
UserID string `json:"user_id,omitempty"`
Locked bool `json:"locked"`
PublishState string `json:"publish_state,omitempty"`
MicSessionID string `json:"mic_session_id,omitempty"`
PublishDeadlineMS int64 `json:"publish_deadline_ms,omitempty"`
MicMuted bool `json:"mic_muted"`
SeatStatus string `json:"seat_status,omitempty"`
MicHeartbeatAtMS int64 `json:"mic_heartbeat_at_ms,omitempty"`
GiftValue int64 `json:"gift_value"`
}
type roomRankItemData struct {
UserID string `json:"user_id"`
Score int64 `json:"score"`
GiftValue int64 `json:"gift_value"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
}
type roomIMData struct {
GroupID string `json:"group_id"`
NeedJoinGroup bool `json:"need_join_group"`
}
type roomRTCData struct {
NeedToken bool `json:"need_token"`
Available bool `json:"available"`
Token *tencentrtc.TokenResult `json:"token,omitempty"`
Reason string `json:"reason,omitempty"`
}
func roomListDataFromProto(resp *roomv1.ListRoomsResponse) roomListData {
if resp == nil {
return roomListData{}
}
items := make([]roomListItemData, 0, len(resp.GetRooms()))
for _, room := range resp.GetRooms() {
items = append(items, roomListItemDataFromProto(room))
}
return roomListData{Rooms: items, NextCursor: resp.GetNextCursor()}
}
func myRoomDataFromProto(resp *roomv1.GetMyRoomResponse) myRoomData {
if resp == nil {
return myRoomData{}
}
data := myRoomData{HasRoom: resp.GetHasRoom(), ServerTimeMS: resp.GetServerTimeMs()}
if resp.GetHasRoom() && resp.GetRoom() != nil {
room := roomListItemDataFromProto(resp.GetRoom())
data.Room = &room
}
return data
}
func createRoomDataFromProto(resp *roomv1.CreateRoomResponse) createRoomData {
if resp == nil {
return createRoomData{}
}
snapshot := resp.GetRoom()
roomID := snapshot.GetRoomId()
return createRoomData{
Result: commandResultDataFromProto(resp.GetResult()),
Room: roomInitialRoomDataFromSnapshot(snapshot, roomID),
Seats: roomSeatDataFromSnapshot(snapshot),
IM: roomIMData{GroupID: roomIMGroupID(roomID), NeedJoinGroup: true},
ServerTimeMS: resp.GetResult().GetServerTimeMs(),
}
}
func roomRocketDataFromProto(resp *roomv1.GetRoomRocketResponse, resourceGroups map[int64]*roomRocketResourceGroupData) roomRocketData {
if resp == nil || resp.GetRocket() == nil {
return roomRocketData{}
}
rocket := resp.GetRocket()
return roomRocketData{
Enabled: rocket.GetEnabled(),
Levels: roomRocketLevelsFromProto(rocket.GetLevels(), resourceGroups),
State: roomRocketStateFromProto(rocket.GetState(), resourceGroups),
BroadcastScope: rocket.GetBroadcastScope(),
LaunchDelayMS: rocket.GetLaunchDelayMs(),
BroadcastDelayMS: rocket.GetBroadcastDelayMs(),
RewardStackPolicy: rocket.GetRewardStackPolicy(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomRocketLevelsFromProto(levels []*roomv1.RoomRocketLevel, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketLevelData {
out := make([]roomRocketLevelData, 0, len(levels))
for _, level := range levels {
out = append(out, roomRocketLevelData{
Level: level.GetLevel(),
FuelThreshold: level.GetFuelThreshold(),
CoverURL: level.GetCoverUrl(),
AnimationURL: level.GetAnimationUrl(),
LaunchAnimationURL: level.GetLaunchAnimationUrl(),
LaunchedImageURL: level.GetLaunchedImageUrl(),
InRoomRewards: roomRocketRewardsFromProto(level.GetInRoomRewards(), resourceGroups),
Top1Rewards: roomRocketRewardsFromProto(level.GetTop1Rewards(), resourceGroups),
IgniterRewards: roomRocketRewardsFromProto(level.GetIgniterRewards(), resourceGroups),
})
}
return out
}
func roomRocketRewardsFromProto(rewards []*roomv1.RoomRocketRewardItem, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketRewardData {
out := make([]roomRocketRewardData, 0, len(rewards))
for _, reward := range rewards {
out = append(out, roomRocketRewardData{
RewardItemID: reward.GetRewardItemId(),
ResourceGroupID: reward.GetResourceGroupId(),
ResourceGroup: resourceGroups[reward.GetResourceGroupId()],
Weight: reward.GetWeight(),
DisplayName: reward.GetDisplayName(),
IconURL: reward.GetIconUrl(),
})
}
return out
}
func roomRocketStateFromProto(state *roomv1.RoomRocketState, resourceGroups map[int64]*roomRocketResourceGroupData) roomRocketStateData {
if state == nil {
return roomRocketStateData{}
}
return roomRocketStateData{
CurrentLevel: state.GetCurrentLevel(),
CurrentFuel: state.GetCurrentFuel(),
FuelThreshold: state.GetFuelThreshold(),
Status: state.GetStatus(),
IgnitedAtMS: state.GetIgnitedAtMs(),
LaunchAtMS: state.GetLaunchAtMs(),
LaunchedAtMS: state.GetLaunchedAtMs(),
ResetAtMS: state.GetResetAtMs(),
Top1UserID: formatOptionalUserID(state.GetTop1UserId()),
IgniterUserID: formatOptionalUserID(state.GetIgniterUserId()),
RocketID: state.GetRocketId(),
ConfigVersion: state.GetConfigVersion(),
LastRewards: roomRocketRewardGrantsFromProto(state.GetLastRewards(), resourceGroups),
PendingLaunches: roomRocketPendingLaunchesFromProto(state.GetPendingLaunches()),
}
}
func roomRocketPendingLaunchesFromProto(launches []*roomv1.RoomRocketPendingLaunch) []roomRocketPendingLaunchData {
out := make([]roomRocketPendingLaunchData, 0, len(launches))
for _, launch := range launches {
out = append(out, roomRocketPendingLaunchData{
RocketID: launch.GetRocketId(),
Level: launch.GetLevel(),
FuelThreshold: launch.GetFuelThreshold(),
IgnitedAtMS: launch.GetIgnitedAtMs(),
LaunchAtMS: launch.GetLaunchAtMs(),
ResetAtMS: launch.GetResetAtMs(),
Top1UserID: formatOptionalUserID(launch.GetTop1UserId()),
IgniterUserID: formatOptionalUserID(launch.GetIgniterUserId()),
ConfigVersion: launch.GetConfigVersion(),
CoverURL: launch.GetCoverUrl(),
})
}
return out
}
func roomRocketRewardGrantsFromProto(rewards []*roomv1.RoomRocketRewardGrant, resourceGroups map[int64]*roomRocketResourceGroupData) []roomRocketRewardGrantData {
out := make([]roomRocketRewardGrantData, 0, len(rewards))
for _, reward := range rewards {
out = append(out, roomRocketRewardGrantData{
RewardRole: reward.GetRewardRole(),
UserID: formatOptionalUserID(reward.GetUserId()),
RewardItemID: reward.GetRewardItemId(),
ResourceGroupID: reward.GetResourceGroupId(),
ResourceGroup: resourceGroups[reward.GetResourceGroupId()],
DisplayName: reward.GetDisplayName(),
IconURL: reward.GetIconUrl(),
GrantID: reward.GetGrantId(),
Status: reward.GetStatus(),
})
}
return out
}
func roomRocketResourceGroupIDs(resp *roomv1.GetRoomRocketResponse) []int64 {
if resp == nil || resp.GetRocket() == nil {
return nil
}
seen := map[int64]struct{}{}
ids := make([]int64, 0)
add := func(id int64) {
if id <= 0 {
return
}
if _, exists := seen[id]; exists {
return
}
seen[id] = struct{}{}
ids = append(ids, id)
}
for _, level := range resp.GetRocket().GetLevels() {
for _, reward := range level.GetInRoomRewards() {
add(reward.GetResourceGroupId())
}
for _, reward := range level.GetTop1Rewards() {
add(reward.GetResourceGroupId())
}
for _, reward := range level.GetIgniterRewards() {
add(reward.GetResourceGroupId())
}
}
for _, reward := range resp.GetRocket().GetState().GetLastRewards() {
add(reward.GetResourceGroupId())
}
return ids
}
func roomRocketResourceGroupFromProto(group *walletv1.ResourceGroup) *roomRocketResourceGroupData {
if group == nil {
return nil
}
items := make([]roomRocketResourceGroupItemData, 0, len(group.GetItems()))
for _, item := range group.GetItems() {
items = append(items, roomRocketResourceGroupItemData{
GroupItemID: item.GetGroupItemId(),
ItemType: item.GetItemType(),
ResourceID: item.GetResourceId(),
Resource: roomRocketResourceFromProto(item.GetResource()),
WalletAssetType: item.GetWalletAssetType(),
WalletAssetAmount: item.GetWalletAssetAmount(),
Quantity: item.GetQuantity(),
DurationMS: item.GetDurationMs(),
SortOrder: item.GetSortOrder(),
})
}
return &roomRocketResourceGroupData{
GroupID: group.GetGroupId(),
GroupCode: group.GetGroupCode(),
Name: group.GetName(),
Status: group.GetStatus(),
Description: group.GetDescription(),
SortOrder: group.GetSortOrder(),
Items: items,
CreatedAtMS: group.GetCreatedAtMs(),
UpdatedAtMS: group.GetUpdatedAtMs(),
}
}
func roomRocketResourceFromProto(item *walletv1.Resource) *roomRocketResourceData {
if item == nil {
return nil
}
return &roomRocketResourceData{
ResourceID: item.GetResourceId(),
ResourceCode: item.GetResourceCode(),
ResourceType: item.GetResourceType(),
Name: item.GetName(),
Status: item.GetStatus(),
Grantable: item.GetGrantable(),
GrantStrategy: item.GetGrantStrategy(),
WalletAssetType: item.GetWalletAssetType(),
WalletAssetAmount: item.GetWalletAssetAmount(),
UsageScopes: item.GetUsageScopes(),
AssetURL: item.GetAssetUrl(),
PreviewURL: item.GetPreviewUrl(),
AnimationURL: item.GetAnimationUrl(),
MetadataJSON: item.GetMetadataJson(),
SortOrder: item.GetSortOrder(),
CreatedAtMS: item.GetCreatedAtMs(),
UpdatedAtMS: item.GetUpdatedAtMs(),
}
}
func roomListItemDataFromProto(room *roomv1.RoomListItem) roomListItemData {
if room == nil {
return roomListItemData{}
}
roomID := room.GetRoomId()
return roomListItemData{
RoomID: roomID,
IMGroupID: roomIMGroupID(roomID),
OwnerUserID: formatOptionalUserID(room.GetOwnerUserId()),
Title: room.GetTitle(),
CoverURL: room.GetCoverUrl(),
Mode: room.GetMode(),
Status: room.GetStatus(),
Locked: room.GetLocked(),
Heat: room.GetHeat(),
OnlineCount: room.GetOnlineCount(),
SeatCount: room.GetSeatCount(),
OccupiedSeatCount: room.GetOccupiedSeatCount(),
VisibleRegionID: room.GetVisibleRegionId(),
AppCode: room.GetAppCode(),
RoomShortID: room.GetRoomShortId(),
CountryCode: room.GetCountryCode(),
CountryFlag: roomProfileCountryFlag(room.GetCountryCode()),
}
}
func commandResultDataFromProto(result *roomv1.CommandResult) roomCommandResultData {
if result == nil {
return roomCommandResultData{}
}
return roomCommandResultData{
Applied: result.GetApplied(),
RoomVersion: result.GetRoomVersion(),
ServerTimeMS: result.GetServerTimeMs(),
}
}
func updateRoomProfileDataFromProto(resp *roomv1.UpdateRoomProfileResponse) updateRoomProfileData {
if resp == nil {
return updateRoomProfileData{}
}
snapshot := resp.GetRoom()
return updateRoomProfileData{
Result: commandResultDataFromProto(resp.GetResult()),
Room: roomInitialRoomDataFromSnapshot(snapshot, snapshot.GetRoomId()),
Seats: roomSeatDataFromSnapshot(snapshot),
ServerTimeMS: resp.GetResult().GetServerTimeMs(),
}
}
func saveRoomBackgroundDataFromProto(resp *roomv1.SaveRoomBackgroundResponse) saveRoomBackgroundData {
if resp == nil {
return saveRoomBackgroundData{}
}
return saveRoomBackgroundData{
Background: roomBackgroundDataFromProto(resp.GetBackground()),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func listRoomBackgroundsDataFromProto(resp *roomv1.ListRoomBackgroundsResponse) listRoomBackgroundsData {
if resp == nil {
return listRoomBackgroundsData{}
}
backgrounds := make([]roomBackgroundData, 0, len(resp.GetBackgrounds()))
for _, background := range resp.GetBackgrounds() {
backgrounds = append(backgrounds, roomBackgroundDataFromProto(background))
}
return listRoomBackgroundsData{
Backgrounds: backgrounds,
SelectedBackgroundURL: resp.GetSelectedBackgroundUrl(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func setRoomBackgroundDataFromProto(resp *roomv1.SetRoomBackgroundResponse) setRoomBackgroundData {
if resp == nil {
return setRoomBackgroundData{}
}
snapshot := resp.GetRoom()
return setRoomBackgroundData{
Result: commandResultDataFromProto(resp.GetResult()),
Room: roomInitialRoomDataFromSnapshot(snapshot, snapshot.GetRoomId()),
Background: roomBackgroundDataFromProto(resp.GetBackground()),
ServerTimeMS: resp.GetResult().GetServerTimeMs(),
}
}
func roomBackgroundDataFromProto(background *roomv1.RoomBackgroundImage) roomBackgroundData {
if background == nil {
return roomBackgroundData{}
}
return roomBackgroundData{
BackgroundID: background.GetBackgroundId(),
RoomID: background.GetRoomId(),
ImageURL: background.GetImageUrl(),
CreatedByUserID: formatOptionalUserID(background.GetCreatedByUserId()),
CreatedAtMS: background.GetCreatedAtMs(),
UpdatedAtMS: background.GetUpdatedAtMs(),
}
}
func roomSnapshotDataFromProto(resp *roomv1.GetRoomSnapshotResponse) roomSnapshotData {
if resp == nil {
return roomSnapshotData{}
}
snapshot := resp.GetRoom()
return roomSnapshotData{
Room: roomInitialRoomDataFromSnapshot(snapshot, snapshot.GetRoomId()),
Seats: roomSeatDataFromSnapshot(snapshot),
ContributionRank: roomRankDataFromSnapshot(snapshot, 0),
IsFollowed: resp.GetIsFollowed(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomInitialRoomDataFromSnapshot(snapshot *roomv1.RoomSnapshot, roomID string) roomInitialData {
if snapshot == nil {
return roomInitialData{RoomID: roomID, IMGroupID: roomIMGroupID(roomID)}
}
ext := snapshot.GetRoomExt()
if roomID == "" {
roomID = snapshot.GetRoomId()
}
roomShortID := snapshot.GetRoomShortId()
if roomShortID == "" {
roomShortID = ext["room_short_id"]
}
seatCount, occupiedSeatCount := roomSeatCounts(snapshot)
onlineCount := snapshot.GetOnlineCount()
if onlineCount <= 0 && len(snapshot.GetOnlineUsers()) > 0 {
// 老快照或非 lite 上游可能还没有 online_count 字段;此时用明细长度保持兼容。
onlineCount = int32(len(snapshot.GetOnlineUsers()))
}
return roomInitialData{
RoomID: roomID,
IMGroupID: roomIMGroupID(roomID),
RoomShortID: roomShortID,
Title: ext["title"],
CoverURL: ext["cover_url"],
BackgroundURL: ext["background_url"],
Description: ext["description"],
OwnerUserID: formatOptionalUserID(snapshot.GetOwnerUserId()),
Mode: snapshot.GetMode(),
Status: snapshot.GetStatus(),
ChatEnabled: snapshot.GetChatEnabled(),
Locked: snapshot.GetLocked(),
Heat: snapshot.GetHeat(),
OnlineCount: onlineCount,
SeatCount: seatCount,
OccupiedSeatCount: occupiedSeatCount,
VisibleRegionID: snapshot.GetVisibleRegionId(),
Version: snapshot.GetVersion(),
}
}
func roomViewerDataFromProto(user *roomv1.RoomUser, fallbackUserID int64) roomViewerData {
userID := fallbackUserID
if user.GetUserId() > 0 {
userID = user.GetUserId()
}
return roomViewerData{
UserID: formatOptionalUserID(userID),
Role: user.GetRole(),
JoinedAtMS: user.GetJoinedAtMs(),
LastSeenAtMS: user.GetLastSeenAtMs(),
}
}
func roomSeatDataFromSnapshot(snapshot *roomv1.RoomSnapshot) []roomSeatData {
seats := snapshot.GetMicSeats()
items := make([]roomSeatData, 0, len(seats))
for _, seat := range seats {
items = append(items, roomSeatData{
SeatNo: seat.GetSeatNo(),
UserID: formatOptionalUserID(seat.GetUserId()),
Locked: seat.GetLocked(),
PublishState: seat.GetPublishState(),
MicSessionID: seat.GetMicSessionId(),
PublishDeadlineMS: seat.GetPublishDeadlineMs(),
MicMuted: seat.GetMicMuted(),
SeatStatus: seat.GetSeatStatus(),
MicHeartbeatAtMS: seat.GetMicHeartbeatAtMs(),
GiftValue: seat.GetGiftValue(),
})
}
return items
}
func roomDetailDataFromSnapshot(resp *roomv1.GetRoomSnapshotResponse, viewerUserID int64, rtc roomRTCData, profiles []roomDisplayProfileData) roomDetailData {
if resp == nil {
return roomDetailData{}
}
snapshot := resp.GetRoom()
roomID := snapshot.GetRoomId()
viewer := protoRoomUserByID(snapshot, viewerUserID)
return roomDetailData{
Room: roomInitialRoomDataFromSnapshot(snapshot, roomID),
Viewer: roomViewerDataFromProto(viewer, viewerUserID),
IsFollowed: resp.GetIsFollowed(),
Seats: roomSeatDataFromSnapshot(snapshot),
RankTop: roomRankDataFromSnapshot(snapshot, joinRoomContributionRankLimit),
OnlineCount: int32(len(snapshot.GetOnlineUsers())),
Permissions: roomPermissionsFromSnapshot(snapshot, viewerUserID),
Profiles: profiles,
IM: roomIMData{GroupID: roomIMGroupID(roomID), NeedJoinGroup: true},
RTC: rtc,
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomFollowDataFromProto(resp *roomv1.FollowRoomResponse) roomFollowData {
if resp == nil {
return roomFollowData{}
}
return roomFollowData{
RoomID: resp.GetRoomId(),
IsFollowed: resp.GetFollowed(),
FollowedAtMS: resp.GetFollowedAtMs(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomUnfollowDataFromProto(resp *roomv1.UnfollowRoomResponse) roomFollowData {
if resp == nil {
return roomFollowData{}
}
return roomFollowData{
RoomID: resp.GetRoomId(),
IsFollowed: resp.GetFollowed(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomPermissionsFromSnapshot(snapshot *roomv1.RoomSnapshot, viewerUserID int64) roomPermissionsData {
if snapshot == nil || viewerUserID <= 0 {
return roomPermissionsData{}
}
isOwner := snapshot.GetOwnerUserId() == viewerUserID
isAdmin := !isOwner && containsInt64(snapshot.GetAdminUserIds(), viewerUserID)
isMuted := containsInt64(snapshot.GetMuteUserIds(), viewerUserID)
inRoom := protoRoomUserByID(snapshot, viewerUserID) != nil
return roomPermissionsData{
IsOwner: isOwner,
IsAdmin: isAdmin,
IsMuted: isMuted,
CanSpeak: inRoom && snapshot.GetChatEnabled() && !isMuted,
CanSendGift: inRoom && snapshot.GetStatus() == "active",
CanMicUp: inRoom && snapshot.GetStatus() == "active" && protoSeatByUserID(snapshot, viewerUserID) == nil,
CanManageRoom: inRoom && (isOwner || isAdmin),
CanLeaveRoom: inRoom,
CanCloseRoom: inRoom,
CanMuteMic: inRoom && protoSeatByUserID(snapshot, viewerUserID) != nil,
CanOpenGiftBox: inRoom && snapshot.GetStatus() == "active",
}
}
func roomRankDataFromSnapshot(snapshot *roomv1.RoomSnapshot, limit int) []roomRankItemData {
rank := snapshot.GetGiftRank()
if limit > 0 && len(rank) > limit {
rank = rank[:limit]
}
items := make([]roomRankItemData, 0, len(rank))
for _, item := range rank {
items = append(items, roomRankItemData{
UserID: formatOptionalUserID(item.GetUserId()),
Score: item.GetScore(),
GiftValue: item.GetGiftValue(),
UpdatedAtMS: item.GetUpdatedAtMs(),
})
}
return items
}
func roomInitialProfileUserIDs(snapshot *roomv1.RoomSnapshot, viewerUserID int64, rankLimit int) []int64 {
seen := map[int64]bool{}
userIDs := make([]int64, 0, 16)
add := func(userID int64) {
if userID <= 0 || seen[userID] {
return
}
seen[userID] = true
userIDs = append(userIDs, userID)
}
add(snapshot.GetOwnerUserId())
add(viewerUserID)
for _, seat := range snapshot.GetMicSeats() {
add(seat.GetUserId())
}
rank := snapshot.GetGiftRank()
if rankLimit > 0 && len(rank) > rankLimit {
rank = rank[:rankLimit]
}
for _, item := range rank {
add(item.GetUserId())
}
for _, userID := range robotRoomProfileUserIDs(snapshot) {
// 机器人房间的礼物时间线会马上用机器人 sender/target 资料;首屏带出这些 profile避免 Flutter 落到 User <id> 兜底。
add(userID)
}
return userIDs
}
func roomInitialBasicProfileUserIDs(snapshot *roomv1.RoomSnapshot, viewerUserID int64) []int64 {
seen := map[int64]bool{}
userIDs := make([]int64, 0, len(snapshot.GetMicSeats())+2)
add := func(userID int64) {
if userID <= 0 || seen[userID] {
return
}
seen[userID] = true
userIDs = append(userIDs, userID)
}
add(snapshot.GetOwnerUserId())
add(viewerUserID)
for _, seat := range snapshot.GetMicSeats() {
add(seat.GetUserId())
}
return userIDs
}
func roomOnlineUserDataFromProto(resp *roomv1.ListRoomOnlineUsersResponse, profiles map[int64]roomDisplayProfileData) roomOnlineUsersData {
if resp == nil {
return roomOnlineUsersData{}
}
source := resp.GetItems()
if len(source) == 0 {
source = roomOnlineUsersFromLegacyUsers(resp.GetUsers())
}
items := make([]roomOnlineUserData, 0, len(source))
for _, user := range source {
item := roomOnlineUserData{
UserID: formatOptionalUserID(user.GetUserId()),
Role: user.GetRole(),
IsOwner: user.GetIsOwner(),
RoomRole: user.GetRoomRole(),
GiftValue: user.GetGiftValue(),
JoinedAtMS: user.GetJoinedAtMs(),
LastSeenAtMS: user.GetLastSeenAtMs(),
}
if profile, ok := profiles[user.GetUserId()]; ok {
copied := profile
item.Profile = &copied
}
items = append(items, item)
}
return roomOnlineUsersData{
Items: items,
Total: resp.GetTotal(),
Page: resp.GetPage(),
PageSize: resp.GetPageSize(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomBannedUsersDataFromProto(roomID string, resp *roomv1.ListRoomBannedUsersResponse, profiles map[int64]roomDisplayProfileData) roomBannedUsersData {
if resp == nil {
return roomBannedUsersData{RoomID: roomID}
}
items := make([]roomBannedUserData, 0, len(resp.GetItems()))
for _, user := range resp.GetItems() {
item := roomBannedUserData{
UserID: formatOptionalUserID(user.GetUserId()),
ActorUserID: formatOptionalUserID(user.GetActorUserId()),
CreatedAtMS: user.GetCreatedAtMs(),
UnbanAtMS: user.GetUnbanAtMs(),
RemainingMS: user.GetRemainingMs(),
Permanent: user.GetPermanent(),
}
if profile, ok := profiles[user.GetUserId()]; ok {
item.DisplayUserID = profile.DisplayUserID
item.Username = profile.Username
item.Avatar = profile.Avatar
item.Country = profile.Country
item.CountryName = profile.CountryName
item.CountryDisplayName = profile.CountryDisplayName
item.CountryFlag = profile.CountryFlag
}
items = append(items, item)
}
return roomBannedUsersData{
RoomID: roomID,
Items: items,
Total: resp.GetTotal(),
Page: resp.GetPage(),
PageSize: resp.GetPageSize(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomContributionRankDataFromProto(resp *roomv1.ListRoomContributionRankResponse, profiles map[int64]roomDisplayProfileData) roomContributionRankData {
if resp == nil {
return roomContributionRankData{}
}
items := make([]roomContributionRankItemData, 0, len(resp.GetItems()))
for _, rankItem := range resp.GetItems() {
item := roomContributionRankItemData{
Rank: rankItem.GetRank(),
UserID: formatOptionalUserID(rankItem.GetUserId()),
Score: rankItem.GetScore(),
GiftValue: rankItem.GetGiftValue(),
UpdatedAtMS: rankItem.GetUpdatedAtMs(),
}
if profile, ok := profiles[rankItem.GetUserId()]; ok {
copied := profile
item.Profile = &copied
}
items = append(items, item)
}
profileItems := make([]roomDisplayProfileData, 0, len(profiles))
for _, rankItem := range resp.GetItems() {
if profile, ok := profiles[rankItem.GetUserId()]; ok {
profileItems = append(profileItems, profile)
}
}
return roomContributionRankData{
RoomID: resp.GetRoomId(),
Period: resp.GetPeriod(),
StartAtMS: resp.GetStartAtMs(),
EndAtMS: resp.GetEndAtMs(),
Total: resp.GetTotal(),
ContributionRank: items,
Profiles: profileItems,
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomOnlineUsersFromLegacyUsers(users []*roomv1.RoomUser) []*roomv1.RoomOnlineUser {
items := make([]*roomv1.RoomOnlineUser, 0, len(users))
for _, user := range users {
items = append(items, &roomv1.RoomOnlineUser{
UserId: user.GetUserId(),
Role: user.GetRole(),
JoinedAtMs: user.GetJoinedAtMs(),
LastSeenAtMs: user.GetLastSeenAtMs(),
RoomRole: "normal",
})
}
return items
}
func protoRoomUserByID(snapshot *roomv1.RoomSnapshot, userID int64) *roomv1.RoomUser {
if snapshot == nil || userID <= 0 {
return nil
}
for _, user := range snapshot.GetOnlineUsers() {
if user.GetUserId() == userID {
return user
}
}
return nil
}
func protoSeatByUserID(snapshot *roomv1.RoomSnapshot, userID int64) *roomv1.SeatState {
if snapshot == nil || userID <= 0 {
return nil
}
for _, seat := range snapshot.GetMicSeats() {
if seat.GetUserId() == userID {
return seat
}
}
return nil
}
func containsInt64(values []int64, target int64) bool {
for _, value := range values {
if value == target {
return true
}
}
return false
}
func roomSeatCounts(snapshot *roomv1.RoomSnapshot) (int32, int32) {
seatCount := int32(len(snapshot.GetMicSeats()))
occupiedSeatCount := int32(0)
for _, seat := range snapshot.GetMicSeats() {
if seat.GetUserId() > 0 {
occupiedSeatCount++
}
}
return seatCount, occupiedSeatCount
}
func roomIMGroupID(roomID string) string {
// 房间 IM 群 ID 必须等于内部 room_id线上腾讯云已按这个 ID 建群,客户端也按这个字段入群。
groupID, err := imgroup.RoomGroupID(roomID)
if err != nil {
return roomID
}
return groupID
}
func formatOptionalUserID(userID int64) string {
if userID <= 0 {
return ""
}
return strconv.FormatInt(userID, 10)
}