From 1e7ed0356b103d91ef031dd01c4791b31561ee42 Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Tue, 26 Mar 2024 22:16:47 +0800 Subject: [PATCH 1/6] feat(upload): add support on upload to topic --- app/up/elem.go | 11 ++-- app/up/iter.go | 107 ++++++++++++++++++++++++++++++--------- app/up/up.go | 46 ++++++++++++++--- app/up/walk.go | 8 +-- cmd/up.go | 11 +++- pkg/uploader/iter.go | 1 + pkg/uploader/uploader.go | 1 + 7 files changed, 147 insertions(+), 38 deletions(-) diff --git a/app/up/elem.go b/app/up/elem.go index 8c2d0a0d2..37871db0e 100644 --- a/app/up/elem.go +++ b/app/up/elem.go @@ -11,9 +11,10 @@ import ( ) type iterElem struct { - file *uploaderFile - thumb *uploaderFile - to peers.Peer + file *uploaderFile + thumb *uploaderFile + to peers.Peer + thread int asPhoto bool remove bool @@ -34,6 +35,10 @@ func (e *iterElem) To() tg.InputPeerClass { return e.to.InputPeer() } +func (e *iterElem) Thread() int { + return e.thread +} + func (e *iterElem) AsPhoto() bool { return e.asPhoto } diff --git a/app/up/iter.go b/app/up/iter.go index da2f25110..4097c80a9 100644 --- a/app/up/iter.go +++ b/app/up/iter.go @@ -2,38 +2,51 @@ package up import ( "context" + "github.com/expr-lang/expr/vm" + "github.com/gotd/td/telegram/peers" + "github.com/iyear/tdl/pkg/texpr" + "github.com/mitchellh/mapstructure" "os" "github.com/gabriel-vasile/mimetype" "github.com/go-faster/errors" - "github.com/gotd/td/telegram/peers" - "github.com/iyear/tdl/pkg/uploader" "github.com/iyear/tdl/pkg/utils" ) -type file struct { - file string - thumb string +type File struct { + File string + Thumb string } type iter struct { - files []*file - to peers.Peer - photo bool - remove bool + files []*File + to *vm.Program + chat string + topic int + photo bool + remove bool + manager *peers.Manager cur int err error file uploader.Elem } -func newIter(files []*file, to peers.Peer, photo, remove bool) *iter { +type dest struct { + Peer string + Thread int +} + +func newIter(files []*File, to *vm.Program, chat string, topic int, photo, remove bool, manager *peers.Manager) *iter { return &iter{ - files: files, - to: to, - photo: photo, - remove: remove, + files: files, + to: to, + chat: chat, + topic: topic, + photo: photo, + remove: remove, + manager: manager, cur: 0, err: nil, @@ -56,21 +69,61 @@ func (i *iter) Next(ctx context.Context) bool { cur := i.files[i.cur] i.cur++ - f, err := os.Open(cur.file) + f, err := os.Open(cur.File) if err != nil { i.err = errors.Wrap(err, "open file") return false } + var ( + to peers.Peer + thread int + ) + if i.chat != "" { + to, i.err = i.resolvePeer(ctx, i.chat) + thread = i.topic + if i.err != nil { + return false + } + } else { + // message routing + result, err := texpr.Run(i.to, *cur) + if err != nil { + i.err = errors.Wrap(err, "message routing") + return false + } + + switch r := result.(type) { + case string: + // pure chat, no reply to, which is a compatible with old version + // and a convenient way to send message to self + to, err = i.resolvePeer(ctx, r) + case map[string]interface{}: + // chat with reply to topic or message + var d dest + + if err = mapstructure.WeakDecode(r, &d); err != nil { + i.err = errors.Wrapf(err, "decode dest: %v", result) + return false + } + + to, err = i.resolvePeer(ctx, d.Peer) + thread = d.Thread + default: + i.err = errors.Errorf("message router must return string or dest: %T", result) + return false + } + } + var thumb *uploaderFile = nil // has thumbnail - if cur.thumb != "" { - tMime, err := mimetype.DetectFile(cur.thumb) + if cur.Thumb != "" { + tMime, err := mimetype.DetectFile(cur.Thumb) if err != nil || !utils.Media.IsImage(tMime.String()) { // TODO(iyear): jpg only - i.err = errors.Wrapf(err, "invalid thumbnail file: %v", cur.thumb) + i.err = errors.Wrapf(err, "invalid thumbnail file: %v", cur.Thumb) return false } - thumbFile, err := os.Open(cur.thumb) + thumbFile, err := os.Open(cur.Thumb) if err != nil { i.err = errors.Wrap(err, "open thumbnail file") return false @@ -86,10 +139,10 @@ func (i *iter) Next(ctx context.Context) bool { } i.file = &iterElem{ - file: &uploaderFile{File: f, size: stat.Size()}, - thumb: thumb, - to: i.to, - + file: &uploaderFile{File: f, size: stat.Size()}, + thumb: thumb, + to: to, + thread: thread, asPhoto: i.photo, remove: i.remove, } @@ -97,6 +150,14 @@ func (i *iter) Next(ctx context.Context) bool { return true } +func (i *iter) resolvePeer(ctx context.Context, peer string) (peers.Peer, error) { + if peer == "" { // self + return i.manager.Self(ctx) + } + + return utils.Telegram.GetInputPeer(ctx, i.manager, peer) +} + func (i *iter) Value() uploader.Elem { return i.file } diff --git a/app/up/up.go b/app/up/up.go index b63853917..18afcf4ac 100644 --- a/app/up/up.go +++ b/app/up/up.go @@ -2,12 +2,15 @@ package up import ( "context" + "fmt" + "github.com/expr-lang/expr" + "github.com/expr-lang/expr/vm" + "os" "github.com/fatih/color" "github.com/go-faster/errors" "github.com/gotd/td/telegram" "github.com/gotd/td/telegram/peers" - "github.com/gotd/td/tg" "github.com/spf13/viper" "go.uber.org/multierr" @@ -23,6 +26,8 @@ import ( type Options struct { Chat string + Thread int + To string Paths []string Excludes []string Remove bool @@ -44,7 +49,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr manager := peers.Options{Storage: storage.NewPeers(kvd)}.Build(pool.Default(ctx)) - to, err := resolveDestPeer(ctx, manager, opts.Chat) + to, err := resolveDest(ctx, manager, opts.To) if err != nil { return errors.Wrap(err, "get target peer") } @@ -57,7 +62,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr Client: pool.Default(ctx), PartSize: viper.GetInt(consts.FlagPartSize), Threads: viper.GetInt(consts.FlagThreads), - Iter: newIter(files, to, opts.Photo, opts.Remove), + Iter: newIter(files, to, opts.Chat, opts.Thread, opts.Photo, opts.Remove, manager), Progress: newProgress(upProgress), } @@ -69,10 +74,37 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr return up.Upload(ctx, viper.GetInt(consts.FlagLimit)) } -func resolveDestPeer(ctx context.Context, manager *peers.Manager, chat string) (peers.Peer, error) { - if chat == "" { - return manager.FromInputPeer(ctx, &tg.InputPeerSelf{}) +//func resolveDestPeer(ctx context.Context, manager *peers.Manager, chat string) (peers.Peer, error) { +// if chat == "" { +// return manager.FromInputPeer(ctx, &tg.InputPeerSelf{}) +// } +// +// return utils.Telegram.GetInputPeer(ctx, manager, chat) +//} + +// resolveDest parses the input string and returns a vm.Program. It can be a CHAT, a text or a file based on expression engine. +func resolveDest(ctx context.Context, manager *peers.Manager, input string) (*vm.Program, error) { + compile := func(i string) (*vm.Program, error) { + // we pass empty peer and message to enable type checking + return expr.Compile(i, expr.Env(File{})) } - return utils.Telegram.GetInputPeer(ctx, manager, chat) + // default + if input == "" { + return compile(`""`) + } + + // file + if exp, err := os.ReadFile(input); err == nil { + return compile(string(exp)) + } + + // chat + if _, err := utils.Telegram.GetInputPeer(ctx, manager, input); err == nil { + // convert to const string + return compile(fmt.Sprintf(`"%s"`, input)) + } + + // text + return compile(input) } diff --git a/app/up/walk.go b/app/up/walk.go index e493b2dd2..988db7c2e 100644 --- a/app/up/walk.go +++ b/app/up/walk.go @@ -9,8 +9,8 @@ import ( "github.com/iyear/tdl/pkg/utils" ) -func walk(paths, excludes []string) ([]*file, error) { - files := make([]*file, 0) +func walk(paths, excludes []string) ([]*File, error) { + files := make([]*File, 0) excludesMap := map[string]struct{}{ consts.UploadThumbExt: {}, // ignore thumbnail files } @@ -31,10 +31,10 @@ func walk(paths, excludes []string) ([]*file, error) { return nil } - f := file{file: path} + f := File{File: path} t := strings.TrimRight(path, filepath.Ext(path)) + consts.UploadThumbExt if utils.FS.PathExists(t) { - f.thumb = t + f.Thumb = t } files = append(files, &f) diff --git a/cmd/up.go b/cmd/up.go index 17741fe0c..c152703b4 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "github.com/go-faster/errors" "github.com/gotd/td/telegram" "github.com/spf13/cobra" @@ -20,6 +21,12 @@ func NewUpload() *cobra.Command { Short: "Upload anything to Telegram", RunE: func(cmd *cobra.Command, args []string) error { return tRun(cmd.Context(), func(ctx context.Context, c *telegram.Client, kvd kv.KV) error { + if opts.Thread != 0 && opts.Chat == "" { + return errors.New("error flags: --chat should be set when --topic is set") + } + if opts.Chat != "" && opts.To != "" { + return errors.New("conflicting flags: --chat and --to cannot be set at the same time") + } return up.Run(logger.Named(ctx, "up"), c, kvd, opts) }) }, @@ -29,7 +36,9 @@ func NewUpload() *cobra.Command { _chat = "chat" path = "path" ) - cmd.Flags().StringVarP(&opts.Chat, _chat, "c", "", "chat id or domain, and empty means 'Saved Messages'") + cmd.Flags().StringVarP(&opts.Chat, _chat, "c", "", "chat id or domain, and empty means 'Saved Messages'. Can be used together with --topic flag. Conflicts with --to flag.") + cmd.Flags().IntVar(&opts.Thread, "topic", 0, "specify topic id. Must be used together with --chat flag. Conflicts with --to flag.") + cmd.Flags().StringVar(&opts.To, "to", "", "destination peer, can be a CHAT or router based on expression engine. Conflicts with --chat and --topic flag.") cmd.Flags().StringSliceVarP(&opts.Paths, path, "p", []string{}, "dirs or files") cmd.Flags().StringSliceVarP(&opts.Excludes, "excludes", "e", []string{}, "exclude the specified file extensions") cmd.Flags().BoolVar(&opts.Remove, "rm", false, "remove the uploaded files after uploading") diff --git a/pkg/uploader/iter.go b/pkg/uploader/iter.go index 8e0d778d2..d56522a0d 100644 --- a/pkg/uploader/iter.go +++ b/pkg/uploader/iter.go @@ -23,5 +23,6 @@ type Elem interface { File() File Thumb() (File, bool) To() tg.InputPeerClass + Thread() int AsPhoto() bool } diff --git a/pkg/uploader/uploader.go b/pkg/uploader/uploader.go index d380a2256..e3599d152 100644 --- a/pkg/uploader/uploader.go +++ b/pkg/uploader/uploader.go @@ -136,6 +136,7 @@ func (u *Uploader) upload(ctx context.Context, elem Elem) error { _, err = message.NewSender(u.opts.Client). WithUploader(up). To(elem.To()). + Reply(elem.Thread()). Media(ctx, media) if err != nil { return errors.Wrap(err, "send message") From 270577fc582f7879f37f92037250c38d01bb588c Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Thu, 28 Mar 2024 15:16:54 +0800 Subject: [PATCH 2/6] feat(upload): add support on upload to topic --- app/up/iter.go | 17 ++++++++++++++--- app/up/up.go | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/up/iter.go b/app/up/iter.go index 4097c80a9..1107603d3 100644 --- a/app/up/iter.go +++ b/app/up/iter.go @@ -14,9 +14,20 @@ import ( "github.com/iyear/tdl/pkg/utils" ) +type toEnv struct { + File File +} + +func exprToEnv(file *File) toEnv { + if file == nil { + file = &File{} + } + return toEnv{File: *file} +} + type File struct { - File string - Thumb string + File string `comment:"File path"` + Thumb string `comment:"Thumbnail path"` } type iter struct { @@ -87,7 +98,7 @@ func (i *iter) Next(ctx context.Context) bool { } } else { // message routing - result, err := texpr.Run(i.to, *cur) + result, err := texpr.Run(i.to, exprToEnv(cur)) if err != nil { i.err = errors.Wrap(err, "message routing") return false diff --git a/app/up/up.go b/app/up/up.go index 18afcf4ac..ec46d51cf 100644 --- a/app/up/up.go +++ b/app/up/up.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/expr-lang/expr" "github.com/expr-lang/expr/vm" + "github.com/iyear/tdl/pkg/texpr" "os" "github.com/fatih/color" @@ -32,9 +33,21 @@ type Options struct { Excludes []string Remove bool Photo bool + Caption string } func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr error) { + if opts.To == "-" { + fg := texpr.NewFieldsGetter(nil) + + fields, err := fg.Walk(exprToEnv(nil)) + if err != nil { + return fmt.Errorf("failed to walk fields: %w", err) + } + + fmt.Print(fg.Sprint(fields, true)) + return nil + } files, err := walk(opts.Paths, opts.Excludes) if err != nil { return err @@ -86,7 +99,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr func resolveDest(ctx context.Context, manager *peers.Manager, input string) (*vm.Program, error) { compile := func(i string) (*vm.Program, error) { // we pass empty peer and message to enable type checking - return expr.Compile(i, expr.Env(File{})) + return expr.Compile(i, expr.Env(exprToEnv(nil))) } // default From d33d8dfc51f7e038356fafc162a2eaa86f0f7a76 Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Thu, 28 Mar 2024 16:31:14 +0800 Subject: [PATCH 3/6] =?UTF-8?q?feat(upload):=20add=20filename,=20extension?= =?UTF-8?q?=20and=20mime=20to=20`=E2=80=94to`=20expr=20env?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/up/iter.go | 36 +++++++++++++++++++++++++++++------- app/up/up.go | 4 ++-- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/app/up/iter.go b/app/up/iter.go index 1107603d3..2c8ff74b4 100644 --- a/app/up/iter.go +++ b/app/up/iter.go @@ -4,9 +4,13 @@ import ( "context" "github.com/expr-lang/expr/vm" "github.com/gotd/td/telegram/peers" + "github.com/iyear/tdl/pkg/logger" "github.com/iyear/tdl/pkg/texpr" "github.com/mitchellh/mapstructure" + "go.uber.org/zap" "os" + "path/filepath" + "strings" "github.com/gabriel-vasile/mimetype" "github.com/go-faster/errors" @@ -15,19 +19,37 @@ import ( ) type toEnv struct { - File File + File string `comment:"File path"` + Thumb string `comment:"Thumbnail path"` + Filename string `comment:"Filename"` + Extension string `comment:"File extension"` + Mime string `comment:"File mime type"` } -func exprToEnv(file *File) toEnv { +func exprToEnv(ctx context.Context, file *File) toEnv { if file == nil { - file = &File{} + return toEnv{} + } + + var extension = filepath.Ext(file.File) + var filename = strings.TrimSuffix(filepath.Base(file.File), extension) + var mime, err = mimetype.DetectFile(file.File) + if err != nil { + mime = &mimetype.MIME{} + logger.From(ctx).Error("detect file mime", zap.Error(err)) + } + return toEnv{ + File: file.File, + Thumb: file.Thumb, + Filename: filename, + Extension: extension, + Mime: mime.String(), } - return toEnv{File: *file} } type File struct { - File string `comment:"File path"` - Thumb string `comment:"Thumbnail path"` + File string + Thumb string } type iter struct { @@ -98,7 +120,7 @@ func (i *iter) Next(ctx context.Context) bool { } } else { // message routing - result, err := texpr.Run(i.to, exprToEnv(cur)) + result, err := texpr.Run(i.to, exprToEnv(ctx, cur)) if err != nil { i.err = errors.Wrap(err, "message routing") return false diff --git a/app/up/up.go b/app/up/up.go index ec46d51cf..da2cbc207 100644 --- a/app/up/up.go +++ b/app/up/up.go @@ -40,7 +40,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr if opts.To == "-" { fg := texpr.NewFieldsGetter(nil) - fields, err := fg.Walk(exprToEnv(nil)) + fields, err := fg.Walk(exprToEnv(nil, nil)) if err != nil { return fmt.Errorf("failed to walk fields: %w", err) } @@ -99,7 +99,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr func resolveDest(ctx context.Context, manager *peers.Manager, input string) (*vm.Program, error) { compile := func(i string) (*vm.Program, error) { // we pass empty peer and message to enable type checking - return expr.Compile(i, expr.Env(exprToEnv(nil))) + return expr.Compile(i, expr.Env(exprToEnv(nil, nil))) } // default From daff9b8f691077995d75ff528798fc65e5029db4 Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Thu, 28 Mar 2024 16:53:01 +0800 Subject: [PATCH 4/6] refactor(upload): rename toEnv -> Env --- app/up/iter.go | 10 +++++----- app/up/up.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/up/iter.go b/app/up/iter.go index 2c8ff74b4..f2855f2e4 100644 --- a/app/up/iter.go +++ b/app/up/iter.go @@ -18,7 +18,7 @@ import ( "github.com/iyear/tdl/pkg/utils" ) -type toEnv struct { +type Env struct { File string `comment:"File path"` Thumb string `comment:"Thumbnail path"` Filename string `comment:"Filename"` @@ -26,9 +26,9 @@ type toEnv struct { Mime string `comment:"File mime type"` } -func exprToEnv(ctx context.Context, file *File) toEnv { +func exprEnv(ctx context.Context, file *File) Env { if file == nil { - return toEnv{} + return Env{} } var extension = filepath.Ext(file.File) @@ -38,7 +38,7 @@ func exprToEnv(ctx context.Context, file *File) toEnv { mime = &mimetype.MIME{} logger.From(ctx).Error("detect file mime", zap.Error(err)) } - return toEnv{ + return Env{ File: file.File, Thumb: file.Thumb, Filename: filename, @@ -120,7 +120,7 @@ func (i *iter) Next(ctx context.Context) bool { } } else { // message routing - result, err := texpr.Run(i.to, exprToEnv(ctx, cur)) + result, err := texpr.Run(i.to, exprEnv(ctx, cur)) if err != nil { i.err = errors.Wrap(err, "message routing") return false diff --git a/app/up/up.go b/app/up/up.go index da2cbc207..07159bade 100644 --- a/app/up/up.go +++ b/app/up/up.go @@ -40,7 +40,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr if opts.To == "-" { fg := texpr.NewFieldsGetter(nil) - fields, err := fg.Walk(exprToEnv(nil, nil)) + fields, err := fg.Walk(exprEnv(nil, nil)) if err != nil { return fmt.Errorf("failed to walk fields: %w", err) } @@ -99,7 +99,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr func resolveDest(ctx context.Context, manager *peers.Manager, input string) (*vm.Program, error) { compile := func(i string) (*vm.Program, error) { // we pass empty peer and message to enable type checking - return expr.Compile(i, expr.Env(exprToEnv(nil, nil))) + return expr.Compile(i, expr.Env(exprEnv(nil, nil))) } // default From 3b4f5e58be2c7c3b55de0a27ef2166f904d87011 Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Thu, 28 Mar 2024 21:40:47 +0800 Subject: [PATCH 5/6] =?UTF-8?q?feat(upload):=20support=20custom=20file?= =?UTF-8?q?=E2=80=99s=20caption=20by=20Expr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/up/elem.go | 14 ++- app/up/iter.go | 36 ++++++- app/up/up.go | 29 +++++- cmd/up.go | 1 + go.mod | 16 +++ go.sum | 71 +++++++++++++ pkg/tstyle/tstyle.go | 140 ++++++++++++++++++++++++++ pkg/tstyle/tstyle_enum.go | 203 ++++++++++++++++++++++++++++++++++++++ pkg/uploader/iter.go | 2 + pkg/uploader/uploader.go | 10 +- 10 files changed, 507 insertions(+), 15 deletions(-) create mode 100644 pkg/tstyle/tstyle.go create mode 100644 pkg/tstyle/tstyle_enum.go diff --git a/app/up/elem.go b/app/up/elem.go index 37871db0e..89e34d7e7 100644 --- a/app/up/elem.go +++ b/app/up/elem.go @@ -1,6 +1,7 @@ package up import ( + "github.com/gotd/td/telegram/message" "os" "path/filepath" @@ -11,10 +12,11 @@ import ( ) type iterElem struct { - file *uploaderFile - thumb *uploaderFile - to peers.Peer - thread int + file *uploaderFile + thumb *uploaderFile + to peers.Peer + caption []message.StyledTextOption + thread int asPhoto bool remove bool @@ -31,6 +33,10 @@ func (e *iterElem) Thumb() (uploader.File, bool) { return e.thumb, true } +func (e *iterElem) Caption() []message.StyledTextOption { + return e.caption +} + func (e *iterElem) To() tg.InputPeerClass { return e.to.InputPeer() } diff --git a/app/up/iter.go b/app/up/iter.go index f2855f2e4..c581d6ab7 100644 --- a/app/up/iter.go +++ b/app/up/iter.go @@ -3,9 +3,12 @@ package up import ( "context" "github.com/expr-lang/expr/vm" + "github.com/gotd/td/telegram/message" + "github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/peers" "github.com/iyear/tdl/pkg/logger" "github.com/iyear/tdl/pkg/texpr" + "github.com/iyear/tdl/pkg/tstyle" "github.com/mitchellh/mapstructure" "go.uber.org/zap" "os" @@ -55,6 +58,7 @@ type File struct { type iter struct { files []*File to *vm.Program + caption *vm.Program chat string topic int photo bool @@ -71,10 +75,11 @@ type dest struct { Thread int } -func newIter(files []*File, to *vm.Program, chat string, topic int, photo, remove bool, manager *peers.Manager) *iter { +func newIter(files []*File, to, caption *vm.Program, chat string, topic int, photo, remove bool, manager *peers.Manager) *iter { return &iter{ files: files, to: to, + caption: caption, chat: chat, topic: topic, photo: photo, @@ -148,6 +153,34 @@ func (i *iter) Next(ctx context.Context) bool { } } + result, err := texpr.Run(i.caption, exprEnv(ctx, cur)) + caption := make([]message.StyledTextOption, 0, 1) + if err != nil { + i.err = errors.Wrap(err, "caption parse") + return false + } + switch r := result.(type) { + case string: + caption = append(caption, styling.Plain(r)) + case []interface{}: + for _, v := range r { + switch v := v.(type) { + case string: + caption = append(caption, styling.Plain(v)) + case map[string]interface{}: + styledText, err := tstyle.ParseToStyledText(v) + if err != nil { + i.err = errors.Wrap(err, "parse styled text") + return false + } + caption = append(caption, *styledText) + } + } + default: + i.err = errors.Errorf("caption must return string or array of object: %T", result) + return false + } + var thumb *uploaderFile = nil // has thumbnail if cur.Thumb != "" { @@ -175,6 +208,7 @@ func (i *iter) Next(ctx context.Context) bool { file: &uploaderFile{File: f, size: stat.Size()}, thumb: thumb, to: to, + caption: caption, thread: thread, asPhoto: i.photo, remove: i.remove, diff --git a/app/up/up.go b/app/up/up.go index 07159bade..c331be54c 100644 --- a/app/up/up.go +++ b/app/up/up.go @@ -37,7 +37,7 @@ type Options struct { } func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr error) { - if opts.To == "-" { + if opts.To == "-" || opts.Caption == "-" { fg := texpr.NewFieldsGetter(nil) fields, err := fg.Walk(exprEnv(nil, nil)) @@ -67,6 +67,11 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr return errors.Wrap(err, "get target peer") } + caption, err := resolveCaption(ctx, opts.Caption) + if err != nil { + return errors.Wrap(err, "get caption") + } + upProgress := prog.New(utils.Byte.FormatBinaryBytes) upProgress.SetNumTrackersExpected(len(files)) prog.EnablePS(ctx, upProgress) @@ -75,7 +80,7 @@ func Run(ctx context.Context, c *telegram.Client, kvd kv.KV, opts Options) (rerr Client: pool.Default(ctx), PartSize: viper.GetInt(consts.FlagPartSize), Threads: viper.GetInt(consts.FlagThreads), - Iter: newIter(files, to, opts.Chat, opts.Thread, opts.Photo, opts.Remove, manager), + Iter: newIter(files, to, caption, opts.Chat, opts.Thread, opts.Photo, opts.Remove, manager), Progress: newProgress(upProgress), } @@ -121,3 +126,23 @@ func resolveDest(ctx context.Context, manager *peers.Manager, input string) (*vm // text return compile(input) } + +func resolveCaption(ctx context.Context, input string) (*vm.Program, error) { + compile := func(i string) (*vm.Program, error) { + // we pass empty peer and message to enable type checking + return expr.Compile(i, expr.Env(exprEnv(nil, nil))) + } + + // default + if input == "" { + return compile(`""`) + } + + // file + if exp, err := os.ReadFile(input); err == nil { + return compile(string(exp)) + } + + // text + return compile(input) +} diff --git a/cmd/up.go b/cmd/up.go index c152703b4..a18feebb6 100644 --- a/cmd/up.go +++ b/cmd/up.go @@ -43,6 +43,7 @@ func NewUpload() *cobra.Command { cmd.Flags().StringSliceVarP(&opts.Excludes, "excludes", "e", []string{}, "exclude the specified file extensions") cmd.Flags().BoolVar(&opts.Remove, "rm", false, "remove the uploaded files after uploading") cmd.Flags().BoolVar(&opts.Photo, "photo", false, "upload the image as a photo instead of a file") + cmd.Flags().StringVar(&opts.Caption, "caption", `[{style:"code", text: Filename }, "-", {style:"code", text: Mime }]`, "caption for the uploaded media") // completion and validation _ = cmd.MarkFlagRequired(path) diff --git a/go.mod b/go.mod index c909a3622..a7a8fe951 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,10 @@ require ( ) require ( + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.0 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/abice/go-enum v0.6.0 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -54,19 +58,26 @@ require ( github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gotd/ige v0.2.2 // indirect github.com/gotd/neo v0.1.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/labstack/gommon v0.4.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lufia/plan9stats v0.0.0-20230326075908-cb1d2100619a // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/goveralls v0.0.12 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect @@ -76,6 +87,7 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/shopspring/decimal v1.2.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect @@ -84,15 +96,19 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect + github.com/urfave/cli/v2 v2.26.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opentelemetry.io/otel v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect golang.org/x/crypto v0.21.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/mod v0.16.0 // indirect golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.19.0 // indirect + golang.org/x/tools/cmd/cover v0.1.0-deprecated // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.10 // indirect diff --git a/go.sum b/go.sum index e43f6b445..cce2f43da 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,15 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/abice/go-enum v0.6.0 h1:J6xiV+nyu/D5c5+/rQfgkMi9zJ1Hkap8clxCZf8KNsk= +github.com/abice/go-enum v0.6.0/go.mod h1:istq/zbgIh0kwEdbwHb+t8OS5dsB7w4w4VygV6HcpLg= github.com/bcicen/jstream v1.0.1 h1:BXY7Cu4rdmc0rhyTVyT3UkxAiX3bnLpKLas9btbH5ck= github.com/bcicen/jstream v1.0.1/go.mod h1:9ielPxqFry7Y4Tg3j4BfjPocfJ3TbsRtXOAYXYmRuAQ= github.com/beevik/ntp v1.3.1 h1:Y/srlT8L1yQr58kyPWFPZIxRL8ttx2SRIpVYJqZIlAM= @@ -50,12 +58,15 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -72,9 +83,14 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/iyear/connectproxy v0.1.1 h1:JZOF/62vvwRGBWcgSyWRb0BpKD4FSs0++B5/y5pNE4c= @@ -91,6 +107,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/gommon v0.4.1 h1:gqEff0p/hTENGMABzezPoPSRtIh1Cvw0ueMOe0/dfOk= +github.com/labstack/gommon v0.4.1/go.mod h1:TyTrpPqxR5KMk8LKVtLmfMjeQ5FEkBYdxLYPw/WfrOM= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= @@ -108,11 +126,19 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/goveralls v0.0.12 h1:PEEeF0k1SsTjOBQ8FOmrOAoCu4ytuMaWCnWe94zxbCg= +github.com/mattn/goveralls v0.0.12/go.mod h1:44ImGEUfmqH8bBtaMrYKsM65LXfNLWmwaxFGjZwgMSQ= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/onsi/ginkgo/v2 v2.17.0 h1:kdnunFXpBjbzN56hcJHrXZ8M+LOkenKA7NnBzTNigTI= github.com/onsi/ginkgo/v2 v2.17.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= @@ -145,12 +171,15 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= @@ -162,6 +191,8 @@ github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMV github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -182,8 +213,13 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= +github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yapingcat/gomedia v0.0.0-20230727105416-c491e66c9d2a h1:x60q0A7QmoUTzixNz7zVTdEA9JC0oYqm8S51PdbTWgs= github.com/yapingcat/gomedia v0.0.0-20230727105416-c491e66c9d2a/go.mod h1:WSZ59bidJOO40JSJmLqlkBJrjZCtjbKKkygEMfzY/kc= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= @@ -202,34 +238,54 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -237,23 +293,35 @@ golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools/cmd/cover v0.1.0-deprecated h1:Rwy+mWYz6loAF+LnG1jHG/JWMHRMMC2/1XX3Ejkx9lA= +golang.org/x/tools/cmd/cover v0.1.0-deprecated/go.mod h1:hMDiIvlpN1NoVgmjLjUJE9tMHyxHjFX7RuQ+rW12mSA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -263,7 +331,10 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= diff --git a/pkg/tstyle/tstyle.go b/pkg/tstyle/tstyle.go new file mode 100644 index 000000000..6550122ff --- /dev/null +++ b/pkg/tstyle/tstyle.go @@ -0,0 +1,140 @@ +package tstyle + +import ( + "github.com/go-faster/errors" + "github.com/gotd/td/telegram/message" + "github.com/gotd/td/telegram/message/styling" + "github.com/mitchellh/mapstructure" +) + +//go:generate go-enum --values --names --flag --nocase --lower + +// Style represents the Telegram StyledText +// ENUM(Plain,Unknown,Mention,Hashtag,BotCommand,URL,Email,Bold,Italic,Code,Pre,TextURL,MentionName,Phone,Cashtag,Underline,Strike,BankCard,Spoiler,CustomEmoji,Blockquote) +type Style string + +func ParseToStyledText(input map[string]any) (result *message.StyledTextOption, err error) { + style, err := ParseStyle(input["style"].(string)) + if err != nil { + return nil, errors.Wrap(err, "parse style") + } + switch style { + case StylePre: + o := new(preStyle) + if err = mapstructure.WeakDecode(input, &o); err != nil { + return nil, errors.Wrap(err, "decode options") + } + r := styling.Pre(o.Text, o.Language) + result = &r + return + case StyleTextURL: + o := new(textURLStyle) + if err = mapstructure.WeakDecode(input, &o); err != nil { + return nil, errors.Wrap(err, "decode options") + } + r := styling.TextURL(o.Text, o.URL) + result = &r + return + case StyleMentionName: + return nil, errors.New("unsupported style") + case StyleCustomEmoji: + o := new(customEmojiStyle) + if err = mapstructure.WeakDecode(input, &o); err != nil { + return nil, errors.Wrap(err, "decode options") + } + r := styling.CustomEmoji(o.Text, o.DocumentID) + result = &r + return + default: + o := new(commonStyle) + if err = mapstructure.WeakDecode(input, &o); err != nil { + return nil, errors.Wrap(err, "decode options") + } + var r message.StyledTextOption + r, err = processCommonStyle(*o) + if err != nil { + return nil, errors.Wrap(err, "process common style") + } + result = &r + return + } +} + +func processCommonStyle(commonStyle commonStyle) (result message.StyledTextOption, err error) { + style, err := ParseStyle(commonStyle.Style) + if err != nil { + err = errors.Wrap(err, "parse style") + return + } + switch style { + case StylePlain: + result = styling.Plain(commonStyle.Text) + case StyleUnknown: + result = styling.Unknown(commonStyle.Text) + case StyleMention: + result = styling.Mention(commonStyle.Text) + case StyleHashtag: + result = styling.Hashtag(commonStyle.Text) + case StyleBotCommand: + result = styling.BotCommand(commonStyle.Text) + case StyleURL: + result = styling.URL(commonStyle.Text) + case StyleEmail: + result = styling.Email(commonStyle.Text) + case StyleBold: + result = styling.Bold(commonStyle.Text) + case StyleItalic: + result = styling.Italic(commonStyle.Text) + case StyleCode: + result = styling.Code(commonStyle.Text) + case StylePre: + err = errors.New("special style in common style switch") + case StyleTextURL: + err = errors.New("special style in common style switch") + case StyleMentionName: + err = errors.New("special style in common style switch") + case StylePhone: + result = styling.Phone(commonStyle.Text) + case StyleCashtag: + result = styling.Cashtag(commonStyle.Text) + case StyleUnderline: + result = styling.Underline(commonStyle.Text) + case StyleStrike: + result = styling.Strike(commonStyle.Text) + case StyleBankCard: + result = styling.BankCard(commonStyle.Text) + case StyleSpoiler: + result = styling.Spoiler(commonStyle.Text) + case StyleCustomEmoji: + err = errors.New("special style in common style switch") + case StyleBlockquote: + result = styling.Blockquote(commonStyle.Text) + default: + err = errors.Wrap(ErrInvalidStyle, "switch style") + } + return + +} + +type commonStyle struct { + Style string + Text string +} + +type preStyle struct { + Style string + Text string + Language string +} + +type textURLStyle struct { + Style string + Text string + URL string +} + +type customEmojiStyle struct { + Style string + Text string + DocumentID int64 +} diff --git a/pkg/tstyle/tstyle_enum.go b/pkg/tstyle/tstyle_enum.go new file mode 100644 index 000000000..6aef88e7d --- /dev/null +++ b/pkg/tstyle/tstyle_enum.go @@ -0,0 +1,203 @@ +// Code generated by go-enum DO NOT EDIT. +// Version: +// Revision: +// Build Date: +// Built By: + +package tstyle + +import ( + "fmt" + "strings" +) + +const ( + // StylePlain is a Style of type Plain. + StylePlain Style = "Plain" + // StyleUnknown is a Style of type Unknown. + StyleUnknown Style = "Unknown" + // StyleMention is a Style of type Mention. + StyleMention Style = "Mention" + // StyleHashtag is a Style of type Hashtag. + StyleHashtag Style = "Hashtag" + // StyleBotCommand is a Style of type BotCommand. + StyleBotCommand Style = "BotCommand" + // StyleURL is a Style of type URL. + StyleURL Style = "URL" + // StyleEmail is a Style of type Email. + StyleEmail Style = "Email" + // StyleBold is a Style of type Bold. + StyleBold Style = "Bold" + // StyleItalic is a Style of type Italic. + StyleItalic Style = "Italic" + // StyleCode is a Style of type Code. + StyleCode Style = "Code" + // StylePre is a Style of type Pre. + StylePre Style = "Pre" + // StyleTextURL is a Style of type TextURL. + StyleTextURL Style = "TextURL" + // StyleMentionName is a Style of type MentionName. + StyleMentionName Style = "MentionName" + // StylePhone is a Style of type Phone. + StylePhone Style = "Phone" + // StyleCashtag is a Style of type Cashtag. + StyleCashtag Style = "Cashtag" + // StyleUnderline is a Style of type Underline. + StyleUnderline Style = "Underline" + // StyleStrike is a Style of type Strike. + StyleStrike Style = "Strike" + // StyleBankCard is a Style of type BankCard. + StyleBankCard Style = "BankCard" + // StyleSpoiler is a Style of type Spoiler. + StyleSpoiler Style = "Spoiler" + // StyleCustomEmoji is a Style of type CustomEmoji. + StyleCustomEmoji Style = "CustomEmoji" + // StyleBlockquote is a Style of type Blockquote. + StyleBlockquote Style = "Blockquote" +) + +var ErrInvalidStyle = fmt.Errorf("not a valid Style, try [%s]", strings.Join(_StyleNames, ", ")) + +var _StyleNames = []string{ + string(StylePlain), + string(StyleUnknown), + string(StyleMention), + string(StyleHashtag), + string(StyleBotCommand), + string(StyleURL), + string(StyleEmail), + string(StyleBold), + string(StyleItalic), + string(StyleCode), + string(StylePre), + string(StyleTextURL), + string(StyleMentionName), + string(StylePhone), + string(StyleCashtag), + string(StyleUnderline), + string(StyleStrike), + string(StyleBankCard), + string(StyleSpoiler), + string(StyleCustomEmoji), + string(StyleBlockquote), +} + +// StyleNames returns a list of possible string values of Style. +func StyleNames() []string { + tmp := make([]string, len(_StyleNames)) + copy(tmp, _StyleNames) + return tmp +} + +// StyleValues returns a list of the values for Style +func StyleValues() []Style { + return []Style{ + StylePlain, + StyleUnknown, + StyleMention, + StyleHashtag, + StyleBotCommand, + StyleURL, + StyleEmail, + StyleBold, + StyleItalic, + StyleCode, + StylePre, + StyleTextURL, + StyleMentionName, + StylePhone, + StyleCashtag, + StyleUnderline, + StyleStrike, + StyleBankCard, + StyleSpoiler, + StyleCustomEmoji, + StyleBlockquote, + } +} + +// String implements the Stringer interface. +func (x Style) String() string { + return string(x) +} + +// IsValid provides a quick way to determine if the typed value is +// part of the allowed enumerated values +func (x Style) IsValid() bool { + _, err := ParseStyle(string(x)) + return err == nil +} + +var _StyleValue = map[string]Style{ + "Plain": StylePlain, + "plain": StylePlain, + "Unknown": StyleUnknown, + "unknown": StyleUnknown, + "Mention": StyleMention, + "mention": StyleMention, + "Hashtag": StyleHashtag, + "hashtag": StyleHashtag, + "BotCommand": StyleBotCommand, + "botcommand": StyleBotCommand, + "URL": StyleURL, + "url": StyleURL, + "Email": StyleEmail, + "email": StyleEmail, + "Bold": StyleBold, + "bold": StyleBold, + "Italic": StyleItalic, + "italic": StyleItalic, + "Code": StyleCode, + "code": StyleCode, + "Pre": StylePre, + "pre": StylePre, + "TextURL": StyleTextURL, + "texturl": StyleTextURL, + "MentionName": StyleMentionName, + "mentionname": StyleMentionName, + "Phone": StylePhone, + "phone": StylePhone, + "Cashtag": StyleCashtag, + "cashtag": StyleCashtag, + "Underline": StyleUnderline, + "underline": StyleUnderline, + "Strike": StyleStrike, + "strike": StyleStrike, + "BankCard": StyleBankCard, + "bankcard": StyleBankCard, + "Spoiler": StyleSpoiler, + "spoiler": StyleSpoiler, + "CustomEmoji": StyleCustomEmoji, + "customemoji": StyleCustomEmoji, + "Blockquote": StyleBlockquote, + "blockquote": StyleBlockquote, +} + +// ParseStyle attempts to convert a string to a Style. +func ParseStyle(name string) (Style, error) { + if x, ok := _StyleValue[name]; ok { + return x, nil + } + // Case insensitive parse, do a separate lookup to prevent unnecessary cost of lowercasing a string if we don't need to. + if x, ok := _StyleValue[strings.ToLower(name)]; ok { + return x, nil + } + return Style(""), fmt.Errorf("%s is %w", name, ErrInvalidStyle) +} + +// Set implements the Golang flag.Value interface func. +func (x *Style) Set(val string) error { + v, err := ParseStyle(val) + *x = v + return err +} + +// Get implements the Golang flag.Getter interface func. +func (x *Style) Get() interface{} { + return *x +} + +// Type implements the github.com/spf13/pFlag Value interface. +func (x *Style) Type() string { + return "Style" +} diff --git a/pkg/uploader/iter.go b/pkg/uploader/iter.go index d56522a0d..b6eda2537 100644 --- a/pkg/uploader/iter.go +++ b/pkg/uploader/iter.go @@ -2,6 +2,7 @@ package uploader import ( "context" + "github.com/gotd/td/telegram/message" "io" "github.com/gotd/td/tg" @@ -22,6 +23,7 @@ type File interface { type Elem interface { File() File Thumb() (File, bool) + Caption() []message.StyledTextOption To() tg.InputPeerClass Thread() int AsPhoto() bool diff --git a/pkg/uploader/uploader.go b/pkg/uploader/uploader.go index e3599d152..ab033e876 100644 --- a/pkg/uploader/uploader.go +++ b/pkg/uploader/uploader.go @@ -8,7 +8,6 @@ import ( "github.com/gabriel-vasile/mimetype" "github.com/go-faster/errors" "github.com/gotd/td/telegram/message" - "github.com/gotd/td/telegram/message/styling" "github.com/gotd/td/telegram/uploader" "github.com/gotd/td/tg" "golang.org/x/sync/errgroup" @@ -91,12 +90,7 @@ func (u *Uploader) upload(ctx context.Context, elem Elem) error { return errors.Wrap(err, "detect mime") } - caption := []message.StyledTextOption{ - styling.Code(elem.File().Name()), - styling.Plain(" - "), - styling.Code(mime.String()), - } - doc := message.UploadedDocument(f, caption...). + doc := message.UploadedDocument(f, elem.Caption()...). MIME(mime.String()). Filename(elem.File().Name()) // upload thumbnail TODO(iyear): maybe still unavailable @@ -116,7 +110,7 @@ func (u *Uploader) upload(ctx context.Context, elem Elem) error { break } // upload as photo - media = message.UploadedPhoto(f, caption...) + media = message.UploadedPhoto(f, elem.Caption()...) case utils.Media.IsVideo(mime.String()): // reset reader if _, err = elem.File().Seek(0, io.SeekStart); err != nil { From 4bf5efceedf6eb52de2306e14f46ad137df34997 Mon Sep 17 00:00:00 2001 From: XMLHexagram Date: Thu, 28 Mar 2024 22:57:32 +0800 Subject: [PATCH 6/6] doc(upload): simply update about custom file caption --- docs/content/en/guide/upload.md | 65 +++++++++++++++++++++++++++++++++ docs/content/zh/guide/upload.md | 65 +++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) diff --git a/docs/content/en/guide/upload.md b/docs/content/en/guide/upload.md index b89ee8476..19da7397f 100644 --- a/docs/content/en/guide/upload.md +++ b/docs/content/en/guide/upload.md @@ -31,6 +31,71 @@ Upload with 8 threads per task, 512KiB(MAX) part size, 4 concurrent tasks: tdl up -p /path/to/file -t 8 -s 524288 -l 4 {{< /command >}} +## Custom Caption + +Custom Caption is based on [expression](/reference/expr). + +List all available fields: + +{{< command >}} +tdl up -p ./foo --caption - +{{< /command >}} + +Supported Style: + +```go +const ( + // StylePlain is a Style of type Plain. + StylePlain Style = "Plain" + // StyleUnknown is a Style of type Unknown. + StyleUnknown Style = "Unknown" + // StyleMention is a Style of type Mention. + StyleMention Style = "Mention" + // StyleHashtag is a Style of type Hashtag. + StyleHashtag Style = "Hashtag" + // StyleBotCommand is a Style of type BotCommand. + StyleBotCommand Style = "BotCommand" + // StyleURL is a Style of type URL. + StyleURL Style = "URL" + // StyleEmail is a Style of type Email. + StyleEmail Style = "Email" + // StyleBold is a Style of type Bold. + StyleBold Style = "Bold" + // StyleItalic is a Style of type Italic. + StyleItalic Style = "Italic" + // StyleCode is a Style of type Code. + StyleCode Style = "Code" + // StylePre is a Style of type Pre. + StylePre Style = "Pre" + // StyleTextURL is a Style of type TextURL. + StyleTextURL Style = "TextURL" + // StyleMentionName is a Style of type MentionName. + StyleMentionName Style = "MentionName" + // StylePhone is a Style of type Phone. + StylePhone Style = "Phone" + // StyleCashtag is a Style of type Cashtag. + StyleCashtag Style = "Cashtag" + // StyleUnderline is a Style of type Underline. + StyleUnderline Style = "Underline" + // StyleStrike is a Style of type Strike. + StyleStrike Style = "Strike" + // StyleBankCard is a Style of type BankCard. + StyleBankCard Style = "BankCard" + // StyleSpoiler is a Style of type Spoiler. + StyleSpoiler Style = "Spoiler" + // StyleCustomEmoji is a Style of type CustomEmoji. + StyleCustomEmoji Style = "CustomEmoji" + // StyleBlockquote is a Style of type Blockquote. + StyleBlockquote Style = "Blockquote" +) +``` + +Example: + +{{< command >}} +tdl up -p ./downloads --caption '[{style: "code", text: File}, "-", {style: "bold", text: Filename}, "-", {style: "strike", text: Extension}, "-", {style: "italic", text: Mime}]' +{{< /command >}} + ## Filter Upload files except specified extensions: diff --git a/docs/content/zh/guide/upload.md b/docs/content/zh/guide/upload.md index 60a2720bb..d5640492a 100644 --- a/docs/content/zh/guide/upload.md +++ b/docs/content/zh/guide/upload.md @@ -31,6 +31,71 @@ tdl up -p /path/to/file -c CHAT tdl up -p /path/to/file -t 8 -s 524288 -l 4 {{< /command >}} +## 自定义说明文字 + +自定义说明文字基于 [表达式](/reference/expr)。 + +列出所有可用字段: + +{{< command >}} +tdl up -p ./foo --caption - +{{< /command >}} + +支持的样式: + +```go +const ( + // StylePlain is a Style of type Plain. + StylePlain Style = "Plain" + // StyleUnknown is a Style of type Unknown. + StyleUnknown Style = "Unknown" + // StyleMention is a Style of type Mention. + StyleMention Style = "Mention" + // StyleHashtag is a Style of type Hashtag. + StyleHashtag Style = "Hashtag" + // StyleBotCommand is a Style of type BotCommand. + StyleBotCommand Style = "BotCommand" + // StyleURL is a Style of type URL. + StyleURL Style = "URL" + // StyleEmail is a Style of type Email. + StyleEmail Style = "Email" + // StyleBold is a Style of type Bold. + StyleBold Style = "Bold" + // StyleItalic is a Style of type Italic. + StyleItalic Style = "Italic" + // StyleCode is a Style of type Code. + StyleCode Style = "Code" + // StylePre is a Style of type Pre. + StylePre Style = "Pre" + // StyleTextURL is a Style of type TextURL. + StyleTextURL Style = "TextURL" + // StyleMentionName is a Style of type MentionName. + StyleMentionName Style = "MentionName" + // StylePhone is a Style of type Phone. + StylePhone Style = "Phone" + // StyleCashtag is a Style of type Cashtag. + StyleCashtag Style = "Cashtag" + // StyleUnderline is a Style of type Underline. + StyleUnderline Style = "Underline" + // StyleStrike is a Style of type Strike. + StyleStrike Style = "Strike" + // StyleBankCard is a Style of type BankCard. + StyleBankCard Style = "BankCard" + // StyleSpoiler is a Style of type Spoiler. + StyleSpoiler Style = "Spoiler" + // StyleCustomEmoji is a Style of type CustomEmoji. + StyleCustomEmoji Style = "CustomEmoji" + // StyleBlockquote is a Style of type Blockquote. + StyleBlockquote Style = "Blockquote" +) +``` + +例子: + +{{< command >}} +tdl up -p ./downloads --caption '[{style: "code", text: File}, "-", {style: "bold", text: Filename}, "-", {style: "strike", text: Extension}, "-", {style: "italic", text: Mime}]' +{{< /command >}} + ## 过滤器 上传除指定扩展名之外的文件: