#twitch #api #request #auth-token #user #client-secret #channel

twitch-api-rs

An async binding on the twitch api (helix) using reqwest

3 unstable releases

0.2.1 Feb 1, 2021
0.2.0 Feb 1, 2021
0.1.0 Oct 19, 2020

#1835 in Web programming


Used in twitch-clip-downloader

GPL-3.0-or-later

77KB
1.5K SLoC

Crates.io Docs.rs Gihub Actions Testing CI

Note: this library only covers the three apis on the critical path for my other project which downloads all the clips for a user's channel. Eventually I'd like to get to 100% coverage, at the very least for Application Endpoints.

twitch-api

A twitch crate used to build and send requests to the twitch helix api.

See Twitch API Reference

Example: Getting the top 20 clips from a creator

To get a list of clips from the twitch api, by a streamers dislplay name, there are 4 steps.

  1. Get an application ClientId and ClientSecret 1
  2. Request a client_flow Authentication Token
  3. Use that token to request information on a user by their display name
  4. Use the UserId returned by that request to request a list of clips associated with their channel.
use std::env;
use std::sync::Arc;

#[tokio::main]
async fn main() {
    use twitch_api_rs::prelude::*;
    use twitch_api_rs::auth::{ClientId, ClientSecret};

    let client_id: ClientId =
        env::var("TWITCH_API_RS_TEST_CLIENT_ID")
            .expect("Client Id environment variable not defined")
            .into();

    let client_secret: ClientSecret =
        env::var("TWITCH_API_RS_TEST_CLIENT_SECRET")
            .expect("Client Secret environment variable not defined")
            .into();

    // Make a reqwest client to send the requests, and wrap it in an arc so it
    // can be reused in multiple futures
    let client = Arc::new(reqwest::Client::new());

    use twitch_api_rs::auth::client_credentials::{
        ClientAuthRequest, ClientAuthResponse, ClientAuthToken,
    };

    // Get a client credentials (application) access token and wrap it in an arc
    // to be used across multiple tasks without repeating
    let auth_token: Arc<ClientAuthToken> = Arc::new(
        match ClientAuthRequest::builder()
            .set_client_id(client_id.clone())
            .set_client_secret(client_secret)
            .make_request(client.clone())
            .await {
                Ok(resp) => {
                    // Create the token from the token value provided by twitch and
                    // your client_id
                    ClientAuthToken::from_client(resp, client_id)
                }

                // Better error handling can be performed here by matching against
                // the type of this requests::RequestError. Elided for conciseness
                Err(e) => panic!("Could not complete auth request for reason {}", e),
            }
    );

    use twitch_api_rs::values::users::UserId;
    use twitch_api_rs::resource::users::get_users::*;

    let user_ids: Vec<UserId> =
        match GetUsersRequest::builder()
            .set_auth(auth_token.clone())
            .add_login("TheHoodlum12")
            .make_request(client.clone())
            .await {
                Ok(mut resp) => {
                    resp.users.into_iter().map(|i| i.id).collect()
                }

                Err(e) =>
                    panic!("Could not complete request for user by display name for reason {}", e),
            };

    use twitch_api_rs::resource::clips::ClipInfo;
    use twitch_api_rs::resource::clips::get_clips::*;
    use twitch_api_rs::values::clips::ClipTitle;

    for user_id in user_ids {
        let auth_token = auth_token.clone();
        let client = client.clone();

        tokio::spawn(async move {
            match GetClipsRequest::builder()
                .set_auth(auth_token)
                .set_broadcaster_id(user_id.clone())
                .make_request(client)
                .await {
                    Ok(resp) => {
                        // Print out the response
                        for clip in resp.clips {
                            eprintln!(
                                "Found clip for broadcaster {:?} titled {:?}",
                                &user_id, clip.title
                            );
                        }
                    }
                    Err(e) =>
                        panic!("Could not get clips for user {:?} for reason {}", user_id, e),
                }
        });
    }
}

1: If you do not already have these then you will need to log into your twitch developer console, create and name a new application, and copy down the associated values.
The client secret that you get from this process is imediately invalidated if you request a new one, and it will not be shown to you once you leave the page where you generate it, so make sure to write it down somewhere safe. It is recommended that you treat it like a password.

Testing

To run the integration tests you need to set the environment variables with valid values from the twitch developer console.

TWITCH_API_RS_TEST_CLIENT_ID=<client_id> /
TWITCH_API_RS_TEST_CLIENT_SECRET=<client_secret> /
cargo test -- --nocapture

If you use cargo-make you can also add the following to your Makefile.toml

[tasks.test-env]
env = { "TWITCH_API_RS_TEST_CLIENT_ID" = "<client_id>", "TWITCH_API_RS_TEST_CLIENT_SECRET" = "<client_secret>" }
command = "cargo"
args = [ "test", "--", "--nocapture" ]

Maintainer: oldwomanjosiah (jhilden13@gmail.com)

Dependencies

~3.5–8.5MB
~195K SLoC