owpengram-server/internal/rpc/messages_quick_replies.go

395 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package rpc
import (
"context"
"errors"
"github.com/gotd/td/tg"
"telesrv/internal/domain"
)
func (r *Router) onMessagesGetQuickReplies(ctx context.Context, hash int64) (tg.MessagesQuickRepliesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return &tg.MessagesQuickReplies{
QuickReplies: []tg.QuickReply{},
Messages: []tg.MessageClass{},
Chats: []tg.ChatClass{},
Users: []tg.UserClass{},
}, nil
}
list, err := svc.ListQuickReplies(ctx, userID)
if err != nil {
return nil, businessAutomationErr(err)
}
if hash != 0 && hash == list.Hash {
return &tg.MessagesQuickRepliesNotModified{}, nil
}
out := tgMessagesQuickReplies(list)
out.Users = r.quickReplyUsers(ctx, userID)
return out, nil
}
func (r *Router) onMessagesCheckQuickReplyShortcut(ctx context.Context, shortcut string) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return false, premiumAccountRequiredErr()
}
available, err := svc.CheckQuickReplyShortcut(ctx, userID, shortcut)
if err != nil {
return false, businessAutomationErr(err)
}
return available, nil
}
func (r *Router) onMessagesReorderQuickReplies(ctx context.Context, order []int) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return false, premiumAccountRequiredErr()
}
mutation, err := svc.ReorderQuickReplies(ctx, userID, order)
if err != nil {
return false, businessAutomationErr(err)
}
if err := r.pushQuickReplyMutation(ctx, userID, mutation); err != nil {
return false, err
}
return true, nil
}
func (r *Router) onMessagesEditQuickReplyShortcut(ctx context.Context, req *tg.MessagesEditQuickReplyShortcutRequest) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
if req == nil || req.ShortcutID <= 0 {
return false, shortcutInvalidErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return false, premiumAccountRequiredErr()
}
mutation, err := svc.RenameQuickReplyShortcut(ctx, userID, req.ShortcutID, req.Shortcut)
if err != nil {
return false, businessAutomationErr(err)
}
if err := r.pushQuickReplyMutation(ctx, userID, mutation); err != nil {
return false, err
}
return true, nil
}
func (r *Router) onMessagesDeleteQuickReplyShortcut(ctx context.Context, shortcutID int) (bool, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return false, internalErr()
}
if shortcutID <= 0 {
return false, shortcutInvalidErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return false, shortcutInvalidErr()
}
mutation, err := svc.DeleteQuickReplyShortcut(ctx, userID, shortcutID)
if err != nil {
return false, businessAutomationErr(err)
}
if err := r.pushQuickReplyMutation(ctx, userID, mutation); err != nil {
return false, err
}
return true, nil
}
func (r *Router) onMessagesGetQuickReplyMessages(ctx context.Context, req *tg.MessagesGetQuickReplyMessagesRequest) (tg.MessagesMessagesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
if req == nil || len(req.ID) > domain.MaxQuickReplyMessages {
return nil, shortcutInvalidErr()
}
if req.ShortcutID <= 0 {
return emptyQuickReplyMessages(), nil
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return emptyQuickReplyMessages(), nil
}
list, err := svc.GetQuickReplyMessages(ctx, userID, req.ShortcutID, req.ID)
if err != nil {
if errors.Is(err, domain.ErrShortcutInvalid) {
return emptyQuickReplyMessages(), nil
}
return nil, businessAutomationErr(err)
}
if req.Hash != 0 && req.Hash == list.Hash {
return &tg.MessagesMessagesNotModified{Count: list.Count}, nil
}
out := tgMessagesQuickReplyMessages(list)
out.Users = r.quickReplyUsers(ctx, userID)
return out, nil
}
func emptyQuickReplyMessages() *tg.MessagesMessages {
return &tg.MessagesMessages{
Messages: []tg.MessageClass{},
Chats: []tg.ChatClass{},
Users: []tg.UserClass{},
}
}
func (r *Router) onMessagesSendQuickReplyMessages(ctx context.Context, req *tg.MessagesSendQuickReplyMessagesRequest) (tg.UpdatesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
if req == nil || req.ShortcutID <= 0 || len(req.ID) > domain.MaxQuickReplyMessages || len(req.RandomID) > domain.MaxQuickReplyMessages {
return nil, shortcutInvalidErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok || r.deps.Messages == nil {
return nil, shortcutInvalidErr()
}
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
if err != nil {
return nil, err
}
if peer.Type != domain.PeerTypeUser {
return nil, peerIDInvalidErr()
}
list, err := svc.GetQuickReplyMessages(ctx, userID, req.ShortcutID, req.ID)
if err != nil {
return nil, businessAutomationErr(err)
}
if len(list.Messages) == 0 {
return &tg.Updates{Updates: []tg.UpdateClass{}, Users: []tg.UserClass{}, Chats: []tg.ChatClass{}, Date: int(r.clock.Now().Unix())}, nil
}
randomIDs := append([]int64(nil), req.RandomID...)
if len(randomIDs) == 0 {
randomIDs = r.quickReplyRandomIDs(len(list.Messages))
}
if len(randomIDs) != len(list.Messages) {
return nil, inputRequestInvalidErr()
}
for _, randomID := range randomIDs {
if randomID == 0 {
return nil, randomIDEmptyErr()
}
}
if err := r.checkSendRateLimit(ctx, userID, len(list.Messages)); err != nil {
return nil, err
}
recipientBlocked, err := r.peerBlocksUser(ctx, userID, peer.ID)
if err != nil {
return nil, err
}
sessionID, _ := SessionIDFrom(ctx)
authKeyID, _ := AuthKeyIDFrom(ctx)
res := domain.ForwardPrivateMessagesResult{OwnerUserID: userID}
now := int(r.clock.Now().Unix())
for i, template := range list.Messages {
sent, err := r.deps.Messages.SendPrivateText(ctx, userID, domain.SendPrivateTextRequest{
SenderUserID: userID,
RecipientUserID: peer.ID,
RandomID: randomIDs[i],
Message: template.Message,
Entities: append([]domain.MessageEntity(nil), template.Entities...),
Date: now,
OriginAuthKeyID: authKeyID,
OriginSessionID: sessionID,
RecipientBlocked: recipientBlocked,
})
if err != nil {
return nil, messageSendErr(err)
}
res.SenderMessages = append(res.SenderMessages, sent.SenderMessage)
res.RecipientMessages = append(res.RecipientMessages, sent.RecipientMessage)
res.SenderEvents = append(res.SenderEvents, sent.SenderEvent)
res.RecipientEvents = append(res.RecipientEvents, sent.RecipientEvent)
res.Duplicates = append(res.Duplicates, sent.Duplicate)
}
return tgForwardMessagesUpdates(res, randomIDs, r.usersForMessageUpdates(ctx, userID, res.SenderMessages), r.chatsForMessageUpdates(ctx, userID, res.SenderMessages)), nil
}
func (r *Router) onMessagesDeleteQuickReplyMessages(ctx context.Context, req *tg.MessagesDeleteQuickReplyMessagesRequest) (tg.UpdatesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
if req == nil || req.ShortcutID <= 0 || len(req.ID) == 0 || len(req.ID) > domain.MaxQuickReplyMessages {
return nil, shortcutInvalidErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return nil, shortcutInvalidErr()
}
mutation, err := svc.DeleteQuickReplyMessages(ctx, userID, req.ShortcutID, req.ID)
if err != nil {
return nil, businessAutomationErr(err)
}
return r.quickReplyMutationUpdates(ctx, userID, mutation, nil)
}
func (r *Router) onMessagesSaveQuickReplyText(ctx context.Context, req *tg.MessagesSendMessageRequest) (tg.UpdatesClass, error) {
userID, _, err := r.currentUserID(ctx)
if err != nil {
return nil, internalErr()
}
shortcut, err := quickReplyShortcutName(req.QuickReplyShortcut)
if err != nil {
return nil, err
}
peer, err := r.checkedDomainPeerFromInputPeer(ctx, userID, req.Peer)
if err != nil {
return nil, err
}
if peer.Type != domain.PeerTypeUser || peer.ID != userID {
return nil, shortcutInvalidErr()
}
svc, ok := r.accountBusinessAutomation()
if !ok {
return nil, premiumAccountRequiredErr()
}
mutation, err := svc.SaveQuickReplyText(ctx, userID, shortcut, domain.QuickReplyMessage{
RandomID: req.RandomID,
Date: int(r.clock.Now().Unix()),
Message: req.Message,
// 快速回复模板与普通发送一致补服务端自动实体url/@mention/#hashtag/bot command
Entities: domainMessageEntitiesForViewer(userID, augmentAutoEntities(req.Message, req.Entities)),
})
if err != nil {
return nil, businessAutomationErr(err)
}
return r.quickReplyMutationUpdates(ctx, userID, mutation, []tg.UpdateClass{
&tg.UpdateMessageID{ID: mutation.Message.ID, RandomID: req.RandomID},
})
}
func quickReplyShortcutName(input tg.InputQuickReplyShortcutClass) (string, error) {
switch shortcut := input.(type) {
case *tg.InputQuickReplyShortcut:
return shortcut.Shortcut, nil
default:
return "", shortcutInvalidErr()
}
}
func (r *Router) quickReplyRandomIDs(n int) []int64 {
base := r.clock.Now().UnixNano()
if base == 0 {
base = 1
}
out := make([]int64, n)
for i := range out {
out[i] = base + int64(i)
if out[i] == 0 {
out[i] = int64(i + 1)
}
}
return out
}
func (r *Router) quickReplyUsers(ctx context.Context, userID int64) []tg.UserClass {
if r.deps.Users == nil || userID == 0 {
return []tg.UserClass{}
}
self, err := r.deps.Users.Self(ctx, userID)
if err != nil || self.ID == 0 {
return []tg.UserClass{}
}
return []tg.UserClass{r.tgSelfUser(self)}
}
func (r *Router) quickReplyMutationUpdates(ctx context.Context, userID int64, mutation domain.QuickReplyMutation, prefix []tg.UpdateClass) (*tg.Updates, error) {
event, err := r.recordQuickReplyMutation(ctx, userID, mutation)
if err != nil {
return nil, err
}
updates := append([]tg.UpdateClass(nil), prefix...)
if update := tgOtherUpdateFromEvent(event); update != nil {
updates = append(updates, update)
}
updates = appendAuxPtsBookkeeping(updates, event)
date := event.Date
if date == 0 {
date = int(r.clock.Now().Unix())
}
return &tg.Updates{Updates: updates, Users: r.quickReplyUsers(ctx, userID), Chats: []tg.ChatClass{}, Date: date, Seq: 0}, nil
}
func (r *Router) pushQuickReplyMutation(ctx context.Context, userID int64, mutation domain.QuickReplyMutation) error {
event, err := r.recordQuickReplyMutation(ctx, userID, mutation)
if err != nil {
return err
}
updates := make([]tg.UpdateClass, 0, 2)
if update := tgOtherUpdateFromEvent(event); update != nil {
updates = append(updates, update)
}
updates = appendAuxPtsBookkeeping(updates, event)
r.bookkeepAuxPtsForCurrentSession(ctx, event)
r.pushUserUpdatesIfNoReliableDispatch(ctx, userID, &tg.Updates{
Updates: updates,
Users: r.quickReplyUsers(ctx, userID),
Chats: []tg.ChatClass{},
Date: event.Date,
Seq: 0,
})
return nil
}
func (r *Router) recordQuickReplyMutation(ctx context.Context, userID int64, mutation domain.QuickReplyMutation) (domain.UpdateEvent, error) {
if r.deps.Updates == nil {
return quickReplyEventFromMutation(userID, mutation, int(r.clock.Now().Unix())), nil
}
authKeyID, _ := AuthKeyIDFrom(ctx)
sessionID, _ := SessionIDFrom(ctx)
event, _, err := r.deps.Updates.RecordQuickReplyMutation(ctx, authKeyID, userID, mutation, sessionID)
if err != nil {
return domain.UpdateEvent{}, internalErr()
}
return event, nil
}
func quickReplyEventFromMutation(userID int64, mutation domain.QuickReplyMutation, date int) domain.UpdateEvent {
event := domain.UpdateEvent{
UserID: userID,
Date: date,
QuickReply: mutation.QuickReply,
MaxID: mutation.ShortcutID,
MessageIDs: append([]int(nil), mutation.MessageIDs...),
}
switch mutation.Kind {
case domain.QuickReplyMutationNew:
event.Type = domain.UpdateEventNewQuickReply
event.QuickReply = mutation.QuickReply
case domain.QuickReplyMutationDelete:
event.Type = domain.UpdateEventDeleteQuickReply
case domain.QuickReplyMutationMessage:
event.Type = domain.UpdateEventQuickReplyMessage
event.QuickReplyMessage = mutation.Message
case domain.QuickReplyMutationIDs:
event.Type = domain.UpdateEventDeleteQuickReplyMessages
default:
event.Type = domain.UpdateEventQuickReplies
event.QuickReplies = append([]domain.QuickReply(nil), mutation.List.QuickReplies...)
}
if event.Date == 0 && mutation.Date != 0 {
event.Date = mutation.Date
}
return event
}