Files
trbot/plugins/saved_message/channel.go

404 lines
14 KiB
Go

package saved_message
import (
"fmt"
"strconv"
"strings"
"time"
"trbot/utils"
"trbot/utils/configs"
"trbot/utils/flaterr"
"trbot/utils/handler_params"
"trbot/utils/meilisearch_utils"
"trbot/utils/origin_info"
"trbot/utils/plugin_utils"
"trbot/utils/type/contain"
"github.com/go-telegram/bot"
"github.com/go-telegram/bot/models"
"github.com/meilisearch/meilisearch-go"
"github.com/rs/zerolog"
)
var channelPubliclink string
var channelPrivateLink string
func channelSaveMessageHandler(opts *handler_params.Message) error {
logger := zerolog.Ctx(opts.Ctx).
With().
Str("pluginName", "Saved Message").
Str(utils.GetCurrentFuncName()).
Dict(utils.GetUserDict(opts.Message.From)).
Dict(utils.GetChatDict(&opts.Message.Chat)).
Logger()
if meilisearchClient == nil {
logger.Warn().
Int("messageID", opts.Message.ID).
Msg("Meilisearch client is not initialized, skipping indexing")
} else {
msgData := meilisearch_utils.BuildMessageData(opts.Ctx, opts.Thebot, opts.Message)
var messageLength int
if opts.Message.Caption != "" {
messageLength = utf16Length(opts.Message.Caption)
msgData.Entities = opts.Message.CaptionEntities
} else if opts.Message.Text != "" {
messageLength = utf16Length(opts.Message.Text)
msgData.Entities = opts.Message.Entities
}
// 若字符长度大于设定的阈值,添加折叠样式引用再保存,如果是全文引用但不折叠,改成折叠样式
if messageLength > textExpandableLength && (len(msgData.Entities) == 0 || msgData.Entities[0].Type == models.MessageEntityTypeBlockquote && msgData.Entities[0].Length == messageLength) {
msgData.Entities = []models.MessageEntity{{
Type: models.MessageEntityTypeExpandableBlockquote,
Offset: 0,
Length: messageLength,
}}
}
msgData.OriginInfo = origin_info.GetOriginInfo(opts.Message)
taskinfo, err := meilisearchClient.Index(SavedMessageList.ChannelIDStr()).AddDocuments(msgData)
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Msg("failed to send add document request to Meilisearch")
return fmt.Errorf("failed to send add document request to Meilisearch: %w", err)
} else {
task, err := meilisearch_utils.WaitForTask(opts.Ctx, &meilisearchClient, taskinfo.TaskUID, time.Second * 1)
if err != nil {
return fmt.Errorf("wait for add document task failed: %w", err)
} else if task.Status != meilisearch.TaskStatusSucceeded {
return fmt.Errorf("failed to add document: %s", task.Error.Message)
}
}
if SavedMessageList.NoticeChatID != 0 {
_, err = opts.Thebot.SendMessage(opts.Ctx, &bot.SendMessageParams{
ChatID: SavedMessageList.NoticeChatID,
Text: "已经在频道中保存了一个新消息,要为它添加关键词吗?",
DisableNotification: true,
ReplyParameters: &models.ReplyParameters{
MessageID: opts.Message.ID,
ChatID: SavedMessageList.ChannelID,
},
ReplyMarkup: &models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{
{
{
Text: "添加描述",
CallbackData: fmt.Sprintf("savedmsg_channel_add_desc_%d", opts.Message.ID),
},
{
Text: "查看消息",
URL: utils.MsgLinkPrivate(SavedMessageList.ChannelID, opts.Message.ID),
},
},
{{
Text: "忽略",
CallbackData: "delete_this_message",
}},
}},
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "channel message saved").
Msg(flaterr.SendMessage.Str())
return fmt.Errorf(flaterr.SendMessage.Fmt(), "channel message saved", err)
}
}
}
return nil
}
func channelMetadataHandler(opts *handler_params.Message) error {
logger := zerolog.Ctx(opts.Ctx).
With().
Str("pluginName", "Saved Message").
Str(utils.GetCurrentFuncName()).
Dict(utils.GetUserDict(opts.Message.From)).
Dict(utils.GetChatDict(&opts.Message.Chat)).
Logger()
var handlerErr flaterr.MultErr
var msgIDString string
var msgData meilisearch_utils.MessageData
indexManager := meilisearchClient.Index(SavedMessageList.ChannelIDStr())
if !contain.Int64(opts.Message.From.ID, configs.BotConfig.AdminIDs...) {
_, err := opts.Thebot.SendMessage(opts.Ctx, &bot.SendMessageParams{
ChatID: opts.Message.Chat.ID,
Text: "??",
ReplyParameters: &models.ReplyParameters{ MessageID: opts.Message.ID },
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "no permission to edit metadata").
Msg(flaterr.SendMessage.Str())
handlerErr.Addt(flaterr.SendMessage, "no permission to edit metadata", err)
}
} else {
if len(opts.Fields) > 1 {
msgIDString = opts.Fields[1]
} else if strings.HasPrefix(opts.Message.Text, channelPubliclink) {
msgIDString = strings.TrimPrefix(opts.Message.Text, channelPubliclink + "/")
} else if strings.HasPrefix(opts.Message.Text, channelPrivateLink) {
msgIDString = strings.TrimPrefix(opts.Message.Text, channelPrivateLink + "/")
}
if msgIDString != "" {
err := indexManager.GetDocument(msgIDString, nil, &msgData)
if err != nil {
if err.(*meilisearch.Error).MeilisearchApiError.Code == "document_not_found" {
_, err = opts.Thebot.SendMessage(opts.Ctx, &bot.SendMessageParams{
ChatID: opts.Message.Chat.ID,
Text: "未找到该消息,请检查消息 ID 是否正确",
ReplyParameters: &models.ReplyParameters{ MessageID: opts.Message.ID },
LinkPreviewOptions: &models.LinkPreviewOptions{
},
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "document not found in index").
Msg(flaterr.SendMessage.Str())
handlerErr.Addt(flaterr.SendMessage, "document not found in index", err)
}
} else {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Msg("Failed to get message from index")
handlerErr.Addf("failed to get message from index: %w", err)
_, err = opts.Thebot.SendMessage(opts.Ctx, &bot.SendMessageParams{
ChatID: opts.Message.Chat.ID,
Text: "获取消息失败,请稍后再试",
ReplyParameters: &models.ReplyParameters{ MessageID: opts.Message.ID },
LinkPreviewOptions: &models.LinkPreviewOptions{
},
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "failed to get message notice").
Msg(flaterr.SendMessage.Str())
handlerErr.Addt(flaterr.SendMessage, "failed to get message notice", err)
}
}
} else {
var pendingMessage string
pendingMessage += fmt.Sprintf("ID: %d\n", msgData.ID)
pendingMessage += fmt.Sprintf("类型: %s\n", msgData.Type)
if msgData.FileID != "" {
pendingMessage += fmt.Sprintf("文件 ID: %s\n", msgData.FileID)
}
if msgData.FileName != "" {
pendingMessage += fmt.Sprintf("文件名: %s\n", msgData.FileName)
}
if msgData.FileTitle != "" {
pendingMessage += fmt.Sprintf("文件标题: %s\n", msgData.FileTitle)
}
if msgData.Text != "" {
pendingMessage += fmt.Sprintf("文本: <blockquote expandable>%s</blockquote>\n", msgData.Text)
}
if msgData.Desc != "" {
pendingMessage += fmt.Sprintf("描述: <blockquote expandable>%s</blockquote>\n", msgData.Desc)
}
_, err = opts.Thebot.SendMessage(opts.Ctx, &bot.SendMessageParams{
ChatID: opts.Message.Chat.ID,
Text: pendingMessage,
ParseMode: models.ParseModeHTML,
ReplyMarkup: buildMessageDataKeyboard(msgData),
ReplyParameters: &models.ReplyParameters{ MessageID: opts.Message.ID },
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "channel document info").
Msg(flaterr.SendMessage.Str())
handlerErr.Addt(flaterr.SendMessage, "channel document info", err)
}
logger.Info().
Interface("msgData", msgData).
Msg("Retrieved message data from index")
}
}
}
return handlerErr.Flat()
}
func buildMessageDataKeyboard(msgData meilisearch_utils.MessageData) models.ReplyMarkup {
var button [][]models.InlineKeyboardButton
button = append(button, []models.InlineKeyboardButton{
{
Text: "修改描述",
CallbackData: fmt.Sprintf("savedmsg_channel_add_desc_%d", msgData.ID),
},
{
Text: "查看消息",
URL: utils.MsgLinkPrivate(SavedMessageList.ChannelID, msgData.ID),
},
// {
// Text: "移除来源信息",
// CallbackData: fmt.Sprintf("savedmsg_remove_origin_%d", msgData.MsgID),
// },
// {
// Text: "删除消息",
// CallbackData: fmt.Sprintf("delete_message:%d", msgData.MsgID),
// },
})
return &models.InlineKeyboardMarkup{
InlineKeyboard: button,
}
}
func channelCallbackHandler(opts *handler_params.CallbackQuery) error {
logger := zerolog.Ctx(opts.Ctx).
With().
Str("pluginName", "Saved Message").
Str(utils.GetCurrentFuncName()).
Dict(utils.GetUserDict(&opts.CallbackQuery.From)).
Str("callbackData", opts.CallbackQuery.Data).
Logger()
if strings.HasPrefix(opts.CallbackQuery.Data, "savedmsg_channel_add_desc_") {
msgIDString := strings.TrimPrefix(opts.CallbackQuery.Data, "savedmsg_channel_add_desc_")
msgID, err := strconv.Atoi(msgIDString)
if err != nil {
logger.Error().
Err(err).
Msg("Failed to parse message ID from callback data")
return fmt.Errorf("failed to parse message ID: %w", err)
}
msg, err := opts.Thebot.EditMessageText(opts.Ctx, &bot.EditMessageTextParams{
ChatID: opts.CallbackQuery.Message.Message.Chat.ID,
MessageID: opts.CallbackQuery.Message.Message.ID,
Text: "请输入新的描述信息,或发送 /cancel 取消编辑。",
ReplyMarkup: &models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{{{
Text: "查看消息",
URL: utils.MsgLinkPrivate(SavedMessageList.ChannelID, editingMessageID),
}}}},
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.CallbackQuery.Message.Message.ID).
Str("content", "send text to edit description").
Msg(flaterr.EditMessageText.Str())
return fmt.Errorf(flaterr.EditMessageText.Fmt(), "send text to edit description", err)
} else {
stateMessageID = msg.ID
editingMessageID = msgID
plugin_utils.AddMessageStateHandler(plugin_utils.MessageStateHandler{
ForChatID: opts.CallbackQuery.Message.Message.Chat.ID,
PluginName: "edit_description_state",
Remaining: 1,
MessageHandler: editDescriptionHandler,
CancelHandler: func(opts *handler_params.Message) error {
_, err := opts.Thebot.EditMessageText(opts.Ctx, &bot.EditMessageTextParams{
ChatID: opts.Message.Chat.ID,
MessageID: stateMessageID,
Text: "已取消编辑",
ReplyMarkup: &models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{{{
Text: "查看消息",
URL: utils.MsgLinkPrivate(SavedMessageList.ChannelID, editingMessageID),
}}}},
})
stateMessageID = 0
editingMessageID = 0
if err != nil {
return fmt.Errorf(flaterr.EditMessageText.Fmt(), "cancel edit description notice", err)
}
_, err = opts.Thebot.DeleteMessage(opts.Ctx, &bot.DeleteMessageParams{
ChatID: opts.Message.Chat.ID,
MessageID: opts.Message.ID,
})
if err != nil {
return fmt.Errorf(flaterr.DeleteMessage.Fmt(), "cancel edit description command", err)
}
return nil
},
})
}
}
return nil
}
func editDescriptionHandler(opts *handler_params.Message) error {
logger := zerolog.Ctx(opts.Ctx).
With().
Str("pluginName", "Saved Message").
Str(utils.GetCurrentFuncName()).
Dict(utils.GetUserDict(opts.Message.From)).
Dict(utils.GetChatDict(&opts.Message.Chat)).
Logger()
taskinfo, err := meilisearchClient.Index(SavedMessageList.ChannelIDStr()).UpdateDocuments(&meilisearch_utils.MessageData{
ID: editingMessageID,
Desc: opts.Message.Text,
})
if err != nil {
logger.Error().
Err(err).
Msg("Failed to send update document description request to Meilisearch")
return fmt.Errorf("failed to send update document description request to Meilisearch: %w", err)
} else {
task, err := meilisearch_utils.WaitForTask(opts.Ctx, &meilisearchClient, taskinfo.TaskUID, time.Second * 1)
if err != nil {
return fmt.Errorf("wait for update document description failed: %w", err)
} else if task.Status != meilisearch.TaskStatusSucceeded {
return fmt.Errorf("failed to update document description: %s", task.Error.Message)
}
_, err = opts.Thebot.EditMessageText(opts.Ctx, &bot.EditMessageTextParams{
ChatID: opts.Message.Chat.ID,
MessageID: stateMessageID,
Text: fmt.Sprintf("消息描述已更新为:[ %s ]", opts.Message.Text),
ReplyMarkup: &models.InlineKeyboardMarkup{ InlineKeyboard: [][]models.InlineKeyboardButton{{{
Text: "查看消息",
URL: utils.MsgLinkPrivate(SavedMessageList.ChannelID, editingMessageID),
}}}},
})
editingMessageID = 0 // Reset the editing message ID after successful update
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "description updated notice").
Msg(flaterr.SendMessage.Str())
return fmt.Errorf(flaterr.SendMessage.Fmt(), "description updated notice", err)
}
_, err = opts.Thebot.DeleteMessage(opts.Ctx, &bot.DeleteMessageParams{
ChatID: opts.Message.Chat.ID,
MessageID: opts.Message.ID,
})
if err != nil {
logger.Error().
Err(err).
Int("messageID", opts.Message.ID).
Str("content", "update description text message").
Msg(flaterr.DeleteMessage.Str())
return fmt.Errorf(flaterr.DeleteMessage.Fmt(), "update description text message", err)
}
}
return nil
}