369 lines
10 KiB
Go
369 lines
10 KiB
Go
package rpc
|
|
|
|
import (
|
|
"context"
|
|
"unicode/utf8"
|
|
|
|
"github.com/iamxvbaba/td/tg"
|
|
|
|
"telesrv/internal/domain"
|
|
)
|
|
|
|
func (r *Router) onMessagesSavePreparedInlineMessage(ctx context.Context, req *tg.MessagesSavePreparedInlineMessageRequest) (*tg.MessagesBotPreparedInlineMessage, error) {
|
|
botID, err := r.callerBotID(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if req == nil || req.Result == nil {
|
|
return nil, resultIDInvalidErr()
|
|
}
|
|
if r.deps.Users == nil {
|
|
return nil, internalErr()
|
|
}
|
|
currentUserID, _, err := r.currentUserID(ctx)
|
|
if err != nil {
|
|
return nil, internalErr()
|
|
}
|
|
target, found, err := r.userFromInput(ctx, currentUserID, req.UserID)
|
|
if err != nil {
|
|
return nil, internalErr()
|
|
}
|
|
if !found {
|
|
return nil, userIDInvalidErr()
|
|
}
|
|
result, err := r.domainInlineResultFromTG(ctx, botID, req.Result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := r.prepareTelegramLoginMarkup(ctx, botID, result.ReplyMarkup); err != nil {
|
|
return nil, replyMarkupErr(err)
|
|
}
|
|
peerTypes, err := preparedInlinePeerTypesFromTG(req.PeerTypes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
id, expireDate := r.inlines.savePreparedInlineContext(ctx, r.clock.Now(), botID, target.ID, result, peerTypes)
|
|
return &tg.MessagesBotPreparedInlineMessage{
|
|
ID: id,
|
|
ExpireDate: expireDate,
|
|
}, nil
|
|
}
|
|
|
|
func preparedInlinePeerTypesFromTG(in []tg.InlineQueryPeerTypeClass) ([]string, error) {
|
|
if len(in) == 0 {
|
|
return nil, nil
|
|
}
|
|
out := make([]string, 0, len(in))
|
|
seen := make(map[string]struct{}, len(in))
|
|
for _, peerType := range in {
|
|
value := storeInlineQueryPeerType(peerType)
|
|
if value == "" {
|
|
return nil, peerIDInvalidErr()
|
|
}
|
|
if _, ok := seen[value]; ok {
|
|
continue
|
|
}
|
|
seen[value] = struct{}{}
|
|
out = append(out, value)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func tgPreparedInlinePeerTypes(in []string) []tg.InlineQueryPeerTypeClass {
|
|
if len(in) == 0 {
|
|
return []tg.InlineQueryPeerTypeClass{}
|
|
}
|
|
out := make([]tg.InlineQueryPeerTypeClass, 0, len(in))
|
|
for _, peerType := range in {
|
|
if value, ok := tgInlineQueryPeerTypeFromStore(peerType); ok {
|
|
out = append(out, value)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (r *Router) onMessagesEditInlineBotMessage(ctx context.Context, req *tg.MessagesEditInlineBotMessageRequest) (bool, error) {
|
|
botID, _, err := r.currentUserID(ctx)
|
|
if err != nil {
|
|
return false, internalErr()
|
|
}
|
|
if req == nil {
|
|
return false, messageIDInvalidErr()
|
|
}
|
|
if botID == 0 || !r.userIsBot(ctx, botID) || r.deps.Messages == nil {
|
|
return false, messageIDInvalidErr()
|
|
}
|
|
target, found, err := r.privateMessageFromInlineID(ctx, botID, req.ID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if found {
|
|
return r.editPrivateInlineBotMessage(ctx, botID, target, req)
|
|
}
|
|
_, channelTarget, found, err := r.channelMessageFromInlineID(ctx, botID, req.ID)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if found {
|
|
return r.editChannelInlineBotMessage(ctx, botID, channelTarget, req)
|
|
}
|
|
return false, messageIDInvalidErr()
|
|
}
|
|
|
|
func (r *Router) editPrivateInlineBotMessage(ctx context.Context, botID int64, target domain.Message, req *tg.MessagesEditInlineBotMessageRequest) (bool, error) {
|
|
newMedia, err := r.inlineEditMedia(ctx, target.OwnerUserID, req)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
message := target.Body
|
|
entities := append([]domain.MessageEntity(nil), target.Entities...)
|
|
richMessage := target.RichMessage
|
|
setRichMessage := false
|
|
rawRichMessage, hasRichMessage := req.GetRichMessage()
|
|
rawMessage, hasMessage := req.GetMessage()
|
|
if hasMessage && hasRichMessage {
|
|
return false, mediaInvalidErr()
|
|
}
|
|
if hasRichMessage {
|
|
richMessage, err = r.domainRichMessageFromInput(ctx, rawRichMessage)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if richMessage.IsZero() {
|
|
return false, richMessageInvalidErr()
|
|
}
|
|
message, entities, setRichMessage = "", nil, true
|
|
} else if hasMessage {
|
|
if rawMessage == "" && newMedia == nil && target.Media.IsZero() {
|
|
return false, messageEmptyErr()
|
|
}
|
|
if utf8.RuneCountInString(rawMessage) > maxSendMessageTextLength {
|
|
return false, messageTooLongErr()
|
|
}
|
|
rawEntities, _ := req.GetEntities()
|
|
if len(rawEntities) > maxMessageEntityCount {
|
|
return false, entitiesTooLongErr()
|
|
}
|
|
message = rawMessage
|
|
entities = domainMessageEntitiesForViewer(botID, rawEntities)
|
|
richMessage, setRichMessage = nil, true
|
|
} else if req.ReplyMarkup == nil && newMedia == nil {
|
|
return false, messageNotModifiedErr()
|
|
}
|
|
var replyMarkup *domain.MessageReplyMarkup
|
|
setReplyMarkup := false
|
|
if req.ReplyMarkup != nil {
|
|
var err error
|
|
replyMarkup, err = domainReplyMarkupForSender(req.ReplyMarkup, true)
|
|
if err != nil {
|
|
return false, replyMarkupErr(err)
|
|
}
|
|
if _, ok := req.ReplyMarkup.(*tg.ReplyInlineMarkup); ok {
|
|
setReplyMarkup = true
|
|
}
|
|
}
|
|
if setReplyMarkup {
|
|
if err := r.prepareTelegramLoginMarkup(ctx, botID, replyMarkup); err != nil {
|
|
return false, replyMarkupErr(err)
|
|
}
|
|
}
|
|
_, err = r.deps.Messages.EditMessage(ctx, target.OwnerUserID, domain.EditMessageRequest{
|
|
OwnerUserID: target.OwnerUserID,
|
|
Peer: target.Peer,
|
|
ID: target.ID,
|
|
Message: message,
|
|
Entities: entities,
|
|
Media: newMedia,
|
|
EditDate: int(r.clock.Now().Unix()),
|
|
SetReplyMarkup: setReplyMarkup,
|
|
ReplyMarkup: replyMarkup,
|
|
SetRichMessage: setRichMessage,
|
|
RichMessage: richMessage,
|
|
ViaBotEditBotID: botID,
|
|
})
|
|
if err != nil {
|
|
return false, messageEditErr(err)
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
func (r *Router) editChannelInlineBotMessage(ctx context.Context, botID int64, target domain.ChannelMessage, req *tg.MessagesEditInlineBotMessageRequest) (bool, error) {
|
|
if r.deps.Channels == nil {
|
|
return false, messageIDInvalidErr()
|
|
}
|
|
newMedia, err := r.inlineEditMedia(ctx, target.SenderUserID, req)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
message := target.Body
|
|
entities := append([]domain.MessageEntity(nil), target.Entities...)
|
|
var mentionUserIDs []int64
|
|
richMessage := target.RichMessage
|
|
setRichMessage := false
|
|
rawRichMessage, hasRichMessage := req.GetRichMessage()
|
|
rawMessage, hasMessage := req.GetMessage()
|
|
if hasMessage && hasRichMessage {
|
|
return false, mediaInvalidErr()
|
|
}
|
|
if hasRichMessage {
|
|
richMessage, err = r.domainRichMessageFromInput(ctx, rawRichMessage)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
if richMessage.IsZero() {
|
|
return false, richMessageInvalidErr()
|
|
}
|
|
message, entities, setRichMessage = "", nil, true
|
|
} else if hasMessage {
|
|
if rawMessage == "" && newMedia == nil && target.Media.IsZero() {
|
|
return false, messageEmptyErr()
|
|
}
|
|
if utf8.RuneCountInString(rawMessage) > maxSendMessageTextLength {
|
|
return false, messageTooLongErr()
|
|
}
|
|
rawEntities, _ := req.GetEntities()
|
|
if len(rawEntities) > maxMessageEntityCount {
|
|
return false, entitiesTooLongErr()
|
|
}
|
|
message = rawMessage
|
|
entities = domainMessageEntitiesForViewer(botID, rawEntities)
|
|
richMessage, setRichMessage = nil, true
|
|
var err error
|
|
mentionUserIDs, err = r.mentionedUserIDsFromMessage(ctx, botID, message, rawEntities)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
} else {
|
|
if req.ReplyMarkup == nil && newMedia == nil {
|
|
return false, messageNotModifiedErr()
|
|
}
|
|
mentionUserIDs, err = r.mentionedUserIDsFromDomainMessage(ctx, botID, message, entities)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
}
|
|
var replyMarkup *domain.MessageReplyMarkup
|
|
setReplyMarkup := false
|
|
if req.ReplyMarkup != nil {
|
|
var err error
|
|
replyMarkup, err = domainReplyMarkupForSender(req.ReplyMarkup, true)
|
|
if err != nil {
|
|
return false, replyMarkupErr(err)
|
|
}
|
|
if _, ok := req.ReplyMarkup.(*tg.ReplyInlineMarkup); ok {
|
|
setReplyMarkup = true
|
|
}
|
|
}
|
|
if setReplyMarkup {
|
|
if err := r.prepareTelegramLoginMarkup(ctx, botID, replyMarkup); err != nil {
|
|
return false, replyMarkupErr(err)
|
|
}
|
|
}
|
|
res, err := r.deps.Channels.EditInlineBotMessage(ctx, botID, domain.EditChannelMessageRequest{
|
|
UserID: target.SenderUserID,
|
|
ChannelID: target.ChannelID,
|
|
ID: target.ID,
|
|
Message: message,
|
|
Entities: entities,
|
|
Media: newMedia,
|
|
MentionUserIDs: mentionUserIDs,
|
|
EditDate: int(r.clock.Now().Unix()),
|
|
SetReplyMarkup: setReplyMarkup,
|
|
ReplyMarkup: replyMarkup,
|
|
SetRichMessage: setRichMessage,
|
|
RichMessage: richMessage,
|
|
ViaBotEditBotID: botID,
|
|
})
|
|
if err != nil {
|
|
return false, channelEditErr(err)
|
|
}
|
|
r.enqueueChannelEditMessageFanout(ctx, target.SenderUserID, res)
|
|
return true, nil
|
|
}
|
|
|
|
func (r *Router) inlineEditMedia(ctx context.Context, userID int64, req *tg.MessagesEditInlineBotMessageRequest) (*domain.MessageMedia, error) {
|
|
input, ok := req.GetMedia()
|
|
if !ok || editMessageMediaCanDegradeToText(input) {
|
|
return nil, nil
|
|
}
|
|
media, err := r.resolveInputMedia(ctx, userID, input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if !inlineEditMediaAllowed(media) {
|
|
return nil, mediaInvalidErr()
|
|
}
|
|
return media, nil
|
|
}
|
|
|
|
func inlineEditMediaAllowed(media *domain.MessageMedia) bool {
|
|
if media == nil || media.IsZero() {
|
|
return false
|
|
}
|
|
switch media.Kind {
|
|
case domain.MessageMediaKindPhoto,
|
|
domain.MessageMediaKindDocument,
|
|
domain.MessageMediaKindContact,
|
|
domain.MessageMediaKindGeo,
|
|
domain.MessageMediaKindVenue:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (r *Router) mentionedUserIDsFromDomainMessage(ctx context.Context, currentUserID int64, message string, entities []domain.MessageEntity) ([]int64, error) {
|
|
if r.deps.Users == nil {
|
|
return nil, nil
|
|
}
|
|
identity, _ := r.deps.Users.(UserIdentityService)
|
|
seen := make(map[int64]struct{})
|
|
out := make([]int64, 0)
|
|
add := func(id int64) {
|
|
if id == 0 {
|
|
return
|
|
}
|
|
if _, ok := seen[id]; ok {
|
|
return
|
|
}
|
|
seen[id] = struct{}{}
|
|
out = append(out, id)
|
|
}
|
|
for _, entity := range entities {
|
|
if entity.Type == domain.MessageEntityMentionName {
|
|
add(entity.UserID)
|
|
if len(out) >= domain.MaxChannelMentionRecipients {
|
|
return out, nil
|
|
}
|
|
}
|
|
}
|
|
if identity != nil {
|
|
for _, username := range extractMentionUsernames(message, domain.MaxChannelMentionRecipients-len(out)) {
|
|
user, found, err := identity.ResolveUsername(ctx, currentUserID, username)
|
|
if err != nil {
|
|
return nil, internalErr()
|
|
}
|
|
if found {
|
|
add(user.ID)
|
|
}
|
|
if len(out) >= domain.MaxChannelMentionRecipients {
|
|
return out, nil
|
|
}
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (r *Router) onMessagesSetBotShippingResults(ctx context.Context, req *tg.MessagesSetBotShippingResultsRequest) (bool, error) {
|
|
if _, err := r.callerBotID(ctx); err != nil {
|
|
return false, err
|
|
}
|
|
return false, queryIDInvalidErr()
|
|
}
|
|
|
|
func (r *Router) onMessagesSetBotPrecheckoutResults(ctx context.Context, req *tg.MessagesSetBotPrecheckoutResultsRequest) (bool, error) {
|
|
if _, err := r.callerBotID(ctx); err != nil {
|
|
return false, err
|
|
}
|
|
return false, queryIDInvalidErr()
|
|
}
|