├── .github ├── CODEOWNERS └── workflows │ ├── ci.yaml │ ├── lint.yaml │ ├── reviewdog.yaml │ ├── initiate_release.yml │ └── release.yml ├── assets └── logo.png ├── .gitignore ├── pkg ├── version │ └── version.go ├── cmd │ ├── chat │ │ ├── imports │ │ │ ├── validator │ │ │ │ ├── testdata │ │ │ │ │ ├── invalid-user-mutes.json │ │ │ │ │ ├── invalid-channel-mutes.json │ │ │ │ │ ├── invalid-users.json │ │ │ │ │ ├── valid-channels-with-light.json │ │ │ │ │ ├── invalid-channels.json │ │ │ │ │ ├── invalid-devices.json │ │ │ │ │ ├── invalid-members.json │ │ │ │ │ ├── invalid-messages.json │ │ │ │ │ └── valid-data.json │ │ │ │ ├── decoder.go │ │ │ │ ├── errors.go │ │ │ │ ├── validator.go │ │ │ │ └── index_test.go │ │ │ └── imports_test.go │ │ ├── push │ │ │ └── push_test.go │ │ ├── device │ │ │ ├── device_test.go │ │ │ └── device.go │ │ ├── utils │ │ │ ├── async.go │ │ │ └── fileupload.go │ │ ├── reaction │ │ │ ├── reaction_test.go │ │ │ └── reaction.go │ │ ├── app │ │ │ ├── app_test.go │ │ │ └── app.go │ │ ├── root.go │ │ ├── watch │ │ │ └── watch.go │ │ ├── channeltype │ │ │ └── channeltype_test.go │ │ ├── file │ │ │ └── file_test.go │ │ ├── events │ │ │ └── events.go │ │ └── message │ │ │ └── message_test.go │ └── root │ │ └── root.go ├── utils │ ├── param.go │ └── printer.go └── config │ ├── config.go │ └── config_test.go ├── cmd ├── stream-cli │ └── main.go └── gen-docs │ └── main.go ├── Makefile ├── docs ├── stream-cli_config_list.md ├── stream-cli_config.md ├── stream-cli_chat_unban-user.md ├── stream-cli_chat_delete-channel-type.md ├── stream-cli_chat_validate-import.md ├── stream-cli_chat_list-pushproviders.md ├── stream-cli.md ├── stream-cli_chat_get-message.md ├── stream-cli_chat_delete-file.md ├── stream-cli_chat_delete-image.md ├── stream-cli_chat_flag-user.md ├── stream-cli_chat_get-reactions.md ├── stream-cli_chat_unmute-user.md ├── stream-cli_config_remove.md ├── stream-cli_chat_add-members.md ├── stream-cli_chat_demote-moderators.md ├── stream-cli_chat_remove-members.md ├── stream-cli_chat_delete-pushprovider.md ├── stream-cli_chat_promote-moderators.md ├── stream-cli_chat_list-devices.md ├── stream-cli_chat_get-channel-type.md ├── stream-cli_chat_delete-device.md ├── stream-cli_config_new.md ├── stream-cli_chat_reactivate-user.md ├── stream-cli_chat_list-channel-types.md ├── stream-cli_config_default.md ├── stream-cli_chat_get-import.md ├── stream-cli_chat_list-imports.md ├── stream-cli_chat_assign-role.md ├── stream-cli_chat_upload-import.md ├── stream-cli_chat_upsert-user.md ├── stream-cli_chat_flag-message.md ├── stream-cli_chat_deactivate-user.md ├── stream-cli_chat_delete-reaction.md ├── stream-cli_chat_get-messages.md ├── stream-cli_chat_update-app.md ├── stream-cli_chat_delete-message.md ├── stream-cli_chat_get-channel.md ├── stream-cli_chat_update-channel-type.md ├── stream-cli_chat_revoke-token.md ├── stream-cli_chat_revoke-all-tokens.md ├── stream-cli_chat_create-channel-type.md ├── stream-cli_chat_show-channel.md ├── stream-cli_chat_watch.md ├── stream-cli_chat_mute-user.md ├── stream-cli_chat_update-user-partial.md ├── stream-cli_chat_hide-channel.md ├── stream-cli_chat_list-channels.md ├── stream-cli_chat_get-app.md ├── stream-cli_chat_update-channel.md ├── stream-cli_chat_translate-message.md ├── stream-cli_chat_create-device.md ├── stream-cli_chat_send-reaction.md ├── stream-cli_chat_update-message-partial.md ├── stream-cli_chat_delete-channel.md ├── stream-cli_chat_listen-events.md ├── stream-cli_chat_update-channel-partial.md ├── stream-cli_chat_test-push.md ├── stream-cli_chat_create-channel.md ├── stream-cli_chat_query-users.md ├── stream-cli_chat_create-token.md ├── stream-cli_chat_upload-file.md ├── stream-cli_chat_upload-image.md ├── stream-cli_chat_ban-user.md ├── stream-cli_chat_send-message.md ├── stream-cli_chat_delete-user.md ├── stream-cli_chat_upsert-pushprovider.md ├── stream-cli_chat_delete-users.md ├── README.md └── use_cases.md ├── scripts └── get_changelog_diff.js ├── .golangci.yml ├── .versionrc.js ├── .goreleaser.yaml ├── LICENSE ├── install ├── install.ps1 └── install.sh ├── stream-cli.rb ├── go.mod ├── test └── helpers.go └── README.md /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @gumuz @yaziine 2 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GetStream/stream-cli/HEAD/assets/logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .bin/ 2 | dist/ 3 | build/ 4 | node_modules/ 5 | 6 | .idea 7 | .vscode 8 | .envrc 9 | stream-cli 10 | stream 11 | latest_changes.txt 12 | # Test coverage output 13 | *.out 14 | vendor/ 15 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const ( 8 | versionMajor = 1 9 | versionMinor = 7 10 | versionPatch = 1 11 | ) 12 | 13 | func FmtVersion() string { 14 | return fmt.Sprintf("%d.%d.%d", 15 | versionMajor, 16 | versionMinor, 17 | versionPatch) 18 | } 19 | -------------------------------------------------------------------------------- /cmd/stream-cli/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/GetStream/stream-cli/pkg/cmd/root" 8 | ) 9 | 10 | func main() { 11 | if err := mainRun(); err != nil { 12 | fmt.Fprintf(os.Stderr, "%s\n", err) 13 | os.Exit(1) 14 | } 15 | } 16 | 17 | func mainRun() error { 18 | return root.NewCmd().Execute() 19 | } 20 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-user-mutes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "user1", 6 | "role": "user", 7 | "user_mutes": [ 8 | "user2", 9 | "missing_user" 10 | ] 11 | } 12 | }, 13 | { 14 | "type": "user", 15 | "item": { 16 | "id": "user2", 17 | "role": "user" 18 | } 19 | } 20 | ] 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = stream-cli 2 | 3 | GOLANGCI_VERSION = 1.55.2 4 | GOLANGCI = .bin/golangci/$(GOLANGCI_VERSION)/golangci-lint 5 | $(GOLANGCI): 6 | @curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(dir $(GOLANGCI)) v$(GOLANGCI_VERSION) 7 | 8 | lint: $(GOLANGCI) $(NAME) 9 | $(GOLANGCI) -v run ./... 10 | 11 | lint-fix: $(GOLANGCI) $(NAME) 12 | $(GOLANGCI) -v run --fix ./... 13 | 14 | $(NAME): 15 | @go install ./cmd/$(NAME) 16 | 17 | build: 18 | @go build ./cmd/$(NAME) 19 | 20 | .PHONY: test 21 | test: 22 | @go test -v ./... 23 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-channel-mutes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "user1", 6 | "role": "user", 7 | "channel_mutes": [ 8 | "messaging:123", 9 | "messaging:456" 10 | ] 11 | } 12 | }, 13 | { 14 | "type": "user", 15 | "item": { 16 | "id": "user2", 17 | "role": "user" 18 | } 19 | }, 20 | { 21 | "type": "channel", 22 | "item": { 23 | "id": "abc", 24 | "type": "messaging", 25 | "created_by": "user2" 26 | } 27 | }, 28 | { 29 | "type": "channel", 30 | "item": { 31 | "id": "123", 32 | "type": "messaging", 33 | "created_by": "user2" 34 | } 35 | } 36 | ] 37 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.head_ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | test-build: 14 | name: 👷 Test & Build 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-go@v4 19 | with: 20 | go-version-file: 'go.mod' 21 | 22 | - name: Test 23 | env: 24 | STREAM_KEY: ${{ secrets.STREAM_KEY }} 25 | STREAM_SECRET: ${{ secrets.STREAM_SECRET }} 26 | run: | 27 | go test -coverprofile cover.out -v -race ./... 28 | go tool cover -func=cover.out 29 | -------------------------------------------------------------------------------- /pkg/cmd/chat/push/push_test.go: -------------------------------------------------------------------------------- 1 | package push 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/GetStream/stream-cli/test" 10 | ) 11 | 12 | func TestPushTest(t *testing.T) { 13 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 14 | ch := test.InitChannel(t) 15 | u := test.CreateUser() 16 | msgID := test.CreateMessage(ch, u) 17 | t.Cleanup(func() { 18 | test.DeleteMessage(msgID) 19 | test.DeleteChannel(ch) 20 | test.DeleteUser(u) 21 | }) 22 | 23 | cmd.SetArgs([]string{"test-push", "--message-id", msgID, "--user-id", u, "--skip-devices", "true"}) 24 | _, err := cmd.ExecuteC() 25 | require.NoError(t, err) 26 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), msgID) 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: [pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.head_ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | lint: 11 | name: 👮 Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout 15 | uses: actions/checkout@v4 16 | 17 | - name: Commit message linter 18 | uses: wagoid/commitlint-github-action@v5 19 | 20 | - uses: actions/setup-go@v4 21 | with: 22 | go-version-file: 'go.mod' 23 | 24 | - name: Tidy 25 | run: go mod tidy -v && git diff --no-patch --exit-code || { git status; echo 'Unchecked diff, did you forget go mod tidy again?' ; false ; }; 26 | 27 | - name: Linters 28 | run: make lint 29 | 30 | -------------------------------------------------------------------------------- /docs/stream-cli_config_list.md: -------------------------------------------------------------------------------- 1 | ## stream-cli config list 2 | 3 | List all applications 4 | 5 | ### Synopsis 6 | 7 | List all applications which are configured in the configuration file 8 | 9 | ``` 10 | stream-cli config list [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | # List all applications 17 | $ stream-cli config list 18 | 19 | ``` 20 | 21 | ### Options 22 | 23 | ``` 24 | -h, --help help for list 25 | ``` 26 | 27 | ### Options inherited from parent commands 28 | 29 | ``` 30 | --app string [optional] Application name to use as it's defined in the configuration file 31 | --config string [optional] Explicit config file path 32 | ``` 33 | 34 | ### SEE ALSO 35 | 36 | * [stream-cli config](stream-cli_config.md) - Manage app configurations 37 | 38 | -------------------------------------------------------------------------------- /scripts/get_changelog_diff.js: -------------------------------------------------------------------------------- 1 | /* 2 | Here we're trying to parse the latest changes from CHANGELOG.md file. 3 | The changelog looks like this: 4 | 5 | ## 0.0.3 6 | - Something #3 7 | ## 0.0.2 8 | - Something #2 9 | ## 0.0.1 10 | - Something #1 11 | 12 | In this case we're trying to extract "- Something #3" since that's the latest change. 13 | */ 14 | module.exports = () => { 15 | const fs = require('fs') 16 | 17 | changelog = fs.readFileSync('CHANGELOG.md', 'utf8') 18 | releases = changelog.match(/## [?[0-9](.+)/g) 19 | 20 | current_release = changelog.indexOf(releases[0]) 21 | previous_release = changelog.indexOf(releases[1]) 22 | 23 | latest_changes = changelog.substr(current_release, previous_release - current_release) 24 | 25 | return latest_changes 26 | } 27 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | go: '1.19' 3 | deadline: 210s 4 | timeout: 10m 5 | skip-dirs: 6 | - mocks 7 | - '.*_mock' 8 | skip-files: 9 | - '.*_mock.go' 10 | - ".*\\.pb\\.go$" 11 | 12 | linters-settings: 13 | dupl: 14 | threshold: 250 15 | goconst: 16 | min-len: 5 17 | min-occurrences: 5 18 | gocritic: 19 | disabled-checks: 20 | - ifElseChain 21 | enabled-tags: 22 | - diagnostic 23 | - experimental 24 | - opinionated 25 | - performance 26 | - style 27 | settings: 28 | hugeParam: 29 | sizeThreshold: 364 30 | rangeValCopy: 31 | sizeThreshold: 364 32 | skipTestFuncs: true 33 | gofumpt: 34 | simplify: true 35 | goimports: 36 | local-prefixes: github.com/GetStream/stream-cli 37 | -------------------------------------------------------------------------------- /docs/stream-cli_config.md: -------------------------------------------------------------------------------- 1 | ## stream-cli config 2 | 3 | Manage app configurations 4 | 5 | ### Options 6 | 7 | ``` 8 | -h, --help help for config 9 | ``` 10 | 11 | ### Options inherited from parent commands 12 | 13 | ``` 14 | --app string [optional] Application name to use as it's defined in the configuration file 15 | --config string [optional] Explicit config file path 16 | ``` 17 | 18 | ### SEE ALSO 19 | 20 | * [stream-cli](stream-cli.md) - Stream CLI 21 | * [stream-cli config default](stream-cli_config_default.md) - Set an application as the default 22 | * [stream-cli config list](stream-cli_config_list.md) - List all applications 23 | * [stream-cli config new](stream-cli_config_new.md) - Add a new application 24 | * [stream-cli config remove](stream-cli_config_remove.md) - Remove one or more application. 25 | 26 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_unban-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat unban-user 2 | 3 | Unban a user 4 | 5 | ``` 6 | stream-cli chat unban-user --target-user-id [user-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Unban user 'joe' 13 | $ stream-cli chat unban-user --target-user-id joe 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for unban-user 21 | -t, --target-user-id string [required] ID of the user to unban 22 | ``` 23 | 24 | ### Options inherited from parent commands 25 | 26 | ``` 27 | --app string [optional] Application name to use as it's defined in the configuration file 28 | --config string [optional] Explicit config file path 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | 35 | -------------------------------------------------------------------------------- /cmd/gen-docs/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/spf13/cobra/doc" 8 | "github.com/spf13/pflag" 9 | 10 | "github.com/GetStream/stream-cli/pkg/cmd/root" 11 | ) 12 | 13 | func main() { 14 | if err := run(os.Args); err != nil { 15 | fmt.Fprintln(os.Stderr, err) 16 | os.Exit(1) 17 | } 18 | } 19 | 20 | func run(args []string) error { 21 | fl := pflag.FlagSet{} 22 | dir := fl.StringP("output", "o", "./docs", "Path directory where you want generate doc files") 23 | 24 | if err := fl.Parse(args); err != nil { 25 | return err 26 | } 27 | 28 | if _, err := os.Stat(*dir); os.IsNotExist(err) { 29 | if err := os.MkdirAll(*dir, 0o755); err != nil { 30 | return err 31 | } 32 | } 33 | 34 | rootCmd := root.NewCmd() 35 | rootCmd.DisableAutoGenTag = true 36 | return doc.GenMarkdownTree(rootCmd, *dir) 37 | } 38 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-channel-type.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-channel-type 2 | 3 | Delete channel type 4 | 5 | ### Synopsis 6 | 7 | This command deletes a channel type. 8 | 9 | 10 | ``` 11 | stream-cli chat delete-channel-type [channel-type] [flags] 12 | ``` 13 | 14 | ### Examples 15 | 16 | ``` 17 | # Delete a channel type called my-ch-type 18 | $ stream-cli chat delete-channel-type my-ch-type 19 | 20 | ``` 21 | 22 | ### Options 23 | 24 | ``` 25 | -h, --help help for delete-channel-type 26 | ``` 27 | 28 | ### Options inherited from parent commands 29 | 30 | ``` 31 | --app string [optional] Application name to use as it's defined in the configuration file 32 | --config string [optional] Explicit config file path 33 | ``` 34 | 35 | ### SEE ALSO 36 | 37 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 38 | 39 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_validate-import.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat validate-import 2 | 3 | Validate import file 4 | 5 | ``` 6 | stream-cli chat validate-import [filename] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Validates a JSON import file 13 | $ stream-cli chat validate-import data.json 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for validate-import 21 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 22 | ``` 23 | 24 | ### Options inherited from parent commands 25 | 26 | ``` 27 | --app string [optional] Application name to use as it's defined in the configuration file 28 | --config string [optional] Explicit config file path 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | 35 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_list-pushproviders.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat list-pushproviders 2 | 3 | List all push providers 4 | 5 | ``` 6 | stream-cli chat list-pushproviders --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # List all push providers 13 | $ stream-cli chat list-pushproviders 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for list-pushproviders 21 | -o, --output-format string Output format. One of: json|tree (default "json") 22 | ``` 23 | 24 | ### Options inherited from parent commands 25 | 26 | ``` 27 | --app string [optional] Application name to use as it's defined in the configuration file 28 | --config string [optional] Explicit config file path 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | 35 | -------------------------------------------------------------------------------- /docs/stream-cli.md: -------------------------------------------------------------------------------- 1 | ## stream-cli 2 | 3 | Stream CLI 4 | 5 | ### Synopsis 6 | 7 | Interact with your Stream applications easily 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Get Chat application settings 13 | $ stream-cli chat get-app 14 | 15 | # List all Chat channel types 16 | $ stream-cli chat list-channel-types 17 | 18 | # Create a new Chat user 19 | $ stream-cli chat upsert-user --properties "{\"id\":\"my-user-1\"}" 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | --app string [optional] Application name to use as it's defined in the configuration file 27 | --config string [optional] Explicit config file path 28 | -h, --help help for stream-cli 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | * [stream-cli config](stream-cli_config.md) - Manage app configurations 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-message.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-message 2 | 3 | Return a single message 4 | 5 | ``` 6 | stream-cli chat get-message [message-id] --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Returns a message with id 'msgid-1' 13 | $ stream-cli chat get-message msgid-1 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for get-message 21 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 22 | ``` 23 | 24 | ### Options inherited from parent commands 25 | 26 | ``` 27 | --app string [optional] Application name to use as it's defined in the configuration file 28 | --config string [optional] Explicit config file path 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | 35 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-file.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-file 2 | 3 | Delete a file 4 | 5 | ``` 6 | stream-cli chat delete-file --channel-type [channel-type] --channel-id [channel-id] --file-url [file-url] [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -i, --channel-id string [required] Channel id to interact with 13 | -t, --channel-type string [required] Channel type to interact with 14 | -u, --file-url string [required] URL of the file to delete 15 | -h, --help help for delete-file 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --app string [optional] Application name to use as it's defined in the configuration file 22 | --config string [optional] Explicit config file path 23 | ``` 24 | 25 | ### SEE ALSO 26 | 27 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 28 | 29 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-image.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-image 2 | 3 | Delete an image 4 | 5 | ``` 6 | stream-cli chat delete-image --channel-type [channel-type] --channel-id [channel-id] --image-url [image-url] [flags] 7 | ``` 8 | 9 | ### Options 10 | 11 | ``` 12 | -i, --channel-id string [required] Channel id to interact with 13 | -t, --channel-type string [required] Channel type to interact with 14 | -h, --help help for delete-image 15 | -u, --image-url string [required] URL of the image to delete 16 | ``` 17 | 18 | ### Options inherited from parent commands 19 | 20 | ``` 21 | --app string [optional] Application name to use as it's defined in the configuration file 22 | --config string [optional] Explicit config file path 23 | ``` 24 | 25 | ### SEE ALSO 26 | 27 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 28 | 29 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_flag-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat flag-user 2 | 3 | Flag a user 4 | 5 | ``` 6 | stream-cli chat flag-user --user-id [user-id] --flagged-by-id [user-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Flag the user 'joe' 13 | $ stream-cli chat flag-user --user-id joe --flagged-by-id admin 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -b, --flagged-by-id string [required] ID of the user who flagged the user 21 | -h, --help help for flag-user 22 | -u, --user-id string [required] ID of the user to flag 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-reactions.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-reactions 2 | 3 | Get reactions for a message 4 | 5 | ``` 6 | stream-cli chat get-reactions [message-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Get reactions for a [08f64828-3bba-42bd-8430-c26a3634ee5c] message 13 | $ stream-cli chat get-reactions 08f64828-3bba-42bd-8430-c26a3634ee5c --output-format json 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for get-reactions 21 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 22 | ``` 23 | 24 | ### Options inherited from parent commands 25 | 26 | ``` 27 | --app string [optional] Application name to use as it's defined in the configuration file 28 | --config string [optional] Explicit config file path 29 | ``` 30 | 31 | ### SEE ALSO 32 | 33 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 34 | 35 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_unmute-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat unmute-user 2 | 3 | Unmute a user 4 | 5 | ``` 6 | stream-cli chat unmute-user --target-user-id [user-id] --unmuted-by-id [user-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Unmute the user 'joe' 13 | $ stream-cli chat unmute-user --target-user-id joe --unmuted-by-id admin 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for unmute-user 21 | -t, --target-user-id string [required] ID of the user to unmute 22 | -b, --unmuted-by-id string [required] ID of the user who unmuted the user 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_config_remove.md: -------------------------------------------------------------------------------- 1 | ## stream-cli config remove 2 | 3 | Remove one or more application. 4 | 5 | ### Synopsis 6 | 7 | Remove one or more application from the configuration file. This operation is irrevocable. 8 | 9 | ``` 10 | stream-cli config remove [app-name-1] [app-name-2] [app-name-n] [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | # Remove a single application from the CLI 17 | $ stream-cli config remove staging 18 | 19 | # Remove multiple applications from the CLI 20 | $ stream-cli config remove staging testing 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -h, --help help for remove 28 | ``` 29 | 30 | ### Options inherited from parent commands 31 | 32 | ``` 33 | --app string [optional] Application name to use as it's defined in the configuration file 34 | --config string [optional] Explicit config file path 35 | ``` 36 | 37 | ### SEE ALSO 38 | 39 | * [stream-cli config](stream-cli_config.md) - Manage app configurations 40 | 41 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_add-members.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat add-members 2 | 3 | Add members to a channel 4 | 5 | ``` 6 | stream-cli chat add-members --type [channel-type] --id [channel-id] [user-id-1] [user-id-2] ... [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Add members joe, jill and jane to 'red-team' channel 13 | $ stream-cli chat add-members --type messaging --id red-team joe jill jane 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for add-members 21 | -i, --id string [required] Channel id 22 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /pkg/cmd/chat/device/device_test.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/GetStream/stream-cli/test" 10 | ) 11 | 12 | func TestDevice(t *testing.T) { 13 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 14 | userID := test.CreateUser() 15 | deviceID := test.RandomString(10) 16 | t.Cleanup(func() { 17 | test.DeleteUser(userID) 18 | }) 19 | 20 | cmd.SetArgs([]string{"create-device", "-i", deviceID, "-p", "apn", "-u", userID}) 21 | _, err := cmd.ExecuteC() 22 | require.NoError(t, err) 23 | 24 | cmd.SetArgs([]string{"list-devices", "-u", userID}) 25 | _, err = cmd.ExecuteC() 26 | require.NoError(t, err) 27 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), deviceID) 28 | 29 | cmd.SetArgs([]string{"delete-device", "-i", deviceID, "-u", userID}) 30 | _, err = cmd.ExecuteC() 31 | require.NoError(t, err) 32 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully deleted device") 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yaml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | on: 3 | pull_request: 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.head_ref }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | reviewdog: 11 | name: 🐶 Reviewdog 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - uses: reviewdog/action-setup@v1 17 | with: 18 | reviewdog_version: latest 19 | 20 | - uses: actions/setup-go@v4 21 | with: 22 | go-version-file: 'go.mod' 23 | 24 | - name: Install golangci-lint 25 | run: 26 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2 27 | 28 | - name: Reviewdog 29 | env: 30 | REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | run: 32 | $(go env GOPATH)/bin/golangci-lint run --out-format line-number | reviewdog -f=golangci-lint -name=golangci-lint -reporter=github-pr-review 33 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_demote-moderators.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat demote-moderators 2 | 3 | Demote users from moderator role 4 | 5 | ``` 6 | stream-cli chat demote-moderators --type [channel-type] --id [channel-id] [user-id-1] [user-id-2] ... [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Demote 4 users from moderator role 13 | $ stream-cli chat demote-moderators --type messaging --id red-team joe mike jane jill 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for demote-moderators 21 | -i, --id string [required] Channel id 22 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_remove-members.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat remove-members 2 | 3 | Remove members from a channel 4 | 5 | ``` 6 | stream-cli chat remove-members --type [channel-type] --id [channel-id] [user-id-1] [user-id-2] ... [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Remove members joe, jill and jane from 'red-team' channel 13 | $ stream-cli chat remove-members --type messaging --id red-team joe jill jane 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for remove-members 21 | -i, --id string [required] Channel id 22 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-pushprovider.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-pushprovider 2 | 3 | Delete a push provider 4 | 5 | ``` 6 | stream-cli chat delete-pushprovider --push-provider-type [type] --push-provider-name [name] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Delete an APN push provider 13 | $ stream-cli chat delete-pushprovider --push-provider-type apn --push-provider-name staging 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for delete-pushprovider 21 | -n, --push-provider-name string [required] Push provider name 22 | -t, --push-provider-type string [required] Push provider type 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_promote-moderators.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat promote-moderators 2 | 3 | Promote users to channel moderator role 4 | 5 | ``` 6 | stream-cli chat promote-moderators --type [channel-type] --id [channel-id] [user-id-1] [user-id-2] ... [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Promote 4 users to moderator 13 | $ stream-cli chat promote-moderators --type messaging --id red-team joe mike jane jill 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for promote-moderators 21 | -i, --id string [required] Channel id 22 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 23 | ``` 24 | 25 | ### Options inherited from parent commands 26 | 27 | ``` 28 | --app string [optional] Application name to use as it's defined in the configuration file 29 | --config string [optional] Explicit config file path 30 | ``` 31 | 32 | ### SEE ALSO 33 | 34 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 35 | 36 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_list-devices.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat list-devices 2 | 3 | List devices 4 | 5 | ### Synopsis 6 | 7 | Provides a list of all devices associated with a user. 8 | 9 | ``` 10 | stream-cli chat list-devices --user-id [user-id] --output-format [json|tree] [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | # List devices for a user 17 | $ stream-cli chat list-devices --user-id "my-user-id" 18 | 19 | ``` 20 | 21 | ### Options 22 | 23 | ``` 24 | -h, --help help for list-devices 25 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 26 | -u, --user-id string [required] User ID 27 | ``` 28 | 29 | ### Options inherited from parent commands 30 | 31 | ``` 32 | --app string [optional] Application name to use as it's defined in the configuration file 33 | --config string [optional] Explicit config file path 34 | ``` 35 | 36 | ### SEE ALSO 37 | 38 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 39 | 40 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-users.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "role": "user" 6 | } 7 | }, 8 | { 9 | "type": "user", 10 | "item": { 11 | "id": "id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long", 12 | "role": "user" 13 | } 14 | }, 15 | { 16 | "type": "user", 17 | "item": { 18 | "id": "invalid|user id", 19 | "role": "user" 20 | } 21 | }, 22 | { 23 | "type": "user", 24 | "item": { 25 | "id": "userA", 26 | "role": "user", 27 | "online": true 28 | } 29 | }, 30 | { 31 | "type": "user", 32 | "item": { 33 | "id": "userA", 34 | "role": "admin" 35 | } 36 | }, 37 | { 38 | "type": "user", 39 | "item": { 40 | "id": "userA", 41 | "role": "user" 42 | } 43 | } 44 | ] -------------------------------------------------------------------------------- /pkg/cmd/chat/utils/async.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "time" 7 | 8 | stream "github.com/GetStream/stream-chat-go/v5" 9 | "github.com/spf13/cobra" 10 | ) 11 | 12 | func WaitForAsyncCompletion(cmd *cobra.Command, c *stream.Client, taskID string, timeoutSeconds int) error { 13 | cmd.Println(fmt.Sprintf("Task id: %s\n", taskID)) 14 | cmd.Println("Waiting for async task to complete...⏳") 15 | 16 | for i := 0; i < timeoutSeconds; i++ { 17 | resp, err := c.GetTask(cmd.Context(), taskID) 18 | if err != nil { 19 | return err 20 | } 21 | 22 | if resp.Status == stream.TaskStatusCompleted { 23 | cmd.Println("Async operation completed successfully") 24 | return nil 25 | } 26 | if resp.Status == stream.TaskStatusFailed { 27 | return errors.New("async operation failed") 28 | } 29 | 30 | if i%5 == 0 { 31 | cmd.Print("Still loading... ⏳") 32 | } 33 | 34 | time.Sleep(time.Second) 35 | } 36 | 37 | cmd.PrintErrf("Async operation timed out after [%d] seconds", timeoutSeconds) 38 | 39 | return nil 40 | } 41 | -------------------------------------------------------------------------------- /.versionrc.js: -------------------------------------------------------------------------------- 1 | const versionFileUpdater = { 2 | MAJOR_REGEX: /versionMajor = (\d+)/, 3 | MINOR_REGEX: /versionMinor = (\d+)/, 4 | PATCH_REGEX: /versionPatch = (\d+)/, 5 | 6 | readVersion: function (contents) { 7 | const major = this.MAJOR_REGEX.exec(contents)[1]; 8 | const minor = this.MINOR_REGEX.exec(contents)[1]; 9 | const patch = this.PATCH_REGEX.exec(contents)[1]; 10 | 11 | return `${major}.${minor}.${patch}`; 12 | }, 13 | 14 | writeVersion: function (contents, version) { 15 | const splitted = version.split('.'); 16 | const [major, minor, patch] = [splitted[0], splitted[1], splitted[2]]; 17 | 18 | return contents 19 | .replace(this.MAJOR_REGEX, `versionMajor = ${major}`) 20 | .replace(this.MINOR_REGEX, `versionMinor = ${minor}`) 21 | .replace(this.PATCH_REGEX, `versionPatch = ${patch}`); 22 | } 23 | } 24 | 25 | module.exports = { 26 | bumpFiles: [ 27 | { filename: './pkg/version/version.go', updater: versionFileUpdater }, 28 | ], 29 | } 30 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-channel-type.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-channel-type 2 | 3 | Get channel type 4 | 5 | ``` 6 | stream-cli chat get-channel-type [channel-type] --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Returns a channel type and prints it as JSON 13 | $ stream-cli chat get-channel-type livestream 14 | 15 | # Returns a channel type and prints it as a browsable tree 16 | $ stream-cli chat get-channel-type messaging --output-format tree 17 | 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | -h, --help help for get-channel-type 24 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 25 | ``` 26 | 27 | ### Options inherited from parent commands 28 | 29 | ``` 30 | --app string [optional] Application name to use as it's defined in the configuration file 31 | --config string [optional] Explicit config file path 32 | ``` 33 | 34 | ### SEE ALSO 35 | 36 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 37 | 38 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-device.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-device 2 | 3 | Delete a device 4 | 5 | ### Synopsis 6 | 7 | Unregistering a device removes the device from the user 8 | and stops further new message notifications. 9 | 10 | 11 | ``` 12 | stream-cli chat delete-device --id [device-id] --user-id [user-id] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Delete "my-device-id" device 19 | $ stream-cli chat delete-device --id "my-device-id" --user-id "my-user-id" 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for delete-device 27 | -i, --id string [required] Device ID to delete 28 | -u, --user-id string [required] ID of the user who deletes the device 29 | ``` 30 | 31 | ### Options inherited from parent commands 32 | 33 | ``` 34 | --app string [optional] Application name to use as it's defined in the configuration file 35 | --config string [optional] Explicit config file path 36 | ``` 37 | 38 | ### SEE ALSO 39 | 40 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 41 | 42 | -------------------------------------------------------------------------------- /docs/stream-cli_config_new.md: -------------------------------------------------------------------------------- 1 | ## stream-cli config new 2 | 3 | Add a new application 4 | 5 | ### Synopsis 6 | 7 | Add a new application which can be used for further operations 8 | 9 | ``` 10 | stream-cli config new [flags] 11 | ``` 12 | 13 | ### Examples 14 | 15 | ``` 16 | # Add a new application to the CLI 17 | $ stream-cli config new 18 | ? What is the name of your app? (eg. prod, staging, testing) testing 19 | ? What is your access key? abcd1234efgh456 20 | ? What is your access secret key? *********************************** 21 | ? (optional) Which base URL do you want to use for Chat? https://chat.stream-io-api.com 22 | 23 | Application successfully added. 🚀 24 | 25 | ``` 26 | 27 | ### Options 28 | 29 | ``` 30 | -h, --help help for new 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli config](stream-cli_config.md) - Manage app configurations 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_reactivate-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat reactivate-user 2 | 3 | Reactivate a user 4 | 5 | ### Synopsis 6 | 7 | Deactivated users cannot connect to Stream Chat or send/receive messages. 8 | This function reactivates a user. 9 | 10 | 11 | ``` 12 | stream-cli chat reactivate-user --user-id [user-id] --restore-messages [true|false] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Reactivate the user 'joe' 19 | $ stream-cli chat reactivate-user --user-id joe --restore-messages true 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for reactivate-user 27 | --restore-messages [optional] Restore messages for the user 28 | -u, --user-id string [required] ID of the user to reactivate 29 | ``` 30 | 31 | ### Options inherited from parent commands 32 | 33 | ``` 34 | --app string [optional] Application name to use as it's defined in the configuration file 35 | --config string [optional] Explicit config file path 36 | ``` 37 | 38 | ### SEE ALSO 39 | 40 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 41 | 42 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_list-channel-types.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat list-channel-types 2 | 3 | List channel types 4 | 5 | ### Synopsis 6 | 7 | This command lists all channel types, including built-in and custom ones. 8 | 9 | 10 | ``` 11 | stream-cli chat list-channel-types --output-format [json|tree] [flags] 12 | ``` 13 | 14 | ### Examples 15 | 16 | ``` 17 | # List all channel types as json (default) 18 | $ stream-cli chat list-channel-types 19 | 20 | # List all channel types as browsable tree 21 | $ stream-cli chat list-channel-types --output-format tree 22 | 23 | ``` 24 | 25 | ### Options 26 | 27 | ``` 28 | -h, --help help for list-channel-types 29 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 30 | ``` 31 | 32 | ### Options inherited from parent commands 33 | 34 | ``` 35 | --app string [optional] Application name to use as it's defined in the configuration file 36 | --config string [optional] Explicit config file path 37 | ``` 38 | 39 | ### SEE ALSO 40 | 41 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 42 | 43 | -------------------------------------------------------------------------------- /.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | before: 2 | hooks: 3 | - go mod tidy 4 | builds: 5 | - main: ./cmd/stream-cli/ 6 | binary: stream-cli 7 | env: 8 | - CGO_ENABLED=0 9 | goos: 10 | - linux 11 | - windows 12 | - darwin 13 | 14 | checksum: 15 | name_template: 'checksums.txt' 16 | 17 | snapshot: 18 | name_template: '{{ incpatch .Version }}-next' 19 | 20 | archives: 21 | - name_template: >- 22 | {{ .ProjectName }}_ 23 | {{- title .Os }}_ 24 | {{- if eq .Arch "amd64" }}x86_64 25 | {{- else if eq .Arch "386" }}i386 26 | {{- else }}{{ .Arch }}{{ end }} 27 | format_overrides: 28 | - goos: windows 29 | format: zip 30 | # By default goreleaser wants to add CHANGELOG, README and LICENSE to the zip package which is annoying. 31 | # This is a workaround to not include them. https://github.com/goreleaser/goreleaser/issues/602 32 | files: 33 | - none* 34 | 35 | brews: 36 | - name: stream-cli 37 | homepage: https://github.com/GetStream/stream-cli 38 | description: Manage your Stream applications easily. 39 | tap: 40 | owner: GetStream 41 | name: stream-cli 42 | -------------------------------------------------------------------------------- /docs/stream-cli_config_default.md: -------------------------------------------------------------------------------- 1 | ## stream-cli config default 2 | 3 | Set an application as the default 4 | 5 | ### Synopsis 6 | 7 | Set an application as the default which will be used 8 | for all further operations unless specified otherwise. 9 | 10 | 11 | ``` 12 | stream-cli config default [app-name] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Set an application as the default 19 | $ stream-cli config default staging 20 | 21 | # All underlying operations will use it if not specified otherwise 22 | $ stream-cli chat get-app 23 | # Prints the settings of staging app 24 | 25 | # Specifying other apps during an operation 26 | $ stream-cli chat get-app --app prod 27 | # Prints the settings of prod app 28 | 29 | ``` 30 | 31 | ### Options 32 | 33 | ``` 34 | -h, --help help for default 35 | ``` 36 | 37 | ### Options inherited from parent commands 38 | 39 | ``` 40 | --app string [optional] Application name to use as it's defined in the configuration file 41 | --config string [optional] Explicit config file path 42 | ``` 43 | 44 | ### SEE ALSO 45 | 46 | * [stream-cli config](stream-cli_config.md) - Manage app configurations 47 | 48 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-import.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-import 2 | 3 | Get import 4 | 5 | ``` 6 | stream-cli chat get-import [import-id] --output-format [json|tree] --watch [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Returns an import and prints it as JSON 13 | $ stream-cli chat get-import dcb6e366-93ec-4e52-af6f-b0c030ad5272 14 | 15 | # Returns an import and prints it as JSON, and wait for it to complete 16 | $ stream-cli chat get-import dcb6e366-93ec-4e52-af6f-b0c030ad5272 --watch 17 | 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | -h, --help help for get-import 24 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 25 | -w, --watch [optional] Keep polling the import to track its status 26 | ``` 27 | 28 | ### Options inherited from parent commands 29 | 30 | ``` 31 | --app string [optional] Application name to use as it's defined in the configuration file 32 | --config string [optional] Explicit config file path 33 | ``` 34 | 35 | ### SEE ALSO 36 | 37 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 38 | 39 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_list-imports.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat list-imports 2 | 3 | List imports 4 | 5 | ``` 6 | stream-cli chat list-imports --offset [int] --limit [int] --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # List all imports as json (default) 13 | $ stream-cli chat list-imports 14 | 15 | # List all imports as browsable tree 16 | $ stream-cli chat list-imports --output-format tree 17 | 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | -h, --help help for list-imports 24 | -l, --limit int [optional] The number of imports returned (default 10) 25 | -O, --offset int [optional] The starting offset of imports returned 26 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 27 | ``` 28 | 29 | ### Options inherited from parent commands 30 | 31 | ``` 32 | --app string [optional] Application name to use as it's defined in the configuration file 33 | --config string [optional] Explicit config file path 34 | ``` 35 | 36 | ### SEE ALSO 37 | 38 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 39 | 40 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_assign-role.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat assign-role 2 | 3 | Assign a role to a user 4 | 5 | ``` 6 | stream-cli chat assign-role --type [channel-type] --id [channel-id] --user-id [user-id] --role [channel-role-name] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Assign 'channel_moderator' role to user 'joe' 13 | $ stream-cli chat assign-role --type messaging --id red-team --user-id joe --role channel_moderator 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for assign-role 21 | -i, --id string [required] Channel id 22 | -r, --role string [required] Channel role name to assign 23 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 24 | -u, --user-id string [required] User id to assign a role to 25 | ``` 26 | 27 | ### Options inherited from parent commands 28 | 29 | ``` 30 | --app string [optional] Application name to use as it's defined in the configuration file 31 | --config string [optional] Explicit config file path 32 | ``` 33 | 34 | ### SEE ALSO 35 | 36 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 37 | 38 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_upload-import.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat upload-import 2 | 3 | Upload an import 4 | 5 | ``` 6 | stream-cli chat upload-import [filename] --mode [upsert|insert] --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Uploads an import and prints it as JSON 13 | $ stream-cli chat upload-import data.json --mode insert 14 | 15 | # Uploads an import and prints it as a browsable tree 16 | $ stream-cli chat upload-import data.json --mode insert --output-format tree 17 | 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | -h, --help help for upload-import 24 | -m, --mode string [optional] Import mode. Canbe upsert or insert (default "upsert") 25 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 26 | ``` 27 | 28 | ### Options inherited from parent commands 29 | 30 | ``` 31 | --app string [optional] Application name to use as it's defined in the configuration file 32 | --config string [optional] Explicit config file path 33 | ``` 34 | 35 | ### SEE ALSO 36 | 37 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 38 | 39 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_upsert-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat upsert-user 2 | 3 | Upsert a user 4 | 5 | ### Synopsis 6 | 7 | This command inserts a new or updates an existing user. 8 | Stream Users require only an id to be created. 9 | Any user present in the payload will have its data replaced with the new version. 10 | 11 | 12 | ``` 13 | stream-cli chat upsert-user --properties [raw-json] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Create a new user with id 'my-user-1' 20 | $ stream-cli chat upsert-user --properties "{\"id\":\"my-user-1\"}" 21 | 22 | Check the Go SDK's 'User' struct for the properties that you can use here. 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -h, --help help for upsert-user 30 | -p, --properties string [required] Raw JSON properties of the user 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_flag-message.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat flag-message 2 | 3 | Flag a message 4 | 5 | ### Synopsis 6 | 7 | Any user is allowed to flag a message. This triggers the message.flagged webhook event 8 | and adds the message to the inbox of your Stream Dashboard Chat Moderation view. 9 | 10 | 11 | ``` 12 | stream-cli chat flag-message --message-id [message-id] --user-id [user-id] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Flags a message with id 'msgid-1' by 'userid-1' 19 | $ stream-cli chat flag-message --message-id msgid-1 --user-id userid-1 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for flag-message 27 | -m, --message-id string [required] Message id to flag 28 | -u, --user-id string [required] ID of the user who flagged the message 29 | ``` 30 | 31 | ### Options inherited from parent commands 32 | 33 | ``` 34 | --app string [optional] Application name to use as it's defined in the configuration file 35 | --config string [optional] Explicit config file path 36 | ``` 37 | 38 | ### SEE ALSO 39 | 40 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 41 | 42 | -------------------------------------------------------------------------------- /pkg/cmd/chat/reaction/reaction_test.go: -------------------------------------------------------------------------------- 1 | package reaction 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/GetStream/stream-cli/test" 10 | ) 11 | 12 | func TestReactions(t *testing.T) { 13 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 14 | ch := test.InitChannel(t) 15 | u := test.CreateUser() 16 | msgID := test.CreateMessage(ch, u) 17 | t.Cleanup(func() { 18 | test.DeleteMessage(msgID) 19 | test.DeleteChannel(ch) 20 | test.DeleteUser(u) 21 | }) 22 | 23 | cmd.SetArgs([]string{"send-reaction", "-m", msgID, "-u", u, "-r", "like"}) 24 | _, err := cmd.ExecuteC() 25 | require.NoError(t, err) 26 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully sent reaction") 27 | 28 | cmd.SetArgs([]string{"get-reactions", msgID}) 29 | _, err = cmd.ExecuteC() 30 | require.NoError(t, err) 31 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "like") 32 | 33 | cmd.SetArgs([]string{"delete-reaction", "-m", msgID, "-r", "like", "-u", u}) 34 | _, err = cmd.ExecuteC() 35 | require.NoError(t, err) 36 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully deleted reaction") 37 | } 38 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_deactivate-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat deactivate-user 2 | 3 | Deactivate a user 4 | 5 | ### Synopsis 6 | 7 | Deactivated users cannot connect to Stream Chat or send/receive messages. 8 | Deactivated users can be re-activated with the 'reactivate-user' command. 9 | 10 | 11 | ``` 12 | stream-cli chat deactivate-user --user-id [user-id] --mark-messages-deleted [true|false] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Deactivate the user 'joe' 19 | $ stream-cli chat deactivate-user --user-id joe --mark-messages-deleted true 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for deactivate-user 27 | --mark-messages-deleted [optional] Mark all messages from the user as deleted 28 | -u, --user-id string [required] ID of the user to deactivate 29 | ``` 30 | 31 | ### Options inherited from parent commands 32 | 33 | ``` 34 | --app string [optional] Application name to use as it's defined in the configuration file 35 | --config string [optional] Explicit config file path 36 | ``` 37 | 38 | ### SEE ALSO 39 | 40 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 41 | 42 | -------------------------------------------------------------------------------- /pkg/utils/param.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "strings" 6 | 7 | stream "github.com/GetStream/stream-chat-go/v5" 8 | "github.com/spf13/pflag" 9 | ) 10 | 11 | func GetJSONParam(f *pflag.FlagSet, name string) (map[string]any, error) { 12 | data, err := f.GetString(name) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | var result map[string]any 18 | err = json.Unmarshal([]byte(data), &result) 19 | return result, err 20 | } 21 | 22 | func GetStringSliceParam(f *pflag.FlagSet, name string) ([]string, error) { 23 | data, err := f.GetString(name) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | parts := strings.Split(data, ",") 29 | result := make([]string, 0, len(parts)) 30 | for _, part := range parts { 31 | if v := strings.TrimSpace(part); v != "" { 32 | result = append(result, v) 33 | } 34 | } 35 | return result, nil 36 | } 37 | 38 | func GetPartialUpdateParam(f *pflag.FlagSet) (stream.PartialUpdate, error) { 39 | var update stream.PartialUpdate 40 | var err error 41 | update.Set, err = GetJSONParam(f, "set") 42 | if err != nil { 43 | return update, err 44 | } 45 | update.Unset, err = GetStringSliceParam(f, "unset") 46 | return update, err 47 | } 48 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-reaction.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-reaction 2 | 3 | Delete a reaction from a message 4 | 5 | ``` 6 | stream-cli chat delete-reaction --message-id [message-id] --reaction-type [reaction-type] --user-id [user-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Delete a reaction from [08f64828-3bba-42bd-8430-c26a3634ee5c] message 13 | $ stream-cli chat delete-reaction --message-id 08f64828-3bba-42bd-8430-c26a3634ee5c --reaction-type like --user-id 12345 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for delete-reaction 21 | -m, --message-id string [required] The message id to delete the reaction from 22 | -r, --reaction-type string [required] The reaction type to delete 23 | -u, --user-id string [required] The user id of the user deleting the reaction 24 | ``` 25 | 26 | ### Options inherited from parent commands 27 | 28 | ``` 29 | --app string [optional] Application name to use as it's defined in the configuration file 30 | --config string [optional] Explicit config file path 31 | ``` 32 | 33 | ### SEE ALSO 34 | 35 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 36 | 37 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-messages.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-messages 2 | 3 | Return multiple messages 4 | 5 | ``` 6 | stream-cli chat get-messages --channel-type [channel-type] --channel-id [channel-id] --output-format [json|tree] [message-id-1] [message-id-2] [message-id ...] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Returns 3 messages of 'redteam' channel of 'messaging' channel type 13 | $ stream-cli chat get-messages --channel-type messaging --channel-id redteam msgid-1 msgid-2 msgid-3 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -i, --channel-id string [required] Channel id 21 | -t, --channel-type string [required] Channel type such as 'messaging' or 'livestream' 22 | -h, --help help for get-messages 23 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 24 | ``` 25 | 26 | ### Options inherited from parent commands 27 | 28 | ``` 29 | --app string [optional] Application name to use as it's defined in the configuration file 30 | --config string [optional] Explicit config file path 31 | ``` 32 | 33 | ### SEE ALSO 34 | 35 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 36 | 37 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-app.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-app 2 | 3 | Update application settings 4 | 5 | ### Synopsis 6 | 7 | Update the application settings. 8 | 9 | Application level settings allow you to configure settings that 10 | impact all the channel types in your app. 11 | 12 | See https://getstream.io/chat/docs/rest/#settings-updateapp for 13 | the available JSON options. 14 | 15 | 16 | ``` 17 | stream-cli chat update-app --properties [raw-json-update-properties] [flags] 18 | ``` 19 | 20 | ### Examples 21 | 22 | ``` 23 | # Enable multi-tenant and update permission version to v2 24 | $ stream-cli chat update-app --properties '{"multi_tenant_enabled": true, "permission_version": "v2"}' 25 | 26 | ``` 27 | 28 | ### Options 29 | 30 | ``` 31 | -h, --help help for update-app 32 | -p, --properties string [required] Raw json properties to update 33 | ``` 34 | 35 | ### Options inherited from parent commands 36 | 37 | ``` 38 | --app string [optional] Application name to use as it's defined in the configuration file 39 | --config string [optional] Explicit config file path 40 | ``` 41 | 42 | ### SEE ALSO 43 | 44 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 45 | 46 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-message.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-message 2 | 3 | Delete a message 4 | 5 | ### Synopsis 6 | 7 | You can delete a message by calling DeleteMessage and including a message 8 | with an ID. Messages can be soft deleted or hard deleted. Unless specified 9 | via the hard parameter, messages are soft deleted. Be aware that deleting 10 | a message doesn't delete its attachments. 11 | 12 | 13 | ``` 14 | stream-cli chat delete-message [message-id] [flags] 15 | ``` 16 | 17 | ### Examples 18 | 19 | ``` 20 | # Soft deletes a message with id 'msgid-1' 21 | $ stream-cli chat delete-message msgid-1 22 | 23 | # Hard deletes a message with id 'msgid-2' 24 | $ stream-cli chat delete-message msgid-2 --hard 25 | 26 | ``` 27 | 28 | ### Options 29 | 30 | ``` 31 | -H, --hard [optional] Hard delete message. Default is false 32 | -h, --help help for delete-message 33 | ``` 34 | 35 | ### Options inherited from parent commands 36 | 37 | ``` 38 | --app string [optional] Application name to use as it's defined in the configuration file 39 | --config string [optional] Explicit config file path 40 | ``` 41 | 42 | ### SEE ALSO 43 | 44 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 45 | 46 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/valid-channels-with-light.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "user1", 6 | "role": "user" 7 | } 8 | }, 9 | { 10 | "type": "user", 11 | "item": { 12 | "id": "user2", 13 | "role": "user" 14 | } 15 | }, 16 | { 17 | "type": "channel", 18 | "item": { 19 | "id": "channelA", 20 | "type": "messaging", 21 | "created_by": "user1" 22 | } 23 | }, 24 | { 25 | "type": "member", 26 | "item": { 27 | "channel_id": "channelA", 28 | "channel_type": "messaging", 29 | "user_id": "user1" 30 | } 31 | }, 32 | { 33 | "type": "channel", 34 | "item": { 35 | "id": "!members-channelB", 36 | "type": "messaging", 37 | "created_by": "user2" 38 | } 39 | }, 40 | { 41 | "type": "member", 42 | "item": { 43 | "channel_id": "!members-channelB", 44 | "channel_type": "messaging", 45 | "user_id": "user2" 46 | } 47 | }, 48 | { 49 | "type": "message", 50 | "item": { 51 | "id": "message2", 52 | "channel_id": "!members-channelB", 53 | "channel_type": "messaging", 54 | "user": "user2", 55 | "text": "hi!" 56 | } 57 | } 58 | ] 59 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-channel 2 | 3 | Return a channel 4 | 5 | ``` 6 | stream-cli chat get-channel --type [channel-type] --id [channel-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Returns 'redteam' channel of 'messaging' channel type as JSON 13 | $ stream-cli chat get-channel --type messaging --id redteam 14 | 15 | # Returns 'blueteam' channel of 'messaging' channel type as a browsable tree 16 | $ stream-cli chat get-channel --type messaging --id blueteam --output-format tree 17 | 18 | ``` 19 | 20 | ### Options 21 | 22 | ``` 23 | -h, --help help for get-channel 24 | -i, --id string [required] Channel id 25 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 26 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 27 | ``` 28 | 29 | ### Options inherited from parent commands 30 | 31 | ``` 32 | --app string [optional] Application name to use as it's defined in the configuration file 33 | --config string [optional] Explicit config file path 34 | ``` 35 | 36 | ### SEE ALSO 37 | 38 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 39 | 40 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-channel-type.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-channel-type 2 | 3 | Update channel type 4 | 5 | ### Synopsis 6 | 7 | This command updates an existing channel type. The 'properties' are raw JSON string. 8 | The available fields can be checked here: 9 | https://getstream.io/chat/docs/rest/#channel-types-updatechanneltype 10 | 11 | 12 | ``` 13 | stream-cli chat update-channel-type --type [channel-type] --properties [raw-json-properties] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Enabling quotes in an existing channel type 20 | $ stream-cli chat update-channel-type --type my-channel-type --properties '{"quotes": true}' 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -h, --help help for update-channel-type 28 | -p, --properties string [required] Raw JSON properties 29 | -t, --type string [required] Channel type 30 | ``` 31 | 32 | ### Options inherited from parent commands 33 | 34 | ``` 35 | --app string [optional] Application name to use as it's defined in the configuration file 36 | --config string [optional] Explicit config file path 37 | ``` 38 | 39 | ### SEE ALSO 40 | 41 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 42 | 43 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_revoke-token.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat revoke-token 2 | 3 | Revoke a token 4 | 5 | ### Synopsis 6 | 7 | Revokes a token for a single user. All requests will be rejected that 8 | were issued before the given epoch timestamp. 9 | 10 | 11 | ``` 12 | stream-cli chat revoke-token --user [user-id] --before [epoch] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Revoke token for user 'joe' before today's date (default date) 19 | $ stream-cli revoke-token --user joe 20 | 21 | # Revoke token for user 'mike' before 2019-01-01 22 | $ stream-cli revoke-token --user mike --before 1546300800 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -b, --before int [optional] The epoch timestamp before which tokens should be revoked. Defaults to now. 30 | -h, --help help for revoke-token 31 | -u, --user string [required] ID of the user to revoke token for 32 | ``` 33 | 34 | ### Options inherited from parent commands 35 | 36 | ``` 37 | --app string [optional] Application name to use as it's defined in the configuration file 38 | --config string [optional] Explicit config file path 39 | ``` 40 | 41 | ### SEE ALSO 42 | 43 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 44 | 45 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_revoke-all-tokens.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat revoke-all-tokens 2 | 3 | Revoke all tokens 4 | 5 | ### Synopsis 6 | 7 | This command revokes ALL tokens for all users of an application. 8 | This should be used with caution as it will expire every user’s token, 9 | regardless of whether the token has an iat claim. 10 | 11 | 12 | ``` 13 | stream-cli chat revoke-all-tokens --before [epoch] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Revoke all tokens for the default app, from now 20 | $ stream-cli chat revoke-all-tokens 21 | 22 | # Revoke all tokens for the test app, before 2019-01-01 23 | $ stream-cli chat revoke-all-tokens --before 1546300800 --app test 24 | 25 | ``` 26 | 27 | ### Options 28 | 29 | ``` 30 | -b, --before int [optional] The epoch timestamp before which tokens should be revoked. Defaults to now. 31 | -h, --help help for revoke-all-tokens 32 | ``` 33 | 34 | ### Options inherited from parent commands 35 | 36 | ``` 37 | --app string [optional] Application name to use as it's defined in the configuration file 38 | --config string [optional] Explicit config file path 39 | ``` 40 | 41 | ### SEE ALSO 42 | 43 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 44 | 45 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/decoder.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "io" 7 | ) 8 | 9 | type Decoder struct { 10 | source io.ReadSeeker 11 | } 12 | 13 | func NewDecoder(r io.ReadSeeker) *Decoder { 14 | return &Decoder{source: r} 15 | } 16 | 17 | func (d *Decoder) Items(fn func(item Item) error) error { 18 | // Reset source 19 | if _, err := d.source.Seek(0, io.SeekStart); err != nil { 20 | return newParseError(err) 21 | } 22 | 23 | jd := json.NewDecoder(d.source) 24 | 25 | // Validate opening-token 26 | token, err := jd.Token() 27 | if err != nil { 28 | return newParseError(err) 29 | } 30 | if token != json.Delim('[') { 31 | return newParseError(errors.New("invalid format")) 32 | } 33 | 34 | // Decode items 35 | errs := new(multiError) 36 | for jd.More() { 37 | offset := jd.InputOffset() 38 | 39 | var v rawItem 40 | if err := jd.Decode(&v); err != nil { 41 | return err 42 | } 43 | 44 | item, err := newItem(&v) 45 | if err != nil { 46 | errs.add(newItemError(v, offset, newParseError(err))) 47 | continue 48 | } 49 | 50 | err = fn(item) 51 | if err != nil { 52 | errs.add(newItemError(v, offset, err)) 53 | } 54 | } 55 | if errs.hasErrors() { 56 | return errs 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_create-channel-type.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat create-channel-type 2 | 3 | Create channel type 4 | 5 | ### Synopsis 6 | 7 | This command creates a new channel type. The 'properties' are raw JSON string. 8 | The available properties can be found in the Go SDK's 'ChannelType' struct. 9 | 10 | 11 | ``` 12 | stream-cli chat create-channel-type --properties [raw-json-properties] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Create a new channel type called my-ch-type 19 | $ stream-cli chat create-channel-type -p "{\"name\": \"my-ch-type\"}" 20 | 21 | # Create a new channel type called reactionless with reactions disabled 22 | $ stream-cli chat create-channel-type -p "{\"name\": \"reactionless\", \"reactions\": false}" 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -h, --help help for create-channel-type 30 | -p, --properties string [required] Raw JSON properties 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_show-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat show-channel 2 | 3 | Show a channel 4 | 5 | ### Synopsis 6 | 7 | Hiding a channel will remove it from query channel requests for that 8 | user until a new message is added. 9 | As opposed to this, showing a channel will add it to query channel requests for that user. 10 | 11 | 12 | ``` 13 | stream-cli chat show-channel --type [channel-type] --id [channel-id] --user-id [user-id] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Show a 'red-team' channel for user 'joe' 20 | $ stream-cli chat show-channel --type messaging --id red-team --user-id joe 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -h, --help help for show-channel 28 | -i, --id string [required] Channel id 29 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 30 | -u, --user-id string [required] User id to show the channel to 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_watch.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat watch 2 | 3 | Wait for an async task to complete 4 | 5 | ### Synopsis 6 | 7 | This command waits for a specific async backend operation 8 | to complete. Such as deleting a user or exporting a channel. 9 | 10 | 11 | ``` 12 | stream-cli chat watch [task-id] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Delete user and watching it complete 19 | $ stream-cli chat delete-users "my-user-1" 20 | > Successfully initiated user deletion. Task id: 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 21 | 22 | # Waiting for the task to complete 23 | $ stream-cli chat watch 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 24 | 25 | # Providing a timeout of 80 seconds 26 | $ stream-cli chat watch 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 --timeout 80 27 | 28 | ``` 29 | 30 | ### Options 31 | 32 | ``` 33 | -h, --help help for watch 34 | -t, --timeout int [optional] Timeout in seconds. Default is 30 (default 30) 35 | ``` 36 | 37 | ### Options inherited from parent commands 38 | 39 | ``` 40 | --app string [optional] Application name to use as it's defined in the configuration file 41 | --config string [optional] Explicit config file path 42 | ``` 43 | 44 | ### SEE ALSO 45 | 46 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 47 | 48 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_mute-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat mute-user 2 | 3 | Mute a user 4 | 5 | ### Synopsis 6 | 7 | Any user is allowed to mute another user. Mutes are stored at the user 8 | level and returned with the rest of the user information when connectUser is called. 9 | A user will be muted until the user is unmuted or the mute is expired. 10 | 11 | 12 | ``` 13 | stream-cli chat mute-user --target-user-id [user-id] --muted-by-id [user-id] --expiration [minutes] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Mute the user 'joe' for 5 minutes 20 | $ stream-cli chat mute-user --target-user-id joe --muted-by-id admin --expiration 5 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -e, --expiration int [optional] Number of minutes until the mute expires 28 | -h, --help help for mute-user 29 | -b, --muted-by-id string [required] ID of the user who muted the user 30 | -t, --target-user-id string [required] ID of the user to mute 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-user-partial.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-user-partial 2 | 3 | Partially update a user 4 | 5 | ### Synopsis 6 | 7 | Updates an existing user. The 'set' property is a comma separated list of key value pairs. 8 | The 'unset' property is a comma separated list of property names. 9 | 10 | 11 | ``` 12 | stream-cli chat update-user-partial --user [user-id] --set [raw-json] --unset [property-names] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Set a user's role to 'admin' and set 'age' to 21. At the same time, remove 'haircolor' and 'height'. 19 | $ stream-cli chat update-user-partial --user-id my-user-1 --set '{"role":"admin","age":21}' --unset haircolor,height 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for update-user-partial 27 | -s, --set string [optional] Raw JSON of key-value pairs to set 28 | -u, --unset string [optional] Comma separated list of properties to unset 29 | -i, --user-id string [required] Channel ID 30 | ``` 31 | 32 | ### Options inherited from parent commands 33 | 34 | ``` 35 | --app string [optional] Application name to use as it's defined in the configuration file 36 | --config string [optional] Explicit config file path 37 | ``` 38 | 39 | ### SEE ALSO 40 | 41 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 42 | 43 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_hide-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat hide-channel 2 | 3 | Hide a channel 4 | 5 | ### Synopsis 6 | 7 | Hiding a channel will remove it from query channel requests for that 8 | user until a new message is added. Please keep in mind that hiding a channel 9 | is only available to members of that channel. 10 | You can still retrieve the list of hidden channels using the { "hidden" : true } query parameter. 11 | 12 | 13 | ``` 14 | stream-cli chat hide-channel --type [channel-type] --id [channel-id] --user-id [user-id] [flags] 15 | ``` 16 | 17 | ### Examples 18 | 19 | ``` 20 | # Hide a 'red-team' channel for user 'joe' 21 | $ stream-cli chat hide-channel --type messaging --id red-team --user-id joe 22 | 23 | ``` 24 | 25 | ### Options 26 | 27 | ``` 28 | -h, --help help for hide-channel 29 | -i, --id string [required] Channel id 30 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 31 | -u, --user-id string [required] User id to hide the channel to 32 | ``` 33 | 34 | ### Options inherited from parent commands 35 | 36 | ``` 37 | --app string [optional] Application name to use as it's defined in the configuration file 38 | --config string [optional] Explicit config file path 39 | ``` 40 | 41 | ### SEE ALSO 42 | 43 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 44 | 45 | -------------------------------------------------------------------------------- /pkg/cmd/root/root.go: -------------------------------------------------------------------------------- 1 | package root 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/MakeNowJust/heredoc" 7 | "github.com/spf13/cobra" 8 | 9 | "github.com/GetStream/stream-cli/pkg/cmd/chat" 10 | cfgCmd "github.com/GetStream/stream-cli/pkg/cmd/config" 11 | "github.com/GetStream/stream-cli/pkg/config" 12 | "github.com/GetStream/stream-cli/pkg/version" 13 | ) 14 | 15 | var cfgPath = new(string) 16 | 17 | func NewCmd() *cobra.Command { 18 | root := &cobra.Command{ 19 | Use: "stream-cli [flags]", 20 | Short: "Stream CLI", 21 | Long: "Interact with your Stream applications easily", 22 | Example: heredoc.Doc(` 23 | # Get Chat application settings 24 | $ stream-cli chat get-app 25 | 26 | # List all Chat channel types 27 | $ stream-cli chat list-channel-types 28 | 29 | # Create a new Chat user 30 | $ stream-cli chat upsert-user --properties "{\"id\":\"my-user-1\"}" 31 | `), 32 | Version: version.FmtVersion(), 33 | } 34 | 35 | fl := root.PersistentFlags() 36 | fl.String("app", "", "[optional] Application name to use as it's defined in the configuration file") 37 | fl.StringVar(cfgPath, "config", "", "[optional] Explicit config file path") 38 | 39 | root.AddCommand( 40 | cfgCmd.NewRootCmd(), 41 | chat.NewRootCmd(), 42 | ) 43 | 44 | cobra.OnInitialize(config.GetInitConfig(root, cfgPath)) 45 | 46 | root.SetOut(os.Stdout) 47 | 48 | return root 49 | } 50 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_list-channels.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat list-channels 2 | 3 | List channels 4 | 5 | ### Synopsis 6 | 7 | List all channels of a given channel type. You can also provide 8 | a limit for paginating the results. 9 | 10 | 11 | ``` 12 | stream-cli chat list-channels --type [channel-type] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # List the top 5 'messaging' channels as a json 19 | $ stream-cli chat list-channels --type messaging --limit 5 20 | 21 | # List the top 20 'livestream' channels as a browsable tree 22 | $ stream-cli chat list-channels --type livestream --limit 20 --output-format tree 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -h, --help help for list-channels 30 | -l, --limit int [optional] Number of channels to return. Used for pagination (default 10) 31 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 32 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 33 | ``` 34 | 35 | ### Options inherited from parent commands 36 | 37 | ``` 38 | --app string [optional] Application name to use as it's defined in the configuration file 39 | --config string [optional] Explicit config file path 40 | ``` 41 | 42 | ### SEE ALSO 43 | 44 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 45 | 46 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_get-app.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat get-app 2 | 3 | Get application settings 4 | 5 | ### Synopsis 6 | 7 | Get the application settings. 8 | 9 | Application level settings allow you to configure settings that 10 | impact all the channel types in your app. 11 | 12 | 13 | ``` 14 | stream-cli chat get-app --output-format [json|tree] [flags] 15 | ``` 16 | 17 | ### Examples 18 | 19 | ``` 20 | # Print the application settings in json format (default format) 21 | $ stream-cli chat get-app 22 | 23 | # Print the application settings in a browsable tree 24 | $ stream-cli chat get-app --output-format tree 25 | 26 | # Print the application settings for another application 27 | $ stream-cli chat get-app --app testenvironment 28 | 29 | # Note: 30 | # Use this command to list all the available Stream applications 31 | $ stream-cli config list 32 | 33 | ``` 34 | 35 | ### Options 36 | 37 | ``` 38 | -h, --help help for get-app 39 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 40 | ``` 41 | 42 | ### Options inherited from parent commands 43 | 44 | ``` 45 | --app string [optional] Application name to use as it's defined in the configuration file 46 | --config string [optional] Explicit config file path 47 | ``` 48 | 49 | ### SEE ALSO 50 | 51 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 52 | 53 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-channel 2 | 3 | Update a channel 4 | 5 | ### Synopsis 6 | 7 | Updates an existing channel. The 'properties' are specified as a raw json string. The valid 8 | properties are the 'ChannelRequest' object of the official documentation. 9 | Such as 'team', 'frozen', 'disabled' or any custom property. 10 | https://getstream.io/chat/docs/rest/#channels-updatechannel 11 | 12 | 13 | ``` 14 | stream-cli chat update-channel --type [channel-type] --id [channel-id] --properties [raw-json-properties] [flags] 15 | ``` 16 | 17 | ### Examples 18 | 19 | ``` 20 | # Unfreeze a channel 21 | $ stream-cli chat update-channel --type messaging --id redteam --properties "{\"frozen\":false}" 22 | 23 | ``` 24 | 25 | ### Options 26 | 27 | ``` 28 | -h, --help help for update-channel 29 | -i, --id string [required] Channel id 30 | -p, --properties string [required] Channel properties to update 31 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 32 | ``` 33 | 34 | ### Options inherited from parent commands 35 | 36 | ``` 37 | --app string [optional] Application name to use as it's defined in the configuration file 38 | --config string [optional] Explicit config file path 39 | ``` 40 | 41 | ### SEE ALSO 42 | 43 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 44 | 45 | -------------------------------------------------------------------------------- /pkg/cmd/chat/app/app_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/GetStream/stream-cli/test" 10 | ) 11 | 12 | func TestGetAppJsonFormat(t *testing.T) { 13 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 14 | cmd.SetArgs([]string{"get-app", "--output-format", "json"}) 15 | _, err := cmd.ExecuteC() 16 | require.NoError(t, err) 17 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "organization") 18 | } 19 | 20 | func TestGetAppUnknownFormat(t *testing.T) { 21 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 22 | cmd.SetArgs([]string{"get-app", "--output-format", "unknown"}) 23 | _, err := cmd.ExecuteC() 24 | require.Error(t, err) 25 | require.NotContains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "organization") 26 | } 27 | 28 | func TestUpdateApp(t *testing.T) { 29 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 30 | cmd.SetArgs([]string{"update-app", "--properties", "{\"multi_tenant_enabled\":true}"}) 31 | _, err := cmd.ExecuteC() 32 | require.NoError(t, err) 33 | } 34 | 35 | func TestRevokeAlTokens(t *testing.T) { 36 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 37 | cmd.SetArgs([]string{"revoke-all-tokens", "--before", "2000"}) 38 | _, err := cmd.ExecuteC() 39 | require.NoError(t, err) 40 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully revoked all tokens") 41 | } 42 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_translate-message.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat translate-message 2 | 3 | Translate a message 4 | 5 | ### Synopsis 6 | 7 | Chat messages can be translated on-demand or automatically, this 8 | allows users speaking different languages on the same channel. 9 | 10 | The translate endpoint returns the translated message, updates 11 | it and sends a message.updated event to all users on the channel. 12 | 13 | 14 | ``` 15 | stream-cli chat translate-message --message-id [message-id] --language [language] --output-format [json|tree] [flags] 16 | ``` 17 | 18 | ### Examples 19 | 20 | ``` 21 | # Translates a message with id 'msgid-1' to English 22 | $ stream-cli chat translate-message --message-id msgid-1 --language en 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -h, --help help for translate-message 30 | -l, --language string [required] Language to translate to 31 | -m, --message-id string [required] Message id to translate 32 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 33 | ``` 34 | 35 | ### Options inherited from parent commands 36 | 37 | ``` 38 | --app string [optional] Application name to use as it's defined in the configuration file 39 | --config string [optional] Explicit config file path 40 | ``` 41 | 42 | ### SEE ALSO 43 | 44 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 45 | 46 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_create-device.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat create-device 2 | 3 | Create a device 4 | 5 | ### Synopsis 6 | 7 | Registering a device associates it with a user and tells 8 | the push provider to send new message notifications to the device. 9 | 10 | 11 | ``` 12 | stream-cli chat create-device --id [device-id] --push-provider [firebase|apn|xiaomi|huawei] --push-provider-name [provider-name] --user-id [user-id] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Create a device with a firebase push provider 19 | $ stream-cli chat create-device --id "my-device-id" --push-provider firebase --push-provider-name "my-firebase-project-id" --user-id "my-user-id" 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for create-device 27 | -i, --id string [required] Device ID 28 | -p, --push-provider string [required] Push provider. Can be apn, firebase, xiaomi, huawei 29 | -n, --push-provider-name string [optional] Push provider name 30 | -u, --user-id string [required] User ID 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_send-reaction.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat send-reaction 2 | 3 | Send a reaction to a message 4 | 5 | ### Synopsis 6 | 7 | Stream Chat has built-in support for user Reactions. Common examples are 8 | likes, comments, loves, etc. Reactions can be customized so that you 9 | are able to use any type of reaction your application requires. 10 | 11 | 12 | ``` 13 | stream-cli chat send-reaction --message-id [message-id] --user-id [user-id] --reaction-type [reaction-type] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Send a reaction to a [08f64828-3bba-42bd-8430-c26a3634ee5c] message 20 | $ stream-cli chat send-reaction --message-id 08f64828-3bba-42bd-8430-c26a3634ee5c --user-id 12345 --reaction-type like 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -h, --help help for send-reaction 28 | -m, --message-id string [required] The message id to send the reaction to 29 | -r, --reaction-type string [required] The reaction type to send 30 | -u, --user-id string [required] The user id of the user sending the reaction 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-message-partial.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-message-partial 2 | 3 | Partially update a message 4 | 5 | ### Synopsis 6 | 7 | A partial update can be used to set and unset specific fields when it 8 | is necessary to retain additional data fields on the object. AKA a patch style update. 9 | 10 | 11 | ``` 12 | stream-cli chat update-message-partial --message-id [message-id] --user [user-id] --set [raw-json] --unset [property-names] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Partially updates a message with id 'msgid-1'. Updates a custom field and removes the silent flag. 19 | $ stream-cli chat update-message-partial -message-id msgid-1 --set '{"importance": "low"}' --unset silent 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for update-message-partial 27 | -m, --message-id string [required] Message id 28 | -s, --set string [optional] Raw JSON of key-value pairs to set 29 | --unset string [optional] Comma separated list of properties to unset 30 | -u, --user string [required] User id 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /pkg/cmd/chat/root.go: -------------------------------------------------------------------------------- 1 | package chat 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | 6 | "github.com/GetStream/stream-cli/pkg/cmd/chat/app" 7 | "github.com/GetStream/stream-cli/pkg/cmd/chat/channel" 8 | "github.com/GetStream/stream-cli/pkg/cmd/chat/channeltype" 9 | "github.com/GetStream/stream-cli/pkg/cmd/chat/device" 10 | "github.com/GetStream/stream-cli/pkg/cmd/chat/events" 11 | "github.com/GetStream/stream-cli/pkg/cmd/chat/file" 12 | "github.com/GetStream/stream-cli/pkg/cmd/chat/imports" 13 | "github.com/GetStream/stream-cli/pkg/cmd/chat/message" 14 | "github.com/GetStream/stream-cli/pkg/cmd/chat/push" 15 | "github.com/GetStream/stream-cli/pkg/cmd/chat/reaction" 16 | "github.com/GetStream/stream-cli/pkg/cmd/chat/user" 17 | "github.com/GetStream/stream-cli/pkg/cmd/chat/watch" 18 | ) 19 | 20 | func NewRootCmd() *cobra.Command { 21 | cmd := &cobra.Command{ 22 | Use: "chat", 23 | Short: "Allows you to interact with your Chat applications", 24 | } 25 | 26 | cmd.AddCommand(app.NewCmds()...) 27 | cmd.AddCommand(channel.NewCmds()...) 28 | cmd.AddCommand(channeltype.NewCmds()...) 29 | cmd.AddCommand(device.NewCmds()...) 30 | cmd.AddCommand(events.NewCmds()...) 31 | cmd.AddCommand(file.NewCmds()...) 32 | cmd.AddCommand(imports.NewCmds()...) 33 | cmd.AddCommand(message.NewCmds()...) 34 | cmd.AddCommand(user.NewCmds()...) 35 | cmd.AddCommand(push.NewCmds()...) 36 | cmd.AddCommand(reaction.NewCmds()...) 37 | cmd.AddCommand(watch.NewCmds()...) 38 | 39 | return cmd 40 | } 41 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-channel 2 | 3 | Delete a channel 4 | 5 | ### Synopsis 6 | 7 | This command allows you to delete a channel. This operation is asynchronous 8 | in the backend so a task id is returned. You need to use the watch 9 | commnand to poll the results. 10 | 11 | 12 | ``` 13 | stream-cli chat delete-channel --type [channel-type] --id [channel-id] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Delete a channel with id 'redteam' of type 'messaging' 20 | $ stream-cli chat delete-channel --type messaging --id redteam 21 | > Successfully initiated channel deletion. Task id: 66bbcdcd-b133-43ce-ab63-557c14d2a168 22 | 23 | # Wait for the task to complete 24 | $ stream-cli chat watch 66bbcdcd-b133-43ce-ab63-557c14d2a168 25 | 26 | ``` 27 | 28 | ### Options 29 | 30 | ``` 31 | --hard [optional] Channel will be hard deleted. This action is irrevocable. 32 | -h, --help help for delete-channel 33 | -i, --id string [required] Channel id 34 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 35 | ``` 36 | 37 | ### Options inherited from parent commands 38 | 39 | ``` 40 | --app string [optional] Application name to use as it's defined in the configuration file 41 | --config string [optional] Explicit config file path 42 | ``` 43 | 44 | ### SEE ALSO 45 | 46 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 47 | 48 | -------------------------------------------------------------------------------- /pkg/cmd/chat/utils/fileupload.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | stream "github.com/GetStream/stream-chat-go/v5" 8 | "github.com/spf13/cobra" 9 | ) 10 | 11 | type uploadType string 12 | 13 | const ( 14 | uploadTypeFile uploadType = "file" 15 | uploadTypeImage uploadType = "image" 16 | ) 17 | 18 | func UploadFile(c *stream.Client, cmd *cobra.Command, chType, chID, userID, filePath string) (string, error) { 19 | return uploadFile(c, cmd, uploadTypeFile, chType, chID, userID, filePath) 20 | } 21 | 22 | func UploadImage(c *stream.Client, cmd *cobra.Command, chType, chID, userID, filePath string) (string, error) { 23 | return uploadFile(c, cmd, uploadTypeImage, chType, chID, userID, filePath) 24 | } 25 | 26 | func uploadFile(c *stream.Client, cmd *cobra.Command, uploadtype uploadType, chType, chID, userID, filePath string) (string, error) { 27 | file, err := os.Open(filePath) 28 | if err != nil { 29 | return "", err 30 | } 31 | defer file.Close() 32 | 33 | req := stream.SendFileRequest{ 34 | User: &stream.User{ID: userID}, 35 | FileName: filepath.Base(file.Name()), 36 | Reader: file, 37 | } 38 | 39 | var resp *stream.SendFileResponse 40 | 41 | if uploadtype == uploadTypeImage { 42 | resp, err = c.Channel(chType, chID).SendImage(cmd.Context(), req) 43 | } else { 44 | resp, err = c.Channel(chType, chID).SendFile(cmd.Context(), req) 45 | } 46 | 47 | if err != nil { 48 | return "", err 49 | } 50 | 51 | return resp.File, nil 52 | } 53 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_listen-events.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat listen-events 2 | 3 | Listen to events 4 | 5 | ### Synopsis 6 | 7 | The command opens a WebSocket connection to the backend in the name of the user 8 | and prints the received events to the standard output. 9 | Press Ctrl+C to exit. 10 | 11 | 12 | ``` 13 | stream-cli chat listen-events --user-id [user-id] --timeout [number] [flags] 14 | ``` 15 | 16 | ### Examples 17 | 18 | ``` 19 | # Listen to events for user with id 'my-user-1' 20 | $ stream-cli chat listen-events --user-id my-user-1 21 | 22 | # Listen to events for user with id 'my-user-2' and keeping the connection open for 120 seconds 23 | $ stream-cli chat listen-events --user-id my-user-1 --timeout 120 24 | 25 | ``` 26 | 27 | ### Options 28 | 29 | ``` 30 | -h, --help help for listen-events 31 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 32 | -t, --timeout int32 [optional] For how many seconds do we keep the connection alive. Default is 60 seconds, max is 300. (default 60) 33 | -u, --user-id string [required] User ID 34 | ``` 35 | 36 | ### Options inherited from parent commands 37 | 38 | ``` 39 | --app string [optional] Application name to use as it's defined in the configuration file 40 | --config string [optional] Explicit config file path 41 | ``` 42 | 43 | ### SEE ALSO 44 | 45 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 46 | 47 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_update-channel-partial.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat update-channel-partial 2 | 3 | Update a channel partially 4 | 5 | ### Synopsis 6 | 7 | Updates an existing channel. The 'set' property is a comma separated list of key value pairs. 8 | The 'unset' property is a comma separated list of property names. 9 | 10 | 11 | ``` 12 | stream-cli chat update-channel-partial --type [channel-type] --id [channel-id] --set [raw-json] --unset [property-names] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Freeze a channel and set 'age' to 21. At the same time, remove 'haircolor' and 'height'. 19 | $ stream-cli chat update-channel-partial --type messaging --id channel1 --set '{"frozen":true,"age":21}' --unset haircolor,height 20 | 21 | ``` 22 | 23 | ### Options 24 | 25 | ``` 26 | -h, --help help for update-channel-partial 27 | -i, --id string [required] Channel id 28 | -s, --set string [optional] Raw JSON of key-value pairs to set 29 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 30 | -u, --unset string [optional] Comma separated list of properties to unset 31 | ``` 32 | 33 | ### Options inherited from parent commands 34 | 35 | ``` 36 | --app string [optional] Application name to use as it's defined in the configuration file 37 | --config string [optional] Explicit config file path 38 | ``` 39 | 40 | ### SEE ALSO 41 | 42 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 43 | 44 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_test-push.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat test-push 2 | 3 | Test push notifications 4 | 5 | ``` 6 | stream-cli chat test-push --message-id [string] --apn-template [string] --firebase-template [string] --firebase-data-template [string] --skip-devices [true|false] --push-provider-name [string] --push-provider-type [string] --user-id [string] --output-format [json|tree] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # A test push notification for a certain message id 13 | $ stream-cli chat test-push --message-id msgid --user-id id --skip-devices true 14 | 15 | ``` 16 | 17 | ### Options 18 | 19 | ``` 20 | -h, --help help for test-push 21 | --message-id string [optional] Message id to test 22 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 23 | --push-provider-name string [optional] Push provider name to use 24 | --push-provider-type string [optional] Push provider type to use 25 | --skip-devices [optional] Whether to notify devices 26 | --user-id string [optional] User id to initiate the test 27 | ``` 28 | 29 | ### Options inherited from parent commands 30 | 31 | ``` 32 | --app string [optional] Application name to use as it's defined in the configuration file 33 | --config string [optional] Explicit config file path 34 | ``` 35 | 36 | ### SEE ALSO 37 | 38 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 39 | 40 | -------------------------------------------------------------------------------- /pkg/cmd/chat/watch/watch.go: -------------------------------------------------------------------------------- 1 | package watch 2 | 3 | import ( 4 | "github.com/MakeNowJust/heredoc" 5 | "github.com/spf13/cobra" 6 | 7 | "github.com/GetStream/stream-cli/pkg/cmd/chat/utils" 8 | "github.com/GetStream/stream-cli/pkg/config" 9 | ) 10 | 11 | func NewCmds() []*cobra.Command { 12 | cmd := &cobra.Command{ 13 | Use: "watch [task-id]", 14 | Short: "Wait for an async task to complete", 15 | Long: heredoc.Doc(` 16 | This command waits for a specific async backend operation 17 | to complete. Such as deleting a user or exporting a channel. 18 | `), 19 | Example: heredoc.Doc(` 20 | # Delete user and watching it complete 21 | $ stream-cli chat delete-users "my-user-1" 22 | > Successfully initiated user deletion. Task id: 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 23 | 24 | # Waiting for the task to complete 25 | $ stream-cli chat watch 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 26 | 27 | # Providing a timeout of 80 seconds 28 | $ stream-cli chat watch 7586fa0d-dc8d-4f6f-be2d-f952d0e26167 --timeout 80 29 | `), 30 | Args: cobra.ExactArgs(1), 31 | RunE: func(cmd *cobra.Command, args []string) error { 32 | taskID := args[0] 33 | timeout, _ := cmd.Flags().GetInt("timeout") 34 | c, err := config.GetConfig(cmd).GetClient(cmd) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | return utils.WaitForAsyncCompletion(cmd, c, taskID, timeout) 40 | }, 41 | } 42 | 43 | cmd.Flags().IntP("timeout", "t", 30, "[optional] Timeout in seconds. Default is 30") 44 | 45 | return []*cobra.Command{cmd} 46 | } 47 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_create-channel.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat create-channel 2 | 3 | Create a channel 4 | 5 | ### Synopsis 6 | 7 | This command allows you to create a new channel. If it 8 | exists already an error will be thrown. 9 | 10 | 11 | ``` 12 | stream-cli chat create-channel --type [channel-type] --id [channel-id] --user [user-id] --properties [raw-json] [flags] 13 | ``` 14 | 15 | ### Examples 16 | 17 | ``` 18 | # Create a channel with id 'redteam' of type 'messaging' by 'joe' 19 | $ stream-cli chat create-channel --type messaging --id redteam --user joe 20 | 21 | # Create a channel with id 'blueteam' of type 'messaging' by 'joe' with extra data 22 | $ stream-cli chat create-channel --type messaging --id blueteam --user joe --properties "{\"age\":\"28\"}" 23 | 24 | ``` 25 | 26 | ### Options 27 | 28 | ``` 29 | -h, --help help for create-channel 30 | -i, --id string [required] Channel id 31 | -p, --properties string [optional] JSON string of channel properties 32 | -t, --type string [required] Channel type such as 'messaging' or 'livestream' 33 | -u, --user string [required] User id who will be considered as the creator of the channel 34 | ``` 35 | 36 | ### Options inherited from parent commands 37 | 38 | ``` 39 | --app string [optional] Application name to use as it's defined in the configuration file 40 | --config string [optional] Explicit config file path 41 | ``` 42 | 43 | ### SEE ALSO 44 | 45 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 46 | 47 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_query-users.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat query-users 2 | 3 | Query users 4 | 5 | ### Synopsis 6 | 7 | This command allows you to search for users. The 'filter' flag is a raw JSON string, 8 | and you can check the valid combinations in the official documentation. 9 | 10 | https://getstream.io/chat/docs/node/query_users/?language=javascript 11 | 12 | 13 | ``` 14 | stream-cli chat query-users --filter [raw-json] --limit [int] --output-format [json|tree] [flags] 15 | ``` 16 | 17 | ### Examples 18 | 19 | ``` 20 | # Query for 'user-1'. The results are shown as json. 21 | $ stream-cli chat query-users --filter '{"id": {"$eq": "user-1"}}' 22 | 23 | # Query for 'user-1' and 'user-2'. The results are shown as a browsable tree. 24 | $ stream-cli chat query-users --filter '{"id": {"$in": ["user-1", "user-2"]}}' --output-format tree 25 | 26 | ``` 27 | 28 | ### Options 29 | 30 | ``` 31 | -f, --filter string [required] Filter for users (default "{}") 32 | -h, --help help for query-users 33 | -l, --limit int [optional] The number of users returned (default 10) 34 | -o, --output-format string [optional] Output format. Can be json or tree (default "json") 35 | ``` 36 | 37 | ### Options inherited from parent commands 38 | 39 | ``` 40 | --app string [optional] Application name to use as it's defined in the configuration file 41 | --config string [optional] Explicit config file path 42 | ``` 43 | 44 | ### SEE ALSO 45 | 46 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 47 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Stream 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_create-token.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat create-token 2 | 3 | Create a token 4 | 5 | ### Synopsis 6 | 7 | Stream uses JWT (JSON Web Tokens) to authenticate chat users, enabling them to login. 8 | Knowing whether a user is authorized to perform certain actions is 9 | managed separately via a role based permissions system. 10 | 11 | With this command you can generate token for a specific user that can be 12 | used on the frontend. 13 | 14 | 15 | ``` 16 | stream-cli chat create-token --user [user-id] --expiration [epoch] --issued-at [epoch] [flags] 17 | ``` 18 | 19 | ### Examples 20 | 21 | ``` 22 | # Create a JWT token for a user with id '123'. This token has no expiration. 23 | $ stream-cli chat create-token --user 123 24 | 25 | # Create a JWT for user 'joe' with 'exp' and 'iat' claim 26 | $ stream-cli chat create-token --user joe --expiration 1577880000 --issued-at 1577880000 27 | 28 | ``` 29 | 30 | ### Options 31 | 32 | ``` 33 | -e, --expiration int [optional] Expiration (exp) of the JWT in epoch timestamp 34 | -h, --help help for create-token 35 | -i, --issued-at int [optional] Issued at (iat) of the JWT in epoch timestamp 36 | -u, --user string [required] ID of the user to create token for 37 | ``` 38 | 39 | ### Options inherited from parent commands 40 | 41 | ``` 42 | --app string [optional] Application name to use as it's defined in the configuration file 43 | --config string [optional] Explicit config file path 44 | ``` 45 | 46 | ### SEE ALSO 47 | 48 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 49 | 50 | -------------------------------------------------------------------------------- /install/install.ps1: -------------------------------------------------------------------------------- 1 | 2 | $latestRelease = Invoke-WebRequest "https://api.github.com/repos/GetStream/stream-cli/releases/latest" 3 | $json = $latestRelease.Content | ConvertFrom-Json 4 | 5 | $url = $json.assets | ? { $_.name -match "Windows_x86" } | select -expand browser_download_url 6 | if ($env:PROCESSOR_ARCHITECTURE -ne $null -and $env:PROCESSOR_ARCHITECTURE.Contains("arm")) { 7 | $url = $json.assets | ? { $_.name -match "Windows_arm" } | select -expand browser_download_url 8 | } 9 | 10 | # Typically C:\Users\User\AppData\Roaming 11 | $appDataFolder = [Environment]::GetFolderPath([Environment+SpecialFolder]::ApplicationData) 12 | $cliFolder = Join-Path $appDataFolder "stream-cli" 13 | $zipFile = Join-Path $cliFolder "stream-cli.zip" 14 | $exeFile = Join-Path $cliFolder "stream-cli.exe" 15 | if (-not (Test-Path $cliFolder)) { 16 | New-Item -Path $cliFolder -ItemType Directory | Out-Null 17 | } 18 | if (Test-Path $zipFile) { 19 | Remove-Item -Force $zipFile 20 | } 21 | if (Test-Path $exeFile) { 22 | Remove-Item -Force $exeFile 23 | } 24 | 25 | # Download the zip file 26 | Write-Host " > Downloading stream-cli zip file from GitHub..." 27 | Invoke-WebRequest -Uri $url -OutFile $zipFile 28 | 29 | # Unzip it and remove the zip file 30 | Write-Host " > Unzipping stream-cli zip file in $cliFolder" 31 | Expand-Archive -Path $zipFile -DestinationPath $cliFolder 32 | Remove-Item $zipFile 33 | Write-Host " > The exe is available at $exeFile" 34 | 35 | # Add to $PATH 36 | # It requires elevated access, so we'll rather ask the user to do it 37 | 38 | Write-Host " > Done! Please add $cliFolder to your PATH environment variable." -------------------------------------------------------------------------------- /docs/stream-cli_chat_upload-file.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat upload-file 2 | 3 | Upload a file 4 | 5 | ### Synopsis 6 | 7 | Stream will not block any file types from uploading, however, different 8 | clients may handle different types differently or not at all. 9 | You can set a more restrictive list for your application if needed. 10 | The maximum file size is 100MB. 11 | Stream will allow any file extension. If you want to be more restrictive 12 | for an application, this is can be set via API or by logging into your dashboard. 13 | 14 | 15 | ``` 16 | stream-cli chat upload-file --channel-type [channel-type] --channel-id [channel-id] --user-id [user-id] --file [file] [flags] 17 | ``` 18 | 19 | ### Examples 20 | 21 | ``` 22 | # Uploads a file to 'redteam' channel of 'messaging' channel type 23 | $ stream-cli chat upload-file --channel-type messaging --channel-id redteam --user-id "user-1" --file "./snippet.txt" 24 | 25 | ``` 26 | 27 | ### Options 28 | 29 | ``` 30 | -i, --channel-id string [required] Channel id to interact with 31 | -t, --channel-type string [required] Channel type to interact with 32 | -f, --file string [required] File path 33 | -h, --help help for upload-file 34 | -u, --user-id string [required] User id 35 | ``` 36 | 37 | ### Options inherited from parent commands 38 | 39 | ``` 40 | --app string [optional] Application name to use as it's defined in the configuration file 41 | --config string [optional] Explicit config file path 42 | ``` 43 | 44 | ### SEE ALSO 45 | 46 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 47 | 48 | -------------------------------------------------------------------------------- /stream-cli.rb: -------------------------------------------------------------------------------- 1 | # typed: false 2 | # frozen_string_literal: true 3 | 4 | # This file was generated by GoReleaser. DO NOT EDIT. 5 | class StreamCli < Formula 6 | desc "Manage your Stream applications easily." 7 | homepage "https://github.com/GetStream/stream-cli" 8 | version "1.7.2" 9 | 10 | on_macos do 11 | if Hardware::CPU.arm? 12 | url "https://github.com/GetStream/stream-cli/releases/download/v1.7.2/stream-cli_Darwin_arm64.tar.gz" 13 | sha256 "731abfc2a08440861e9340e5989b8a5357677bb2fc352aa6cae24d5e9b8c54e3" 14 | 15 | def install 16 | bin.install "stream-cli" 17 | end 18 | end 19 | if Hardware::CPU.intel? 20 | url "https://github.com/GetStream/stream-cli/releases/download/v1.7.2/stream-cli_Darwin_x86_64.tar.gz" 21 | sha256 "90d2442c6071647ca5a20b28e373e4ee030b664f10e589b6355e2a245dc27d46" 22 | 23 | def install 24 | bin.install "stream-cli" 25 | end 26 | end 27 | end 28 | 29 | on_linux do 30 | if Hardware::CPU.arm? && Hardware::CPU.is_64_bit? 31 | url "https://github.com/GetStream/stream-cli/releases/download/v1.7.2/stream-cli_Linux_arm64.tar.gz" 32 | sha256 "5608f00b633c1ccec0ee65cadd42a9765337838269024c3f23003ceaa2feaf40" 33 | 34 | def install 35 | bin.install "stream-cli" 36 | end 37 | end 38 | if Hardware::CPU.intel? 39 | url "https://github.com/GetStream/stream-cli/releases/download/v1.7.2/stream-cli_Linux_x86_64.tar.gz" 40 | sha256 "274fd30b6635d40135390f4cf96c33ca8770a230718d460ffe0ad10eaa62ed02" 41 | 42 | def install 43 | bin.install "stream-cli" 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/imports_test.go: -------------------------------------------------------------------------------- 1 | package imports 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "testing" 7 | 8 | stream_chat "github.com/GetStream/stream-chat-go/v5" 9 | "github.com/stretchr/testify/require" 10 | 11 | "github.com/GetStream/stream-cli/test" 12 | ) 13 | 14 | func TestValidateImport(t *testing.T) { 15 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 16 | cmd.SetArgs([]string{"validate-import", "./validator/testdata/valid-data.json"}) 17 | _, err := cmd.ExecuteC() 18 | require.NoError(t, err) 19 | } 20 | 21 | func TestUploadImport(t *testing.T) { 22 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 23 | cmd.SetArgs([]string{"upload-import", "./validator/testdata/valid-data.json"}) 24 | _, err := cmd.ExecuteC() 25 | require.NoError(t, err) 26 | } 27 | 28 | func TestGetImport(t *testing.T) { 29 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 30 | cmd.SetArgs([]string{"upload-import", "./validator/testdata/valid-data.json"}) 31 | _, err := cmd.ExecuteC() 32 | require.NoError(t, err) 33 | 34 | var task stream_chat.ImportTask 35 | require.NoError(t, json.Unmarshal(cmd.OutOrStdout().(*bytes.Buffer).Bytes(), &task)) 36 | 37 | cmd.SetArgs([]string{"get-import", task.ID}) 38 | _, err = cmd.ExecuteC() 39 | require.NoError(t, err) 40 | } 41 | 42 | func TestListImports(t *testing.T) { 43 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 44 | cmd.SetArgs([]string{"list-imports", "--limit", "1"}) 45 | _, err := cmd.ExecuteC() 46 | require.NoError(t, err) 47 | 48 | var tasks []stream_chat.ImportTask 49 | require.NoError(t, json.Unmarshal(cmd.OutOrStdout().(*bytes.Buffer).Bytes(), &tasks)) 50 | 51 | require.Len(t, tasks, 1) 52 | } 53 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_upload-image.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat upload-image 2 | 3 | Upload an image 4 | 5 | ### Synopsis 6 | 7 | Stream supported image types are: image/bmp, image/gif, image/jpeg, image/png, 8 | image/webp, image/heic, image/heic-sequence, image/heif, image/heif-sequence, 9 | image/svg+xml. 10 | You can set a more restrictive list for your application if needed. 11 | The maximum file size is 100MB. 12 | Stream will allow any file extension. If you want to be more restrictive 13 | for an application, this is can be set via API or by logging into your dashboard. 14 | 15 | 16 | ``` 17 | stream-cli chat upload-image --channel-type [channel-type] --channel-id [channel-id] --user-id [user-id] --file [file] --content-type [content-type] [flags] 18 | ``` 19 | 20 | ### Examples 21 | 22 | ``` 23 | # Uploads an image to 'redteam' channel of 'messaging' channel type 24 | $ stream-cli chat upload-image --channel-type messaging --channel-id redteam --user-id "user-1" --file "./picture.png" --content-type "image/png" 25 | 26 | ``` 27 | 28 | ### Options 29 | 30 | ``` 31 | -i, --channel-id string [required] Channel id to interact with 32 | -t, --channel-type string [required] Channel type to interact with 33 | -f, --file string [required] Image file path 34 | -h, --help help for upload-image 35 | -u, --user-id string [required] User id 36 | ``` 37 | 38 | ### Options inherited from parent commands 39 | 40 | ``` 41 | --app string [optional] Application name to use as it's defined in the configuration file 42 | --config string [optional] Explicit config file path 43 | ``` 44 | 45 | ### SEE ALSO 46 | 47 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 48 | 49 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-channels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "userA", 6 | "role": "user" 7 | } 8 | }, 9 | { 10 | "type": "channel", 11 | "item": { 12 | "type": "messaging", 13 | "created_by": "userA" 14 | } 15 | }, 16 | { 17 | "type": "channel", 18 | "item": { 19 | "type": "messaging", 20 | "id": "channelA", 21 | "member_ids": ["userA"], 22 | "created_by": "userA" 23 | } 24 | }, 25 | { 26 | "type": "channel", 27 | "item": { 28 | "type": "messaging", 29 | "id": "id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long", 30 | "created_by": "userA" 31 | } 32 | }, 33 | { 34 | "type": "channel", 35 | "item": { 36 | "id": "channelA", 37 | "created_by": "userA" 38 | } 39 | }, 40 | { 41 | "type": "channel", 42 | "item": { 43 | "type": "messaging", 44 | "id": "channelA@abc", 45 | "created_by": "userA" 46 | } 47 | }, 48 | { 49 | "type": "channel", 50 | "item": { 51 | "type": "messaging", 52 | "id": "channelA", 53 | "created_by": "" 54 | } 55 | }, 56 | { 57 | "type": "channel", 58 | "item": { 59 | "type": "messaging", 60 | "id": "channelA", 61 | "created_by": "userA", 62 | "cid": "messaging:channelA" 63 | } 64 | }, 65 | { 66 | "type": "channel", 67 | "item": { 68 | "type": "livestream", 69 | "member_ids": ["userA"], 70 | "created_by": "userA" 71 | } 72 | }, 73 | { 74 | "type": "channel", 75 | "item": { 76 | "type": "messaging", 77 | "id": "channelB", 78 | "created_by": "userB" 79 | } 80 | } 81 | ] -------------------------------------------------------------------------------- /docs/stream-cli_chat_ban-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat ban-user 2 | 3 | Ban a user 4 | 5 | ### Synopsis 6 | 7 | Users can be banned from an app entirely. 8 | When a user is banned, they will not be allowed to post messages until 9 | the ban is removed or expired but will be able to connect to Chat 10 | and to channels as before. 11 | 12 | Channel watchers cannot be banned. 13 | 14 | 15 | ``` 16 | stream-cli chat ban-user --target-user-id [user-id] --banned-by-id [user-id] --reason [reason] --expiration [expiration-in-minutes] [flags] 17 | ``` 18 | 19 | ### Examples 20 | 21 | ``` 22 | # 'admin-user-1' bans user 'joe' 23 | $ stream-cli chat ban-user --target-user-id joe --banned-by admin-user-1 24 | 25 | # 'admin-user-2' bans user 'mike' with a reason 26 | $ stream-cli chat ban-user --target-user-id mike --banned-by admin-user-2 --reason "Bad behavior" 27 | 28 | # 'admin-user-3' bans user 'jill' with a reason for 1 hour 29 | $ stream-cli chat ban-user --target-user-id jill --banned-by admin-user-3 --expiration 60 30 | 31 | ``` 32 | 33 | ### Options 34 | 35 | ``` 36 | -b, --banned-by-id string [required] ID of the user who is performing the ban 37 | -e, --expiration int [optional] Number of minutes until the ban expires. Defaults to forever. 38 | -h, --help help for ban-user 39 | -r, --reason string [optional] Reason for the ban 40 | -t, --target-user-id string [required] ID of the user to ban 41 | ``` 42 | 43 | ### Options inherited from parent commands 44 | 45 | ``` 46 | --app string [optional] Application name to use as it's defined in the configuration file 47 | --config string [optional] Explicit config file path 48 | ``` 49 | 50 | ### SEE ALSO 51 | 52 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 53 | 54 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_send-message.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat send-message 2 | 3 | Send a message to a channel 4 | 5 | ``` 6 | stream-cli chat send-message --channel-type [channel-type] --channel-id [channel-id] --text [text] --user [user-id] [flags] 7 | ``` 8 | 9 | ### Examples 10 | 11 | ``` 12 | # Sends a message to 'redteam' channel of 'messaging' channel type 13 | $ stream-cli chat send-message --channel-type messaging --channel-id redteam --text "Hello World!" --user "user-1" 14 | 15 | # Sends a message to 'redteam' channel of 'livestream' channel type with an URL attachment 16 | $ stream-cli chat send-message --channel-type livestream --channel-id redteam --attachment "https://example.com/image.png" --text "Hello World!" --user "user-1" 17 | 18 | # You can also send a message with a local file attachment 19 | # In this scenario, we'll upload the file first then send the message 20 | $ stream-cli chat send-message --channel-type livestream --channel-id redteam --attachment "./image.png" --text "Hello World!" --user "user-1" 21 | 22 | ``` 23 | 24 | ### Options 25 | 26 | ``` 27 | -a, --attachment string [optional] URL of the an attachment 28 | -i, --channel-id string [required] Channel id 29 | -t, --channel-type string [required] Channel type such as 'messaging' or 'livestream' 30 | -h, --help help for send-message 31 | --text string [required] Text of the message 32 | -u, --user string [required] User id 33 | ``` 34 | 35 | ### Options inherited from parent commands 36 | 37 | ``` 38 | --app string [optional] Application name to use as it's defined in the configuration file 39 | --config string [optional] Explicit config file path 40 | ``` 41 | 42 | ### SEE ALSO 43 | 44 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 45 | 46 | -------------------------------------------------------------------------------- /.github/workflows/initiate_release.yml: -------------------------------------------------------------------------------- 1 | name: Create release PR 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | version: 7 | description: "The new version number with 'v' prefix. Example: v1.40.1" 8 | required: true 9 | 10 | jobs: 11 | init_release: 12 | name: 🚀 Create release PR 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | fetch-depth: 0 # gives the changelog generator access to all previous commits 18 | 19 | - name: Update CHANGELOG.md, version.go and push release branch 20 | env: 21 | VERSION: ${{ github.event.inputs.version }} 22 | run: | 23 | npx --yes standard-version@9.3.2 --release-as "$VERSION" --skip.tag --skip.commit --tag-prefix=v 24 | git config --global user.name 'github-actions' 25 | git config --global user.email 'release@getstream.io' 26 | git checkout -q -b "release-$VERSION" 27 | git commit -am "chore(release): $VERSION" 28 | git push -q -u origin "release-$VERSION" 29 | 30 | - name: Get changelog diff 31 | uses: actions/github-script@v6 32 | with: 33 | script: | 34 | const get_change_log_diff = require('./scripts/get_changelog_diff.js') 35 | core.exportVariable('CHANGELOG', get_change_log_diff()) 36 | 37 | - name: Open pull request 38 | env: 39 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 40 | run: | 41 | gh pr create \ 42 | -t "chore(release): ${{ github.event.inputs.version }}" \ 43 | -b "# :rocket: ${{ github.event.inputs.version }} 44 | Make sure to use squash & merge when merging! 45 | Once this is merged, another job will kick off automatically and publish the package. 46 | # :memo: Changelog 47 | ${{ env.CHANGELOG }}" 48 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/GetStream/stream-cli 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/AlecAivazis/survey/v2 v2.3.4 7 | github.com/GetStream/stream-chat-go/v5 v5.8.1 8 | github.com/MakeNowJust/heredoc v1.0.0 9 | github.com/cheynewallace/tabby v1.1.1 10 | github.com/gizak/termui/v3 v3.1.0 11 | github.com/gorilla/websocket v1.5.0 12 | github.com/spf13/cobra v1.4.0 13 | github.com/spf13/pflag v1.0.5 14 | github.com/spf13/viper v1.11.0 15 | ) 16 | 17 | require ( 18 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/fsnotify/fsnotify v1.5.4 // indirect 21 | github.com/golang-jwt/jwt/v4 v4.4.1 // indirect 22 | github.com/hashicorp/hcl v1.0.0 // indirect 23 | github.com/inconshreveable/mousetrap v1.0.0 // indirect 24 | github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect 25 | github.com/magiconair/properties v1.8.6 // indirect 26 | github.com/mattn/go-colorable v0.1.12 // indirect 27 | github.com/mattn/go-isatty v0.0.14 // indirect 28 | github.com/mattn/go-runewidth v0.0.13 // indirect 29 | github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect 30 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 31 | github.com/mitchellh/mapstructure v1.5.0 // indirect 32 | github.com/nsf/termbox-go v1.1.1 // indirect 33 | github.com/pelletier/go-toml v1.9.5 // indirect 34 | github.com/pelletier/go-toml/v2 v2.0.1 // indirect 35 | github.com/pmezard/go-difflib v1.0.0 // indirect 36 | github.com/rivo/uniseg v0.2.0 // indirect 37 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 38 | github.com/spf13/afero v1.8.2 // indirect 39 | github.com/spf13/cast v1.5.0 // indirect 40 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 41 | github.com/stretchr/testify v1.7.1 42 | github.com/subosito/gotenv v1.2.0 // indirect 43 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect 44 | golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect 45 | golang.org/x/text v0.3.8 // indirect 46 | gopkg.in/ini.v1 v1.66.4 // indirect 47 | gopkg.in/yaml.v2 v2.4.0 // indirect 48 | gopkg.in/yaml.v3 v3.0.0 // indirect 49 | ) 50 | -------------------------------------------------------------------------------- /pkg/cmd/chat/channeltype/channeltype_test.go: -------------------------------------------------------------------------------- 1 | package channeltype 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "testing" 7 | "time" 8 | 9 | "github.com/stretchr/testify/require" 10 | 11 | "github.com/GetStream/stream-cli/test" 12 | ) 13 | 14 | func deleteChannelType(name string) { 15 | c := test.InitClient() 16 | 17 | for i := 0; i < 5; i++ { 18 | _, err := c.DeleteChannelType(context.Background(), name) 19 | if err == nil { 20 | break 21 | } 22 | time.Sleep(time.Second) 23 | } 24 | } 25 | 26 | func TestCreateChannelType(t *testing.T) { 27 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 28 | name := test.RandomString(10) 29 | t.Cleanup(func() { 30 | deleteChannelType(name) 31 | }) 32 | 33 | cmd.SetArgs([]string{"create-channel-type", "-p", "{\"name\":\"" + name + "\"}"}) 34 | _, err := cmd.ExecuteC() 35 | require.NoError(t, err) 36 | } 37 | 38 | func TestUpdateChannelType(t *testing.T) { 39 | t.Skip("Fix this") 40 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 41 | name := test.RandomString(10) 42 | t.Cleanup(func() { 43 | deleteChannelType(name) 44 | }) 45 | 46 | cmd.SetArgs([]string{"create-channel-type", "-p", "{\"name\":\"" + name + "\"}"}) 47 | _, err := cmd.ExecuteC() 48 | require.NoError(t, err) 49 | 50 | cmd.SetArgs([]string{"update-channel-type", "-t", name, "-p", "{\"quotes\": true}"}) 51 | _, err = cmd.ExecuteC() 52 | require.NoError(t, err) 53 | } 54 | 55 | func TestDeleteChannelType(t *testing.T) { 56 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 57 | name := test.RandomString(10) 58 | t.Cleanup(func() { 59 | deleteChannelType(name) 60 | }) 61 | 62 | cmd.SetArgs([]string{"create-channel-type", "-p", "{\"name\":\"" + name + "\"}"}) 63 | _, err := cmd.ExecuteC() 64 | require.NoError(t, err) 65 | 66 | cmd.SetArgs([]string{"delete-channel-type", name}) 67 | _, err = cmd.ExecuteC() 68 | require.NoError(t, err) 69 | } 70 | 71 | func TestListChannelTypes(t *testing.T) { 72 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 73 | 74 | cmd.SetArgs([]string{"list-channel-types"}) 75 | _, err := cmd.ExecuteC() 76 | require.NoError(t, err) 77 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "messaging") 78 | } 79 | -------------------------------------------------------------------------------- /install/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | test_curl_exists() { 4 | if ! curl --version > /dev/null 2>&1; then 5 | echo "curl is required (it's used to download the CLI). Exiting." 6 | exit 1 7 | fi 8 | } 9 | 10 | is_macos() { 11 | [[ "$OSTYPE" == "darwin"* ]] 12 | } 13 | 14 | get_platform() { 15 | linuxOrMac="$(uname -s)" 16 | intelOrArm="$(uname -m)" 17 | echo "${linuxOrMac}_${intelOrArm}" 18 | } 19 | 20 | # We need curl to download from GitHub Releases 21 | test_curl_exists 22 | 23 | # Get the current platform. Such as Linux_x86 or Darwin_arm64 24 | PLATFORM=$(get_platform) 25 | 26 | # Grab the download url for that platform 27 | URL=$(curl -s https://api.github.com/repos/GetStream/stream-cli/releases/latest | grep "$PLATFORM" | cut -d '"' -f 4 | sed '1d') 28 | 29 | # Create a folder for the CLI in ~/.stream-cli 30 | echo " > Ensuring folder exists: ~/.stream-cli" 31 | mkdir -p ~/.stream-cli 32 | 33 | # Download the CLI 34 | echo " > Downloading the tarball from GitHub." 35 | curl -s -L "$URL" -o ~/.stream-cli/stream-cli.tar.gz 36 | 37 | # Extract the tar then remove it 38 | echo " > Extracting the tarball to ~/.stream-cli" 39 | tar -xzf ~/.stream-cli/stream-cli.tar.gz -C ~/.stream-cli 40 | rm ~/.stream-cli/stream-cli.tar.gz 41 | 42 | # If MacOS, we need to trust the binary 43 | if is_macos; then 44 | echo " > In MacOS, the binary needs to be trusted by the system. We use 'xattr -d com.apple.quarantine ~/.stream-cli' command to do that." 45 | echo " > Do you agree to continue? [y/n]" 46 | read -r AGREE 47 | if [ "$AGREE" == "y" ]; then 48 | xattr -d com.apple.quarantine ~/.stream-cli 49 | fi 50 | fi 51 | 52 | echo " > The CLI has been installed in ~/.stream-cli." 53 | # shellcheck disable=SC2016 54 | echo ' > As a last step, add it to your $PATH or create a symbolic link to /usr/local/bin/stream-cli.' 55 | echo " > Do you want to create a symbolic link now? (requires sudo) [y/n]" 56 | 57 | read -r AGREE 58 | if [ "$AGREE" != "y" ]; then 59 | echo " > All done. 🎉 You'll find the CLI at ~/.stream-cli/stream-cli." 60 | exit 0 61 | fi 62 | 63 | sudo ln -f -s ~/.stream-cli/stream-cli /usr/local/bin/stream-cli 64 | 65 | echo " > All done! 🎉 Try 'stream-cli --help'" 66 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/errors.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | const maxNrOfErrors = 25 11 | 12 | type multiError struct { 13 | errors []error 14 | } 15 | 16 | func (e multiError) Error() string { 17 | msgs := make([]string, 0, len(e.errors)) 18 | for i := range e.errors { 19 | msgs = append(msgs, e.errors[i].Error()) 20 | } 21 | return strings.Join(msgs, ",") 22 | } 23 | 24 | func (e *multiError) add(err error) { 25 | if err == nil { 26 | return 27 | } 28 | 29 | var errs *multiError 30 | if errors.As(err, &errs) { 31 | for _, err := range errs.errors { 32 | e.add(err) 33 | } 34 | return 35 | } 36 | 37 | e.errors = append(e.errors, err) 38 | } 39 | 40 | func (e multiError) hasErrors() bool { 41 | return e.len() > 0 42 | } 43 | 44 | func (e multiError) len() int { 45 | return len(e.errors) 46 | } 47 | 48 | type ItemError struct { 49 | item rawItem 50 | offset int64 51 | err error 52 | } 53 | 54 | func (e ItemError) MarshalJSON() ([]byte, error) { 55 | return json.Marshal(fmt.Sprintf("%s (%d)", e.Error(), e.Offset())) 56 | } 57 | 58 | func newItemError(item rawItem, offset int64, err error) *ItemError { 59 | return &ItemError{ 60 | item: item, 61 | offset: offset, 62 | err: err, 63 | } 64 | } 65 | 66 | func (e *ItemError) Error() string { 67 | return e.err.Error() 68 | } 69 | 70 | func (e *ItemError) Offset() int64 { 71 | return e.offset 72 | } 73 | 74 | func (e *ItemError) Item() rawItem { 75 | return e.item 76 | } 77 | 78 | type WrapError struct { 79 | err error 80 | } 81 | 82 | func (e *WrapError) Error() string { 83 | return e.err.Error() 84 | } 85 | 86 | func (e WrapError) MarshalJSON() ([]byte, error) { 87 | return json.Marshal(e.err.Error()) 88 | } 89 | 90 | func newValidationError(err error) error { 91 | return &WrapError{fmt.Errorf("validation error: %w", err)} 92 | } 93 | 94 | func newReferenceError(err error) error { 95 | return &WrapError{fmt.Errorf("reference error: %w", err)} 96 | } 97 | 98 | func newParseError(err error) error { 99 | return &WrapError{fmt.Errorf("parse error: %w", err)} 100 | } 101 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-user.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-user 2 | 3 | Delete a user 4 | 5 | ### Synopsis 6 | 7 | This command deletes a user. If not flags are provided, user and messages will be soft deleted. 8 | 9 | There are 3 additional options that you can provide: 10 | 11 | --hard-delete: If set to true, hard deletes everything related to this user, channels, messages and everything related to it. 12 | --mark-messages-deleted: If set to true, hard deletes all messages related to this user. 13 | --delete-conversations: If set to true, hard deletes all conversations related to this user. 14 | 15 | User deletion is an async operation in the backend. 16 | Once it succeeded, you'll need to use the 'watch' command to see the async task's result. 17 | 18 | 19 | ``` 20 | stream-cli chat delete-user --user [user-id] --hard-delete [true|false] --mark-messages-deleted [true|false] --delete-conversations [true|false] [flags] 21 | ``` 22 | 23 | ### Examples 24 | 25 | ``` 26 | # Soft delete a user with id 'my-user-1' 27 | $ stream-cli chat delete-user --user my-user-1 28 | 29 | # Hard delete a user with id 'my-user-2' 30 | $ stream-cli chat delete-user --user my-user-2 --hard-delete 31 | > Successfully initiated user deletion. Task id: 8d011daa-cbcd-4cba-ad16-701de599873a 32 | 33 | # Watch the async task's result 34 | $ stream-cli chat watch 8d011daa-cbcd-4cba-ad16-701de599873a 35 | > Async operation completed successfully. 36 | 37 | ``` 38 | 39 | ### Options 40 | 41 | ``` 42 | --delete-conversations [optional] Hard delete all conversations related to the user 43 | --hard-delete [optional] Hard delete everything related to this user 44 | -h, --help help for delete-user 45 | --mark-messages-deleted [optional] Hard delete all messages related to the user 46 | -u, --user string [required] Id of the user to delete 47 | ``` 48 | 49 | ### Options inherited from parent commands 50 | 51 | ``` 52 | --app string [optional] Application name to use as it's defined in the configuration file 53 | --config string [optional] Explicit config file path 54 | ``` 55 | 56 | ### SEE ALSO 57 | 58 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 59 | 60 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_upsert-pushprovider.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat upsert-pushprovider 2 | 3 | Create or updates a push provider 4 | 5 | ### Synopsis 6 | 7 | 8 | The "--properties" parameter expects a raw json string that can be 9 | unmarshalled into a stream_chat.PushProvider object on the Go SDK side. 10 | See the example section. 11 | Available properties: 12 | type 13 | name 14 | description 15 | disabled_at 16 | disabled_reason 17 | 18 | apn_auth_key 19 | apn_key_id 20 | apn_team_id 21 | apn_topic 22 | 23 | firebase_notification_template 24 | firebase_apn_template 25 | firebase_credentials 26 | 27 | huawei_app_id 28 | huawei_app_secret 29 | 30 | xiaomi_package_name 31 | xiaomi_app_secret 32 | 33 | 34 | ``` 35 | stream-cli chat upsert-pushprovider --properties [raw-json] [flags] 36 | ``` 37 | 38 | ### Examples 39 | 40 | ``` 41 | # Setting up an APN push provider 42 | $ stream-cli chat upsert-pushprovider --properties "{'type': 'apn', 'name': 'staging', 'apn_auth_key': 'key', 'apn_key_id': 'id', 'apn_topic': 'topic', 'apn_team_id': 'id'}" 43 | 44 | # Setting up a Firebase push provider 45 | $ stream-cli chat upsert-pushprovider --properties "{'type': 'firebase', 'name': 'staging', 'firebase_credentials': 'credentials'}" 46 | 47 | # Setting up a Huawei push provider 48 | $ stream-cli chat upsert-pushprovider --properties "{'type': 'huawei', 'name': 'staging', 'huawei_app_id': 'id', 'huawei_app_secret': 'secret'}" 49 | 50 | # Setting up a Xiaomi push provider 51 | $ stream-cli chat upsert-pushprovider --properties "{'type': 'xiaomi', 'name': 'staging', 'xiaomi_package_name': 'name', 'xiaomi_app_secret': 'secret'}" 52 | 53 | ``` 54 | 55 | ### Options 56 | 57 | ``` 58 | -h, --help help for upsert-pushprovider 59 | -p, --properties string [required] Raw json properties to send to the backend 60 | ``` 61 | 62 | ### Options inherited from parent commands 63 | 64 | ``` 65 | --app string [optional] Application name to use as it's defined in the configuration file 66 | --config string [optional] Explicit config file path 67 | ``` 68 | 69 | ### SEE ALSO 70 | 71 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 72 | 73 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-devices.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "userA", 6 | "role": "user" 7 | } 8 | }, 9 | { 10 | "type": "device", 11 | "item": { 12 | "id": "id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long", 13 | "user_id": "userA", 14 | "created_at": "2022-01-01T01:01:01Z", 15 | "disabled": false, 16 | "push_provider_type": "firebase" 17 | } 18 | }, 19 | { 20 | "type": "device", 21 | "item": { 22 | "id": "123", 23 | "user_id": "userB", 24 | "created_at": "2022-01-01T01:01:01Z", 25 | "disabled": false, 26 | "push_provider_type": "firebase" 27 | } 28 | }, 29 | { 30 | "type": "device", 31 | "item": { 32 | "user_id": "userA", 33 | "created_at": "2022-01-01T01:01:01Z", 34 | "disabled": false, 35 | "push_provider_type": "firebase" 36 | } 37 | }, 38 | { 39 | "type": "device", 40 | "item": { 41 | "id": "no user ID", 42 | "created_at": "2022-01-01T01:01:01Z", 43 | "disabled": false, 44 | "push_provider_type": "firebase" 45 | } 46 | }, 47 | { 48 | "type": "device", 49 | "item": { 50 | "id": "123", 51 | "user_id": "userA", 52 | "created_at": "2022-01-01T01:01:01Z", 53 | "disabled": false, 54 | "push_provider_type": "unknown_provider" 55 | } 56 | }, 57 | { 58 | "type": "device", 59 | "item": { 60 | "id": "123", 61 | "user_id": "userA", 62 | "disabled": false, 63 | "push_provider_type": "unknown_provider" 64 | } 65 | }, 66 | { 67 | "type": "device", 68 | "item": { 69 | "id": "duplicate", 70 | "user_id": "userA", 71 | "created_at": "2022-01-01T01:01:01Z", 72 | "disabled": false, 73 | "push_provider_type": "apn" 74 | } 75 | }, 76 | { 77 | "type": "device", 78 | "item": { 79 | "id": "duplicate", 80 | "user_id": "userA", 81 | "created_at": "2022-01-01T01:01:01Z", 82 | "disabled": false, 83 | "push_provider_type": "xiaomi" 84 | } 85 | } 86 | ] 87 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/validator.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "io" 5 | "regexp" 6 | 7 | streamchat "github.com/GetStream/stream-chat-go/v5" 8 | ) 9 | 10 | type Results struct { 11 | Stats map[string]int 12 | Errors []error 13 | } 14 | 15 | func (r *Results) HasErrors() bool { 16 | return len(r.Errors) > 0 17 | } 18 | 19 | func newResults(stats map[string]int, errs *multiError) *Results { 20 | return &Results{ 21 | Stats: stats, 22 | Errors: errs.errors, 23 | } 24 | } 25 | 26 | type Options func(*option) 27 | 28 | type option struct { 29 | lighterValidationChannelID bool 30 | } 31 | 32 | func LighterValidationChannelID() Options { 33 | return func(o *option) { 34 | o.lighterValidationChannelID = true 35 | } 36 | } 37 | 38 | type Validator struct { 39 | decoder *Decoder 40 | index *index 41 | } 42 | 43 | func New(r io.ReadSeeker, roles []*streamchat.Role, channelTypes channelTypeMap, options ...Options) *Validator { 44 | roleMap := make(roleMap, len(roles)) 45 | for _, role := range roles { 46 | roleMap[role.Name] = role 47 | } 48 | 49 | opt := &option{} 50 | for _, o := range options { 51 | o(opt) 52 | } 53 | 54 | if opt.lighterValidationChannelID { 55 | // allows to pass ! in channel ID 56 | validChannelIDRe = regexp.MustCompile(`^[\w!-]*$`) 57 | } else { 58 | validChannelIDRe = regexp.MustCompile(`^[\w-]*$`) 59 | } 60 | 61 | return &Validator{ 62 | decoder: NewDecoder(r), 63 | index: newIndex(roleMap, channelTypes), 64 | } 65 | } 66 | 67 | func (v *Validator) Validate() *Results { 68 | errs := new(multiError) 69 | 70 | // first pass: validate item field and index items for reference validation 71 | errs.add(v.decoder.Items(func(item Item) error { 72 | if err := item.validateFields(); err != nil { 73 | return newValidationError(err) 74 | } 75 | return item.index(v.index) 76 | })) 77 | if errs.len() > maxNrOfErrors { 78 | return newResults(v.index.stats(), errs) 79 | } 80 | 81 | // second pass: reference validation 82 | errs.add(v.decoder.Items(func(item Item) error { 83 | if err := item.validateReferences(v.index); err != nil { 84 | return newReferenceError(err) 85 | } 86 | return nil 87 | })) 88 | if errs.len() > maxNrOfErrors { 89 | return newResults(v.index.stats(), errs) 90 | } 91 | 92 | // last pass: validate any channel members that are unaccounted for 93 | errs.add(v.index.validateChannelMembers()) 94 | 95 | return newResults(v.index.stats(), errs) 96 | } 97 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | pull_request: 5 | types: [closed] 6 | branches: 7 | - master 8 | - main 9 | 10 | permissions: 11 | contents: write 12 | 13 | jobs: 14 | Release: 15 | name: 🚀 Release 16 | if: github.event.pull_request.merged && startsWith(github.head_ref, 'release-') 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | with: 21 | fetch-depth: 0 22 | 23 | - uses: actions/github-script@v5 24 | with: 25 | script: | 26 | // Getting the release version from the PR source branch 27 | // Source branch looks like this: release-1.0.0 28 | const version = context.payload.pull_request.head.ref.split('-')[1] 29 | core.exportVariable('VERSION', version) 30 | 31 | const get_change_log_diff = require('./scripts/get_changelog_diff.js') 32 | const fs = require('fs'); 33 | fs.writeFileSync("latest_changes.txt", get_change_log_diff()); 34 | 35 | - name: Set up Go 36 | uses: actions/setup-go@v3 37 | with: 38 | go-version: '1.19' 39 | 40 | - run: | 41 | git tag "${{ env.VERSION }}" 42 | git push --tags 43 | 44 | - name: Run GoReleaser 45 | uses: goreleaser/goreleaser-action@v2 46 | with: 47 | distribution: goreleaser 48 | version: latest 49 | args: release --rm-dist --release-notes latest_changes.txt 50 | env: 51 | GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} 52 | GORELEASER_CURRENT_TAG: ${{ env.VERSION }} 53 | 54 | - name: Update docs 55 | env: 56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 57 | run: | 58 | # Update local state, since goreleaser pushes into master 59 | git pull 60 | 61 | git config --global user.name 'GitHub Bot' 62 | git config --global user.email 'cicd@getstream.io' 63 | git checkout -q -b "docs-${{ env.VERSION }}" 64 | 65 | # Generate new Markdown docs 66 | rm -rf ./docs/stream-cli*.md 67 | go run ./cmd/gen-docs --output ./docs 68 | git add ./docs 69 | git commit -m "docs(${{ env.VERSION }}): update markdown docs" 70 | git push -q -u origin "docs-${{ env.VERSION }}" 71 | 72 | # Push to docs branch 73 | gh pr create --fill -t "docs(${{ env.VERSION }}): update markdown docs" 74 | -------------------------------------------------------------------------------- /pkg/cmd/chat/file/file_test.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "bytes" 5 | "image" 6 | "image/png" 7 | "os" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | 13 | "github.com/GetStream/stream-cli/test" 14 | ) 15 | 16 | func TestFileUploadAndDelete(t *testing.T) { 17 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 18 | ch := test.InitChannel(t) 19 | u := test.CreateUser() 20 | 21 | tmpfile, err := os.CreateTemp("", "*.txt") 22 | require.NoError(t, err) 23 | 24 | err = os.WriteFile(tmpfile.Name(), []byte("hello\nworld\n"), 0o644) 25 | require.NoError(t, err) 26 | 27 | t.Cleanup(func() { 28 | _ = tmpfile.Close() 29 | _ = os.Remove(tmpfile.Name()) 30 | test.DeleteUser(u) 31 | test.DeleteChannel(ch) 32 | }) 33 | 34 | cmd.SetArgs([]string{"upload-file", "-t", "messaging", "-i", ch, "-u", u, "--file", tmpfile.Name()}) 35 | _, err = cmd.ExecuteC() 36 | require.NoError(t, err) 37 | stdOut := cmd.OutOrStdout().(*bytes.Buffer).String() 38 | require.Contains(t, stdOut, "Successfully uploaded file") 39 | 40 | // The stdout looks like this: 41 | // Successfully uploaded file: http://example.org/snippet.txt\n 42 | url := strings.Split(stdOut, ": ")[1] 43 | url = strings.TrimSuffix(url, "\n") 44 | cmd.SetArgs([]string{"delete-file", "-t", "messaging", "-i", ch, "-u", u, "--file-url", url}) 45 | _, err = cmd.ExecuteC() 46 | require.NoError(t, err) 47 | } 48 | 49 | func TestImageUploadAndDelete(t *testing.T) { 50 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 51 | ch := test.InitChannel(t) 52 | u := test.CreateUser() 53 | 54 | tmpfile, err := os.CreateTemp("", "*.png") 55 | require.NoError(t, err) 56 | 57 | m := image.NewRGBA(image.Rect(0, 0, 1, 1)) 58 | err = png.Encode(tmpfile, m) 59 | require.NoError(t, err) 60 | 61 | t.Cleanup(func() { 62 | _ = tmpfile.Close() 63 | _ = os.Remove(tmpfile.Name()) 64 | test.DeleteUser(u) 65 | test.DeleteChannel(ch) 66 | }) 67 | 68 | cmd.SetArgs([]string{"upload-image", "-t", "messaging", "-i", ch, "-u", u, "--file", tmpfile.Name()}) 69 | _, err = cmd.ExecuteC() 70 | require.NoError(t, err) 71 | 72 | stdOut := cmd.OutOrStdout().(*bytes.Buffer).String() 73 | require.Contains(t, stdOut, "Successfully uploaded image") 74 | 75 | // The stdout looks like this: 76 | // Successfully uploaded image: http://example.org/image.png\n 77 | url := strings.Split(stdOut, ": ")[1] 78 | url = strings.TrimSuffix(url, "\n") 79 | cmd.SetArgs([]string{"delete-image", "-t", "messaging", "-i", ch, "--image-url", url}) 80 | _, err = cmd.ExecuteC() 81 | require.NoError(t, err) 82 | } 83 | -------------------------------------------------------------------------------- /docs/stream-cli_chat_delete-users.md: -------------------------------------------------------------------------------- 1 | ## stream-cli chat delete-users 2 | 3 | Delete multiple users 4 | 5 | ### Synopsis 6 | 7 | You can delete up to 100 users and optionally all of their channels and 8 | messages using this method. First the users are marked deleted synchronously 9 | so the user will not be directly visible in the API. Then the process deletes 10 | the user and related objects asynchronously by scheduling a task to be handle 11 | by the task worker. 12 | 13 | The delete users response contain a task ID which can be polled using the 14 | get task endpoint to check the status of the deletions. 15 | 16 | Note: when deleting a user with hard delete, it also required hard deletion 17 | for messages and conversations as well! 18 | 19 | 20 | ``` 21 | stream-cli chat delete-users --new-channel-user-id [user-id] --hard-delete-users [true|false] --hard-delete-messages [true|false] --hard-delete-conversations [user-id1] [user-id2] ... [flags] 22 | ``` 23 | 24 | ### Examples 25 | 26 | ``` 27 | # Soft delete users with ids 'my-user-1' and 'my-user-2' 28 | $ stream-cli chat delete-users my-user-1 my-user-2 29 | > Successfully initiated user deletion. Task id: bf1c2d1b-04d6-4e67-873c-5b3ade478b0a 30 | # Waiting for it to succeed 31 | $ stream-cli chat watch bf1c2d1b-04d6-4e67-873c-5b3ade478b0a 32 | > Async operation completed successfully. 33 | 34 | # Hard delete users with ids 'my-user-3' and 'my-user-4' 35 | $ stream-cli chat delete-users --hard-delete-users --hard-delete-messages --hard-delete-conversations my-user-3 my-user-4 36 | > Successfully initiated user deletion. Task id: 71516d9a-0764-4aa8-b017-8d2a99748e16 37 | 38 | # Waiting for it to succeed 39 | $ stream-cli chat watch 71516d9a-0764-4aa8-b017-8d2a99748e16 40 | > Async operation completed successfully. 41 | 42 | ``` 43 | 44 | ### Options 45 | 46 | ``` 47 | --hard-delete-conversations [optional] Hard delete all conversations related to the users 48 | --hard-delete-messages [optional] Hard delete all messages related to the users 49 | --hard-delete-users [optional] Hard delete everything related to the users 50 | -h, --help help for delete-users 51 | --new-channel-owner-id string [optional] Channels owned by hard-deleted users will be transferred to this userID 52 | ``` 53 | 54 | ### Options inherited from parent commands 55 | 56 | ``` 57 | --app string [optional] Application name to use as it's defined in the configuration file 58 | --config string [optional] Explicit config file path 59 | ``` 60 | 61 | ### SEE ALSO 62 | 63 | * [stream-cli chat](stream-cli_chat.md) - Allows you to interact with your Chat applications 64 | 65 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-members.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "userA", 6 | "role": "user" 7 | } 8 | }, 9 | { 10 | "type": "user", 11 | "item": { 12 | "id": "userB", 13 | "role": "user" 14 | } 15 | }, 16 | { 17 | "type": "user", 18 | "item": { 19 | "id": "userC", 20 | "role": "user" 21 | } 22 | }, 23 | { 24 | "type": "channel", 25 | "item": { 26 | "type": "messaging", 27 | "id": "channelA", 28 | "created_by": "userA" 29 | } 30 | }, 31 | { 32 | "type": "channel", 33 | "item": { 34 | "type": "messaging", 35 | "member_ids": [ 36 | "userA" 37 | ], 38 | "created_by": "userA" 39 | } 40 | }, 41 | { 42 | "type": "member", 43 | "item": { 44 | "channel_type": "messaging", 45 | "channel_id": "channelA" 46 | } 47 | }, 48 | { 49 | "type": "member", 50 | "item": { 51 | "channel_type": "messaging", 52 | "channel_id": "channelA", 53 | "user_id": "userA" 54 | } 55 | }, 56 | { 57 | "type": "member", 58 | "item": { 59 | "channel_type": "messaging", 60 | "channel_member_ids": [ 61 | "userA" 62 | ], 63 | "user_id": "userA" 64 | } 65 | }, 66 | { 67 | "type": "member", 68 | "item": { 69 | "channel_id": "channelA", 70 | "user_id": "userA" 71 | } 72 | }, 73 | { 74 | "type": "member", 75 | "item": { 76 | "channel_type": "messaging", 77 | "user_id": "userA" 78 | } 79 | }, 80 | { 81 | "type": "channel", 82 | "item": { 83 | "type": "messaging", 84 | "id": "channelB", 85 | "created_by": "userA", 86 | "team": "teamB" 87 | } 88 | }, 89 | { 90 | "type": "member", 91 | "item": { 92 | "channel_type": "messaging", 93 | "channel_id": "channelB", 94 | "user_id": "userA" 95 | } 96 | }, 97 | { 98 | "type": "channel", 99 | "item": { 100 | "type": "messaging", 101 | "created_by": "userA", 102 | "member_ids": [ 103 | "userA", 104 | "userB" 105 | ] 106 | } 107 | }, 108 | { 109 | "type": "member", 110 | "item": { 111 | "channel_type": "messaging", 112 | "channel_member_ids": [ 113 | "userA", 114 | "userB" 115 | ], 116 | "user_id": "userA" 117 | } 118 | }, 119 | { 120 | "type": "member", 121 | "item": { 122 | "channel_type": "messaging", 123 | "channel_member_ids": [ 124 | "userA", 125 | "userB" 126 | ], 127 | "user_id": "userC" 128 | } 129 | } 130 | ] -------------------------------------------------------------------------------- /test/helpers.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "math/rand" 7 | "os" 8 | "testing" 9 | "time" 10 | 11 | stream "github.com/GetStream/stream-chat-go/v5" 12 | "github.com/spf13/cobra" 13 | "github.com/spf13/viper" 14 | 15 | "github.com/GetStream/stream-cli/pkg/config" 16 | ) 17 | 18 | func prepareViperConfig() { 19 | viper.Set("default", "default_app") 20 | viper.Set("apps", []config.App{ 21 | { 22 | Name: "default_app", 23 | AccessKey: os.Getenv("STREAM_KEY"), 24 | AccessSecretKey: os.Getenv("STREAM_SECRET"), 25 | ChatURL: config.DefaultChatEdgeURL, 26 | }, 27 | }) 28 | } 29 | 30 | func GetRootCmdWithSubCommands(c ...*cobra.Command) *cobra.Command { 31 | prepareViperConfig() 32 | 33 | rootCmd := &cobra.Command{} 34 | rootCmd.PersistentFlags().String("app", "", "app name") 35 | rootCmd.AddCommand(c...) 36 | rootCmd.SetIn(&bytes.Buffer{}) 37 | rootCmd.SetOut(&bytes.Buffer{}) 38 | rootCmd.SetErr(&bytes.Buffer{}) 39 | 40 | return rootCmd 41 | } 42 | 43 | func InitClient() *stream.Client { 44 | c, err := stream.NewClientFromEnvVars() 45 | if err != nil { 46 | panic(err) 47 | } 48 | return c 49 | } 50 | 51 | func InitChannel(t *testing.T) string { 52 | name := RandomString(10) 53 | c := InitClient() 54 | _, _ = c.CreateChannel(context.Background(), "messaging", name, "userid", nil) 55 | return name 56 | } 57 | 58 | func DeleteChannel(id string) { 59 | c := InitClient() 60 | _, _ = c.DeleteChannels(context.Background(), []string{"messaging:" + id}, true) 61 | } 62 | 63 | func CreateUser() string { 64 | c := InitClient() 65 | id := RandomString(10) 66 | _, _ = c.UpsertUser(context.Background(), &stream.User{ID: id}) 67 | return id 68 | } 69 | 70 | func DeleteUser(id string) { 71 | c := InitClient() 72 | _, _ = c.DeleteUser(context.Background(), 73 | id, 74 | stream.DeleteUserWithHardDelete(), 75 | stream.DeleteUserWithDeleteConversations()) 76 | } 77 | 78 | func CreateMessage(channelID, userID string) string { 79 | return CreateMessageWithText(channelID, userID, RandomString(10)) 80 | } 81 | 82 | func CreateMessageWithText(channelID, userID, text string) string { 83 | c := InitClient() 84 | msg, _ := c.Channel("messaging", channelID).SendMessage(context.Background(), &stream.Message{Text: text}, userID) 85 | return msg.Message.ID 86 | } 87 | 88 | func DeleteMessage(id string) { 89 | c := InitClient() 90 | _, _ = c.HardDeleteMessage(context.Background(), id) 91 | } 92 | 93 | func RandomString(n int) string { 94 | rand.Seed(time.Now().UnixNano()) 95 | bytes := make([]byte, n) 96 | for i := 0; i < n; i++ { 97 | bytes[i] = byte(65 + rand.Intn(25)) // A=65 and Z = 65+25 98 | } 99 | return string(bytes) 100 | } 101 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/invalid-messages.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "userA", 6 | "role": "user" 7 | } 8 | }, 9 | { 10 | "type": "channel", 11 | "item": { 12 | "type": "messaging", 13 | "id": "channelA", 14 | "created_by": "userA" 15 | } 16 | }, 17 | { 18 | "type": "member", 19 | "item": { 20 | "channel_type": "messaging", 21 | "channel_id": "channelA", 22 | "user_id": "userA" 23 | } 24 | }, 25 | { 26 | "type": "message", 27 | "item": { 28 | "id": "id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long-id-too-long", 29 | "channel_type": "messaging", 30 | "channel_id": "channelA", 31 | "user": "userA", 32 | "text": "hi!" 33 | } 34 | }, 35 | { 36 | "type": "message", 37 | "item": { 38 | "id": "messageA", 39 | "channel_id": "channelA", 40 | "user": "userA", 41 | "text": "hi!" 42 | } 43 | }, 44 | { 45 | "type": "message", 46 | "item": { 47 | "id": "messageA", 48 | "channel_type": "messaging", 49 | "user": "userA", 50 | "text": "hi!" 51 | } 52 | }, 53 | { 54 | "type": "message", 55 | "item": { 56 | "id": "messageA", 57 | "channel_type": "messaging", 58 | "channel_id": "channelA", 59 | "text": "hi!" 60 | } 61 | }, 62 | { 63 | "type": "message", 64 | "item": { 65 | "id": "messageA", 66 | "channel_type": "messaging", 67 | "channel_id": "channelA", 68 | "user": "userA", 69 | "text": "hi!", 70 | "type": "error" 71 | } 72 | }, 73 | { 74 | "type": "message", 75 | "item": { 76 | "id": "messageA", 77 | "channel_type": "messaging", 78 | "channel_id": "channelA", 79 | "user": "userA", 80 | "text": "hi!", 81 | "type": "deleted" 82 | } 83 | }, 84 | { 85 | "type": "message", 86 | "item": { 87 | "id": "messageA", 88 | "channel_type": "messaging", 89 | "channel_id": "channelA", 90 | "user": "userA", 91 | "text": "hi!", 92 | "deleted_at": "2022-02-14T12:34:30+00:00" 93 | } 94 | }, 95 | { 96 | "type": "message", 97 | "item": { 98 | "id": "thread", 99 | "channel_type": "messaging", 100 | "channel_id": "channelA", 101 | "user": "userA", 102 | "text": "reply!", 103 | "parent_id": "parentID" 104 | } 105 | }, 106 | { 107 | "type": "channel", 108 | "item": { 109 | "type": "messaging", 110 | "member_ids": [ 111 | "userA" 112 | ], 113 | "created_by": "userA" 114 | } 115 | }, 116 | { 117 | "type": "member", 118 | "item": { 119 | "channel_type": "messaging", 120 | "channel_member_ids": [ 121 | "userA" 122 | ], 123 | "user_id": "userA" 124 | } 125 | } 126 | ] -------------------------------------------------------------------------------- /pkg/utils/printer.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | 8 | ui "github.com/gizak/termui/v3" 9 | "github.com/gizak/termui/v3/widgets" 10 | "github.com/spf13/cobra" 11 | ) 12 | 13 | func PrintObject(cmd *cobra.Command, object interface{}) error { 14 | format, err := cmd.Flags().GetString("output-format") 15 | if err != nil { 16 | return err 17 | } 18 | 19 | switch format { 20 | case "json": 21 | return printJSONObject(cmd, object) 22 | case "tree": 23 | return printUIObject(cmd, object) 24 | default: 25 | return fmt.Errorf("unknown output format: %s", format) 26 | } 27 | } 28 | 29 | func printJSONObject(cmd *cobra.Command, object interface{}) error { 30 | b, err := json.MarshalIndent(object, "", " ") 31 | if err != nil { 32 | return err 33 | } 34 | 35 | cmd.Println(string(b)) 36 | 37 | return nil 38 | } 39 | 40 | func printUIObject(cmd *cobra.Command, object interface{}) error { 41 | var asMap map[string]interface{} 42 | b, err := json.Marshal(object) 43 | if err != nil { 44 | return err 45 | } 46 | err = json.Unmarshal(b, &asMap) 47 | if err != nil { 48 | return err 49 | } 50 | 51 | _, ok := asMap["ratelimit"] 52 | if ok { 53 | delete(asMap, "ratelimit") 54 | } 55 | 56 | rootNode := &widgets.TreeNode{Nodes: []*widgets.TreeNode{}} 57 | for k, v := range asMap { 58 | addNodesRecursive(rootNode, k+": ", v) 59 | } 60 | 61 | if err := ui.Init(); err != nil { 62 | return err 63 | } 64 | defer ui.Close() 65 | 66 | renderUI(rootNode) 67 | return nil 68 | } 69 | 70 | type nodeValue string 71 | 72 | func (nv nodeValue) String() string { 73 | return string(nv) 74 | } 75 | 76 | func addNodesRecursive(parentNode *widgets.TreeNode, prefix string, value interface{}) { 77 | node := &widgets.TreeNode{Value: nodeValue(prefix), Nodes: []*widgets.TreeNode{}} 78 | 79 | switch v := value.(type) { 80 | case map[string]interface{}: 81 | for k, val := range v { 82 | addNodesRecursive(node, k+": ", val) 83 | } 84 | case []interface{}: 85 | for i, v := range v { 86 | addNodesRecursive(node, fmt.Sprintf("[%v] ", i), v) 87 | } 88 | case bool: 89 | node = &widgets.TreeNode{Value: nodeValue(prefix + strconv.FormatBool(v))} 90 | case string: 91 | if v == "" { 92 | v = "\"\"" 93 | } 94 | 95 | node = &widgets.TreeNode{Value: nodeValue(prefix + v)} 96 | case float64: 97 | node = &widgets.TreeNode{Value: nodeValue(prefix + strconv.FormatFloat(v, 'f', -1, 64))} 98 | default: 99 | node = &widgets.TreeNode{Value: nodeValue(prefix + "null")} 100 | } 101 | 102 | parentNode.Nodes = append(parentNode.Nodes, node) 103 | } 104 | 105 | func renderUI(rootNode *widgets.TreeNode) { 106 | // First section: instructions 107 | p := widgets.NewParagraph() 108 | p.Text = `- Press [q] or [Ctrl+C] to quit 109 | - Use [Arrow-Keys] to navigate 110 | - Press [Enter] to expand/collapse 111 | ` 112 | x, y := ui.TerminalDimensions() 113 | p.SetRect(0, 0, x, 5) 114 | p.TextStyle.Fg = ui.ColorWhite 115 | p.BorderStyle.Fg = ui.ColorCyan 116 | 117 | // Second section: the tree 118 | l := widgets.NewTree() 119 | l.TextStyle = ui.NewStyle(ui.ColorYellow) 120 | l.SetNodes(rootNode.Nodes) 121 | l.CollapseAll() 122 | l.SetRect(0, 5, x, y) 123 | 124 | // Printing both sections 125 | ui.Render(p, l) 126 | 127 | uiEvents := ui.PollEvents() 128 | for { 129 | e := <-uiEvents 130 | switch e.ID { 131 | case "q", "": 132 | return 133 | case "": 134 | l.ScrollDown() 135 | case "": 136 | l.ScrollUp() 137 | case "": 138 | l.ToggleExpand() 139 | case "": 140 | x, y := ui.TerminalDimensions() 141 | l.SetRect(0, 0, x, y) 142 | } 143 | 144 | ui.Render(l) 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/index_test.go: -------------------------------------------------------------------------------- 1 | package validator 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func Test_index_AddUser(t *testing.T) { 11 | i := newIndex(nil, nil) 12 | 13 | require.Zero(t, i.stats()["users"]) 14 | require.NoError(t, i.addUser("john", nil)) 15 | require.Equal(t, 1, i.stats()["users"]) 16 | require.Error(t, i.addUser("john", nil)) 17 | require.NoError(t, i.addUser("paul", nil)) 18 | require.Equal(t, 2, i.stats()["users"]) 19 | } 20 | 21 | func Test_getChannelID(t *testing.T) { 22 | channelID, isDistinct := getChannelID("xyz", nil) 23 | require.Equal(t, "xyz", channelID) 24 | require.False(t, isDistinct) 25 | 26 | channelID, isDistinct = getChannelID("", []string{"john", "paul"}) 27 | require.True(t, strings.HasPrefix(channelID, DistinctChannelPrefix)) 28 | require.True(t, isDistinct) 29 | } 30 | 31 | func Test_index_addChannel(t *testing.T) { 32 | i := newIndex(nil, nil) 33 | 34 | require.Zero(t, i.stats()["channels"]) 35 | require.NoError(t, i.addChannel("messaging", "abc", nil, "")) 36 | require.Equal(t, 1, i.stats()["channels"]) 37 | require.Error(t, i.addChannel("messaging", "abc", nil, "")) 38 | require.NoError(t, i.addChannel("messaging", "", []string{"john", "paul"}, "")) 39 | require.Equal(t, 2, i.stats()["channels"]) 40 | require.Error(t, i.addChannel("messaging", "", []string{"john", "paul"}, "")) 41 | } 42 | 43 | func Test_index_addMember(t *testing.T) { 44 | i := newIndex(nil, nil) 45 | 46 | require.Zero(t, i.stats()["members"]) 47 | require.NoError(t, i.addMember("messaging", "abc", nil, "john")) 48 | require.Equal(t, 1, i.stats()["members"]) 49 | require.Error(t, i.addMember("messaging", "abc", nil, "john")) 50 | require.NoError(t, i.addMember("messaging", "", []string{"john", "paul"}, "paul")) 51 | require.Equal(t, 2, i.stats()["members"]) 52 | require.Error(t, i.addMember("messaging", "", []string{"john", "paul"}, "paul")) 53 | } 54 | 55 | func Test_index_addMessage(t *testing.T) { 56 | i := newIndex(nil, nil) 57 | 58 | require.Zero(t, i.stats()["messages"]) 59 | require.NoError(t, i.addMessage("msg1", "")) 60 | require.Equal(t, 1, i.stats()["messages"]) 61 | require.Error(t, i.addMessage("msg1", "")) 62 | require.NoError(t, i.addMessage("msg2", "")) 63 | require.Equal(t, 2, i.stats()["messages"]) 64 | } 65 | 66 | func Test_index_addReaction(t *testing.T) { 67 | i := newIndex(nil, nil) 68 | 69 | require.Zero(t, i.stats()["reactions"]) 70 | require.NoError(t, i.addReaction("msg1", "like", "john")) 71 | require.Equal(t, 1, i.stats()["reactions"]) 72 | require.Error(t, i.addReaction("msg1", "like", "john")) 73 | require.NoError(t, i.addReaction("msg1", "like", "paul")) 74 | require.Equal(t, 2, i.stats()["reactions"]) 75 | } 76 | 77 | func Test_index_channelTypeExist(t *testing.T) { 78 | i := newIndex(nil, channelTypeMap{"messaging": nil}) 79 | 80 | require.True(t, i.channelTypeExist("messaging")) 81 | require.False(t, i.channelTypeExist("livestream")) 82 | } 83 | 84 | func Test_index_isReply(t *testing.T) { 85 | i := newIndex(nil, nil) 86 | 87 | require.NoError(t, i.addMessage("msg1", "")) 88 | require.NoError(t, i.addMessage("msg2", "msg1")) 89 | require.True(t, i.isReply("msg2")) 90 | require.False(t, i.isReply("msg1")) 91 | } 92 | 93 | func Test_index_roleExist(t *testing.T) { 94 | i := newIndex(roleMap{"moderator": nil}, nil) 95 | 96 | require.True(t, i.roleExist("moderator")) 97 | require.False(t, i.roleExist("admin")) 98 | } 99 | 100 | func Test_index_sameTeam(t *testing.T) { 101 | i := newIndex(nil, nil) 102 | 103 | require.NoError(t, i.addUser("john", []string{"the beatles"})) 104 | require.NoError(t, i.addUser("mick", []string{"the rolling stones"})) 105 | require.NoError(t, i.addChannel("messaging", "liverpool", nil, "the beatles")) 106 | require.NoError(t, i.addChannel("messaging", "london", nil, "the rolling stones")) 107 | 108 | require.NoError(t, i.sameTeam("john", "messaging", "liverpool")) 109 | require.Error(t, i.sameTeam("john", "messaging", "london")) 110 | 111 | require.NoError(t, i.sameTeam("mick", "messaging", "london")) 112 | require.Error(t, i.sameTeam("mick", "messaging", "liverpool")) 113 | } 114 | -------------------------------------------------------------------------------- /pkg/cmd/chat/imports/validator/testdata/valid-data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "type": "user", 4 | "item": { 5 | "id": "user1", 6 | "role": "user", 7 | "teams": [ 8 | "teamA" 9 | ], 10 | "foo": "bar", 11 | "push_notifications": { 12 | "disabled": true, 13 | "disabled_until": "2022-12-31T23:59:59Z" 14 | } 15 | } 16 | }, 17 | { 18 | "type": "user", 19 | "item": { 20 | "id": "user2", 21 | "role": "user", 22 | "teams": [ 23 | "teamB" 24 | ], 25 | "push_notifications": { 26 | "disabled": false 27 | } 28 | } 29 | }, 30 | { 31 | "type": "user", 32 | "item": { 33 | "id": "user3", 34 | "role": "user" 35 | } 36 | }, 37 | { 38 | "type": "user", 39 | "item": { 40 | "id": "user4", 41 | "role": "user" 42 | } 43 | }, 44 | { 45 | "type": "channel", 46 | "item": { 47 | "id": "channelA", 48 | "type": "messaging", 49 | "team": "teamA", 50 | "created_by": "user1" 51 | } 52 | }, 53 | { 54 | "type": "member", 55 | "item": { 56 | "channel_id": "channelA", 57 | "channel_type": "messaging", 58 | "user_id": "user1" 59 | } 60 | }, 61 | { 62 | "type": "message", 63 | "item": { 64 | "id": "message1", 65 | "channel_id": "channelA", 66 | "channel_type": "messaging", 67 | "user": "user1", 68 | "text": "hi!" 69 | } 70 | }, 71 | { 72 | "type": "reaction", 73 | "item": { 74 | "message_id": "message1", 75 | "type": "like", 76 | "user_id": "user1", 77 | "created_at": "2022-01-01T01:01:01Z" 78 | } 79 | }, 80 | { 81 | "type": "channel", 82 | "item": { 83 | "id": "channelB", 84 | "type": "messaging", 85 | "team": "teamB", 86 | "created_by": "user2" 87 | } 88 | }, 89 | { 90 | "type": "member", 91 | "item": { 92 | "channel_id": "channelB", 93 | "channel_type": "messaging", 94 | "user_id": "user2" 95 | } 96 | }, 97 | { 98 | "type": "message", 99 | "item": { 100 | "id": "message2", 101 | "channel_id": "channelB", 102 | "channel_type": "messaging", 103 | "user": "user2", 104 | "text": "hi!" 105 | } 106 | }, 107 | { 108 | "type": "reaction", 109 | "item": { 110 | "message_id": "message2", 111 | "type": "like", 112 | "user_id": "user2", 113 | "created_at": "2022-01-01T01:01:01Z" 114 | } 115 | }, 116 | { 117 | "type": "channel", 118 | "item": { 119 | "type": "messaging", 120 | "member_ids": [ 121 | "user3", 122 | "user4" 123 | ], 124 | "created_by": "user3" 125 | } 126 | }, 127 | { 128 | "type": "member", 129 | "item": { 130 | "channel_member_ids": [ 131 | "user3", 132 | "user4" 133 | ], 134 | "channel_type": "messaging", 135 | "user_id": "user3" 136 | } 137 | }, 138 | { 139 | "type": "member", 140 | "item": { 141 | "channel_member_ids": [ 142 | "user3", 143 | "user4" 144 | ], 145 | "channel_type": "messaging", 146 | "user_id": "user4" 147 | } 148 | }, 149 | { 150 | "type": "message", 151 | "item": { 152 | "id": "message3", 153 | "channel_member_ids": [ 154 | "user3", 155 | "user4" 156 | ], 157 | "channel_type": "messaging", 158 | "user": "user3", 159 | "text": "hi!" 160 | } 161 | }, 162 | { 163 | "type": "reaction", 164 | "item": { 165 | "message_id": "message3", 166 | "type": "hate", 167 | "user_id": "user3", 168 | "created_at": "2022-01-01T01:01:01Z" 169 | } 170 | }, 171 | { 172 | "type": "device", 173 | "item": { 174 | "id": "123", 175 | "user_id": "user1", 176 | "created_at": "2022-01-01T01:01:01Z", 177 | "disabled": true, 178 | "disabled_reason": "provider creds are not valid", 179 | "push_provider_type": "xiaomi" 180 | } 181 | }, 182 | { 183 | "type": "device", 184 | "item": { 185 | "id": "456", 186 | "user_id": "user2", 187 | "created_at": "2022-01-01T01:01:01Z", 188 | "disabled": false, 189 | "push_provider_type": "firebase" 190 | } 191 | } 192 | ] 193 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 📚 Documentation 2 | 3 | Stream's Command Line Interface (CLI) makes it easy to create and manage your [Stream](https://getstream.io) apps directly from the terminal. 4 | 5 | > Currently, only Chat is supported; however, the ability to manage Feeds will be coming soon. 6 | 7 | The generated CLI documentation is available [here](./stream-cli.md) - you can learn about all of the available commands there. 8 | 9 | - [🏗 Installation](#-installation) 10 | - [Download the binaries](#download-the-binaries) 11 | - [Homebrew](#homebrew) 12 | - [Compile yourself](#compile-yourself) 13 | - [🚀 Getting Started](#-getting-started) 14 | - [📃 Use cases and examples](#-use-cases-and-examples) 15 | - [🚨 Warning](#-warning) 16 | - [🔨 Syntax](#-syntax) 17 | - [💬 Auto completion](#-auto-completion) 18 | - [🗒 Issues](#-issues) 19 | - [📝 Changelog](#-changelog) 20 | 21 | # 🏗 Installation 22 | 23 | The Stream CLI is written in Go and precompiled into a single binary. It doesn't have any prerequisites. 24 | 25 | ## Download the binaries 26 | You can find the binaries in the [Release section](https://github.com/GetStream/stream-cli/releases) of the repository. We also wrote a short script to download them and put it to your $PATH. 27 | 28 | ### Bash (MacOS and Linux) 29 | ```shell 30 | $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/GetStream/stream-cli/master/install/install.sh)" 31 | ``` 32 | 33 | ### PowerShell (Windows) 34 | ```powershell 35 | $ Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GetStream/stream-cli/master/install/install.ps1" -OutFile "install.ps1"; powershell.exe -ExecutionPolicy Bypass -File ./install.ps1 36 | ``` 37 | ## Homebrew 38 | 39 | For MacOS users, it's also available via Homebrew: 40 | 41 | ```shell 42 | $ brew tap GetStream/stream-cli https://github.com/GetStream/stream-cli 43 | $ brew install stream-cli 44 | ``` 45 | 46 | ## Compile yourself 47 | ```shell 48 | $ git clone git@github.com:GetStream/stream-cli.git 49 | $ cd stream-cli 50 | $ go build ./cmd/stream-cli 51 | $ ./stream-cli --version 52 | stream-cli version 1.0.0 53 | ``` 54 | 55 | # 🚀 Getting Started 56 | 57 | In order to initialize the CLI, it's as simple as: 58 | 59 | ![Stream](./first_config.svg) 60 | 61 | > Note: Your API key and secret can be found on the [Stream Dashboard](https://getstream.io/dashboard) and is specific to your application. 62 | 63 | # 📃 Use cases and examples 64 | 65 | A couple of example use cases can be found [here](./use_cases.md). We've also created a separate documentation [for the import feature](./imports.md). 66 | 67 | # 🚨 Warning 68 | 69 | We purposefully chose the executable name `stream-cli` to avoid conflict with another tool called [`imagemagick`](https://imagemagick.org/index.php) which [already has a `stream` executable](https://github.com/GetStream/stream-cli/issues/33). 70 | 71 | If you do not have `imagemagick` installed, it might be more comfortable to rename `stream-cli` to `stream`. Alternatively you can set up a symbolic link: 72 | 73 | ```shell 74 | $ ln -s ~/Downloads/stream-cli /usr/local/bin/stream 75 | $ stream --version 76 | stream-cli version 1.0.0 77 | ``` 78 | 79 | # 🔨 Syntax 80 | 81 | Basic commands use the following syntax: 82 | 83 | ```shell 84 | $ stream-cli [chat|feeds] [command] [args] [options] 85 | ``` 86 | 87 | Example: 88 | 89 | ```shell 90 | $ stream-cli chat get-channel -t messaging -i redteam 91 | ``` 92 | 93 | The `--help` keyword is available every step of the way. Examples: 94 | 95 | ```shell 96 | $ stream-cli --help 97 | $ stream-cli chat --help 98 | $ stream-cli chat get-channel --help 99 | ``` 100 | 101 | # 💬 Auto completion 102 | We provide autocompletion for the most popular shells (PowerShell, Bash, ZSH, Fish). 103 | 104 | ```shell 105 | $ stream-cli completion --help 106 | ``` 107 | 108 | # 🗒 Issues 109 | 110 | If you're experiencing problems directly related to the CLI, please add an [issue on GitHub](https://github.com/getstream/stream-cli/issues). 111 | 112 | For other issues, submit a [support ticket](https://getstream.io/support). 113 | 114 | # 📝 Changelog 115 | 116 | As with any project, things are always changing. If you're interested in seeing what's changed in the Stream CLI, the changelog for this project can be tracked in the [Release](https://github.com/GetStream/stream-cli/releases) page of the repository. 117 | -------------------------------------------------------------------------------- /pkg/cmd/chat/device/device.go: -------------------------------------------------------------------------------- 1 | package device 2 | 3 | import ( 4 | stream_chat "github.com/GetStream/stream-chat-go/v5" 5 | "github.com/MakeNowJust/heredoc" 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/GetStream/stream-cli/pkg/config" 9 | "github.com/GetStream/stream-cli/pkg/utils" 10 | ) 11 | 12 | func NewCmds() []*cobra.Command { 13 | return []*cobra.Command{ 14 | createCmd(), 15 | listCmd(), 16 | deleteCmd(), 17 | } 18 | } 19 | 20 | func createCmd() *cobra.Command { 21 | cmd := &cobra.Command{ 22 | Use: "create-device --id [device-id] --push-provider [firebase|apn|xiaomi|huawei] --push-provider-name [provider-name] --user-id [user-id]", 23 | Short: "Create a device", 24 | Long: heredoc.Doc(` 25 | Registering a device associates it with a user and tells 26 | the push provider to send new message notifications to the device. 27 | `), 28 | Example: heredoc.Doc(` 29 | # Create a device with a firebase push provider 30 | $ stream-cli chat create-device --id "my-device-id" --push-provider firebase --push-provider-name "my-firebase-project-id" --user-id "my-user-id" 31 | `), 32 | RunE: func(cmd *cobra.Command, args []string) error { 33 | c, err := config.GetConfig(cmd).GetClient(cmd) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | id, _ := cmd.Flags().GetString("id") 39 | provider, _ := cmd.Flags().GetString("push-provider") 40 | providerName, _ := cmd.Flags().GetString("push-provider-name") 41 | userID, _ := cmd.Flags().GetString("user-id") 42 | 43 | d := &stream_chat.Device{ID: id, PushProvider: provider, PushProviderName: providerName, UserID: userID} 44 | 45 | _, err = c.AddDevice(cmd.Context(), d) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | cmd.Println("Successfully created device") 51 | return nil 52 | }, 53 | } 54 | 55 | fl := cmd.Flags() 56 | fl.StringP("id", "i", "", "[required] Device ID") 57 | fl.StringP("push-provider", "p", "", "[required] Push provider. Can be apn, firebase, xiaomi, huawei") 58 | fl.StringP("push-provider-name", "n", "", "[optional] Push provider name") 59 | fl.StringP("user-id", "u", "", "[required] User ID") 60 | _ = cmd.MarkFlagRequired("id") 61 | _ = cmd.MarkFlagRequired("push-provider") 62 | _ = cmd.MarkFlagRequired("user-id") 63 | 64 | return cmd 65 | } 66 | 67 | func listCmd() *cobra.Command { 68 | cmd := &cobra.Command{ 69 | Use: "list-devices --user-id [user-id] --output-format [json|tree]", 70 | Short: "List devices", 71 | Long: "Provides a list of all devices associated with a user.", 72 | Example: heredoc.Doc(` 73 | # List devices for a user 74 | $ stream-cli chat list-devices --user-id "my-user-id" 75 | `), 76 | RunE: func(cmd *cobra.Command, args []string) error { 77 | c, err := config.GetConfig(cmd).GetClient(cmd) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | userID, _ := cmd.Flags().GetString("user-id") 83 | 84 | devices, err := c.GetDevices(cmd.Context(), userID) 85 | if err != nil { 86 | return err 87 | } 88 | 89 | return utils.PrintObject(cmd, devices.Devices) 90 | }, 91 | } 92 | 93 | fl := cmd.Flags() 94 | fl.StringP("user-id", "u", "", "[required] User ID") 95 | fl.StringP("output-format", "o", "json", "[optional] Output format. Can be json or tree") 96 | _ = cmd.MarkFlagRequired("user-id") 97 | 98 | return cmd 99 | } 100 | 101 | func deleteCmd() *cobra.Command { 102 | cmd := &cobra.Command{ 103 | Use: "delete-device --id [device-id] --user-id [user-id]", 104 | Short: "Delete a device", 105 | Long: heredoc.Doc(` 106 | Unregistering a device removes the device from the user 107 | and stops further new message notifications. 108 | `), 109 | Example: heredoc.Doc(` 110 | # Delete "my-device-id" device 111 | $ stream-cli chat delete-device --id "my-device-id" --user-id "my-user-id" 112 | `), 113 | RunE: func(cmd *cobra.Command, args []string) error { 114 | c, err := config.GetConfig(cmd).GetClient(cmd) 115 | if err != nil { 116 | return err 117 | } 118 | 119 | id, _ := cmd.Flags().GetString("id") 120 | userID, _ := cmd.Flags().GetString("user-id") 121 | 122 | _, err = c.DeleteDevice(cmd.Context(), userID, id) 123 | if err != nil { 124 | return err 125 | } 126 | 127 | cmd.Println("Successfully deleted device") 128 | return nil 129 | }, 130 | } 131 | 132 | fl := cmd.Flags() 133 | fl.StringP("id", "i", "", "[required] Device ID to delete") 134 | fl.StringP("user-id", "u", "", "[required] ID of the user who deletes the device") 135 | _ = cmd.MarkFlagRequired("id") 136 | _ = cmd.MarkFlagRequired("user-id") 137 | 138 | return cmd 139 | } 140 | -------------------------------------------------------------------------------- /pkg/cmd/chat/app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | stream "github.com/GetStream/stream-chat-go/v5" 8 | "github.com/MakeNowJust/heredoc" 9 | "github.com/spf13/cobra" 10 | 11 | "github.com/GetStream/stream-cli/pkg/config" 12 | "github.com/GetStream/stream-cli/pkg/utils" 13 | ) 14 | 15 | func NewCmds() []*cobra.Command { 16 | return []*cobra.Command{getCmd(), updateCmd(), revokeAllTokensCmd()} 17 | } 18 | 19 | func getCmd() *cobra.Command { 20 | cmd := &cobra.Command{ 21 | Use: "get-app --output-format [json|tree]", 22 | Short: "Get application settings", 23 | Long: heredoc.Doc(` 24 | Get the application settings. 25 | 26 | Application level settings allow you to configure settings that 27 | impact all the channel types in your app. 28 | `), 29 | Example: heredoc.Doc(` 30 | # Print the application settings in json format (default format) 31 | $ stream-cli chat get-app 32 | 33 | # Print the application settings in a browsable tree 34 | $ stream-cli chat get-app --output-format tree 35 | 36 | # Print the application settings for another application 37 | $ stream-cli chat get-app --app testenvironment 38 | 39 | # Note: 40 | # Use this command to list all the available Stream applications 41 | $ stream-cli config list 42 | `), 43 | RunE: func(cmd *cobra.Command, args []string) error { 44 | c, err := config.GetConfig(cmd).GetClient(cmd) 45 | if err != nil { 46 | return err 47 | } 48 | 49 | r, err := c.GetAppConfig(cmd.Context()) 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return utils.PrintObject(cmd, r.App) 55 | }, 56 | } 57 | 58 | fl := cmd.Flags() 59 | fl.StringP("output-format", "o", "json", "[optional] Output format. Can be json or tree") 60 | 61 | return cmd 62 | } 63 | 64 | func updateCmd() *cobra.Command { 65 | cmd := &cobra.Command{ 66 | Use: "update-app --properties [raw-json-update-properties]", 67 | Short: "Update application settings", 68 | Long: heredoc.Doc(` 69 | Update the application settings. 70 | 71 | Application level settings allow you to configure settings that 72 | impact all the channel types in your app. 73 | 74 | See https://getstream.io/chat/docs/rest/#settings-updateapp for 75 | the available JSON options. 76 | `), 77 | Example: heredoc.Doc(` 78 | # Enable multi-tenant and update permission version to v2 79 | $ stream-cli chat update-app --properties '{"multi_tenant_enabled": true, "permission_version": "v2"}' 80 | `), 81 | RunE: func(cmd *cobra.Command, args []string) error { 82 | c, err := config.GetConfig(cmd).GetClient(cmd) 83 | if err != nil { 84 | return err 85 | } 86 | 87 | p, _ := cmd.Flags().GetString("properties") 88 | 89 | s := &stream.AppSettings{} 90 | err = json.Unmarshal([]byte(p), s) 91 | if err != nil { 92 | return err 93 | } 94 | 95 | _, err = c.UpdateAppSettings(cmd.Context(), s) 96 | if err != nil { 97 | return err 98 | } 99 | 100 | cmd.Println("Successfully updated app settings.") 101 | return nil 102 | }, 103 | } 104 | 105 | fl := cmd.Flags() 106 | fl.StringP("properties", "p", "", "[required] Raw json properties to update") 107 | _ = cmd.MarkFlagRequired("properties") 108 | 109 | return cmd 110 | } 111 | 112 | func revokeAllTokensCmd() *cobra.Command { 113 | cmd := &cobra.Command{ 114 | Use: "revoke-all-tokens --before [epoch]", 115 | Short: "Revoke all tokens", 116 | Long: heredoc.Doc(` 117 | This command revokes ALL tokens for all users of an application. 118 | This should be used with caution as it will expire every user’s token, 119 | regardless of whether the token has an iat claim. 120 | `), 121 | Example: heredoc.Doc(` 122 | # Revoke all tokens for the default app, from now 123 | $ stream-cli chat revoke-all-tokens 124 | 125 | # Revoke all tokens for the test app, before 2019-01-01 126 | $ stream-cli chat revoke-all-tokens --before 1546300800 --app test 127 | `), 128 | RunE: func(cmd *cobra.Command, args []string) error { 129 | c, err := config.GetConfig(cmd).GetClient(cmd) 130 | if err != nil { 131 | return err 132 | } 133 | 134 | before, _ := cmd.Flags().GetInt64("before") 135 | if before == 0 { 136 | before = time.Now().Unix() 137 | } 138 | beforeDate := time.Unix(before, 0) 139 | 140 | _, err = c.RevokeTokens(cmd.Context(), &beforeDate) 141 | if err != nil { 142 | return err 143 | } 144 | 145 | cmd.Println("Successfully revoked all tokens.") 146 | return nil 147 | }, 148 | } 149 | 150 | fl := cmd.Flags() 151 | fl.Int64P("before", "b", 0, "[optional] The epoch timestamp before which tokens should be revoked. Defaults to now.") 152 | 153 | return cmd 154 | } 155 | -------------------------------------------------------------------------------- /pkg/cmd/chat/reaction/reaction.go: -------------------------------------------------------------------------------- 1 | package reaction 2 | 3 | import ( 4 | stream_chat "github.com/GetStream/stream-chat-go/v5" 5 | "github.com/MakeNowJust/heredoc" 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/GetStream/stream-cli/pkg/config" 9 | "github.com/GetStream/stream-cli/pkg/utils" 10 | ) 11 | 12 | func NewCmds() []*cobra.Command { 13 | return []*cobra.Command{ 14 | getCmd(), 15 | sendCmd(), 16 | deleteCmd(), 17 | } 18 | } 19 | 20 | func getCmd() *cobra.Command { 21 | cmd := &cobra.Command{ 22 | Use: "get-reactions [message-id]", 23 | Short: "Get reactions for a message", 24 | Example: heredoc.Doc(` 25 | # Get reactions for a [08f64828-3bba-42bd-8430-c26a3634ee5c] message 26 | $ stream-cli chat get-reactions 08f64828-3bba-42bd-8430-c26a3634ee5c --output-format json 27 | `), 28 | Args: cobra.ExactArgs(1), 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | c, err := config.GetConfig(cmd).GetClient(cmd) 31 | if err != nil { 32 | return err 33 | } 34 | 35 | msgID := args[0] 36 | 37 | resp, err := c.GetReactions(cmd.Context(), msgID, nil) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | return utils.PrintObject(cmd, resp.Reactions) 43 | }, 44 | } 45 | 46 | fl := cmd.Flags() 47 | fl.StringP("output-format", "o", "json", "[optional] Output format. Can be json or tree") 48 | 49 | return cmd 50 | } 51 | 52 | func sendCmd() *cobra.Command { 53 | cmd := &cobra.Command{ 54 | Use: "send-reaction --message-id [message-id] --user-id [user-id] --reaction-type [reaction-type]", 55 | Short: "Send a reaction to a message", 56 | Long: heredoc.Doc(` 57 | Stream Chat has built-in support for user Reactions. Common examples are 58 | likes, comments, loves, etc. Reactions can be customized so that you 59 | are able to use any type of reaction your application requires. 60 | `), 61 | Example: heredoc.Doc(` 62 | # Send a reaction to a [08f64828-3bba-42bd-8430-c26a3634ee5c] message 63 | $ stream-cli chat send-reaction --message-id 08f64828-3bba-42bd-8430-c26a3634ee5c --user-id 12345 --reaction-type like 64 | `), 65 | RunE: func(cmd *cobra.Command, args []string) error { 66 | c, err := config.GetConfig(cmd).GetClient(cmd) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | reactionType, _ := cmd.Flags().GetString("reaction-type") 72 | msgID, _ := cmd.Flags().GetString("message-id") 73 | userID, _ := cmd.Flags().GetString("user-id") 74 | 75 | r := &stream_chat.Reaction{Type: reactionType} 76 | 77 | _, err = c.Channel("", "").SendReaction(cmd.Context(), r, msgID, userID) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | cmd.Println("Successfully sent reaction") 83 | return nil 84 | }, 85 | } 86 | 87 | fl := cmd.Flags() 88 | fl.StringP("reaction-type", "r", "", "[required] The reaction type to send") 89 | fl.StringP("message-id", "m", "", "[required] The message id to send the reaction to") 90 | fl.StringP("user-id", "u", "", "[required] The user id of the user sending the reaction") 91 | _ = cmd.MarkFlagRequired("reaction-type") 92 | _ = cmd.MarkFlagRequired("message-id") 93 | _ = cmd.MarkFlagRequired("user-id") 94 | 95 | return cmd 96 | } 97 | 98 | func deleteCmd() *cobra.Command { 99 | cmd := &cobra.Command{ 100 | Use: "delete-reaction --message-id [message-id] --reaction-type [reaction-type] --user-id [user-id]", 101 | Short: "Delete a reaction from a message", 102 | Example: heredoc.Doc(` 103 | # Delete a reaction from [08f64828-3bba-42bd-8430-c26a3634ee5c] message 104 | $ stream-cli chat delete-reaction --message-id 08f64828-3bba-42bd-8430-c26a3634ee5c --reaction-type like --user-id 12345 105 | `), 106 | RunE: func(cmd *cobra.Command, args []string) error { 107 | c, err := config.GetConfig(cmd).GetClient(cmd) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | reactionType, _ := cmd.Flags().GetString("reaction-type") 113 | userID, _ := cmd.Flags().GetString("user-id") 114 | msgID, _ := cmd.Flags().GetString("message-id") 115 | 116 | _, err = c.Channel("", "").DeleteReaction(cmd.Context(), msgID, reactionType, userID) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | cmd.Println("Successfully deleted reaction") 122 | return nil 123 | }, 124 | } 125 | 126 | fl := cmd.Flags() 127 | fl.StringP("reaction-type", "r", "", "[required] The reaction type to delete") 128 | fl.StringP("message-id", "m", "", "[required] The message id to delete the reaction from") 129 | fl.StringP("user-id", "u", "", "[required] The user id of the user deleting the reaction") 130 | _ = cmd.MarkFlagRequired("reaction-type") 131 | _ = cmd.MarkFlagRequired("message-id") 132 | _ = cmd.MarkFlagRequired("user-id") 133 | 134 | return cmd 135 | } 136 | -------------------------------------------------------------------------------- /pkg/cmd/chat/events/events.go: -------------------------------------------------------------------------------- 1 | package events 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | "time" 11 | 12 | "github.com/GetStream/stream-cli/pkg/config" 13 | "github.com/GetStream/stream-cli/pkg/utils" 14 | "github.com/GetStream/stream-cli/pkg/version" 15 | "github.com/MakeNowJust/heredoc" 16 | "github.com/gorilla/websocket" 17 | "github.com/spf13/cobra" 18 | ) 19 | 20 | func NewCmds() []*cobra.Command { 21 | return []*cobra.Command{listenCmd()} 22 | } 23 | 24 | func listenCmd() *cobra.Command { 25 | cmd := &cobra.Command{ 26 | Use: "listen-events --user-id [user-id] --timeout [number]", 27 | Short: "Listen to events", 28 | Long: heredoc.Doc(` 29 | The command opens a WebSocket connection to the backend in the name of the user 30 | and prints the received events to the standard output. 31 | Press Ctrl+C to exit. 32 | `), 33 | Example: heredoc.Doc(` 34 | # Listen to events for user with id 'my-user-1' 35 | $ stream-cli chat listen-events --user-id my-user-1 36 | 37 | # Listen to events for user with id 'my-user-2' and keeping the connection open for 120 seconds 38 | $ stream-cli chat listen-events --user-id my-user-1 --timeout 120 39 | `), 40 | RunE: func(cmd *cobra.Command, args []string) error { 41 | timeout, _ := cmd.Flags().GetInt32("timeout") 42 | if timeout > 300 { 43 | return fmt.Errorf("timeout cannot be greater than 300") 44 | } 45 | userID, _ := cmd.Flags().GetString("user-id") 46 | config := config.GetConfig(cmd) 47 | 48 | app, err := config.GetDefaultAppOrExplicit(cmd) 49 | if err != nil { 50 | return err 51 | } 52 | 53 | client, err := config.GetClient(cmd) 54 | if err != nil { 55 | return err 56 | } 57 | 58 | token, err := client.CreateToken(userID, time.Time{}) 59 | if err != nil { 60 | return err 61 | } 62 | 63 | cmd.Printf("> 🚨 Warning! The WebSocket connection can be expensive so we close it after %d seconds.\n", timeout) 64 | time.Sleep(2 * time.Second) 65 | // Giving the user 2 seconds to read the warning message 66 | // because the first heartbeat is sent super quickly and 67 | // takes up the whole screen. 68 | 69 | url, err := getUrl(app, userID, token) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | websocket.DefaultDialer.HandshakeTimeout = time.Second * 5 75 | conn, _, err := websocket.DefaultDialer.Dial(url, nil) 76 | if err != nil { 77 | return err 78 | } 79 | cmd.Println("> Successfully connected. Waiting for events...⌛️") 80 | 81 | exit := make(chan os.Signal, 1) 82 | signal.Notify(exit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) 83 | 84 | // Since keeping connections can be expensive 85 | // let's just exit after 'timeout' seconds. 86 | go func() { 87 | time.Sleep(time.Duration(timeout-10) * time.Second) 88 | cmd.Println("> Exiting in 10 seconds...") 89 | time.Sleep(10 * time.Second) 90 | cmd.Printf("> %d seconds passed. Exiting now.\n", timeout) 91 | exit <- syscall.SIGINT 92 | }() 93 | 94 | go func() { 95 | for { 96 | var event map[string]any 97 | err := conn.ReadJSON(&event) 98 | if err != nil { 99 | cmd.PrintErr(err) 100 | continue 101 | } 102 | 103 | err = utils.PrintObject(cmd, event) 104 | if err != nil { 105 | cmd.PrintErr(err) 106 | continue 107 | } 108 | 109 | cmd.Println("> Press Ctrl+C to exit") 110 | } 111 | }() 112 | 113 | <-exit 114 | cmd.Println("> Exiting") 115 | return conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, "")) 116 | }, 117 | } 118 | 119 | fl := cmd.Flags() 120 | fl.Int32P("timeout", "t", 60, "[optional] For how many seconds do we keep the connection alive. Default is 60 seconds, max is 300.") 121 | fl.StringP("user-id", "u", "", "[required] User ID") 122 | fl.StringP("output-format", "o", "json", "[optional] Output format. Can be json or tree") 123 | _ = cmd.MarkFlagRequired("user-id") 124 | 125 | return cmd 126 | } 127 | 128 | func getUrl(app *config.App, userID, token string) (string, error) { 129 | u, err := url.Parse(app.ChatURL) 130 | if err != nil { 131 | return "", err 132 | } 133 | 134 | u.Scheme = "wss" 135 | u.Path = "connect" 136 | 137 | payload := map[string]any{ 138 | "user_id": userID, 139 | "user_details": map[string]string{"id": userID}, 140 | } 141 | b, _ := json.Marshal(payload) 142 | 143 | params := url.Values{} 144 | params.Add("json", string(b)) 145 | params.Add("api_key", app.AccessKey) 146 | params.Add("authorization", token) 147 | params.Add("stream-auth-type", "jwt") 148 | params.Add("X-Stream-Client", fmt.Sprintf("stream-cli-%s", version.FmtVersion())) 149 | u.RawQuery = params.Encode() 150 | 151 | return u.String(), nil 152 | } 153 | -------------------------------------------------------------------------------- /pkg/cmd/chat/message/message_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "bytes" 5 | "os" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | 10 | "github.com/GetStream/stream-cli/test" 11 | ) 12 | 13 | func TestSendMessage(t *testing.T) { 14 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 15 | ch := test.InitChannel(t) 16 | u := test.CreateUser() 17 | t.Cleanup(func() { 18 | test.DeleteUser(u) 19 | test.DeleteChannel(ch) 20 | }) 21 | 22 | cmd.SetArgs([]string{"send-message", "-t", "messaging", "-i", ch, "-u", u, "--attachment", "https://via.placeholder.com/1", "--text", "hello"}) 23 | _, err := cmd.ExecuteC() 24 | require.NoError(t, err) 25 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Message successfully sent") 26 | } 27 | 28 | func TestSendMessageWithFileAttachment(t *testing.T) { 29 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 30 | ch := test.InitChannel(t) 31 | u := test.CreateUser() 32 | 33 | tmpfile, err := os.CreateTemp("", "*.txt") 34 | require.NoError(t, err) 35 | 36 | err = os.WriteFile(tmpfile.Name(), []byte("hello\nworld\n"), 0o644) 37 | require.NoError(t, err) 38 | 39 | t.Cleanup(func() { 40 | _ = tmpfile.Close() 41 | _ = os.Remove(tmpfile.Name()) 42 | test.DeleteUser(u) 43 | test.DeleteChannel(ch) 44 | }) 45 | 46 | cmd.SetArgs([]string{"send-message", "-t", "messaging", "-i", ch, "-u", u, "--attachment", tmpfile.Name(), "--text", "hello"}) 47 | _, err = cmd.ExecuteC() 48 | require.NoError(t, err) 49 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Message successfully sent") 50 | } 51 | 52 | func TestGetMessage(t *testing.T) { 53 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 54 | ch := test.InitChannel(t) 55 | u := test.CreateUser() 56 | m := test.CreateMessage(ch, u) 57 | t.Cleanup(func() { 58 | test.DeleteMessage(m) 59 | test.DeleteUser(u) 60 | test.DeleteChannel(ch) 61 | }) 62 | 63 | cmd.SetArgs([]string{"get-message", m}) 64 | _, err := cmd.ExecuteC() 65 | require.NoError(t, err) 66 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), m) 67 | } 68 | 69 | func TestGetMultipleMessage(t *testing.T) { 70 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 71 | ch := test.InitChannel(t) 72 | u := test.CreateUser() 73 | m1 := test.CreateMessage(ch, u) 74 | m2 := test.CreateMessage(ch, u) 75 | t.Cleanup(func() { 76 | test.DeleteMessage(m1) 77 | test.DeleteMessage(m2) 78 | test.DeleteUser(u) 79 | test.DeleteChannel(ch) 80 | }) 81 | 82 | cmd.SetArgs([]string{"get-messages", "-t", "messaging", "-i", ch, m1, m2}) 83 | _, err := cmd.ExecuteC() 84 | require.NoError(t, err) 85 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), m1) 86 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), m2) 87 | } 88 | 89 | func TestDeleteMessage(t *testing.T) { 90 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 91 | ch := test.InitChannel(t) 92 | u := test.CreateUser() 93 | m := test.CreateMessage(ch, u) 94 | t.Cleanup(func() { 95 | test.DeleteMessage(m) 96 | test.DeleteUser(u) 97 | test.DeleteChannel(ch) 98 | }) 99 | 100 | cmd.SetArgs([]string{"delete-message", m}) 101 | _, err := cmd.ExecuteC() 102 | require.NoError(t, err) 103 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Message successfully deleted") 104 | } 105 | 106 | func TestPartialUpdateMessage(t *testing.T) { 107 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 108 | ch := test.InitChannel(t) 109 | u := test.CreateUser() 110 | m := test.CreateMessage(ch, u) 111 | t.Cleanup(func() { 112 | test.DeleteMessage(m) 113 | test.DeleteUser(u) 114 | test.DeleteChannel(ch) 115 | }) 116 | 117 | cmd.SetArgs([]string{"update-message-partial", "-m", m, "--user", u, "--set", `{"age":15}`}) 118 | _, err := cmd.ExecuteC() 119 | require.NoError(t, err) 120 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully updated message") 121 | } 122 | 123 | func TestFlagMessage(t *testing.T) { 124 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 125 | ch := test.InitChannel(t) 126 | u := test.CreateUser() 127 | m := test.CreateMessage(ch, u) 128 | t.Cleanup(func() { 129 | test.DeleteMessage(m) 130 | test.DeleteUser(u) 131 | test.DeleteChannel(ch) 132 | }) 133 | 134 | cmd.SetArgs([]string{"flag-message", "-m", m, "-u", u}) 135 | _, err := cmd.ExecuteC() 136 | require.NoError(t, err) 137 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "Successfully flagged message") 138 | } 139 | 140 | func TestTranslateMessage(t *testing.T) { 141 | cmd := test.GetRootCmdWithSubCommands(NewCmds()...) 142 | ch := test.InitChannel(t) 143 | u := test.CreateUser() 144 | m := test.CreateMessageWithText(ch, u, "hi") 145 | t.Cleanup(func() { 146 | test.DeleteMessage(m) 147 | test.DeleteUser(u) 148 | test.DeleteChannel(ch) 149 | }) 150 | 151 | cmd.SetArgs([]string{"translate-message", "-m", m, "-l", "hu"}) 152 | _, err := cmd.ExecuteC() 153 | require.NoError(t, err) 154 | require.Contains(t, cmd.OutOrStdout().(*bytes.Buffer).String(), "szia") 155 | } 156 | -------------------------------------------------------------------------------- /pkg/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | 9 | stream "github.com/GetStream/stream-chat-go/v5" 10 | "github.com/spf13/cobra" 11 | "github.com/spf13/viper" 12 | ) 13 | 14 | const ( 15 | configDir = "stream-cli" 16 | 17 | DefaultChatEdgeURL = "https://chat.stream-io-api.com" 18 | ) 19 | 20 | type Config struct { 21 | // Viper uses `yaml` for serializing the object into a file. 22 | // And then uses `mapstructure` to deserialize into a an actual Config object. 23 | 24 | // Default is the default configuration used for operations 25 | Default string `yaml:"default" mapstructure:"default"` 26 | Apps []App `yaml:"apps" mapstructure:"apps"` 27 | } 28 | 29 | type App struct { 30 | Name string `yaml:"name" mapstructure:"name"` 31 | AccessKey string `yaml:"access-key" mapstructure:"access-key"` 32 | AccessSecretKey string `yaml:"access-secret-key" mapstructure:"access-secret-key"` 33 | ChatURL string `yaml:"chat-url" mapstructure:"chat-url"` 34 | } 35 | 36 | func (c *Config) Get(name string) (*App, error) { 37 | if len(c.Apps) == 0 { 38 | return nil, errors.New("no application configured, please run `stream-cli config new` to add a new one") 39 | } 40 | 41 | for _, app := range c.Apps { 42 | if app.Name == name { 43 | return &app, nil 44 | } 45 | } 46 | return nil, fmt.Errorf("application %q doesn't exist", name) 47 | } 48 | 49 | func (c *Config) GetDefaultAppOrExplicit(cmd *cobra.Command) (*App, error) { 50 | appName := c.Default 51 | explicit, err := cmd.Flags().GetString("app") 52 | if err != nil { 53 | return nil, err 54 | } 55 | if explicit != "" { 56 | appName = explicit 57 | } 58 | 59 | a, err := c.Get(appName) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | return a, nil 65 | } 66 | 67 | func (c *Config) GetClient(cmd *cobra.Command) (*stream.Client, error) { 68 | a, err := c.GetDefaultAppOrExplicit(cmd) 69 | if err != nil { 70 | return nil, err 71 | } 72 | 73 | client, err := stream.NewClient(a.AccessKey, a.AccessSecretKey) 74 | if err != nil { 75 | return nil, err 76 | } 77 | if a.ChatURL != DefaultChatEdgeURL { 78 | client.BaseURL = a.ChatURL 79 | } 80 | return client, nil 81 | } 82 | 83 | func (c *Config) Add(newApp App) error { 84 | if len(c.Apps) == 0 { 85 | c.Default = newApp.Name 86 | } 87 | 88 | for _, app := range c.Apps { 89 | if newApp.Name == app.Name { 90 | return fmt.Errorf("application %q already exists", newApp.Name) 91 | } 92 | } 93 | 94 | if newApp.ChatURL == "" { 95 | newApp.ChatURL = DefaultChatEdgeURL 96 | } 97 | 98 | c.Apps = append(c.Apps, newApp) 99 | 100 | viper.Set("default", c.Default) 101 | viper.Set("apps", c.Apps) 102 | return viper.WriteConfig() 103 | } 104 | 105 | func (c *Config) Remove(appName string) error { 106 | var ( 107 | idx int 108 | found bool 109 | ) 110 | for i, app := range c.Apps { 111 | if appName == app.Name { 112 | found = true 113 | idx = i 114 | break 115 | } 116 | } 117 | if !found { 118 | return fmt.Errorf("application %q doesn't exist", appName) 119 | } 120 | 121 | if c.Default == appName { 122 | c.Default = "" 123 | } 124 | 125 | c.Apps = append(c.Apps[:idx], c.Apps[idx+1:]...) 126 | 127 | viper.Set("default", c.Default) 128 | viper.Set("apps", c.Apps) 129 | return viper.WriteConfig() 130 | } 131 | 132 | func (c *Config) SetDefault(appName string) error { 133 | if c.Default == appName { 134 | // if already default, early return 135 | return nil 136 | } 137 | 138 | var found bool 139 | for _, app := range c.Apps { 140 | if appName == app.Name { 141 | found = true 142 | break 143 | } 144 | } 145 | if !found { 146 | return fmt.Errorf("application %q doesn't exist", appName) 147 | } 148 | 149 | c.Default = appName 150 | viper.Set("default", c.Default) 151 | return viper.WriteConfig() 152 | } 153 | 154 | func GetConfig(cmd *cobra.Command) *Config { 155 | cfg := &Config{} 156 | err := viper.Unmarshal(cfg) 157 | if err != nil { 158 | cmd.PrintErr("Configuration is malformed: " + err.Error()) 159 | os.Exit(1) 160 | } 161 | 162 | return cfg 163 | } 164 | 165 | func GetInitConfig(cmd *cobra.Command, cfgPath *string) func() { 166 | return func() { 167 | var configPath string 168 | 169 | if *cfgPath != "" { 170 | // Use config file from the flag. 171 | configPath = *cfgPath 172 | } else { 173 | // Otherwise use UserConfigDir 174 | dir, err := os.UserConfigDir() 175 | if err != nil { 176 | cmd.PrintErr(err) 177 | os.Exit(1) 178 | } 179 | 180 | configPath = filepath.Join(dir, configDir, "config.yml") 181 | } 182 | 183 | viper.SetConfigFile(configPath) 184 | 185 | err := viper.ReadInConfig() 186 | if err != nil && os.IsNotExist(err) { 187 | err = os.MkdirAll(filepath.Dir(configPath), 0o755) 188 | if err != nil { 189 | cmd.PrintErr(err) 190 | os.Exit(1) 191 | } 192 | 193 | f, err := os.Create(configPath) 194 | if err != nil { 195 | cmd.PrintErr(err) 196 | os.Exit(1) 197 | } 198 | 199 | f.Close() 200 | } 201 | if err != nil { 202 | cmd.PrintErr(err) 203 | os.Exit(1) 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /pkg/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "os" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/spf13/viper" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func getTempFile(t *testing.T) *os.File { 13 | tmpFile, err := os.CreateTemp("", "*.yaml") 14 | require.NoError(t, err) 15 | t.Cleanup(func() { 16 | _ = os.Remove(tmpFile.Name()) 17 | }) 18 | return tmpFile 19 | } 20 | 21 | func TestAddNewConfig(t *testing.T) { 22 | tests := []struct { 23 | name string 24 | appConfig func() App 25 | expected string 26 | errored bool 27 | }{ 28 | { 29 | name: "add first configuration", 30 | appConfig: func() App { 31 | app := App{ChatURL: DefaultChatEdgeURL} 32 | app.Name = "BestConfig" 33 | app.AccessKey = "FamousKey" 34 | app.AccessSecretKey = "TopSecret" 35 | return app 36 | }, 37 | expected: ` 38 | apps: 39 | - name: BestConfig 40 | access-key: FamousKey 41 | access-secret-key: TopSecret 42 | chat-url: https://chat.stream-io-api.com 43 | default: BestConfig 44 | `, 45 | }, 46 | { 47 | name: "add second configuration", 48 | appConfig: func() App { 49 | app := App{ChatURL: DefaultChatEdgeURL} 50 | app.Name = "BestConfigEver" 51 | app.AccessKey = "FamousKey" 52 | app.AccessSecretKey = "TopSecret" 53 | return app 54 | }, 55 | expected: ` 56 | apps: 57 | - name: BestConfig 58 | access-key: FamousKey 59 | access-secret-key: TopSecret 60 | chat-url: https://chat.stream-io-api.com 61 | - name: BestConfigEver 62 | access-key: FamousKey 63 | access-secret-key: TopSecret 64 | chat-url: https://chat.stream-io-api.com 65 | default: BestConfig 66 | `, 67 | }, 68 | { 69 | name: "add already existing configuration", 70 | appConfig: func() App { 71 | app := App{ChatURL: DefaultChatEdgeURL} 72 | app.Name = "BestConfig" 73 | app.AccessKey = "FamousKey" 74 | app.AccessSecretKey = "TopSecret" 75 | return app 76 | }, 77 | errored: true, 78 | }, 79 | } 80 | 81 | file := getTempFile(t) 82 | viper.SetConfigFile(file.Name()) 83 | config := &Config{} 84 | 85 | for _, test := range tests { 86 | t.Run(test.name, func(t *testing.T) { 87 | newApp := test.appConfig() 88 | err := config.Add(newApp) 89 | if test.errored { 90 | require.Error(t, err) 91 | return 92 | } 93 | require.NoError(t, err) 94 | 95 | content, err := os.ReadFile(file.Name()) 96 | require.NoError(t, err) 97 | 98 | require.Equal(t, getNormalizedString(test.expected), getNormalizedString(string(content))) 99 | }) 100 | } 101 | } 102 | 103 | func TestRemoveConfig(t *testing.T) { 104 | file := getTempFile(t) 105 | viper.SetConfigFile(file.Name()) 106 | config := &Config{} 107 | 108 | err := config.Add(App{ 109 | Name: "test1", 110 | AccessKey: "test1", 111 | AccessSecretKey: "test1", 112 | ChatURL: DefaultChatEdgeURL, 113 | }) 114 | require.NoError(t, err) 115 | 116 | err = config.Add(App{ 117 | Name: "test2", 118 | AccessKey: "test2", 119 | AccessSecretKey: "test2", 120 | ChatURL: DefaultChatEdgeURL, 121 | }) 122 | require.NoError(t, err) 123 | 124 | // remove non-existing app configuration should fail 125 | err = config.Remove("unknown") 126 | require.Error(t, err) 127 | 128 | err = config.Remove("test1") 129 | require.NoError(t, err) 130 | 131 | expected := ` 132 | apps: 133 | - name: test2 134 | access-key: test2 135 | access-secret-key: test2 136 | chat-url: https://chat.stream-io-api.com 137 | default: "" 138 | ` 139 | content, err := os.ReadFile(file.Name()) 140 | require.NoError(t, err) 141 | require.Equal(t, getNormalizedString(expected), getNormalizedString(string(content))) 142 | } 143 | 144 | func TestSetDefault(t *testing.T) { 145 | file := getTempFile(t) 146 | viper.SetConfigFile(file.Name()) 147 | config := &Config{} 148 | 149 | err := config.Add(App{ 150 | Name: "test1", 151 | AccessKey: "test1", 152 | AccessSecretKey: "test1", 153 | ChatURL: DefaultChatEdgeURL, 154 | }) 155 | require.NoError(t, err) 156 | 157 | err = config.Add(App{ 158 | Name: "test2", 159 | AccessKey: "test2", 160 | AccessSecretKey: "test2", 161 | ChatURL: DefaultChatEdgeURL, 162 | }) 163 | require.NoError(t, err) 164 | 165 | require.True(t, config.Default == "test1") 166 | 167 | err = config.SetDefault("test2") 168 | require.NoError(t, err) 169 | 170 | require.True(t, config.Default == "test2") 171 | 172 | expected := ` 173 | apps: 174 | - name: test1 175 | access-key: test1 176 | access-secret-key: test1 177 | chat-url: https://chat.stream-io-api.com 178 | - name: test2 179 | access-key: test2 180 | access-secret-key: test2 181 | chat-url: https://chat.stream-io-api.com 182 | default: test2 183 | ` 184 | 185 | content, err := os.ReadFile(file.Name()) 186 | require.NoError(t, err) 187 | require.Equal(t, getNormalizedString(expected), getNormalizedString(string(content))) 188 | } 189 | 190 | func getNormalizedString(s string) string { 191 | noSpace := strings.Replace(s, " ", "", -1) 192 | noNewLine := strings.Replace(noSpace, "\n", "", -1) 193 | 194 | return strings.TrimSpace(noNewLine) 195 | } 196 | -------------------------------------------------------------------------------- /docs/use_cases.md: -------------------------------------------------------------------------------- 1 | # 📃 Use cases 2 | 3 | - [App configuration](#app-configuration) 4 | - [Channel](#channel) 5 | - [Imports](#imports) 6 | - [GDPR](#gdpr) 7 | - [Moderation](#moderation) 8 | 9 | ## App configuration 10 | 11 | You can handle multiple Chat apps with the CLI. 12 | 13 | List your configurations: 14 | ```shell 15 | $ stream-cli config list 16 | 17 | Name Access Key Secret Key URL 18 | ---- ---------- ---------- --- 19 | (default) test kujk3ms96pby **************323m https://chat.stream-io-api.com 20 | prod v5hg34n2m2nv **************76as https://chat.stream-io-api.com 21 | staging nrnn2rmnb52u **************242b https://chat.stream-io-api.com 22 | ``` 23 | 24 | - [**config list** docs](./stream-cli_config_list.md) 25 | 26 | The **default** app is used when no `--app` flag is provided to the command. 27 | 28 | Add a new configuration: 29 | 30 | ```shell 31 | $ stream-cli config new 32 | ? What is the name of your app? (eg. prod, staging, testing) prod 33 | ? What is your access key? v5hg34n2m2nv 34 | ? What is your access secret key? *********************************************************** 35 | ? (optional) Which base URL do you want to use for Chat? https://chat.stream-io-api.com 36 | Application successfully added. 🚀 37 | ``` 38 | - [**config new** docs](./stream-cli_config_new.md) 39 | 40 | From that point, you can provide `--app prod` as an argument to any command. Example: 41 | ```shell 42 | # Create a new channel in the prod app 43 | $ stream-cli chat create-channel -i redteam -t messaging -u joe --app prod 44 | ``` 45 | 46 | Delete a configuration: 47 | ```shell 48 | $ stream-cli config remove prod 49 | [prod] application successfully deleted. 50 | ``` 51 | - [**config remove** docs](./stream-cli_config_remove.md) 52 | 53 | ## Channel 54 | 55 | All CRUD channel operations are available in the CLI. 56 | 57 | Create a channel: 58 | ```shell 59 | $ stream-cli chat create-channel -i redteam -t messaging -u joe 60 | Successfully created channel [messaging:redteam2] 61 | ``` 62 | - [**create-channel** docs](./stream-cli_chat_create-channel.md) 63 | 64 | Add members to a channel: 65 | ```shell 66 | $ stream-cli chat add-members --type messaging --id red-team joe jill jane 67 | Successfully added user(s) to channel 68 | ``` 69 | - [**add-members** docs](./stream-cli_chat_add-members.md) 70 | 71 | Send a message to a channel: 72 | ```shell 73 | $ stream-cli chat send-message --channel-type messaging --channel-id redteam --text "Hello World!" --user joe 74 | Message successfully sent. Message id: [74c63670-f5ea-4b62-a149-98f434f321c1] 75 | ``` 76 | - [**send-message** docs](./stream-cli_chat_send-message.md) 77 | 78 | Send a reaction: 79 | ```shell 80 | $ stream-cli chat send-reaction --message-id 74c63670-f5ea-4b62-a149-98f434f321c1 --user user --reaction-type like 81 | Successfully sent reaction 82 | ``` 83 | 84 | - [**send-reaction** docs](./stream-cli_chat_send-reaction.md) 85 | 86 | List channels: 87 | ```shell 88 | $ stream-cli chat list-channels -t messaging 89 | < json payload > 90 | ``` 91 | - [**list-channels** docs](./stream-cli_chat_list-channels.md) 92 | 93 | 94 | ## Imports 95 | 96 | Validate an import file: 97 | ```shell 98 | $ stream-cli chat validate-import data.json 99 | ``` 100 | - [**validate-import** docs](./stream-cli_chat_validate-import.md) 101 | 102 | Upload a new import: 103 | ```shell 104 | $ stream-cli chat upload-import data.json --mode insert 105 | ``` 106 | - [**upload-import** docs](./stream-cli_chat_upload-import.md) 107 | 108 | - [Imports how-to](./imports.md) 109 | 110 | ## GDPR 111 | 112 | Delete users: 113 | ```shell 114 | $ stream-cli chat delete-users joe jill 115 | ``` 116 | - [**delete-users** docs](./stream-cli_chat_delete-users.md) 117 | 118 | Delete channel: 119 | ```shell 120 | $ stream-cli chat delete-channel --type messaging --id redteam 121 | Successfully initiated channel deletion. Task id: 66bbcdcd-b133-43ce-ab63-557c14d2a168 122 | 123 | # Wait for the task to complete 124 | $ stream-cli chat watch 66bbcdcd-b133-43ce-ab63-557c14d2a168 125 | Waiting for async task to complete...⏳ 126 | Still loading... ⏳ 127 | Async operation completed successfully 128 | ``` 129 | - [**delete-channel** docs](./stream-cli_chat_delete-channel.md) 130 | - [**watch** docs](./stream-cli_chat_watch.md) 131 | 132 | ## Moderation 133 | 134 | Ban a user: 135 | ```shell 136 | $ stream-cli chat ban-user --target-user-id mike --banned-by admin-user-2 --reason "Bad behavior" 137 | ``` 138 | - [**ban-user** docs](./stream-cli_chat_ban-user.md) 139 | 140 | Unban a user: 141 | ```shell 142 | $ stream-cli chat unban-user --target-user-id joe 143 | ``` 144 | - [**unban-user** docs](./stream-cli_chat_unban-user.md) 145 | 146 | Flag a message: 147 | ```shell 148 | $ stream-cli chat flag-message --message-id msgid-1 --user-id userid-1 149 | Successfully flagged message. 150 | ``` 151 | - [**flag-message** docs](./stream-cli_chat_flag-message.md) 152 | 153 | Mute a user: 154 | ```shell 155 | $ stream-cli chat mute-user --target-user-id joe --muted-by-id admin --expiration 5 156 | Successfully muted user. 157 | ``` 158 | - [**mute-user** docs](./stream-cli_chat_mute-user.md) 159 | 160 | Unmute a user: 161 | ```shell 162 | $ stream-cli chat unmute-user --target-user-id joe --unmuted-by-id admin 163 | Successfully unmuted user. 164 | ``` 165 | - [**unmute-user** docs](./stream-cli_chat_unmute-user.md) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Stream Cli](./assets/logo.png) 2 | 3 | # Stream CLI 4 | 5 | --- 6 | > ## 🚨 **Breaking changes in v1.0 <** 7 | > We have completely rewritten the Node.JS CLI to Go in 2022 Q1. Some of the changes: 8 | > - The installation process is easier since it doesn't have any prerequisites (such as NPM). You can just simply download the executable and run it. 9 | > - The name of the executable is `stream-cli` instead of `stream` to avoid conflicts with an existing tool ([imagemagick](https://github.com/GetStream/stream-cli/issues/33)). But you can rename it if you want to. 10 | > - The command invocation is `stream-cli chat [verb-noun] [args] [options]` instead of `stream [verb:noun] [args] [options]`. The most obvious change is using dash instead of colon. We also added the `chat` keyword to preserve domain for our other product [Feeds](https://getstream.io/activity-feeds/). 11 | > - The 1.0.0 Go version's feature set is matching the old one. But if you miss anything, feel free to open an issue. 12 | 13 | Stream's Command Line Interface (CLI) makes it easy to create and manage your [Stream](https://getstream.io) apps directly from the terminal. Currently, only Chat is supported; however, the ability to manage Feeds will be coming soon. 14 | 15 | # 📚 Documentation 16 | The full documentation is deployed to [GitHub Pages](https://getstream.github.io/stream-cli/). 17 | 18 | # 🗒 Issues 19 | 20 | If you're experiencing problems directly related to the CLI, please add an [issue on GitHub](https://github.com/getstream/stream-cli/issues). 21 | 22 | For other issues, submit a [support ticket](https://getstream.io/support). 23 | 24 | # 📝 Changelog 25 | 26 | As with any project, things are always changing. If you're interested in seeing what's changed in the Stream CLI, the changelog for this project can be found [here](./CHANGELOG.md). 27 | 28 | # 🏗 Installation 29 | 30 | The Stream CLI is written in Go and precompiled into a single binary. It doesn't have any prerequisites. 31 | 32 | ## Download the binaries 33 | You can find the binaries in the [Release section](https://github.com/GetStream/stream-cli/releases) of this repository. We also wrote a short script to download them and put it to your $PATH. 34 | 35 | ### Bash (MacOS and Linux) 36 | ```shell 37 | $ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/GetStream/stream-cli/master/install/install.sh)" 38 | ``` 39 | 40 | ### PowerShell (Windows) 41 | ```powershell 42 | $ Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GetStream/stream-cli/master/install/install.ps1" -OutFile "install.ps1"; powershell.exe -ExecutionPolicy Bypass -File ./install.ps1 43 | ``` 44 | 45 | ## Homebrew 46 | 47 | For MacOS users, it's also available via Homebrew: 48 | 49 | ```shell 50 | $ brew tap GetStream/stream-cli https://github.com/GetStream/stream-cli 51 | $ brew install stream-cli 52 | ``` 53 | 54 | ## Compile yourself 55 | ```shell 56 | $ git clone git@github.com:GetStream/stream-cli.git 57 | $ cd stream-cli 58 | $ go build ./cmd/stream-cli 59 | $ ./stream-cli --version 60 | stream-cli version 1.0.0 61 | ``` 62 | 63 | # 🚀 Getting Started 64 | 65 | In order to initialize the CLI, it's as simple as: 66 | 67 | ![Stream](./assets/first_config.svg) 68 | 69 | > Note: Your API key and secret can be found on the [Stream Dashboard](https://getstream.io/dashboard) and is specific to your organization. 70 | 71 | # 🚨 Warning 72 | 73 | We purposefully chose the executable name `stream-cli` to avoid conflict with another tool called [`imagemagick`](https://imagemagick.org/index.php) which [already has a `stream` executable](https://github.com/GetStream/stream-cli/issues/33). 74 | 75 | If you do not have `imagemagick` installed, it might be more comfortable to rename `stream-cli` to `stream`. Alternatively you can set up a symbolic link: 76 | 77 | ```shell 78 | $ ln -s ~/Downloads/stream-cli /usr/local/bin/stream 79 | $ stream --version 80 | stream-cli version 1.0.0 81 | ``` 82 | 83 | # 🔨 Syntax 84 | 85 | Basic commands use the following syntax: 86 | 87 | ```shell 88 | $ stream-cli [chat|feeds] [command] [args] [options] 89 | ``` 90 | 91 | Example: 92 | 93 | ```shell 94 | $ stream-cli chat get-channel -t messaging -i redteam 95 | ``` 96 | 97 | The `--help` keyword is available every step of the way. Examples: 98 | 99 | ```shell 100 | $ stream-cli --help 101 | $ stream-cli chat --help 102 | $ stream-cli chat get-channel --help 103 | ``` 104 | 105 | # 💬 Auto completion 106 | We provide autocompletion for the most popular shells (PowerShell, Bash, ZSH, Fish). 107 | 108 | ```shell 109 | $ stream-cli completion --help 110 | ``` 111 | 112 | # 📣 Feedback 113 | 114 | If you have any suggestions or just want to let us know what you think of the Stream CLI, please send us a message at support@getstream.io or create a [GitHub Issue](https://github.com/getstream/stream-cli/issues). 115 | 116 | # 🔧 Development 117 | 118 | We welcome code changes that improve this library or fix a problem, please make sure to follow all best practices and add tests if applicable before submitting a Pull Request on Github. We are very happy to merge your code in the official repository. Make sure to sign our [Contributor License Agreement (CLA)](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) first. See our [license file](./LICENSE) for more details. 119 | 120 | # 🧑‍💻 We are hiring! 121 | 122 | We've recently closed a [$38 million Series B funding round](https://techcrunch.com/2021/03/04/stream-raises-38m-as-its-chat-and-activity-feed-apis-power-communications-for-1b-users/) and we keep actively growing. 123 | Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world. 124 | 125 | Check out our current openings and apply via [Stream's website](https://getstream.io/team/#jobs). 126 | --------------------------------------------------------------------------------