From 226c15ef083f7987de15861e2d14730907970b38 Mon Sep 17 00:00:00 2001 From: krau <71133316+krau@users.noreply.github.com> Date: Sun, 24 Aug 2025 10:28:50 +0800 Subject: [PATCH] feat: add NormalizePathname function and update task handling for parsed items --- client/bot/handlers/add_task.go | 5 ++ client/bot/handlers/parse.go | 7 ++- common/utils/fsutil/fs.go | 20 ++++++++ .../utils/fsutil/normalize_pathname_test.go | 46 +++++++++++++++++++ common/utils/tgutil/message.go | 9 ++-- 5 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 common/utils/fsutil/normalize_pathname_test.go diff --git a/client/bot/handlers/add_task.go b/client/bot/handlers/add_task.go index 578a2c4..f692949 100644 --- a/client/bot/handlers/add_task.go +++ b/client/bot/handlers/add_task.go @@ -3,6 +3,7 @@ package handlers import ( "errors" "fmt" + "path" "strings" "github.com/celestix/gotgproto/dispatcher" @@ -11,6 +12,7 @@ import ( "github.com/gotd/td/tg" "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/common/utils/fsutil" "github.com/krau/SaveAny-Bot/database" "github.com/krau/SaveAny-Bot/pkg/enums/tasktype" "github.com/krau/SaveAny-Bot/pkg/tcbdata" @@ -74,6 +76,9 @@ func handleAddCallback(ctx *ext.Context, update *ext.Update) error { case tasktype.TaskTypeTphpics: return shortcut.CreateAndAddtelegraphWithEdit(ctx, userID, data.TphPageNode, data.TphDirPath, data.TphPics, selectedStorage, msgID) case tasktype.TaskTypeParseditem: + if len(data.ParsedItem.Resources) > 1 { + dirPath = path.Join(dirPath, fsutil.NormalizePathname(data.ParsedItem.Title)) + } shortcut.CreateAndAddParsedTaskWithEdit(ctx, selectedStorage, dirPath, data.ParsedItem, msgID, userID) default: log.FromContext(ctx).Errorf("Unsupported task type: %s", data.TaskType) diff --git a/client/bot/handlers/parse.go b/client/bot/handlers/parse.go index 95ef911..69fdbdf 100644 --- a/client/bot/handlers/parse.go +++ b/client/bot/handlers/parse.go @@ -11,6 +11,7 @@ import ( "github.com/gotd/td/tg" "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/common/utils/fsutil" "github.com/krau/SaveAny-Bot/parsers" "github.com/krau/SaveAny-Bot/pkg/enums/tasktype" "github.com/krau/SaveAny-Bot/pkg/tcbdata" @@ -106,5 +107,9 @@ func handleSilentSaveText(ctx *ext.Context, u *ext.Update) error { logger.Errorf("Failed to send message: %s", err) return dispatcher.EndGroups } - return shortcut.CreateAndAddParsedTaskWithEdit(ctx, stor, "", item, msg.ID, userID) + dirPath := "" + if len(item.Resources) > 1 { + dirPath = fsutil.NormalizePathname(item.Title) + } + return shortcut.CreateAndAddParsedTaskWithEdit(ctx, stor, dirPath, item, msg.ID, userID) } diff --git a/common/utils/fsutil/fs.go b/common/utils/fsutil/fs.go index 9d451c5..9912971 100644 --- a/common/utils/fsutil/fs.go +++ b/common/utils/fsutil/fs.go @@ -3,6 +3,8 @@ package fsutil import ( "os" "path/filepath" + "strings" + "unicode" "github.com/gabriel-vasile/mimetype" ) @@ -55,3 +57,21 @@ func CreateFile(fp string) (*File, error) { } return &File{File: file}, nil } + +func NormalizePathname(s string) string { + specials := `\/:*?"<>|` + "\n\r\t" + var builder strings.Builder + for _, ch := range s { + if strings.ContainsRune(specials, ch) || unicode.IsControl(ch) { + builder.WriteRune('_') + } else { + builder.WriteRune(ch) + } + } + + result := strings.TrimRightFunc(builder.String(), func(r rune) bool { + return r == '.' || r == '_' || unicode.IsSpace(r) + }) + + return result +} diff --git a/common/utils/fsutil/normalize_pathname_test.go b/common/utils/fsutil/normalize_pathname_test.go new file mode 100644 index 0000000..e9fcf02 --- /dev/null +++ b/common/utils/fsutil/normalize_pathname_test.go @@ -0,0 +1,46 @@ +package fsutil_test + +import ( + "testing" + + "github.com/krau/SaveAny-Bot/common/utils/fsutil" +) + +func TestNormalizePathname(t *testing.T) { + tests := []struct { + input string + expected string + }{ + { + input: "hello/world?.txt ", + expected: "hello_world_.txt", + }, + { + input: "bad|name:\nfile\r.", + expected: "bad_name__file", + }, + { + input: "normal.txt", + expected: "normal.txt", + }, + { + input: "test.... ", + expected: "test", + }, + { + input: "abc<>def", + expected: "abc__def", + }, + { + input: "with\tcontrol", + expected: "with_control", + }, + } + + for _, tc := range tests { + got := fsutil.NormalizePathname(tc.input) + if got != tc.expected { + t.Errorf("NormalizePathname(%q) = %q; want %q", tc.input, got, tc.expected) + } + } +} diff --git a/common/utils/tgutil/message.go b/common/utils/tgutil/message.go index b4e234b..454b79d 100644 --- a/common/utils/tgutil/message.go +++ b/common/utils/tgutil/message.go @@ -4,6 +4,7 @@ import ( "fmt" "strconv" "strings" + "unicode" "github.com/celestix/gotgproto/ext" "github.com/duke-git/lancet/v2/maputil" @@ -61,16 +62,12 @@ func GenFileNameFromMessage(message tg.Message) string { return fmt.Sprintf("%s_%s", tagStr, strconv.Itoa(message.GetID())) } text = lcstrutil.Substring(strings.Map(func(r rune) rune { - if r < 0x20 || r == 0x7F { - return '_' - } switch r { - // invalid characters case '/', '\\', ':', '*', '?', '"', '<', '>', '|': return '_' - // empty - case ' ', '\t', '\r', '\n': + } + if unicode.IsControl(r) || unicode.IsSpace(r) { return '_' } if validator.IsPrintable(string(r)) {