Skip to content

Commit

Permalink
add list convo alias for dump
Browse files Browse the repository at this point in the history
  • Loading branch information
rusq committed Jan 19, 2023
1 parent 6068a28 commit 1384466
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 41 deletions.
8 changes: 4 additions & 4 deletions cmd/slackdump/internal/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,21 @@ func SetBaseFlags(fs *flag.FlagSet, mask FlagMask) {
if mask&OmitAuthFlags == 0 {
fs.StringVar(&SlackToken, "token", osenv.Secret("SLACK_TOKEN", ""), "Slack `token`")
// COOKIE environment variable is deprecated and will be removed in v2.5.0, use SLACK_COOKIE instead.
fs.StringVar(&SlackCookie, "cookie", osenv.Secret("SLACK_COOKIE", osenv.Secret("COOKIE", "")), "d= cookie `value` or a path to a cookie.txt file (environment: SLACK_COOKIE)")
fs.StringVar(&SlackCookie, "cookie", osenv.Secret("SLACK_COOKIE", osenv.Secret("COOKIE", "")), "d= cookie `value` or a path to a cookie.txt file\n(environment: SLACK_COOKIE)")
fs.Var(&Browser, "browser", "browser to use for EZ-Login 3000 (default: firefox)")
}
if mask&OmitDownloadFlag == 0 {
fs.BoolVar(&SlackOptions.DumpFiles, "files", true, "enables file attachments download (to disable, specify: -files=false)")
fs.BoolVar(&SlackOptions.DumpFiles, "files", true, "enables file attachments download (to disable,\nspecify: -files=false)")
}
if mask&OmitConfigFlag == 0 {
fs.StringVar(&ConfigFile, "api-config", "", "configuration `file` with Slack API limits overrides.\nYou can generate one with default values with 'slackdump config new`")
}
if mask&OmitBaseLoc == 0 {
base := fmt.Sprintf("slackdump_%s.zip", time.Now().Format("20060102_150405"))
fs.StringVar(&BaseLoc, "base", osenv.Value("BASE_LOC", base), "a `location` (directory or a ZIP file) on a local disk where the files will be saved.")
fs.StringVar(&BaseLoc, "base", osenv.Value("BASE_LOC", base), "a `location` (a directory or a ZIP file) on the local disk to save\ndownloaded files to.")
}
if mask&OmitCacheDir == 0 {
fs.StringVar(&SlackOptions.CacheDir, "cache-dir", osenv.Value("CACHE_DIR", CacheDir()), "cache `directory` location")
fs.StringVar(&SlackOptions.CacheDir, "cache-dir", osenv.Value("CACHE_DIR", CacheDir()), "cache `directory` location\n")
} else {
// If the OmitCacheDir is specified, then the CacheDir will end up being
// the default value, which is "". Therefore, we need to init the
Expand Down
6 changes: 6 additions & 0 deletions cmd/slackdump/internal/convert/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ const (
CJSON // CJSON is JSON format converter
)

var Descriptions = map[Type]string{
CText: "Plain text format",
CCSV: "CSV format",
CJSON: "JSON format",
}

// Types is a list of converter types.
type Types []Type

Expand Down
25 changes: 19 additions & 6 deletions cmd/slackdump/internal/dump/dump.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
_ "embed"
"encoding/json"
"errors"
"flag"
"fmt"
"runtime/trace"
"strings"
Expand Down Expand Up @@ -50,19 +51,26 @@ var opts options
func ptr[T any](a T) *T { return &a }

func init() {
CmdDump.Run = runDump
CmdDump.Flag.Var(ptr(config.TimeValue(opts.Oldest)), "from", "timestamp of the oldest message to fetch")
CmdDump.Flag.Var(ptr(config.TimeValue(opts.Latest)), "to", "timestamp of the newest message to fetch")
CmdDump.Flag.StringVar(&opts.NameTemplate, "ft", nametmpl.Default, "output file naming template.")
CmdDump.Run = RunDump
InitDumpFlagset(&CmdDump.Flag)
}

// runDump is the main entry point for the dump command.
func runDump(ctx context.Context, cmd *base.Command, args []string) error {
func InitDumpFlagset(fs *flag.FlagSet) {
fs.Var(ptr(config.TimeValue(opts.Oldest)), "from", "timestamp of the oldest message to fetch")
fs.Var(ptr(config.TimeValue(opts.Latest)), "to", "timestamp of the newest message to fetch")
fs.StringVar(&opts.NameTemplate, "ft", nametmpl.Default, "output file naming template.\n")
}

// RunDump is the main entry point for the dump command.
func RunDump(ctx context.Context, cmd *base.Command, args []string) error {
if len(args) == 0 {
base.SetExitStatus(base.SInvalidParameters)
return ErrNothingToDo
}

lg := dlog.FromContext(ctx)

// initialize the file name template.
if opts.NameTemplate == "" {
lg.Print("File name template is empty, using the default.")
opts.NameTemplate = nametmpl.Default
Expand All @@ -73,6 +81,7 @@ func runDump(ctx context.Context, cmd *base.Command, args []string) error {
return fmt.Errorf("file template error: %w", err)
}

// initialize the list of entities to dump.
list, err := structures.NewEntityList(args)
if err != nil {
base.SetExitStatus(base.SInvalidParameters)
Expand All @@ -82,12 +91,14 @@ func runDump(ctx context.Context, cmd *base.Command, args []string) error {
return ErrNothingToDo
}

// Retrieve the Authentication provider.
prov, err := auth.FromContext(ctx)
if err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}

// Initialize the filesystem.
if fs, err := fsadapter.New(cfg.BaseLoc); err != nil {
base.SetExitStatus(base.SApplicationError)
return err
Expand All @@ -96,12 +107,14 @@ func runDump(ctx context.Context, cmd *base.Command, args []string) error {
defer fs.Close()
}

// Initialize the session.
sess, err := slackdump.NewWithOptions(ctx, prov, cfg.SlackOptions)
if err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}

// Dump the conversations.
for _, link := range list.Include {
if err := dump(ctx, sess, nameTemplate, opts, link); err != nil {
base.SetExitStatus(base.SApplicationError)
Expand Down
31 changes: 31 additions & 0 deletions cmd/slackdump/internal/list/conversation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package list

import (
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/dump"
"github.com/rusq/slackdump/v2/cmd/slackdump/internal/golang/base"
)

const codeBlock = "```"

// CmdListConversation is the command to list conversations.
var CmdListConversation = &base.Command{
UsageLine: "slackdump list convo [flags] <conversation list>",
Short: "synonym for 'slackdump dump'",
PrintFlags: true,
RequireAuth: true,
FlagMask: dump.CmdDump.FlagMask,
Long: base.Render(`
# List Conversation Command
This command does effectvely the same as the following:
` + codeBlock + `
slackdump dump
` + codeBlock + `
To get extended usage help, run ` + "`slackdump help dump`",
)}

func init() {
CmdListConversation.Run = dump.RunDump
dump.InitDumpFlagset(&CmdListConversation.Flag)
}
106 changes: 75 additions & 31 deletions cmd/slackdump/internal/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ package list

import (
"context"
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"os"
"path/filepath"

"github.com/AlecAivazis/survey/v2"
"github.com/rusq/dlog"
"github.com/rusq/slackdump/v2"
"github.com/rusq/slackdump/v2/auth"
Expand All @@ -21,24 +21,28 @@ import (
"github.com/slack-go/slack"
)

// CmdList is the list command. The logic is in the subcommands.
var CmdList = &base.Command{
UsageLine: "slackdump list",
Short: "list users or channels",
Long: `
List lists users or channels for the Slack Workspace. It may take a while on
large workspaces, as Slack limits the amount of requests on it's own discretion,
Long: base.Render(`
# List Command
List lists users or channels for the Slack Workspace. It may take a while on a
large workspace, as Slack limits the amount of requests on it's own discretion,
which is sometimes unreasonably slow.
`,
`),
Commands: []*base.Command{
CmdListUsers,
CmdListChannels,
CmdListConversation,
},
}

// common flags
var (
listType format.Type = format.CText
screenOutput bool
screenOutput bool // output to screen instead of file
)

func init() {
Expand All @@ -53,19 +57,6 @@ func addCommonFlags(fs *flag.FlagSet) {
fs.BoolVar(&screenOutput, "screen", false, "output to screen instead of file")
}

func serialise(fs fsadapter.FS, name string, a any) error {
f, err := fs.Create(name)
if err != nil {
return err
}
defer f.Close()
enc := json.NewEncoder(f)
if err := enc.Encode(a); err != nil {
return err
}
return nil
}

// listFunc is a function that lists something from the Slack API. It should
// return the object from the api, a filename to save the data to and an
// error.
Expand All @@ -78,11 +69,14 @@ func list(ctx context.Context, listFn listFunc) error {
return errors.New("unknown listing format, seek help")
}

// get the provider from Context.
prov, err := auth.FromContext(ctx)
if err != nil {
base.SetExitStatus(base.SAuthError)
return err
}

// initialize the session.
sess, err := slackdump.NewWithOptions(ctx, prov, cfg.SlackOptions)
if err != nil {
base.SetExitStatus(base.SApplicationError)
Expand All @@ -98,19 +92,30 @@ func list(ctx context.Context, listFn listFunc) error {
if screenOutput {
return fmtPrint(ctx, os.Stdout, data, listType, sess.Users)
} else {
// save to a filesystem.
fs, err := fsadapter.New(cfg.BaseLoc)
if err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}
defer fs.Close()
if err := serialise(fs, filename, data); err != nil {
return err
}
dlog.FromContext(ctx).Printf("data saved to %q\n", filepath.Join(cfg.BaseLoc, filename))
return saveData(ctx, sess, data, filename)
}
// unreachable
}

// saveData saves the given data to the given filename.
func saveData(ctx context.Context, sess *slackdump.Session, data any, filename string) error {
// save to a filesystem.
fs, err := fsadapter.New(cfg.BaseLoc)
if err != nil {
base.SetExitStatus(base.SApplicationError)
return err
}
defer fs.Close()

f, err := fs.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer f.Close()
if err := fmtPrint(ctx, f, data, listType, sess.Users); err != nil {
return err
}
dlog.FromContext(ctx).Printf("Data saved to: %q\n", filepath.Join(cfg.BaseLoc, filename))
return nil
}

Expand All @@ -133,7 +138,6 @@ func fmtPrint(ctx context.Context, w io.Writer, a any, typ format.Type, u []slac
return cvt.Channels(ctx, w, u, val)
case types.Users:
return cvt.Users(ctx, w, val)

default:
return fmt.Errorf("unsupported data type: %T", a)
}
Expand All @@ -155,3 +159,43 @@ func makeFilename(prefix string, teamID string, listType format.Type) string {
}
return fmt.Sprintf("%s-%s.%s", prefix, teamID, ext)
}

func wizard(ctx context.Context, listFn listFunc) error {
// pick format
var types []string
for _, t := range format.All() {
types = append(types, t.String())
}

q := &survey.Select{
Message: "Pick a format:",
Options: types,
Help: "Pick a format for the listing",
Description: func(value string, index int) string {
var v format.Type
v.Set(value)
return format.Descriptions[v]
},
}
var lt int
if err := survey.AskOne(q, &lt); err != nil {
return err
}
listType = format.Type(lt)
// pick output type: screen or file/directory
q = &survey.Select{
Message: "Pick an output type:",
Options: []string{"screen", "ZIP file", "directory"},
Help: "Pick an output type for the listing",
}
var ot string
if err := survey.AskOne(q, &ot); err != nil {
return err
}
if ot != "screen" {
return errors.New("not implemented yet")
}

// if file/directory, pick filename
return nil
}
8 changes: 8 additions & 0 deletions cmd/slackdump/internal/list/users.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

var CmdListUsers = &base.Command{
Run: listUsers,
Wizard: wizUsers,
UsageLine: "slackdump list users [flags] [filename]",
PrintFlags: true,
FlagMask: cfg.OmitDownloadFlag,
Expand All @@ -35,3 +36,10 @@ func listUsers(ctx context.Context, cmd *base.Command, args []string) error {
}
return nil
}

func wizUsers(ctx context.Context, cmd *base.Command, args []string) error {
return wizard(ctx, func(ctx context.Context, sess *slackdump.Session) (any, string, error) {
var filename = makeFilename("users", sess.Info().TeamID, listType)
return sess.Users, filename, nil
})
}

0 comments on commit 1384466

Please sign in to comment.