Skip to content

Commit 3d20fbd

Browse files
committed
feat: implement transfer command for file transfers between storages
1 parent e6d8cc7 commit 3d20fbd

File tree

8 files changed

+166
-159
lines changed

8 files changed

+166
-159
lines changed

client/bot/handlers/register.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ var CommandHandlers = []DescCommandHandler{
3131
{"dl", i18nk.BotMsgCmdDl, handleDlCmd},
3232
{"aria2dl", i18nk.BotMsgCmdAria2dl, handleAria2DlCmd},
3333
{"ytdlp", i18nk.BotMsgCmdYtdlp, handleYtdlpCmd},
34-
{"import", i18nk.BotMsgCmdImport, handleImportCmd},
34+
{"transfer", i18nk.BotMsgCmdTransfer, handleTransferCmd},
3535
{"task", i18nk.BotMsgCmdTask, handleTaskCmd},
3636
{"cancel", i18nk.BotMsgCmdCancel, handleCancelCmd},
3737
{"config", i18nk.BotMsgCmdConfig, handleConfigCmd},
Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package handlers
33
import (
44
"fmt"
55
"regexp"
6+
"strings"
67

78
"github.com/celestix/gotgproto/dispatcher"
89
"github.com/celestix/gotgproto/ext"
@@ -12,90 +13,112 @@ import (
1213
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
1314
"github.com/krau/SaveAny-Bot/common/utils/strutil"
1415
"github.com/krau/SaveAny-Bot/common/utils/tgutil"
15-
"github.com/krau/SaveAny-Bot/config"
16-
storconfig "github.com/krau/SaveAny-Bot/config/storage"
1716
"github.com/krau/SaveAny-Bot/core"
1817
"github.com/krau/SaveAny-Bot/core/tasks/batchimport"
1918
"github.com/krau/SaveAny-Bot/pkg/storagetypes"
2019
"github.com/krau/SaveAny-Bot/storage"
2120
"github.com/rs/xid"
2221
)
2322

24-
func handleImportCmd(ctx *ext.Context, update *ext.Update) error {
23+
func handleTransferCmd(ctx *ext.Context, update *ext.Update) error {
2524
logger := log.FromContext(ctx)
2625
args := strutil.ParseArgsRespectQuotes(update.EffectiveMessage.Text)
2726

2827
if len(args) < 3 {
29-
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportUsage, nil)), nil)
28+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferUsage, nil)), nil)
3029
return dispatcher.EndGroups
3130
}
3231

33-
storageName := args[1]
34-
dirPath := args[2]
32+
// Parse source: storage_name:/path
33+
sourceParts := strings.SplitN(args[1], ":", 2)
34+
if len(sourceParts) != 2 {
35+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorInvalidSource, nil)), nil)
36+
return dispatcher.EndGroups
37+
}
38+
sourceStorageName := sourceParts[0]
39+
sourcePath := sourceParts[1]
40+
41+
// Parse target: storage_name:/path
42+
targetParts := strings.SplitN(args[2], ":", 2)
43+
if len(targetParts) != 2 {
44+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorInvalidTarget, nil)), nil)
45+
return dispatcher.EndGroups
46+
}
47+
targetStorageName := targetParts[0]
48+
targetPath := targetParts[1]
3549

3650
userID := update.GetUserChat().GetID()
3751

38-
stor, err := storage.GetStorageByUserIDAndName(ctx, userID, storageName)
52+
// Get source storage
53+
sourceStorage, err := storage.GetStorageByUserIDAndName(ctx, userID, sourceStorageName)
3954
if err != nil {
40-
logger.Errorf("Failed to get storage by user ID and name: %s", err)
41-
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportErrorStorageNotFound, map[string]any{
42-
"StorageName": storageName,
55+
logger.Errorf("Failed to get source storage by user ID and name: %s", err)
56+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorStorageNotFound, map[string]any{
57+
"StorageName": sourceStorageName,
4358
"Error": err,
4459
})), nil)
4560
return dispatcher.EndGroups
4661
}
4762

48-
listable, ok := stor.(storage.StorageListable)
63+
// Check if source storage supports listing
64+
listable, ok := sourceStorage.(storage.StorageListable)
4965
if !ok {
50-
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportErrorStorageNotListable, map[string]any{
51-
"StorageName": storageName,
66+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorStorageNotListable, map[string]any{
67+
"StorageName": sourceStorageName,
5268
})), nil)
5369
return dispatcher.EndGroups
5470
}
5571

56-
_, ok = stor.(storage.StorageReadable)
72+
// Check if source storage supports reading
73+
_, ok = sourceStorage.(storage.StorageReadable)
5774
if !ok {
58-
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportErrorStorageNotReadable, map[string]any{
59-
"StorageName": storageName,
75+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorStorageNotReadable, map[string]any{
76+
"StorageName": sourceStorageName,
6077
})), nil)
6178
return dispatcher.EndGroups
6279
}
6380

64-
telegramStorage, err := storage.GetTelegramStorageByUserID(ctx, userID)
81+
// Get target storage
82+
targetStorage, err := storage.GetStorageByUserIDAndName(ctx, userID, targetStorageName)
6583
if err != nil {
66-
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportErrorNoTelegramStorage, map[string]any{
67-
"Error": err,
84+
logger.Errorf("Failed to get target storage by user ID and name: %s", err)
85+
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferErrorTargetNotFound, map[string]any{
86+
"StorageName": targetStorageName,
87+
"Error": err,
6888
})), nil)
6989
return dispatcher.EndGroups
7090
}
7191

72-
replied, err := ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgImportInfoFetchingFiles, nil)), nil)
92+
// Fetch file list
93+
replied, err := ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgTransferInfoFetchingFiles, nil)), nil)
7394
if err != nil {
7495
logger.Errorf("Failed to reply: %s", err)
7596
return dispatcher.EndGroups
7697
}
7798

78-
files, err := listable.ListFiles(ctx, dirPath)
99+
files, err := listable.ListFiles(ctx, sourcePath)
79100
if err != nil {
80101
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
81102
ID: replied.ID,
82-
Message: i18n.T(i18nk.BotMsgImportErrorListFilesFailed, map[string]any{"Error": err}),
103+
Message: i18n.T(i18nk.BotMsgTransferErrorListFilesFailed, map[string]any{"Error": err}),
83104
})
84105
return dispatcher.EndGroups
85106
}
86107

108+
// Optional filter
87109
var filter *regexp.Regexp
88-
if len(args) >= 5 {
89-
filter, err = regexp.Compile(args[4])
110+
if len(args) >= 4 {
111+
filter, err = regexp.Compile(args[3])
90112
if err != nil {
91113
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
92114
ID: replied.ID,
93-
Message: i18n.T(i18nk.BotMsgImportErrorInvalidRegex, map[string]any{"Error": err}),
115+
Message: i18n.T(i18nk.BotMsgTransferErrorInvalidRegex, map[string]any{"Error": err}),
94116
})
95117
return dispatcher.EndGroups
96118
}
97119
}
98120

121+
// Filter files
99122
filteredFiles := make([]storagetypes.FileInfo, 0)
100123
for _, file := range files {
101124
if file.IsDir {
@@ -110,47 +133,21 @@ func handleImportCmd(ctx *ext.Context, update *ext.Update) error {
110133
if len(filteredFiles) == 0 {
111134
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
112135
ID: replied.ID,
113-
Message: i18n.T(i18nk.BotMsgImportErrorNoFilesToImport, nil),
114-
})
115-
return dispatcher.EndGroups
116-
}
117-
118-
// Get default chat_id from Telegram storage config
119-
targetChatID := int64(0)
120-
if telegramCfg := config.C().GetStorageByName(telegramStorage.Name()); telegramCfg != nil {
121-
if tgCfg, ok := telegramCfg.(*storconfig.TelegramStorageConfig); ok {
122-
targetChatID = tgCfg.ChatID
123-
}
124-
}
125-
126-
if len(args) >= 4 {
127-
parsedChatID, err := tgutil.ParseChatID(ctx, args[3])
128-
if err != nil {
129-
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
130-
ID: replied.ID,
131-
Message: i18n.T(i18nk.BotMsgImportErrorInvalidChatId, map[string]any{"Error": err}),
132-
})
133-
return dispatcher.EndGroups
134-
}
135-
targetChatID = parsedChatID
136-
}
137-
138-
if targetChatID == 0 {
139-
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
140-
ID: replied.ID,
141-
Message: i18n.T(i18nk.BotMsgImportErrorNoTargetChatId, nil),
136+
Message: i18n.T(i18nk.BotMsgTransferErrorNoFilesToTransfer, nil),
142137
})
143138
return dispatcher.EndGroups
144139
}
145140

141+
// Create task elements
146142
elems := make([]batchimport.TaskElement, 0, len(filteredFiles))
147143
var totalSize int64
148144
for _, file := range filteredFiles {
149-
elem := batchimport.NewTaskElement(stor, file, telegramStorage, targetChatID)
145+
elem := batchimport.NewTaskElement(sourceStorage, file, targetStorage, targetPath)
150146
elems = append(elems, *elem)
151147
totalSize += file.Size
152148
}
153149

150+
// Create and add task
154151
taskID := xid.New().String()
155152
injectCtx := tgutil.ExtWithContext(ctx.Context, ctx)
156153
task := batchimport.NewBatchImportTask(
@@ -164,14 +161,14 @@ func handleImportCmd(ctx *ext.Context, update *ext.Update) error {
164161
if err := core.AddTask(injectCtx, task); err != nil {
165162
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
166163
ID: replied.ID,
167-
Message: i18n.T(i18nk.BotMsgImportErrorAddTaskFailed, map[string]any{"Error": err}),
164+
Message: i18n.T(i18nk.BotMsgTransferErrorAddTaskFailed, map[string]any{"Error": err}),
168165
})
169166
return dispatcher.EndGroups
170167
}
171168

172169
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
173170
ID: replied.ID,
174-
Message: i18n.T(i18nk.BotMsgImportInfoTaskAdded, map[string]any{
171+
Message: i18n.T(i18nk.BotMsgTransferInfoTaskAdded, map[string]any{
175172
"Count": len(elems),
176173
"SizeMB": fmt.Sprintf("%.2f", float64(totalSize)/(1024*1024)),
177174
"TaskID": taskID,

common/i18n/i18nk/keys.go

Lines changed: 29 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)