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

Arg parsing is off, fix it. Immediately. #4

Merged
merged 1 commit into from
Dec 10, 2021
Merged
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
6 changes: 6 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
OSES="windows linux darwin"

for os in $OSES; do
make slackdump-${os}.zip
done
102 changes: 0 additions & 102 deletions cmd/sdconv/sdconv.go

This file was deleted.

75 changes: 40 additions & 35 deletions cmd/slackdump/slackdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"log"
"os"
"os/signal"
"path/filepath"
"strings"

"github.com/joho/godotenv"
"github.com/rusq/slackdump"
Expand All @@ -18,6 +20,9 @@ import (
const (
outputTypeJSON = "json"
outputTypeText = "text"

slackTokenEnv = "SLACK_TOKEN"
slackCookieEnv = "COOKIE"
)

var _ = godotenv.Load()
Expand Down Expand Up @@ -60,21 +65,8 @@ func (lf listFlags) present() bool {
return lf.users || lf.channels
}

func init() {
flag.Usage = func() {
fmt.Fprintf(
flag.CommandLine.Output(),
"Slackdump dumps messages and files from slack using the provided api token.\n"+
"Will create a number of files having the channel_id as a name.\n"+
"Files are downloaded into a respective folder with channel_id\n\n"+
"Usage: %s [flags] [channel_id1 ... channel_idN]\n",
os.Args[0])
flag.PrintDefaults()
}
}

func main() {
params, err := checkParameters()
params, err := checkParameters(os.Args[1:])
if err != nil {
flag.Usage()
log.Fatal(err)
Expand Down Expand Up @@ -113,36 +105,48 @@ func createFile(filename string) (f io.WriteCloser, err error) {
return os.Create(filename)
}

func checkParameters() (params, error) {
func checkParameters(args []string) (params, error) {
fs := flag.NewFlagSet("", flag.ExitOnError)
fs.Usage = func() {
fmt.Fprintf(
flag.CommandLine.Output(),
"Slackdump dumps messages and files from slack using the provided api token.\n"+
"Will create a number of files having the channel_id as a name.\n"+
"Files are downloaded into a respective folder with channel_id name\n\n"+
"Usage: %s [flags] [channel_id1 ... channel_idN]\n\n",
filepath.Base(os.Args[0]))
fs.PrintDefaults()
}

var p params
// flags
{
flag.BoolVar(&p.list.channels, "c", false, "list channels (aka conversations) and their IDs for export.")
flag.BoolVar(&p.list.users, "u", false, "list users and their IDs. ")
flag.BoolVar(&p.dumpFiles, "f", false, "enable files download")
flag.StringVar(&p.output.filename, "o", "-", "output `filename` for users and channels. Use '-' for standard\noutput.")
flag.StringVar(&p.output.format, "r", "", "report `format`. One of 'json' or 'text'")
flag.StringVar(&p.creds.token, "t", os.Getenv("SLACK_TOKEN"), "Specify slack `API_token`, get it here:\nhttps://api.slack.com/custom-integrations/legacy-tokens\n"+
"It is also possible to define SLACK_TOKEN environment variable.")
flag.StringVar(&p.creds.cookie, "cookie", os.Getenv("COOKIE"), "d= cookie value")
flag.Parse()
fs.BoolVar(&p.list.channels, "c", false, "list channels (aka conversations) and their IDs for export.")
fs.BoolVar(&p.list.users, "u", false, "list users and their IDs. ")
fs.BoolVar(&p.dumpFiles, "f", false, "enable files download")
fs.StringVar(&p.output.filename, "o", "-", "output `filename` for users and channels. Use '-' for standard\noutput.")
fs.StringVar(&p.output.format, "r", "", "report `format`. One of 'json' or 'text'")
fs.StringVar(&p.creds.token, "t", os.Getenv(slackTokenEnv), "Specify slack `API_token`, (environment: "+slackTokenEnv+")")
fs.StringVar(&p.creds.cookie, "cookie", os.Getenv(slackCookieEnv), "d= cookie `value` (environment: "+slackCookieEnv+")")
fs.Parse(args)

os.Unsetenv("SLACK_TOKEN")
os.Unsetenv("COOKIE")
os.Unsetenv(slackTokenEnv)
os.Unsetenv(slackCookieEnv)

p.channelsToExport = flag.Args()
}
p.channelsToExport = fs.Args()

return p, p.validate()
}

func (p *params) validate() error {
if !p.creds.valid() {
return p, fmt.Errorf("slack token or cookie not specified")
return fmt.Errorf("slack token or cookie not specified")
}

if len(p.channelsToExport) == 0 && !p.list.present() {
return p, fmt.Errorf("no list flags specified and no channels to export")
return fmt.Errorf("no list flags specified and no channels to export")
}

if !p.output.validFormat() {
return p, fmt.Errorf("invalid output type: %q, must use one of %v", p.output.format, []string{outputTypeJSON, outputTypeText})
if !p.list.present() && !p.output.validFormat() {
return fmt.Errorf("invalid output type: %q, must use one of %v", p.output.format, []string{outputTypeJSON, outputTypeText})
}

// channels and users listings will be in the text format (if not specified otherwise)
Expand All @@ -153,8 +157,9 @@ func checkParameters() (params, error) {
p.output.format = outputTypeJSON
}
}
p.creds.cookie = strings.TrimPrefix(p.creds.cookie, "d=")

return p, nil
return nil
}

func listEntities(ctx context.Context, output output, creds slackCreds, list listFlags) error {
Expand Down
63 changes: 62 additions & 1 deletion cmd/slackdump/slackdump_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package main

import "testing"
import (
"testing"

"github.com/stretchr/testify/assert"
)

func Test_output_validFormat(t *testing.T) {
type fields struct {
Expand Down Expand Up @@ -29,3 +33,60 @@ func Test_output_validFormat(t *testing.T) {
})
}
}

func Test_checkParameters(t *testing.T) {
type args struct {
args []string
}
tests := []struct {
name string
args args
want params
wantErr bool
}{
{
"channels",
args{[]string{"-c", "-t", "x", "-cookie", "d"}},
params{
list: listFlags{
users: false,
channels: true,
},
creds: slackCreds{
token: "x",
cookie: "d",
},
output: output{filename: "-"},
channelsToExport: []string{},
},
false,
},
{
"users",
args{[]string{"-u", "-t", "x", "-cookie", "d"}},
params{
list: listFlags{
channels: false,
users: true,
},
creds: slackCreds{
token: "x",
cookie: "d",
},
output: output{filename: "-"},
channelsToExport: []string{},
},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := checkParameters(tt.args.args)
if (err != nil) != tt.wantErr {
t.Errorf("checkParameters() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got)
})
}
}
12 changes: 8 additions & 4 deletions messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,20 @@ import (
"github.com/slack-go/slack"
)

// Messages keeps slice of messages
// minMsgTimeApart defines the time interval in minutes to separate group
// of messages from a single user in the conversation. This increases the
// readability of the text output.
const minMsgTimeApart = 2

// Messages keeps the slice of messages.
type Messages struct {
Messages []slack.Message
ChannelID string
SD *SlackDumper
}

// ToText outputs Messages m to io.Writer w in Text format
func (m Messages) ToText(w io.Writer) (err error) {
const minMsgTimeApart = 2 //minutes
// ToText outputs Messages m to io.Writer w in text format.
func (m *Messages) ToText(w io.Writer) (err error) {
writer := bufio.NewWriter(w)
defer writer.Flush()

Expand Down
12 changes: 12 additions & 0 deletions slackdump.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package slackdump

import (
"context"
"errors"
"fmt"
"io"
"log"
Expand Down Expand Up @@ -139,6 +140,17 @@ func (sd *SlackDumper) DumpMessages(ctx context.Context, channelID string, dumpF
return &Messages{Messages: allMessages, ChannelID: channelID, SD: sd}, nil
}

// ErrNoThread is the error indicating that error is not a threaded message.
var ErrNoThread = errors.New("message has no thread")

// DumpThread retrieves all messages in the thread and returns them as a slice of messages.
func (sd *SlackDumper) DumpThread(m *slack.Message) ([]slack.Message, error) {
if m.ThreadTimestamp == "" {
return nil, ErrNoThread
}
panic("implement me")
}

// UpdateUserMap updates user[id]->*User mapping from the current Users slice.
func (sd *SlackDumper) UpdateUserMap() error {
if sd.Users.Len() == 0 {
Expand Down