refactor(cmd): create cmd in functions

This commit is contained in:
iyear 2023-03-29 18:20:23 +08:00
parent 5e3a798f42
commit dd71331ac5
22 changed files with 421 additions and 343 deletions

44
cmd/archive.go Normal file
View File

@ -0,0 +1,44 @@
package cmd
import (
"fmt"
"github.com/iyear/tdl/app/archive"
"github.com/spf13/cobra"
"time"
)
func NewBackup() *cobra.Command {
var dst string
cmd := &cobra.Command{
Use: "backup",
Short: "Backup your data",
RunE: func(cmd *cobra.Command, args []string) error {
if dst == "" {
dst = fmt.Sprintf("tdl-backup-%s.zip", time.Now().Format("2006-01-02-15_04_05"))
}
return archive.Backup(cmd.Context(), dst)
},
}
cmd.Flags().StringVarP(&dst, "dst", "d", "", "destination file path. Default: tdl-backup-<time>.zip")
return cmd
}
func NewRecover() *cobra.Command {
var file string
cmd := &cobra.Command{
Use: "recover",
Short: "Recover your data",
RunE: func(cmd *cobra.Command, args []string) error {
return archive.Recover(cmd.Context(), file)
},
}
cmd.Flags().StringVarP(&file, "file", "f", "", "backup file path")
return cmd
}

View File

@ -1,26 +0,0 @@
package archive
import (
"fmt"
"github.com/iyear/tdl/app/archive"
"github.com/spf13/cobra"
"time"
)
var dst string
var CmdBackup = &cobra.Command{
Use: "backup",
Short: "Backup your data",
RunE: func(cmd *cobra.Command, args []string) error {
if dst == "" {
dst = fmt.Sprintf("tdl-backup-%s.zip", time.Now().Format("2006-01-02-15_04_05"))
}
return archive.Backup(cmd.Context(), dst)
},
}
func init() {
CmdBackup.Flags().StringVarP(&dst, "dst", "d", "", "destination file path. Default: tdl-backup-<time>.zip")
}

View File

@ -1,20 +0,0 @@
package archive
import (
"github.com/iyear/tdl/app/archive"
"github.com/spf13/cobra"
)
var file string
var CmdRecover = &cobra.Command{
Use: "recover",
Short: "Recover your data",
RunE: func(cmd *cobra.Command, args []string) error {
return archive.Recover(cmd.Context(), file)
},
}
func init() {
CmdRecover.Flags().StringVarP(&file, "file", "f", "", "backup file path")
}

87
cmd/chat.go Normal file
View File

@ -0,0 +1,87 @@
package cmd
import (
"fmt"
"github.com/iyear/tdl/app/chat"
"github.com/iyear/tdl/pkg/logger"
"github.com/iyear/tdl/pkg/utils"
"github.com/spf13/cobra"
"math"
"strings"
)
func NewChat() *cobra.Command {
cmd := &cobra.Command{
Use: "chat",
Short: "A set of chat tools",
}
cmd.AddCommand(NewChatList(), NewChatExport())
return cmd
}
func NewChatList() *cobra.Command {
cmd := &cobra.Command{
Use: "ls",
Short: "List your chats",
RunE: func(cmd *cobra.Command, args []string) error {
return chat.List(logger.Named(cmd.Context(), "ls"))
},
}
return cmd
}
func NewChatExport() *cobra.Command {
var opts chat.ExportOptions
cmd := &cobra.Command{
Use: "export",
Short: "export messages from (protected) chat for download",
RunE: func(cmd *cobra.Command, args []string) error {
switch opts.Type {
case chat.ExportTypeTime, chat.ExportTypeID:
// set default value
switch len(opts.Input) {
case 0:
opts.Input = []int{0, math.MaxInt}
case 1:
opts.Input = append(opts.Input, math.MaxInt)
}
if len(opts.Input) != 2 {
return fmt.Errorf("input data should be 2 integers when export type is %s", opts.Type)
}
// sort helper
if opts.Input[0] > opts.Input[1] {
opts.Input[0], opts.Input[1] = opts.Input[1], opts.Input[0]
}
case chat.ExportTypeLast:
if len(opts.Input) != 1 {
return fmt.Errorf("input data should be 1 integer when export type is %s", opts.Type)
}
default:
return fmt.Errorf("unknown export type: %s", opts.Type)
}
// set default filters
for _, filter := range chat.Filters {
if opts.Filter[filter] == "" {
opts.Filter[filter] = ".*"
}
}
return chat.Export(logger.Named(cmd.Context(), "export"), &opts)
},
}
utils.Cmd.StringEnumFlag(cmd, &opts.Type, "type", "T", chat.ExportTypeTime, []string{chat.ExportTypeTime, chat.ExportTypeID, chat.ExportTypeLast}, "export type. time: timestamp range, id: message id range, last: last N messages")
cmd.Flags().StringVarP(&opts.Chat, "chat", "c", "", "chat id or domain")
cmd.Flags().IntSliceVarP(&opts.Input, "input", "i", []int{}, "input data, depends on export type")
cmd.Flags().StringToStringVarP(&opts.Filter, "filter", "f", map[string]string{}, "only export media files that match the filter (regex). Default to all. Options: "+strings.Join(chat.Filters, ", "))
cmd.Flags().StringVarP(&opts.Output, "output", "o", "tdl-export.json", "output JSON file path")
return cmd
}

View File

@ -1,14 +0,0 @@
package chat
import (
"github.com/spf13/cobra"
)
var Cmd = &cobra.Command{
Use: "chat",
Short: "A set of chat tools",
}
func init() {
Cmd.AddCommand(cmdList, cmdExport)
}

View File

@ -1,62 +0,0 @@
package chat
import (
"fmt"
"github.com/iyear/tdl/app/chat"
"github.com/iyear/tdl/pkg/logger"
"github.com/iyear/tdl/pkg/utils"
"github.com/spf13/cobra"
"math"
"strings"
)
var expOpts = &chat.ExportOptions{}
var cmdExport = &cobra.Command{
Use: "export",
Short: "export messages from (protected) chat for download",
RunE: func(cmd *cobra.Command, args []string) error {
switch expOpts.Type {
case chat.ExportTypeTime, chat.ExportTypeID:
// set default value
switch len(expOpts.Input) {
case 0:
expOpts.Input = []int{0, math.MaxInt}
case 1:
expOpts.Input = append(expOpts.Input, math.MaxInt)
}
if len(expOpts.Input) != 2 {
return fmt.Errorf("input data should be 2 integers when export type is %s", expOpts.Type)
}
// sort helper
if expOpts.Input[0] > expOpts.Input[1] {
expOpts.Input[0], expOpts.Input[1] = expOpts.Input[1], expOpts.Input[0]
}
case chat.ExportTypeLast:
if len(expOpts.Input) != 1 {
return fmt.Errorf("input data should be 1 integer when export type is %s", expOpts.Type)
}
default:
return fmt.Errorf("unknown export type: %s", expOpts.Type)
}
// set default filters
for _, filter := range chat.Filters {
if expOpts.Filter[filter] == "" {
expOpts.Filter[filter] = ".*"
}
}
return chat.Export(logger.Named(cmd.Context(), "export"), expOpts)
},
}
func init() {
utils.Cmd.StringEnumFlag(cmdExport, &expOpts.Type, "type", "T", chat.ExportTypeTime, []string{chat.ExportTypeTime, chat.ExportTypeID, chat.ExportTypeLast}, "export type. time: timestamp range, id: message id range, last: last N messages")
cmdExport.Flags().StringVarP(&expOpts.Chat, "chat", "c", "", "chat id or domain")
cmdExport.Flags().IntSliceVarP(&expOpts.Input, "input", "i", []int{}, "input data, depends on export type")
cmdExport.Flags().StringToStringVarP(&expOpts.Filter, "filter", "f", map[string]string{}, "only export media files that match the filter (regex). Default to all. Options: "+strings.Join(chat.Filters, ", "))
cmdExport.Flags().StringVarP(&expOpts.Output, "output", "o", "tdl-export.json", "output JSON file path")
}

View File

@ -1,15 +0,0 @@
package chat
import (
"github.com/iyear/tdl/app/chat"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
)
var cmdList = &cobra.Command{
Use: "ls",
Short: "List your chats",
RunE: func(cmd *cobra.Command, args []string) error {
return chat.List(logger.Named(cmd.Context(), "ls"))
},
}

67
cmd/dl.go Normal file
View File

@ -0,0 +1,67 @@
package cmd
import (
"errors"
"fmt"
"github.com/iyear/tdl/app/dl"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"strings"
)
func NewDownload() *cobra.Command {
var opts dl.Options
cmd := &cobra.Command{
Use: "download",
Aliases: []string{"dl"},
Short: "Download anything from Telegram (protected) chat",
RunE: func(cmd *cobra.Command, args []string) error {
// only one of include and exclude can be specified
if len(opts.Include) > 0 && len(opts.Exclude) > 0 {
return errors.New("only one of `include` and `exclude` can be specified")
}
// only one of continue and restart can be specified
if opts.Continue && opts.Restart {
return errors.New("only one of `continue` and `restart` can be specified, or none of them")
}
opts.Template = viper.GetString(consts.FlagDlTemplate)
return dl.Run(logger.Named(cmd.Context(), "dl"), &opts)
},
}
cmd.Flags().StringSliceVarP(&opts.URLs, consts.FlagDlUrl, "u", []string{}, "telegram message links")
cmd.Flags().StringSliceVarP(&opts.Files, consts.FlagDlFile, "f", []string{}, "official client exported files")
// generate default replacer
builder := strings.Builder{}
chars := []string{`/`, `\`, `:`, `*`, `?`, `<`, `>`, `|`, ` `}
for _, c := range chars {
builder.WriteString(fmt.Sprintf("`%s` `_` ", c))
}
t := fmt.Sprintf(`{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName %s }}`, builder.String())
cmd.Flags().String(consts.FlagDlTemplate, t, "download file name template")
cmd.Flags().StringSliceVarP(&opts.Include, consts.FlagDlInclude, "i", []string{}, "include the specified file extensions, and only judge by file name, not file MIME. Example: -i mp4,mp3")
cmd.Flags().StringSliceVarP(&opts.Exclude, consts.FlagDlExclude, "e", []string{}, "exclude the specified file extensions, and only judge by file name, not file MIME. Example: -e png,jpg")
cmd.Flags().StringVarP(&opts.Dir, consts.FlagDlDir, "d", "downloads", "specify the download directory. If the directory does not exist, it will be created automatically")
cmd.Flags().BoolVar(&opts.RewriteExt, consts.FlagDlRewriteExt, false, "rewrite file extension according to file header MIME")
// do not match extension, because some files' extension is corrected by --rewrite-ext flag
cmd.Flags().BoolVar(&opts.SkipSame, consts.FlagDlSkipSame, false, "skip files with the same name(without extension) and size")
cmd.Flags().Int64Var(&opts.PoolSize, consts.FlagDlPool, 3, "specify the size of the DC pool")
cmd.Flags().BoolVar(&opts.Desc, consts.FlagDlDesc, false, "download files from the newest to the oldest ones (may affect resume download)")
// resume flags, if both false then ask user
cmd.Flags().BoolVar(&opts.Continue, consts.FlagDlContinue, false, "continue the last download directly")
cmd.Flags().BoolVar(&opts.Restart, consts.FlagDlRestart, false, "restart the last download directly")
_ = viper.BindPFlag(consts.FlagDlTemplate, cmd.Flags().Lookup(consts.FlagDlTemplate))
return cmd
}

View File

@ -1,65 +0,0 @@
package dl
import (
"errors"
"fmt"
"github.com/iyear/tdl/app/dl"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"strings"
)
var opts = &dl.Options{}
var Cmd = &cobra.Command{
Use: "dl",
Aliases: []string{"download"},
Short: "Download anything from Telegram (protected) chat",
RunE: func(cmd *cobra.Command, args []string) error {
// only one of include and exclude can be specified
if len(opts.Include) > 0 && len(opts.Exclude) > 0 {
return errors.New("only one of `include` and `exclude` can be specified")
}
// only one of continue and restart can be specified
if opts.Continue && opts.Restart {
return errors.New("only one of `continue` and `restart` can be specified, or none of them")
}
opts.Template = viper.GetString(consts.FlagDlTemplate)
return dl.Run(logger.Named(cmd.Context(), "dl"), opts)
},
}
func init() {
Cmd.Flags().StringSliceVarP(&opts.URLs, consts.FlagDlUrl, "u", []string{}, "telegram message links")
Cmd.Flags().StringSliceVarP(&opts.Files, consts.FlagDlFile, "f", []string{}, "official client exported files")
// generate default replacer
builder := strings.Builder{}
chars := []string{`/`, `\`, `:`, `*`, `?`, `<`, `>`, `|`, ` `}
for _, c := range chars {
builder.WriteString(fmt.Sprintf("`%s` `_` ", c))
}
t := fmt.Sprintf(`{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName %s }}`, builder.String())
Cmd.Flags().String(consts.FlagDlTemplate, t, "download file name template")
Cmd.Flags().StringSliceVarP(&opts.Include, consts.FlagDlInclude, "i", []string{}, "include the specified file extensions, and only judge by file name, not file MIME. Example: -i mp4,mp3")
Cmd.Flags().StringSliceVarP(&opts.Exclude, consts.FlagDlExclude, "e", []string{}, "exclude the specified file extensions, and only judge by file name, not file MIME. Example: -e png,jpg")
Cmd.Flags().StringVarP(&opts.Dir, consts.FlagDlDir, "d", "downloads", "specify the download directory. If the directory does not exist, it will be created automatically")
Cmd.Flags().BoolVar(&opts.RewriteExt, consts.FlagDlRewriteExt, false, "rewrite file extension according to file header MIME")
// do not match extension, because some files' extension is corrected by --rewrite-ext flag
Cmd.Flags().BoolVar(&opts.SkipSame, consts.FlagDlSkipSame, false, "skip files with the same name(without extension) and size")
Cmd.Flags().Int64Var(&opts.PoolSize, consts.FlagDlPool, 3, "specify the size of the DC pool")
Cmd.Flags().BoolVar(&opts.Desc, consts.FlagDlDesc, false, "download files from the newest to the oldest ones (may affect resume download)")
// resume flags, if both false then ask user
Cmd.Flags().BoolVar(&opts.Continue, consts.FlagDlContinue, false, "continue the last download directly")
Cmd.Flags().BoolVar(&opts.Restart, consts.FlagDlRestart, false, "restart the last download directly")
_ = viper.BindPFlag(consts.FlagDlTemplate, Cmd.Flags().Lookup(consts.FlagDlTemplate))
}

36
cmd/login.go Normal file
View File

@ -0,0 +1,36 @@
package cmd
import (
"github.com/fatih/color"
"github.com/iyear/tdl/app/login"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
)
func NewLogin() *cobra.Command {
var (
code bool
opts login.Options
)
cmd := &cobra.Command{
Use: "login",
Short: "Login to Telegram",
RunE: func(cmd *cobra.Command, args []string) error {
color.Yellow("WARN: If data exists in the namespace, data will be overwritten")
if code {
return login.Code(logger.Named(cmd.Context(), "login"))
}
return login.Desktop(cmd.Context(), &opts)
},
}
cmd.Flags().StringVarP(&opts.Desktop, consts.FlagLoginDesktop, "d", "", "official desktop client path, and automatically find possible paths if empty")
cmd.Flags().StringVarP(&opts.Passcode, consts.FlagLoginPasscode, "p", "", "passcode for desktop client, keep empty if no passcode")
cmd.Flags().BoolVar(&code, consts.FlagLoginCode, false, "login with code, instead of importing session from desktop client")
return cmd
}

View File

@ -1,34 +0,0 @@
package login
import (
"github.com/fatih/color"
"github.com/iyear/tdl/app/login"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
)
var (
code bool
opts login.Options
)
var Cmd = &cobra.Command{
Use: "login",
Short: "Login to Telegram",
RunE: func(cmd *cobra.Command, args []string) error {
color.Yellow("WARN: If data exists in the namespace, data will be overwritten")
if code {
return login.Code(logger.Named(cmd.Context(), "login"))
}
return login.Desktop(cmd.Context(), &opts)
},
}
func init() {
Cmd.Flags().StringVarP(&opts.Desktop, consts.FlagLoginDesktop, "d", "", "official desktop client path, and automatically find possible paths if empty")
Cmd.Flags().StringVarP(&opts.Passcode, consts.FlagLoginPasscode, "p", "", "passcode for desktop client, keep empty if no passcode")
Cmd.Flags().BoolVar(&code, consts.FlagLoginCode, false, "login with code, instead of importing session from desktop client")
}

View File

@ -1,14 +1,7 @@
package cmd
import (
"context"
"github.com/fatih/color"
"github.com/iyear/tdl/cmd/archive"
"github.com/iyear/tdl/cmd/chat"
"github.com/iyear/tdl/cmd/dl"
"github.com/iyear/tdl/cmd/login"
"github.com/iyear/tdl/cmd/up"
"github.com/iyear/tdl/cmd/version"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/iyear/tdl/pkg/utils"
@ -16,39 +9,39 @@ import (
"github.com/spf13/cobra/doc"
"github.com/spf13/viper"
"go.uber.org/zap"
"os"
"os/signal"
"path/filepath"
)
var cmd = &cobra.Command{
Use: "tdl",
Short: "Telegram Downloader, but more than a downloader",
DisableAutoGenTag: true,
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// init logger
debug, level := viper.GetBool(consts.FlagDebug), zap.InfoLevel
if debug {
level = zap.DebugLevel
}
cmd.SetContext(logger.With(cmd.Context(), logger.New(level)))
func New() *cobra.Command {
cmd := &cobra.Command{
Use: "tdl",
Short: "Telegram Downloader, but more than a downloader",
DisableAutoGenTag: true,
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
// init logger
debug, level := viper.GetBool(consts.FlagDebug), zap.InfoLevel
if debug {
level = zap.DebugLevel
}
cmd.SetContext(logger.With(cmd.Context(), logger.New(level)))
ns := viper.GetString(consts.FlagNamespace)
if ns != "" {
color.Cyan("Namespace: %s", ns)
logger.From(cmd.Context()).Info("Namespace",
zap.String("namespace", ns))
}
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
return logger.From(cmd.Context()).Sync()
},
}
ns := viper.GetString(consts.FlagNamespace)
if ns != "" {
color.Cyan("Namespace: %s", ns)
logger.From(cmd.Context()).Info("Namespace",
zap.String("namespace", ns))
}
},
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
return logger.From(cmd.Context()).Sync()
},
}
cmd.AddCommand(NewVersion(), NewLogin(), NewDownload(),
NewChat(), NewUpload(), NewBackup(), NewRecover())
func init() {
cmd.AddCommand(version.Cmd, login.Cmd, dl.Cmd, chat.Cmd, up.Cmd, archive.CmdBackup, archive.CmdRecover)
cmd.PersistentFlags().String(consts.FlagProxy, "", "proxy address, only socks5 is supported, format: protocol://username:password@host:port")
cmd.PersistentFlags().StringP(consts.FlagNamespace, "n", "", "namespace for Telegram session")
cmd.PersistentFlags().Bool(consts.FlagDebug, false, "enable debug mode")
@ -66,18 +59,16 @@ func init() {
viper.SetEnvPrefix("tdl")
viper.AutomaticEnv()
generateCommandDocs(cmd)
return cmd
}
func generateCommandDocs(cmd *cobra.Command) {
docs := filepath.Join(consts.DocsPath, "command")
if utils.FS.PathExists(docs) {
if err := doc.GenMarkdownTree(cmd, docs); err != nil {
color.Red("generate cmd docs failed: %v", err)
return
}
}
}
func Execute() error {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
return cmd.ExecuteContext(ctx)
}

27
cmd/up.go Normal file
View File

@ -0,0 +1,27 @@
package cmd
import (
"github.com/iyear/tdl/app/up"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
)
func NewUpload() *cobra.Command {
var opts up.Options
cmd := &cobra.Command{
Use: "upload",
Aliases: []string{"up"},
Short: "Upload anything to Telegram",
RunE: func(cmd *cobra.Command, args []string) error {
return up.Run(logger.Named(cmd.Context(), "up"), &opts)
},
}
cmd.Flags().StringVarP(&opts.Chat, "chat", "c", "", "chat id or domain, and empty means 'Saved Messages'")
cmd.Flags().StringSliceVarP(&opts.Paths, consts.FlagUpPath, "p", []string{}, "dirs or files")
cmd.Flags().StringSliceVarP(&opts.Excludes, consts.FlagUpExcludes, "e", []string{}, "exclude the specified file extensions")
return cmd
}

View File

@ -1,25 +0,0 @@
package up
import (
"github.com/iyear/tdl/app/up"
"github.com/iyear/tdl/pkg/consts"
"github.com/iyear/tdl/pkg/logger"
"github.com/spf13/cobra"
)
var opts = &up.Options{}
var Cmd = &cobra.Command{
Use: "up",
Aliases: []string{"upload"},
Short: "Upload anything to Telegram",
RunE: func(cmd *cobra.Command, args []string) error {
return up.Run(logger.Named(cmd.Context(), "up"), opts)
},
}
func init() {
Cmd.Flags().StringVarP(&opts.Chat, "chat", "c", "", "chat id or domain, and empty means 'Saved Messages'")
Cmd.Flags().StringSliceVarP(&opts.Paths, consts.FlagUpPath, "p", []string{}, "dirs or files")
Cmd.Flags().StringSliceVarP(&opts.Excludes, consts.FlagUpExcludes, "e", []string{}, "exclude the specified file extensions")
}

36
cmd/version.go Normal file
View File

@ -0,0 +1,36 @@
package cmd
import (
"bytes"
_ "embed"
"github.com/fatih/color"
"github.com/iyear/tdl/pkg/consts"
"github.com/spf13/cobra"
"runtime"
"text/template"
)
//go:embed version.tmpl
var version string
func NewVersion() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Check the version info",
RunE: func(cmd *cobra.Command, args []string) error {
buf := &bytes.Buffer{}
if err := template.Must(template.New("version").Parse(version)).Execute(buf, map[string]interface{}{
"Version": consts.Version,
"Commit": consts.Commit,
"Date": consts.CommitDate,
"GoVersion": runtime.Version(),
"GOOS": runtime.GOOS,
"GOARCH": runtime.GOARCH,
}); err != nil {
return err
}
color.Blue(buf.String())
return nil
},
}
}

View File

@ -1,31 +0,0 @@
package version
import (
"bytes"
_ "embed"
"github.com/fatih/color"
"github.com/iyear/tdl/pkg/consts"
"github.com/spf13/cobra"
"runtime"
"text/template"
)
//go:embed version.tmpl
var version string
var Cmd = &cobra.Command{
Use: "version",
Short: "Check the version info",
Run: func(cmd *cobra.Command, args []string) {
buf := &bytes.Buffer{}
_ = template.Must(template.New("version").Parse(version)).Execute(buf, map[string]interface{}{
"Version": consts.Version,
"Commit": consts.Commit,
"Date": consts.CommitDate,
"GoVersion": runtime.Version(),
"GOOS": runtime.GOOS,
"GOARCH": runtime.GOARCH,
})
color.Blue(buf.String())
},
}

View File

@ -19,9 +19,9 @@ Telegram Downloader, but more than a downloader
* [tdl backup](tdl_backup.md) - Backup your data
* [tdl chat](tdl_chat.md) - A set of chat tools
* [tdl dl](tdl_dl.md) - Download anything from Telegram (protected) chat
* [tdl download](tdl_download.md) - Download anything from Telegram (protected) chat
* [tdl login](tdl_login.md) - Login to Telegram
* [tdl recover](tdl_recover.md) - Recover your data
* [tdl up](tdl_up.md) - Upload anything to Telegram
* [tdl upload](tdl_upload.md) - Upload anything to Telegram
* [tdl version](tdl_version.md) - Check the version info

View File

@ -9,11 +9,12 @@ tdl chat export [flags]
### Options
```
-c, --chat string chat id or domain
-h, --help help for export
-i, --input ints input data, depends on export type
-o, --output string output JSON file path (default "tdl-export.json")
-T, --type string export type. time: timestamp range, id: message id range, last: last N messages: {time|id|last} (default "time")
-c, --chat string chat id or domain
-f, --filter stringToString only export media files that match the filter (regex). Default to all. Options: file, content (default [])
-h, --help help for export
-i, --input ints input data, depends on export type
-o, --output string output JSON file path (default "tdl-export.json")
-T, --type string export type. time: timestamp range, id: message id range, last: last N messages: {time|id|last} (default "time")
```
### Options inherited from parent commands

View File

@ -0,0 +1,42 @@
## tdl download
Download anything from Telegram (protected) chat
```
tdl download [flags]
```
### Options
```
--continue continue the last download directly
--desc download files from the newest to the oldest ones (may affect resume download)
-d, --dir string specify the download directory. If the directory does not exist, it will be created automatically (default "downloads")
-e, --exclude strings exclude the specified file extensions, and only judge by file name, not file MIME. Example: -e png,jpg
-f, --file strings official client exported files
-h, --help help for download
-i, --include strings include the specified file extensions, and only judge by file name, not file MIME. Example: -i mp4,mp3
--pool int specify the size of the DC pool (default 3)
--restart restart the last download directly
--rewrite-ext rewrite file extension according to file header MIME
--skip-same skip files with the same name(without extension) and size
--template string download file name template (default "{{ .DialogID }}_{{ .MessageID }}_{{ replace .FileName `/` `_` `\\` `_` `:` `_` `*` `_` `?` `_` `<` `_` `>` `_` `|` `_` ` ` `_` }}")
-u, --url strings telegram message links
```
### Options inherited from parent commands
```
--debug enable debug mode
-l, --limit int max number of concurrent tasks (default 2)
-n, --ns string namespace for Telegram session
--ntp string ntp server host, if not set, use system time
--proxy string proxy address, only socks5 is supported, format: protocol://username:password@host:port
-s, --size int part size for transfer, max is 512*1024 (default 131072)
-t, --threads int max threads for transfer one item (default 4)
```
### SEE ALSO
* [tdl](tdl.md) - Telegram Downloader, but more than a downloader

View File

@ -0,0 +1,33 @@
## tdl upload
Upload anything to Telegram
```
tdl upload [flags]
```
### Options
```
-c, --chat string chat id or domain, and empty means 'Saved Messages'
-e, --excludes strings exclude the specified file extensions
-h, --help help for upload
-p, --path strings dirs or files
```
### Options inherited from parent commands
```
--debug enable debug mode
-l, --limit int max number of concurrent tasks (default 2)
-n, --ns string namespace for Telegram session
--ntp string ntp server host, if not set, use system time
--proxy string proxy address, only socks5 is supported, format: protocol://username:password@host:port
-s, --size int part size for transfer, max is 512*1024 (default 131072)
-t, --threads int max threads for transfer one item (default 4)
```
### SEE ALSO
* [tdl](tdl.md) - Telegram Downloader, but more than a downloader

View File

@ -1,12 +1,18 @@
package main
import (
"context"
"github.com/fatih/color"
"github.com/iyear/tdl/cmd"
"os"
"os/signal"
)
func main() {
if err := cmd.Execute(); err != nil {
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
if err := cmd.New().ExecuteContext(ctx); err != nil {
color.Red("%v", err)
}
}