Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(upload): support custom file’s caption by Expr #565

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions app/up/elem.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package up

import (
"github.com/gotd/td/telegram/message"
"os"
"path/filepath"

Expand All @@ -11,9 +12,11 @@ import (
)

type iterElem struct {
file *uploaderFile
thumb *uploaderFile
to peers.Peer
file *uploaderFile
thumb *uploaderFile
to peers.Peer
caption []message.StyledTextOption
thread int

asPhoto bool
remove bool
Expand All @@ -30,10 +33,18 @@ 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()
}

func (e *iterElem) Thread() int {
return e.thread
}

func (e *iterElem) AsPhoto() bool {
return e.asPhoto
}
Expand Down
174 changes: 151 additions & 23 deletions app/up/iter.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,89 @@ 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"
"path/filepath"
"strings"

"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 Env struct {
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 exprEnv(ctx context.Context, file *File) Env {
if file == nil {
return Env{}
}

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 Env{
File: file.File,
Thumb: file.Thumb,
Filename: filename,
Extension: extension,
Mime: mime.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
caption *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, caption *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,
caption: caption,
chat: chat,
topic: topic,
photo: photo,
remove: remove,
manager: manager,

cur: 0,
err: nil,
Expand All @@ -56,21 +107,89 @@ 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, exprEnv(ctx, 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
}
}

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 != "" {
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
Expand All @@ -86,17 +205,26 @@ 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,
caption: caption,
thread: thread,
asPhoto: i.photo,
remove: i.remove,
}

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
}
Expand Down
Loading