fix: align private and channel update semantics
(cherry picked from commit c65f76f56278f74082c4fa792ed49104d5d33c38)
This commit is contained in:
parent
dce7b92772
commit
d84fa6e126
36 changed files with 1765 additions and 382 deletions
|
|
@ -2700,12 +2700,15 @@ func (r *Router) channelOperationUpdates(ctx context.Context, viewerUserID int64
|
|||
for _, member := range res.Members {
|
||||
users = append(users, member.UserID)
|
||||
}
|
||||
updates := make([]tg.UpdateClass, 0, 1)
|
||||
updates := make([]tg.UpdateClass, 0, 2)
|
||||
if res.Event.Pts != 0 {
|
||||
if update := tgChannelUpdate(viewerUserID, res.Event); update != nil {
|
||||
updates = append(updates, update)
|
||||
}
|
||||
}
|
||||
if res.Channel.ID != 0 {
|
||||
updates = append(updates, &tg.UpdateChannel{ChannelID: res.Channel.ID})
|
||||
}
|
||||
return &tg.Updates{
|
||||
Updates: updates,
|
||||
Users: r.tgUsersForIDs(ctx, viewerUserID, users),
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@ func (r *Router) registerContacts(d *tg.ServerDispatcher) {
|
|||
d.OnContactsImportContacts(r.onContactsImportContacts)
|
||||
d.OnContactsAddContact(r.onContactsAddContact)
|
||||
d.OnContactsDeleteContacts(r.onContactsDeleteContacts)
|
||||
d.OnContactsBlock(r.onContactsBlock)
|
||||
d.OnContactsUnblock(r.onContactsUnblock)
|
||||
d.OnContactsUpdateContactNote(r.onContactsUpdateContactNote)
|
||||
d.OnContactsSearch(r.onContactsSearch)
|
||||
d.OnContactsResolveUsername(r.onContactsResolveUsername)
|
||||
|
|
@ -38,12 +40,7 @@ func (r *Router) registerContacts(d *tg.ServerDispatcher) {
|
|||
d.OnContactsGetTopPeers(func(ctx context.Context, req *tg.ContactsGetTopPeersRequest) (tg.ContactsTopPeersClass, error) {
|
||||
return tdesktop.TopPeers(), nil
|
||||
})
|
||||
d.OnContactsGetBlocked(func(ctx context.Context, req *tg.ContactsGetBlockedRequest) (tg.ContactsBlockedClass, error) {
|
||||
if req.Limit > 50 {
|
||||
return nil, limitInvalidErr()
|
||||
}
|
||||
return tdesktop.BlockedContacts(), nil
|
||||
})
|
||||
d.OnContactsGetBlocked(r.onContactsGetBlocked)
|
||||
d.OnContactsGetSponsoredPeers(func(ctx context.Context, q string) (tg.ContactsSponsoredPeersClass, error) {
|
||||
if utf8.RuneCountInString(q) > maxContactSearchQLen {
|
||||
return nil, limitInvalidErr()
|
||||
|
|
@ -52,6 +49,81 @@ func (r *Router) registerContacts(d *tg.ServerDispatcher) {
|
|||
})
|
||||
}
|
||||
|
||||
func (r *Router) onContactsBlock(ctx context.Context, req *tg.ContactsBlockRequest) (bool, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
peer, ok := r.domainPeerFromInputPeer(userID, req.ID)
|
||||
if !ok || peer.Type != domain.PeerTypeUser || peer.ID == 0 || peer.ID == userID {
|
||||
return false, userIDInvalidErr()
|
||||
}
|
||||
if r.deps.Contacts == nil {
|
||||
return true, nil
|
||||
}
|
||||
if _, err := r.deps.Contacts.BlockContact(ctx, userID, peer.ID, int(r.clock.Now().Unix())); err != nil {
|
||||
return false, contactErr(err)
|
||||
}
|
||||
if settings, err := r.deps.Contacts.GetPeerSettings(ctx, userID, peer); err == nil {
|
||||
_ = r.recordPeerSettings(ctx, userID, peer, settings)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Router) onContactsUnblock(ctx context.Context, req *tg.ContactsUnblockRequest) (bool, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
peer, ok := r.domainPeerFromInputPeer(userID, req.ID)
|
||||
if !ok || peer.Type != domain.PeerTypeUser || peer.ID == 0 || peer.ID == userID {
|
||||
return false, userIDInvalidErr()
|
||||
}
|
||||
if r.deps.Contacts == nil {
|
||||
return true, nil
|
||||
}
|
||||
if _, err := r.deps.Contacts.UnblockContact(ctx, userID, peer.ID); err != nil {
|
||||
return false, contactErr(err)
|
||||
}
|
||||
if settings, err := r.deps.Contacts.GetPeerSettings(ctx, userID, peer); err == nil {
|
||||
_ = r.recordPeerSettings(ctx, userID, peer, settings)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *Router) onContactsGetBlocked(ctx context.Context, req *tg.ContactsGetBlockedRequest) (tg.ContactsBlockedClass, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if req.Limit > 100 || req.Offset < 0 {
|
||||
return nil, limitInvalidErr()
|
||||
}
|
||||
if r.deps.Contacts == nil {
|
||||
return tdesktop.BlockedContacts(), nil
|
||||
}
|
||||
list, err := r.deps.Contacts.GetBlocked(ctx, userID, req.Offset, req.Limit)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
blocked := make([]tg.PeerBlocked, 0, len(list.Blocked))
|
||||
users := make([]tg.UserClass, 0, len(list.Blocked))
|
||||
for _, item := range list.Blocked {
|
||||
if item.User.ID == 0 {
|
||||
continue
|
||||
}
|
||||
blocked = append(blocked, tg.PeerBlocked{
|
||||
PeerID: &tg.PeerUser{UserID: item.User.ID},
|
||||
Date: item.Date,
|
||||
})
|
||||
users = append(users, r.tgUser(item.User))
|
||||
}
|
||||
if list.Count > len(blocked)+req.Offset {
|
||||
return &tg.ContactsBlockedSlice{Count: list.Count, Blocked: blocked, Chats: []tg.ChatClass{}, Users: users}, nil
|
||||
}
|
||||
return &tg.ContactsBlocked{Blocked: blocked, Chats: []tg.ChatClass{}, Users: users}, nil
|
||||
}
|
||||
|
||||
func (r *Router) onContactsGetContacts(ctx context.Context, hash int64) (tg.ContactsContactsClass, error) {
|
||||
if r.deps.Contacts == nil {
|
||||
return &tg.ContactsContacts{}, nil
|
||||
|
|
|
|||
|
|
@ -349,6 +349,16 @@ func tgUpdatesDifference(diff domain.UpdateDifference) tg.UpdatesDifferenceClass
|
|||
}
|
||||
}
|
||||
}
|
||||
for _, nudge := range diff.ChannelNudges {
|
||||
if nudge.ChannelID == 0 {
|
||||
continue
|
||||
}
|
||||
update := &tg.UpdateChannelTooLong{ChannelID: nudge.ChannelID}
|
||||
if nudge.Pts > 0 {
|
||||
update.SetPts(nudge.Pts)
|
||||
}
|
||||
out.OtherUpdates = append(out.OtherUpdates, update)
|
||||
}
|
||||
// Partial:连续事件被 limit 截断、后面还有 → updates.differenceSlice,客户端据 IntermediateState 续拉。
|
||||
if diff.Partial {
|
||||
return &tg.UpdatesDifferenceSlice{
|
||||
|
|
@ -561,6 +571,15 @@ func tgOtherUpdateFromEvent(event domain.UpdateEvent) tg.UpdateClass {
|
|||
}
|
||||
case domain.UpdateEventReadHistoryOutbox:
|
||||
return tgReadHistoryOutbox(event)
|
||||
case domain.UpdateEventReadMessageContents:
|
||||
if len(event.MessageIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
return &tg.UpdateReadMessagesContents{
|
||||
Messages: append([]int(nil), event.MessageIDs...),
|
||||
Pts: event.Pts,
|
||||
PtsCount: event.PtsCount,
|
||||
}
|
||||
case domain.UpdateEventEditMessage:
|
||||
msg := tgMessage(event.Message)
|
||||
if msg == nil {
|
||||
|
|
@ -856,12 +875,13 @@ func tgMessage(m domain.Message) tg.MessageClass {
|
|||
return nil
|
||||
}
|
||||
msg := &tg.Message{
|
||||
Out: m.Out,
|
||||
ID: m.ID,
|
||||
PeerID: peer,
|
||||
Date: m.Date,
|
||||
Message: m.Body,
|
||||
Entities: tgMessageEntities(m.Entities),
|
||||
Out: m.Out,
|
||||
MediaUnread: m.MediaUnread,
|
||||
ID: m.ID,
|
||||
PeerID: peer,
|
||||
Date: m.Date,
|
||||
Message: m.Body,
|
||||
Entities: tgMessageEntities(m.Entities),
|
||||
}
|
||||
if m.EditDate != 0 {
|
||||
msg.SetEditDate(m.EditDate)
|
||||
|
|
@ -993,16 +1013,18 @@ func tgChannelMessage(viewerUserID int64, m domain.ChannelMessage) tg.MessageCla
|
|||
return msg
|
||||
}
|
||||
msg := &tg.Message{
|
||||
Out: outgoing,
|
||||
Silent: m.Silent,
|
||||
Post: m.Post,
|
||||
Noforwards: m.NoForwards,
|
||||
ID: m.ID,
|
||||
FromID: from,
|
||||
PeerID: peer,
|
||||
Date: m.Date,
|
||||
Message: m.Body,
|
||||
Entities: tgMessageEntities(m.Entities),
|
||||
Out: outgoing,
|
||||
Silent: m.Silent,
|
||||
Post: m.Post,
|
||||
Noforwards: m.NoForwards,
|
||||
Mentioned: m.Mentioned,
|
||||
MediaUnread: m.MediaUnread,
|
||||
ID: m.ID,
|
||||
FromID: from,
|
||||
PeerID: peer,
|
||||
Date: m.Date,
|
||||
Message: m.Body,
|
||||
Entities: tgMessageEntities(m.Entities),
|
||||
}
|
||||
if m.EditDate != 0 {
|
||||
msg.SetEditDate(m.EditDate)
|
||||
|
|
|
|||
|
|
@ -138,6 +138,10 @@ type ContactsService interface {
|
|||
DeleteContacts(ctx context.Context, userID int64, contactUserIDs []int64) (int, error)
|
||||
UpdateContactNote(ctx context.Context, userID, contactUserID int64, note string, entities []domain.MessageEntity) (domain.Contact, error)
|
||||
GetPeerSettings(ctx context.Context, userID int64, peer domain.Peer) (domain.PeerSettings, error)
|
||||
BlockContact(ctx context.Context, userID, peerUserID int64, date int) (bool, error)
|
||||
UnblockContact(ctx context.Context, userID, peerUserID int64) (bool, error)
|
||||
IsBlocked(ctx context.Context, userID, peerUserID int64) (bool, error)
|
||||
GetBlocked(ctx context.Context, userID int64, offset, limit int) (domain.BlockedContactList, error)
|
||||
}
|
||||
|
||||
// DialogsService 抽象会话列表查询。
|
||||
|
|
@ -278,6 +282,7 @@ type ChannelsService interface {
|
|||
GetMessageReadParticipants(ctx context.Context, userID int64, req domain.ChannelReadParticipantsRequest) (domain.ChannelReadParticipantsResult, error)
|
||||
GetDifference(ctx context.Context, userID int64, req domain.ChannelDifferenceRequest) (domain.ChannelDifference, error)
|
||||
ActiveChannelIDsForUser(ctx context.Context, userID, afterChannelID int64, limit int) ([]int64, error)
|
||||
DirtyActiveChannelsForUser(ctx context.Context, userID int64, sinceDate int, afterChannelID int64, limit int) ([]domain.DirtyChannel, error)
|
||||
ActiveMemberIDs(ctx context.Context, userID, channelID int64, limit int) ([]int64, error)
|
||||
InviteAdminMemberIDs(ctx context.Context, channelID int64, limit int) ([]int64, error)
|
||||
FilterActiveMemberIDs(ctx context.Context, channelID int64, userIDs []int64) ([]int64, error)
|
||||
|
|
|
|||
|
|
@ -140,6 +140,10 @@ func messageAuthorRequiredErr() error { return tgerr.New(403, "MESSAGE_AUTHOR_RE
|
|||
|
||||
func messageNotModifiedErr() error { return tgerr.New(400, "MESSAGE_NOT_MODIFIED") }
|
||||
|
||||
func messageEditForbiddenErr() error { return tgerr.New(403, "EDIT_MESSAGES_FORBIDDEN") }
|
||||
|
||||
func messageDeleteForbiddenErr() error { return tgerr.New(403, "DELETE_MESSAGES_FORBIDDEN") }
|
||||
|
||||
func messageNotReadYetErr() error { return tgerr.New(400, "MESSAGE_NOT_READ_YET") }
|
||||
|
||||
func replyMessageIDInvalidErr() error { return tgerr.New(400, "REPLY_MESSAGE_ID_INVALID") }
|
||||
|
|
|
|||
|
|
@ -2808,6 +2808,7 @@ func (r *Router) onMessagesGetSponsoredMessages(ctx context.Context, req *tg.Mes
|
|||
|
||||
func (r *Router) onMessagesReadMessageContents(ctx context.Context, ids []int) (*tg.MessagesAffectedMessages, error) {
|
||||
id, _ := AuthKeyIDFrom(ctx)
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
|
|
@ -2823,8 +2824,11 @@ func (r *Router) onMessagesReadMessageContents(ctx context.Context, ids []int) (
|
|||
read := domain.ReadMessageContentsResult{OwnerUserID: userID}
|
||||
if r.deps.Messages != nil {
|
||||
read, err = r.deps.Messages.ReadMessageContents(ctx, userID, domain.ReadMessageContentsRequest{
|
||||
OwnerUserID: userID,
|
||||
IDs: ids,
|
||||
OwnerUserID: userID,
|
||||
IDs: ids,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: id,
|
||||
OriginSessionID: sessionID,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, domain.ErrMessageIDInvalid) {
|
||||
|
|
@ -2833,12 +2837,15 @@ func (r *Router) onMessagesReadMessageContents(ctx context.Context, ids []int) (
|
|||
return nil, internalErr()
|
||||
}
|
||||
}
|
||||
affected, err := r.affectedMessages(ctx, id, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
affected := &tg.MessagesAffectedMessages{Pts: read.Event.Pts, PtsCount: read.Event.PtsCount}
|
||||
if read.Event.Pts == 0 {
|
||||
affected, err = r.affectedMessages(ctx, id, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if contentIDs := readMessageContentIDs(read.MessageIDs); len(contentIDs) > 0 {
|
||||
r.pushUserUpdates(ctx, userID, &tg.Updates{
|
||||
r.pushUserUpdatesIfNoReliableDispatch(ctx, userID, &tg.Updates{
|
||||
Updates: []tg.UpdateClass{&tg.UpdateReadMessagesContents{
|
||||
Messages: contentIDs,
|
||||
Pts: affected.Pts,
|
||||
|
|
@ -4378,6 +4385,10 @@ func (r *Router) onMessagesForwardMessages(ctx context.Context, req *tg.Messages
|
|||
if r.deps.Channels == nil || r.deps.Messages == nil {
|
||||
return nil, peerIDInvalidErr()
|
||||
}
|
||||
recipientBlocked, err := r.peerBlocksUser(ctx, userID, toPeer.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sources, err := r.forwardSources(ctx, userID, fromPeer, req.ID)
|
||||
if err != nil {
|
||||
return nil, messageForwardErr(err)
|
||||
|
|
@ -4391,19 +4402,20 @@ func (r *Router) onMessagesForwardMessages(ctx context.Context, req *tg.Messages
|
|||
forward = nil
|
||||
}
|
||||
sent, err := r.deps.Messages.SendPrivateText(ctx, userID, domain.SendPrivateTextRequest{
|
||||
SenderUserID: userID,
|
||||
RecipientUserID: toPeer.ID,
|
||||
RandomID: req.RandomID[i],
|
||||
Message: source.body,
|
||||
Entities: source.entities,
|
||||
Media: source.media,
|
||||
Silent: req.Silent,
|
||||
NoForwards: req.Noforwards,
|
||||
ReplyTo: replyTo,
|
||||
Forward: forward,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
SenderUserID: userID,
|
||||
RecipientUserID: toPeer.ID,
|
||||
RandomID: req.RandomID[i],
|
||||
Message: source.body,
|
||||
Entities: source.entities,
|
||||
Media: source.media,
|
||||
Silent: req.Silent,
|
||||
NoForwards: req.Noforwards,
|
||||
ReplyTo: replyTo,
|
||||
Forward: forward,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
RecipientBlocked: recipientBlocked,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, messageForwardErr(err)
|
||||
|
|
@ -4421,19 +4433,24 @@ func (r *Router) onMessagesForwardMessages(ctx context.Context, req *tg.Messages
|
|||
}
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
authKeyID, _ := AuthKeyIDFrom(ctx)
|
||||
recipientBlocked, err := r.peerBlocksUser(ctx, userID, toPeer.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := r.deps.Messages.ForwardPrivateMessages(ctx, userID, domain.ForwardPrivateMessagesRequest{
|
||||
OwnerUserID: userID,
|
||||
FromPeer: fromPeer,
|
||||
ToUserID: toPeer.ID,
|
||||
MessageIDs: append([]int(nil), req.ID...),
|
||||
RandomIDs: append([]int64(nil), req.RandomID...),
|
||||
Silent: req.Silent,
|
||||
NoForwards: req.Noforwards,
|
||||
DropAuthor: req.DropAuthor,
|
||||
ReplyTo: replyTo,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
OwnerUserID: userID,
|
||||
FromPeer: fromPeer,
|
||||
ToUserID: toPeer.ID,
|
||||
MessageIDs: append([]int(nil), req.ID...),
|
||||
RandomIDs: append([]int64(nil), req.RandomID...),
|
||||
Silent: req.Silent,
|
||||
NoForwards: req.Noforwards,
|
||||
DropAuthor: req.DropAuthor,
|
||||
ReplyTo: replyTo,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
RecipientBlocked: recipientBlocked,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, messageForwardErr(err)
|
||||
|
|
@ -4641,6 +4658,13 @@ func (r *Router) onMessagesEditMessage(ctx context.Context, req *tg.MessagesEdit
|
|||
if peer.Type != domain.PeerTypeUser || r.deps.Messages == nil {
|
||||
return nil, peerIDInvalidErr()
|
||||
}
|
||||
blocked, err := r.peerBlocksUser(ctx, userID, peer.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blocked {
|
||||
return nil, messageEditForbiddenErr()
|
||||
}
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
authKeyID, _ := AuthKeyIDFrom(ctx)
|
||||
res, err := r.deps.Messages.EditMessage(ctx, userID, domain.EditMessageRequest{
|
||||
|
|
@ -4818,6 +4842,19 @@ func (r *Router) onMessagesDeleteMessages(ctx context.Context, req *tg.MessagesD
|
|||
if len(req.ID) > domain.MaxDeleteMessageIDs {
|
||||
return nil, limitInvalidErr()
|
||||
}
|
||||
if req.GetRevoke() {
|
||||
list, err := r.deps.Messages.GetMessages(ctx, userID, req.ID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
blocked, err := r.messagesTouchBlockedPeer(ctx, userID, list.Messages)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blocked {
|
||||
return nil, messageDeleteForbiddenErr()
|
||||
}
|
||||
}
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
res, err := r.deps.Messages.DeleteMessages(ctx, userID, domain.DeleteMessagesRequest{
|
||||
OwnerUserID: userID,
|
||||
|
|
@ -4892,6 +4929,15 @@ func (r *Router) onMessagesDeleteHistory(ctx context.Context, req *tg.MessagesDe
|
|||
if r.deps.Messages == nil {
|
||||
return r.affectedHistory(ctx, authKeyID, userID, 0)
|
||||
}
|
||||
if req.GetRevoke() {
|
||||
blocked, err := r.peerBlocksUser(ctx, userID, peer.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blocked {
|
||||
return nil, messageDeleteForbiddenErr()
|
||||
}
|
||||
}
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
res, err := r.deps.Messages.DeleteHistory(ctx, userID, domain.DeleteHistoryRequest{
|
||||
OwnerUserID: userID,
|
||||
|
|
@ -4972,6 +5018,38 @@ func messageSendErr(err error) error {
|
|||
}
|
||||
}
|
||||
|
||||
func (r *Router) peerBlocksUser(ctx context.Context, userID, peerUserID int64) (bool, error) {
|
||||
if userID == 0 || peerUserID == 0 || userID == peerUserID || r.deps.Contacts == nil {
|
||||
return false, nil
|
||||
}
|
||||
blocked, err := r.deps.Contacts.IsBlocked(ctx, peerUserID, userID)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
return blocked, nil
|
||||
}
|
||||
|
||||
func (r *Router) messagesTouchBlockedPeer(ctx context.Context, userID int64, messages []domain.Message) (bool, error) {
|
||||
seen := make(map[int64]struct{}, len(messages))
|
||||
for _, msg := range messages {
|
||||
if msg.Peer.Type != domain.PeerTypeUser || msg.Peer.ID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[msg.Peer.ID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[msg.Peer.ID] = struct{}{}
|
||||
blocked, err := r.peerBlocksUser(ctx, userID, msg.Peer.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if blocked {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func messageForwardErr(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrMessageIDInvalid):
|
||||
|
|
|
|||
|
|
@ -829,13 +829,16 @@ func TestMessagesCreateChatCreatesMegagroupAndDialogsRPC(t *testing.T) {
|
|||
if !ok || !channel.Megagroup || channel.Broadcast {
|
||||
t.Fatalf("chat = %#v, want megagroup channel", updates.Chats[0])
|
||||
}
|
||||
if len(updates.Updates) != 2 {
|
||||
t.Fatalf("updates len = %d, want create + invite service messages", len(updates.Updates))
|
||||
if len(updates.Updates) != 4 {
|
||||
t.Fatalf("updates len = %d, want create/invite service messages plus channel refreshes", len(updates.Updates))
|
||||
}
|
||||
newMsg, ok := updates.Updates[0].(*tg.UpdateNewChannelMessage)
|
||||
if !ok || newMsg.Pts != 1 || newMsg.PtsCount != 1 {
|
||||
t.Fatalf("create update = %#v, want channel pts=1", updates.Updates[0])
|
||||
}
|
||||
if refresh, ok := updates.Updates[1].(*tg.UpdateChannel); !ok || refresh.ChannelID != channel.ID {
|
||||
t.Fatalf("create refresh = %#v, want channel refresh", updates.Updates[1])
|
||||
}
|
||||
service, ok := newMsg.Message.(*tg.MessageService)
|
||||
if !ok {
|
||||
t.Fatalf("create message = %T, want service", newMsg.Message)
|
||||
|
|
@ -843,9 +846,12 @@ func TestMessagesCreateChatCreatesMegagroupAndDialogsRPC(t *testing.T) {
|
|||
if _, ok := service.Action.(*tg.MessageActionChannelCreate); !ok {
|
||||
t.Fatalf("service action = %T, want channel create", service.Action)
|
||||
}
|
||||
inviteMsg, ok := updates.Updates[1].(*tg.UpdateNewChannelMessage)
|
||||
inviteMsg, ok := updates.Updates[2].(*tg.UpdateNewChannelMessage)
|
||||
if !ok || inviteMsg.Pts != 2 || inviteMsg.PtsCount != 1 {
|
||||
t.Fatalf("invite update = %#v, want channel pts=2", updates.Updates[1])
|
||||
t.Fatalf("invite update = %#v, want channel pts=2", updates.Updates[2])
|
||||
}
|
||||
if refresh, ok := updates.Updates[3].(*tg.UpdateChannel); !ok || refresh.ChannelID != channel.ID {
|
||||
t.Fatalf("invite refresh = %#v, want channel refresh", updates.Updates[3])
|
||||
}
|
||||
inviteService, ok := inviteMsg.Message.(*tg.MessageService)
|
||||
if !ok {
|
||||
|
|
@ -1102,8 +1108,8 @@ func TestMessagesCreateChatDispatchRemembersTDesktopClientInfo(t *testing.T) {
|
|||
if !ok || !channel.Megagroup || !channel.Creator {
|
||||
t.Fatalf("second chat = %#v, want creator megagroup channel", updates.Chats[1])
|
||||
}
|
||||
if len(updates.Updates) != 2 {
|
||||
t.Fatalf("updates len = %d, want create + invite service messages", len(updates.Updates))
|
||||
if len(updates.Updates) != 4 {
|
||||
t.Fatalf("updates len = %d, want create/invite service messages plus channel refreshes", len(updates.Updates))
|
||||
}
|
||||
participants, err := r.onChannelsGetParticipants(WithUserID(ctx, owner.ID), &tg.ChannelsGetParticipantsRequest{
|
||||
Channel: &tg.InputChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
||||
|
|
@ -4337,8 +4343,12 @@ func TestChannelAdminPinInviteRPC(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("import invite: %v", err)
|
||||
}
|
||||
if updates := imported.(*tg.Updates); len(updates.Chats) != 1 || len(updates.Updates) != 1 {
|
||||
t.Fatalf("import updates = %+v, want chat and join service update", updates)
|
||||
if updates := imported.(*tg.Updates); len(updates.Chats) != 1 || len(updates.Updates) != 2 {
|
||||
t.Fatalf("import updates = %+v, want chat, join service update, and channel refresh", updates)
|
||||
} else if _, ok := updates.Updates[0].(*tg.UpdateNewChannelMessage); !ok {
|
||||
t.Fatalf("import first update = %T, want join service update", updates.Updates[0])
|
||||
} else if refresh, ok := updates.Updates[1].(*tg.UpdateChannel); !ok || refresh.ChannelID != channel.ID {
|
||||
t.Fatalf("import second update = %#v, want channel refresh", updates.Updates[1])
|
||||
}
|
||||
inviteList, err := r.onMessagesGetExportedChatInvites(WithUserID(ctx, friend.ID), &tg.MessagesGetExportedChatInvitesRequest{
|
||||
Peer: &tg.InputPeerChannel{ChannelID: channel.ID, AccessHash: channel.AccessHash},
|
||||
|
|
@ -7202,6 +7212,29 @@ func TestUpdatesDifferenceIncludesDeleteMessages(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesChannelTooLongNudge(t *testing.T) {
|
||||
got, ok := tgUpdatesDifference(domain.UpdateDifference{
|
||||
State: domain.UpdateState{Pts: 8, Date: 1700000250, Seq: 0},
|
||||
ChannelNudges: []domain.ChannelDifferenceNudge{{
|
||||
ChannelID: 2000000001,
|
||||
Pts: 12,
|
||||
}},
|
||||
}).(*tg.UpdatesDifference)
|
||||
if !ok {
|
||||
t.Fatalf("difference = %T, want *tg.UpdatesDifference", got)
|
||||
}
|
||||
if got.State.Pts != 8 || len(got.OtherUpdates) != 1 {
|
||||
t.Fatalf("difference = %+v, want one channel nudge and account pts unchanged", got)
|
||||
}
|
||||
update, ok := got.OtherUpdates[0].(*tg.UpdateChannelTooLong)
|
||||
if !ok || update.ChannelID != 2000000001 {
|
||||
t.Fatalf("update = %T %+v, want UpdateChannelTooLong", got.OtherUpdates[0], got.OtherUpdates[0])
|
||||
}
|
||||
if pts, ok := update.GetPts(); !ok || pts != 12 {
|
||||
t.Fatalf("channel nudge pts = %d set=%v, want 12", pts, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatesDifferenceIncludesSettingsUpdates(t *testing.T) {
|
||||
peer := domain.Peer{Type: domain.PeerTypeUser, ID: 1000000002}
|
||||
got, ok := tgUpdatesDifference(domain.UpdateDifference{
|
||||
|
|
@ -8354,6 +8387,137 @@ func TestMessagesSendMessageReturnsUpdateAndRecordsOwnerContext(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestContactsBlockGetBlockedAndUnblockRPC(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550009001", FirstName: "Alice"})
|
||||
if err != nil {
|
||||
t.Fatalf("create alice: %v", err)
|
||||
}
|
||||
bob, err := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550009002", FirstName: "Bob"})
|
||||
if err != nil {
|
||||
t.Fatalf("create bob: %v", err)
|
||||
}
|
||||
r := New(Config{}, Deps{
|
||||
Users: appusers.NewService(userStore),
|
||||
Contacts: appcontacts.NewService(memory.NewContactStore(), userStore),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
ok, err := r.onContactsBlock(WithUserID(ctx, bob.ID), &tg.ContactsBlockRequest{
|
||||
ID: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
||||
})
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("contacts.block = %v, %v", ok, err)
|
||||
}
|
||||
blocked, err := r.onContactsGetBlocked(WithUserID(ctx, bob.ID), &tg.ContactsGetBlockedRequest{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("contacts.getBlocked: %v", err)
|
||||
}
|
||||
full, ok := blocked.(*tg.ContactsBlocked)
|
||||
if !ok || len(full.Blocked) != 1 || len(full.Users) != 1 {
|
||||
t.Fatalf("blocked = %T %+v, want one blocked user", blocked, blocked)
|
||||
}
|
||||
if peer, ok := full.Blocked[0].PeerID.(*tg.PeerUser); !ok || peer.UserID != alice.ID {
|
||||
t.Fatalf("blocked peer = %#v, want alice", full.Blocked[0].PeerID)
|
||||
}
|
||||
if user, ok := full.Users[0].(*tg.User); !ok || user.ID != alice.ID {
|
||||
t.Fatalf("blocked user = %#v, want alice", full.Users[0])
|
||||
}
|
||||
|
||||
ok, err = r.onContactsUnblock(WithUserID(ctx, bob.ID), &tg.ContactsUnblockRequest{
|
||||
ID: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
||||
})
|
||||
if err != nil || !ok {
|
||||
t.Fatalf("contacts.unblock = %v, %v", ok, err)
|
||||
}
|
||||
blocked, err = r.onContactsGetBlocked(WithUserID(ctx, bob.ID), &tg.ContactsGetBlockedRequest{Limit: 10})
|
||||
if err != nil {
|
||||
t.Fatalf("contacts.getBlocked after unblock: %v", err)
|
||||
}
|
||||
if full, ok := blocked.(*tg.ContactsBlocked); !ok || len(full.Blocked) != 0 {
|
||||
t.Fatalf("blocked after unblock = %T %+v, want empty contacts.blocked", blocked, blocked)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesPrivateBlockPreventsRecipientInboxAndRevokeRPC(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
userStore := memory.NewUserStore()
|
||||
alice, err := userStore.Create(ctx, domain.User{AccessHash: 11, Phone: "15550009101", FirstName: "Alice"})
|
||||
if err != nil {
|
||||
t.Fatalf("create alice: %v", err)
|
||||
}
|
||||
bob, err := userStore.Create(ctx, domain.User{AccessHash: 22, Phone: "15550009102", FirstName: "Bob"})
|
||||
if err != nil {
|
||||
t.Fatalf("create bob: %v", err)
|
||||
}
|
||||
dialogs := memory.NewDialogStore()
|
||||
messageStore := memory.NewMessageStore(dialogs)
|
||||
contactStore := memory.NewContactStore()
|
||||
r := New(Config{}, Deps{
|
||||
Users: appusers.NewService(userStore),
|
||||
Contacts: appcontacts.NewService(contactStore, userStore),
|
||||
Messages: appmessages.NewService(messageStore, dialogs),
|
||||
Dialogs: appdialogs.NewService(dialogs),
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
|
||||
delivered, err := r.onMessagesSendMessage(WithUserID(ctx, alice.ID), &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
|
||||
Message: "before block",
|
||||
RandomID: 91001,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("send before block: %v", err)
|
||||
}
|
||||
deliveredUpdates := delivered.(*tg.Updates)
|
||||
deliveredMsg := deliveredUpdates.Updates[1].(*tg.UpdateNewMessage).Message.(*tg.Message)
|
||||
|
||||
if ok, err := r.onContactsBlock(WithUserID(ctx, bob.ID), &tg.ContactsBlockRequest{
|
||||
ID: &tg.InputPeerUser{UserID: alice.ID, AccessHash: alice.AccessHash},
|
||||
}); err != nil || !ok {
|
||||
t.Fatalf("bob block alice = %v, %v", ok, err)
|
||||
}
|
||||
blockedSend, err := r.onMessagesSendMessage(WithUserID(ctx, alice.ID), &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: bob.ID, AccessHash: bob.AccessHash},
|
||||
Message: "after block",
|
||||
RandomID: 91002,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("send after block: %v", err)
|
||||
}
|
||||
blockedUpdates := blockedSend.(*tg.Updates)
|
||||
blockedMsg := blockedUpdates.Updates[1].(*tg.UpdateNewMessage).Message.(*tg.Message)
|
||||
if blockedMsg.ID == 0 || !blockedMsg.Out {
|
||||
t.Fatalf("blocked sender update = %#v, want outgoing sender message", blockedMsg)
|
||||
}
|
||||
bobHistory, err := messageStore.ListByUser(ctx, bob.ID, domain.MessageFilter{
|
||||
HasPeer: true,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: alice.ID},
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("bob history: %v", err)
|
||||
}
|
||||
if len(bobHistory.Messages) != 1 || bobHistory.Messages[0].Body != "before block" {
|
||||
t.Fatalf("bob history = %+v, want only pre-block delivered message", bobHistory.Messages)
|
||||
}
|
||||
aliceHistory, err := messageStore.ListByUser(ctx, alice.ID, domain.MessageFilter{
|
||||
HasPeer: true,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: bob.ID},
|
||||
Limit: 10,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("alice history: %v", err)
|
||||
}
|
||||
if len(aliceHistory.Messages) != 2 {
|
||||
t.Fatalf("alice history len = %d, want delivered + sender-only blocked message", len(aliceHistory.Messages))
|
||||
}
|
||||
deleteReq := &tg.MessagesDeleteMessagesRequest{ID: []int{deliveredMsg.ID}}
|
||||
deleteReq.SetRevoke(true)
|
||||
if _, err := r.onMessagesDeleteMessages(WithUserID(ctx, alice.ID), deleteReq); err == nil || !strings.Contains(err.Error(), "DELETE_MESSAGES_FORBIDDEN") {
|
||||
t.Fatalf("revoke after block err = %v, want DELETE_MESSAGES_FORBIDDEN", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesSendMessageSupportsReplyAndFlags(t *testing.T) {
|
||||
const (
|
||||
senderID = int64(1000000001)
|
||||
|
|
|
|||
|
|
@ -94,21 +94,26 @@ func (r *Router) sendOutgoing(ctx context.Context, userID int64, peer domain.Pee
|
|||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
recipientBlocked, err := r.peerBlocksUser(ctx, userID, peer.ID)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
sessionID, _ := SessionIDFrom(ctx)
|
||||
authKeyID, _ := AuthKeyIDFrom(ctx)
|
||||
res, err := r.deps.Messages.SendPrivateText(ctx, userID, domain.SendPrivateTextRequest{
|
||||
SenderUserID: userID,
|
||||
RecipientUserID: peer.ID,
|
||||
RandomID: p.randomID,
|
||||
Message: p.message,
|
||||
Entities: domainMessageEntities(p.entities),
|
||||
Media: p.media,
|
||||
Silent: p.silent,
|
||||
NoForwards: p.noforwards,
|
||||
ReplyTo: replyTo,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
SenderUserID: userID,
|
||||
RecipientUserID: peer.ID,
|
||||
RandomID: p.randomID,
|
||||
Message: p.message,
|
||||
Entities: domainMessageEntities(p.entities),
|
||||
Media: p.media,
|
||||
Silent: p.silent,
|
||||
NoForwards: p.noforwards,
|
||||
ReplyTo: replyTo,
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
OriginAuthKeyID: authKeyID,
|
||||
OriginSessionID: sessionID,
|
||||
RecipientBlocked: recipientBlocked,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, false, messageSendErr(err)
|
||||
|
|
|
|||
|
|
@ -63,13 +63,32 @@ func (r *Router) onUpdatesGetDifference(ctx context.Context, req *tg.UpdatesGetD
|
|||
return nil, internalErr()
|
||||
}
|
||||
r.markSessionReceivesUpdates(ctx, userID)
|
||||
if len(st.Events) == 0 {
|
||||
st.ChannelNudges = r.accountChannelDifferenceNudges(ctx, userID, req.Date)
|
||||
if len(st.Events) == 0 && len(st.ChannelNudges) == 0 {
|
||||
return &tg.UpdatesDifferenceEmpty{Date: st.State.Date, Seq: st.State.Seq}, nil
|
||||
}
|
||||
st.Events = r.enrichUpdateEvents(ctx, userID, st.Events)
|
||||
return tgUpdatesDifference(st), nil
|
||||
}
|
||||
|
||||
func (r *Router) accountChannelDifferenceNudges(ctx context.Context, userID int64, sinceDate int) []domain.ChannelDifferenceNudge {
|
||||
if r.deps.Channels == nil || userID == 0 || sinceDate <= 0 {
|
||||
return nil
|
||||
}
|
||||
dirty, err := r.deps.Channels.DirtyActiveChannelsForUser(ctx, userID, sinceDate, 0, domain.MaxChannelDifferenceLimit)
|
||||
if err != nil || len(dirty) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]domain.ChannelDifferenceNudge, 0, len(dirty))
|
||||
for _, item := range dirty {
|
||||
if item.ChannelID == 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, domain.ChannelDifferenceNudge{ChannelID: item.ChannelID, Pts: item.Pts})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (r *Router) markSessionReceivesUpdates(ctx context.Context, userID int64) {
|
||||
if r.deps.Sessions == nil {
|
||||
return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue