Skip to content

improve examples and comments #367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,111 @@
# Contributing to twitch_api

## Git basics

### Cloning the repo

```sh
git clone https://github.com/twitch-rs/twitch_api.git --recurse-submodules
cd twitch_api
```

You can also use the [GitHub CLI](https://cli.github.com/) to clone the repository.

```sh
gh repo clone twitch-rs/twitch_api
cd twitch_api
```

### Checking out a branch

```sh
git checkout <branch_name>
```

to create a new branch (this is now default behaviour in newer git version)

```sh
git checkout -b <branch_name>
```

### Fetching the git submodules

To get started with using this repository, ensure you have initialized and updated the submodules in order to retrieve their contents.

```sh
git submodule update --init --recursive
```

alternatively, you can also run

```sh
git submodule init
git submodule update
```

### Resetting

```sh
# reset the last commit, keeping changes
git reset HEAD~1
# reset the last commit, discarding changes
git reset HEAD~1 --hard
```

### Rebasing

To rebase your branch on top of the latest changes on the main branch, run

```sh
git pull upstream main --rebase
```

if you want to combine changes into one commit, you can also use rebase to interactively squash commits

```sh
# Using relative commit
git rebase -i HEAD~<number_of_commits>
# Using a specific commit
git rebase -i <commit_hash>
```

## Creating a pull request

To create a pull request, see [GitHub's documentation](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).

To make things easy, you can use the [GitHub CLI](https://cli.github.com/) to fork the repository, checkout issues and create pull requests from the command line.

```sh
# fork the repository, creates a upstream remote, can be used in an already cloned repository as well
gh repo fork twitch-rs/twitch_api --clone
# navigate into the fork
cd twitch_api

# sync submodules
git submodule update --init --recursive

# create a branch
git checkout -b <branch_name>

# checkout an issue and create a branch
gh issue develop -c <number/url>

# create a pull request
gh pr create

# rebase changes with main
git pull upstream main --rebase
```

### Maintaining

#### Updating twitch_oauth2

To update the `twitch_oauth2` submodule, run the appropriate
[`git submodule` command](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
For example, to update to the latest commit on the remote default branch,
you may want to run:

```
```sh
git submodule update --remote twitch_oauth2
```
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ authors = ["Emil Gardström <[email protected]>"]
edition = "2018"
repository = "https://github.com/twitch-rs/twitch_api"
license = "MIT OR Apache-2.0"
description = "Library for talking with the new Twitch API aka. \"Helix\", TMI and more!"
description = "Library for talking with the new Twitch API aka. \"Helix\", EventSub and more!"
keywords = ["oauth", "twitch", "async", "asynchronous", "api"]
documentation = "https://docs.rs/twitch_api/0.7.0-rc.6"
readme = "README.md"
Expand All @@ -28,7 +28,7 @@ exclude = ["twitch_types", "twitch_oauth2"]

[workspace.dependencies]
twitch_api = { version = "0.7.0-rc.6", path = "." }
twitch_oauth2 = { version = "0.12.0", path = "twitch_oauth2/" }
twitch_oauth2 = { version = "0.12.1", path = "twitch_oauth2/" }
twitch_types = { version = "0.4.1", features = [
"serde",
], path = "./twitch_types" }
Expand Down Expand Up @@ -69,7 +69,7 @@ default = ["deser_borrow"]
client = ["twitch_oauth2/client", "dep:futures", "dep:hyper"]

unsupported = ["serde_json?/raw_value", "beta"]
beta = []
beta = ["serde_json?/raw_value"]
deny_unknown_fields = []
trace_unknown_fields = ["dep:serde_ignored", "tracing"]

Expand Down
27 changes: 10 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Twitch API | Rust library for talking with the new Twitch API aka. "Helix", TMI and more!
# Twitch API | Rust library for talking with the new Twitch API aka. "Helix", EventSub and more!

[![github]](https://github.com/twitch-rs/twitch_api)&ensp;[![crates-io]](https://crates.io/crates/twitch_api)&ensp;[![docs-rs-big]](https://docs.rs/twitch_api/0.7.0-rc.6/twitch_api/)&ensp;[![discord]](https://discord.gg/7APWQeEmnK)

Expand All @@ -11,28 +11,24 @@ See [documentation](https://docs.rs/twitch_api) for more info.

You can see current unpublished docs for the main branch here: [![local-docs]](https://twitch-rs.github.io/twitch_api/twitch_api)

See [examples](./examples) for examples.
See [examples](./examples) for examples. If you want to run them locally,
make sure you [get the git submodules](./CONTRIBUTING.md#fetching-the-git-submodules) first.

[local-docs]: https://img.shields.io/github/actions/workflow/status/twitch-rs/twitch_api/gh-pages.yml?label=dev%20docs&style=flat-square&event=push

```rust ,no_run
use twitch_api::helix::HelixClient;
use twitch_api::twitch_oauth2::{AccessToken, UserToken};
use reqwest::Client as ReqwestClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
let client: HelixClient<ReqwestClient> = HelixClient::default();
// Create the HelixClient, which is used to make requests to the Twitch API
let client: HelixClient<reqwest::Client> = HelixClient::default();
// Create a UserToken, which is used to authenticate requests
let token = UserToken::from_token(&client, AccessToken::from("mytoken")).await?;

let token = UserToken::from_existing(
&client,
AccessToken::new("mytoken".to_string()),
None, // Refresh Token
None, // Client Secret
)
.await?;

println!("Channel: {:?}",
println!(
"Channel: {:?}",
client.get_channel_from_login("twitchdev", &token).await?
);

Expand All @@ -50,9 +46,6 @@ This crate aims to target

- [Helix](https://dev.twitch.tv/docs/api/reference)
- See [implemented endpoints](https://github.com/twitch-rs/twitch_api/wiki/Implemented-Features#helix)
- TMI
- See [implemented endpoints](https://github.com/twitch-rs/twitch_api/wiki/Implemented-Features#tmi)
- Note that TMI is planned to be deprecated, with [`Get Chatters`](https://dev.twitch.tv/docs/api/reference#get-chatters) replacing the remaining functionality
- [EventSub](https://dev.twitch.tv/docs/eventsub/eventsub-reference)
- See [implemented eventsub Helix endpoints](https://github.com/twitch-rs/twitch_api/wiki/Implemented-Features#eventsub)
- [PubSub](https://dev.twitch.tv/docs/pubsub) (without a client)
Expand All @@ -67,7 +60,7 @@ There are no current plans to support

- [GraphQL](https://github.com/mauricew/twitch-graphql-api)
- [Drops](https://dev.twitch.tv/docs/drops) (except what is in Helix)
- [Twitch IRC Chat](https://dev.twitch.tv/docs/irc), use [museun/twitchchat](https://github.com/museun/twitchchat) or [robotty/twitch-irc](https://github.com/robotty/twitch-irc-rs/)
- [Twitch IRC Chat](https://dev.twitch.tv/docs/irc), use [museun/twitch_message](https://github.com/museun/twitch_message) or [robotty/twitch-irc](https://github.com/robotty/twitch-irc-rs/)
- [Authentication](https://dev.twitch.tv/docs/authentication), use [twitch-rs/twitch_oauth2](https://github.com/twitch-rs/twitch_oauth2)
- Undocumented Helix endpoints, i.e endpoints mobile Twitch app uses. Including [working "hidden" endpoints](https://thomassen.sh/twitch-api-endpoints/)

Expand Down
38 changes: 38 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Examples

## Getting a token

To run the examples, you will need to have a Twitch OAuth token. You can get one by following the [Twitch OAuth guide](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth).

There are sites available that can help you generate these tokens, or you can use the official [Twitch CLI](https://github.com/twitchdev/twitch-cli), `twitch_oauth2::UserToken::builder()` or `twitch_oauth2::tokens::ImplicitUserTokenBuilder`

## Running the examples

To run an example, ensure you've gotten the [submodules](../CONTRIBUTING.md#fetching-the-git-submodules) and have a [token](#getting-a-token) available.

```sh
git clone https://github.com/twitch-rs/twitch_api.git --recurse-submodules
cd twitch_api
# if you didn't get the submodules, run
# git submodule update --init --recursive
cargo run --example <example_name> -- <token>
```

Some examples are their own crates/workspace members, you can run these with

```sh
cargo run -p <example> -- <args>
```

## .env

Instead of passing a token to every example, you can also create a `.env` file in the root of the repository with the following contents

```txt
# .env
TWITCH_TOKEN=mytoken
CLIENT_ID=myclientid
CLIENT_SECRET=myclientid
```

All of these are optional, you do not need to specify a value if the example doesn't need it or it's supplied as an argument in the commandline or from the environment.
12 changes: 7 additions & 5 deletions examples/eventsub/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ COPY ../../ .
ARG RUSTFLAGS=-Ctarget-feature=-crt-static
ARG CARGO_UNSTABLE_SPARSE_REGISTRY=true
RUN --mount=type=cache,target=$CARGO_HOME/git \
--mount=type=cache,target=$CARGO_HOME/registry \
--mount=type=cache,sharing=private,target=/app/target \
cargo -V; cargo build --release -p eventsub && mv /app/target/release/eventsub /app/is_live
--mount=type=cache,target=$CARGO_HOME/registry \
--mount=type=cache,sharing=private,target=/app/target \
cargo -V
cargo build --release -p eventsub && mv /app/target/release/eventsub /app/is_live
FROM alpine:3.16 as runtime
WORKDIR /app
ARG RUN_DEPS="ca-certificates openssl libgcc"
RUN apk add --no-cache \
${RUN_DEPS}
${RUN_DEPS}
COPY --from=builder /app/is_live /app/is_live
COPY ./examples/eventsub/static ./static
ENTRYPOINT "/app/is_live"
EXPOSE 80
ENTRYPOINT ["/app/is_live", "--interface", "0.0.0.0"]
45 changes: 30 additions & 15 deletions examples/eventsub/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ async fn main() -> Result<(), eyre::Report> {
Ok(())
}

/// Run the application
pub async fn run(opts: &Opts) -> eyre::Result<()> {
let client: HelixClient<'static, _> = twitch_api::HelixClient::with_client(
// Create the HelixClient, which is used to make requests to the Twitch API
let client: HelixClient<_> = twitch_api::HelixClient::with_client(
<reqwest::Client>::default_client_with_name(Some(
"twitch-rs/eventsub"
.parse()
Expand All @@ -65,6 +67,7 @@ pub async fn run(opts: &Opts) -> eyre::Result<()> {
.wrap_err_with(|| "when creating client")?,
);

// Get the app access token
let token = twitch_oauth2::AppAccessToken::get_app_access_token(
&client,
opts.client_id.clone(),
Expand All @@ -73,29 +76,37 @@ pub async fn run(opts: &Opts) -> eyre::Result<()> {
)
.await?;

// Get the user we want to listen to
let broadcaster = client
.get_user_from_login(&opts.broadcaster_login, &token)
.await?
.ok_or_else(|| eyre::eyre!("broadcaster not found"))?;

// Create the config, which is shared between all requests
let config = Arc::new(Config {
broadcaster_url: stream_url_from_user(&broadcaster.login),
broadcaster,
website_url: opts.website.clone(),
});

// Status of the channel
let live = twitch::is_live(&config, &client, &token).await?;

// make the token sharable via an Arc<RwLock<_>>
let token = Arc::new(tokio::sync::RwLock::new(token));

// watch channel for the live status, sent to every website websocket client
let (sender, recv) = watch::channel(live);
let sender = Arc::new(sender);

// Retainer/cache for the eventsub subscriptions, stores the header Twitch-Eventsub-Message-Id to check if we've already seen the request from twitch.
let retainer = Arc::new(retainer::Cache::<axum::http::HeaderValue, ()>::new());
let ret = retainer.clone();
let retainer_cleanup = async move {
let retainer_cleanup = tokio::spawn(async move {
ret.monitor(10, 0.50, tokio::time::Duration::from_secs(86400 / 2))
.await;
Ok::<(), eyre::Report>(())
};
});

let app = Router::new()
.route(
Expand Down Expand Up @@ -143,23 +154,27 @@ pub async fn run(opts: &Opts) -> eyre::Result<()> {
.layer(CatchPanicLayer::new()),
);

// spawn the server
let address = (opts.interface, opts.port).into();
let server = tokio::spawn(async move {
axum::Server::bind(
&"0.0.0.0:80"
.parse()
.wrap_err_with(|| "when parsing address")?,
)
.serve(app.into_make_service())
.await
.wrap_err_with(|| "when serving")?;
axum::Server::bind(&address)
.serve(app.into_make_service())
.await
.wrap_err_with(|| "when serving")?;
Ok::<(), eyre::Report>(())
});
tracing::info!("spinning up server!");
tracing::info!("spinning up server! http://{}", address);

if std::env::var("IM_A_SERVER").is_err() {
tracing::warn!("to run this example you need to be a server with a url with tls,
this means you're either behind a reverse-proxy, or you've setup this example to handle that");
tracing::warn!("to run this example you need to be a server with a url and have https via tls,
this means you're either behind a reverse-proxy, or you've setup this example to handle that");
tracing::warn!(
"set IM_A_SERVER=1 to bypass this check, see eventsub_websocket
for an example which doesn't require a server"
);
std::env::set_var("DEV", "1");
}

let r = tokio::try_join!(
flatten(server),
flatten(tokio::spawn(twitch::checker(
Expand All @@ -181,7 +196,7 @@ pub async fn run(opts: &Opts) -> eyre::Result<()> {
opts.website_callback.clone(),
opts.sign_secret.clone()
))),
flatten(tokio::spawn(retainer_cleanup)),
flatten(retainer_cleanup),
);
r?;
Ok(())
Expand Down
Loading