feat: sync bot keyboards and callbacks
Sync telesrv b96f2dd (feat(bot): complete keyboards callbacks and durable delivery). Skipped private docs and preserved public README files per sync rules; normalized the appearance seed log label for public naming.
This commit is contained in:
parent
0c99ae0a9d
commit
bf965f610c
80 changed files with 7212 additions and 349 deletions
|
|
@ -1276,6 +1276,9 @@ func (s *Service) SendMessage(ctx context.Context, userID int64, req domain.Send
|
|||
if req.UserID != userID {
|
||||
return domain.SendChannelMessageResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
if err := domain.ValidateReplyMarkup(req.ReplyMarkup); err != nil {
|
||||
return domain.SendChannelMessageResult{}, err
|
||||
}
|
||||
if req.RandomID != 0 && !req.IdempotencyPreflighted {
|
||||
fingerprint, err := store.ChannelSendFingerprint(req)
|
||||
if err != nil {
|
||||
|
|
@ -1343,6 +1346,11 @@ func (s *Service) EditMessage(ctx context.Context, userID int64, req domain.Edit
|
|||
if req.UserID != userID || req.ChannelID == 0 || req.ID <= 0 {
|
||||
return domain.EditChannelMessageResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
if req.SetReplyMarkup {
|
||||
if err := domain.ValidateReplyMarkup(req.ReplyMarkup); err != nil {
|
||||
return domain.EditChannelMessageResult{}, err
|
||||
}
|
||||
}
|
||||
return s.channels.EditChannelMessage(ctx, req)
|
||||
}
|
||||
|
||||
|
|
@ -1359,6 +1367,11 @@ func (s *Service) EditInlineBotMessage(ctx context.Context, botID int64, req dom
|
|||
if s == nil || s.channels == nil || botID == 0 || req.ChannelID == 0 || req.ID <= 0 || req.UserID == 0 {
|
||||
return domain.EditChannelMessageResult{}, domain.ErrChannelInvalid
|
||||
}
|
||||
if req.SetReplyMarkup {
|
||||
if err := domain.ValidateReplyMarkup(req.ReplyMarkup); err != nil {
|
||||
return domain.EditChannelMessageResult{}, err
|
||||
}
|
||||
}
|
||||
req.ViaBotEditBotID = botID
|
||||
return s.channels.EditChannelMessage(ctx, req)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -461,7 +461,14 @@ func cloneReplyMarkupForDialogCache(in *domain.MessageReplyMarkup) *domain.Messa
|
|||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := &domain.MessageReplyMarkup{}
|
||||
out := &domain.MessageReplyMarkup{
|
||||
Type: in.Type,
|
||||
Resize: in.Resize,
|
||||
SingleUse: in.SingleUse,
|
||||
Selective: in.Selective,
|
||||
Persistent: in.Persistent,
|
||||
Placeholder: in.Placeholder,
|
||||
}
|
||||
if len(in.Inline) > 0 {
|
||||
out.Inline = make([][]domain.MarkupButton, len(in.Inline))
|
||||
for i, row := range in.Inline {
|
||||
|
|
@ -472,6 +479,12 @@ func cloneReplyMarkupForDialogCache(in *domain.MessageReplyMarkup) *domain.Messa
|
|||
}
|
||||
}
|
||||
}
|
||||
if len(in.Keyboard) > 0 {
|
||||
out.Keyboard = make([][]domain.MarkupButton, len(in.Keyboard))
|
||||
for i, row := range in.Keyboard {
|
||||
out.Keyboard[i] = append([]domain.MarkupButton(nil), row...)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,41 @@ func (s *Service) GetPhoto(ctx context.Context, id int64) (domain.Photo, bool, e
|
|||
return s.media.GetPhoto(ctx, id)
|
||||
}
|
||||
|
||||
type photoBatchStore interface {
|
||||
GetPhotos(ctx context.Context, ids []int64) ([]domain.Photo, error)
|
||||
}
|
||||
|
||||
// GetPhotos loads immutable photo metadata in caller order without requiring
|
||||
// one storage round-trip per requested-peer response. PostgreSQL implements the
|
||||
// optional batch primitive; lightweight stores retain a bounded fallback.
|
||||
func (s *Service) GetPhotos(ctx context.Context, ids []int64) ([]domain.Photo, error) {
|
||||
if s == nil || s.media == nil || len(ids) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if batch, ok := s.media.(photoBatchStore); ok {
|
||||
return batch.GetPhotos(ctx, ids)
|
||||
}
|
||||
seen := make(map[int64]struct{}, len(ids))
|
||||
out := make([]domain.Photo, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
if id == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
photo, found, err := s.media.GetPhoto(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found {
|
||||
out = append(out, photo)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetDocument 按 id 返回已存储文档(贴纸 / 文件)。
|
||||
func (s *Service) GetDocument(ctx context.Context, id int64) (domain.Document, bool, error) {
|
||||
return s.media.GetDocument(ctx, id)
|
||||
|
|
|
|||
|
|
@ -100,6 +100,9 @@ func (s *Service) SendPrivateText(ctx context.Context, userID int64, req domain.
|
|||
if req.SenderUserID != userID {
|
||||
return domain.SendPrivateTextResult{}, domain.ErrAuthenticatedScopeInvalid
|
||||
}
|
||||
if err := domain.ValidateReplyMarkup(req.ReplyMarkup); err != nil {
|
||||
return domain.SendPrivateTextResult{}, err
|
||||
}
|
||||
if req.RandomID != 0 && !req.IdempotencyPreflighted {
|
||||
fingerprint, err := store.PrivateSendFingerprint(req)
|
||||
if err != nil {
|
||||
|
|
@ -263,6 +266,32 @@ func (s *Service) GetMessages(ctx context.Context, userID int64, ids []int) (dom
|
|||
return s.projectMessageUsers(ctx, userID, list)
|
||||
}
|
||||
|
||||
type messageByUIDStore interface {
|
||||
GetByUID(ctx context.Context, userID, uid int64) (domain.Message, bool, error)
|
||||
}
|
||||
|
||||
// GetMessageByUID translates a shared private message id into one owner's exact box row.
|
||||
// It is intentionally an optional capability so lightweight MessageStore test doubles that
|
||||
// never exercise callback translation do not need a meaningless implementation.
|
||||
func (s *Service) GetMessageByUID(ctx context.Context, userID, uid int64) (domain.Message, bool, error) {
|
||||
if s == nil || userID == 0 || uid == 0 {
|
||||
return domain.Message{}, false, nil
|
||||
}
|
||||
provider, ok := s.messages.(messageByUIDStore)
|
||||
if !ok {
|
||||
return domain.Message{}, false, nil
|
||||
}
|
||||
msg, found, err := provider.GetByUID(ctx, userID, uid)
|
||||
if err != nil || !found {
|
||||
return domain.Message{}, found, err
|
||||
}
|
||||
list, err := s.projectMessageUsers(ctx, userID, domain.MessageList{Messages: []domain.Message{msg}})
|
||||
if err != nil || len(list.Messages) != 1 {
|
||||
return domain.Message{}, false, err
|
||||
}
|
||||
return list.Messages[0], true, nil
|
||||
}
|
||||
|
||||
// GetHistory 返回当前账号某个 peer 的历史消息。
|
||||
func (s *Service) GetHistory(ctx context.Context, userID int64, filter domain.MessageFilter) (domain.MessageList, error) {
|
||||
return s.list(ctx, userID, filter)
|
||||
|
|
@ -406,6 +435,11 @@ func (s *Service) EditMessage(ctx context.Context, userID int64, req domain.Edit
|
|||
if req.OwnerUserID == 0 {
|
||||
req.OwnerUserID = userID
|
||||
}
|
||||
if req.SetReplyMarkup {
|
||||
if err := domain.ValidateReplyMarkup(req.ReplyMarkup); err != nil {
|
||||
return domain.EditMessageResult{OwnerUserID: userID}, err
|
||||
}
|
||||
}
|
||||
return s.messages.EditMessage(ctx, req)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue