完善我的页面房间关系接口

This commit is contained in:
zhx 2026-05-11 17:53:18 +08:00
parent b95224bff1
commit 8fcf491b74
20 changed files with 1763 additions and 1110 deletions

View File

@ -3187,6 +3187,8 @@ type ListRoomFeedsRequest struct {
Cursor string `protobuf:"bytes,5,opt,name=cursor,proto3" json:"cursor,omitempty"`
Limit int32 `protobuf:"varint,6,opt,name=limit,proto3" json:"limit,omitempty"`
Query string `protobuf:"bytes,7,opt,name=query,proto3" json:"query,omitempty"`
// related_users 只用于 friend/following关系事实由 gateway 从 user-service 读取后传入。
RelatedUsers []*RoomFeedRelatedUser `protobuf:"bytes,8,rep,name=related_users,json=relatedUsers,proto3" json:"related_users,omitempty"`
}
func (x *ListRoomFeedsRequest) Reset() {
@ -3268,6 +3270,67 @@ func (x *ListRoomFeedsRequest) GetQuery() string {
return ""
}
func (x *ListRoomFeedsRequest) GetRelatedUsers() []*RoomFeedRelatedUser {
if x != nil {
return x.RelatedUsers
}
return nil
}
// RoomFeedRelatedUser 是 Mine 关系房间流的排序输入,不让 room-service 持有好友/关注事实。
type RoomFeedRelatedUser struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
UserId int64 `protobuf:"varint,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
RelationUpdatedAtMs int64 `protobuf:"varint,2,opt,name=relation_updated_at_ms,json=relationUpdatedAtMs,proto3" json:"relation_updated_at_ms,omitempty"`
}
func (x *RoomFeedRelatedUser) Reset() {
*x = RoomFeedRelatedUser{}
mi := &file_proto_room_v1_room_proto_msgTypes[48]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RoomFeedRelatedUser) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RoomFeedRelatedUser) ProtoMessage() {}
func (x *RoomFeedRelatedUser) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[48]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use RoomFeedRelatedUser.ProtoReflect.Descriptor instead.
func (*RoomFeedRelatedUser) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{48}
}
func (x *RoomFeedRelatedUser) GetUserId() int64 {
if x != nil {
return x.UserId
}
return 0
}
func (x *RoomFeedRelatedUser) GetRelationUpdatedAtMs() int64 {
if x != nil {
return x.RelationUpdatedAtMs
}
return 0
}
// RoomListItem 是房间发现页卡片读模型,不承载完整 Room Cell 状态。
type RoomListItem struct {
state protoimpl.MessageState
@ -3292,7 +3355,7 @@ type RoomListItem struct {
func (x *RoomListItem) Reset() {
*x = RoomListItem{}
mi := &file_proto_room_v1_room_proto_msgTypes[48]
mi := &file_proto_room_v1_room_proto_msgTypes[49]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3304,7 +3367,7 @@ func (x *RoomListItem) String() string {
func (*RoomListItem) ProtoMessage() {}
func (x *RoomListItem) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[48]
mi := &file_proto_room_v1_room_proto_msgTypes[49]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3317,7 +3380,7 @@ func (x *RoomListItem) ProtoReflect() protoreflect.Message {
// Deprecated: Use RoomListItem.ProtoReflect.Descriptor instead.
func (*RoomListItem) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{48}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{49}
}
func (x *RoomListItem) GetRoomId() string {
@ -3430,7 +3493,7 @@ type ListRoomsResponse struct {
func (x *ListRoomsResponse) Reset() {
*x = ListRoomsResponse{}
mi := &file_proto_room_v1_room_proto_msgTypes[49]
mi := &file_proto_room_v1_room_proto_msgTypes[50]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3442,7 +3505,7 @@ func (x *ListRoomsResponse) String() string {
func (*ListRoomsResponse) ProtoMessage() {}
func (x *ListRoomsResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[49]
mi := &file_proto_room_v1_room_proto_msgTypes[50]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3455,7 +3518,7 @@ func (x *ListRoomsResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListRoomsResponse.ProtoReflect.Descriptor instead.
func (*ListRoomsResponse) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{49}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{50}
}
func (x *ListRoomsResponse) GetRooms() []*RoomListItem {
@ -3485,7 +3548,7 @@ type GetMyRoomRequest struct {
func (x *GetMyRoomRequest) Reset() {
*x = GetMyRoomRequest{}
mi := &file_proto_room_v1_room_proto_msgTypes[50]
mi := &file_proto_room_v1_room_proto_msgTypes[51]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3497,7 +3560,7 @@ func (x *GetMyRoomRequest) String() string {
func (*GetMyRoomRequest) ProtoMessage() {}
func (x *GetMyRoomRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[50]
mi := &file_proto_room_v1_room_proto_msgTypes[51]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3510,7 +3573,7 @@ func (x *GetMyRoomRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetMyRoomRequest.ProtoReflect.Descriptor instead.
func (*GetMyRoomRequest) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{50}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{51}
}
func (x *GetMyRoomRequest) GetMeta() *RequestMeta {
@ -3541,7 +3604,7 @@ type GetMyRoomResponse struct {
func (x *GetMyRoomResponse) Reset() {
*x = GetMyRoomResponse{}
mi := &file_proto_room_v1_room_proto_msgTypes[51]
mi := &file_proto_room_v1_room_proto_msgTypes[52]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3553,7 +3616,7 @@ func (x *GetMyRoomResponse) String() string {
func (*GetMyRoomResponse) ProtoMessage() {}
func (x *GetMyRoomResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[51]
mi := &file_proto_room_v1_room_proto_msgTypes[52]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3566,7 +3629,7 @@ func (x *GetMyRoomResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetMyRoomResponse.ProtoReflect.Descriptor instead.
func (*GetMyRoomResponse) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{51}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{52}
}
func (x *GetMyRoomResponse) GetHasRoom() bool {
@ -3603,7 +3666,7 @@ type GetCurrentRoomRequest struct {
func (x *GetCurrentRoomRequest) Reset() {
*x = GetCurrentRoomRequest{}
mi := &file_proto_room_v1_room_proto_msgTypes[52]
mi := &file_proto_room_v1_room_proto_msgTypes[53]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3615,7 +3678,7 @@ func (x *GetCurrentRoomRequest) String() string {
func (*GetCurrentRoomRequest) ProtoMessage() {}
func (x *GetCurrentRoomRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[52]
mi := &file_proto_room_v1_room_proto_msgTypes[53]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3628,7 +3691,7 @@ func (x *GetCurrentRoomRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetCurrentRoomRequest.ProtoReflect.Descriptor instead.
func (*GetCurrentRoomRequest) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{52}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{53}
}
func (x *GetCurrentRoomRequest) GetMeta() *RequestMeta {
@ -3665,7 +3728,7 @@ type GetCurrentRoomResponse struct {
func (x *GetCurrentRoomResponse) Reset() {
*x = GetCurrentRoomResponse{}
mi := &file_proto_room_v1_room_proto_msgTypes[53]
mi := &file_proto_room_v1_room_proto_msgTypes[54]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3677,7 +3740,7 @@ func (x *GetCurrentRoomResponse) String() string {
func (*GetCurrentRoomResponse) ProtoMessage() {}
func (x *GetCurrentRoomResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[53]
mi := &file_proto_room_v1_room_proto_msgTypes[54]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3690,7 +3753,7 @@ func (x *GetCurrentRoomResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetCurrentRoomResponse.ProtoReflect.Descriptor instead.
func (*GetCurrentRoomResponse) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{53}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{54}
}
func (x *GetCurrentRoomResponse) GetHasCurrentRoom() bool {
@ -3770,7 +3833,7 @@ type GetRoomSnapshotRequest struct {
func (x *GetRoomSnapshotRequest) Reset() {
*x = GetRoomSnapshotRequest{}
mi := &file_proto_room_v1_room_proto_msgTypes[54]
mi := &file_proto_room_v1_room_proto_msgTypes[55]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3782,7 +3845,7 @@ func (x *GetRoomSnapshotRequest) String() string {
func (*GetRoomSnapshotRequest) ProtoMessage() {}
func (x *GetRoomSnapshotRequest) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[54]
mi := &file_proto_room_v1_room_proto_msgTypes[55]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3795,7 +3858,7 @@ func (x *GetRoomSnapshotRequest) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRoomSnapshotRequest.ProtoReflect.Descriptor instead.
func (*GetRoomSnapshotRequest) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{54}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{55}
}
func (x *GetRoomSnapshotRequest) GetMeta() *RequestMeta {
@ -3831,7 +3894,7 @@ type GetRoomSnapshotResponse struct {
func (x *GetRoomSnapshotResponse) Reset() {
*x = GetRoomSnapshotResponse{}
mi := &file_proto_room_v1_room_proto_msgTypes[55]
mi := &file_proto_room_v1_room_proto_msgTypes[56]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@ -3843,7 +3906,7 @@ func (x *GetRoomSnapshotResponse) String() string {
func (*GetRoomSnapshotResponse) ProtoMessage() {}
func (x *GetRoomSnapshotResponse) ProtoReflect() protoreflect.Message {
mi := &file_proto_room_v1_room_proto_msgTypes[55]
mi := &file_proto_room_v1_room_proto_msgTypes[56]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@ -3856,7 +3919,7 @@ func (x *GetRoomSnapshotResponse) ProtoReflect() protoreflect.Message {
// Deprecated: Use GetRoomSnapshotResponse.ProtoReflect.Descriptor instead.
func (*GetRoomSnapshotResponse) Descriptor() ([]byte, []int) {
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{55}
return file_proto_room_v1_room_proto_rawDescGZIP(), []int{56}
}
func (x *GetRoomSnapshotResponse) GetRoom() *RoomSnapshot {
@ -4351,7 +4414,7 @@ var file_proto_room_v1_room_proto_rawDesc = []byte{
0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14, 0x0a,
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c, 0x69,
0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01,
0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0xee, 0x01, 0x0a, 0x14, 0x4c, 0x69,
0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0xb7, 0x02, 0x0a, 0x14, 0x4c, 0x69,
0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
@ -4366,245 +4429,256 @@ var file_proto_room_v1_room_proto_rawDesc = []byte{
0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x12, 0x14,
0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6c,
0x69, 0x6d, 0x69, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x07, 0x20,
0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x22, 0xbd, 0x03, 0x0a, 0x0c, 0x52,
0x6f, 0x6f, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x72,
0x6f, 0x6f, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f,
0x6f, 0x6d, 0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x73,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6f, 0x77, 0x6e,
0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x6f, 0x73, 0x74,
0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a,
0x68, 0x6f, 0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69,
0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65,
0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a,
0x04, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64,
0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28,
0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61,
0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x68, 0x65, 0x61, 0x74, 0x12, 0x21, 0x0a,
0x0c, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20,
0x01, 0x28, 0x05, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74,
0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a,
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x73, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x2e, 0x0a, 0x13, 0x6f, 0x63, 0x63, 0x75, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x61, 0x74,
0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6f, 0x63,
0x63, 0x75, 0x70, 0x69, 0x65, 0x64, 0x53, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x2a, 0x0a, 0x11, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x76, 0x69, 0x73, 0x69,
0x62, 0x6c, 0x65, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61,
0x70, 0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61,
0x70, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x72, 0x6f, 0x6f, 0x6d, 0x5f, 0x73,
0x68, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72,
0x6f, 0x6f, 0x6d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x64, 0x22, 0x67, 0x0a, 0x11, 0x4c, 0x69,
0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x31, 0x0a, 0x05, 0x72, 0x6f, 0x6f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52,
0x6f, 0x6f, 0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x72, 0x6f, 0x6f,
0x6d, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f,
0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72,
0x73, 0x6f, 0x72, 0x22, 0x66, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d,
0x01, 0x28, 0x09, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x12, 0x47, 0x0a, 0x0d, 0x72, 0x65,
0x6c, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65,
0x64, 0x55, 0x73, 0x65, 0x72, 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x55, 0x73,
0x65, 0x72, 0x73, 0x22, 0x63, 0x0a, 0x13, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x52,
0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x55, 0x73, 0x65, 0x72, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65,
0x72, 0x49, 0x64, 0x12, 0x33, 0x0a, 0x16, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x03, 0x52, 0x13, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64,
0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x4d, 0x73, 0x22, 0xbd, 0x03, 0x0a, 0x0c, 0x52, 0x6f, 0x6f,
0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6f,
0x6d, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d,
0x49, 0x64, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x75,
0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x68, 0x6f,
0x73, 0x74, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c,
0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x1b,
0x0a, 0x09, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x55, 0x72, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x6d,
0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6d, 0x6f, 0x64, 0x65, 0x12,
0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52,
0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x65, 0x61, 0x74, 0x18,
0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x68, 0x65, 0x61, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x6f,
0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28,
0x05, 0x52, 0x0b, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1d,
0x0a, 0x0a, 0x73, 0x65, 0x61, 0x74, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0a, 0x20, 0x01,
0x28, 0x05, 0x52, 0x09, 0x73, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2e, 0x0a,
0x13, 0x6f, 0x63, 0x63, 0x75, 0x70, 0x69, 0x65, 0x64, 0x5f, 0x73, 0x65, 0x61, 0x74, 0x5f, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6f, 0x63, 0x63, 0x75,
0x70, 0x69, 0x65, 0x64, 0x53, 0x65, 0x61, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a,
0x11, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x5f,
0x69, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x76, 0x69, 0x73, 0x69, 0x62, 0x6c,
0x65, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x61, 0x70, 0x70,
0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x70, 0x70,
0x43, 0x6f, 0x64, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x72, 0x6f, 0x6f, 0x6d, 0x5f, 0x73, 0x68, 0x6f,
0x72, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x72, 0x6f, 0x6f,
0x6d, 0x53, 0x68, 0x6f, 0x72, 0x74, 0x49, 0x64, 0x22, 0x67, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a,
0x05, 0x72, 0x6f, 0x6f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f,
0x6d, 0x4c, 0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x72, 0x6f, 0x6f, 0x6d, 0x73,
0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x63, 0x75, 0x72, 0x73, 0x6f, 0x72, 0x18,
0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x65, 0x78, 0x74, 0x43, 0x75, 0x72, 0x73, 0x6f,
0x72, 0x22, 0x66, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52,
0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x5f, 0x75,
0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x6f, 0x77,
0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x11, 0x47, 0x65,
0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x2f, 0x0a, 0x04, 0x72, 0x6f,
0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x4c, 0x69, 0x73,
0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20,
0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d,
0x73, 0x22, 0x60, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52,
0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65,
0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65,
0x72, 0x49, 0x64, 0x22, 0xd6, 0x02, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x28,
0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x72, 0x6f,
0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x43, 0x75, 0x72,
0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6f, 0x6d,
0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x49,
0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x6f, 0x6f, 0x6d, 0x56, 0x65, 0x72,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01,
0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69, 0x63, 0x5f,
0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09,
0x52, 0x0c, 0x6d, 0x69, 0x63, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x23,
0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18,
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x53, 0x74,
0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x65, 0x65, 0x64, 0x5f, 0x6a, 0x6f, 0x69, 0x6e,
0x5f, 0x69, 0x6d, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52,
0x0f, 0x6e, 0x65, 0x65, 0x64, 0x4a, 0x6f, 0x69, 0x6e, 0x49, 0x6d, 0x47, 0x72, 0x6f, 0x75, 0x70,
0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x65, 0x65, 0x64, 0x5f, 0x72, 0x74, 0x63, 0x5f, 0x74, 0x6f, 0x6b,
0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x65, 0x65, 0x64, 0x52, 0x74,
0x63, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x22, 0x87, 0x01, 0x0a,
0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18,
0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74,
0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x6f, 0x77, 0x6e, 0x65, 0x72,
0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b,
0x6f, 0x77, 0x6e, 0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x85, 0x01, 0x0a, 0x11,
0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x19, 0x0a, 0x08, 0x68, 0x61, 0x73, 0x5f, 0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20,
0x01, 0x28, 0x08, 0x52, 0x07, 0x68, 0x61, 0x73, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x2f, 0x0a, 0x04,
0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x4c,
0x69, 0x73, 0x74, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x04, 0x72, 0x6f, 0x6f, 0x6d, 0x12, 0x24, 0x0a,
0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18,
0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d,
0x65, 0x4d, 0x73, 0x22, 0x60, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04,
0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07,
0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75,
0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0xd6, 0x02, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72,
0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x28, 0x0a, 0x10, 0x68, 0x61, 0x73, 0x5f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f,
0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x68, 0x61, 0x73, 0x43,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f,
0x6f, 0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f,
0x6d, 0x49, 0x64, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x6f, 0x6f, 0x6d, 0x5f, 0x76, 0x65, 0x72, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x72, 0x6f, 0x6f, 0x6d, 0x56,
0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x6f, 0x6c, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6d, 0x69,
0x63, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0c, 0x6d, 0x69, 0x63, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64,
0x12, 0x23, 0x0a, 0x0d, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x5f, 0x73, 0x74, 0x61, 0x74,
0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x12, 0x6e, 0x65, 0x65, 0x64, 0x5f, 0x6a, 0x6f,
0x69, 0x6e, 0x5f, 0x69, 0x6d, 0x5f, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28,
0x08, 0x52, 0x0f, 0x6e, 0x65, 0x65, 0x64, 0x4a, 0x6f, 0x69, 0x6e, 0x49, 0x6d, 0x47, 0x72, 0x6f,
0x75, 0x70, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x65, 0x65, 0x64, 0x5f, 0x72, 0x74, 0x63, 0x5f, 0x74,
0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x6e, 0x65, 0x65, 0x64,
0x52, 0x74, 0x63, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x03,
0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x22, 0x87,
0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68,
0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74,
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d,
0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6f,
0x6d, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d,
0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65,
0x72, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x76, 0x69, 0x65, 0x77,
0x65, 0x72, 0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52,
0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x04,
0x72, 0x6f, 0x6f, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74,
0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65,
0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x32, 0x9a, 0x0c, 0x0a, 0x12, 0x52,
0x6f, 0x6f, 0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
0x65, 0x12, 0x51, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12,
0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x21, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d,
0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x5a, 0x0a, 0x0d, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65,
0x61, 0x74, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e,
0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72,
0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a,
0x09, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65,
0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76,
0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a,
0x09, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65,
0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73,
0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a,
0x05, 0x4d, 0x69, 0x63, 0x55, 0x70, 0x12, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x1c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x48, 0x0a, 0x07, 0x4d, 0x69, 0x63, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63,
0x44, 0x6f, 0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x44,
0x6f, 0x77, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0d, 0x43,
0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x12, 0x23, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69,
0x72, 0x6d, 0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12,
0x2a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d, 0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73,
0x68, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x72, 0x6d, 0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c,
0x79, 0x52, 0x54, 0x43, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52,
0x54, 0x43, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41,
0x70, 0x70, 0x6c, 0x79, 0x52, 0x54, 0x43, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x63, 0x53, 0x65,
0x61, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61,
0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74,
0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43,
0x68, 0x61, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x6d,
0x69, 0x6e, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e,
0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64,
0x6d, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x10, 0x54,
0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74, 0x12,
0x26, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72,
0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x4b, 0x0a, 0x08, 0x4d, 0x75, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74,
0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74,
0x65, 0x55, 0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a,
0x08, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73,
0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x55, 0x6e,
0x62, 0x61, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x55, 0x73, 0x65,
0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x55, 0x73,
0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x65,
0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xee, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x6f, 0x6d,
0x47, 0x75, 0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6f, 0x0a, 0x14,
0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73,
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50,
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x2b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x69,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a,
0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72, 0x65, 0x73, 0x65,
0x6e, 0x63, 0x65, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72,
0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65,
0x72, 0x69, 0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xcb, 0x03, 0x0a, 0x10, 0x52, 0x6f, 0x6f,
0x6d, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a,
0x09, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52,
0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a,
0x0d, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x73, 0x12, 0x23,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f,
0x6f, 0x6d, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e,
0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72,
0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53,
0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x25, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53,
0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47,
0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x72, 0x6f, 0x6f, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x6f, 0x6f, 0x6d, 0x76, 0x31, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x72, 0x6f, 0x6f, 0x6d, 0x5f,
0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x6f, 0x6d, 0x49, 0x64,
0x12, 0x24, 0x0a, 0x0e, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x76, 0x69, 0x65, 0x77, 0x65, 0x72,
0x55, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x70, 0x0a, 0x17, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f,
0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x2f, 0x0a, 0x04, 0x72, 0x6f, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x04, 0x72, 0x6f,
0x6f, 0x6d, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d,
0x65, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x32, 0x9a, 0x0c, 0x0a, 0x12, 0x52, 0x6f, 0x6f,
0x6d, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
0x51, 0x0a, 0x0a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x20, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72,
0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x21, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x4a, 0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x1e,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4a,
0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4a,
0x6f, 0x69, 0x6e, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x5a, 0x0a, 0x0d, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74,
0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62,
0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x4c,
0x65, 0x61, 0x76, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52, 0x6f,
0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x65, 0x61, 0x76, 0x65, 0x52,
0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x43,
0x6c, 0x6f, 0x73, 0x65, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52, 0x6f,
0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6c, 0x6f, 0x73, 0x65, 0x52,
0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x42, 0x0a, 0x05, 0x4d,
0x69, 0x63, 0x55, 0x70, 0x12, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x55, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x1c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x4d, 0x69, 0x63, 0x55, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x48, 0x0a, 0x07, 0x4d, 0x69, 0x63, 0x44, 0x6f, 0x77, 0x6e, 0x12, 0x1d, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x44, 0x6f,
0x77, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x69, 0x63, 0x44, 0x6f, 0x77,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0d, 0x43, 0x68, 0x61,
0x6e, 0x67, 0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x67,
0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e,
0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x6f, 0x0a, 0x14, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72, 0x6d,
0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x12, 0x2a, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x72, 0x6d, 0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69,
0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x72,
0x6d, 0x4d, 0x69, 0x63, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5a, 0x0a, 0x0d, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52,
0x54, 0x43, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x6c, 0x79, 0x52, 0x54, 0x43,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70,
0x6c, 0x79, 0x52, 0x54, 0x43, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74,
0x4c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69, 0x63, 0x53, 0x65, 0x61, 0x74, 0x4c,
0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x4d, 0x69,
0x63, 0x53, 0x65, 0x61, 0x74, 0x4c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d,
0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61, 0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x43, 0x68, 0x61,
0x74, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x57, 0x0a, 0x0c, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x6d, 0x69, 0x6e,
0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x41, 0x64, 0x6d, 0x69,
0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x63, 0x0a, 0x10, 0x54, 0x72, 0x61,
0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x26, 0x2e,
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x6f, 0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x52, 0x6f,
0x6f, 0x6d, 0x48, 0x6f, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b,
0x0a, 0x08, 0x4d, 0x75, 0x74, 0x65, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61,
0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x75, 0x74, 0x65, 0x55,
0x73, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x4b,
0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4b, 0x69, 0x63, 0x6b, 0x55, 0x73, 0x65, 0x72,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x55, 0x6e, 0x62, 0x61,
0x6e, 0x55, 0x73, 0x65, 0x72, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x55, 0x73, 0x65, 0x72, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72,
0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x6e, 0x62, 0x61, 0x6e, 0x55, 0x73, 0x65, 0x72,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4b, 0x0a, 0x08, 0x53, 0x65, 0x6e, 0x64,
0x47, 0x69, 0x66, 0x74, 0x12, 0x1e, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f,
0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x47, 0x69, 0x66, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xee, 0x01, 0x0a, 0x10, 0x52, 0x6f, 0x6f, 0x6d, 0x47, 0x75,
0x61, 0x72, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x6f, 0x0a, 0x14, 0x43, 0x68,
0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69,
0x6f, 0x6e, 0x12, 0x2a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e,
0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50, 0x65, 0x72,
0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b,
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x43,
0x68, 0x65, 0x63, 0x6b, 0x53, 0x70, 0x65, 0x61, 0x6b, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x56,
0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63,
0x65, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72, 0x65, 0x73,
0x65, 0x6e, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x56, 0x65, 0x72, 0x69,
0x66, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x63, 0x65, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xcb, 0x03, 0x0a, 0x10, 0x52, 0x6f, 0x6f, 0x6d, 0x51,
0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f,
0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70,
0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f,
0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0d, 0x4c,
0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x73, 0x12, 0x23, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73,
0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x46, 0x65, 0x65, 0x64, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d,
0x12, 0x1f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x20, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76,
0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x79, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x12, 0x5d, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e,
0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x12, 0x24, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74,
0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x79,
0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x43,
0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x60, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x12, 0x25, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f,
0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61,
0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x26, 0x2e, 0x68,
0x79, 0x61, 0x70, 0x70, 0x2e, 0x72, 0x6f, 0x6f, 0x6d, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
0x52, 0x6f, 0x6f, 0x6d, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70,
0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f,
0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x6f,
0x6f, 0x6d, 0x2f, 0x76, 0x31, 0x3b, 0x72, 0x6f, 0x6f, 0x6d, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
@ -4619,7 +4693,7 @@ func file_proto_room_v1_room_proto_rawDescGZIP() []byte {
return file_proto_room_v1_room_proto_rawDescData
}
var file_proto_room_v1_room_proto_msgTypes = make([]protoimpl.MessageInfo, 57)
var file_proto_room_v1_room_proto_msgTypes = make([]protoimpl.MessageInfo, 58)
var file_proto_room_v1_room_proto_goTypes = []any{
(*RequestMeta)(nil), // 0: hyapp.room.v1.RequestMeta
(*CommandResult)(nil), // 1: hyapp.room.v1.CommandResult
@ -4669,21 +4743,22 @@ var file_proto_room_v1_room_proto_goTypes = []any{
(*VerifyRoomPresenceResponse)(nil), // 45: hyapp.room.v1.VerifyRoomPresenceResponse
(*ListRoomsRequest)(nil), // 46: hyapp.room.v1.ListRoomsRequest
(*ListRoomFeedsRequest)(nil), // 47: hyapp.room.v1.ListRoomFeedsRequest
(*RoomListItem)(nil), // 48: hyapp.room.v1.RoomListItem
(*ListRoomsResponse)(nil), // 49: hyapp.room.v1.ListRoomsResponse
(*GetMyRoomRequest)(nil), // 50: hyapp.room.v1.GetMyRoomRequest
(*GetMyRoomResponse)(nil), // 51: hyapp.room.v1.GetMyRoomResponse
(*GetCurrentRoomRequest)(nil), // 52: hyapp.room.v1.GetCurrentRoomRequest
(*GetCurrentRoomResponse)(nil), // 53: hyapp.room.v1.GetCurrentRoomResponse
(*GetRoomSnapshotRequest)(nil), // 54: hyapp.room.v1.GetRoomSnapshotRequest
(*GetRoomSnapshotResponse)(nil), // 55: hyapp.room.v1.GetRoomSnapshotResponse
nil, // 56: hyapp.room.v1.RoomSnapshot.RoomExtEntry
(*RoomFeedRelatedUser)(nil), // 48: hyapp.room.v1.RoomFeedRelatedUser
(*RoomListItem)(nil), // 49: hyapp.room.v1.RoomListItem
(*ListRoomsResponse)(nil), // 50: hyapp.room.v1.ListRoomsResponse
(*GetMyRoomRequest)(nil), // 51: hyapp.room.v1.GetMyRoomRequest
(*GetMyRoomResponse)(nil), // 52: hyapp.room.v1.GetMyRoomResponse
(*GetCurrentRoomRequest)(nil), // 53: hyapp.room.v1.GetCurrentRoomRequest
(*GetCurrentRoomResponse)(nil), // 54: hyapp.room.v1.GetCurrentRoomResponse
(*GetRoomSnapshotRequest)(nil), // 55: hyapp.room.v1.GetRoomSnapshotRequest
(*GetRoomSnapshotResponse)(nil), // 56: hyapp.room.v1.GetRoomSnapshotResponse
nil, // 57: hyapp.room.v1.RoomSnapshot.RoomExtEntry
}
var file_proto_room_v1_room_proto_depIdxs = []int32{
3, // 0: hyapp.room.v1.RoomSnapshot.mic_seats:type_name -> hyapp.room.v1.SeatState
2, // 1: hyapp.room.v1.RoomSnapshot.online_users:type_name -> hyapp.room.v1.RoomUser
4, // 2: hyapp.room.v1.RoomSnapshot.gift_rank:type_name -> hyapp.room.v1.RankItem
56, // 3: hyapp.room.v1.RoomSnapshot.room_ext:type_name -> hyapp.room.v1.RoomSnapshot.RoomExtEntry
57, // 3: hyapp.room.v1.RoomSnapshot.room_ext:type_name -> hyapp.room.v1.RoomSnapshot.RoomExtEntry
0, // 4: hyapp.room.v1.CreateRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta
1, // 5: hyapp.room.v1.CreateRoomResponse.result:type_name -> hyapp.room.v1.CommandResult
5, // 6: hyapp.room.v1.CreateRoomResponse.room:type_name -> hyapp.room.v1.RoomSnapshot
@ -4743,67 +4818,68 @@ var file_proto_room_v1_room_proto_depIdxs = []int32{
5, // 60: hyapp.room.v1.SendGiftResponse.room:type_name -> hyapp.room.v1.RoomSnapshot
0, // 61: hyapp.room.v1.ListRoomsRequest.meta:type_name -> hyapp.room.v1.RequestMeta
0, // 62: hyapp.room.v1.ListRoomFeedsRequest.meta:type_name -> hyapp.room.v1.RequestMeta
48, // 63: hyapp.room.v1.ListRoomsResponse.rooms:type_name -> hyapp.room.v1.RoomListItem
0, // 64: hyapp.room.v1.GetMyRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta
48, // 65: hyapp.room.v1.GetMyRoomResponse.room:type_name -> hyapp.room.v1.RoomListItem
0, // 66: hyapp.room.v1.GetCurrentRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta
0, // 67: hyapp.room.v1.GetRoomSnapshotRequest.meta:type_name -> hyapp.room.v1.RequestMeta
5, // 68: hyapp.room.v1.GetRoomSnapshotResponse.room:type_name -> hyapp.room.v1.RoomSnapshot
6, // 69: hyapp.room.v1.RoomCommandService.CreateRoom:input_type -> hyapp.room.v1.CreateRoomRequest
8, // 70: hyapp.room.v1.RoomCommandService.JoinRoom:input_type -> hyapp.room.v1.JoinRoomRequest
10, // 71: hyapp.room.v1.RoomCommandService.RoomHeartbeat:input_type -> hyapp.room.v1.RoomHeartbeatRequest
12, // 72: hyapp.room.v1.RoomCommandService.LeaveRoom:input_type -> hyapp.room.v1.LeaveRoomRequest
14, // 73: hyapp.room.v1.RoomCommandService.CloseRoom:input_type -> hyapp.room.v1.CloseRoomRequest
16, // 74: hyapp.room.v1.RoomCommandService.MicUp:input_type -> hyapp.room.v1.MicUpRequest
18, // 75: hyapp.room.v1.RoomCommandService.MicDown:input_type -> hyapp.room.v1.MicDownRequest
20, // 76: hyapp.room.v1.RoomCommandService.ChangeMicSeat:input_type -> hyapp.room.v1.ChangeMicSeatRequest
22, // 77: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:input_type -> hyapp.room.v1.ConfirmMicPublishingRequest
24, // 78: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:input_type -> hyapp.room.v1.ApplyRTCEventRequest
26, // 79: hyapp.room.v1.RoomCommandService.SetMicSeatLock:input_type -> hyapp.room.v1.SetMicSeatLockRequest
28, // 80: hyapp.room.v1.RoomCommandService.SetChatEnabled:input_type -> hyapp.room.v1.SetChatEnabledRequest
30, // 81: hyapp.room.v1.RoomCommandService.SetRoomAdmin:input_type -> hyapp.room.v1.SetRoomAdminRequest
32, // 82: hyapp.room.v1.RoomCommandService.TransferRoomHost:input_type -> hyapp.room.v1.TransferRoomHostRequest
34, // 83: hyapp.room.v1.RoomCommandService.MuteUser:input_type -> hyapp.room.v1.MuteUserRequest
36, // 84: hyapp.room.v1.RoomCommandService.KickUser:input_type -> hyapp.room.v1.KickUserRequest
38, // 85: hyapp.room.v1.RoomCommandService.UnbanUser:input_type -> hyapp.room.v1.UnbanUserRequest
40, // 86: hyapp.room.v1.RoomCommandService.SendGift:input_type -> hyapp.room.v1.SendGiftRequest
42, // 87: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:input_type -> hyapp.room.v1.CheckSpeakPermissionRequest
44, // 88: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:input_type -> hyapp.room.v1.VerifyRoomPresenceRequest
46, // 89: hyapp.room.v1.RoomQueryService.ListRooms:input_type -> hyapp.room.v1.ListRoomsRequest
47, // 90: hyapp.room.v1.RoomQueryService.ListRoomFeeds:input_type -> hyapp.room.v1.ListRoomFeedsRequest
50, // 91: hyapp.room.v1.RoomQueryService.GetMyRoom:input_type -> hyapp.room.v1.GetMyRoomRequest
52, // 92: hyapp.room.v1.RoomQueryService.GetCurrentRoom:input_type -> hyapp.room.v1.GetCurrentRoomRequest
54, // 93: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:input_type -> hyapp.room.v1.GetRoomSnapshotRequest
7, // 94: hyapp.room.v1.RoomCommandService.CreateRoom:output_type -> hyapp.room.v1.CreateRoomResponse
9, // 95: hyapp.room.v1.RoomCommandService.JoinRoom:output_type -> hyapp.room.v1.JoinRoomResponse
11, // 96: hyapp.room.v1.RoomCommandService.RoomHeartbeat:output_type -> hyapp.room.v1.RoomHeartbeatResponse
13, // 97: hyapp.room.v1.RoomCommandService.LeaveRoom:output_type -> hyapp.room.v1.LeaveRoomResponse
15, // 98: hyapp.room.v1.RoomCommandService.CloseRoom:output_type -> hyapp.room.v1.CloseRoomResponse
17, // 99: hyapp.room.v1.RoomCommandService.MicUp:output_type -> hyapp.room.v1.MicUpResponse
19, // 100: hyapp.room.v1.RoomCommandService.MicDown:output_type -> hyapp.room.v1.MicDownResponse
21, // 101: hyapp.room.v1.RoomCommandService.ChangeMicSeat:output_type -> hyapp.room.v1.ChangeMicSeatResponse
23, // 102: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:output_type -> hyapp.room.v1.ConfirmMicPublishingResponse
25, // 103: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:output_type -> hyapp.room.v1.ApplyRTCEventResponse
27, // 104: hyapp.room.v1.RoomCommandService.SetMicSeatLock:output_type -> hyapp.room.v1.SetMicSeatLockResponse
29, // 105: hyapp.room.v1.RoomCommandService.SetChatEnabled:output_type -> hyapp.room.v1.SetChatEnabledResponse
31, // 106: hyapp.room.v1.RoomCommandService.SetRoomAdmin:output_type -> hyapp.room.v1.SetRoomAdminResponse
33, // 107: hyapp.room.v1.RoomCommandService.TransferRoomHost:output_type -> hyapp.room.v1.TransferRoomHostResponse
35, // 108: hyapp.room.v1.RoomCommandService.MuteUser:output_type -> hyapp.room.v1.MuteUserResponse
37, // 109: hyapp.room.v1.RoomCommandService.KickUser:output_type -> hyapp.room.v1.KickUserResponse
39, // 110: hyapp.room.v1.RoomCommandService.UnbanUser:output_type -> hyapp.room.v1.UnbanUserResponse
41, // 111: hyapp.room.v1.RoomCommandService.SendGift:output_type -> hyapp.room.v1.SendGiftResponse
43, // 112: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:output_type -> hyapp.room.v1.CheckSpeakPermissionResponse
45, // 113: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:output_type -> hyapp.room.v1.VerifyRoomPresenceResponse
49, // 114: hyapp.room.v1.RoomQueryService.ListRooms:output_type -> hyapp.room.v1.ListRoomsResponse
49, // 115: hyapp.room.v1.RoomQueryService.ListRoomFeeds:output_type -> hyapp.room.v1.ListRoomsResponse
51, // 116: hyapp.room.v1.RoomQueryService.GetMyRoom:output_type -> hyapp.room.v1.GetMyRoomResponse
53, // 117: hyapp.room.v1.RoomQueryService.GetCurrentRoom:output_type -> hyapp.room.v1.GetCurrentRoomResponse
55, // 118: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:output_type -> hyapp.room.v1.GetRoomSnapshotResponse
94, // [94:119] is the sub-list for method output_type
69, // [69:94] is the sub-list for method input_type
69, // [69:69] is the sub-list for extension type_name
69, // [69:69] is the sub-list for extension extendee
0, // [0:69] is the sub-list for field type_name
48, // 63: hyapp.room.v1.ListRoomFeedsRequest.related_users:type_name -> hyapp.room.v1.RoomFeedRelatedUser
49, // 64: hyapp.room.v1.ListRoomsResponse.rooms:type_name -> hyapp.room.v1.RoomListItem
0, // 65: hyapp.room.v1.GetMyRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta
49, // 66: hyapp.room.v1.GetMyRoomResponse.room:type_name -> hyapp.room.v1.RoomListItem
0, // 67: hyapp.room.v1.GetCurrentRoomRequest.meta:type_name -> hyapp.room.v1.RequestMeta
0, // 68: hyapp.room.v1.GetRoomSnapshotRequest.meta:type_name -> hyapp.room.v1.RequestMeta
5, // 69: hyapp.room.v1.GetRoomSnapshotResponse.room:type_name -> hyapp.room.v1.RoomSnapshot
6, // 70: hyapp.room.v1.RoomCommandService.CreateRoom:input_type -> hyapp.room.v1.CreateRoomRequest
8, // 71: hyapp.room.v1.RoomCommandService.JoinRoom:input_type -> hyapp.room.v1.JoinRoomRequest
10, // 72: hyapp.room.v1.RoomCommandService.RoomHeartbeat:input_type -> hyapp.room.v1.RoomHeartbeatRequest
12, // 73: hyapp.room.v1.RoomCommandService.LeaveRoom:input_type -> hyapp.room.v1.LeaveRoomRequest
14, // 74: hyapp.room.v1.RoomCommandService.CloseRoom:input_type -> hyapp.room.v1.CloseRoomRequest
16, // 75: hyapp.room.v1.RoomCommandService.MicUp:input_type -> hyapp.room.v1.MicUpRequest
18, // 76: hyapp.room.v1.RoomCommandService.MicDown:input_type -> hyapp.room.v1.MicDownRequest
20, // 77: hyapp.room.v1.RoomCommandService.ChangeMicSeat:input_type -> hyapp.room.v1.ChangeMicSeatRequest
22, // 78: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:input_type -> hyapp.room.v1.ConfirmMicPublishingRequest
24, // 79: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:input_type -> hyapp.room.v1.ApplyRTCEventRequest
26, // 80: hyapp.room.v1.RoomCommandService.SetMicSeatLock:input_type -> hyapp.room.v1.SetMicSeatLockRequest
28, // 81: hyapp.room.v1.RoomCommandService.SetChatEnabled:input_type -> hyapp.room.v1.SetChatEnabledRequest
30, // 82: hyapp.room.v1.RoomCommandService.SetRoomAdmin:input_type -> hyapp.room.v1.SetRoomAdminRequest
32, // 83: hyapp.room.v1.RoomCommandService.TransferRoomHost:input_type -> hyapp.room.v1.TransferRoomHostRequest
34, // 84: hyapp.room.v1.RoomCommandService.MuteUser:input_type -> hyapp.room.v1.MuteUserRequest
36, // 85: hyapp.room.v1.RoomCommandService.KickUser:input_type -> hyapp.room.v1.KickUserRequest
38, // 86: hyapp.room.v1.RoomCommandService.UnbanUser:input_type -> hyapp.room.v1.UnbanUserRequest
40, // 87: hyapp.room.v1.RoomCommandService.SendGift:input_type -> hyapp.room.v1.SendGiftRequest
42, // 88: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:input_type -> hyapp.room.v1.CheckSpeakPermissionRequest
44, // 89: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:input_type -> hyapp.room.v1.VerifyRoomPresenceRequest
46, // 90: hyapp.room.v1.RoomQueryService.ListRooms:input_type -> hyapp.room.v1.ListRoomsRequest
47, // 91: hyapp.room.v1.RoomQueryService.ListRoomFeeds:input_type -> hyapp.room.v1.ListRoomFeedsRequest
51, // 92: hyapp.room.v1.RoomQueryService.GetMyRoom:input_type -> hyapp.room.v1.GetMyRoomRequest
53, // 93: hyapp.room.v1.RoomQueryService.GetCurrentRoom:input_type -> hyapp.room.v1.GetCurrentRoomRequest
55, // 94: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:input_type -> hyapp.room.v1.GetRoomSnapshotRequest
7, // 95: hyapp.room.v1.RoomCommandService.CreateRoom:output_type -> hyapp.room.v1.CreateRoomResponse
9, // 96: hyapp.room.v1.RoomCommandService.JoinRoom:output_type -> hyapp.room.v1.JoinRoomResponse
11, // 97: hyapp.room.v1.RoomCommandService.RoomHeartbeat:output_type -> hyapp.room.v1.RoomHeartbeatResponse
13, // 98: hyapp.room.v1.RoomCommandService.LeaveRoom:output_type -> hyapp.room.v1.LeaveRoomResponse
15, // 99: hyapp.room.v1.RoomCommandService.CloseRoom:output_type -> hyapp.room.v1.CloseRoomResponse
17, // 100: hyapp.room.v1.RoomCommandService.MicUp:output_type -> hyapp.room.v1.MicUpResponse
19, // 101: hyapp.room.v1.RoomCommandService.MicDown:output_type -> hyapp.room.v1.MicDownResponse
21, // 102: hyapp.room.v1.RoomCommandService.ChangeMicSeat:output_type -> hyapp.room.v1.ChangeMicSeatResponse
23, // 103: hyapp.room.v1.RoomCommandService.ConfirmMicPublishing:output_type -> hyapp.room.v1.ConfirmMicPublishingResponse
25, // 104: hyapp.room.v1.RoomCommandService.ApplyRTCEvent:output_type -> hyapp.room.v1.ApplyRTCEventResponse
27, // 105: hyapp.room.v1.RoomCommandService.SetMicSeatLock:output_type -> hyapp.room.v1.SetMicSeatLockResponse
29, // 106: hyapp.room.v1.RoomCommandService.SetChatEnabled:output_type -> hyapp.room.v1.SetChatEnabledResponse
31, // 107: hyapp.room.v1.RoomCommandService.SetRoomAdmin:output_type -> hyapp.room.v1.SetRoomAdminResponse
33, // 108: hyapp.room.v1.RoomCommandService.TransferRoomHost:output_type -> hyapp.room.v1.TransferRoomHostResponse
35, // 109: hyapp.room.v1.RoomCommandService.MuteUser:output_type -> hyapp.room.v1.MuteUserResponse
37, // 110: hyapp.room.v1.RoomCommandService.KickUser:output_type -> hyapp.room.v1.KickUserResponse
39, // 111: hyapp.room.v1.RoomCommandService.UnbanUser:output_type -> hyapp.room.v1.UnbanUserResponse
41, // 112: hyapp.room.v1.RoomCommandService.SendGift:output_type -> hyapp.room.v1.SendGiftResponse
43, // 113: hyapp.room.v1.RoomGuardService.CheckSpeakPermission:output_type -> hyapp.room.v1.CheckSpeakPermissionResponse
45, // 114: hyapp.room.v1.RoomGuardService.VerifyRoomPresence:output_type -> hyapp.room.v1.VerifyRoomPresenceResponse
50, // 115: hyapp.room.v1.RoomQueryService.ListRooms:output_type -> hyapp.room.v1.ListRoomsResponse
50, // 116: hyapp.room.v1.RoomQueryService.ListRoomFeeds:output_type -> hyapp.room.v1.ListRoomsResponse
52, // 117: hyapp.room.v1.RoomQueryService.GetMyRoom:output_type -> hyapp.room.v1.GetMyRoomResponse
54, // 118: hyapp.room.v1.RoomQueryService.GetCurrentRoom:output_type -> hyapp.room.v1.GetCurrentRoomResponse
56, // 119: hyapp.room.v1.RoomQueryService.GetRoomSnapshot:output_type -> hyapp.room.v1.GetRoomSnapshotResponse
95, // [95:120] is the sub-list for method output_type
70, // [70:95] is the sub-list for method input_type
70, // [70:70] is the sub-list for extension type_name
70, // [70:70] is the sub-list for extension extendee
0, // [0:70] is the sub-list for field type_name
}
func init() { file_proto_room_v1_room_proto_init() }
@ -4817,7 +4893,7 @@ func file_proto_room_v1_room_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_room_v1_room_proto_rawDesc,
NumEnums: 0,
NumMessages: 57,
NumMessages: 58,
NumExtensions: 0,
NumServices: 3,
},

View File

@ -393,6 +393,14 @@ message ListRoomFeedsRequest {
string cursor = 5;
int32 limit = 6;
string query = 7;
// related_users friend/following gateway user-service
repeated RoomFeedRelatedUser related_users = 8;
}
// RoomFeedRelatedUser Mine room-service /
message RoomFeedRelatedUser {
int64 user_id = 1;
int64 relation_updated_at_ms = 2;
}
// RoomListItem Room Cell

File diff suppressed because it is too large Load Diff

View File

@ -254,6 +254,9 @@ message ListFollowingRequest {
int64 user_id = 2;
int32 page = 3;
int32 page_size = 4;
// cursor_updated_at_ms/cursor_user_id HTTP 使 page/page_size
int64 cursor_updated_at_ms = 5;
int64 cursor_user_id = 6;
}
message ListFollowingResponse {
@ -303,6 +306,9 @@ message ListFriendsRequest {
int64 user_id = 2;
int32 page = 3;
int32 page_size = 4;
// cursor_updated_at_ms/cursor_user_id HTTP 使 page/page_size
int64 cursor_updated_at_ms = 5;
int64 cursor_user_id = 6;
}
message ListFriendsResponse {

View File

@ -948,7 +948,7 @@ paths:
- rooms
summary: Mine 房间关系流
operationId: listRoomFeeds
description: 查询 Mine 页下方 Visited/Friend/Following 房间流;该接口读取当前用户的房间 feed 索引,再返回房间卡片和 `im_group_id`,不作为公共房间发现列表。
description: 查询 Mine 页下方 Visited/Friend/Following 房间流;`visited` 读取 JoinRoom 写入的房间访问索引,`friend/following` 由 gateway 先读取 user-service 关系事实,再让 room-service 返回这些关系用户的 active 房间卡片和 `im_group_id`,不作为公共房间发现列表。
security:
- BearerAuth: []
parameters:
@ -972,7 +972,7 @@ paths:
format: int32
responses:
"200":
description: 查询成功;`visited` 由 JoinRoom 成功写入,`friend/following` 依赖关系流读模型填充
description: 查询成功;`visited` 由 JoinRoom 成功写入,`friend/following` 同步使用 user-service 好友/关注关系,不依赖 Redis 缓存命中
schema:
$ref: "#/definitions/RoomListEnvelope"
"400":

View File

@ -578,7 +578,7 @@ activity-service 额外需要:
推荐顺序:
1. 写 `docs/grpc错误模型.md` 或把本文件错误模型抽成独立文档。
1. 写 `docs/gRPC错误模型.md` 或把本文件错误模型抽成独立文档。
2. 扩展 `pkg/xerr`,支持 reason 和 transport 映射。
3. 新增 `api/proto/user/v1/auth.proto``user.proto`
4. 补 `user-service` 的 app/service/domain/storage/transport 骨架。

View File

@ -105,6 +105,13 @@ sequenceDiagram
计数更新和关系边写入必须在同一个 MySQL 事务中完成。`user_profile_stats` 是首屏计数来源,不能在 `/users/me/overview` 里实时扫关系表。
## Room Feed Integration
- `GET /api/v1/rooms/feeds?tab=visited` 是房间访问历史,只由 JoinRoom 成功后写入 room-service 的房间 feed 索引;不要使用 `/users/{user_id}/visit` 用户主页访问记录填充。
- `GET /api/v1/rooms/feeds?tab=following` 由 gateway 调用 `ListFollowing` 读取关注用户,再把关系用户和 `followed_at_ms` 传给 room-service 查询 active 房间卡片。
- `GET /api/v1/rooms/feeds?tab=friend` 由 gateway 调用 `ListFriends` 读取好友用户,再把关系用户和 `friended_at_ms` 传给 room-service 查询 active 房间卡片。
- 好友/关注关系事实仍归 user-serviceroom-service 只拥有房间状态和房间列表卡片,不保存好友/关注关系作为事实。
## App Notes
- 好友申请成功后App 发送腾讯 IM 自定义消息给对方;消息里携带 `requester_user_id` 和申请状态展示参数。

View File

@ -26,6 +26,7 @@
sequenceDiagram
participant C as Client
participant G as gateway-service
participant U as user-service
participant R as room-service
participant IM as Tencent IM SDK
participant RTC as Tencent RTC SDK
@ -40,7 +41,11 @@ sequenceDiagram
G-->>C: has_room + room card + im_group_id
C->>G: GET /api/v1/rooms/feeds?tab=visited|friend|following
G->>R: ListRoomFeeds(viewer region from user-service)
opt tab=friend|following
G->>U: ListFriends/ListFollowing(cursor)
U-->>G: related user ids + relation times
end
G->>R: ListRoomFeeds(viewer region + related users)
R-->>G: room cards
G-->>C: room cards + im_group_id
@ -70,7 +75,7 @@ sequenceDiagram
| 1 | `GET /api/v1/im/usersig` | yes | 用户级 IM 登录票据,可在 App 启动后预取并按 TTL 复用 |
| 2 | Tencent IM SDK `login` | yes | 客户端先建立 IM 身份,房间群仍等 JoinRoom 成功后再进 |
| 3 | `GET /api/v1/rooms/me` | yes for Mine top card | 直接读取 owner 房间权威元数据,不依赖发现页投影 |
| 4 | `GET /api/v1/rooms/feeds?tab=visited\|friend\|following` | yes for Mine lists | Mine 列表 tab`visited` 由 JoinRoom 成功写入,`friend/following` 读取关系流读模型 |
| 4 | `GET /api/v1/rooms/feeds?tab=visited\|friend\|following` | yes for Mine lists | Mine 列表 tab`visited` 由 JoinRoom 成功写入,`friend/following` 由 gateway 先查 user-service 关系,再让 room-service 返回这些关系用户的 active 房间卡片 |
| 5 | `POST /api/v1/rooms/join` | yes | 建立业务 presence并返回首屏房间数据和内嵌 RTC token |
| 6 | Tencent IM SDK `joinGroup(im_group_id)` | yes | 必须在 JoinRoom 成功后执行IM 回调守卫会校验 room-service presence |
| 7 | Tencent RTC SDK `enterRoom` | yes for voice room audio | 使用 JoinRoom 响应里的 `rtc.token` 进入真实音频频道旁听 |
@ -78,6 +83,19 @@ sequenceDiagram
如果 JoinRoom 响应里的 `rtc.available=false`,客户端可以先展示房间页并重试 `POST /api/v1/rtc/token`RTC 失败不能回滚已经成功的业务 presence。
### Mine Feed Cache Boundary
当前 `friend/following` 不强制加 Redis。原因是关注、取关、同意好友和删除好友都要求列表立即反映关系事实首版直接读 user-service MySQL 关系表,再查 room-service active 房间卡片,正确性更直接。
后续可以加 Redis 的位置:
- `hot/new` 公共列表room-service 用 Redis ZSET 缓存排序 room_idmiss 后回源 `room_list_entries`
- 房间卡片短 TTL 缓存key 可按 `app_code + region_id + room_id`,但 MySQL `room_list_entries` 仍是事实来源。
- `friend/following` 关系 ID 短 TTL 缓存:只能放在 gateway 或 user-service 读优化层,`follow/unfollow/accept/delete` 成功后必须主动失效。
- `visited` room_id 列表缓存:可以由 JoinRoom 成功后刷新Redis 写失败不能影响 JoinRoom。
不建议首版缓存 `friend/following` 完整房间卡片列表。这个列表同时依赖 user-service 关系事实、room-service 房间 active 状态、区域过滤和搜索条件,失效面较大;先保持同步查询,等 QPS 或延迟数据证明需要缓存再加。
## Core Interfaces
### Join Room

View File

@ -1363,11 +1363,15 @@ func TestListRoomFeedsSupportsMineFeedTabs(t *testing.T) {
for _, tab := range []string{"visited", "friend", "following"} {
t.Run(tab, func(t *testing.T) {
profileClient := &fakeUserProfileClient{regionID: 1001}
socialClient := &fakeUserSocialClient{}
queryClient := &fakeRoomQueryClient{feedsResp: &roomv1.ListRoomsResponse{
Rooms: []*roomv1.RoomListItem{{RoomId: "room-feed", VisibleRegionId: 1001, Heat: 7}},
}}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
handler.SetRoomQueryClient(queryClient)
if tab != "visited" {
handler.SetUserSocialClient(socialClient)
}
router := handler.Routes(auth.NewVerifier("secret"))
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms/feeds?tab="+tab+"&limit=3", nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
@ -1382,10 +1386,60 @@ func TestListRoomFeedsSupportsMineFeedTabs(t *testing.T) {
if queryClient.lastFeeds == nil || queryClient.lastFeeds.GetTab() != tab || queryClient.lastFeeds.GetViewerUserId() != 42 || queryClient.lastFeeds.GetVisibleRegionId() != 1001 {
t.Fatalf("feed tab was not forwarded with authenticated user and region: %+v", queryClient.lastFeeds)
}
switch tab {
case "visited":
if len(queryClient.lastFeeds.GetRelatedUsers()) != 0 || socialClient.lastFollowing != nil || socialClient.lastFriends != nil {
t.Fatalf("visited feed must not load social relations: feeds=%+v social=%+v/%+v", queryClient.lastFeeds, socialClient.lastFollowing, socialClient.lastFriends)
}
case "following":
if socialClient.lastFollowing == nil || socialClient.lastFollowing.GetUserId() != 42 {
t.Fatalf("following feed must load following relation facts: %+v", socialClient.lastFollowing)
}
if got := queryClient.lastFeeds.GetRelatedUsers(); len(got) != 1 || got[0].GetUserId() != 10002 || got[0].GetRelationUpdatedAtMs() != 8000 {
t.Fatalf("following related users mismatch: %+v", got)
}
case "friend":
if socialClient.lastFriends == nil || socialClient.lastFriends.GetUserId() != 42 {
t.Fatalf("friend feed must load friend relation facts: %+v", socialClient.lastFriends)
}
if got := queryClient.lastFeeds.GetRelatedUsers(); len(got) != 1 || got[0].GetUserId() != 10002 || got[0].GetRelationUpdatedAtMs() != 10000 {
t.Fatalf("friend related users mismatch: %+v", got)
}
}
})
}
}
func TestListRoomFeedsRelationCursorContinuesUserSocialScan(t *testing.T) {
profileClient := &fakeUserProfileClient{regionID: 1001}
socialClient := &fakeUserSocialClient{}
queryClient := &fakeRoomQueryClient{}
handler := NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient)
handler.SetRoomQueryClient(queryClient)
handler.SetUserSocialClient(socialClient)
router := handler.Routes(auth.NewVerifier("secret"))
cursorPayload, _ := json.Marshal(roomFeedRelationCursor{
Tab: "following",
Query: "music",
UpdatedAtMS: 8000,
SubjectUserID: 10002,
RoomID: "room-feed",
})
cursor := base64.RawURLEncoding.EncodeToString(cursorPayload)
request := httptest.NewRequest(http.MethodGet, "/api/v1/rooms/feeds?tab=following&q=music&cursor="+cursor, nil)
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
recorder := httptest.NewRecorder()
router.ServeHTTP(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
}
if socialClient.lastFollowing == nil || socialClient.lastFollowing.GetCursorUpdatedAtMs() != 8000 || socialClient.lastFollowing.GetCursorUserId() != 10002 {
t.Fatalf("following cursor was not propagated to user-service: %+v", socialClient.lastFollowing)
}
}
func TestGetMyRoomUsesAuthenticatedOwnerAndReturnsIMGroup(t *testing.T) {
queryClient := &fakeRoomQueryClient{myRoomResp: &roomv1.GetMyRoomResponse{
HasRoom: true,

View File

@ -1,6 +1,8 @@
package http
import (
"encoding/base64"
"encoding/json"
"net/http"
"strconv"
"strings"
@ -9,9 +11,23 @@ import (
userv1 "hyapp.local/api/proto/user/v1"
"hyapp/pkg/appcode"
"hyapp/pkg/roomid"
"hyapp/pkg/xerr"
"hyapp/services/gateway-service/internal/auth"
)
const (
defaultRoomFeedRelationPageSize = 100
maxRoomFeedRelationScanCount = 500
)
type roomFeedRelationCursor struct {
Tab string `json:"tab"`
Query string `json:"query,omitempty"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
SubjectUserID int64 `json:"subject_user_id,omitempty"`
RoomID string `json:"room_id"`
}
// listRooms 按当前登录用户的服务端 region_id 查询房间发现列表。
func (h *Handler) listRooms(writer http.ResponseWriter, request *http.Request) {
if h.userProfileClient == nil || h.roomQueryClient == nil {
@ -87,6 +103,16 @@ func (h *Handler) listRoomFeeds(writer http.ResponseWriter, request *http.Reques
return
}
relatedUsers, ok, err := h.roomFeedRelatedUsers(request, tab, roomListQuery(request), request.URL.Query().Get("cursor"))
if !ok {
writeError(writer, request, http.StatusBadRequest, codeInvalidArgument, "invalid argument")
return
}
if err != nil {
writeRPCError(writer, request, err)
return
}
resp, err := h.roomQueryClient.ListRoomFeeds(request.Context(), &roomv1.ListRoomFeedsRequest{
Meta: meta(request, "", ""),
ViewerUserId: viewerUserID,
@ -95,6 +121,7 @@ func (h *Handler) listRoomFeeds(writer http.ResponseWriter, request *http.Reques
Cursor: request.URL.Query().Get("cursor"),
Limit: limit,
Query: roomListQuery(request),
RelatedUsers: relatedUsers,
})
if err != nil {
writeRPCError(writer, request, err)
@ -103,6 +130,105 @@ func (h *Handler) listRoomFeeds(writer http.ResponseWriter, request *http.Reques
writeOK(writer, request, roomListDataFromProto(resp))
}
func (h *Handler) roomFeedRelatedUsers(request *http.Request, tab string, query string, rawCursor string) ([]*roomv1.RoomFeedRelatedUser, bool, error) {
if tab == "visited" {
return nil, true, nil
}
if h.userSocialClient == nil {
// friend/following 必须以 user-service 关系事实为准,不能退回旧 room feed 投影。
return nil, true, xerr.New(xerr.Unavailable, "user social client is not configured")
}
cursor, ok := decodeRoomFeedRelationCursor(tab, query, rawCursor)
if !ok {
return nil, false, nil
}
viewerUserID := auth.UserIDFromContext(request.Context())
relatedUsers := make([]*roomv1.RoomFeedRelatedUser, 0, defaultRoomFeedRelationPageSize)
for len(relatedUsers) < maxRoomFeedRelationScanCount {
batch, nextCursor, err := h.roomFeedRelatedUserBatch(request, tab, viewerUserID, cursor)
if err != nil {
return nil, true, err
}
relatedUsers = append(relatedUsers, batch...)
if len(batch) < defaultRoomFeedRelationPageSize {
break
}
cursor = nextCursor
}
return relatedUsers, true, nil
}
func (h *Handler) roomFeedRelatedUserBatch(request *http.Request, tab string, viewerUserID int64, cursor roomFeedRelationCursor) ([]*roomv1.RoomFeedRelatedUser, roomFeedRelationCursor, error) {
switch tab {
case "following":
resp, err := h.userSocialClient.ListFollowing(request.Context(), &userv1.ListFollowingRequest{
Meta: authRequestMeta(request, ""),
UserId: viewerUserID,
Page: 1,
PageSize: defaultRoomFeedRelationPageSize,
CursorUpdatedAtMs: cursor.UpdatedAtMS,
CursorUserId: cursor.SubjectUserID,
})
if err != nil {
return nil, cursor, err
}
relatedUsers := make([]*roomv1.RoomFeedRelatedUser, 0, len(resp.GetRecords()))
for _, record := range resp.GetRecords() {
relatedUsers = append(relatedUsers, &roomv1.RoomFeedRelatedUser{
UserId: record.GetFolloweeUserId(),
RelationUpdatedAtMs: record.GetFollowedAtMs(),
})
cursor.UpdatedAtMS = record.GetFollowedAtMs()
cursor.SubjectUserID = record.GetFolloweeUserId()
}
return relatedUsers, cursor, nil
case "friend":
resp, err := h.userSocialClient.ListFriends(request.Context(), &userv1.ListFriendsRequest{
Meta: authRequestMeta(request, ""),
UserId: viewerUserID,
Page: 1,
PageSize: defaultRoomFeedRelationPageSize,
CursorUpdatedAtMs: cursor.UpdatedAtMS,
CursorUserId: cursor.SubjectUserID,
})
if err != nil {
return nil, cursor, err
}
relatedUsers := make([]*roomv1.RoomFeedRelatedUser, 0, len(resp.GetRecords()))
for _, record := range resp.GetRecords() {
relatedUsers = append(relatedUsers, &roomv1.RoomFeedRelatedUser{
UserId: record.GetFriendUserId(),
RelationUpdatedAtMs: record.GetFriendedAtMs(),
})
cursor.UpdatedAtMS = record.GetFriendedAtMs()
cursor.SubjectUserID = record.GetFriendUserId()
}
return relatedUsers, cursor, nil
default:
return nil, cursor, xerr.New(xerr.InvalidArgument, "tab is invalid")
}
}
func decodeRoomFeedRelationCursor(tab string, query string, raw string) (roomFeedRelationCursor, bool) {
raw = strings.TrimSpace(raw)
if raw == "" {
return roomFeedRelationCursor{Tab: tab, Query: query}, true
}
payload, err := base64.RawURLEncoding.DecodeString(raw)
if err != nil {
return roomFeedRelationCursor{}, false
}
var cursor roomFeedRelationCursor
if err := json.Unmarshal(payload, &cursor); err != nil {
return roomFeedRelationCursor{}, false
}
if cursor.Tab != tab || cursor.Query != query || cursor.RoomID == "" || cursor.UpdatedAtMS <= 0 || cursor.SubjectUserID <= 0 {
return roomFeedRelationCursor{}, false
}
return cursor, true
}
func parsePublicRoomListTab(raw string) (string, bool) {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "", "hot":

View File

@ -27,12 +27,13 @@ const (
// roomListCursor 是服务端生成的不透明翻页游标。
// 客户端只能原样传回不能依赖内部字段tab 写入游标用于拒绝跨 tab 复用。
type roomListCursor struct {
Tab string `json:"tab"`
Query string `json:"query,omitempty"`
SortScore int64 `json:"sort_score,omitempty"`
CreatedAtMS int64 `json:"created_at_ms,omitempty"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
RoomID string `json:"room_id"`
Tab string `json:"tab"`
Query string `json:"query,omitempty"`
SortScore int64 `json:"sort_score,omitempty"`
CreatedAtMS int64 `json:"created_at_ms,omitempty"`
UpdatedAtMS int64 `json:"updated_at_ms,omitempty"`
SubjectUserID int64 `json:"subject_user_id,omitempty"`
RoomID string `json:"room_id"`
}
// ListRooms 查询当前用户区域内的公共房间发现列表读模型。
@ -99,17 +100,35 @@ func (s *Service) ListRoomFeeds(ctx context.Context, req *roomv1.ListRoomFeedsRe
if err != nil {
return nil, err
}
if tab != roomListTabVisited && strings.TrimSpace(req.GetCursor()) != "" && cursor.SubjectUserID <= 0 {
return nil, xerr.New(xerr.InvalidArgument, "cursor is invalid")
}
entries, err := s.repository.ListRoomUserFeedEntries(ctx, RoomUserFeedQuery{
AppCode: appcode.FromContext(ctx),
UserID: viewerUserID,
FeedType: tab,
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
Query: query,
Limit: limit + 1,
CursorUpdatedAtMS: cursor.UpdatedAtMS,
CursorRoomID: cursor.RoomID,
})
var entries []RoomListEntry
if tab == roomListTabVisited {
entries, err = s.repository.ListRoomUserFeedEntries(ctx, RoomUserFeedQuery{
AppCode: appcode.FromContext(ctx),
UserID: viewerUserID,
FeedType: tab,
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
Query: query,
Limit: limit + 1,
CursorUpdatedAtMS: cursor.UpdatedAtMS,
CursorRoomID: cursor.RoomID,
})
} else {
// friend/following 的关系事实属于 user-serviceroom-service 只根据 gateway 传入的一页关系用户查询 active 房间卡片。
entries, err = s.repository.ListRoomRelatedFeedEntries(ctx, RoomRelatedFeedQuery{
AppCode: appcode.FromContext(ctx),
VisibleRegionID: normalizeVisibleRegionID(req.GetVisibleRegionId()),
RelatedUsers: roomFeedRelatedUsersFromProto(req.GetRelatedUsers()),
Query: query,
Limit: limit + 1,
CursorUpdatedAtMS: cursor.UpdatedAtMS,
CursorSubjectUserID: cursor.SubjectUserID,
CursorRoomID: cursor.RoomID,
})
}
if err != nil {
return nil, err
}
@ -167,6 +186,20 @@ func roomListResponseFromEntries(tab string, query string, entries []RoomListEnt
}
}
func roomFeedRelatedUsersFromProto(items []*roomv1.RoomFeedRelatedUser) []RoomFeedRelatedUser {
relatedUsers := make([]RoomFeedRelatedUser, 0, len(items))
for _, item := range items {
if item == nil || item.GetUserId() <= 0 || item.GetRelationUpdatedAtMs() <= 0 {
continue
}
relatedUsers = append(relatedUsers, RoomFeedRelatedUser{
UserID: item.GetUserId(),
RelationUpdatedAtMS: item.GetRelationUpdatedAtMs(),
})
}
return relatedUsers
}
// projectRoomListBestEffort 根据最新快照刷新房间列表投影。
func (s *Service) projectRoomListBestEffort(ctx context.Context, snapshot *roomv1.RoomSnapshot) {
if snapshot == nil || snapshot.GetRoomId() == "" {
@ -320,12 +353,13 @@ func decodeRoomListCursor(tab string, query string, encoded string) (roomListCur
// cursor 不暴露契约语义,后续调整排序字段时可以只兼容服务端解析逻辑。
func encodeRoomListCursor(tab string, query string, entry RoomListEntry) string {
cursor := roomListCursor{
Tab: tab,
Query: query,
SortScore: entry.SortScore,
CreatedAtMS: entry.CreatedAtMS,
UpdatedAtMS: entry.UpdatedAtMS,
RoomID: entry.RoomID,
Tab: tab,
Query: query,
SortScore: entry.SortScore,
CreatedAtMS: entry.CreatedAtMS,
UpdatedAtMS: entry.UpdatedAtMS,
SubjectUserID: entry.FeedSubjectUserID,
RoomID: entry.RoomID,
}
payload, err := json.Marshal(cursor)

View File

@ -113,6 +113,8 @@ type RoomListEntry struct {
CreatedAtMS int64
// UpdatedAtMS 是投影最后更新时间。
UpdatedAtMS int64
// FeedSubjectUserID 是关系房间流命中的好友/关注用户;只用于生成稳定 cursor不出现在 HTTP 卡片里。
FeedSubjectUserID int64
}
// RoomPresence 是当前用户可恢复房间的轻量读模型。
@ -201,6 +203,35 @@ type RoomUserFeedQuery struct {
CursorRoomID string
}
// RoomFeedRelatedUser 是 gateway 从 user-service 读取到的关系事实快照。
// room-service 只使用它做 active 房间卡片查询和排序,不保存好友/关注关系。
type RoomFeedRelatedUser struct {
// UserID 是好友或关注对象的用户 ID。
UserID int64
// RelationUpdatedAtMS 是该关系的排序时间followed_at_ms/friended_at_ms 都映射到这里。
RelationUpdatedAtMS int64
}
// RoomRelatedFeedQuery 根据一批关系用户查询他们当前 active 房间卡片。
type RoomRelatedFeedQuery struct {
// AppCode 是 feed 查询所属 App。
AppCode string
// VisibleRegionID 沿用房间发现页区域桶,避免关系流跨区域露出。
VisibleRegionID int64
// RelatedUsers 是 user-service 已授权返回的好友/关注对象。
RelatedUsers []RoomFeedRelatedUser
// Query 非空时按 room_id、room_short_id 或 title 做包含匹配。
Query string
// Limit 是实际查询数量service 层会限制最大值。
Limit int
// CursorUpdatedAtMS 是上一页最后一条关系排序时间。
CursorUpdatedAtMS int64
// CursorSubjectUserID 是关系排序时间相同时的好友/关注用户边界。
CursorSubjectUserID int64
// CursorRoomID 是同时间下的稳定翻页边界。
CursorRoomID string
}
// Repository 聚合 room-service 在首版需要的全部持久化读写。
type Repository interface {
// SaveRoomMeta 保存房间基础元数据,创建房间时必须先建立它。
@ -239,6 +270,8 @@ type Repository interface {
UpsertRoomUserFeedEntry(ctx context.Context, entry RoomUserFeedEntry) error
// ListRoomUserFeedEntries 按用户关系流读取房间卡片,不访问 Room Cell 内存集合。
ListRoomUserFeedEntries(ctx context.Context, query RoomUserFeedQuery) ([]RoomListEntry, error)
// ListRoomRelatedFeedEntries 根据 user-service 关系事实查询 active 房间卡片,不持久化关系状态。
ListRoomRelatedFeedEntries(ctx context.Context, query RoomRelatedFeedQuery) ([]RoomListEntry, error)
// ProjectRoomPresence 用最新快照刷新用户当前房间读模型;失败不应破坏 Room Cell 已提交状态。
ProjectRoomPresence(ctx context.Context, snapshot RoomPresenceSnapshot) error
// GetCurrentRoomPresence 查询用户当前 active 房间 presence不扫描房间列表或快照。

View File

@ -414,6 +414,89 @@ func TestListRoomFeedsVisitedUsesJoinRoomFeed(t *testing.T) {
}
}
func TestListRoomFeedsFriendFollowingUsesGatewayRelations(t *testing.T) {
ctx := context.Background()
repository := mysqltest.NewRepository(t)
svc := newRoomService("node-a", 1, router.NewMemoryDirectory(), repository, &fakeSyncPublisher{}, &fakeOutboxPublisher{})
for _, room := range []struct {
roomID string
ownerID int64
regionID int64
title string
}{
{roomID: "room-related-low", ownerID: 10002, regionID: 1001, title: "Low Relation"},
{roomID: "room-related-high", ownerID: 10003, regionID: 1001, title: "High Relation"},
{roomID: "room-related-other-region", ownerID: 10004, regionID: 2002, title: "Other Region"},
} {
if _, err := svc.CreateRoom(ctx, &roomv1.CreateRoomRequest{
Meta: roomservice.NewRequestMeta(room.roomID, room.ownerID),
SeatCount: 4,
Mode: "voice",
RoomName: room.title,
VisibleRegionId: room.regionID,
}); err != nil {
t.Fatalf("CreateRoom %s failed: %v", room.roomID, err)
}
}
resp, err := svc.ListRoomFeeds(ctx, &roomv1.ListRoomFeedsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 42,
VisibleRegionId: 1001,
Tab: "following",
Limit: 1,
RelatedUsers: []*roomv1.RoomFeedRelatedUser{
{UserId: 10002, RelationUpdatedAtMs: 8000},
{UserId: 10003, RelationUpdatedAtMs: 9000},
{UserId: 10004, RelationUpdatedAtMs: 10000},
},
})
if err != nil {
t.Fatalf("ListRoomFeeds following failed: %v", err)
}
if len(resp.GetRooms()) != 1 || resp.GetRooms()[0].GetRoomId() != "room-related-high" {
t.Fatalf("following feed should sort by user-service relation time and filter region: %+v", resp.GetRooms())
}
if resp.GetNextCursor() == "" {
t.Fatal("following feed should return cursor when more related rooms exist")
}
nextResp, err := svc.ListRoomFeeds(ctx, &roomv1.ListRoomFeedsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 42,
VisibleRegionId: 1001,
Tab: "following",
Limit: 20,
Cursor: resp.GetNextCursor(),
RelatedUsers: []*roomv1.RoomFeedRelatedUser{
{UserId: 10002, RelationUpdatedAtMs: 8000},
{UserId: 10003, RelationUpdatedAtMs: 9000},
{UserId: 10004, RelationUpdatedAtMs: 10000},
},
})
if err != nil {
t.Fatalf("ListRoomFeeds following next page failed: %v", err)
}
if len(nextResp.GetRooms()) != 1 || nextResp.GetRooms()[0].GetRoomId() != "room-related-low" {
t.Fatalf("following cursor should continue after previous relation subject: %+v", nextResp.GetRooms())
}
emptyResp, err := svc.ListRoomFeeds(ctx, &roomv1.ListRoomFeedsRequest{
Meta: &roomv1.RequestMeta{AppCode: appcode.Default},
ViewerUserId: 42,
VisibleRegionId: 1001,
Tab: "friend",
Limit: 20,
})
if err != nil {
t.Fatalf("ListRoomFeeds friend with empty related users failed: %v", err)
}
if len(emptyResp.GetRooms()) != 0 {
t.Fatalf("friend feed without user-service relations must be empty: %+v", emptyResp.GetRooms())
}
}
func TestListRoomsRejectsUserFeedTabs(t *testing.T) {
ctx := context.Background()
repository := mysqltest.NewRepository(t)

View File

@ -5,6 +5,7 @@ import (
"context"
"database/sql"
"errors"
"sort"
"strings"
"time"
@ -890,6 +891,74 @@ func (r *Repository) ListRoomUserFeedEntries(ctx context.Context, query roomserv
return entries, nil
}
// ListRoomRelatedFeedEntries 根据 user-service 给出的好友/关注用户查询 active 房间卡片。
func (r *Repository) ListRoomRelatedFeedEntries(ctx context.Context, query roomservice.RoomRelatedFeedQuery) ([]roomservice.RoomListEntry, error) {
if query.Limit <= 0 {
query.Limit = 20
}
query.AppCode = normalizedRecordAppCode(ctx, query.AppCode)
relations := normalizeRoomFeedRelations(query.RelatedUsers)
if len(relations) == 0 {
return []roomservice.RoomListEntry{}, nil
}
sqlText, args := buildRoomRelatedFeedQuerySQL(query, relations)
rows, err := r.db.QueryContext(ctx, sqlText, args...)
if err != nil {
return nil, err
}
defer rows.Close()
entries := make([]roomservice.RoomListEntry, 0, query.Limit)
for rows.Next() {
var entry roomservice.RoomListEntry
if err := rows.Scan(
&entry.AppCode,
&entry.RoomID,
&entry.RoomShortID,
&entry.VisibleRegionID,
&entry.OwnerUserID,
&entry.HostUserID,
&entry.Title,
&entry.CoverURL,
&entry.Mode,
&entry.Status,
&entry.Heat,
&entry.OnlineCount,
&entry.SeatCount,
&entry.OccupiedSeatCount,
&entry.SortScore,
&entry.CreatedAtMS,
&entry.UpdatedAtMS,
); err != nil {
return nil, err
}
entry.FeedSubjectUserID, entry.UpdatedAtMS = relatedFeedSortKey(entry, relations)
if entry.FeedSubjectUserID == 0 || !roomRelatedFeedAfterCursor(entry, query.CursorUpdatedAtMS, query.CursorSubjectUserID, query.CursorRoomID) {
continue
}
entries = append(entries, entry)
}
if err := rows.Err(); err != nil {
return nil, err
}
sort.SliceStable(entries, func(i, j int) bool {
if entries[i].UpdatedAtMS != entries[j].UpdatedAtMS {
return entries[i].UpdatedAtMS > entries[j].UpdatedAtMS
}
if entries[i].FeedSubjectUserID != entries[j].FeedSubjectUserID {
return entries[i].FeedSubjectUserID > entries[j].FeedSubjectUserID
}
return entries[i].RoomID < entries[j].RoomID
})
if len(entries) > query.Limit {
entries = entries[:query.Limit]
}
return entries, nil
}
const roomListSelectColumns = `
SELECT app_code, room_id, room_short_id, visible_region_id, owner_user_id, host_user_id, title, cover_url, mode, status,
heat, online_count, seat_count, occupied_seat_count, sort_score, created_at_ms, updated_at_ms
@ -946,6 +1015,73 @@ func buildRoomUserFeedQuerySQL(query roomservice.RoomUserFeedQuery) (string, []a
return roomUserFeedSelectColumns + "\n\tWHERE " + strings.Join(where, " AND ") + "\n\tORDER BY f.updated_at_ms DESC, f.room_id ASC\n\tLIMIT ?", args
}
func buildRoomRelatedFeedQuerySQL(query roomservice.RoomRelatedFeedQuery, relations map[int64]int64) (string, []any) {
where := []string{"app_code = ?", "visible_region_id = ?", "status = ?"}
args := []any{appcode.Normalize(query.AppCode), query.VisibleRegionID, "active"}
placeholders := make([]string, 0, len(relations))
relatedIDs := make([]any, 0, len(relations))
for userID := range relations {
placeholders = append(placeholders, "?")
relatedIDs = append(relatedIDs, userID)
}
inClause := strings.Join(placeholders, ",")
where = append(where, "(owner_user_id IN ("+inClause+") OR host_user_id IN ("+inClause+"))")
args = append(args, relatedIDs...)
args = append(args, relatedIDs...)
if strings.TrimSpace(query.Query) != "" {
where = append(where, "(room_id LIKE ? ESCAPE '\\\\' OR room_short_id LIKE ? ESCAPE '\\\\' OR title LIKE ? ESCAPE '\\\\')")
like := "%" + escapeRoomListLike(query.Query) + "%"
args = append(args, like, like, like)
}
return roomListSelectColumns + "\n\tWHERE " + strings.Join(where, " AND "), args
}
func normalizeRoomFeedRelations(items []roomservice.RoomFeedRelatedUser) map[int64]int64 {
relations := make(map[int64]int64, len(items))
for _, item := range items {
if item.UserID <= 0 || item.RelationUpdatedAtMS <= 0 {
continue
}
if item.RelationUpdatedAtMS > relations[item.UserID] {
relations[item.UserID] = item.RelationUpdatedAtMS
}
}
return relations
}
func relatedFeedSortKey(entry roomservice.RoomListEntry, relations map[int64]int64) (int64, int64) {
bestUserID, bestUpdatedAtMS := int64(0), int64(0)
choose := func(userID int64) {
updatedAtMS := relations[userID]
if updatedAtMS == 0 {
return
}
if updatedAtMS > bestUpdatedAtMS || (updatedAtMS == bestUpdatedAtMS && userID > bestUserID) {
bestUserID = userID
bestUpdatedAtMS = updatedAtMS
}
}
choose(entry.OwnerUserID)
choose(entry.HostUserID)
return bestUserID, bestUpdatedAtMS
}
func roomRelatedFeedAfterCursor(entry roomservice.RoomListEntry, cursorUpdatedAtMS int64, cursorSubjectUserID int64, cursorRoomID string) bool {
if cursorUpdatedAtMS <= 0 || cursorSubjectUserID <= 0 || cursorRoomID == "" {
return true
}
if entry.UpdatedAtMS != cursorUpdatedAtMS {
return entry.UpdatedAtMS < cursorUpdatedAtMS
}
if entry.FeedSubjectUserID != cursorSubjectUserID {
return entry.FeedSubjectUserID < cursorSubjectUserID
}
return entry.RoomID > cursorRoomID
}
// ProjectRoomPresence 用最新 RoomSnapshot 投影用户当前房间读模型。
func (r *Repository) ProjectRoomPresence(ctx context.Context, snapshot roomservice.RoomPresenceSnapshot) error {
appCode := normalizedRecordAppCode(ctx, snapshot.AppCode)

View File

@ -24,16 +24,16 @@ type UserRepository interface {
FollowUser(ctx context.Context, followerUserID int64, followeeUserID int64, nowMs int64) (userdomain.ProfileStats, error)
// UnfollowUser 取消单向关注,并更新关注计数。
UnfollowUser(ctx context.Context, followerUserID int64, followeeUserID int64, nowMs int64) (userdomain.ProfileStats, error)
// ListFollowing 分页读取当前用户关注的人。
ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FollowRecord, int64, error)
// ListFollowing 读取当前用户关注的人;公开列表使用 page/pageSize房间关系流可用 cursor 做稳定续扫
ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FollowRecord, int64, error)
// ApplyFriend 创建好友申请;已有好友时不重复建申请。
ApplyFriend(ctx context.Context, requesterUserID int64, targetUserID int64, nowMs int64) (userdomain.FriendApplication, bool, error)
// AcceptFriendApplication 同意指定 requester 发来的好友申请,并写双向好友关系。
AcceptFriendApplication(ctx context.Context, accepterUserID int64, requesterUserID int64, nowMs int64) (userdomain.FriendRecord, error)
// DeleteFriend 删除双向好友关系。
DeleteFriend(ctx context.Context, userID int64, friendUserID int64, nowMs int64) (bool, error)
// ListFriends 分页读取好友列表。
ListFriends(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FriendRecord, int64, error)
// ListFriends 读取好友列表;公开列表使用 page/pageSize房间关系流可用 cursor 做稳定续扫
ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error)
// ListFriendApplications 分页读取好友申请。
ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error)
// BatchGetUsers 批量查询用户主状态,缺失用户不应返回占位对象。

View File

@ -194,6 +194,7 @@ func TestSocialRelationsUseReadModelsAndIdempotentEdges(t *testing.T) {
t.Fatalf("visitors mismatch: visitors=%+v total=%d err=%v", visitors, total, err)
}
now = time.UnixMilli(8000)
stats, err = svc.FollowUser(context.Background(), 10001, 10002)
if err != nil || stats.FollowingCount != 1 {
t.Fatalf("follow failed: stats=%+v err=%v", stats, err)
@ -202,24 +203,33 @@ func TestSocialRelationsUseReadModelsAndIdempotentEdges(t *testing.T) {
if err != nil || stats.FollowingCount != 1 {
t.Fatalf("repeat follow should be idempotent: stats=%+v err=%v", stats, err)
}
following, total, err := svc.ListFollowing(context.Background(), 10001, 1, 20)
if err != nil || total != 1 || len(following) != 1 || following[0].FolloweeUserID != 10002 {
now = time.UnixMilli(9000)
stats, err = svc.FollowUser(context.Background(), 10001, 10003)
if err != nil || stats.FollowingCount != 2 {
t.Fatalf("second follow failed: stats=%+v err=%v", stats, err)
}
following, total, err := svc.ListFollowing(context.Background(), 10001, 1, 20, 0, 0)
if err != nil || total != 2 || len(following) != 2 || following[0].FolloweeUserID != 10003 || following[1].FolloweeUserID != 10002 {
t.Fatalf("following mismatch: following=%+v total=%d err=%v", following, total, err)
}
following, total, err = svc.ListFollowing(context.Background(), 10001, 1, 20, 9000, 10003)
if err != nil || total != 2 || len(following) != 1 || following[0].FolloweeUserID != 10002 {
t.Fatalf("following cursor mismatch: following=%+v total=%d err=%v", following, total, err)
}
stats, err = svc.UnfollowUser(context.Background(), 10001, 10002)
if err != nil || stats.FollowingCount != 0 {
if err != nil || stats.FollowingCount != 1 {
t.Fatalf("unfollow failed: stats=%+v err=%v", stats, err)
}
stats, err = svc.UnfollowUser(context.Background(), 10001, 10002)
if err != nil || stats.FollowingCount != 0 {
t.Fatalf("repeat unfollow should stay zero: stats=%+v err=%v", stats, err)
if err != nil || stats.FollowingCount != 1 {
t.Fatalf("repeat unfollow should stay unchanged: stats=%+v err=%v", stats, err)
}
application, alreadyFriends, err := svc.ApplyFriend(context.Background(), 10001, 10002)
if err != nil || alreadyFriends || application.Status != userdomain.FriendApplicationStatusPending {
t.Fatalf("apply friend mismatch: app=%+v already=%v err=%v", application, alreadyFriends, err)
}
friends, total, err := svc.ListFriends(context.Background(), 10001, 1, 20)
friends, total, err := svc.ListFriends(context.Background(), 10001, 1, 20, 0, 0)
if err != nil || total != 0 || len(friends) != 0 {
t.Fatalf("friend application must not create friendship before accept: friends=%+v total=%d err=%v", friends, total, err)
}
@ -236,7 +246,7 @@ func TestSocialRelationsUseReadModelsAndIdempotentEdges(t *testing.T) {
t.Fatalf("repeat accept should be idempotent: friend=%+v err=%v", friend, err)
}
for _, userID := range []int64{10001, 10002} {
friends, total, err = svc.ListFriends(context.Background(), userID, 1, 20)
friends, total, err = svc.ListFriends(context.Background(), userID, 1, 20, 0, 0)
if err != nil || total != 1 || len(friends) != 1 {
t.Fatalf("bidirectional friendship missing for user %d: friends=%+v total=%d err=%v", userID, friends, total, err)
}
@ -254,7 +264,7 @@ func TestSocialRelationsUseReadModelsAndIdempotentEdges(t *testing.T) {
t.Fatalf("repeat delete should be idempotent false: deleted=%v err=%v", deleted, err)
}
for _, userID := range []int64{10001, 10002} {
friends, total, err = svc.ListFriends(context.Background(), userID, 1, 20)
friends, total, err = svc.ListFriends(context.Background(), userID, 1, 20, 0, 0)
if err != nil || total != 0 || len(friends) != 0 {
t.Fatalf("friend should be removed for user %d: friends=%+v total=%d err=%v", userID, friends, total, err)
}

View File

@ -64,7 +64,7 @@ func (s *Service) UnfollowUser(ctx context.Context, followerUserID int64, follow
return s.userRepository.UnfollowUser(ctx, followerUserID, followeeUserID, s.now().UnixMilli())
}
func (s *Service) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FollowRecord, int64, error) {
func (s *Service) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FollowRecord, int64, error) {
if userID <= 0 {
return nil, 0, xerr.New(xerr.InvalidArgument, "user_id is required")
}
@ -72,7 +72,7 @@ func (s *Service) ListFollowing(ctx context.Context, userID int64, page int32, p
return nil, 0, xerr.New(xerr.Unavailable, "user repository is not configured")
}
page, pageSize = normalizeSocialPage(page, pageSize)
return s.userRepository.ListFollowing(ctx, userID, page, pageSize)
return s.userRepository.ListFollowing(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}
func (s *Service) ApplyFriend(ctx context.Context, requesterUserID int64, targetUserID int64) (userdomain.FriendApplication, bool, error) {
@ -114,7 +114,7 @@ func (s *Service) DeleteFriend(ctx context.Context, userID int64, friendUserID i
return s.userRepository.DeleteFriend(ctx, userID, friendUserID, s.now().UnixMilli())
}
func (s *Service) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FriendRecord, int64, error) {
func (s *Service) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) {
if userID <= 0 {
return nil, 0, xerr.New(xerr.InvalidArgument, "user_id is required")
}
@ -122,7 +122,7 @@ func (s *Service) ListFriends(ctx context.Context, userID int64, page int32, pag
return nil, 0, xerr.New(xerr.Unavailable, "user repository is not configured")
}
page, pageSize = normalizeSocialPage(page, pageSize)
return s.userRepository.ListFriends(ctx, userID, page, pageSize)
return s.userRepository.ListFriends(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}
func (s *Service) ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error) {

View File

@ -3,6 +3,7 @@ package user
import (
"context"
"database/sql"
"strings"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
@ -191,7 +192,7 @@ func (r *Repository) UnfollowUser(ctx context.Context, followerUserID int64, fol
return stats, tx.Commit()
}
func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FollowRecord, int64, error) {
func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FollowRecord, int64, error) {
appCode := appcode.FromContext(ctx)
total, err := countRows(ctx, r.db, `
SELECT COUNT(*) FROM user_follows
@ -201,13 +202,21 @@ func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32
if err != nil {
return nil, 0, err
}
where := []string{"app_code = ?", "follower_user_id = ?", "status = ?"}
args := []any{appCode, userID, userdomain.SocialStatusActive}
if cursorUpdatedAtMS > 0 && cursorUserID > 0 {
// 关系流游标沿用列表排序时间倒序user_id 倒序;避免 OFFSET 在关系变更时漂移。
where = append(where, "(followed_at_ms < ? OR (followed_at_ms = ? AND followee_user_id < ?))")
args = append(args, cursorUpdatedAtMS, cursorUpdatedAtMS, cursorUserID)
}
args = append(args, pageSize, socialOffset(page, pageSize))
rows, err := r.db.QueryContext(ctx, `
SELECT follower_user_id, followee_user_id, followed_at_ms
FROM user_follows
WHERE app_code = ? AND follower_user_id = ? AND status = ?
WHERE `+strings.Join(where, " AND ")+`
ORDER BY followed_at_ms DESC, followee_user_id DESC
LIMIT ? OFFSET ?`,
appCode, userID, userdomain.SocialStatusActive, pageSize, socialOffset(page, pageSize),
args...,
)
if err != nil {
return nil, 0, err
@ -305,7 +314,7 @@ func (r *Repository) DeleteFriend(ctx context.Context, userID int64, friendUserI
return true, tx.Commit()
}
func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FriendRecord, int64, error) {
func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) {
appCode := appcode.FromContext(ctx)
total, err := countRows(ctx, r.db, `
SELECT COUNT(*) FROM user_friendships
@ -315,13 +324,21 @@ func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32,
if err != nil {
return nil, 0, err
}
where := []string{"app_code = ?", "user_id = ?", "status = ?"}
args := []any{appCode, userID, userdomain.SocialStatusActive}
if cursorUpdatedAtMS > 0 && cursorUserID > 0 {
// 好友表同样按建立时间倒序、friend_user_id 倒序续扫,避免 Mine 房间流翻页漏项。
where = append(where, "(friended_at_ms < ? OR (friended_at_ms = ? AND friend_user_id < ?))")
args = append(args, cursorUpdatedAtMS, cursorUpdatedAtMS, cursorUserID)
}
args = append(args, pageSize, socialOffset(page, pageSize))
rows, err := r.db.QueryContext(ctx, `
SELECT user_id, friend_user_id, friended_at_ms
FROM user_friendships
WHERE app_code = ? AND user_id = ? AND status = ?
WHERE `+strings.Join(where, " AND ")+`
ORDER BY friended_at_ms DESC, friend_user_id DESC
LIMIT ? OFFSET ?`,
appCode, userID, userdomain.SocialStatusActive, pageSize, socialOffset(page, pageSize),
args...,
)
if err != nil {
return nil, 0, err

View File

@ -87,8 +87,8 @@ func (r *Repository) UnfollowUser(ctx context.Context, followerUserID int64, fol
return r.Repository.UserRepository().UnfollowUser(ctx, followerUserID, followeeUserID, nowMs)
}
func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FollowRecord, int64, error) {
return r.Repository.UserRepository().ListFollowing(ctx, userID, page, pageSize)
func (r *Repository) ListFollowing(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FollowRecord, int64, error) {
return r.Repository.UserRepository().ListFollowing(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}
func (r *Repository) ApplyFriend(ctx context.Context, requesterUserID int64, targetUserID int64, nowMs int64) (userdomain.FriendApplication, bool, error) {
@ -103,8 +103,8 @@ func (r *Repository) DeleteFriend(ctx context.Context, userID int64, friendUserI
return r.Repository.UserRepository().DeleteFriend(ctx, userID, friendUserID, nowMs)
}
func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32) ([]userdomain.FriendRecord, int64, error) {
return r.Repository.UserRepository().ListFriends(ctx, userID, page, pageSize)
func (r *Repository) ListFriends(ctx context.Context, userID int64, page int32, pageSize int32, cursorUpdatedAtMS int64, cursorUserID int64) ([]userdomain.FriendRecord, int64, error) {
return r.Repository.UserRepository().ListFriends(ctx, userID, page, pageSize, cursorUpdatedAtMS, cursorUserID)
}
func (r *Repository) ListFriendApplications(ctx context.Context, userID int64, direction string, page int32, pageSize int32) ([]userdomain.FriendApplication, int64, error) {

View File

@ -235,7 +235,7 @@ func (s *Server) UnfollowUser(ctx context.Context, req *userv1.UnfollowUserReque
// ListFollowing 返回当前用户正在关注的人。
func (s *Server) ListFollowing(ctx context.Context, req *userv1.ListFollowingRequest) (*userv1.ListFollowingResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
records, total, err := s.userSvc.ListFollowing(ctx, req.GetUserId(), req.GetPage(), req.GetPageSize())
records, total, err := s.userSvc.ListFollowing(ctx, req.GetUserId(), req.GetPage(), req.GetPageSize(), req.GetCursorUpdatedAtMs(), req.GetCursorUserId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}
@ -279,7 +279,7 @@ func (s *Server) DeleteFriend(ctx context.Context, req *userv1.DeleteFriendReque
// ListFriends 返回好友列表。
func (s *Server) ListFriends(ctx context.Context, req *userv1.ListFriendsRequest) (*userv1.ListFriendsResponse, error) {
ctx = contextWithApp(ctx, req.GetMeta())
records, total, err := s.userSvc.ListFriends(ctx, req.GetUserId(), req.GetPage(), req.GetPageSize())
records, total, err := s.userSvc.ListFriends(ctx, req.GetUserId(), req.GetPage(), req.GetPageSize(), req.GetCursorUpdatedAtMs(), req.GetCursorUserId())
if err != nil {
return nil, xerr.ToGRPCError(err)
}