feat: Refactor bot handlers to utilize i18n for internationalization

- Updated silent.go to replace hardcoded strings with i18n keys for user feedback.
- Enhanced telegraph.go to use i18n for error messages and prompts.
- Modified update.go to implement i18n for version update notifications and errors.
- Refactored dir.go to utilize i18n for help messages related to directory operations.
- Updated parse.go to replace static text with i18n keys for parsed text entities.
- Enhanced rule.go to use i18n for rule management help messages.
- Refactored storage.go to implement i18n for storage selection prompts.
- Updated task.go to utilize i18n for task addition notifications.
- Added new i18n keys in keys.go for various messages.
- Updated Chinese localization in zh-Hans.yaml to reflect new i18n keys and messages.
This commit is contained in:
krau
2025-12-19 20:20:27 +08:00
parent 9ed2c425be
commit 6e16dc6051
21 changed files with 359 additions and 169 deletions
+19 -20
View File
@@ -8,19 +8,21 @@ import (
"github.com/celestix/gotgproto/dispatcher" "github.com/celestix/gotgproto/dispatcher"
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/pkg/enums/fnamest" "github.com/krau/SaveAny-Bot/pkg/enums/fnamest"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
) )
func handleConfigCmd(ctx *ext.Context, update *ext.Update) error { func handleConfigCmd(ctx *ext.Context, update *ext.Update) error {
ctx.Reply(update, ext.ReplyTextString("请选择要配置的选项"), &ext.ReplyOpts{ ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgConfigPromptSelectOption)), &ext.ReplyOpts{
Markup: &tg.ReplyInlineMarkup{ Markup: &tg.ReplyInlineMarkup{
Rows: []tg.KeyboardButtonRow{ Rows: []tg.KeyboardButtonRow{
{ {
Buttons: []tg.KeyboardButtonClass{ Buttons: []tg.KeyboardButtonClass{
&tg.KeyboardButtonCallback{ &tg.KeyboardButtonCallback{
Text: "文件名策略", Text: i18n.T(i18nk.BotMsgConfigButtonFilenameStrategy),
Data: fmt.Appendf(nil, "%s %s", tcbdata.TypeConfig, "fnamest"), Data: fmt.Appendf(nil, "%s %s", tcbdata.TypeConfig, "fnamest"),
}, },
}, },
@@ -37,7 +39,7 @@ func handleConfigCallback(ctx *ext.Context, update *ext.Update) error {
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{ ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
QueryID: update.CallbackQuery.GetQueryID(), QueryID: update.CallbackQuery.GetQueryID(),
Alert: true, Alert: true,
Message: "无效的回调数据", Message: i18n.T(i18nk.BotMsgConfigErrorInvalidCallbackData),
CacheTime: 5, CacheTime: 5,
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
@@ -72,7 +74,9 @@ func handleConfigFnameSTCallback(ctx *ext.Context, update *ext.Update) error {
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
ID: update.CallbackQuery.GetMsgID(), ID: update.CallbackQuery.GetMsgID(),
Message: fmt.Sprintf("已将文件名策略设置为: %s", fnamest.FnameSTDisplay[st]), Message: i18n.T(i18nk.BotMsgConfigInfoFilenameStrategySet, map[string]any{
"Strategy": fnamest.FnameSTDisplay[st],
}),
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
} }
@@ -97,7 +101,9 @@ func handleConfigFnameSTCallback(ctx *ext.Context, update *ext.Update) error {
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
ID: update.CallbackQuery.GetMsgID(), ID: update.CallbackQuery.GetMsgID(),
Message: fmt.Sprintf("请选择文件名策略, 当前策略: %s", fnamest.FnameSTDisplay[currentSt]), Message: i18n.T(i18nk.BotMsgConfigPromptSelectFilenameStrategy, map[string]any{
"Strategy": fnamest.FnameSTDisplay[currentSt],
}),
ReplyMarkup: markup, ReplyMarkup: markup,
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
@@ -111,34 +117,27 @@ func handleConfigFnameTmpl(ctx *ext.Context, update *ext.Update) error {
} }
args := strings.Fields(string(update.EffectiveMessage.Text)) args := strings.Fields(string(update.EffectiveMessage.Text))
if len(args) <= 1 { if len(args) <= 1 {
text := `使用该命令设置文件名模板, 示例: text := i18n.T(i18nk.BotMsgConfigFnametmplHelp, nil)
/fnametmpl 图片_{{.msgid}}_{{.msgdate}}.jpg
可用变量:
- {{.msgid}}: 消息ID
- {{.msgtags}}: 消息中的标签, 将以下划线分隔输出
- {{.msggen}}: 根据消息生成的文件名
- {{.msgdate}}: 消息日期, 格式 YYYY-MM-DD_HH-MM-SS
- {{.origname}}: 媒体的原始文件名 (如果有)
- {{.chatid}}: 消息的聊天ID
`
if user.FilenameTemplate != "" { if user.FilenameTemplate != "" {
text += fmt.Sprintf("\n\n当前模板: %s", user.FilenameTemplate) text += "\n\n" + i18n.T(i18nk.BotMsgConfigInfoCurrentTemplatePrefix, map[string]any{
"Template": user.FilenameTemplate,
})
} }
text += "\n\n模板仅在文件名策略设置为 '自定义模板' 时生效, 且模板解析错误时会回退到默认文件名"
ctx.Reply(update, ext.ReplyTextString(text), nil) ctx.Reply(update, ext.ReplyTextString(text), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
newTmpl := strings.Join(args[1:], " ") newTmpl := strings.Join(args[1:], " ")
_, err = template.New("filename").Parse(newTmpl) _, err = template.New("filename").Parse(newTmpl)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("无效的模板, 请检查语法\n"+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgConfigErrorInvalidTemplate, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
user.FilenameTemplate = newTmpl user.FilenameTemplate = newTmpl
if err := database.UpdateUser(ctx, user); err != nil { if err := database.UpdateUser(ctx, user); err != nil {
return err return err
} }
ctx.Reply(update, ext.ReplyTextString("已更新文件名模板"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgConfigInfoTemplateUpdated, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
+10 -8
View File
@@ -8,6 +8,8 @@ import (
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
) )
@@ -19,7 +21,7 @@ func handleDirCmd(ctx *ext.Context, update *ext.Update) error {
dirs, err := database.GetUserDirsByChatID(ctx, userChatID) dirs, err := database.GetUserDirsByChatID(ctx, userChatID)
if err != nil { if err != nil {
logger.Errorf("Failed to get user directories: %s", err) logger.Errorf("Failed to get user directories: %s", err)
ctx.Reply(update, ext.ReplyTextString("获取用户文件夹失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorGetUserDirsFailed)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if len(args) < 2 { if len(args) < 2 {
@@ -29,7 +31,7 @@ func handleDirCmd(ctx *ext.Context, update *ext.Update) error {
user, err := database.GetUserByChatID(ctx, update.GetUserChat().GetID()) user, err := database.GetUserByChatID(ctx, update.GetUserChat().GetID())
if err != nil { if err != nil {
logger.Errorf("Failed to get user: %s", err) logger.Errorf("Failed to get user: %s", err)
ctx.Reply(update, ext.ReplyTextString("获取用户失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorGetUserFailed)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
switch args[1] { switch args[1] {
@@ -46,10 +48,10 @@ func handleDirCmd(ctx *ext.Context, update *ext.Update) error {
if err := database.CreateDirForUser(ctx, user.ID, args[2], args[3]); err != nil { if err := database.CreateDirForUser(ctx, user.ID, args[2], args[3]); err != nil {
logger.Errorf("Failed to create directory: %s", err) logger.Errorf("Failed to create directory: %s", err)
ctx.Reply(update, ext.ReplyTextString("创建文件夹失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorCreateDirFailed)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(update, ext.ReplyTextString("文件夹添加成功"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirInfoCreateDirSuccess)), nil)
case "del": case "del":
// /dir del 3 // /dir del 3
if len(args) < 3 { if len(args) < 3 {
@@ -58,17 +60,17 @@ func handleDirCmd(ctx *ext.Context, update *ext.Update) error {
} }
dirID, err := strconv.Atoi(args[2]) dirID, err := strconv.Atoi(args[2])
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("文件夹ID无效"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorInvalidDirId)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if err := database.DeleteDirByID(ctx, uint(dirID)); err != nil { if err := database.DeleteDirByID(ctx, uint(dirID)); err != nil {
logger.Errorf("Failed to delete directory: %s", err) logger.Errorf("Failed to delete directory: %s", err)
ctx.Reply(update, ext.ReplyTextString("删除文件夹失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorDeleteDirFailed)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(update, ext.ReplyTextString("文件夹删除成功"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirInfoDeleteDirSuccess)), nil)
default: default:
ctx.Reply(update, ext.ReplyTextString("未知操作"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDirErrorUnknownOperation)), nil)
} }
return dispatcher.EndGroups return dispatcher.EndGroups
} }
+7 -4
View File
@@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"net/url" "net/url"
"strings" "strings"
@@ -9,6 +8,8 @@ import (
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/slice"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/pkg/enums/tasktype" "github.com/krau/SaveAny-Bot/pkg/enums/tasktype"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
@@ -18,7 +19,7 @@ func handleDlCmd(ctx *ext.Context, update *ext.Update) error {
logger := log.FromContext(ctx) logger := log.FromContext(ctx)
args := strings.Split(update.EffectiveMessage.Text, " ") args := strings.Split(update.EffectiveMessage.Text, " ")
if len(args) < 2 { if len(args) < 2 {
ctx.Reply(update, ext.ReplyTextString("用法: /dl <链接1> <链接2> ..."), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDlUsage)), nil)
return nil return nil
} }
links := args[1:] links := args[1:]
@@ -32,7 +33,7 @@ func handleDlCmd(ctx *ext.Context, update *ext.Update) error {
} }
links = slice.Compact(links) links = slice.Compact(links)
if len(links) == 0 { if len(links) == 0 {
ctx.Reply(update, ext.ReplyTextString("没有有效的链接可供下载"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDlErrorNoValidLinks)), nil)
return nil return nil
} }
markup, err := msgelem.BuildAddSelectStorageKeyboard(storage.GetUserStorages(ctx, update.GetUserChat().GetID()), tcbdata.Add{ markup, err := msgelem.BuildAddSelectStorageKeyboard(storage.GetUserStorages(ctx, update.GetUserChat().GetID()), tcbdata.Add{
@@ -42,7 +43,9 @@ func handleDlCmd(ctx *ext.Context, update *ext.Update) error {
if err != nil { if err != nil {
return err return err
} }
ctx.Reply(update, ext.ReplyTextString(fmt.Sprintf("共 %d 个文件, 请选择存储位置", len(links))), &ext.ReplyOpts{ ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgDlInfoFilesSelectStorage, map[string]any{
"Count": len(links),
})), &ext.ReplyOpts{
Markup: markup, Markup: markup,
}) })
return nil return nil
+11 -5
View File
@@ -1,14 +1,14 @@
package handlers package handlers
import ( import (
"fmt"
"github.com/celestix/gotgproto/dispatcher" "github.com/celestix/gotgproto/dispatcher"
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
) )
@@ -25,7 +25,9 @@ func handleMessageLink(ctx *ext.Context, update *ext.Update) error {
req, err := msgelem.BuildAddOneSelectStorageMessage(ctx, stors, files[0], replied.ID) req, err := msgelem.BuildAddOneSelectStorageMessage(ctx, stors, files[0], replied.ID)
if err != nil { if err != nil {
logger.Errorf("Failed to build storage selection message: %s", err) logger.Errorf("Failed to build storage selection message: %s", err)
editReplied("构建存储选择消息失败: "+err.Error(), nil) editReplied(i18n.T(i18nk.BotMsgCommonErrorBuildStorageSelectMessageFailed, map[string]any{
"Error": err.Error(),
}), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.EditMessage(update.EffectiveChat().GetID(), req) ctx.EditMessage(update.EffectiveChat().GetID(), req)
@@ -36,10 +38,14 @@ func handleMessageLink(ctx *ext.Context, update *ext.Update) error {
}) })
if err != nil { if err != nil {
logger.Errorf("Failed to build storage selection keyboard: %s", err) logger.Errorf("Failed to build storage selection keyboard: %s", err)
editReplied("构建存储选择键盘失败: "+err.Error(), nil) editReplied(i18n.T(i18nk.BotMsgCommonErrorBuildStorageSelectKeyboardFailed, map[string]any{
"Error": err.Error(),
}), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
editReplied(fmt.Sprintf("找到 %d 个文件, 请选择存储位置", len(files)), markup) editReplied(i18n.T(i18nk.BotMsgCommonInfoFoundFilesSelectStorage, map[string]any{
"Count": len(files),
}), markup)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
+5 -1
View File
@@ -8,6 +8,8 @@ import (
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
) )
@@ -35,7 +37,9 @@ func handleMediaMessage(ctx *ext.Context, update *ext.Update) error {
req, err := msgelem.BuildAddOneSelectStorageMessage(ctx, stors, file, msg.ID) req, err := msgelem.BuildAddOneSelectStorageMessage(ctx, stors, file, msg.ID)
if err != nil { if err != nil {
logger.Errorf("Failed to build storage selection message: %s", err) logger.Errorf("Failed to build storage selection message: %s", err)
ctx.Reply(update, ext.ReplyTextString("构建存储选择消息失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorBuildStorageSelectMessageFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.EditMessage(update.EffectiveChat().GetID(), req) ctx.EditMessage(update.EffectiveChat().GetID(), req)
+10 -5
View File
@@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"sync" "sync"
"time" "time"
@@ -12,6 +11,8 @@ import (
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/common/utils/tgutil" "github.com/krau/SaveAny-Bot/common/utils/tgutil"
"github.com/krau/SaveAny-Bot/config" "github.com/krau/SaveAny-Bot/config"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
@@ -89,7 +90,7 @@ func processMediaGroup(ctx *ext.Context, update *ext.Update, groupID int64) {
logger.Debugf("Processing media group %d with %d items", groupID, len(items)) logger.Debugf("Processing media group %d with %d items", groupID, len(items))
userId := update.GetUserChat().GetID() userId := update.GetUserChat().GetID()
msg, err := ctx.Reply(update, ext.ReplyTextString("正在保存文件..."), nil) msg, err := ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgMediaGroupInfoSavingFiles, nil)), nil)
if err != nil { if err != nil {
logger.Errorf("Failed to reply: %s", err) logger.Errorf("Failed to reply: %s", err)
return return
@@ -114,13 +115,17 @@ func processMediaGroup(ctx *ext.Context, update *ext.Update, groupID int64) {
logger.Errorf("Failed to build storage selection keyboard: %s", err) logger.Errorf("Failed to build storage selection keyboard: %s", err)
ctx.EditMessage(userId, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userId, &tg.MessagesEditMessageRequest{
ID: msg.ID, ID: msg.ID,
Message: "构建存储选择键盘失败: " + err.Error(), Message: i18n.T(i18nk.BotMsgMediaGroupErrorBuildStorageSelectKeyboardFailed, map[string]any{
"Error": err.Error(),
}),
}) })
return return
} }
ctx.EditMessage(userId, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userId, &tg.MessagesEditMessageRequest{
ID: msg.ID, ID: msg.ID,
Message: fmt.Sprintf("共 %d 个文件, 请选择存储位置", len(items)), Message: i18n.T(i18nk.BotMsgMediaGroupInfoGroupFoundFilesSelectStorage, map[string]any{
"Count": len(items),
}),
ReplyMarkup: markup, ReplyMarkup: markup,
}) })
} }
+13 -9
View File
@@ -5,6 +5,8 @@ import (
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/slice"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/config" "github.com/krau/SaveAny-Bot/config"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
@@ -13,11 +15,7 @@ import (
func checkPermission(ctx *ext.Context, update *ext.Update) error { func checkPermission(ctx *ext.Context, update *ext.Update) error {
userID := update.GetUserChat().GetID() userID := update.GetUserChat().GetID()
if !slice.Contain(config.C().GetUsersID(), userID) { if !slice.Contain(config.C().GetUsersID(), userID) {
const noPermissionText string = ` ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorNoPermission, nil)), nil)
您不在白名单中, 无法使用此 Bot.
您可以部署自己的实例: https://github.com/krau/SaveAny-Bot
`
ctx.Reply(update, ext.ReplyTextString(noPermissionText), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
@@ -29,25 +27,31 @@ func handleSilentMode(next func(*ext.Context, *ext.Update) error, handler func(*
userID := update.GetUserChat().GetID() userID := update.GetUserChat().GetID()
user, err := database.GetUserByChatID(ctx, userID) user, err := database.GetUserByChatID(ctx, userID)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("获取用户信息失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetUserInfoFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if !user.Silent { if !user.Silent {
return next(ctx, update) return next(ctx, update)
} }
if user.DefaultStorage == "" { if user.DefaultStorage == "" {
ctx.Reply(update, ext.ReplyTextString("您已开启静默模式, 但未设置默认存储端, 请先使用 /storage 设置"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorDefaultStorageNotSet, nil)), nil)
return next(ctx, update) return next(ctx, update)
} }
stor, err := storage.GetStorageByUserIDAndName(ctx, userID, user.DefaultStorage) stor, err := storage.GetStorageByUserIDAndName(ctx, userID, user.DefaultStorage)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("获取默认存储失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetStorageFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if user.DefaultDir != 0 { if user.DefaultDir != 0 {
dir, err := database.GetDirByID(ctx, user.DefaultDir) dir, err := database.GetDirByID(ctx, user.DefaultDir)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("获取默认文件夹失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetDirFailed, map[string]any{
"Error": err.Error(),
})), nil)
return next(ctx, update) return next(ctx, update)
} }
ctx.Context = dirutil.WithContext(ctx.Context, dir) ctx.Context = dirutil.WithContext(ctx.Context, dir)
+18 -6
View File
@@ -14,6 +14,8 @@ import (
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/common/utils/fsutil" "github.com/krau/SaveAny-Bot/common/utils/fsutil"
"github.com/krau/SaveAny-Bot/common/utils/tgutil" "github.com/krau/SaveAny-Bot/common/utils/tgutil"
"github.com/krau/SaveAny-Bot/parsers" "github.com/krau/SaveAny-Bot/parsers"
@@ -33,7 +35,7 @@ func handleTextMessage(ctx *ext.Context, u *ext.Update) error {
if !ok { if !ok {
return dispatcher.EndGroups return dispatcher.EndGroups
} }
msg, err := ctx.Reply(u, ext.ReplyTextString("正在解析..."), nil) msg, err := ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseInfoParsing, nil)), nil)
if err != nil { if err != nil {
return err return err
} }
@@ -44,7 +46,9 @@ func handleTextMessage(ctx *ext.Context, u *ext.Update) error {
} }
if err != nil { if err != nil {
logger.Error("Failed to parse text", "error", err) logger.Error("Failed to parse text", "error", err)
ctx.Reply(u, ext.ReplyTextString("Failed to parse text: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseErrorParseTextFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
logger.Debug("Parsed item from text message", "title", item.Title, "url", item.URL) logger.Debug("Parsed item from text message", "title", item.Title, "url", item.URL)
@@ -55,13 +59,17 @@ func handleTextMessage(ctx *ext.Context, u *ext.Update) error {
}) })
if err != nil { if err != nil {
logger.Errorf("Failed to build storage selection keyboard: %s", err) logger.Errorf("Failed to build storage selection keyboard: %s", err)
ctx.Reply(u, ext.ReplyTextString("Failed to build storage selection keyboard: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseErrorBuildStorageSelectKeyboardFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
text, entities, err := msgelem.BuildParsedTextEntity(*item) text, entities, err := msgelem.BuildParsedTextEntity(*item)
if err != nil { if err != nil {
logger.Errorf("Failed to build parsed text entity: %s", err) logger.Errorf("Failed to build parsed text entity: %s", err)
ctx.Reply(u, ext.ReplyTextString("Failed to build parsed text entity: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseErrorBuildParsedTextEntityFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
@@ -87,7 +95,9 @@ func handleSilentSaveText(ctx *ext.Context, u *ext.Update) error {
} }
if err != nil { if err != nil {
logger.Error("Failed to parse text", "error", err) logger.Error("Failed to parse text", "error", err)
ctx.Reply(u, ext.ReplyTextString("Failed to parse text: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseErrorParseTextFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
logger.Debug("Parsed item from text message", "title", item.Title, "url", item.URL) logger.Debug("Parsed item from text message", "title", item.Title, "url", item.URL)
@@ -95,7 +105,9 @@ func handleSilentSaveText(ctx *ext.Context, u *ext.Update) error {
text, entities, err := msgelem.BuildParsedTextEntity(*item) text, entities, err := msgelem.BuildParsedTextEntity(*item)
if err != nil { if err != nil {
logger.Errorf("Failed to build parsed text entity: %s", err) logger.Errorf("Failed to build parsed text entity: %s", err)
ctx.Reply(u, ext.ReplyTextString("Failed to build parsed text entity: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParseErrorBuildParsedTextEntityFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
msg, err := ctx.SendMessage(userID, &tg.MessagesSendMessageRequest{ msg, err := ctx.SendMessage(userID, &tg.MessagesSendMessageRequest{
+21 -17
View File
@@ -7,17 +7,15 @@ import (
"github.com/celestix/gotgproto/dispatcher" "github.com/celestix/gotgproto/dispatcher"
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/config" "github.com/krau/SaveAny-Bot/config"
"github.com/krau/SaveAny-Bot/parsers" "github.com/krau/SaveAny-Bot/parsers"
) )
func handleParserCmd(ctx *ext.Context, u *ext.Update) error { func handleParserCmd(ctx *ext.Context, u *ext.Update) error {
args := strings.Split(u.EffectiveMessage.Text, " ") args := strings.Split(u.EffectiveMessage.Text, " ")
help := ` help := i18n.T(i18nk.BotMsgParserHelpText, nil)
用法:
/parser install <回复一个文件> - 安装解析器
`
if len(args) < 2 { if len(args) < 2 {
ctx.Reply(u, ext.ReplyTextString(help), nil) ctx.Reply(u, ext.ReplyTextString(help), nil)
return nil return nil
@@ -36,35 +34,35 @@ func handleParserCmd(ctx *ext.Context, u *ext.Update) error {
func handleParserInstallCmd(ctx *ext.Context, u *ext.Update) error { func handleParserInstallCmd(ctx *ext.Context, u *ext.Update) error {
if !config.C().Parser.PluginEnable { if !config.C().Parser.PluginEnable {
ctx.Reply(u, ext.ReplyTextString("解析器插件功能未启用"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserPluginNotEnabled, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if u.EffectiveMessage.ReplyToMessage == nil || u.EffectiveMessage.ReplyToMessage.Media == nil { if u.EffectiveMessage.ReplyToMessage == nil || u.EffectiveMessage.ReplyToMessage.Media == nil {
ctx.Reply(u, ext.ReplyTextString("请回复一个包含解析器文件的消息"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserPromptReplyWithParserFile, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
media := u.EffectiveMessage.ReplyToMessage.Media media := u.EffectiveMessage.ReplyToMessage.Media
document, ok := media.(*tg.MessageMediaDocument) document, ok := media.(*tg.MessageMediaDocument)
if !ok { if !ok {
ctx.Reply(u, ext.ReplyTextString("回复的消息不包含有效的文件"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorNoValidFileInReply, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
value, ok := document.GetDocument() value, ok := document.GetDocument()
if !ok { if !ok {
ctx.Reply(u, ext.ReplyTextString("回复的消息不包含有效的文件"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorNoValidFileInReply, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
doc, ok := value.AsNotEmpty() doc, ok := value.AsNotEmpty()
if !ok { if !ok {
ctx.Reply(u, ext.ReplyTextString("回复的消息不包含有效的文件"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorNoValidFileInReply, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if !strings.HasPrefix(doc.MimeType, "text/") { if !strings.HasPrefix(doc.MimeType, "text/") {
ctx.Reply(u, ext.ReplyTextString("错误的文件类型"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorWrongFileType, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if doc.Size > 1024*1024*10 { if doc.Size > 1024*1024*10 {
ctx.Reply(u, ext.ReplyTextString("文件过大"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorFileTooLarge, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
var fileName string var fileName string
@@ -75,23 +73,29 @@ func handleParserInstallCmd(ctx *ext.Context, u *ext.Update) error {
} }
} }
if fileName == "" { if fileName == "" {
ctx.Reply(u, ext.ReplyTextString("无法获取文件名"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorGetFilenameFailed, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if !strings.HasSuffix(fileName, ".js") { if !strings.HasSuffix(fileName, ".js") {
ctx.Reply(u, ext.ReplyTextString("仅支持 .js 文件作为解析器"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorOnlyJsSupported, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
data := bytes.NewBuffer(nil) data := bytes.NewBuffer(nil)
_, err := ctx.DownloadMedia(media, ext.DownloadOutputStream{Writer: data}, nil) _, err := ctx.DownloadMedia(media, ext.DownloadOutputStream{Writer: data}, nil)
if err != nil { if err != nil {
ctx.Reply(u, ext.ReplyTextString("文件下载失败: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorDownloadFileFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if err := parsers.AddPlugin(ctx, data.String(), fileName); err != nil { if err := parsers.AddPlugin(ctx, data.String(), fileName); err != nil {
ctx.Reply(u, ext.ReplyTextString("插件安装失败: "+err.Error()), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserErrorInstallPluginFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(u, ext.ReplyTextString("插件安装成功: "+fileName), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgParserInfoInstallPluginSuccess, map[string]any{
"Name": fileName,
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
+19 -10
View File
@@ -10,6 +10,8 @@ import (
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/duke-git/lancet/v2/slice" "github.com/duke-git/lancet/v2/slice"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/common/utils/strutil" "github.com/krau/SaveAny-Bot/common/utils/strutil"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/pkg/rule" "github.com/krau/SaveAny-Bot/pkg/rule"
@@ -22,7 +24,7 @@ func handleRuleCmd(ctx *ext.Context, update *ext.Update) error {
user, err := database.GetUserByChatID(ctx, userChatID) user, err := database.GetUserByChatID(ctx, userChatID)
if err != nil { if err != nil {
logger.Errorf("Failed to get user rules: %s", err) logger.Errorf("Failed to get user rules: %s", err)
ctx.Reply(update, ext.ReplyTextString("获取用户规则失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorGetUserRulesFailed, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if len(args) < 2 { if len(args) < 2 {
@@ -35,10 +37,14 @@ func handleRuleCmd(ctx *ext.Context, update *ext.Update) error {
applyRule := !user.ApplyRule applyRule := !user.ApplyRule
if err := database.UpdateUserApplyRule(ctx, user.ChatID, applyRule); err != nil { if err := database.UpdateUserApplyRule(ctx, user.ChatID, applyRule); err != nil {
logger.Errorf("更新用户失败: %s", err) logger.Errorf("更新用户失败: %s", err)
ctx.Reply(update, ext.ReplyTextString("更新用户失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorUpdateUserFailed, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(update, ext.ReplyTextString(fmt.Sprintf("已%s规则模式", map[bool]string{true: "启用", false: "禁用"}[applyRule])), nil) if applyRule {
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleInfoRuleModeEnabled, nil)), nil)
} else {
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleInfoRuleModeDisabled, nil)), nil)
}
case "add": case "add":
// /rule add <type> <data> <storage> <dirpath> // /rule add <type> <data> <storage> <dirpath>
if len(args) < 6 { if len(args) < 6 {
@@ -55,7 +61,10 @@ func handleRuleCmd(ctx *ext.Context, update *ext.Update) error {
return rule.RuleType(""), fmt.Errorf("无效的规则类型: %s\n可用: %v", ruleTypeArg, slice.Join(rule.Values(), ", ")) return rule.RuleType(""), fmt.Errorf("无效的规则类型: %s\n可用: %v", ruleTypeArg, slice.Join(rule.Values(), ", "))
}() }()
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString(err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorInvalidRuleType, map[string]any{
"Type": ruleTypeArg,
"Available": slice.Join(rule.Values(), ", "),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
@@ -72,28 +81,28 @@ func handleRuleCmd(ctx *ext.Context, update *ext.Update) error {
} }
if err := database.CreateRule(ctx, rd); err != nil { if err := database.CreateRule(ctx, rd); err != nil {
logger.Errorf("创建规则失败: %s", err) logger.Errorf("创建规则失败: %s", err)
ctx.Reply(update, ext.ReplyTextString("创建规则失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorCreateRuleFailed, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(update, ext.ReplyTextString("创建规则成功"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleInfoCreateRuleSuccess, nil)), nil)
case "del": case "del":
// /rule del <id> // /rule del <id>
if len(args) < 3 { if len(args) < 3 {
ctx.Reply(update, ext.ReplyTextString("请提供规则ID"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRulePromptProvideRuleId, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ruleID := args[2] ruleID := args[2]
id, err := strconv.Atoi(ruleID) id, err := strconv.Atoi(ruleID)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("无效的规则ID"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorInvalidRuleId, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if err := database.DeleteRule(ctx, uint(id)); err != nil { if err := database.DeleteRule(ctx, uint(id)); err != nil {
logger.Errorf("删除规则失败: %s", err) logger.Errorf("删除规则失败: %s", err)
ctx.Reply(update, ext.ReplyTextString("删除规则失败"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleErrorDeleteRuleFailed, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.Reply(update, ext.ReplyTextString("删除规则成功"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgRuleInfoDeleteRuleSuccess, nil)), nil)
default: default:
ctx.Reply(update, ext.ReplyTextStyledTextArray(msgelem.BuildRuleHelpStyling(user.ApplyRule, user.Rules)), nil) ctx.Reply(update, ext.ReplyTextStyledTextArray(msgelem.BuildRuleHelpStyling(user.ApplyRule, user.Rules)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
+44 -19
View File
@@ -1,7 +1,6 @@
package handlers package handlers
import ( import (
"fmt"
"strings" "strings"
"github.com/celestix/gotgproto/dispatcher" "github.com/celestix/gotgproto/dispatcher"
@@ -9,6 +8,8 @@ import (
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/common/cache" "github.com/krau/SaveAny-Bot/common/cache"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
@@ -17,20 +18,27 @@ import (
func handleSilentCmd(ctx *ext.Context, update *ext.Update) error { func handleSilentCmd(ctx *ext.Context, update *ext.Update) error {
user, err := database.GetUserByChatID(ctx, update.GetUserChat().GetID()) user, err := database.GetUserByChatID(ctx, update.GetUserChat().GetID())
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("获取用户信息失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetUserInfoFailed, map[string]any{
"Error": err.Error(),
})), nil)
return nil return nil
} }
if !user.Silent && user.DefaultStorage == "" { if !user.Silent && user.DefaultStorage == "" {
ctx.Reply(update, ext.ReplyTextString("请先使用 /storage 设置默认存储位置"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorDefaultStorageNotSet, nil)), nil)
return nil return nil
} }
user.Silent = !user.Silent user.Silent = !user.Silent
if err := database.UpdateUser(ctx, user); err != nil { if err := database.UpdateUser(ctx, user); err != nil {
ctx.Reply(update, ext.ReplyTextString("更新用户信息失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorUpdateUserInfoFailed, map[string]any{
"Error": err.Error(),
})), nil)
return nil return nil
} }
responseText := "已" + map[bool]string{true: "开启", false: "关闭"}[user.Silent] + "静默模式" if user.Silent {
ctx.Reply(update, ext.ReplyTextString(responseText), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonInfoSilentModeOn, nil)), nil)
} else {
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonInfoSilentModeOff, nil)), nil)
}
return dispatcher.EndGroups return dispatcher.EndGroups
} }
@@ -49,18 +57,22 @@ func handleSetDefaultCallback(ctx *ext.Context, update *ext.Update) error {
} }
if !ok { if !ok {
return failedAnswer("数据已过期") return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorDataExpired, nil))
} }
userID := update.CallbackQuery.GetUserID() userID := update.CallbackQuery.GetUserID()
storageName := data.StorageName storageName := data.StorageName
selectedStorage, err := storage.GetStorageByUserIDAndName(ctx, userID, storageName) selectedStorage, err := storage.GetStorageByUserIDAndName(ctx, userID, storageName)
if err != nil { if err != nil {
return failedAnswer("存储获取失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorGetStorageFailed, map[string]any{
"Error": err.Error(),
}))
} }
user, err := database.GetUserByChatID(ctx, userID) user, err := database.GetUserByChatID(ctx, userID)
if err != nil { if err != nil {
return failedAnswer("获取用户信息失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorGetUserInfoFailed, map[string]any{
"Error": err.Error(),
}))
} }
var dir *database.Dir var dir *database.Dir
if data.DirID != 0 { if data.DirID != 0 {
@@ -68,24 +80,28 @@ func handleSetDefaultCallback(ctx *ext.Context, update *ext.Update) error {
var err error var err error
dir, err = database.GetDirByID(ctx, data.DirID) dir, err = database.GetDirByID(ctx, data.DirID)
if err != nil { if err != nil {
return failedAnswer("获取文件夹信息失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgDirErrorGetUserDirsFailed, nil))
} }
user.DefaultDir = dir.ID user.DefaultDir = dir.ID
} else { } else {
// 检查是否有可用的文件夹 // 检查是否有可用的文件夹
dirs, err := database.GetDirsByUserIDAndStorageName(ctx, user.ID, storageName) dirs, err := database.GetDirsByUserIDAndStorageName(ctx, user.ID, storageName)
if err != nil { if err != nil {
return failedAnswer("获取目录失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorGetDirFailed, map[string]any{
"Error": err.Error(),
}))
} }
if len(dirs) > 0 { if len(dirs) > 0 {
// 要求选择文件夹 // 要求选择文件夹
markup, err := msgelem.BuildSetDefaultDirMarkup(ctx, storageName, dirs) markup, err := msgelem.BuildSetDefaultDirMarkup(ctx, storageName, dirs)
if err != nil { if err != nil {
return failedAnswer("构建目录选择失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorBuildDirSelectKeyboardFailed, map[string]any{
"Error": err.Error(),
}))
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
ID: update.CallbackQuery.GetMsgID(), ID: update.CallbackQuery.GetMsgID(),
Message: "请选择要保存到的默认文件夹", Message: i18n.T(i18nk.BotMsgCommonPromptSelectDefaultDir, nil),
ReplyMarkup: markup, ReplyMarkup: markup,
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
@@ -93,11 +109,18 @@ func handleSetDefaultCallback(ctx *ext.Context, update *ext.Update) error {
} }
user.DefaultStorage = selectedStorage.Name() user.DefaultStorage = selectedStorage.Name()
if err := database.UpdateUser(ctx, user); err != nil { if err := database.UpdateUser(ctx, user); err != nil {
return failedAnswer("更新用户信息失败: " + err.Error()) return failedAnswer(i18n.T(i18nk.BotMsgCommonErrorUpdateUserInfoFailed, map[string]any{
"Error": err.Error(),
}))
} }
msg := fmt.Sprintf("已将默认存储位置设为: %s", selectedStorage.Name()) msg := i18n.T(i18nk.BotMsgCommonInfoDefaultStorageSet, map[string]any{
"Name": selectedStorage.Name(),
})
if dir != nil { if dir != nil {
msg += fmt.Sprintf(":/%s", strings.TrimPrefix(dir.Path, "/")) msg = i18n.T(i18nk.BotMsgCommonInfoDefaultStorageWithDirSet, map[string]any{
"Name": selectedStorage.Name(),
"Dir": strings.TrimPrefix(dir.Path, "/"),
})
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
ID: update.CallbackQuery.GetMsgID(), ID: update.CallbackQuery.GetMsgID(),
@@ -110,15 +133,17 @@ func handleStorageCmd(ctx *ext.Context, update *ext.Update) error {
userID := update.GetUserChat().GetID() userID := update.GetUserChat().GetID()
storages := storage.GetUserStorages(ctx, userID) storages := storage.GetUserStorages(ctx, userID)
if len(storages) == 0 { if len(storages) == 0 {
ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorNoAvailableStorage, nil)), nil)
return nil return nil
} }
markup, err := msgelem.BuildSetDefaultStorageMarkup(ctx, storages) markup, err := msgelem.BuildSetDefaultStorageMarkup(ctx, storages)
if err != nil { if err != nil {
ctx.Reply(update, ext.ReplyTextString("获取存储失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetStorageFailed, map[string]any{
"Error": err.Error(),
})), nil)
return nil return nil
} }
ctx.Reply(update, ext.ReplyTextString("请选择要设为默认的存储位置"), &ext.ReplyOpts{ ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonPromptSelectDefaultStorage, nil)), &ext.ReplyOpts{
Markup: markup, Markup: markup,
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
+8 -4
View File
@@ -13,6 +13,8 @@ import (
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut" "github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/pkg/enums/tasktype" "github.com/krau/SaveAny-Bot/pkg/enums/tasktype"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
"github.com/krau/SaveAny-Bot/storage" "github.com/krau/SaveAny-Bot/storage"
@@ -35,17 +37,19 @@ func handleTelegraphUrlMessage(ctx *ext.Context, update *ext.Update) error {
}) })
if err != nil { if err != nil {
logger.Errorf("Failed to build storage selection keyboard: %s", err) logger.Errorf("Failed to build storage selection keyboard: %s", err)
ctx.Reply(update, ext.ReplyTextString("构建存储选择键盘失败: "+err.Error()), nil) ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTelegraphErrorBuildStorageSelectKeyboardFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
eb := entity.Builder{} eb := entity.Builder{}
if err := styling.Perform(&eb, if err := styling.Perform(&eb,
styling.Plain("标题: "), styling.Plain(i18n.T(i18nk.BotMsgTelegraphInfoTitlePrefix, nil)),
styling.Code(result.Page.Title), styling.Code(result.Page.Title),
styling.Plain("\n图片数量: "), styling.Plain(i18n.T(i18nk.BotMsgTelegraphInfoPicCountPrefix, nil)),
styling.Code(fmt.Sprintf("%d", len(result.Pics))), styling.Code(fmt.Sprintf("%d", len(result.Pics))),
styling.Plain("\n请选择存储位置"), styling.Plain(i18n.T(i18nk.BotMsgTelegraphInfoPromptSelectStorage, nil)),
); err != nil { ); err != nil {
log.FromContext(ctx).Errorf("Failed to build entity: %s", err) log.FromContext(ctx).Errorf("Failed to build entity: %s", err)
return dispatcher.EndGroups return dispatcher.EndGroups
+38 -26
View File
@@ -2,7 +2,6 @@ package handlers
import ( import (
"errors" "errors"
"fmt"
"regexp" "regexp"
"strings" "strings"
@@ -11,6 +10,8 @@ import (
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/gotd/td/telegram/message/html" "github.com/gotd/td/telegram/message/html"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/config" "github.com/krau/SaveAny-Bot/config"
"github.com/unvgo/ghselfupdate" "github.com/unvgo/ghselfupdate"
) )
@@ -18,24 +19,33 @@ import (
func handleUpdateCmd(ctx *ext.Context, u *ext.Update) error { func handleUpdateCmd(ctx *ext.Context, u *ext.Update) error {
currentV, err := semver.Parse(config.Version) currentV, err := semver.Parse(config.Version)
if err != nil { if err != nil {
ctx.Reply(u, ext.ReplyTextString(fmt.Sprintf("You are in dev or the version var failed to inject: %v", err)), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgUpdateErrorVersionVarInvalid, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
latest, ok, err := ghselfupdate.DetectLatest(config.GitRepo) latest, ok, err := ghselfupdate.DetectLatest(config.GitRepo)
if err != nil { if err != nil {
ctx.Reply(u, ext.ReplyTextString(fmt.Sprintf("检测最新版本失败: %v", err)), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgUpdateErrorCheckLatestFailed, map[string]any{
"Error": err.Error(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if !ok { if !ok {
ctx.Reply(u, ext.ReplyTextString("没有找到版本信息"), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgUpdateErrorNoReleaseFound, nil)), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if latest.Version.Major != currentV.Major { if latest.Version.Major != currentV.Major {
ctx.Reply(u, ext.ReplyTextString(fmt.Sprintf("检测到大版本更新: %s -> %s , 请前往 GitHub 手动下载最新版本并查看迁移指南", currentV, latest.Version)), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgUpdateInfoMajorUpgradeRequired, map[string]any{
"Current": currentV.String(),
"Latest": latest.Version.String(),
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
if latest.Version.LT(currentV) || latest.Version.Equals(currentV) { if latest.Version.LT(currentV) || latest.Version.Equals(currentV) {
ctx.Reply(u, ext.ReplyTextString(fmt.Sprintf("当前已经是最新版本: %s", config.Version)), nil) ctx.Reply(u, ext.ReplyTextString(i18n.T(i18nk.BotMsgUpdateInfoAlreadyLatest, map[string]any{
"Version": config.Version,
})), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
indocker := config.Docker == "true" indocker := config.Docker == "true"
@@ -55,32 +65,28 @@ func handleUpdateCmd(ctx *ext.Context, u *ext.Update) error {
return `<blockquote expandable>` + md + `</blockquote>` return `<blockquote expandable>` + md + `</blockquote>`
}())) }()))
if indocker { if indocker {
text := fmt.Sprintf("发现新版本: %s\n当前版本: %s\n发布时间: %s\n由于您正在使用 Docker 部署, 请自行在部署平台上执行更新命令", text := i18n.T(i18nk.BotMsgUpdateInfoNewVersionInDocker, map[string]any{
latest.Version, "Latest": latest.Version.String(),
config.Version, "Current": config.Version,
latest.PublishedAt.Format("2006-01-02 15:04:05"), "PublishedAt": latest.PublishedAt.Format("2006-01-02 15:04:05"),
) })
ctx.Reply(u, ext.ReplyTextString(text), nil) ctx.Reply(u, ext.ReplyTextString(text), nil)
return dispatcher.EndGroups return dispatcher.EndGroups
} }
text := fmt.Sprintf(`发现新版本: %s text := i18n.T(i18nk.BotMsgUpdateInfoNewVersionPromptUpgrade, map[string]any{
当前版本: %s "Latest": latest.Version.String(),
"Current": config.Version,
文件大小: %.2f MB "SizeMB": float64(latest.AssetByteSize) / (1024 * 1024),
下载链接: %s "URL": latest.AssetURL,
发布时间: %s "PublishedAt": latest.PublishedAt.Format("2006-01-02 15:04:05"),
})
升级将重启 Bot , 是否升级?`, latest.Version, config.Version,
float64(latest.AssetByteSize)/(1024*1024), latest.AssetURL,
latest.PublishedAt.Format("2006-01-02 15:04:05"),
)
ctx.Reply(u, ext.ReplyTextString(text), &ext.ReplyOpts{ ctx.Reply(u, ext.ReplyTextString(text), &ext.ReplyOpts{
Markup: &tg.ReplyInlineMarkup{ Markup: &tg.ReplyInlineMarkup{
Rows: []tg.KeyboardButtonRow{ Rows: []tg.KeyboardButtonRow{
{ {
Buttons: []tg.KeyboardButtonClass{ Buttons: []tg.KeyboardButtonClass{
&tg.KeyboardButtonCallback{ &tg.KeyboardButtonCallback{
Text: "升级", Text: i18n.T(i18nk.BotMsgUpdateButtonUpgrade, nil),
Data: []byte("update"), Data: []byte("update"),
}, },
}, },
@@ -98,19 +104,25 @@ func handleUpdateCallback(ctx *ext.Context, u *ext.Update) error {
} }
ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{ ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{
ID: u.CallbackQuery.GetMsgID(), ID: u.CallbackQuery.GetMsgID(),
Message: fmt.Sprintf("正在升级中, 当前版本: %s", config.Version), Message: i18n.T(i18nk.BotMsgUpdateInfoUpgradingWithVersion, map[string]any{
"Current": config.Version,
}),
}) })
latest, err := ghselfupdate.UpdateSelf(currentV, config.GitRepo) latest, err := ghselfupdate.UpdateSelf(currentV, config.GitRepo)
if err != nil { if err != nil {
ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{ ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{
ID: u.CallbackQuery.GetMsgID(), ID: u.CallbackQuery.GetMsgID(),
Message: fmt.Sprintf("升级失败: %v", err), Message: i18n.T(i18nk.BotMsgUpdateErrorUpgradeFailed, map[string]any{
"Error": err.Error(),
}),
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{ ctx.EditMessage(u.GetUserChat().GetID(), &tg.MessagesEditMessageRequest{
ID: u.CallbackQuery.GetMsgID(), ID: u.CallbackQuery.GetMsgID(),
Message: fmt.Sprintf("已升级至版本 %s\n若 Bot 未自动重启请手动启动", latest.Version), Message: i18n.T(i18nk.BotMsgUpdateInfoUpgradeSuccess, map[string]any{
"Version": latest.Version.String(),
}),
}) })
return errors.New("SAVEANTBOT-RESTART") return errors.New("SAVEANTBOT-RESTART")
} }
+11 -9
View File
@@ -5,22 +5,24 @@ import (
"strings" "strings"
"github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/message/styling"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
) )
func BuildDirHelpStyling(dirs []database.Dir) []styling.StyledTextOption { func BuildDirHelpStyling(dirs []database.Dir) []styling.StyledTextOption {
return []styling.StyledTextOption{ return []styling.StyledTextOption{
styling.Bold("使用方法: /dir <操作> <参数...>"), styling.Bold(i18n.T(i18nk.BotMsgDirHelpUsage, nil)),
styling.Plain("\n\n可用操作:\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpAvailableOps, nil)),
styling.Code("add"), styling.Code("add"),
styling.Plain(" <存储名> <路径> - 添加路径\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpAddSuffix, nil)),
styling.Code("del"), styling.Code("del"),
styling.Plain(" <路径ID> - 删除路径\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpDelSuffix, nil)),
styling.Plain("\n添加路径示例:\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpAddExamplePrefix, nil)),
styling.Code("/dir add local1 path/to/dir"), styling.Code(i18n.T(i18nk.BotMsgDirHelpAddExampleCmd, nil)),
styling.Plain("\n\n删除路径示例:\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpDelExamplePrefix, nil)),
styling.Code("/dir del 3"), styling.Code(i18n.T(i18nk.BotMsgDirHelpDelExampleCmd, nil)),
styling.Plain("\n\n当前已添加的路径:\n"), styling.Plain(i18n.T(i18nk.BotMsgDirHelpExistingDirsPrefix, nil)),
styling.Blockquote(func() string { styling.Blockquote(func() string {
var sb strings.Builder var sb strings.Builder
for _, dir := range dirs { for _, dir := range dirs {
+9 -7
View File
@@ -7,6 +7,8 @@ import (
"github.com/gotd/td/telegram/message/entity" "github.com/gotd/td/telegram/message/entity"
"github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/message/styling"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/pkg/parser" "github.com/krau/SaveAny-Bot/pkg/parser"
) )
@@ -14,15 +16,15 @@ func BuildParsedTextEntity(item parser.Item) (string, []tg.MessageEntityClass, e
eb := entity.Builder{} eb := entity.Builder{}
if err := styling.Perform(&eb, if err := styling.Perform(&eb,
styling.Bold(fmt.Sprintf("[%s]%s", item.Site, item.Title)), styling.Bold(fmt.Sprintf("[%s]%s", item.Site, item.Title)),
styling.Plain("\n链接: "), styling.Plain(i18n.T(i18nk.BotMsgParseInfoLinkPrefix, nil)),
styling.Code(item.URL), styling.Code(item.URL),
styling.Plain("\n作者: "), styling.Plain(i18n.T(i18nk.BotMsgParseInfoAuthorPrefix, nil)),
styling.Code(item.Author), styling.Code(item.Author),
styling.Plain("\n描述: "), styling.Plain(i18n.T(i18nk.BotMsgParseInfoDescriptionPrefix, nil)),
styling.Code(strutil.Ellipsis(item.Description, 233)), styling.Code(strutil.Ellipsis(item.Description, 233)),
styling.Plain("\n文件数量: "), styling.Plain(i18n.T(i18nk.BotMsgParseInfoFileCountPrefix, nil)),
styling.Code(fmt.Sprintf("%d", len(item.Resources))), styling.Code(fmt.Sprintf("%d", len(item.Resources))),
styling.Plain("\n预计总大小: "), styling.Plain(i18n.T(i18nk.BotMsgParseInfoTotalSizePrefix, nil)),
styling.Code(fmt.Sprintf("%.2f MB", func() float64 { styling.Code(fmt.Sprintf("%.2f MB", func() float64 {
var totalSize int64 var totalSize int64
for _, res := range item.Resources { for _, res := range item.Resources {
@@ -30,9 +32,9 @@ func BuildParsedTextEntity(item parser.Item) (string, []tg.MessageEntityClass, e
} }
return float64(totalSize) / 1024 / 1024 return float64(totalSize) / 1024 / 1024
}())), }())),
styling.Plain("\n请选择存储位置"), styling.Plain(i18n.T(i18nk.BotMsgParseInfoPromptSelectStorage, nil)),
); err != nil { ); err != nil {
return "", nil, fmt.Errorf("构建消息失败: %w", err) return "", nil, fmt.Errorf("failed to build parsed text entity: %w", err)
} }
text, entities := eb.Complete() text, entities := eb.Complete()
return text, entities, nil return text, entities, nil
+14 -7
View File
@@ -5,21 +5,28 @@ import (
"strings" "strings"
"github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/message/styling"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
) )
func BuildRuleHelpStyling(enabled bool, rules []database.Rule) []styling.StyledTextOption { func BuildRuleHelpStyling(enabled bool, rules []database.Rule) []styling.StyledTextOption {
return []styling.StyledTextOption{ return []styling.StyledTextOption{
styling.Bold("使用方法: /rule <操作> <参数...>"), styling.Bold(i18n.T(i18nk.BotMsgRuleHelpUsage, nil)),
styling.Bold(fmt.Sprintf("\n当前已%s规则模式", map[bool]string{true: "启用", false: "禁用"}[enabled])), styling.Bold(func() string {
styling.Plain("\n\n可用操作:\n"), if enabled {
return i18n.T(i18nk.BotMsgRuleHelpCurrentModeEnabled, nil)
}
return i18n.T(i18nk.BotMsgRuleHelpCurrentModeDisabled, nil)
}()),
styling.Plain(i18n.T(i18nk.BotMsgRuleHelpAvailableOps, nil)),
styling.Code("switch"), styling.Code("switch"),
styling.Plain(" - 开关规则模式\n"), styling.Plain(i18n.T(i18nk.BotMsgRuleHelpSwitchSuffix, nil)),
styling.Code("add"), styling.Code("add"),
styling.Plain(" <类型> <数据> <存储名> <路径> - 添加规则\n"), styling.Plain(i18n.T(i18nk.BotMsgRuleHelpAddSuffix, nil)),
styling.Code("del"), styling.Code("del"),
styling.Plain(" <规则ID> - 删除规则\n"), styling.Plain(i18n.T(i18nk.BotMsgRuleHelpDelSuffix, nil)),
styling.Plain("\n当前已添加的规则:\n"), styling.Plain(i18n.T(i18nk.BotMsgRuleHelpExistingRulesPrefix, nil)),
styling.Blockquote(func() string { styling.Blockquote(func() string {
var sb strings.Builder var sb strings.Builder
for _, rule := range rules { for _, rule := range rules {
+9 -4
View File
@@ -9,6 +9,8 @@ import (
"github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/message/styling"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/cache" "github.com/krau/SaveAny-Bot/common/cache"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/database"
"github.com/krau/SaveAny-Bot/pkg/enums/tasktype" "github.com/krau/SaveAny-Bot/pkg/enums/tasktype"
"github.com/krau/SaveAny-Bot/pkg/tcbdata" "github.com/krau/SaveAny-Bot/pkg/tcbdata"
@@ -70,11 +72,14 @@ func BuildAddSelectStorageKeyboard(stors []storage.Storage, adddata tcbdata.Add)
func BuildAddOneSelectStorageMessage(ctx context.Context, stors []storage.Storage, file tfile.TGFileMessage, msgId int) (*tg.MessagesEditMessageRequest, error) { func BuildAddOneSelectStorageMessage(ctx context.Context, stors []storage.Storage, file tfile.TGFileMessage, msgId int) (*tg.MessagesEditMessageRequest, error) {
eb := entity.Builder{} eb := entity.Builder{}
var entities []tg.MessageEntityClass var entities []tg.MessageEntityClass
text := fmt.Sprintf("文件名: %s\n请选择存储位置", file.Name()) text := i18n.T(i18nk.BotMsgTasksInfoAddedToQueueFull, map[string]any{
"Filename": file.Name(),
"QueueLength": 0,
})
if err := styling.Perform(&eb, if err := styling.Perform(&eb,
styling.Plain("文件名: "), styling.Plain(i18n.T(i18nk.BotMsgStorageInfoFilenamePrefix, nil)),
styling.Code(file.Name()), styling.Code(file.Name()),
styling.Plain("\n请选择存储位置"), styling.Plain(i18n.T(i18nk.BotMsgStorageInfoPromptSelectStorage, nil)),
); err != nil { ); err != nil {
log.FromContext(ctx).Errorf("Failed to build entity: %s", err) log.FromContext(ctx).Errorf("Failed to build entity: %s", err)
} else { } else {
@@ -185,7 +190,7 @@ func BuildSetDirMarkupForAdd(dirs []database.Dir, dataid string) (*tg.ReplyInlin
return nil, fmt.Errorf("failed to set default directory data in cache: %w", err) return nil, fmt.Errorf("failed to set default directory data in cache: %w", err)
} }
buttons = append(buttons, &tg.KeyboardButtonCallback{ buttons = append(buttons, &tg.KeyboardButtonCallback{
Text: "默认", Text: i18n.T(i18nk.BotMsgDirButtonDefault, nil),
Data: fmt.Appendf(nil, "%s %s", tcbdata.TypeAdd, dirDefaultDataId), Data: fmt.Appendf(nil, "%s %s", tcbdata.TypeAdd, dirDefaultDataId),
}) })
markup := &tg.ReplyInlineMarkup{} markup := &tg.ReplyInlineMarkup{}
+9 -4
View File
@@ -2,13 +2,14 @@ package msgelem
import ( import (
"context" "context"
"fmt"
"strconv" "strconv"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/gotd/td/telegram/message/entity" "github.com/gotd/td/telegram/message/entity"
"github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/message/styling"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
) )
func BuildTaskAddedEntities( func BuildTaskAddedEntities(
@@ -18,11 +19,15 @@ func BuildTaskAddedEntities(
) (string, []tg.MessageEntityClass) { ) (string, []tg.MessageEntityClass) {
entityBuilder := entity.Builder{} entityBuilder := entity.Builder{}
var entities []tg.MessageEntityClass var entities []tg.MessageEntityClass
text := fmt.Sprintf("已添加到任务队列\n文件名: %s\n当前排队任务数: %d", filename, queueLength) text := i18n.T(i18nk.BotMsgTasksInfoAddedToQueueFull, map[string]any{
"Filename": filename,
"QueueLength": queueLength,
})
if err := styling.Perform(&entityBuilder, if err := styling.Perform(&entityBuilder,
styling.Plain("已添加到任务队列\n文件名: "), styling.Plain(i18n.T(i18nk.BotMsgTasksInfoAddedToQueuePrefix, nil)),
styling.Plain(i18n.T(i18nk.BotMsgTasksInfoFilenamePrefix, nil)),
styling.Code(filename), styling.Code(filename),
styling.Plain("\n当前排队任务数: "), styling.Plain(i18n.T(i18nk.BotMsgTasksInfoQueueLengthPrefix, nil)),
styling.Bold(strconv.Itoa(queueLength)), styling.Bold(strconv.Itoa(queueLength)),
); err != nil { ); err != nil {
log.FromContext(ctx).Errorf("Failed to build entity: %s", err) log.FromContext(ctx).Errorf("Failed to build entity: %s", err)
@@ -5,6 +5,8 @@ import (
"github.com/celestix/gotgproto/ext" "github.com/celestix/gotgproto/ext"
"github.com/charmbracelet/log" "github.com/charmbracelet/log"
"github.com/gotd/td/tg" "github.com/gotd/td/tg"
"github.com/krau/SaveAny-Bot/common/i18n"
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
"github.com/krau/SaveAny-Bot/common/utils/tgutil" "github.com/krau/SaveAny-Bot/common/utils/tgutil"
"github.com/krau/SaveAny-Bot/core" "github.com/krau/SaveAny-Bot/core"
"github.com/krau/SaveAny-Bot/core/tasks/directlinks" "github.com/krau/SaveAny-Bot/core/tasks/directlinks"
@@ -18,13 +20,15 @@ func CreateAndAddDirectTaskWithEdit(ctx *ext.Context, stor storage.Storage, dirP
if err := core.AddTask(injectCtx, task); err != nil { if err := core.AddTask(injectCtx, task); err != nil {
log.FromContext(ctx).Errorf("Failed to add task: %s", err) log.FromContext(ctx).Errorf("Failed to add task: %s", err)
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
ID: msgID, ID: msgID,
Message: "任务添加失败: " + err.Error(), Message: i18n.T(i18nk.BotMsgCommonErrorTaskAddFailed, map[string]any{
"Error": err.Error(),
}),
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
} }
ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{ ctx.EditMessage(userID, &tg.MessagesEditMessageRequest{
Message: "任务已添加", Message: i18n.T(i18nk.BotMsgCommonInfoTaskAdded, nil),
}) })
return dispatcher.EndGroups return dispatcher.EndGroups
} }
+36
View File
@@ -8,6 +8,7 @@ const (
BotMsgCancelInfoCancelRequested Key = "bot.msg.cancel.info_cancel_requested" BotMsgCancelInfoCancelRequested Key = "bot.msg.cancel.info_cancel_requested"
BotMsgCancelInfoCancellingTask Key = "bot.msg.cancel.info_cancelling_task" BotMsgCancelInfoCancellingTask Key = "bot.msg.cancel.info_cancelling_task"
BotMsgCancelUsage Key = "bot.msg.cancel.usage" BotMsgCancelUsage Key = "bot.msg.cancel.usage"
BotMsgCommonErrorBuildDirSelectKeyboardFailed Key = "bot.msg.common.error_build_dir_select_keyboard_failed"
BotMsgCommonErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.common.error_build_storage_select_keyboard_failed" BotMsgCommonErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.common.error_build_storage_select_keyboard_failed"
BotMsgCommonErrorBuildStorageSelectMessageFailed Key = "bot.msg.common.error_build_storage_select_message_failed" BotMsgCommonErrorBuildStorageSelectMessageFailed Key = "bot.msg.common.error_build_storage_select_message_failed"
BotMsgCommonErrorDataExpired Key = "bot.msg.common.error_data_expired" BotMsgCommonErrorDataExpired Key = "bot.msg.common.error_data_expired"
@@ -26,6 +27,7 @@ const (
BotMsgCommonErrorNoAvailableStorage Key = "bot.msg.common.error_no_available_storage" BotMsgCommonErrorNoAvailableStorage Key = "bot.msg.common.error_no_available_storage"
BotMsgCommonErrorNoImagesInTelegraphPage Key = "bot.msg.common.error_no_images_in_telegraph_page" BotMsgCommonErrorNoImagesInTelegraphPage Key = "bot.msg.common.error_no_images_in_telegraph_page"
BotMsgCommonErrorNoMessagesInRange Key = "bot.msg.common.error_no_messages_in_range" BotMsgCommonErrorNoMessagesInRange Key = "bot.msg.common.error_no_messages_in_range"
BotMsgCommonErrorNoPermission Key = "bot.msg.common.error_no_permission"
BotMsgCommonErrorNoSavableFilesFound Key = "bot.msg.common.error_no_savable_files_found" BotMsgCommonErrorNoSavableFilesFound Key = "bot.msg.common.error_no_savable_files_found"
BotMsgCommonErrorNoSavableMessagesInRange Key = "bot.msg.common.error_no_savable_messages_in_range" BotMsgCommonErrorNoSavableMessagesInRange Key = "bot.msg.common.error_no_savable_messages_in_range"
BotMsgCommonErrorParseTelegraphPathFailed Key = "bot.msg.common.error_parse_telegraph_path_failed" BotMsgCommonErrorParseTelegraphPathFailed Key = "bot.msg.common.error_parse_telegraph_path_failed"
@@ -49,18 +51,32 @@ const (
BotMsgConfigErrorInvalidCallbackData Key = "bot.msg.config.error_invalid_callback_data" BotMsgConfigErrorInvalidCallbackData Key = "bot.msg.config.error_invalid_callback_data"
BotMsgConfigErrorInvalidTemplate Key = "bot.msg.config.error_invalid_template" BotMsgConfigErrorInvalidTemplate Key = "bot.msg.config.error_invalid_template"
BotMsgConfigFnametmplHelp Key = "bot.msg.config.fnametmpl_help" BotMsgConfigFnametmplHelp Key = "bot.msg.config.fnametmpl_help"
BotMsgConfigInfoCurrentTemplatePrefix Key = "bot.msg.config.info_current_template_prefix"
BotMsgConfigInfoFilenameStrategySet Key = "bot.msg.config.info_filename_strategy_set" BotMsgConfigInfoFilenameStrategySet Key = "bot.msg.config.info_filename_strategy_set"
BotMsgConfigInfoTemplateUpdated Key = "bot.msg.config.info_template_updated" BotMsgConfigInfoTemplateUpdated Key = "bot.msg.config.info_template_updated"
BotMsgConfigPromptSelectFilenameStrategy Key = "bot.msg.config.prompt_select_filename_strategy" BotMsgConfigPromptSelectFilenameStrategy Key = "bot.msg.config.prompt_select_filename_strategy"
BotMsgConfigPromptSelectOption Key = "bot.msg.config.prompt_select_option" BotMsgConfigPromptSelectOption Key = "bot.msg.config.prompt_select_option"
BotMsgDirButtonDefault Key = "bot.msg.dir.button_default"
BotMsgDirErrorCreateDirFailed Key = "bot.msg.dir.error_create_dir_failed" BotMsgDirErrorCreateDirFailed Key = "bot.msg.dir.error_create_dir_failed"
BotMsgDirErrorDeleteDirFailed Key = "bot.msg.dir.error_delete_dir_failed" BotMsgDirErrorDeleteDirFailed Key = "bot.msg.dir.error_delete_dir_failed"
BotMsgDirErrorGetUserDirsFailed Key = "bot.msg.dir.error_get_user_dirs_failed" BotMsgDirErrorGetUserDirsFailed Key = "bot.msg.dir.error_get_user_dirs_failed"
BotMsgDirErrorGetUserFailed Key = "bot.msg.dir.error_get_user_failed" BotMsgDirErrorGetUserFailed Key = "bot.msg.dir.error_get_user_failed"
BotMsgDirErrorInvalidDirId Key = "bot.msg.dir.error_invalid_dir_id" BotMsgDirErrorInvalidDirId Key = "bot.msg.dir.error_invalid_dir_id"
BotMsgDirErrorUnknownOperation Key = "bot.msg.dir.error_unknown_operation" BotMsgDirErrorUnknownOperation Key = "bot.msg.dir.error_unknown_operation"
BotMsgDirHelpAddExampleCmd Key = "bot.msg.dir.help_add_example_cmd"
BotMsgDirHelpAddExamplePrefix Key = "bot.msg.dir.help_add_example_prefix"
BotMsgDirHelpAddSuffix Key = "bot.msg.dir.help_add_suffix"
BotMsgDirHelpAvailableOps Key = "bot.msg.dir.help_available_ops"
BotMsgDirHelpDelExampleCmd Key = "bot.msg.dir.help_del_example_cmd"
BotMsgDirHelpDelExamplePrefix Key = "bot.msg.dir.help_del_example_prefix"
BotMsgDirHelpDelSuffix Key = "bot.msg.dir.help_del_suffix"
BotMsgDirHelpExistingDirsPrefix Key = "bot.msg.dir.help_existing_dirs_prefix"
BotMsgDirHelpUsage Key = "bot.msg.dir.help_usage"
BotMsgDirInfoCreateDirSuccess Key = "bot.msg.dir.info_create_dir_success" BotMsgDirInfoCreateDirSuccess Key = "bot.msg.dir.info_create_dir_success"
BotMsgDirInfoDeleteDirSuccess Key = "bot.msg.dir.info_delete_dir_success" BotMsgDirInfoDeleteDirSuccess Key = "bot.msg.dir.info_delete_dir_success"
BotMsgDlErrorNoValidLinks Key = "bot.msg.dl.error_no_valid_links"
BotMsgDlInfoFilesSelectStorage Key = "bot.msg.dl.info_files_select_storage"
BotMsgDlUsage Key = "bot.msg.dl.usage"
BotMsgHelpTextFmt Key = "bot.msg.help_text_fmt" BotMsgHelpTextFmt Key = "bot.msg.help_text_fmt"
BotMsgMediaGroupErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.media_group.error_build_storage_select_keyboard_failed" BotMsgMediaGroupErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.media_group.error_build_storage_select_keyboard_failed"
BotMsgMediaGroupInfoGroupFoundFilesSelectStorage Key = "bot.msg.media_group.info_group_found_files_select_storage" BotMsgMediaGroupInfoGroupFoundFilesSelectStorage Key = "bot.msg.media_group.info_group_found_files_select_storage"
@@ -68,7 +84,13 @@ const (
BotMsgParseErrorBuildParsedTextEntityFailed Key = "bot.msg.parse.error_build_parsed_text_entity_failed" BotMsgParseErrorBuildParsedTextEntityFailed Key = "bot.msg.parse.error_build_parsed_text_entity_failed"
BotMsgParseErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.parse.error_build_storage_select_keyboard_failed" BotMsgParseErrorBuildStorageSelectKeyboardFailed Key = "bot.msg.parse.error_build_storage_select_keyboard_failed"
BotMsgParseErrorParseTextFailed Key = "bot.msg.parse.error_parse_text_failed" BotMsgParseErrorParseTextFailed Key = "bot.msg.parse.error_parse_text_failed"
BotMsgParseInfoAuthorPrefix Key = "bot.msg.parse.info_author_prefix"
BotMsgParseInfoDescriptionPrefix Key = "bot.msg.parse.info_description_prefix"
BotMsgParseInfoFileCountPrefix Key = "bot.msg.parse.info_file_count_prefix"
BotMsgParseInfoLinkPrefix Key = "bot.msg.parse.info_link_prefix"
BotMsgParseInfoParsing Key = "bot.msg.parse.info_parsing" BotMsgParseInfoParsing Key = "bot.msg.parse.info_parsing"
BotMsgParseInfoPromptSelectStorage Key = "bot.msg.parse.info_prompt_select_storage"
BotMsgParseInfoTotalSizePrefix Key = "bot.msg.parse.info_total_size_prefix"
BotMsgParserErrorDownloadFileFailed Key = "bot.msg.parser.error_download_file_failed" BotMsgParserErrorDownloadFileFailed Key = "bot.msg.parser.error_download_file_failed"
BotMsgParserErrorFileTooLarge Key = "bot.msg.parser.error_file_too_large" BotMsgParserErrorFileTooLarge Key = "bot.msg.parser.error_file_too_large"
BotMsgParserErrorGetFilenameFailed Key = "bot.msg.parser.error_get_filename_failed" BotMsgParserErrorGetFilenameFailed Key = "bot.msg.parser.error_get_filename_failed"
@@ -86,6 +108,14 @@ const (
BotMsgRuleErrorInvalidRuleId Key = "bot.msg.rule.error_invalid_rule_id" BotMsgRuleErrorInvalidRuleId Key = "bot.msg.rule.error_invalid_rule_id"
BotMsgRuleErrorInvalidRuleType Key = "bot.msg.rule.error_invalid_rule_type" BotMsgRuleErrorInvalidRuleType Key = "bot.msg.rule.error_invalid_rule_type"
BotMsgRuleErrorUpdateUserFailed Key = "bot.msg.rule.error_update_user_failed" BotMsgRuleErrorUpdateUserFailed Key = "bot.msg.rule.error_update_user_failed"
BotMsgRuleHelpAddSuffix Key = "bot.msg.rule.help_add_suffix"
BotMsgRuleHelpAvailableOps Key = "bot.msg.rule.help_available_ops"
BotMsgRuleHelpCurrentModeDisabled Key = "bot.msg.rule.help_current_mode_disabled"
BotMsgRuleHelpCurrentModeEnabled Key = "bot.msg.rule.help_current_mode_enabled"
BotMsgRuleHelpDelSuffix Key = "bot.msg.rule.help_del_suffix"
BotMsgRuleHelpExistingRulesPrefix Key = "bot.msg.rule.help_existing_rules_prefix"
BotMsgRuleHelpSwitchSuffix Key = "bot.msg.rule.help_switch_suffix"
BotMsgRuleHelpUsage Key = "bot.msg.rule.help_usage"
BotMsgRuleInfoCreateRuleSuccess Key = "bot.msg.rule.info_create_rule_success" BotMsgRuleInfoCreateRuleSuccess Key = "bot.msg.rule.info_create_rule_success"
BotMsgRuleInfoDeleteRuleSuccess Key = "bot.msg.rule.info_delete_rule_success" BotMsgRuleInfoDeleteRuleSuccess Key = "bot.msg.rule.info_delete_rule_success"
BotMsgRuleInfoRuleModeDisabled Key = "bot.msg.rule.info_rule_mode_disabled" BotMsgRuleInfoRuleModeDisabled Key = "bot.msg.rule.info_rule_mode_disabled"
@@ -93,12 +123,18 @@ const (
BotMsgRulePromptProvideRuleId Key = "bot.msg.rule.prompt_provide_rule_id" BotMsgRulePromptProvideRuleId Key = "bot.msg.rule.prompt_provide_rule_id"
BotMsgSaveErrorInvalidIdOrUsername Key = "bot.msg.save.error_invalid_id_or_username" BotMsgSaveErrorInvalidIdOrUsername Key = "bot.msg.save.error_invalid_id_or_username"
BotMsgSaveHelpText Key = "bot.msg.save_help_text" BotMsgSaveHelpText Key = "bot.msg.save_help_text"
BotMsgStorageInfoFilenamePrefix Key = "bot.msg.storage.info_filename_prefix"
BotMsgStorageInfoPromptSelectStorage Key = "bot.msg.storage.info_prompt_select_storage"
BotMsgTasksCancelFailed Key = "bot.msg.tasks.cancel_failed" BotMsgTasksCancelFailed Key = "bot.msg.tasks.cancel_failed"
BotMsgTasksCancelRequestedPrefix Key = "bot.msg.tasks.cancel_requested_prefix" BotMsgTasksCancelRequestedPrefix Key = "bot.msg.tasks.cancel_requested_prefix"
BotMsgTasksFieldCreated Key = "bot.msg.tasks.field_created" BotMsgTasksFieldCreated Key = "bot.msg.tasks.field_created"
BotMsgTasksFieldId Key = "bot.msg.tasks.field_id" BotMsgTasksFieldId Key = "bot.msg.tasks.field_id"
BotMsgTasksFieldStatus Key = "bot.msg.tasks.field_status" BotMsgTasksFieldStatus Key = "bot.msg.tasks.field_status"
BotMsgTasksFieldTitle Key = "bot.msg.tasks.field_title" BotMsgTasksFieldTitle Key = "bot.msg.tasks.field_title"
BotMsgTasksInfoAddedToQueueFull Key = "bot.msg.tasks.info_added_to_queue_full"
BotMsgTasksInfoAddedToQueuePrefix Key = "bot.msg.tasks.info_added_to_queue_prefix"
BotMsgTasksInfoFilenamePrefix Key = "bot.msg.tasks.info_filename_prefix"
BotMsgTasksInfoQueueLengthPrefix Key = "bot.msg.tasks.info_queue_length_prefix"
BotMsgTasksQueuedEmpty Key = "bot.msg.tasks.queued_empty" BotMsgTasksQueuedEmpty Key = "bot.msg.tasks.queued_empty"
BotMsgTasksQueuedTitle Key = "bot.msg.tasks.queued_title" BotMsgTasksQueuedTitle Key = "bot.msg.tasks.queued_title"
BotMsgTasksRunningEmpty Key = "bot.msg.tasks.running_empty" BotMsgTasksRunningEmpty Key = "bot.msg.tasks.running_empty"
+41 -1
View File
@@ -103,6 +103,10 @@ bot:
info_fetching_telegraph_page: "正在获取 telegraph 页面..." info_fetching_telegraph_page: "正在获取 telegraph 页面..."
error_get_telegraph_page_failed: "获取 telegraph 页面失败: {{.Error}}" error_get_telegraph_page_failed: "获取 telegraph 页面失败: {{.Error}}"
error_no_images_in_telegraph_page: "在 telegraph 页面中未找到图片" error_no_images_in_telegraph_page: "在 telegraph 页面中未找到图片"
error_build_dir_select_keyboard_failed: "构建目录选择键盘失败: {{.Error}}"
error_no_permission: |
您不在白名单中, 无法使用此 Bot.
您可以部署自己的实例: https://github.com/krau/SaveAny-Bot
save: save:
error_invalid_id_or_username: "无效的ID或用户名: {{.Error}}" error_invalid_id_or_username: "无效的ID或用户名: {{.Error}}"
watch: watch:
@@ -135,6 +139,10 @@ bot:
queued_empty: "当前没有排队中的任务" queued_empty: "当前没有排队中的任务"
queued_title: "当前排队中的任务:" queued_title: "当前排队中的任务:"
truncated_note: "...\n只显示前 10 个任务, 共 {{.Count}} 个任务" truncated_note: "...\n只显示前 10 个任务, 共 {{.Count}} 个任务"
info_added_to_queue_full: "已添加到任务队列\n文件名: {{.Filename}}\n当前排队任务数: {{.QueueLength}}"
info_added_to_queue_prefix: "已添加到任务队列\n"
info_filename_prefix: "文件名: "
info_queue_length_prefix: "\n当前排队任务数: "
rule: rule:
error_get_user_rules_failed: "获取用户规则失败" error_get_user_rules_failed: "获取用户规则失败"
error_update_user_failed: "更新用户失败" error_update_user_failed: "更新用户失败"
@@ -147,6 +155,14 @@ bot:
error_invalid_rule_id: "无效的规则ID" error_invalid_rule_id: "无效的规则ID"
error_delete_rule_failed: "删除规则失败" error_delete_rule_failed: "删除规则失败"
info_delete_rule_success: "删除规则成功" info_delete_rule_success: "删除规则成功"
help_usage: "使用方法: /rule <操作> <参数...>"
help_current_mode_enabled: "\n当前已启用规则模式"
help_current_mode_disabled: "\n当前已禁用规则模式"
help_available_ops: "\n\n可用操作:\n"
help_switch_suffix: " - 开关规则模式\n"
help_add_suffix: " <类型> <数据> <存储名> <路径> - 添加规则\n"
help_del_suffix: " <规则ID> - 删除规则\n"
help_existing_rules_prefix: "\n当前已添加的规则:\n"
dir: dir:
error_get_user_dirs_failed: "获取用户文件夹失败" error_get_user_dirs_failed: "获取用户文件夹失败"
error_get_user_failed: "获取用户失败" error_get_user_failed: "获取用户失败"
@@ -156,6 +172,16 @@ bot:
error_delete_dir_failed: "删除文件夹失败" error_delete_dir_failed: "删除文件夹失败"
info_delete_dir_success: "文件夹删除成功" info_delete_dir_success: "文件夹删除成功"
error_unknown_operation: "未知操作" error_unknown_operation: "未知操作"
help_usage: "使用方法: /dir <操作> <参数...>"
help_available_ops: "\n\n可用操作:\n"
help_add_suffix: " <存储名> <路径> - 添加路径\n"
help_del_suffix: " <路径ID> - 删除路径\n"
help_add_example_prefix: "\n添加路径示例:\n"
help_add_example_cmd: "/dir add local1 path/to/dir"
help_del_example_prefix: "\n\n删除路径示例:\n"
help_del_example_cmd: "/dir del 3"
help_existing_dirs_prefix: "\n\n当前已添加的路径:\n"
button_default: "默认"
parser: parser:
help_text: | help_text: |
用法: 用法:
@@ -176,6 +202,12 @@ bot:
error_parse_text_failed: "Failed to parse text: {{.Error}}" error_parse_text_failed: "Failed to parse text: {{.Error}}"
error_build_storage_select_keyboard_failed: "Failed to build storage selection keyboard: {{.Error}}" error_build_storage_select_keyboard_failed: "Failed to build storage selection keyboard: {{.Error}}"
error_build_parsed_text_entity_failed: "Failed to build parsed text entity: {{.Error}}" error_build_parsed_text_entity_failed: "Failed to build parsed text entity: {{.Error}}"
info_link_prefix: "\n链接: "
info_author_prefix: "\n作者: "
info_description_prefix: "\n描述: "
info_file_count_prefix: "\n文件数量: "
info_total_size_prefix: "\n预计总大小: "
info_prompt_select_storage: "\n请选择存储位置"
telegraph: telegraph:
error_build_storage_select_keyboard_failed: "构建存储选择键盘失败: {{.Error}}" error_build_storage_select_keyboard_failed: "构建存储选择键盘失败: {{.Error}}"
info_title_prefix: "标题: " info_title_prefix: "标题: "
@@ -199,7 +231,7 @@ bot:
文件大小: {{.SizeMB}} MB 文件大小: {{.SizeMB}} MB
下载链接: {{.URL}} 下载链接: {{.URL}}
发布时间: {{.PublishedAt}} 发布时间: {{.PublishedAt}}
升级将重启 Bot , 是否升级? 升级将重启 Bot , 是否升级?
info_upgrading_with_version: "正在升级中, 当前版本: {{.Current}}" info_upgrading_with_version: "正在升级中, 当前版本: {{.Current}}"
error_upgrade_failed: "升级失败: {{.Error}}" error_upgrade_failed: "升级失败: {{.Error}}"
@@ -226,6 +258,11 @@ bot:
模板仅在文件名策略设置为 '自定义模板' 时生效, 且模板解析错误时会回退到默认文件名 模板仅在文件名策略设置为 '自定义模板' 时生效, 且模板解析错误时会回退到默认文件名
error_invalid_template: "无效的模板, 请检查语法\n{{.Error}}" error_invalid_template: "无效的模板, 请检查语法\n{{.Error}}"
info_template_updated: "已更新文件名模板" info_template_updated: "已更新文件名模板"
info_current_template_prefix: "当前模板: {{.Template}}"
dl:
usage: "用法: /dl <链接1> <链接2> ..."
error_no_valid_links: "没有有效的链接可供下载"
info_files_select_storage: "共 {{.Count}} 个文件, 请选择存储位置"
cancel: cancel:
usage: "用法: /cancel <task_id>" usage: "用法: /cancel <task_id>"
error_cancel_failed: "取消任务失败: {{.Error}}" error_cancel_failed: "取消任务失败: {{.Error}}"
@@ -235,3 +272,6 @@ bot:
info_saving_files: "正在保存文件..." info_saving_files: "正在保存文件..."
error_build_storage_select_keyboard_failed: "构建存储选择键盘失败: {{.Error}}" error_build_storage_select_keyboard_failed: "构建存储选择键盘失败: {{.Error}}"
info_group_found_files_select_storage: "共 {{.Count}} 个文件, 请选择存储位置" info_group_found_files_select_storage: "共 {{.Count}} 个文件, 请选择存储位置"
storage:
info_filename_prefix: "文件名: "
info_prompt_select_storage: "\n请选择存储位置"