Skip to content

Commit

Permalink
Merge branch 'main' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Xemdo authored Jan 5, 2024
2 parents 6d90dd7 + b0ab98a commit 6ed1090
Show file tree
Hide file tree
Showing 37 changed files with 938 additions and 609 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [Download](#download)
- [Homebrew](#homebrew)
- [Scoop](#scoop)
- [WinGet](#winget)
- [Manual Download](#manual-download)
- [Usage](#usage)
- [Commands](#commands)
Expand Down Expand Up @@ -33,6 +34,22 @@ scoop install twitch-cli

This will install it into your path, and it'll be callable via `twitch`.

### WinGet

Alternatively on Windows you can use [WinGet](https://learn.microsoft.com/en-us/windows/package-manager/winget/) for installing the CLI

To install via Winget, run:

```sh
winget install Twitch.TwitchCLI
```

To update, run:

```sh
winget update Twitch.TwitchCLI
```

### Manual Download

To download, go to the [Releases tab of GitHub](https://github.com/twitchdev/twitch-cli/releases). The examples in the documentation assume you have put this into your PATH and renamed to `twitch` (or symlinked as such).
Expand Down
2 changes: 1 addition & 1 deletion cmd/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func configureCmdRun(cmd *cobra.Command, args []string) error {
}

if err := viper.WriteConfigAs(configPath); err != nil {
fmt.Errorf("Failed to write configuration: %v", err.Error())
return fmt.Errorf("Failed to write configuration: %v", err.Error())
}

fmt.Println("Updated configuration.")
Expand Down
109 changes: 84 additions & 25 deletions cmd/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/spf13/cobra"
"github.com/twitchdev/twitch-cli/internal/events"
configure_event "github.com/twitchdev/twitch-cli/internal/events/configure"
"github.com/twitchdev/twitch-cli/internal/events/trigger"
"github.com/twitchdev/twitch-cli/internal/events/types"
"github.com/twitchdev/twitch-cli/internal/events/verify"
Expand All @@ -25,6 +26,7 @@ var (
forwardAddress string
event string
transport string
noConfig bool
fromUser string
toUser string
giftUser string
Expand All @@ -51,15 +53,16 @@ var (

// websocketCmd-specific flags
var (
wsDebug bool
wsStrict bool
wsClient string
wsSubscription string
wsStatus string
wsReason string
wsServerIP string
wsServerPort int
wsSSL bool
wsDebug bool
wsStrict bool
wsClient string
wsSubscription string
wsStatus string
wsReason string
wsServerIP string
wsServerPort int
wsSSL bool
wsFeatureEnabled bool
)

var eventCmd = &cobra.Command{
Expand Down Expand Up @@ -106,7 +109,8 @@ var websocketCmd = &cobra.Command{
Example: fmt.Sprintf(` twitch event websocket start-server
twitch event websocket reconnect
twitch event websocket close --session=e411cc1e_a2613d4e --reason=4006
twitch event websocket subscription --status=user_removed --subscription=82a855-fae8-93bff0`,
twitch event websocket subscription --status=user_removed --subscription=82a855-fae8-93bff0
twitch event websocket keepalive --session=e411cc1e_a2613d4e --enabled=false`,
),
Aliases: []string{
"websockets",
Expand All @@ -127,16 +131,25 @@ var startWebsocketServerCmd = &cobra.Command{
Deprecated: `use "twitch event websocket start-server" instead.`,
}

var configureEventCmd = &cobra.Command{
Use: "configure",
Short: "Allows users to configure defaults for the twitch event subcommands.",
RunE: configureEventRun,
Example: `twitch event configure`,
}

func init() {
rootCmd.AddCommand(eventCmd)

eventCmd.AddCommand(triggerCmd, retriggerCmd, verifyCmd, websocketCmd, startWebsocketServerCmd)
eventCmd.AddCommand(triggerCmd, retriggerCmd, verifyCmd, websocketCmd, startWebsocketServerCmd, configureEventCmd)
eventCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")

// trigger flags
//// flags for forwarding functionality/changing payloads
triggerCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
triggerCmd.Flags().StringVarP(&transport, "transport", "T", "webhook", fmt.Sprintf("Preferred transport method for event. Defaults to /EventSub.\nSupported values: %s", events.ValidTransports()))
triggerCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
triggerCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")

// trigger flags
//// per-topic flags
Expand All @@ -149,7 +162,7 @@ func init() {
triggerCmd.Flags().StringVarP(&subscriptionStatus, "subscription-status", "r", "enabled", "Status of the Subscription object (.subscription.status in JSON). Defaults to \"enabled\".")
triggerCmd.Flags().StringVarP(&itemID, "item-id", "i", "", "Manually set the ID of the event payload item (for example the reward ID in redemption events). For stream events, this is the game ID.")
triggerCmd.Flags().StringVarP(&itemName, "item-name", "n", "", "Manually set the name of the event payload item (for example the reward ID in redemption events). For stream events, this is the game title.")
triggerCmd.Flags().Int64VarP(&cost, "cost", "C", 0, "Amount of subscriptions, bits, or channel points redeemed/used in the event.")
triggerCmd.Flags().Int64VarP(&cost, "cost", "C", 0, "Amount of drops, subscriptions, bits, or channel points redeemed/used in the event.")
triggerCmd.Flags().StringVarP(&description, "description", "d", "", "Title the stream should be updated with.")
triggerCmd.Flags().StringVarP(&gameID, "game-id", "G", "", "Sets the game/category ID for applicable events.")
triggerCmd.Flags().StringVarP(&tier, "tier", "", "", "Sets the subscription tier. Valid values are 1000, 2000, and 3000.")
Expand All @@ -167,6 +180,7 @@ func init() {
retriggerCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
retriggerCmd.Flags().StringVarP(&eventID, "id", "i", "", "ID of the event to be refired.")
retriggerCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
retriggerCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
retriggerCmd.MarkFlagRequired("id")

// verify-subscription flags
Expand All @@ -176,7 +190,8 @@ func init() {
verifyCmd.Flags().StringVar(&timestamp, "timestamp", "", "Sets the timestamp to be used in payloads and headers. Must be in RFC3339Nano format.")
verifyCmd.Flags().StringVarP(&eventID, "subscription-id", "u", "", "Manually set the subscription/event ID of the event itself.") // TODO: This description will need to change with https://github.com/twitchdev/twitch-cli/issues/184
verifyCmd.Flags().StringVarP(&version, "version", "v", "", "Chooses the EventSub version used for a specific event. Not required for most events.")
verifyCmd.MarkFlagRequired("forward-address")
verifyCmd.Flags().BoolVarP(&noConfig, "no-config", "D", false, "Disables the use of the configuration, if it exists.")
verifyCmd.Flags().StringVarP(&toUser, "broadcaster", "b", "", "User ID of the broadcaster for the verification event.")

// websocket flags
/// flags for start-server
Expand All @@ -192,6 +207,11 @@ func init() {
websocketCmd.Flags().StringVar(&wsSubscription, "subscription", "", `Subscription to target with your server command. Used with "websocket subscription".`)
websocketCmd.Flags().StringVar(&wsStatus, "status", "", `Changes the status of an existing subscription. Used with "websocket subscription".`)
websocketCmd.Flags().StringVar(&wsReason, "reason", "", `Sets the close reason when sending a Close message to the client. Used with "websocket close".`)
websocketCmd.Flags().BoolVar(&wsFeatureEnabled, "enabled", false, "Sets on/off for the specified feature.")

// configure flags
configureEventCmd.Flags().StringVarP(&forwardAddress, "forward-address", "F", "", "Forward address for mock event (webhook only).")
configureEventCmd.Flags().StringVarP(&secret, "secret", "s", "", "Webhook secret. If defined, signs all forwarded events with the SHA256 HMAC and must be 10-100 characters in length.")
}

func triggerCmdRun(cmd *cobra.Command, args []string) error {
Expand All @@ -204,8 +224,14 @@ func triggerCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf(websubDeprecationNotice)
}

if secret != "" && (len(secret) < 10 || len(secret) > 100) {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
defaults := configure_event.GetEventConfiguration(noConfig)

if secret != "" {
if len(secret) < 10 || len(secret) > 100 {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
}
} else {
secret = defaults.Secret
}

// Validate that the forward address is actually a URL
Expand All @@ -214,6 +240,8 @@ func triggerCmdRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
} else {
forwardAddress = defaults.ForwardAddress
}

for i := 0; i < count; i++ {
Expand Down Expand Up @@ -260,8 +288,21 @@ func retriggerCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf(websubDeprecationNotice)
}

if secret != "" && (len(secret) < 10 || len(secret) > 100) {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
defaults := configure_event.GetEventConfiguration(noConfig)

if secret != "" {
if len(secret) < 10 || len(secret) > 100 {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
}
} else {
secret = defaults.Secret
}

if forwardAddress == "" {
if defaults.ForwardAddress == "" {
return fmt.Errorf("if a default configuration is not set, forward-address must be provided")
}
forwardAddress = defaults.ForwardAddress
}

res, err := trigger.RefireEvent(eventID, trigger.TriggerParameters{
Expand All @@ -287,8 +328,14 @@ func verifyCmdRun(cmd *cobra.Command, args []string) error {
return fmt.Errorf(websubDeprecationNotice)
}

if secret != "" && (len(secret) < 10 || len(secret) > 100) {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
defaults := configure_event.GetEventConfiguration(noConfig)

if secret != "" {
if len(secret) < 10 || len(secret) > 100 {
return fmt.Errorf("Invalid secret provided. Secrets must be between 10-100 characters")
}
} else {
secret = defaults.Secret
}

// Validate that the forward address is actually a URL
Expand All @@ -297,6 +344,8 @@ func verifyCmdRun(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
} else {
forwardAddress = defaults.ForwardAddress
}

if timestamp == "" {
Expand All @@ -313,12 +362,14 @@ https://dev.twitch.tv/docs/eventsub/handling-webhook-events#processing-an-event`
}

_, err := verify.VerifyWebhookSubscription(verify.VerifyParameters{
Event: args[0],
Transport: transport,
ForwardAddress: forwardAddress,
Secret: secret,
Timestamp: timestamp,
EventID: eventID,
Event: args[0],
Transport: transport,
ForwardAddress: forwardAddress,
Secret: secret,
Timestamp: timestamp,
EventID: eventID,
BroadcasterUserID: toUser,
Version: version,
})

if err != nil {
Expand All @@ -345,10 +396,18 @@ func websocketCmdRun(cmd *cobra.Command, args []string) error {
Subscription: wsSubscription,
SubscriptionStatus: wsStatus,
CloseReason: wsReason,
FeatureEnabled: wsFeatureEnabled,
})

return err
}

return nil
}

func configureEventRun(cmd *cobra.Command, args []string) error {
return configure_event.ConfigureEvents(configure_event.EventConfigurationParams{
ForwardAddress: forwardAddress,
Secret: secret,
})
}
16 changes: 11 additions & 5 deletions cmd/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var validateToken string
var overrideClientId string
var tokenServerPort int
var tokenServerIP string
var redirectHost string

// loginCmd represents the login command
var loginCmd = &cobra.Command{
Expand All @@ -37,20 +38,25 @@ func init() {
loginCmd.Flags().StringVarP(&revokeToken, "revoke", "r", "", "Instead of generating a new token, revoke the one passed to this parameter.")
loginCmd.Flags().StringVarP(&validateToken, "validate", "v", "", "Instead of generating a new token, validate the one passed to this parameter.")
loginCmd.Flags().StringVar(&overrideClientId, "client-id", "", "Override/manually set client ID for token actions. By default client ID from CLI config will be used.")
loginCmd.Flags().StringVar(&tokenServerIP, "ip", "localhost", "Manually set the IP address to be binded to for the User Token web server.")
loginCmd.Flags().StringVar(&tokenServerIP, "ip", "", "Manually set the IP address to be bound to for the User Token web server.")
loginCmd.Flags().IntVarP(&tokenServerPort, "port", "p", 3000, "Manually set the port to be used for the User Token web server.")
loginCmd.Flags().StringVar(&redirectHost, "redirect-host", "localhost", "Manually set the host to be used for the redirect URL")
}

func loginCmdRun(cmd *cobra.Command, args []string) error {
clientID = viper.GetString("clientId")
clientSecret = viper.GetString("clientSecret")

webserverPort := strconv.Itoa(tokenServerPort)
redirectURL := fmt.Sprintf("http://%v:%v", tokenServerIP, webserverPort)
redirectURL := fmt.Sprintf("http://%v:%v", redirectHost, webserverPort)

if clientID == "" || clientSecret == "" {
println("No Client ID or Secret found in configuration. Triggering configuration now.")
configureCmd.Run(cmd, args)
err := configureCmd.RunE(cmd, args)
if err != nil {
return err
}

clientID = viper.GetString("clientId")
clientSecret = viper.GetString("clientSecret")
}
Expand All @@ -76,7 +82,7 @@ func loginCmdRun(cmd *cobra.Command, args []string) error {
p.URL = login.ValidateTokenURL
r, err := login.ValidateCredentials(p)
if err != nil {
return fmt.Errorf("Failed to validate: %v", err.Error())
return fmt.Errorf("failed to validate: %v", err.Error())
}

tokenType := "App Access Token"
Expand Down Expand Up @@ -105,7 +111,7 @@ func loginCmdRun(cmd *cobra.Command, args []string) error {
fmt.Println(white("- %v\n", s))
}
}
} else if isUserToken == true {
} else if isUserToken {
p.URL = login.UserCredentialsURL
login.UserCredentialsLogin(p, tokenServerIP, webserverPort)
} else {
Expand Down
Loading

0 comments on commit 6ed1090

Please sign in to comment.