chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
480
internal/store/postgres/saved_dialog.go
Normal file
480
internal/store/postgres/saved_dialog.go
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
package postgres
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
"telesrv/internal/store/postgres/sqlcgen"
|
||||
)
|
||||
|
||||
// ListSavedDialogs 返回收藏夹子会话分页:首页(offset 无效)且不排除置顶时
|
||||
// 置顶块按 pinned_order 在前,普通块按 top box id 严格降序;翻页恒排除置顶
|
||||
// (置顶只随首页返回,避免跨页重复)。Count 按过滤口径(含/不含置顶)。
|
||||
func (s *MessageStore) ListSavedDialogs(ctx context.Context, userID int64, filter domain.SavedDialogsFilter) (domain.SavedDialogList, error) {
|
||||
out := domain.SavedDialogList{}
|
||||
if userID == 0 {
|
||||
return out, fmt.Errorf("list saved dialogs: missing user id")
|
||||
}
|
||||
limit := filter.Limit
|
||||
if limit <= 0 || limit > domain.MaxSavedDialogsLimit {
|
||||
limit = domain.MaxSavedDialogsLimit
|
||||
}
|
||||
// TDesktop 首页 offset_id=0,DrKLO Android 首页 offset_id=int32 max;
|
||||
// 两者都视为"从最新开始"。
|
||||
offsetID := filter.OffsetID
|
||||
// DrKLO Android 首页恰好发 offset_id = MaxMessageBoxID(int32 max),必须用 >=
|
||||
// 才能命中首页分支;否则该值被误判为续页,置顶子会话块被整体跳过、首页丢置顶。
|
||||
firstPage := offsetID <= 0 || offsetID >= domain.MaxMessageBoxID
|
||||
if firstPage {
|
||||
offsetID = 0
|
||||
}
|
||||
if firstPage && !filter.ExcludePinned {
|
||||
rows, err := s.q.ListPinnedSavedDialogTops(ctx, userID)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("list pinned saved dialog tops: %w", err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
if len(out.Dialogs) >= limit {
|
||||
break
|
||||
}
|
||||
msg, err := messageFromSavedDialogRow(savedDialogRowFields(row))
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Dialogs = append(out.Dialogs, domain.SavedDialog{
|
||||
Peer: domain.Peer{Type: domain.PeerType(row.DialogPeerType), ID: row.DialogPeerID},
|
||||
TopMessage: msg.ID,
|
||||
Pinned: true,
|
||||
})
|
||||
out.Messages = append(out.Messages, msg)
|
||||
}
|
||||
}
|
||||
remaining := limit - len(out.Dialogs)
|
||||
hasMore := false
|
||||
if remaining > 0 {
|
||||
rows, err := s.q.ListSavedDialogTops(ctx, sqlcgen.ListSavedDialogTopsParams{
|
||||
OwnerUserID: userID,
|
||||
OffsetID: pgInt32NonNegative(offsetID),
|
||||
// 普通块恒排除置顶:置顶要么已随首页置顶块返回,要么被
|
||||
// exclude_pinned 显式排除,任何一页都不允许再出现。
|
||||
ExcludePinned: true,
|
||||
LimitCount: int32(remaining + 1),
|
||||
})
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("list saved dialog tops: %w", err)
|
||||
}
|
||||
if len(rows) > remaining {
|
||||
hasMore = true
|
||||
rows = rows[:remaining]
|
||||
}
|
||||
for _, row := range rows {
|
||||
msg, err := messageFromSavedDialogRow(savedDialogRowFields(row))
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Dialogs = append(out.Dialogs, domain.SavedDialog{
|
||||
Peer: domain.Peer{Type: domain.PeerType(row.DialogPeerType), ID: row.DialogPeerID},
|
||||
TopMessage: msg.ID,
|
||||
Pinned: row.DialogPinned,
|
||||
})
|
||||
out.Messages = append(out.Messages, msg)
|
||||
}
|
||||
}
|
||||
total, err := s.q.CountSavedDialogs(ctx, sqlcgen.CountSavedDialogsParams{
|
||||
OwnerUserID: userID,
|
||||
ExcludePinned: filter.ExcludePinned,
|
||||
})
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("count saved dialogs: %w", err)
|
||||
}
|
||||
out.Count = int(total)
|
||||
out.Full = !hasMore
|
||||
if err := s.enrichPrivateMessageReactions(ctx, s.db, userID, out.Messages); err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListPinnedSavedDialogs 返回全部置顶子会话(messages.getPinnedSavedDialogs)。
|
||||
func (s *MessageStore) ListPinnedSavedDialogs(ctx context.Context, userID int64) (domain.SavedDialogList, error) {
|
||||
out := domain.SavedDialogList{Full: true}
|
||||
if userID == 0 {
|
||||
return out, fmt.Errorf("list pinned saved dialogs: missing user id")
|
||||
}
|
||||
rows, err := s.q.ListPinnedSavedDialogTops(ctx, userID)
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("list pinned saved dialog tops: %w", err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
msg, err := messageFromSavedDialogRow(savedDialogRowFields(row))
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Dialogs = append(out.Dialogs, domain.SavedDialog{
|
||||
Peer: domain.Peer{Type: domain.PeerType(row.DialogPeerType), ID: row.DialogPeerID},
|
||||
TopMessage: msg.ID,
|
||||
Pinned: true,
|
||||
})
|
||||
out.Messages = append(out.Messages, msg)
|
||||
}
|
||||
out.Count = len(out.Dialogs)
|
||||
if err := s.enrichPrivateMessageReactions(ctx, s.db, userID, out.Messages); err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ListSavedDialogsByPeers 返回指定子会话(messages.getSavedDialogsByID);
|
||||
// 不存在的 peer 静默缺席。
|
||||
func (s *MessageStore) ListSavedDialogsByPeers(ctx context.Context, userID int64, peers []domain.Peer) (domain.SavedDialogList, error) {
|
||||
out := domain.SavedDialogList{Full: true}
|
||||
if userID == 0 {
|
||||
return out, fmt.Errorf("list saved dialogs by peers: missing user id")
|
||||
}
|
||||
peerTypes := make([]string, 0, len(peers))
|
||||
peerIDs := make([]int64, 0, len(peers))
|
||||
for _, peer := range peers {
|
||||
if peer.Type == "" || peer.ID == 0 {
|
||||
continue
|
||||
}
|
||||
peerTypes = append(peerTypes, string(peer.Type))
|
||||
peerIDs = append(peerIDs, peer.ID)
|
||||
}
|
||||
if len(peerTypes) == 0 {
|
||||
return out, nil
|
||||
}
|
||||
rows, err := s.q.ListSavedDialogTopsByPeers(ctx, sqlcgen.ListSavedDialogTopsByPeersParams{
|
||||
OwnerUserID: userID,
|
||||
PeerTypes: peerTypes,
|
||||
PeerIds: peerIDs,
|
||||
})
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("list saved dialog tops by peers: %w", err)
|
||||
}
|
||||
for _, row := range rows {
|
||||
msg, err := messageFromSavedDialogRow(savedDialogRowFields(row))
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.Dialogs = append(out.Dialogs, domain.SavedDialog{
|
||||
Peer: domain.Peer{Type: domain.PeerType(row.DialogPeerType), ID: row.DialogPeerID},
|
||||
TopMessage: msg.ID,
|
||||
Pinned: row.DialogPinned,
|
||||
})
|
||||
out.Messages = append(out.Messages, msg)
|
||||
}
|
||||
out.Count = len(out.Dialogs)
|
||||
if err := s.enrichPrivateMessageReactions(ctx, s.db, userID, out.Messages); err != nil {
|
||||
return out, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ToggleSavedDialogPin 翻转一个子会话的置顶状态;新置顶插到最前。
|
||||
// 返回状态是否实际变化。
|
||||
func (s *MessageStore) ToggleSavedDialogPin(ctx context.Context, userID int64, peer domain.Peer, pinned bool) (bool, error) {
|
||||
if userID == 0 || peer.Type == "" || peer.ID == 0 {
|
||||
return false, fmt.Errorf("toggle saved dialog pin: invalid input")
|
||||
}
|
||||
if !pinned {
|
||||
rows, err := s.q.DeleteSavedDialogPin(ctx, sqlcgen.DeleteSavedDialogPinParams{
|
||||
UserID: userID,
|
||||
PeerType: string(peer.Type),
|
||||
PeerID: peer.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("delete saved dialog pin: %w", err)
|
||||
}
|
||||
return rows > 0, nil
|
||||
}
|
||||
count, err := s.q.CountSavedDialogPins(ctx, userID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("count saved dialog pins: %w", err)
|
||||
}
|
||||
if int(count) >= domain.MaxPinnedSavedDialogs {
|
||||
return false, domain.ErrPinnedSavedDialogsTooMuch
|
||||
}
|
||||
rows, err := s.q.UpsertSavedDialogPinFront(ctx, sqlcgen.UpsertSavedDialogPinFrontParams{
|
||||
UserID: userID,
|
||||
PeerType: string(peer.Type),
|
||||
PeerID: peer.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("upsert saved dialog pin: %w", err)
|
||||
}
|
||||
return rows > 0, nil
|
||||
}
|
||||
|
||||
// ReorderPinnedSavedDialogs 全量重排置顶顺序;force 时清掉不在 order 中的
|
||||
// 既有置顶(与 messages.reorderPinnedDialogs 的 force 语义一致)。
|
||||
func (s *MessageStore) ReorderPinnedSavedDialogs(ctx context.Context, userID int64, order []domain.Peer, force bool) error {
|
||||
if userID == 0 {
|
||||
return fmt.Errorf("reorder pinned saved dialogs: missing user id")
|
||||
}
|
||||
if len(order) > domain.MaxPinnedSavedDialogs {
|
||||
return domain.ErrPinnedSavedDialogsTooMuch
|
||||
}
|
||||
peerTypes := make([]string, 0, len(order))
|
||||
peerIDs := make([]int64, 0, len(order))
|
||||
for _, peer := range order {
|
||||
if peer.Type == "" || peer.ID == 0 {
|
||||
continue
|
||||
}
|
||||
peerTypes = append(peerTypes, string(peer.Type))
|
||||
peerIDs = append(peerIDs, peer.ID)
|
||||
}
|
||||
if force {
|
||||
if err := s.q.ClearSavedDialogPinsNotInOrder(ctx, sqlcgen.ClearSavedDialogPinsNotInOrderParams{
|
||||
UserID: userID,
|
||||
PeerTypes: peerTypes,
|
||||
PeerIds: peerIDs,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("clear saved dialog pins not in order: %w", err)
|
||||
}
|
||||
}
|
||||
if len(peerTypes) == 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.q.ReorderSavedDialogPins(ctx, sqlcgen.ReorderSavedDialogPinsParams{
|
||||
UserID: userID,
|
||||
PeerTypes: peerTypes,
|
||||
PeerIds: peerIDs,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("reorder saved dialog pins: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSavedHistory 删除 self-chat 中一个 saved 子会话的消息(单批,
|
||||
// 上限 MaxDeleteHistoryBatch),生成带 pts 的 delete_messages durable 事件,
|
||||
// 修复 self dialog top;子会话删空时顺带清掉它的置顶行。
|
||||
func (s *MessageStore) DeleteSavedHistory(ctx context.Context, req domain.DeleteSavedHistoryRequest) (domain.DeleteSavedHistoryResult, error) {
|
||||
res := domain.DeleteSavedHistoryResult{}
|
||||
if req.OwnerUserID == 0 || req.SavedPeer.Type == "" || req.SavedPeer.ID == 0 {
|
||||
return res, fmt.Errorf("delete saved history: invalid input")
|
||||
}
|
||||
if req.Date == 0 {
|
||||
req.Date = int(time.Now().Unix())
|
||||
}
|
||||
beginner, ok := s.db.(txBeginner)
|
||||
if !ok {
|
||||
return res, fmt.Errorf("delete saved history: db does not support transactions")
|
||||
}
|
||||
tx, err := beginner.Begin(ctx)
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("begin delete saved history tx: %w", err)
|
||||
}
|
||||
qtx := sqlcgen.New(tx)
|
||||
committed := false
|
||||
defer func() {
|
||||
if committed {
|
||||
return
|
||||
}
|
||||
_ = tx.Rollback(ctx)
|
||||
}()
|
||||
if err := lockUsersForUpdate(ctx, tx, req.OwnerUserID); err != nil {
|
||||
return res, fmt.Errorf("lock delete saved history user: %w", err)
|
||||
}
|
||||
rows, err := qtx.DeleteMessageBoxesBySavedPeerBatch(ctx, sqlcgen.DeleteMessageBoxesBySavedPeerBatchParams{
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
SavedPeerType: string(req.SavedPeer.Type),
|
||||
SavedPeerID: req.SavedPeer.ID,
|
||||
MaxID: pgInt32NonNegative(req.MaxID),
|
||||
MinDate: pgInt32NonNegative(req.MinDate),
|
||||
MaxDate: pgInt32NonNegative(req.MaxDate),
|
||||
LimitCount: int32(domain.MaxDeleteHistoryBatch),
|
||||
})
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("delete saved history batch: %w", err)
|
||||
}
|
||||
deleted := make([]deletedBox, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
deleted = append(deleted, deletedBox{
|
||||
ownerUserID: row.OwnerUserID,
|
||||
boxID: int(row.BoxID),
|
||||
privateMessageID: row.PrivateMessageID,
|
||||
messageSenderID: row.MessageSenderID,
|
||||
peer: domain.Peer{Type: domain.PeerType(row.PeerType), ID: row.PeerID},
|
||||
})
|
||||
}
|
||||
delRes, err := s.finishDeleteMessagesTx(ctx, tx, qtx, req.OwnerUserID, req.OriginAuthKeyID, req.OriginSessionID, req.Date, deleted, false)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
if len(deleted) > 0 {
|
||||
more, err := qtx.HasDeletableMessageBoxBySavedPeer(ctx, sqlcgen.HasDeletableMessageBoxBySavedPeerParams{
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
SavedPeerType: string(req.SavedPeer.Type),
|
||||
SavedPeerID: req.SavedPeer.ID,
|
||||
MaxID: pgInt32NonNegative(req.MaxID),
|
||||
MinDate: pgInt32NonNegative(req.MinDate),
|
||||
MaxDate: pgInt32NonNegative(req.MaxDate),
|
||||
})
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("probe deletable saved history: %w", err)
|
||||
}
|
||||
res.More = more
|
||||
if !more {
|
||||
// 边界口径外可能还有残留(如 max_id 截断),子会话仍存活时
|
||||
// 不能清置顶;只有全量删空才清。
|
||||
alive, err := qtx.HasDeletableMessageBoxBySavedPeer(ctx, sqlcgen.HasDeletableMessageBoxBySavedPeerParams{
|
||||
OwnerUserID: req.OwnerUserID,
|
||||
SavedPeerType: string(req.SavedPeer.Type),
|
||||
SavedPeerID: req.SavedPeer.ID,
|
||||
})
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("probe saved dialog alive: %w", err)
|
||||
}
|
||||
if !alive {
|
||||
if _, err := qtx.DeleteSavedDialogPin(ctx, sqlcgen.DeleteSavedDialogPinParams{
|
||||
UserID: req.OwnerUserID,
|
||||
PeerType: string(req.SavedPeer.Type),
|
||||
PeerID: req.SavedPeer.ID,
|
||||
}); err != nil {
|
||||
return res, fmt.Errorf("clear pin after saved history delete: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(ctx); err != nil {
|
||||
return res, fmt.Errorf("commit delete saved history tx: %w", err)
|
||||
}
|
||||
committed = true
|
||||
for _, d := range delRes.Deleted {
|
||||
if d.UserID == req.OwnerUserID {
|
||||
res.MessageIDs = d.MessageIDs
|
||||
res.Event = d.Event
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// savedDialogTopRowFields 是三个 saved dialog top 查询共有的行字段。
|
||||
type savedDialogTopRowFields struct {
|
||||
BoxID int32
|
||||
PrivateMessageID int64
|
||||
OwnerUserID int64
|
||||
PeerType string
|
||||
PeerID int64
|
||||
FromUserID int64
|
||||
MessageDate int32
|
||||
TtlPeriod int32
|
||||
ExpiresAt int32
|
||||
EditDate int32
|
||||
Outgoing bool
|
||||
Body string
|
||||
EntitiesJson string
|
||||
Silent bool
|
||||
Noforwards bool
|
||||
ReplyToMsgID int32
|
||||
ReplyToPeerType string
|
||||
ReplyToPeerID int64
|
||||
ReplyToTopID int32
|
||||
ReplyToStoryID int32
|
||||
QuoteText string
|
||||
QuoteEntitiesJson string
|
||||
QuoteOffset int32
|
||||
FwdFromPeerType string
|
||||
FwdFromPeerID int64
|
||||
FwdFromName string
|
||||
FwdDate int32
|
||||
FwdSavedFromPeerType string
|
||||
FwdSavedFromPeerID int64
|
||||
FwdSavedFromMsgID int32
|
||||
SavedPeerType string
|
||||
SavedPeerID int64
|
||||
Pts int32
|
||||
MediaJson string
|
||||
MediaUnread bool
|
||||
ReactionUnread bool
|
||||
ViaBotID int64
|
||||
GroupedID int64
|
||||
Effect int64
|
||||
ReplyMarkupJson string
|
||||
RichMessageJson string
|
||||
Pinned bool
|
||||
}
|
||||
|
||||
func savedDialogRowFields[T sqlcgen.ListSavedDialogTopsRow | sqlcgen.ListPinnedSavedDialogTopsRow | sqlcgen.ListSavedDialogTopsByPeersRow](row T) savedDialogTopRowFields {
|
||||
switch r := any(row).(type) {
|
||||
case sqlcgen.ListSavedDialogTopsRow:
|
||||
return savedDialogTopRowFields{r.BoxID, r.PrivateMessageID, r.OwnerUserID, r.PeerType, r.PeerID, r.FromUserID, r.MessageDate, r.TtlPeriod, r.ExpiresAt, r.EditDate, r.Outgoing, r.Body, r.EntitiesJson, r.Silent, r.Noforwards, r.ReplyToMsgID, r.ReplyToPeerType, r.ReplyToPeerID, r.ReplyToTopID, r.ReplyToStoryID, r.QuoteText, r.QuoteEntitiesJson, r.QuoteOffset, r.FwdFromPeerType, r.FwdFromPeerID, r.FwdFromName, r.FwdDate, r.FwdSavedFromPeerType, r.FwdSavedFromPeerID, r.FwdSavedFromMsgID, r.SavedPeerType, r.SavedPeerID, r.Pts, r.MediaJson, r.MediaUnread, r.ReactionUnread, r.ViaBotID, r.GroupedID, r.Effect, r.ReplyMarkupJson, r.RichMessageJson, r.Pinned}
|
||||
case sqlcgen.ListPinnedSavedDialogTopsRow:
|
||||
return savedDialogTopRowFields{r.BoxID, r.PrivateMessageID, r.OwnerUserID, r.PeerType, r.PeerID, r.FromUserID, r.MessageDate, r.TtlPeriod, r.ExpiresAt, r.EditDate, r.Outgoing, r.Body, r.EntitiesJson, r.Silent, r.Noforwards, r.ReplyToMsgID, r.ReplyToPeerType, r.ReplyToPeerID, r.ReplyToTopID, r.ReplyToStoryID, r.QuoteText, r.QuoteEntitiesJson, r.QuoteOffset, r.FwdFromPeerType, r.FwdFromPeerID, r.FwdFromName, r.FwdDate, r.FwdSavedFromPeerType, r.FwdSavedFromPeerID, r.FwdSavedFromMsgID, r.SavedPeerType, r.SavedPeerID, r.Pts, r.MediaJson, r.MediaUnread, r.ReactionUnread, r.ViaBotID, r.GroupedID, r.Effect, r.ReplyMarkupJson, r.RichMessageJson, r.Pinned}
|
||||
case sqlcgen.ListSavedDialogTopsByPeersRow:
|
||||
return savedDialogTopRowFields{r.BoxID, r.PrivateMessageID, r.OwnerUserID, r.PeerType, r.PeerID, r.FromUserID, r.MessageDate, r.TtlPeriod, r.ExpiresAt, r.EditDate, r.Outgoing, r.Body, r.EntitiesJson, r.Silent, r.Noforwards, r.ReplyToMsgID, r.ReplyToPeerType, r.ReplyToPeerID, r.ReplyToTopID, r.ReplyToStoryID, r.QuoteText, r.QuoteEntitiesJson, r.QuoteOffset, r.FwdFromPeerType, r.FwdFromPeerID, r.FwdFromName, r.FwdDate, r.FwdSavedFromPeerType, r.FwdSavedFromPeerID, r.FwdSavedFromMsgID, r.SavedPeerType, r.SavedPeerID, r.Pts, r.MediaJson, r.MediaUnread, r.ReactionUnread, r.ViaBotID, r.GroupedID, r.Effect, r.ReplyMarkupJson, r.RichMessageJson, r.Pinned}
|
||||
}
|
||||
return savedDialogTopRowFields{}
|
||||
}
|
||||
|
||||
func messageFromSavedDialogRow(row savedDialogTopRowFields) (domain.Message, error) {
|
||||
entities, err := decodeMessageEntities(row.EntitiesJson)
|
||||
if err != nil {
|
||||
return domain.Message{}, fmt.Errorf("decode saved dialog message entities: %w", err)
|
||||
}
|
||||
silent, noforwards, reply, forward, err := messageMetadataFromFields(
|
||||
row.Silent,
|
||||
row.Noforwards,
|
||||
row.ReplyToMsgID,
|
||||
row.ReplyToPeerType,
|
||||
row.ReplyToPeerID,
|
||||
row.ReplyToTopID,
|
||||
row.ReplyToStoryID,
|
||||
row.QuoteText,
|
||||
row.QuoteEntitiesJson,
|
||||
row.QuoteOffset,
|
||||
row.FwdFromPeerType,
|
||||
row.FwdFromPeerID,
|
||||
row.FwdFromName,
|
||||
row.FwdDate,
|
||||
row.FwdSavedFromPeerType,
|
||||
row.FwdSavedFromPeerID,
|
||||
row.FwdSavedFromMsgID,
|
||||
)
|
||||
if err != nil {
|
||||
return domain.Message{}, fmt.Errorf("decode saved dialog message metadata: %w", err)
|
||||
}
|
||||
media, err := decodeMessageMedia(row.MediaJson)
|
||||
if err != nil {
|
||||
return domain.Message{}, fmt.Errorf("decode saved dialog message media: %w", err)
|
||||
}
|
||||
markup, err := decodeReplyMarkup(row.ReplyMarkupJson)
|
||||
if err != nil {
|
||||
return domain.Message{}, fmt.Errorf("decode saved dialog message reply markup: %w", err)
|
||||
}
|
||||
rich, err := decodeRichMessage(row.RichMessageJson)
|
||||
if err != nil {
|
||||
return domain.Message{}, fmt.Errorf("decode saved dialog message rich message: %w", err)
|
||||
}
|
||||
return 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),
|
||||
TTLPeriod: int(row.TtlPeriod),
|
||||
ExpiresAt: int(row.ExpiresAt),
|
||||
Media: media,
|
||||
MediaUnread: row.MediaUnread,
|
||||
ReactionUnread: row.ReactionUnread,
|
||||
ViaBotID: row.ViaBotID,
|
||||
GroupedID: row.GroupedID,
|
||||
Effect: row.Effect,
|
||||
ReplyMarkup: markup,
|
||||
RichMessage: rich,
|
||||
Pinned: row.Pinned,
|
||||
SavedPeer: savedPeerFromFields(row.SavedPeerType, row.SavedPeerID),
|
||||
}, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue