feat: implement transfer command for file transfers between storages

This commit is contained in:
krau
2026-01-19 21:01:50 +08:00
parent e6d8cc775a
commit 3d20fbd0fe
8 changed files with 166 additions and 159 deletions

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"github.com/charmbracelet/log"
@@ -84,15 +85,15 @@ func (t *Task) processElement(ctx context.Context, elem TaskElement) error {
}
defer reader.Close()
// Build Telegram storage path: /<chat_id>/<filename>
storagePath := fmt.Sprintf("/%d/%s", elem.TargetChatID, elem.FileInfo.Name)
// Build target storage path: /target_path/filename
storagePath := path.Join(elem.TargetPath, elem.FileInfo.Name)
// 注入文件大小到 context
// Inject file size into context
ctx = context.WithValue(ctx, ctxkey.ContentLength, size)
if config.C().Stream {
if err := elem.TargetStorage.Save(ctx, reader, storagePath); err != nil {
return fmt.Errorf("failed to upload file to telegram: %w", err)
return fmt.Errorf("failed to upload file to storage: %w", err)
}
} else {
logger.Info("Downloading to temporary file for ReadSeeker support")
@@ -107,9 +108,9 @@ func (t *Task) processElement(ctx context.Context, elem TaskElement) error {
return fmt.Errorf("failed to seek temp file: %w", err)
}
logger.Infof("Uploading file to Telegram storage (size: %d bytes)", size)
logger.Infof("Uploading file to storage (size: %d bytes)", size)
if err := elem.TargetStorage.Save(ctx, tempFile, storagePath); err != nil {
return fmt.Errorf("failed to upload file to telegram: %w", err)
return fmt.Errorf("failed to upload file to storage: %w", err)
}
}

View File

@@ -43,14 +43,14 @@ func (p *Progress) OnStart(ctx context.Context, info TaskInfo) {
log.FromContext(ctx).Debugf("Batch import task progress tracking started for message %d in chat %d", p.MessageID, p.ChatID)
sizeMB := float64(info.TotalSize()) / (1024 * 1024)
statsText := i18n.T(i18nk.BotMsgImportStartStats, map[string]any{
"SizeMB": fmt.Sprintf("%.2f", sizeMB),
"Count": info.Count(),
statsText := i18n.T(i18nk.BotMsgTransferStartStats, map[string]any{
"SizeMB": fmt.Sprintf("%.2f", sizeMB),
"Count": info.Count(),
})
entityBuilder := entity.Builder{}
if err := styling.Perform(&entityBuilder,
styling.Plain(i18n.T(i18nk.BotMsgProgressImportStartPrefix, nil)),
styling.Plain(i18n.T(i18nk.BotMsgProgressTransferStartPrefix, nil)),
styling.Code(statsText),
); err != nil {
log.FromContext(ctx).Errorf("Failed to build entities: %s", err)
@@ -94,9 +94,9 @@ func (p *Progress) OnProgress(ctx context.Context, info TaskInfo) {
entityBuilder := entity.Builder{}
var progressText strings.Builder
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportProgressPrefix, nil))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferProgressPrefix, nil))
progressText.WriteString(fmt.Sprintf("%d%%", percent))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportUploadedPrefix, nil))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferUploadedPrefix, nil))
progressText.WriteString(fmt.Sprintf("%.2f MB / %.2f MB",
float64(info.Uploaded())/(1024*1024),
float64(info.TotalSize())/(1024*1024)))
@@ -104,22 +104,22 @@ func (p *Progress) OnProgress(ctx context.Context, info TaskInfo) {
if p.start.Unix() > 0 {
elapsed := time.Since(p.start)
speed := float64(info.Uploaded()) / elapsed.Seconds()
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportSpeedPrefix, nil))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferSpeedPrefix, nil))
progressText.WriteString(dlutil.FormatSize(int64(speed)) + "/s")
if info.Uploaded() > 0 {
remaining := time.Duration(float64(info.TotalSize()-info.Uploaded()) / speed * float64(time.Second))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportRemainingTimePrefix, nil))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferRemainingTimePrefix, nil))
progressText.WriteString(formatDuration(remaining))
}
}
processing := info.Processing()
if len(processing) > 0 {
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportProcessingPrefix, nil))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferProcessingPrefix, nil))
for i, elem := range processing {
if i >= 3 {
progressText.WriteString(i18n.T(i18nk.BotMsgProgressImportProcessingMore, map[string]any{"Count": len(processing) - 3}))
progressText.WriteString(i18n.T(i18nk.BotMsgProgressTransferProcessingMore, map[string]any{"Count": len(processing) - 3}))
break
}
fmt.Fprintf(&progressText, "- %s\n", elem.FileName())
@@ -162,36 +162,36 @@ func (p *Progress) OnDone(ctx context.Context, info TaskInfo, err error) {
var resultText strings.Builder
if err != nil {
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportFailedPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferFailedPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressErrorPrefix, nil))
fmt.Fprintf(&resultText, "%v\n", err)
} else {
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportSuccessPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferSuccessPrefix, nil))
}
elapsed := time.Since(p.start)
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportTotalFilesPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferTotalFilesPrefix, nil))
fmt.Fprintf(&resultText, "%d\n", info.Count())
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportTotalSizePrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferTotalSizePrefix, nil))
fmt.Fprintf(&resultText, "%.2f MB\n", float64(info.TotalSize())/(1024*1024))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportUploadedPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferUploadedPrefix, nil))
fmt.Fprintf(&resultText, "%.2f MB\n", float64(info.Uploaded())/(1024*1024))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportElapsedTimePrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferElapsedTimePrefix, nil))
fmt.Fprintf(&resultText, "%s\n", formatDuration(elapsed))
if elapsed.Seconds() > 0 {
avgSpeed := float64(info.Uploaded()) / elapsed.Seconds()
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportAvgSpeedPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferAvgSpeedPrefix, nil))
fmt.Fprintf(&resultText, "%s/s\n", dlutil.FormatSize(int64(avgSpeed)))
}
failedFiles := info.FailedFiles()
if len(failedFiles) > 0 {
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportFailedFilesPrefix, nil))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferFailedFilesPrefix, nil))
fmt.Fprintf(&resultText, "%d\n", len(failedFiles))
for i, name := range failedFiles {
if i >= 5 {
resultText.WriteString(i18n.T(i18nk.BotMsgProgressImportProcessingMore, map[string]any{"Count": len(failedFiles) - 5}))
resultText.WriteString(i18n.T(i18nk.BotMsgProgressTransferProcessingMore, map[string]any{"Count": len(failedFiles) - 5}))
break
}
fmt.Fprintf(&resultText, "- %s\n", name)

View File

@@ -21,7 +21,7 @@ type TaskElement struct {
SourcePath string
FileInfo storagetypes.FileInfo
TargetStorage storage.Storage
TargetChatID int64
TargetPath string
}
type Task struct {
@@ -56,7 +56,7 @@ func NewTaskElement(
sourceStorage storage.Storage,
fileInfo storagetypes.FileInfo,
targetStorage storage.Storage,
targetChatID int64,
targetPath string,
) *TaskElement {
id := xid.New().String()
return &TaskElement{
@@ -65,7 +65,7 @@ func NewTaskElement(
SourcePath: fileInfo.Path,
FileInfo: fileInfo,
TargetStorage: targetStorage,
TargetChatID: targetChatID,
TargetPath: targetPath,
}
}