rpc: fix channel pinned search for Android

(cherry picked from commit 70978db98c3af3fe72100fa6646780c7a5a13daa)
This commit is contained in:
A 2026-06-08 22:56:36 +08:00
parent b4e47c894f
commit 3642e37cfe
8 changed files with 97 additions and 16 deletions

View file

@ -347,6 +347,7 @@ type ChannelMessage struct {
Reactions *ChannelMessageReactions
Action *ChannelMessageAction
Media *MessageMedia
Pinned bool
Mentioned bool
MediaUnread bool
Pts int
@ -1377,6 +1378,7 @@ type ChannelHistoryFilter struct {
ChannelID int64
Query string
SenderUserID int64
PinnedOnly bool
OffsetID int
OffsetDate int
AddOffset int

View file

@ -1042,6 +1042,9 @@ func tgChannelMessage(viewerUserID int64, m domain.ChannelMessage) tg.MessageCla
Message: m.Body,
Entities: tgMessageEntities(m.Entities),
}
if m.Pinned {
msg.SetPinned(true)
}
if m.EditDate != 0 {
msg.SetEditDate(m.EditDate)
}

View file

@ -453,6 +453,12 @@ func (r *Router) registerMessages(d *tg.ServerDispatcher) {
}
return tgChannelHistoryMessages(userID, history), nil
}
if _, ok := req.Filter.(*tg.InputMessagesFilterPinned); ok {
if _, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer); err != nil {
return nil, err
}
return &tg.MessagesMessages{}, nil
}
if r.deps.Messages == nil {
return messagesNotModifiedOrEmpty(req.Hash), nil
}
@ -5746,16 +5752,17 @@ func (r *Router) channelHistoryFilterFromSearchRequest(userID int64, req *tg.Mes
limit = 100
}
filter := domain.ChannelHistoryFilter{
ChannelID: channelID,
Query: req.Q,
OffsetID: req.OffsetID,
AddOffset: domain.ClampMessageHistoryAddOffset(req.AddOffset),
Limit: limit,
MinDate: req.MinDate,
MaxDate: req.MaxDate,
MaxID: req.MaxID,
MinID: req.MinID,
Hash: req.Hash,
ChannelID: channelID,
Query: req.Q,
PinnedOnly: messagesSearchFilterPinned(req.Filter),
OffsetID: req.OffsetID,
AddOffset: domain.ClampMessageHistoryAddOffset(req.AddOffset),
Limit: limit,
MinDate: req.MinDate,
MaxDate: req.MaxDate,
MaxID: req.MaxID,
MinID: req.MinID,
Hash: req.Hash,
}
if req.FromID != nil {
from, ok := r.domainPeerFromInputPeer(userID, req.FromID)
@ -5767,6 +5774,11 @@ func (r *Router) channelHistoryFilterFromSearchRequest(userID int64, req *tg.Mes
return filter, true
}
func messagesSearchFilterPinned(filter tg.MessagesFilterClass) bool {
_, ok := filter.(*tg.InputMessagesFilterPinned)
return ok
}
func searchFilterNeedsMediaStore(filter tg.MessagesFilterClass) bool {
switch filter.(type) {
case nil, *tg.InputMessagesFilterEmpty:

View file

@ -2723,6 +2723,7 @@ func TestMessagesSearchChannelPeerReturnsSingleCopyMessages(t *testing.T) {
t.Fatalf("create chat: %v", err)
}
channel := created.Updates.(*tg.Updates).Chats[0].(*tg.Channel)
pinnedMsgID := 0
for _, item := range []struct {
userID int64
text string
@ -2732,13 +2733,21 @@ func TestMessagesSearchChannelPeerReturnsSingleCopyMessages(t *testing.T) {
{friend.ID, "not this one", 5002},
{friend.ID, "needle from friend", 5003},
} {
if _, err := r.onMessagesSendMessage(WithUserID(ctx, item.userID), &tg.MessagesSendMessageRequest{
sent, err := r.onMessagesSendMessage(WithUserID(ctx, item.userID), &tg.MessagesSendMessageRequest{
Peer: &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
Message: item.text,
RandomID: item.random,
}); err != nil {
})
if err != nil {
t.Fatalf("send %q: %v", item.text, err)
}
if item.text == "not this one" {
channelUpdates := sent.(*tg.Updates)
if len(channelUpdates.Updates) == 0 {
t.Fatalf("send %q updates = %+v, want updateMessageID", item.text, channelUpdates.Updates)
}
pinnedMsgID = channelUpdates.Updates[0].(*tg.UpdateMessageID).ID
}
}
req := &tg.MessagesSearchRequest{
@ -2804,6 +2813,34 @@ func TestMessagesSearchChannelPeerReturnsSingleCopyMessages(t *testing.T) {
if channelMessages.Count != 0 || len(channelMessages.Messages) != 0 {
t.Fatalf("shared media count search = count %d messages %d, want empty without media store", channelMessages.Count, len(channelMessages.Messages))
}
if _, err := r.onMessagesUpdatePinnedMessage(WithUserID(ctx, owner.ID), &tg.MessagesUpdatePinnedMessageRequest{
Peer: &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
ID: pinnedMsgID,
}); err != nil {
t.Fatalf("pin channel message: %v", err)
}
pinnedReq := &tg.MessagesSearchRequest{
Peer: &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
Filter: &tg.InputMessagesFilterPinned{},
Limit: 40,
}
in.Reset()
if err := pinnedReq.Encode(&in); err != nil {
t.Fatalf("encode pinned search: %v", err)
}
enc, err = r.Dispatch(WithUserID(ctx, friend.ID), [8]byte{}, 0, &in)
if err != nil {
t.Fatalf("dispatch pinned search: %v", err)
}
pinnedMessages, _, _ := searchMessagesPayload(t, enc)
if len(pinnedMessages) != 1 {
t.Fatalf("pinned search returned %d messages, want 1", len(pinnedMessages))
}
pinnedMessage, ok := pinnedMessages[0].(*tg.Message)
if !ok || pinnedMessage.ID != pinnedMsgID || !pinnedMessage.GetPinned() {
t.Fatalf("pinned search message = %#v, want pinned message id=%d", pinnedMessages[0], pinnedMsgID)
}
}
func searchMessagesPayload(t *testing.T, enc bin.Encoder) ([]tg.MessageClass, []tg.ChatClass, []tg.UserClass) {

View file

@ -3133,6 +3133,9 @@ func (s *ChannelStore) ListChannelHistory(_ context.Context, viewerUserID int64,
if msg.ID <= member.AvailableMinID {
continue
}
if filter.PinnedOnly && msg.ID != channel.PinnedMessageID {
continue
}
if query != "" && !strings.Contains(strings.ToLower(msg.Body), query) {
continue
}
@ -3159,7 +3162,9 @@ func (s *ChannelStore) ListChannelHistory(_ context.Context, viewerUserID int64,
}
matched++
if len(out) < limit {
out = append(out, cloneChannelMessage(msg))
item := cloneChannelMessage(msg)
item.Pinned = channel.PinnedMessageID != 0 && item.ID == channel.PinnedMessageID
out = append(out, item)
}
}
s.populateChannelMessageRepliesLocked(viewerUserID, filter.ChannelID, out)
@ -3402,7 +3407,9 @@ func (s *ChannelStore) GetChannelMessages(_ context.Context, viewerUserID, chann
if msg.Deleted || msg.ID <= member.AvailableMinID {
continue
}
messages = append(messages, cloneChannelMessage(msg))
item := cloneChannelMessage(msg)
item.Pinned = channel.PinnedMessageID != 0 && item.ID == channel.PinnedMessageID
messages = append(messages, item)
}
sort.Slice(messages, func(i, j int) bool { return messages[i].ID > messages[j].ID })
s.populateChannelMessageRepliesLocked(viewerUserID, channelID, messages)

View file

@ -4435,6 +4435,13 @@ func (s *ChannelStore) ListChannelHistory(ctx context.Context, viewerUserID int6
baseArgs = append(baseArgs, member.AvailableMinID)
base += fmt.Sprintf(" AND id > $%d", len(baseArgs))
}
if filter.PinnedOnly {
if channel.PinnedMessageID <= 0 {
return domain.ChannelHistory{Channel: channel, Self: member}, nil
}
baseArgs = append(baseArgs, channel.PinnedMessageID)
base += fmt.Sprintf(" AND id = $%d", len(baseArgs))
}
if filter.Query != "" {
baseArgs = append(baseArgs, filter.Query)
base += fmt.Sprintf(" AND body ILIKE '%%' || $%d || '%%'", len(baseArgs))
@ -4577,6 +4584,7 @@ func (s *ChannelStore) ListChannelHistory(ctx context.Context, viewerUserID int6
}
out.Messages = older
}
markPinnedChannelMessages(channel, out.Messages)
out.Count = len(out.Messages)
if hasMoreOlder {
out.Count = len(out.Messages) + 1
@ -4830,6 +4838,7 @@ ORDER BY id DESC`, args...)
if err := rows.Err(); err != nil {
return domain.ChannelHistory{}, err
}
markPinnedChannelMessages(channel, out.Messages)
out.Count = len(out.Messages)
if err := s.populateChannelMessageReplies(ctx, s.db, viewerUserID, channel, out.Messages); err != nil {
return domain.ChannelHistory{}, err
@ -4840,6 +4849,17 @@ ORDER BY id DESC`, args...)
return out, nil
}
func markPinnedChannelMessages(channel domain.Channel, messages []domain.ChannelMessage) {
if channel.PinnedMessageID <= 0 {
return
}
for i := range messages {
if messages[i].ChannelID == channel.ID && messages[i].ID == channel.PinnedMessageID {
messages[i].Pinned = true
}
}
}
func (s *ChannelStore) ReadChannelMessageContents(ctx context.Context, req domain.ReadChannelMessageContentsRequest) (domain.ReadChannelMessageContentsResult, error) {
if req.UserID == 0 || req.ChannelID == 0 {
return domain.ReadChannelMessageContentsResult{}, domain.ErrChannelInvalid