fix: align private and channel update semantics

(cherry picked from commit c65f76f56278f74082c4fa792ed49104d5d33c38)
This commit is contained in:
A 2026-06-07 22:22:23 +08:00
parent dce7b92772
commit d84fa6e126
36 changed files with 1765 additions and 382 deletions

View file

@ -6570,6 +6570,9 @@ LIMIT $`+fmt.Sprint(len(args)), args...)
if err := rows.Err(); err != nil {
return domain.ChannelDifference{}, err
}
if err := populateChannelMessageUnreadFlags(ctx, s.db, req.UserID, diff.NewMessages); err != nil {
return domain.ChannelDifference{}, err
}
if preview {
diff.Dialog = previewChannelDialog(req.UserID, channel, member)
} else {
@ -6634,6 +6637,19 @@ LIMIT $3`, req.ChannelID, req.Pts, limit)
} else if lastPts > diff.Pts {
diff.Pts = lastPts
}
if err := populateChannelMessageUnreadFlags(ctx, s.db, req.UserID, diff.NewMessages); err != nil {
return domain.ChannelDifference{}, err
}
for i := range diff.OtherUpdates {
if diff.OtherUpdates[i].Message.ID == 0 {
continue
}
messages := []domain.ChannelMessage{diff.OtherUpdates[i].Message}
if err := populateChannelMessageUnreadFlags(ctx, s.db, req.UserID, messages); err != nil {
return domain.ChannelDifference{}, err
}
diff.OtherUpdates[i].Message = messages[0]
}
users, err := listUsersByIDs(ctx, s.db, mapKeysInt64(userRefs))
if err != nil {
return domain.ChannelDifference{}, err
@ -6687,6 +6703,45 @@ LIMIT $3`, userID, afterChannelID, limit)
return out, rows.Err()
}
func (s *ChannelStore) ListDirtyActiveChannelsForUser(ctx context.Context, userID int64, sinceDate int, afterChannelID int64, limit int) ([]domain.DirtyChannel, error) {
if userID == 0 || sinceDate <= 0 || afterChannelID < 0 {
return nil, domain.ErrChannelInvalid
}
if limit <= 0 || limit > domain.MaxChannelDifferenceLimit {
limit = domain.MaxChannelDifferenceLimit
}
rows, err := s.db.Query(ctx, `
SELECT i.channel_id, c.pts
FROM user_channel_member_index i
JOIN channels c ON c.id = i.channel_id AND NOT c.deleted
WHERE i.user_id = $1
AND i.status = 'active'
AND NOT i.deleted
AND i.channel_id > $3
AND EXISTS (
SELECT 1
FROM channel_update_events e
WHERE e.channel_id = i.channel_id
AND e.date > $2
LIMIT 1
)
ORDER BY i.channel_id ASC
LIMIT $4`, userID, sinceDate, afterChannelID, limit)
if err != nil {
return nil, fmt.Errorf("list dirty active channels for user: %w", err)
}
defer rows.Close()
out := make([]domain.DirtyChannel, 0, limit)
for rows.Next() {
var item domain.DirtyChannel
if err := rows.Scan(&item.ChannelID, &item.Pts); err != nil {
return nil, err
}
out = append(out, item)
}
return out, rows.Err()
}
func (s *ChannelStore) ListActiveChannelMemberIDs(ctx context.Context, viewerUserID, channelID int64, limit int) ([]int64, error) {
if _, _, err := s.getChannelForMember(ctx, s.db, viewerUserID, channelID); err != nil {
return nil, err
@ -7119,6 +7174,9 @@ func (s *ChannelStore) populateChannelMessagesReactions(ctx context.Context, db
if len(messages) == 0 {
return nil
}
if err := populateChannelMessageUnreadFlags(ctx, db, viewerUserID, messages); err != nil {
return err
}
channelsByID := make(map[int64]domain.Channel, len(channels))
for _, ch := range channels {
if ch.ID != 0 {
@ -7230,6 +7288,54 @@ ORDER BY message_id ASC, reaction_date DESC, reacted_user_id DESC, reaction_valu
return nil
}
func populateChannelMessageUnreadFlags(ctx context.Context, db sqlcgen.DBTX, viewerUserID int64, messages []domain.ChannelMessage) error {
if viewerUserID == 0 || len(messages) == 0 {
return nil
}
indexes := make(map[channelReactionMessageKey][]int)
idsByChannel := make(map[int64][]int32)
for i := range messages {
if messages[i].ChannelID == 0 || messages[i].ID <= 0 {
continue
}
key := channelReactionMessageKey{channelID: messages[i].ChannelID, messageID: messages[i].ID}
if _, ok := indexes[key]; !ok {
idsByChannel[messages[i].ChannelID] = append(idsByChannel[messages[i].ChannelID], int32(messages[i].ID))
}
indexes[key] = append(indexes[key], i)
}
for channelID, ids := range idsByChannel {
rows, err := db.Query(ctx, `
SELECT message_id, COALESCE(media_unread, false)
FROM channel_unread_mentions
WHERE user_id = $1
AND channel_id = $2
AND message_id = ANY($3::int[])`, viewerUserID, channelID, ids)
if err != nil {
return fmt.Errorf("load channel message unread flags: %w", err)
}
for rows.Next() {
var messageID int
var mediaUnread bool
if err := rows.Scan(&messageID, &mediaUnread); err != nil {
rows.Close()
return err
}
key := channelReactionMessageKey{channelID: channelID, messageID: messageID}
for _, idx := range indexes[key] {
messages[idx].Mentioned = true
messages[idx].MediaUnread = mediaUnread
}
}
if err := rows.Err(); err != nil {
rows.Close()
return err
}
rows.Close()
}
return nil
}
func channelReactionOffset(row domain.ChannelMessagePeerReaction) string {
return strconv.Itoa(row.Date) + ":" + strconv.FormatInt(row.UserID, 10) + ":" + row.Reaction.Emoticon
}
@ -8611,6 +8717,7 @@ func insertChannelUnreadMentionsTx(ctx context.Context, tx pgx.Tx, channelID int
candidates = candidates[:domain.MaxChannelMentionRecipients]
}
topID := channelMentionTopID(msg)
mediaUnread := !msg.Media.IsZero()
if _, err := tx.Exec(ctx, `
WITH input(user_id) AS (
SELECT DISTINCT unnest($4::bigint[])
@ -8626,8 +8733,8 @@ active AS (
LIMIT $6
),
inserted AS (
INSERT INTO channel_unread_mentions (user_id, channel_id, message_id, top_message_id)
SELECT user_id, $1, $2, $3
INSERT INTO channel_unread_mentions (user_id, channel_id, message_id, top_message_id, media_unread)
SELECT user_id, $1, $2, $3, $7
FROM active
ON CONFLICT DO NOTHING
RETURNING user_id
@ -8641,7 +8748,7 @@ ON CONFLICT (user_id, channel_id) DO UPDATE SET
top_message_id = GREATEST(channel_dialogs.top_message_id, EXCLUDED.top_message_id),
top_message_date = GREATEST(channel_dialogs.top_message_date, EXCLUDED.top_message_date),
unread_mentions_count = channel_dialogs.unread_mentions_count + 1,
updated_at = now()`, channelID, msg.ID, topID, candidates, msg.Date, domain.MaxChannelMentionRecipients); err != nil {
updated_at = now()`, channelID, msg.ID, topID, candidates, msg.Date, domain.MaxChannelMentionRecipients, mediaUnread); err != nil {
return fmt.Errorf("insert channel unread mentions: %w", err)
}
return nil

View file

@ -366,6 +366,118 @@ func contactFromFields(id, accessHash int64, phone, firstName, lastName, usernam
}
}
func (s *ContactStore) Block(ctx context.Context, userID, blockedUserID int64, date int) (bool, error) {
if userID == 0 || blockedUserID == 0 || userID == blockedUserID {
return false, nil
}
tag, err := s.db.Exec(ctx, `
INSERT INTO contact_blocks (owner_user_id, blocked_user_id, date)
VALUES ($1, $2, $3)
ON CONFLICT (owner_user_id, blocked_user_id) DO UPDATE SET
date = EXCLUDED.date,
created_at = contact_blocks.created_at`, userID, blockedUserID, date)
if err != nil {
return false, fmt.Errorf("block contact: %w", err)
}
return tag.RowsAffected() > 0, nil
}
func (s *ContactStore) Unblock(ctx context.Context, userID, blockedUserID int64) (bool, error) {
if userID == 0 || blockedUserID == 0 {
return false, nil
}
tag, err := s.db.Exec(ctx, `
DELETE FROM contact_blocks
WHERE owner_user_id = $1
AND blocked_user_id = $2`, userID, blockedUserID)
if err != nil {
return false, fmt.Errorf("unblock contact: %w", err)
}
return tag.RowsAffected() > 0, nil
}
func (s *ContactStore) IsBlocked(ctx context.Context, userID, blockedUserID int64) (bool, error) {
if userID == 0 || blockedUserID == 0 {
return false, nil
}
var blocked bool
if err := s.db.QueryRow(ctx, `
SELECT EXISTS (
SELECT 1
FROM contact_blocks
WHERE owner_user_id = $1
AND blocked_user_id = $2
)`, userID, blockedUserID).Scan(&blocked); err != nil {
return false, fmt.Errorf("check contact block: %w", err)
}
return blocked, nil
}
func (s *ContactStore) ListBlocked(ctx context.Context, userID int64, offset, limit int) (domain.BlockedContactList, error) {
if userID == 0 {
return domain.BlockedContactList{}, nil
}
if offset < 0 {
offset = 0
}
if limit <= 0 || limit > 100 {
limit = 100
}
var count int
if err := s.db.QueryRow(ctx, `
SELECT COUNT(*)::int
FROM contact_blocks
WHERE owner_user_id = $1`, userID).Scan(&count); err != nil {
return domain.BlockedContactList{}, fmt.Errorf("count blocked contacts: %w", err)
}
rows, err := s.db.Query(ctx, `
SELECT
b.blocked_user_id,
b.date,
u.access_hash,
u.phone,
u.first_name,
u.last_name,
u.username,
u.country_code,
u.verified,
u.support,
u.last_seen_at
FROM contact_blocks b
JOIN users u ON u.id = b.blocked_user_id
WHERE b.owner_user_id = $1
ORDER BY b.date DESC, b.blocked_user_id DESC
OFFSET $2
LIMIT $3`, userID, offset, limit)
if err != nil {
return domain.BlockedContactList{}, fmt.Errorf("list blocked contacts: %w", err)
}
defer rows.Close()
out := domain.BlockedContactList{Count: count, Blocked: make([]domain.BlockedContact, 0, limit)}
for rows.Next() {
var item domain.BlockedContact
var lastSeen int64
if err := rows.Scan(
&item.User.ID,
&item.Date,
&item.User.AccessHash,
&item.User.Phone,
&item.User.FirstName,
&item.User.LastName,
&item.User.Username,
&item.User.CountryCode,
&item.User.Verified,
&item.User.Support,
&lastSeen,
); err != nil {
return domain.BlockedContactList{}, err
}
item.User.LastSeenAt = int(lastSeen)
out.Blocked = append(out.Blocked, item)
}
return out, rows.Err()
}
func contactListHash(contacts []domain.Contact) int64 {
if len(contacts) == 0 {
return 0

View file

@ -175,7 +175,8 @@ func (s *MessageStore) SendPrivateText(ctx context.Context, req domain.SendPriva
var recipientBoxID, recipientPts int
selfMessage := req.RecipientUserID == req.SenderUserID
if !selfMessage {
deliverRecipient := !selfMessage && !req.RecipientBlocked
if deliverRecipient {
recipientBoxID, err = s.boxIDs.NextBoxID(ctx, req.RecipientUserID)
if err != nil {
s.recordPtsGaps(ctx, reserved, req.Date)
@ -249,6 +250,8 @@ func (s *MessageStore) SendPrivateText(ctx context.Context, req domain.SendPriva
EntitiesJson: entities,
Pts: int32(senderPts),
MediaJson: mediaJSON,
MediaUnread: false,
ReactionUnread: false,
}
applyCreateMessageBoxMetadata(&senderArg, senderMeta)
senderRow, err := qtx.CreateMessageBox(ctx, senderArg)
@ -278,8 +281,11 @@ func (s *MessageStore) SendPrivateText(ctx context.Context, req domain.SendPriva
return domain.SendPrivateTextResult{}, fmt.Errorf("enqueue sender dispatch: %w", err)
}
recipient := sender
if !selfMessage {
recipient := domain.Message{}
if selfMessage {
recipient = sender
}
if deliverRecipient {
recipientArg := sqlcgen.CreateMessageBoxParams{
OwnerUserID: req.RecipientUserID,
BoxID: int32(recipientBoxID),
@ -294,6 +300,8 @@ func (s *MessageStore) SendPrivateText(ctx context.Context, req domain.SendPriva
EntitiesJson: entities,
Pts: int32(recipientPts),
MediaJson: mediaJSON,
MediaUnread: !req.Media.IsZero(),
ReactionUnread: false,
}
applyCreateMessageBoxMetadata(&recipientArg, recipientMeta)
recipientRow, err := qtx.CreateMessageBox(ctx, recipientArg)
@ -352,13 +360,23 @@ func (s *MessageStore) duplicateSendResult(ctx context.Context, senderUserID, re
return domain.SendPrivateTextResult{}, fmt.Errorf("get duplicate sender box: %w", err)
}
sender := messageFromGetBoxRow(senderRow)
recipient := sender
recipient := domain.Message{}
if recipientUserID == senderUserID {
recipient = sender
}
if recipientUserID != senderUserID {
recipientRow, err := s.q.GetMessageBoxByPrivateMessage(ctx, sqlcgen.GetMessageBoxByPrivateMessageParams{
OwnerUserID: recipientUserID,
PrivateMessageID: pm.ID,
})
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
return domain.SendPrivateTextResult{
SenderMessage: sender,
SenderEvent: eventFromMessage(sender),
RecipientEvent: domain.UpdateEvent{},
}, nil
}
return domain.SendPrivateTextResult{}, fmt.Errorf("get duplicate recipient box: %w", err)
}
recipient = messageFromGetBoxRow(recipientRow)
@ -514,19 +532,20 @@ func (s *MessageStore) ForwardPrivateMessages(ctx context.Context, req domain.Fo
}
}
sent, err := s.SendPrivateText(ctx, domain.SendPrivateTextRequest{
SenderUserID: req.OwnerUserID,
RecipientUserID: req.ToUserID,
RandomID: req.RandomIDs[i],
Message: source.Body,
Entities: append([]domain.MessageEntity(nil), source.Entities...),
Media: source.Media,
Silent: req.Silent,
NoForwards: req.NoForwards,
ReplyTo: req.ReplyTo,
Forward: forward,
Date: req.Date,
OriginAuthKeyID: req.OriginAuthKeyID,
OriginSessionID: req.OriginSessionID,
SenderUserID: req.OwnerUserID,
RecipientUserID: req.ToUserID,
RandomID: req.RandomIDs[i],
Message: source.Body,
Entities: append([]domain.MessageEntity(nil), source.Entities...),
Media: source.Media,
Silent: req.Silent,
NoForwards: req.NoForwards,
ReplyTo: req.ReplyTo,
Forward: forward,
Date: req.Date,
OriginAuthKeyID: req.OriginAuthKeyID,
OriginSessionID: req.OriginSessionID,
RecipientBlocked: req.RecipientBlocked,
})
if err != nil {
return res, err
@ -640,22 +659,24 @@ func (s *MessageStore) ListByUser(ctx context.Context, userID int64, filter doma
return domain.MessageList{}, fmt.Errorf("decode message media: %w", err)
}
out.Messages = append(out.Messages, domain.Message{
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
})
if out.Count == 0 {
out.Count = int(row.TotalCount)
@ -832,6 +853,9 @@ func (s *MessageStore) ReadMessageContents(ctx context.Context, req domain.ReadM
if req.OwnerUserID == 0 {
return res, fmt.Errorf("read message contents: missing owner user id")
}
if req.Date == 0 {
req.Date = int(time.Now().Unix())
}
if len(req.IDs) > domain.MaxGetMessageIDs {
return res, domain.ErrMessageIDInvalid
}
@ -850,27 +874,128 @@ func (s *MessageStore) ReadMessageContents(ctx context.Context, req domain.ReadM
if len(ids) == 0 {
return res, nil
}
rows, err := s.db.Query(ctx, `
SELECT box_id
FROM message_boxes
WHERE owner_user_id = $1
AND box_id = ANY($2::int[])
AND NOT deleted
beginner, ok := s.db.(txBeginner)
if !ok {
return res, fmt.Errorf("read message contents: db does not support transactions")
}
tx, err := beginner.Begin(ctx)
if err != nil {
return res, fmt.Errorf("begin read message contents tx: %w", err)
}
qtx := sqlcgen.New(tx)
committed := false
var reserved []reservedPts
defer func() {
if committed {
return
}
_ = tx.Rollback(ctx)
s.recordPtsGaps(ctx, reserved, req.Date)
}()
if err := lockUsersForUpdate(ctx, tx, req.OwnerUserID); err != nil {
return res, fmt.Errorf("lock read message contents user: %w", err)
}
rows, err := tx.Query(ctx, `
WITH target AS (
SELECT owner_user_id, box_id, peer_type, peer_id, reaction_unread
FROM message_boxes
WHERE owner_user_id = $1
AND box_id = ANY($2::int[])
AND NOT deleted
AND (media_unread OR reaction_unread)
FOR UPDATE
),
updated AS (
UPDATE message_boxes
SET media_unread = false,
reaction_unread = false
FROM target t
WHERE message_boxes.owner_user_id = t.owner_user_id
AND message_boxes.box_id = t.box_id
RETURNING message_boxes.box_id, t.peer_type, t.peer_id, t.reaction_unread
)
SELECT box_id, peer_type, peer_id, reaction_unread
FROM updated
ORDER BY box_id`, req.OwnerUserID, ids)
if err != nil {
return res, fmt.Errorf("read message contents: %w", err)
}
defer rows.Close()
affectedPeers := make(map[domain.Peer]struct{})
for rows.Next() {
var id int32
if err := rows.Scan(&id); err != nil {
var peerType string
var peerID int64
var reactionUnread bool
if err := rows.Scan(&id, &peerType, &peerID, &reactionUnread); err != nil {
return res, fmt.Errorf("scan read message contents: %w", err)
}
res.MessageIDs = append(res.MessageIDs, int(id))
if reactionUnread && peerID != 0 {
affectedPeers[domain.Peer{Type: domain.PeerType(peerType), ID: peerID}] = struct{}{}
}
}
if err := rows.Err(); err != nil {
return res, fmt.Errorf("read message contents rows: %w", err)
}
if len(res.MessageIDs) == 0 {
if err := tx.Commit(ctx); err != nil {
return res, fmt.Errorf("commit read message contents noop: %w", err)
}
committed = true
return res, nil
}
for peer := range affectedPeers {
if peer.Type != domain.PeerTypeUser || peer.ID == 0 {
continue
}
if _, err := tx.Exec(ctx, `
UPDATE dialogs d
SET unread_reactions_count = (
SELECT COUNT(*)::int
FROM message_boxes m
WHERE m.owner_user_id = d.user_id
AND m.peer_type = d.peer_type
AND m.peer_id = d.peer_id
AND NOT m.deleted
AND m.reaction_unread
),
updated_at = now()
WHERE d.user_id = $1
AND d.peer_type = $2
AND d.peer_id = $3`, req.OwnerUserID, string(peer.Type), peer.ID); err != nil {
return res, fmt.Errorf("refresh dialog unread reactions after content read: %w", err)
}
}
pts, err := s.nextPtsN(ctx, req.OwnerUserID, len(res.MessageIDs))
if err != nil {
return res, fmt.Errorf("allocate read message contents pts: %w", err)
}
reserved = append(reserved, reservedPts{userID: req.OwnerUserID, pts: pts, count: len(res.MessageIDs)})
res.Event = domain.UpdateEvent{
UserID: req.OwnerUserID,
Type: domain.UpdateEventReadMessageContents,
Pts: pts,
PtsCount: len(res.MessageIDs),
Date: req.Date,
MessageIDs: append([]int(nil), res.MessageIDs...),
}
if err := appendUserUpdateEvent(ctx, qtx, req.OwnerUserID, res.Event); err != nil {
return res, fmt.Errorf("append read message contents event: %w", err)
}
if err := qtx.EnqueueDispatch(ctx, sqlcgen.EnqueueDispatchParams{
TargetUserID: req.OwnerUserID,
Pts: int32(pts),
EventType: string(domain.UpdateEventReadMessageContents),
ExcludeAuthKeyID: authKeyIDToInt64(req.OriginAuthKeyID),
ExcludeSessionID: req.OriginSessionID,
}); err != nil {
return res, fmt.Errorf("enqueue read message contents dispatch: %w", err)
}
if err := tx.Commit(ctx); err != nil {
return res, fmt.Errorf("commit read message contents tx: %w", err)
}
committed = true
return res, nil
}
@ -996,6 +1121,39 @@ DO UPDATE SET
return domain.PrivateMessageReactionsResult{}, fmt.Errorf("insert message reaction: %w", err)
}
}
if target.messageSenderID != 0 && target.messageSenderID != req.UserID {
if _, err := tx.Exec(ctx, `
UPDATE message_boxes b
SET reaction_unread = EXISTS (
SELECT 1
FROM private_message_reactions r
WHERE r.message_sender_id = b.message_sender_id
AND r.private_message_id = b.private_message_id
AND r.user_id <> b.owner_user_id
)
WHERE b.owner_user_id = $1
AND b.message_sender_id = $2
AND b.private_message_id = $3`, target.messageSenderID, target.messageSenderID, target.privateMessageID); err != nil {
return domain.PrivateMessageReactionsResult{}, fmt.Errorf("update private reaction unread: %w", err)
}
if _, err := tx.Exec(ctx, `
UPDATE dialogs d
SET unread_reactions_count = (
SELECT COUNT(*)::int
FROM message_boxes m
WHERE m.owner_user_id = d.user_id
AND m.peer_type = d.peer_type
AND m.peer_id = d.peer_id
AND NOT m.deleted
AND m.reaction_unread
),
updated_at = now()
WHERE d.user_id = $1
AND d.peer_type = $2
AND d.peer_id = $3`, target.messageSenderID, string(domain.PeerTypeUser), req.UserID); err != nil {
return domain.PrivateMessageReactionsResult{}, fmt.Errorf("refresh private reaction unread dialog: %w", err)
}
}
boxes, err := qtx.ListVisibleMessageBoxesByPrivateMessage(ctx, sqlcgen.ListVisibleMessageBoxesByPrivateMessageParams{
MessageSenderID: target.messageSenderID,
@ -1911,22 +2069,24 @@ func messageFromBoxRow(row sqlcgen.CreateMessageBoxRow) domain.Message {
row.FwdDate,
)
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}
}
@ -1949,22 +2109,24 @@ func messageFromGetBoxRow(row sqlcgen.GetMessageBoxByPrivateMessageRow) domain.M
row.FwdDate,
)
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}
}
@ -1996,22 +2158,24 @@ func messageFromVisibleBoxRow(row sqlcgen.ListVisibleMessageBoxesByPrivateMessag
return domain.Message{}, fmt.Errorf("decode visible message media: %w", err)
}
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}, nil
}
@ -2043,22 +2207,24 @@ func messageFromUpdateEditRow(row sqlcgen.UpdateMessageBoxEditRow) (domain.Messa
return domain.Message{}, fmt.Errorf("decode edited message media: %w", err)
}
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}, nil
}
@ -2090,22 +2256,24 @@ func messageFromForwardRow(row sqlcgen.GetMessageBoxesForForwardRow) (domain.Mes
return domain.Message{}, fmt.Errorf("decode forward message media: %w", err)
}
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}, nil
}
@ -2137,22 +2305,24 @@ func messageFromIDRow(row sqlcgen.GetMessageBoxesByIDsRow) (domain.Message, erro
return domain.Message{}, fmt.Errorf("decode message media: %w", err)
}
return domain.Message{
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
Media: media,
ID: int(row.BoxID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Pts: int(row.Pts),
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
}, nil
}

View file

@ -167,7 +167,9 @@ INSERT INTO message_boxes (
fwd_from_name,
fwd_date,
pts,
media
media,
media_unread,
reaction_unread
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, sqlc.arg(entities_json)::jsonb,
sqlc.arg(silent)::boolean,
@ -184,7 +186,9 @@ INSERT INTO message_boxes (
sqlc.arg(fwd_from_name)::text,
sqlc.arg(fwd_date)::int,
sqlc.arg(pts)::int,
sqlc.arg(media_json)::jsonb
sqlc.arg(media_json)::jsonb,
sqlc.arg(media_unread)::boolean,
sqlc.arg(reaction_unread)::boolean
)
RETURNING
box_id,
@ -212,7 +216,9 @@ RETURNING
fwd_from_name,
fwd_date,
pts,
media::text AS media_json;
media::text AS media_json,
media_unread,
reaction_unread;
-- name: GetMessageBoxByPrivateMessage :one
SELECT
@ -241,7 +247,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE owner_user_id = $1
AND private_message_id = $2
@ -295,7 +303,9 @@ SELECT
m.fwd_from_name,
m.fwd_date,
m.pts,
m.media::text AS media_json
m.media::text AS media_json,
m.media_unread,
m.reaction_unread
FROM requested r
JOIN message_boxes m
ON m.owner_user_id = sqlc.arg(owner_user_id)::bigint
@ -351,6 +361,8 @@ base AS NOT MATERIALIZED (
m.fwd_date,
m.pts,
m.media::text AS media_json,
m.media_unread,
m.reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -483,6 +495,8 @@ SELECT
fwd_date,
pts,
media_json,
media_unread,
reaction_unread,
peer_user_id,
peer_access_hash,
peer_phone,
@ -537,6 +551,8 @@ SELECT
m.fwd_date,
m.pts,
m.media::text AS media_json,
m.media_unread,
m.reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -594,7 +610,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE owner_user_id = sqlc.arg(owner_user_id)::bigint
AND box_id = sqlc.arg(box_id)::int
@ -632,7 +650,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE message_sender_id = sqlc.arg(message_sender_id)::bigint
AND private_message_id = sqlc.arg(private_message_id)::bigint
@ -684,7 +704,9 @@ RETURNING
fwd_from_name,
fwd_date,
pts,
media::text AS media_json;
media::text AS media_json,
media_unread,
reaction_unread;
-- name: GetDialogReadStateForUpdate :one
SELECT

View file

@ -87,6 +87,8 @@ SELECT
COALESCE(m.fwd_from_name, '')::text AS fwd_from_name,
COALESCE(m.fwd_date, 0)::int AS fwd_date,
COALESCE(m.media::text, '{}')::text AS media_json,
COALESCE(m.media_unread, false)::boolean AS media_unread,
COALESCE(m.reaction_unread, false)::boolean AS reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -341,6 +343,8 @@ SELECT
COALESCE(m.fwd_from_name, '')::text AS fwd_from_name,
COALESCE(m.fwd_date, 0)::int AS fwd_date,
COALESCE(m.media::text, '{}')::text AS media_json,
COALESCE(m.media_unread, false)::boolean AS media_unread,
COALESCE(m.reaction_unread, false)::boolean AS reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,

View file

@ -173,7 +173,9 @@ INSERT INTO message_boxes (
fwd_from_name,
fwd_date,
pts,
media
media,
media_unread,
reaction_unread
) VALUES (
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11::jsonb,
$12::boolean,
@ -190,7 +192,9 @@ INSERT INTO message_boxes (
$23::text,
$24::int,
$25::int,
$26::jsonb
$26::jsonb,
$27::boolean,
$28::boolean
)
RETURNING
box_id,
@ -218,7 +222,9 @@ RETURNING
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
`
type CreateMessageBoxParams struct {
@ -248,6 +254,8 @@ type CreateMessageBoxParams struct {
FwdDate int32
Pts int32
MediaJson []byte
MediaUnread bool
ReactionUnread bool
}
type CreateMessageBoxRow struct {
@ -277,6 +285,8 @@ type CreateMessageBoxRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) CreateMessageBox(ctx context.Context, arg CreateMessageBoxParams) (CreateMessageBoxRow, error) {
@ -307,6 +317,8 @@ func (q *Queries) CreateMessageBox(ctx context.Context, arg CreateMessageBoxPara
arg.FwdDate,
arg.Pts,
arg.MediaJson,
arg.MediaUnread,
arg.ReactionUnread,
)
var i CreateMessageBoxRow
err := row.Scan(
@ -336,6 +348,8 @@ func (q *Queries) CreateMessageBox(ctx context.Context, arg CreateMessageBoxPara
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
)
return i, err
}
@ -845,7 +859,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE owner_user_id = $1
AND private_message_id = $2
@ -884,6 +900,8 @@ type GetMessageBoxByPrivateMessageRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) GetMessageBoxByPrivateMessage(ctx context.Context, arg GetMessageBoxByPrivateMessageParams) (GetMessageBoxByPrivateMessageRow, error) {
@ -916,6 +934,8 @@ func (q *Queries) GetMessageBoxByPrivateMessage(ctx context.Context, arg GetMess
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
)
return i, err
}
@ -948,7 +968,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE owner_user_id = $1::bigint
AND box_id = $2::int
@ -994,6 +1016,8 @@ type GetMessageBoxForEditRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) GetMessageBoxForEdit(ctx context.Context, arg GetMessageBoxForEditParams) (GetMessageBoxForEditRow, error) {
@ -1032,6 +1056,8 @@ func (q *Queries) GetMessageBoxForEdit(ctx context.Context, arg GetMessageBoxFor
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
)
return i, err
}
@ -1104,6 +1130,8 @@ SELECT
m.fwd_date,
m.pts,
m.media::text AS media_json,
m.media_unread,
m.reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -1167,6 +1195,8 @@ type GetMessageBoxesByIDsRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
PeerUserID int64
PeerAccessHash int64
PeerPhone string
@ -1226,6 +1256,8 @@ func (q *Queries) GetMessageBoxesByIDs(ctx context.Context, arg GetMessageBoxesB
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
&i.PeerUserID,
&i.PeerAccessHash,
&i.PeerPhone,
@ -1292,7 +1324,9 @@ SELECT
m.fwd_from_name,
m.fwd_date,
m.pts,
m.media::text AS media_json
m.media::text AS media_json,
m.media_unread,
m.reaction_unread
FROM requested r
JOIN message_boxes m
ON m.owner_user_id = $1::bigint
@ -1339,6 +1373,8 @@ type GetMessageBoxesForForwardRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) GetMessageBoxesForForward(ctx context.Context, arg GetMessageBoxesForForwardParams) ([]GetMessageBoxesForForwardRow, error) {
@ -1384,6 +1420,8 @@ func (q *Queries) GetMessageBoxesForForward(ctx context.Context, arg GetMessageB
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
); err != nil {
return nil, err
}
@ -1633,6 +1671,8 @@ base AS NOT MATERIALIZED (
m.fwd_date,
m.pts,
m.media::text AS media_json,
m.media_unread,
m.reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -1675,7 +1715,7 @@ total AS (
WHERE $12::boolean
),
backward AS (
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.media_unread, b.reaction_unread, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
FROM base b
CROSS JOIN load_params p
WHERE p.load_type = 'backward'
@ -1688,9 +1728,9 @@ backward AS (
LIMIT (SELECT limit_count FROM load_params)
),
around_forward AS (
SELECT f.box_id, f.private_message_id, f.owner_user_id, f.peer_type, f.peer_id, f.from_user_id, f.message_date, f.edit_date, f.outgoing, f.body, f.entities_json, f.silent, f.noforwards, f.reply_to_msg_id, f.reply_to_peer_type, f.reply_to_peer_id, f.reply_to_top_id, f.quote_text, f.quote_entities_json, f.quote_offset, f.fwd_from_peer_type, f.fwd_from_peer_id, f.fwd_from_name, f.fwd_date, f.pts, f.media_json, f.peer_user_id, f.peer_access_hash, f.peer_phone, f.peer_first_name, f.peer_last_name, f.peer_username, f.peer_country_code, f.peer_verified, f.peer_support, f.peer_last_seen_at, f.from_user_user_id, f.from_user_access_hash, f.from_user_phone, f.from_user_first_name, f.from_user_last_name, f.from_user_username, f.from_user_country_code, f.from_user_verified, f.from_user_support, f.from_user_last_seen_at
SELECT f.box_id, f.private_message_id, f.owner_user_id, f.peer_type, f.peer_id, f.from_user_id, f.message_date, f.edit_date, f.outgoing, f.body, f.entities_json, f.silent, f.noforwards, f.reply_to_msg_id, f.reply_to_peer_type, f.reply_to_peer_id, f.reply_to_top_id, f.quote_text, f.quote_entities_json, f.quote_offset, f.fwd_from_peer_type, f.fwd_from_peer_id, f.fwd_from_name, f.fwd_date, f.pts, f.media_json, f.media_unread, f.reaction_unread, f.peer_user_id, f.peer_access_hash, f.peer_phone, f.peer_first_name, f.peer_last_name, f.peer_username, f.peer_country_code, f.peer_verified, f.peer_support, f.peer_last_seen_at, f.from_user_user_id, f.from_user_access_hash, f.from_user_phone, f.from_user_first_name, f.from_user_last_name, f.from_user_username, f.from_user_country_code, f.from_user_verified, f.from_user_support, f.from_user_last_seen_at
FROM (
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.media_unread, b.reaction_unread, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
FROM base b
CROSS JOIN load_params p
WHERE p.load_type = 'around'
@ -1703,7 +1743,7 @@ around_forward AS (
) f
),
around_backward AS (
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.media_unread, b.reaction_unread, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
FROM base b
CROSS JOIN load_params p
WHERE p.load_type = 'around'
@ -1715,9 +1755,9 @@ around_backward AS (
LIMIT GREATEST((SELECT limit_count + add_offset FROM load_params), 0)
),
forward AS (
SELECT f.box_id, f.private_message_id, f.owner_user_id, f.peer_type, f.peer_id, f.from_user_id, f.message_date, f.edit_date, f.outgoing, f.body, f.entities_json, f.silent, f.noforwards, f.reply_to_msg_id, f.reply_to_peer_type, f.reply_to_peer_id, f.reply_to_top_id, f.quote_text, f.quote_entities_json, f.quote_offset, f.fwd_from_peer_type, f.fwd_from_peer_id, f.fwd_from_name, f.fwd_date, f.pts, f.media_json, f.peer_user_id, f.peer_access_hash, f.peer_phone, f.peer_first_name, f.peer_last_name, f.peer_username, f.peer_country_code, f.peer_verified, f.peer_support, f.peer_last_seen_at, f.from_user_user_id, f.from_user_access_hash, f.from_user_phone, f.from_user_first_name, f.from_user_last_name, f.from_user_username, f.from_user_country_code, f.from_user_verified, f.from_user_support, f.from_user_last_seen_at
SELECT f.box_id, f.private_message_id, f.owner_user_id, f.peer_type, f.peer_id, f.from_user_id, f.message_date, f.edit_date, f.outgoing, f.body, f.entities_json, f.silent, f.noforwards, f.reply_to_msg_id, f.reply_to_peer_type, f.reply_to_peer_id, f.reply_to_top_id, f.quote_text, f.quote_entities_json, f.quote_offset, f.fwd_from_peer_type, f.fwd_from_peer_id, f.fwd_from_name, f.fwd_date, f.pts, f.media_json, f.media_unread, f.reaction_unread, f.peer_user_id, f.peer_access_hash, f.peer_phone, f.peer_first_name, f.peer_last_name, f.peer_username, f.peer_country_code, f.peer_verified, f.peer_support, f.peer_last_seen_at, f.from_user_user_id, f.from_user_access_hash, f.from_user_phone, f.from_user_first_name, f.from_user_last_name, f.from_user_username, f.from_user_country_code, f.from_user_verified, f.from_user_support, f.from_user_last_seen_at
FROM (
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
SELECT b.box_id, b.private_message_id, b.owner_user_id, b.peer_type, b.peer_id, b.from_user_id, b.message_date, b.edit_date, b.outgoing, b.body, b.entities_json, b.silent, b.noforwards, b.reply_to_msg_id, b.reply_to_peer_type, b.reply_to_peer_id, b.reply_to_top_id, b.quote_text, b.quote_entities_json, b.quote_offset, b.fwd_from_peer_type, b.fwd_from_peer_id, b.fwd_from_name, b.fwd_date, b.pts, b.media_json, b.media_unread, b.reaction_unread, b.peer_user_id, b.peer_access_hash, b.peer_phone, b.peer_first_name, b.peer_last_name, b.peer_username, b.peer_country_code, b.peer_verified, b.peer_support, b.peer_last_seen_at, b.from_user_user_id, b.from_user_access_hash, b.from_user_phone, b.from_user_first_name, b.from_user_last_name, b.from_user_username, b.from_user_country_code, b.from_user_verified, b.from_user_support, b.from_user_last_seen_at
FROM base b
CROSS JOIN load_params p
WHERE p.load_type = 'forward'
@ -1730,13 +1770,13 @@ forward AS (
) f
),
paged AS (
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM backward
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, media_unread, reaction_unread, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM backward
UNION ALL
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM around_forward
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, media_unread, reaction_unread, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM around_forward
UNION ALL
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM around_backward
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, media_unread, reaction_unread, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM around_backward
UNION ALL
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM forward
SELECT box_id, private_message_id, owner_user_id, peer_type, peer_id, from_user_id, message_date, edit_date, outgoing, body, entities_json, silent, noforwards, reply_to_msg_id, reply_to_peer_type, reply_to_peer_id, reply_to_top_id, quote_text, quote_entities_json, quote_offset, fwd_from_peer_type, fwd_from_peer_id, fwd_from_name, fwd_date, pts, media_json, media_unread, reaction_unread, peer_user_id, peer_access_hash, peer_phone, peer_first_name, peer_last_name, peer_username, peer_country_code, peer_verified, peer_support, peer_last_seen_at, from_user_user_id, from_user_access_hash, from_user_phone, from_user_first_name, from_user_last_name, from_user_username, from_user_country_code, from_user_verified, from_user_support, from_user_last_seen_at FROM forward
)
SELECT
box_id,
@ -1765,6 +1805,8 @@ SELECT
fwd_date,
pts,
media_json,
media_unread,
reaction_unread,
peer_user_id,
peer_access_hash,
peer_phone,
@ -1833,6 +1875,8 @@ type ListMessagesByUserRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
PeerUserID int64
PeerAccessHash int64
PeerPhone string
@ -1905,6 +1949,8 @@ func (q *Queries) ListMessagesByUser(ctx context.Context, arg ListMessagesByUser
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
&i.PeerUserID,
&i.PeerAccessHash,
&i.PeerPhone,
@ -1965,7 +2011,9 @@ SELECT
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
FROM message_boxes
WHERE message_sender_id = $1::bigint
AND private_message_id = $2::bigint
@ -2007,6 +2055,8 @@ type ListVisibleMessageBoxesByPrivateMessageRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) ListVisibleMessageBoxesByPrivateMessage(ctx context.Context, arg ListVisibleMessageBoxesByPrivateMessageParams) ([]ListVisibleMessageBoxesByPrivateMessageRow, error) {
@ -2046,6 +2096,8 @@ func (q *Queries) ListVisibleMessageBoxesByPrivateMessage(ctx context.Context, a
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
); err != nil {
return nil, err
}
@ -2218,7 +2270,9 @@ RETURNING
fwd_from_name,
fwd_date,
pts,
media::text AS media_json
media::text AS media_json,
media_unread,
reaction_unread
`
type UpdateMessageBoxEditParams struct {
@ -2258,6 +2312,8 @@ type UpdateMessageBoxEditRow struct {
FwdDate int32
Pts int32
MediaJson string
MediaUnread bool
ReactionUnread bool
}
func (q *Queries) UpdateMessageBoxEdit(ctx context.Context, arg UpdateMessageBoxEditParams) (UpdateMessageBoxEditRow, error) {
@ -2298,6 +2354,8 @@ func (q *Queries) UpdateMessageBoxEdit(ctx context.Context, arg UpdateMessageBox
&i.FwdDate,
&i.Pts,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
)
return i, err
}

View file

@ -320,6 +320,7 @@ type ChannelUnreadMention struct {
MessageID int32
TopMessageID int32
CreatedAt pgtype.Timestamptz
MediaUnread bool
}
type ChannelUpdateEvent struct {
@ -357,6 +358,13 @@ type Contact struct {
StoriesHidden bool
}
type ContactBlock struct {
OwnerUserID int64
BlockedUserID int64
Date int32
CreatedAt pgtype.Timestamptz
}
type Country struct {
Iso2 string
DefaultName string
@ -514,6 +522,8 @@ type MessageBox struct {
FwdFromName string
FwdDate int32
Media []byte
MediaUnread bool
ReactionUnread bool
}
type Photo struct {

View file

@ -146,6 +146,8 @@ SELECT
COALESCE(m.fwd_from_name, '')::text AS fwd_from_name,
COALESCE(m.fwd_date, 0)::int AS fwd_date,
COALESCE(m.media::text, '{}')::text AS media_json,
COALESCE(m.media_unread, false)::boolean AS media_unread,
COALESCE(m.reaction_unread, false)::boolean AS reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -291,6 +293,8 @@ type BatchListDispatchEventsRow struct {
FwdFromName string
FwdDate int32
MediaJson string
MediaUnread bool
ReactionUnread bool
PeerUserID int64
PeerAccessHash int64
PeerPhone string
@ -432,6 +436,8 @@ func (q *Queries) BatchListDispatchEvents(ctx context.Context, arg BatchListDisp
&i.FwdFromName,
&i.FwdDate,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
&i.PeerUserID,
&i.PeerAccessHash,
&i.PeerPhone,
@ -737,6 +743,8 @@ SELECT
COALESCE(m.fwd_from_name, '')::text AS fwd_from_name,
COALESCE(m.fwd_date, 0)::int AS fwd_date,
COALESCE(m.media::text, '{}')::text AS media_json,
COALESCE(m.media_unread, false)::boolean AS media_unread,
COALESCE(m.reaction_unread, false)::boolean AS reaction_unread,
COALESCE(peer_u.id, 0)::bigint AS peer_user_id,
COALESCE(peer_u.access_hash, 0)::bigint AS peer_access_hash,
COALESCE(peer_u.phone, '')::text AS peer_phone,
@ -885,6 +893,8 @@ type ListUserUpdateEventsAfterRow struct {
FwdFromName string
FwdDate int32
MediaJson string
MediaUnread bool
ReactionUnread bool
PeerUserID int64
PeerAccessHash int64
PeerPhone string
@ -1024,6 +1034,8 @@ func (q *Queries) ListUserUpdateEventsAfter(ctx context.Context, arg ListUserUpd
&i.FwdFromName,
&i.FwdDate,
&i.MediaJson,
&i.MediaUnread,
&i.ReactionUnread,
&i.PeerUserID,
&i.PeerAccessHash,
&i.PeerPhone,

View file

@ -260,21 +260,23 @@ func (s *UpdateEventStore) ListAfter(ctx context.Context, userID int64, pts, lim
FolderPeers: folderPeers,
TagsEnabled: row.TagsEnabled,
Message: domain.Message{
ID: int(row.MessageID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Media: media,
ID: int(row.MessageID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Media: media,
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
},
Users: usersFromUpdateEventRow(row),
Channels: channelsFromUpdateEventRow(row),
@ -519,21 +521,23 @@ func (s *UpdateEventStore) BatchByCursor(ctx context.Context, cursors []store.Ev
FolderPeers: folderPeers,
TagsEnabled: row.TagsEnabled,
Message: domain.Message{
ID: int(row.MessageID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Media: media,
ID: int(row.MessageID),
UID: row.PrivateMessageID,
OwnerUserID: row.OwnerUserID,
Peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
From: domain.Peer{Type: domain.PeerTypeUser, ID: row.FromUserID},
Date: int(row.MessageDate),
EditDate: int(row.EditDate),
Out: row.Outgoing,
Silent: silent,
NoForwards: noforwards,
Body: row.Body,
Entities: entities,
ReplyTo: reply,
Forward: forward,
Media: media,
MediaUnread: row.MediaUnread,
ReactionUnread: row.ReactionUnread,
},
Users: usersFromBatchDispatchRow(row),
Channels: channelsFromBatchDispatchRow(row),