helix Go Client for Twitch Helix API

repository·main·Indexed 43 days ago

https://github.com/nicklaw5/helix

A Go client library for the Twitch Helix API. It provides programmatic access to Twitch endpoints, including support for App, User, and Device access tokens, automatic token refreshing, and built-in rate limit handling. Features include methods for managing commercials via StartCommercial, retrieving game and extension analytics, and handling OAuth2 authentication flows.

Tokens
70.4K
Snippets
268
Records
290
Agent score
53%

What's inside helix

  1. What is helix?

    main
    helix is a Go (Golang) client library for interacting with the Twitch Helix API. It provides a structured way to access Twitch's expanding set of endpoints and features. Note that as Twitch updates its API, the maintainers of this package implement new changes on a best-effort basis.
  2. Manage request rate limiting

    main

    Twitch enforces rate limits. Helix provides helper methods on response objects to retrieve rate limit headers as integers:

    • Response.GetRateLimit()
    • Response.GetRateLimitRemaining()
    • Response.GetRateLimitReset()
    • Response.GetClipsCreationRateLimit() (specific to CreateClip)
    • Response.GetClipsCreationRateLimitRemaining() (specific to CreateClip)

    You can provide a custom RateLimitFunc in helix.Options. If provided, the client will re-attempt a request if it receives a 429 (Too Many Requests) response, applying the function before retrying.

    Helix includes a DefaultRateLimitFunc that automatically waits until the rate limit reset time before retrying.

    client, err := helix.NewClient(&helix.Options{
        ClientID:      "your-client-id",
        RateLimitFunc: helix.DefaultRateLimitFunc,
    })
  3. Handle API responses and errors

    main

    Every API request returns a response object that includes a ResponseCommon struct. This struct contains the HTTP status code and any error information returned by Twitch. This is useful for detecting 500 errors or invalid requests.

    Common fields in ResponseCommon:

    • StatusCode (int): The HTTP status code.
    • Header (http.Header): The response headers.
    • Error (string): The error string returned by the API.
    • ErrorStatus (int): The error status code.
    • ErrorMessage (string): The error message returned by the API.
    type ResponseCommon struct {
        StatusCode   int
        Header       http.Header
        Error        string `json:"error"`
        ErrorStatus  int    `json:"status"`
        ErrorMessage string `json:"message"`
    }
  4. Cancel a Raid using helix.CancelRaid

    main

    To cancel an ongoing raid, use the CancelRaid method on a helix.Client instance. You must provide a CancelRaidParams object containing the BroadcasterID of the broadcaster whose raid is being cancelled.

    client, err := helix.NewClient(&helix.Options{
        ClientID: "your-client-id",
    })
    if err != nil {
        // handle error
    }
    
    resp, err := client.CancelRaid(&helix.CancelRaidParams{
        BroadcasterID: "22484632", 
    })
    if err != nil {
        // handle error
    }
    
    fmt.Printf("%+v\n", resp)
  5. Paginate through drops entitlements results

    main

    When retrieving drops entitlements, use the After and First fields in GetDropEntitlementsParams to handle pagination. To fetch the next page of results, pass the Cursor value from the current response's pagination object into the After field of the next request.

    Example of fetching the first page and then the subsequent page:

    // First page 
    resp, err := client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{
    	GameID: "your-game-id",
        After: "",
        First: 50,
    })
    
    // Next page
    resp, err = client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{
    	GameID: "your-game-id",
        After: resp.Data.Pagination.Cursor,
        First: 50,
    })
    // Next page
    resp, err = client.GetDropsEntitlements(&helix.GetDropEntitlementsParams{
    	GameID: "your-game-id",
        After: resp.Data.Pagination.Cursor,
        First: 50,
    })
  6. Revoke a User Access Token

    main

    Use RevokeUserAccessToken to invalidate an existing user access token. You can retrieve the current token from the client using GetUserAccessToken() before passing it to the revocation method.

    client, err := helix.NewClient(&helix.Options{
        ClientID: "your-client-id",
    })
    if err != nil {
        // handle error
    }
    
    userAccessToken := client.GetUserAccessToken()
    
    resp, err := client.RevokeUserAccessToken(userAccessToken)
    if err != nil {
        // handle error
    }
    
    fmt.Printf("%+v\n", resp)
  7. Set the User-Agent header

    main

    You can customize the User-Agent header sent with every request using two methods:

    1. During Client Initialization: Pass the UserAgent string within the helix.Options struct when calling helix.NewClient().
    2. After Client Initialization: Call the SetUserAgent(string) method on an existing client instance.

    This is useful for identifying your application to the Helix API.

    // Method 1: Via Options
    client, err := helix.NewClient(&helix.Options{
        ClientID:  "your-client-id",
        UserAgent: "your-user-agent-value",
    })
    
    // Method 2: Via SetUserAgent method
    client.SetUserAgent("your-user-agent-value")
  8. Authenticate Extension Helix Requests using EBS JWT

    main

    To make authenticated requests to Extension endpoints, you must initialize the client with ExtensionOptions, generate claims with the appropriate permissions, sign the JWT, and then set it on the client.

    Note: Currently, only the 'external' role is supported by helix endpoints.

    Steps:

    1. Initialize helix.NewClient with ExtensionOpts containing OwnerUserID, Secret, ConfigurationVersion, and Version.
    2. Generate claims using client.ExtensionCreateClaims(broadcasterID, permissions, role, expiration).
    3. Sign the claims using client.ExtensionJWTSign(claims).
    4. Apply the token to the client using client.SetExtensionSignedJWTToken(jwt).
    client, err := helix.NewClient(&helix.Options{
        ClientID:        "your-client-id",
        UserAccessToken: "your-user-access-token",
        ExtensionOpts: helix.ExtensionOptions{
            OwnerUserID:          os.Getenv("EXT_OWNER_ID"),
            Secret:               os.Getenv("EXT_SECRET"),
            ConfigurationVersion: os.Getenv("EXT_CFG_VERSION"),
            Version:              os.Getenv("EXT_VERSION"),
        },
    })
    if err != nil {
        // handle err
    }
    
    // Generate claims with permissions (e.g., Broadcast)
    claims, err := client.ExtensionCreateClaims(broadcasterID, client.FormBroadcastSendPubSubPermissions(), 0)
    if err != nil {
        // handle err
    }
    
    // Sign the JWT
    jwt, err := client.ExtensionJWTSign(claims)
    if err != nil {
        // handle err
    }
    
    // Set the token before making extension endpoint requests
    client.SetExtensionSignedJWTToken(jwt)
  9. Get the Twitch Authorization URL

    main

    Use GetAuthorizationURL to generate a URL that allows users to authenticate with their Twitch accounts. After authorization, the user is redirected to your RedirectURI with an authorization code.

    To use this, initialize a client with ClientID and RedirectURI, then call GetAuthorizationURL with an AuthorizationURLParams object.

    client, err := helix.NewClient(&helix.Options{
        ClientID:    "your-client-id",
        RedirectURI: "https://example.com/auth/callback",
    })
    if err != nil {
        // handle error
    }
    
    url := client.GetAuthorizationURL(&helix.AuthorizationURLParams{
        ResponseType: "code", // or "token"
        Scopes:       []string{"user:read:email"},
        State:        "some-state",
        ForceVerify:  false,
    })
    
    fmt.Printf("%s\n", url)
  10. Verify and handle EventSub notifications

    main

    When receiving a webhook notification from Twitch, follow these steps to ensure security and successful verification:

    1. Verify Signature: Use helix.VerifyEventSubNotification with your configured Secret, the request Header, and the raw request body. This ensures the notification actually came from Twitch.
    2. Handle Challenges: If the request contains a Challenge field (used during the initial subscription handshake), you must respond to the request by writing only the Challenge string back to the client.
    3. Parse Event Data: Once verified, decode the event field (which is a json.RawMessage) into the specific event type struct (e.g., helix.EventSubChannelFollowEvent).
    4. Respond Successfully: Always return a 200 OK status to Twitch to prevent subscription expiration due to high failure rates.
    type eventSubNotification struct {
    	Subscription helix.EventSubSubscription `json:"subscription"`
    	Challenge    string                     `json:"challenge"`
    	Event        json.RawMessage            `json:"event"`
    }
    
    func eventsubFollow(w http.ResponseWriter, r *http.Request) {
        body, err := io.ReadAll(r.Body)
        if err != nil {
            log.Println(err)
            return
        }
        defer r.Body.Close()
    
        // verify that the notification came from twitch using the secret.
        if !helix.VerifyEventSubNotification("s3cre7w0rd", r.Header, string(body)) {
            log.Println("no valid signature on subscription")
            return
        } else {
            log.Println("verified signature for subscription")
        }
    
        var vals eventSubNotification
        err = json.NewDecoder(bytes.NewReader(body)).Decode(&vals)
        if err != nil {
            log.Println(err)
            return
        }
    
        // if there's a challenge in the request, respond with only the challenge to verify your eventsub.
        if vals.Challenge != "" {
            w.Write([]byte(vals.Challenge))
            return
        }
    
        var followEvent helix.EventSubChannelFollowEvent
        err = json.NewDecoder(bytes.NewReader(vals.Event)).Decode(&followEvent)
    
        log.Printf("got follow webhook: %s follows %s\n", followEvent.UserName, followEvent.BroadcasterUserName)
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
  11. Refresh a Device Access Token

    main

    Use RefreshToken to refresh a device access token. You can retrieve the current refresh token from the client using GetRefreshToken().

    client, err := helix.NewClient(&helix.Options{
        ClientID:     "your-client-id",
    })
    if err != nil {
        // handle error
    }
    
    // Get the refresh token from the client
    refreshToken := client.GetRefreshToken() 
    
    if refreshToken != "" {
      resp, err := client.RefreshToken(refreshToken)
      if err != nil {
          // handle error
      }
    
      fmt.Printf("%+v\n", resp)
    }
  12. Refresh a User Access Token

    main

    Use RefreshUserAccessToken to obtain a new user access token using a provided refreshToken.

    client, err := helix.NewClient(&helix.Options{
        ClientID:     "your-client-id",
        ClientSecret: "your-client-secret",
    })
    if err != nil {
        // handle error
    }
    
    refreshToken := "your-refresh-token"
    
    resp, err := client.RefreshUserAccessToken(refreshToken)
    if err != nil {
        // handle error
    }
    
    fmt.Printf("%+v\n", resp)