911 lines
35 KiB
Go
911 lines
35 KiB
Go
package main
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"trbot/database"
|
||
"trbot/database/db_struct"
|
||
"trbot/utils"
|
||
"trbot/utils/configs"
|
||
"trbot/utils/flaterr"
|
||
"trbot/utils/handler_params"
|
||
"trbot/utils/inline_utils"
|
||
"trbot/utils/plugin_utils"
|
||
"trbot/utils/type/contain"
|
||
"trbot/utils/type/message_utils"
|
||
"trbot/utils/type/update_utils"
|
||
|
||
"github.com/go-telegram/bot"
|
||
"github.com/go-telegram/bot/models"
|
||
"github.com/rs/zerolog"
|
||
)
|
||
|
||
func defaultHandler(ctx context.Context, thebot *bot.Bot, update *models.Update) {
|
||
defer utils.PanicCatcher(ctx, "defaultHandler")
|
||
logger := zerolog.Ctx(ctx)
|
||
|
||
var opts = handler_params.Update{
|
||
Ctx: ctx,
|
||
Thebot: thebot,
|
||
Update: update,
|
||
// ChatInfo will fill in `database.RecordData()` function
|
||
}
|
||
|
||
// 判断更新类型
|
||
updateType := update_utils.GetUpdateType(update)
|
||
|
||
// 记录数据和读取信息
|
||
database.RecordData(&opts, updateType)
|
||
|
||
// Debug or Trace Level
|
||
// 消息日志,因为比较占用资源,先判断日志等级
|
||
if zerolog.GlobalLevel() <= zerolog.InfoLevel {
|
||
switch {
|
||
case updateType.Message:
|
||
// 正常消息
|
||
if update.Message.Photo != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Str("caption", update.Message.Caption).
|
||
Str("photoID", update.Message.Photo[len(update.Message.Photo)-1].FileID).
|
||
Msg("photoMessage")
|
||
} else if update.Message.Sticker != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Dict("sticker", zerolog.Dict().
|
||
Str("emoji", update.Message.Sticker.Emoji).
|
||
Str("setname", update.Message.Sticker.SetName).
|
||
Str("fileID", update.Message.Sticker.FileID),
|
||
).
|
||
Msg("stickerMessage")
|
||
} else if update.Message.Video != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Str("caption", update.Message.Caption).
|
||
Dict("video", zerolog.Dict().
|
||
Str("type", update.Message.Video.MimeType).
|
||
Int("duration", update.Message.Video.Duration).
|
||
Str("fileID", update.Message.Video.FileID),
|
||
).
|
||
Msg("videoMessage")
|
||
} else if update.Message.Animation != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Dict("animation", zerolog.Dict().
|
||
Int("duration", update.Message.Animation.Duration).
|
||
Str("fileID", update.Message.Animation.FileID),
|
||
).
|
||
Msg("gifMessage")
|
||
} else if update.Message.Document != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Str("caption", update.Message.Caption).
|
||
Dict("document", zerolog.Dict().
|
||
Str("fileName", update.Message.Document.FileName).
|
||
Str("fileID", update.Message.Document.FileID),
|
||
).
|
||
Msg("documentMessage")
|
||
} else if update.Message.PinnedMessage != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Msg("pinMessage")
|
||
} else if update.Message.NewChatMembers != nil {
|
||
for _, chatMember := range update.Message.NewChatMembers {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(&chatMember)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Msg("newChatMemberMessage")
|
||
}
|
||
} else if update.Message.LeftChatMember != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.Message.LeftChatMember)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Msg("leftChatMemberMessage")
|
||
} else {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.Message)).
|
||
Dict(utils.GetChatDict(&update.Message.Chat)).
|
||
Int("messageID", update.Message.ID).
|
||
Str("text", update.Message.Text).
|
||
Str("type", message_utils.GetMessageType(update.Message).Str()).
|
||
Msg("normalMessage")
|
||
}
|
||
case updateType.EditedMessage:
|
||
// 私聊或群组消息被编辑
|
||
if update.EditedMessage.Caption != "" {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.EditedMessage)).
|
||
Dict(utils.GetChatDict(&update.EditedMessage.Chat)).
|
||
Int("messageID", update.EditedMessage.ID).
|
||
Str("editedCaption", update.EditedMessage.Caption).
|
||
Msg("editedMessage")
|
||
} else {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.EditedMessage)).
|
||
Dict(utils.GetChatDict(&update.EditedMessage.Chat)).
|
||
Int("messageID", update.EditedMessage.ID).
|
||
Str("editedText", update.EditedMessage.Text).
|
||
Msg("editedMessage")
|
||
}
|
||
case updateType.InlineQuery:
|
||
// inline 查询
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.InlineQuery.From)).
|
||
Str("query", update.InlineQuery.Query).
|
||
Msg("inline request")
|
||
case updateType.ChosenInlineResult:
|
||
// inline 查询结果被选择
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(&update.ChosenInlineResult.From)).
|
||
Str("query", update.ChosenInlineResult.Query).
|
||
Str("resultID", update.ChosenInlineResult.ResultID).
|
||
Msg("chosen inline result")
|
||
case updateType.CallbackQuery:
|
||
// replymarkup 回调
|
||
var chat = zerolog.Dict()
|
||
if update.CallbackQuery.Message.Message != nil {
|
||
// some time `update.CallbackQuery.Message` will be nil
|
||
_, chat = utils.GetChatDict(&update.CallbackQuery.Message.Message.Chat)
|
||
}
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(&update.CallbackQuery.From)).
|
||
Dict("chat", chat).
|
||
Str("callbackQueryData", update.CallbackQuery.Data).
|
||
Msg("callback query")
|
||
case updateType.MessageReaction:
|
||
// 私聊或群组表情回应
|
||
if len(update.MessageReaction.OldReaction) > 0 {
|
||
for i, oldReaction := range update.MessageReaction.OldReaction {
|
||
if oldReaction.ReactionTypeEmoji != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("removedEmoji", oldReaction.ReactionTypeEmoji.Emoji).
|
||
Str("emojiType", string(oldReaction.ReactionTypeEmoji.Type)).
|
||
Int("count", i + 1).
|
||
Msg("removed emoji reaction")
|
||
} else if oldReaction.ReactionTypeCustomEmoji != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("removedEmojiID", oldReaction.ReactionTypeCustomEmoji.CustomEmojiID).
|
||
Str("emojiType", string(oldReaction.ReactionTypeCustomEmoji.Type)).
|
||
Int("count", i + 1).
|
||
Msg("removed custom emoji reaction")
|
||
} else if oldReaction.ReactionTypePaid != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("emojiType", string(oldReaction.ReactionTypePaid.Type)).
|
||
Int("count", i + 1).
|
||
Msg("removed paid emoji reaction")
|
||
}
|
||
}
|
||
}
|
||
if len(update.MessageReaction.NewReaction) > 0 {
|
||
for i, newReaction := range update.MessageReaction.NewReaction {
|
||
if newReaction.ReactionTypeEmoji != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("addEmoji", newReaction.ReactionTypeEmoji.Emoji).
|
||
Str("emojiType", string(newReaction.ReactionTypeEmoji.Type)).
|
||
Int("count", i + 1).
|
||
Msg("add emoji reaction")
|
||
} else if newReaction.ReactionTypeCustomEmoji != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("addEmojiID", newReaction.ReactionTypeCustomEmoji.CustomEmojiID).
|
||
Str("emojiType", string(newReaction.ReactionTypeCustomEmoji.Type)).
|
||
Int("count", i + 1).
|
||
Msg("add custom emoji reaction")
|
||
} else if newReaction.ReactionTypePaid != nil {
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(update.MessageReaction.User)).
|
||
Dict(utils.GetChatDict(&update.MessageReaction.Chat)).
|
||
Int("messageID", update.MessageReaction.MessageID).
|
||
Str("emojiType", string(newReaction.ReactionTypePaid.Type)).
|
||
Int("count", i + 1).
|
||
Msg("add paid emoji reaction")
|
||
}
|
||
}
|
||
}
|
||
case updateType.MessageReactionCount:
|
||
// 频道消息表情回应数量
|
||
var emoji = zerolog.Dict()
|
||
var customEmoji = zerolog.Dict()
|
||
var paid = zerolog.Dict()
|
||
for _, n := range update.MessageReactionCount.Reactions {
|
||
switch n.Type.Type {
|
||
case models.ReactionTypeTypeEmoji:
|
||
emoji.Dict(n.Type.ReactionTypeEmoji.Emoji, zerolog.Dict().
|
||
// Str("type", string(n.Type.ReactionTypeEmoji.Type)).
|
||
// Str("emoji", n.Type.ReactionTypeEmoji.Emoji).
|
||
Int("count", n.TotalCount),
|
||
)
|
||
case models.ReactionTypeTypeCustomEmoji:
|
||
customEmoji.Dict(n.Type.ReactionTypeCustomEmoji.CustomEmojiID, zerolog.Dict().
|
||
// Str("type", string(n.Type.ReactionTypeCustomEmoji.Type)).
|
||
// Str("customEmojiID", n.Type.ReactionTypeCustomEmoji.CustomEmojiID).
|
||
Int("count", n.TotalCount),
|
||
)
|
||
case models.ReactionTypeTypePaid:
|
||
paid.Dict(n.Type.ReactionTypePaid.Type, zerolog.Dict().
|
||
// Str("type", n.Type.ReactionTypePaid.Type).
|
||
Int("count", n.TotalCount),
|
||
)
|
||
}
|
||
|
||
}
|
||
|
||
logger.Info().
|
||
Dict(utils.GetChatDict(&update.MessageReactionCount.Chat)).
|
||
Dict("reactions", zerolog.Dict().
|
||
Dict("emoji", emoji).
|
||
Dict("customEmoji", customEmoji).
|
||
Dict("paid", paid),
|
||
).
|
||
Int("messageID", update.MessageReactionCount.MessageID).
|
||
Msg("emoji reaction count updated")
|
||
case updateType.ChannelPost:
|
||
// 频道信息
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.ChannelPost)).
|
||
Dict(utils.GetChatDict(&update.ChannelPost.Chat)).
|
||
Str("text", update.ChannelPost.Text).
|
||
Int("messageID", update.ChannelPost.ID).
|
||
Msg("channel post")
|
||
if update.ChannelPost.ViaBot != nil {
|
||
// 在频道中由 bot 发送
|
||
_, viaBot := utils.GetUserDict(update.ChannelPost.ViaBot)
|
||
logger.Info().
|
||
Dict("viaBot", viaBot).
|
||
Dict(utils.GetChatDict(&update.ChannelPost.Chat)).
|
||
Str("text", update.ChannelPost.Text).
|
||
Int("messageID", update.ChannelPost.ID).
|
||
Msg("channel post send via bot")
|
||
}
|
||
case updateType.EditedChannelPost:
|
||
// 频道中编辑过的消息
|
||
if update.EditedChannelPost.Caption != "" {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.EditedChannelPost)).
|
||
Dict(utils.GetChatDict(&update.EditedChannelPost.Chat)).
|
||
Int("messageID", update.EditedChannelPost.ID).
|
||
Str("editedCaption", update.EditedChannelPost.Caption).
|
||
Msg("edited channel post caption")
|
||
} else {
|
||
logger.Info().
|
||
Dict(utils.GetUserOrSenderChatDict(update.EditedChannelPost)).
|
||
Dict(utils.GetChatDict(&update.EditedChannelPost.Chat)).
|
||
Int("messageID", update.EditedChannelPost.ID).
|
||
Str("editedText", update.EditedChannelPost.Text).
|
||
Msg("edited channel post")
|
||
}
|
||
case updateType.ChatMember:
|
||
logger.Info().
|
||
Dict(utils.GetUserDict(&update.ChatMember.From)).
|
||
Dict(utils.GetChatDict(&update.ChatMember.Chat)).
|
||
Str("oldType", string(update.ChatMember.OldChatMember.Type)).
|
||
Str("newType", string(update.ChatMember.NewChatMember.Type)).
|
||
Str("notice", "the user field may not be the actual user whose status was changed").
|
||
Msg("chatMemberUpdated")
|
||
default:
|
||
// 其他没有加入的更新类型
|
||
logger.Warn().
|
||
Str("updateType", updateType.Str()).
|
||
Msg("Receive a no tagged update type")
|
||
}
|
||
}
|
||
|
||
// 根据更新类型调用相应的处理函数
|
||
switch {
|
||
case updateType.Message:
|
||
messageHandler(&handler_params.Message{
|
||
Ctx: opts.Ctx,
|
||
Thebot: opts.Thebot,
|
||
Message: opts.Update.Message,
|
||
ChatInfo: opts.ChatInfo,
|
||
Fields: strings.Fields(opts.Update.Message.Text),
|
||
})
|
||
case updateType.ChannelPost:
|
||
messageHandler(&handler_params.Message{
|
||
Ctx: opts.Ctx,
|
||
Thebot: opts.Thebot,
|
||
Message: opts.Update.ChannelPost,
|
||
ChatInfo: opts.ChatInfo,
|
||
Fields: strings.Fields(opts.Update.ChannelPost.Text),
|
||
})
|
||
case updateType.InlineQuery:
|
||
inlineHandler(&handler_params.InlineQuery{
|
||
Ctx: opts.Ctx,
|
||
Thebot: opts.Thebot,
|
||
InlineQuery: opts.Update.InlineQuery,
|
||
ChatInfo: opts.ChatInfo,
|
||
Fields: strings.Fields(opts.Update.InlineQuery.Query),
|
||
})
|
||
case updateType.CallbackQuery:
|
||
callbackQueryHandler(&handler_params.CallbackQuery{
|
||
Ctx: opts.Ctx,
|
||
Thebot: opts.Thebot,
|
||
CallbackQuery: opts.Update.CallbackQuery,
|
||
ChatInfo: opts.ChatInfo,
|
||
})
|
||
}
|
||
}
|
||
|
||
// 处理所有信息请求的处理函数,触发条件为任何消息
|
||
func messageHandler(opts *handler_params.Message) {
|
||
defer utils.PanicCatcher(opts.Ctx, "messageHandler")
|
||
logger := zerolog.Ctx(opts.Ctx).
|
||
With().
|
||
Dict(utils.GetUserDict(opts.Message.From)).
|
||
Dict(utils.GetChatDict(&opts.Message.Chat)).
|
||
Str("text", opts.Message.Text).
|
||
Str("caption", opts.Message.Caption).
|
||
Logger()
|
||
|
||
if plugin_utils.RunMessageStateHandler(opts) {
|
||
// 如果状态处理函数返回 true,则表示已经处理了该消息,直接返回
|
||
return
|
||
}
|
||
|
||
// 判断是否为命令
|
||
if isProcessed, err := plugin_utils.RunCommandHandlers(opts); isProcessed {
|
||
if err != nil {
|
||
logger.
|
||
Err(err).
|
||
Msg("Error when running command handler")
|
||
}
|
||
return
|
||
}
|
||
|
||
// handler by message type
|
||
if err := plugin_utils.RunByMessageTypeHandlers(opts); err != nil {
|
||
logger.Error().
|
||
Err(err).
|
||
Msg("Error when running by message type handler")
|
||
}
|
||
|
||
// handler by message chat ID
|
||
if count, err := plugin_utils.RunByMessageChatIDHandlers(opts); err != nil {
|
||
logger.Error().
|
||
Err(err).
|
||
Int("handlerRunCount", count).
|
||
Msg("Error when running by message chat ID handlers")
|
||
}
|
||
}
|
||
|
||
// 处理 inline 模式下的请求
|
||
func inlineHandler(opts *handler_params.InlineQuery) {
|
||
defer utils.PanicCatcher(opts.Ctx, "inlineHandler")
|
||
inlineLogger := zerolog.Ctx(opts.Ctx).
|
||
With().
|
||
Dict(utils.GetUserDict(opts.InlineQuery.From)).
|
||
Str("query", opts.InlineQuery.Query).
|
||
Logger()
|
||
|
||
IsAdmin := contain.Int64(opts.InlineQuery.From.ID, configs.BotConfig.AdminIDs...)
|
||
parsedQuery := inline_utils.ParseInlineFields(opts.Fields)
|
||
|
||
if strings.HasPrefix(opts.InlineQuery.Query, configs.BotConfig.InlineSubCommandSymbol) {
|
||
// 用户输入了分页符号和一些字符,判断接着的命令是否正确,正确则交给对应的插件处理,否则显示命令菜单
|
||
|
||
if opts.Fields[0] != configs.BotConfig.InlineSubCommandSymbol {
|
||
// 插件处理完后返回全部列表,由设定好的函数进行分页输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if opts.Fields[0][1:] == plugin.Command {
|
||
slogger := inlineLogger.With().
|
||
Str("handlerCommand", plugin.Command).
|
||
Str("handlerType", "returnResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit inline handler")
|
||
ResultList := plugin.InlineHandler(opts)
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: inline_utils.ResultPagination(parsedQuery, ResultList),
|
||
IsPersonal: true,
|
||
CacheTime: 0,
|
||
})
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Str("content", "sub inline handler").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
// 本来想写一个发生错误后再给用户回答一个错误信息,让用户可以点击发送,结果同一个 ID 的 inlineQuery 只能回答一次
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit inline handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 完全由插件控制输出,若回答请求时列表数量超过 50 项会出错,无法回应用户请求
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineManualHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if opts.Fields[0][1:] == plugin.Command {
|
||
slogger := inlineLogger.With().
|
||
Str("handlerCommand", plugin.Command).
|
||
Str("handlerType", "manuallyAnswerResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit inline manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in inline manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit inline manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 符合命令前缀,完全由插件自行控制输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlinePrefixHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if strings.HasPrefix(opts.InlineQuery.Query, configs.BotConfig.InlineSubCommandSymbol + plugin.PrefixCommand) {
|
||
slogger := inlineLogger.With().
|
||
Str("handlerPrefixCommand", plugin.PrefixCommand).
|
||
Str("handlerType", "manuallyAnswerResult_PrefixCommand").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit inline prefix manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in inline prefix manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit inline prefix manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 没有触发任何 handler
|
||
inlineLogger.Debug().Msg("No any handler is hit")
|
||
}
|
||
|
||
// 创建变量存放提示和命令菜单
|
||
var results []models.InlineQueryResult
|
||
|
||
if opts.Fields[0] == configs.BotConfig.InlineSubCommandSymbol {
|
||
results = append(results, &models.InlineQueryResultArticle{
|
||
ID: "needCommand",
|
||
Title: "请不要点击列表中的命令",
|
||
Description: fmt.Sprintf("由于限制,您需要继续输入完整的命令才能使用,例如 %ssaved (带 %s 号前缀)", configs.BotConfig.InlineSubCommandSymbol, configs.BotConfig.InlineSubCommandSymbol),
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "请不要点击选单中的命令..." },
|
||
})
|
||
} else {
|
||
results = append(results, &models.InlineQueryResultArticle{
|
||
ID: "keepInput",
|
||
Title: "请继续输入下方您想使用的命令",
|
||
Description: "若您想在命令中使用关键词搜索,请在命令和关键词之间使用空格分开",
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "请不要点击选单中的命令..." },
|
||
})
|
||
}
|
||
|
||
// 添加匹配输入的命令列表
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineCommandList {
|
||
if !IsAdmin && plugin.Attr.IsHideInCommandList { continue }
|
||
if strings.HasPrefix(plugin.Command, parsedQuery.SubCommand) {
|
||
var description string
|
||
if plugin.Command == opts.ChatInfo.DefaultInlinePlugin { description += "您的默认命令 | " }
|
||
if plugin.Command == configs.BotConfig.InlineDefaultHandler { description += "机器人默认命令 | " }
|
||
|
||
if plugin.Attr.IsOnlyAllowAdmin { description += "管理员 | " }
|
||
if plugin.Attr.IsHideInCommandList { description += "隐藏 | " }
|
||
|
||
results = append(results, &models.InlineQueryResultArticle{
|
||
ID: "inlineMenu_" + plugin.Command,
|
||
Title: plugin.Command,
|
||
Description: description + plugin.Description,
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "请不要点击选单中的命令..." },
|
||
})
|
||
}
|
||
}
|
||
|
||
// 没有匹配的命令
|
||
if len(results) == 1 {
|
||
if opts.Fields[0] != configs.BotConfig.InlineSubCommandSymbol {
|
||
results = []models.InlineQueryResult{&models.InlineQueryResultArticle{
|
||
ID: "noThisInlineCommand",
|
||
Title: fmt.Sprintf("不存在的命令 [%s]", opts.Fields[0]),
|
||
Description: "请检查命令是否正确。若要使用分页、分类符号或搜索关键词,请使用空格将它们分开",
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "您在使用 inline 模式时没有输入正确的命令..." },
|
||
}}
|
||
} else {
|
||
results = []models.InlineQueryResult{&models.InlineQueryResultArticle{
|
||
ID: "noInlineCommand",
|
||
Title: "没有可用的命令",
|
||
Description: "此机器人当前并没有配置任何 inline 模式的插件或命令",
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "这个机器人当前并没有配置任何 inline 模式的插件或命令..." },
|
||
}}
|
||
}
|
||
}
|
||
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: results,
|
||
IsPersonal: true,
|
||
CacheTime: 1,
|
||
Button: &models.InlineQueryResultsButton{
|
||
Text: "点击此处修改默认命令",
|
||
StartParameter: "via-inline_change-inline-command",
|
||
},
|
||
})
|
||
if err != nil {
|
||
inlineLogger.Error().
|
||
Err(err).
|
||
Str("content", "bot inline handler list").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
}
|
||
} else {
|
||
// inline query 不以命令符号开头,检查是否有默认 handler
|
||
if opts.ChatInfo.DefaultInlinePlugin != "" {
|
||
// 来自用户设定的默认命令
|
||
defaultHandlerLogger := inlineLogger.With().
|
||
Str("userDefaultHandlerCommand", opts.ChatInfo.DefaultInlinePlugin).
|
||
Logger()
|
||
|
||
// 插件处理完后返回全部列表,由设定好的函数进行分页输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if opts.ChatInfo.DefaultInlinePlugin == plugin.Command {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "returnResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit user default inline handler")
|
||
resultList := plugin.InlineHandler(opts)
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: inline_utils.ResultPagination(parsedQuery, resultList),
|
||
IsPersonal: true,
|
||
CacheTime: 0,
|
||
})
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Str("content", "user default inline handler result").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
// 本来想写一个发生错误后再给用户回答一个错误信息,让用户可以点击发送,结果同一个 ID 的 inlineQuery 只能回答一次
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit user default inline handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 完全由插件控制输出,若回答请求时列表数量超过 50 项会出错,无法回应用户请求
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineManualHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if opts.ChatInfo.DefaultInlinePlugin == plugin.Command {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "manuallyAnswerResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit user default inline manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in user default inline manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit user default inline manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 符合命令前缀,完全由插件自行控制输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlinePrefixHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if opts.ChatInfo.DefaultInlinePlugin == plugin.PrefixCommand {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "manuallyAnswerResult_PrefixCommand").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit user default inline prefix manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in user inline prefix manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit user inline prefix manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 没有匹配到命令,提示不存在的命令
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: []models.InlineQueryResult{&models.InlineQueryResultArticle{
|
||
ID: "noInlinePlugin",
|
||
Title: fmt.Sprintf("不存在的默认命令 [%s]", opts.ChatInfo.DefaultInlinePlugin),
|
||
Description: "或许是因为管理员已经移除了这个插件,请重新选择一个默认命令",
|
||
InputMessageContent: &models.InputTextMessageContent{ MessageText: "此默认命令无效,或许是因为管理员已经移除了这个插件,请重新选择一个默认命令" },
|
||
}},
|
||
Button: &models.InlineQueryResultsButton{
|
||
Text: "点击此处修改默认命令",
|
||
StartParameter: "via-inline_change-inline-command",
|
||
},
|
||
})
|
||
if err != nil {
|
||
defaultHandlerLogger.Error().
|
||
Err(err).
|
||
Str("content", "invalid user default inline handler").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
}
|
||
return
|
||
} else if configs.BotConfig.InlineDefaultHandler != "" {
|
||
// 全局设定里设定的默认命令
|
||
defaultHandlerLogger := inlineLogger.With().
|
||
Str("botDefaultHandlerCommand", configs.BotConfig.InlineDefaultHandler).
|
||
Logger()
|
||
|
||
// 插件处理完后返回全部列表,由设定好的函数进行分页输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if configs.BotConfig.InlineDefaultHandler == plugin.Command {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "returnResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit bot default inline handler")
|
||
resultList := plugin.InlineHandler(opts)
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: inline_utils.ResultPagination(parsedQuery, resultList),
|
||
IsPersonal: true,
|
||
CacheTime: 0,
|
||
Button: &models.InlineQueryResultsButton{
|
||
Text: fmt.Sprintf("输入 %s 号显示菜单,或点击此处修改默认命令", configs.BotConfig.InlineSubCommandSymbol),
|
||
StartParameter: "via-inline_change-inline-command",
|
||
},
|
||
})
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Str("content", "bot default inline handler result").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
// 本来想写一个发生错误后再给用户回答一个错误信息,让用户可以点击发送,结果同一个 ID 的 inlineQuery 只能回答一次
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit bot default inline handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 完全由插件控制输出,若回答请求时列表数量超过 50 项会出错,无法回应用户请求
|
||
for _, plugin := range plugin_utils.AllPlugins.InlineManualHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if configs.BotConfig.InlineDefaultHandler == plugin.Command {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "manuallyAnswerResult").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit bot default inline manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in bot default inline manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit bot default inline manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
// 符合命令前缀,完全由插件自行控制输出
|
||
for _, plugin := range plugin_utils.AllPlugins.InlinePrefixHandler {
|
||
if plugin.Attr.IsOnlyAllowAdmin && !IsAdmin { continue }
|
||
if configs.BotConfig.InlineDefaultHandler == plugin.PrefixCommand {
|
||
slogger := defaultHandlerLogger.With().
|
||
Str("handlerType", "manuallyAnswerResult_PrefixCommand").
|
||
Logger()
|
||
|
||
if plugin.InlineHandler != nil {
|
||
slogger.Info().Msg("Hit bot default inline prefix manual answer handler")
|
||
err := plugin.InlineHandler(opts)
|
||
if err != nil {
|
||
slogger.Error().
|
||
Err(err).
|
||
Msg("Error in bot default inline prefix manual answer handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit bot default inline prefix manual answer handler, but this handler function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
|
||
// 判断是否有足够的插件,以及默认插件是否存在
|
||
var pendingMessage string
|
||
if len(plugin_utils.AllPlugins.InlineCommandList) == 0 {
|
||
pendingMessage = "此 bot 似乎并没有使用任何 inline 模式插件,请联系管理员"
|
||
} else {
|
||
pendingMessage = fmt.Sprintf("您可以继续输入 %s 号来查看其他可用的命令", configs.BotConfig.InlineSubCommandSymbol)
|
||
}
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: []models.InlineQueryResult{&models.InlineQueryResultArticle{
|
||
ID: "invalidDefaultHandler",
|
||
Title: "管理员设定了无效的默认命令",
|
||
Description: pendingMessage,
|
||
InputMessageContent: &models.InputTextMessageContent{
|
||
MessageText: "机器人管理员设定了一个无效的默认 inline 命令",
|
||
},
|
||
}},
|
||
Button: &models.InlineQueryResultsButton{
|
||
Text: "您可以点击此处设定一个默认命令",
|
||
StartParameter: "via-inline_change-inline-command",
|
||
},
|
||
})
|
||
if err != nil {
|
||
defaultHandlerLogger.Error().
|
||
Err(err).
|
||
Str("content", "invalid bot default inline handler").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
}
|
||
return
|
||
}
|
||
|
||
// 用户没设定默认命令,配置里也没有填写默认命令 consts.InlineDefaultHandler,提示如何打开命令菜单
|
||
_, err := opts.Thebot.AnswerInlineQuery(opts.Ctx, &bot.AnswerInlineQueryParams{
|
||
InlineQueryID: opts.InlineQuery.ID,
|
||
Results: []models.InlineQueryResult{&models.InlineQueryResultArticle{
|
||
ID: "nodefaulthandler",
|
||
Title: fmt.Sprintf("请继续输入 %s 来查看可用的命令", configs.BotConfig.InlineSubCommandSymbol),
|
||
Description: "由于管理员没有设定默认命令,您需要手动选择一个命令,点击此处查看命令列表",
|
||
InputMessageContent: &models.InputTextMessageContent{
|
||
MessageText: func() string {
|
||
var message string = "可用的 Inline 模式命令:\n\n"
|
||
for _, command := range plugin_utils.AllPlugins.InlineCommandList {
|
||
if command.Attr.IsHideInCommandList { continue }
|
||
message += fmt.Sprintf("命令: <code>%s%s</code>\n", configs.BotConfig.InlineSubCommandSymbol, command.Command)
|
||
if command.Description != "" {
|
||
message += fmt.Sprintf("描述: %s\n", command.Description)
|
||
}
|
||
message += "\n"
|
||
}
|
||
return message
|
||
}(),
|
||
ParseMode: models.ParseModeHTML,
|
||
},
|
||
}},
|
||
Button: &models.InlineQueryResultsButton{
|
||
Text: "您可以点击此处设定一个默认命令",
|
||
StartParameter: "via-inline_change-inline-command",
|
||
},
|
||
})
|
||
if err != nil {
|
||
inlineLogger.Error().
|
||
Err(err).
|
||
Str("content", "bot no default inline handler").
|
||
Msg(flaterr.AnswerInlineQuery.Str())
|
||
}
|
||
}
|
||
}
|
||
|
||
func callbackQueryHandler(params *handler_params.CallbackQuery) {
|
||
defer utils.PanicCatcher(params.Ctx, "callbackQueryHandler")
|
||
var isProcessing bool
|
||
defer func() {
|
||
if isProcessing { database.UpdateOperationStatus(params.Ctx, params.ChatInfo.ID, db_struct.HasPendingCallbackQuery, false) }
|
||
}()
|
||
|
||
callbackQueryLogger := zerolog.Ctx(params.Ctx).
|
||
With().
|
||
Dict(utils.GetUserDict(¶ms.CallbackQuery.From)).
|
||
Str("callbackQueryData", params.CallbackQuery.Data).
|
||
Logger()
|
||
|
||
// 如果有一个正在处理的请求,且用户再次发送相同的请求,则提示用户等待
|
||
if params.ChatInfo.HasPendingCallbackQuery && params.CallbackQuery.Data == params.ChatInfo.LatestCallbackQueryData {
|
||
callbackQueryLogger.Info().Msg("this callback request is processing, ignore")
|
||
|
||
_, err := params.Thebot.AnswerCallbackQuery(params.Ctx, &bot.AnswerCallbackQueryParams{
|
||
CallbackQueryID: params.CallbackQuery.ID,
|
||
Text: "当前请求正在处理中,请等待处理完成",
|
||
ShowAlert: true,
|
||
})
|
||
if err != nil {
|
||
callbackQueryLogger.Error().
|
||
Err(err).
|
||
Str("content", "this callback request is processing").
|
||
Msg(flaterr.AnswerCallbackQuery.Str())
|
||
}
|
||
return
|
||
} else if params.ChatInfo.HasPendingCallbackQuery {
|
||
// 如果有一个正在处理的请求,用户发送了不同的请求,则提示用户等待
|
||
callbackQueryLogger.Info().
|
||
Str("pendingQueryData", params.ChatInfo.LatestCallbackQueryData).
|
||
Msg("another callback request is processing, ignore")
|
||
_, err := params.Thebot.AnswerCallbackQuery(params.Ctx, &bot.AnswerCallbackQueryParams{
|
||
CallbackQueryID: params.CallbackQuery.ID,
|
||
Text: "请等待上一个请求处理完成后再尝试发送新的请求",
|
||
ShowAlert: true,
|
||
})
|
||
if err != nil {
|
||
callbackQueryLogger.Error().
|
||
Err(err).
|
||
Str("content", "a callback request is processing, send new request later").
|
||
Msg(flaterr.AnswerCallbackQuery.Str())
|
||
}
|
||
return
|
||
} else {
|
||
// 如果没有正在处理的请求,则接受新的请求
|
||
callbackQueryLogger.Debug().Msg("accept callback query")
|
||
|
||
isProcessing = true
|
||
database.UpdateOperationStatus(params.Ctx, params.ChatInfo.ID, db_struct.HasPendingCallbackQuery, true)
|
||
params.ChatInfo.LatestCallbackQueryData = params.CallbackQuery.Data
|
||
// params.Thebot.AnswerCallbackQuery(ctx, &bot.AnswerCallbackQueryParams{
|
||
// CallbackQueryID: params.CallbackQuery.ID,
|
||
// Text: "已接受请求",
|
||
// ShowAlert: false,
|
||
// })
|
||
}
|
||
|
||
for _, plugin := range plugin_utils.AllPlugins.CallbackQuery {
|
||
if strings.HasPrefix(params.CallbackQuery.Data, plugin.CallbackDataPrefix) {
|
||
slogger := callbackQueryLogger.With().
|
||
Str("handlerPrefix", plugin.CallbackDataPrefix).
|
||
Logger()
|
||
|
||
if plugin.CallbackQueryHandler != nil {
|
||
slogger.Info().Msg("Hit callback query handler")
|
||
err := plugin.CallbackQueryHandler(params)
|
||
if err != nil {
|
||
callbackQueryLogger.Error().
|
||
Err(err).
|
||
Msg("Error in callback query handler")
|
||
}
|
||
return
|
||
} else {
|
||
slogger.Warn().Msg("Hit callback query handler, but this handler all function is nil, skip")
|
||
}
|
||
}
|
||
}
|
||
}
|