Skip to main content

twitch_irc/
lib.rs

1#![warn(missing_docs)]
2//! This is a client library to interface with [Twitch](https://www.twitch.tv/) chat.
3//!
4//! This library is async and runs using the `tokio` runtime.
5//!
6//! # Getting started
7//!
8//! The central feature of this library is the `TwitchIRCClient` which connects to Twitch IRC for you using a pool of
9//! connections and handles all the important bits. Here is a minimal example to get you started:
10//!
11//! ```no_run
12//! use twitch_irc::login::StaticLoginCredentials;
13//! use twitch_irc::ClientConfig;
14//! use twitch_irc::SecureTCPTransport;
15//! use twitch_irc::TwitchIRCClient;
16//!
17//! #[tokio::main]
18//! pub async fn main() {
19//!     // default configuration is to join chat as anonymous.
20//!     let config = ClientConfig::default();
21//!     let (mut incoming_messages, client) =
22//!         TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config);
23//!
24//!     // first thing you should do: start consuming incoming messages,
25//!     // otherwise they will back up.
26//!     let join_handle = tokio::spawn(async move {
27//!         while let Some(message) = incoming_messages.recv().await {
28//!             println!("Received message: {:?}", message);
29//!         }
30//!     });
31//!
32//!     // join a channel
33//!     // This function only returns an error if the passed channel login name is malformed,
34//!     // so in this simple case where the channel name is hardcoded we can ignore the potential
35//!     // error with `unwrap`.
36//!     client.join("sodapoppin".to_owned()).unwrap();
37//!
38//!     // keep the tokio executor alive.
39//!     // If you return instead of waiting the background task will exit.
40//!     join_handle.await.unwrap();
41//! }
42//! ```
43//!
44//! The above example connects to chat anonymously and listens to messages coming to the channel `sodapoppin`.
45//!
46//! # Features
47//!
48//! * Simple API
49//! * Integrated connection pool, new connections will be made based on your application's demand (based on amount of
50//!   channels joined as well as number of outgoing messages)
51//! * Automatic reconnect of failed connections, automatically re-joins channels
52//! * Rate limiting of new connections
53//! * Support for refreshing login tokens, see below
54//! * Fully parses all message types (see [`ServerMessage`](message/enum.ServerMessage.html) for all supported types)
55//! * Can connect using all protocol types supported by Twitch
56//! * Supports Rustls as well as Native TLS
57//! * No unsafe code
58//! * Feature flags to reduce compile time and binary size
59//!
60//! # Send messages
61//!
62//! To send messages, use the `TwitchIRCClient` handle you get from `TwitchIRCClient::new`.
63//!
64//! ```no_run
65//! # use twitch_irc::login::StaticLoginCredentials;
66//! # use twitch_irc::ClientConfig;
67//! # use twitch_irc::SecureTCPTransport;
68//! # use twitch_irc::TwitchIRCClient;
69//! #
70//! # #[tokio::main]
71//! # async fn main() {
72//! # let config = ClientConfig::default();
73//! # let (mut incoming_messages, client) = TwitchIRCClient::<SecureTCPTransport, StaticLoginCredentials>::new(config);
74//! client.say("a_channel".to_owned(), "Hello world!".to_owned()).await.unwrap();
75//! # }
76//! ```
77//!
78//! The `TwitchIRCClient` handle can also be cloned and then used from multiple threads.
79//!
80//! See the documentation on [`TwitchIRCClient`](struct.TwitchIRCClient.html) for the possible methods.
81//!
82//! # Receive and handle messages
83//!
84//! Incoming messages are [`ServerMessage`](message/enum.ServerMessage.html)s. You can use a match block to
85//! differentiate between the possible server messages:
86//!
87//! ```no_run
88//! # use twitch_irc::message::ServerMessage;
89//! # use tokio::sync::mpsc;
90//! #
91//! # #[tokio::main]
92//! # async fn main() {
93//! # let mut incoming_messages: mpsc::UnboundedReceiver<ServerMessage> = unimplemented!();
94//! while let Some(message) = incoming_messages.recv().await {
95//!      match message {
96//!          ServerMessage::Privmsg(msg) => {
97//!              println!("(#{}) {}: {}", msg.channel_login, msg.sender.name, msg.message_text);
98//!          },
99//!          ServerMessage::Whisper(msg) => {
100//!              println!("(w) {}: {}", msg.sender.name, msg.message_text);
101//!          },
102//!          _ => {}
103//!      }
104//! }
105//! # }
106//! ```
107//!
108//! # Logging in
109//!
110//! `twitch_irc` ships with [`StaticLoginCredentials`](login/struct.StaticLoginCredentials.html) and
111//! [`RefreshingLoginCredentials`](login/struct.RefreshingLoginCredentials.html).
112//!
113//! For simple cases, `StaticLoginCredentials` fulfills all needs:
114//!
115//! ```
116//! use twitch_irc::login::StaticLoginCredentials;
117//! use twitch_irc::ClientConfig;
118//!
119//! let login_name = "your_bot_name".to_owned();
120//! let oauth_token = "u0i05p6kbswa1w72wu1h1skio3o20t".to_owned();
121//!
122//! let config = ClientConfig::new_simple(
123//!     StaticLoginCredentials::new(login_name, Some(oauth_token))
124//! );
125//! ```
126//!
127//! However for most applications it is strongly recommended to have your login token automatically refreshed when it
128//! expires. For this, enable one of the `refreshing-token` feature flags (see [Feature flags](#feature-flags)), and use
129//! [`RefreshingLoginCredentials`](login/struct.RefreshingLoginCredentials.html), for example like this:
130//!
131//! ```no_run
132//! # #[cfg(feature = "__refreshing-token")] {
133//! use async_trait::async_trait;
134//! use twitch_irc::login::{RefreshingLoginCredentials, TokenStorage, UserAccessToken};
135//! use twitch_irc::ClientConfig;
136//!
137//! #[derive(Debug)]
138//! struct CustomTokenStorage {
139//!     // fields...
140//! }
141//!
142//! #[async_trait]
143//! impl TokenStorage for CustomTokenStorage {
144//!     type LoadError = std::io::Error; // or some other error
145//!     type UpdateError = std::io::Error;
146//!
147//!     async fn load_token(&mut self) -> Result<UserAccessToken, Self::LoadError> {
148//!         // Load the currently stored token from the storage.
149//!         Ok(UserAccessToken {
150//!             access_token: todo!(),
151//!             refresh_token: todo!(),
152//!             created_at: todo!(),
153//!             expires_at: todo!()
154//!         })
155//!     }
156//!
157//!     async fn update_token(&mut self, token: &UserAccessToken) -> Result<(), Self::UpdateError> {
158//!         // Called after the token was updated successfully, to save the new token.
159//!         // After `update_token()` completes, the `load_token()` method should then return
160//!         // that token for future invocations
161//!         todo!()
162//!     }
163//! }
164//!
165//! // these credentials can be generated for your app at https://dev.twitch.tv/console/apps
166//! // the bot's username will be fetched based on your access token
167//! let client_id = "rrbau1x7hl2ssz78nd2l32ns9jrx2w".to_owned();
168//! let client_secret = "m6nuam2b2zgn2fw8actt8hwdummz1g".to_owned();
169//! let storage = CustomTokenStorage { /* ... */ };
170//!
171//! let credentials = RefreshingLoginCredentials::init(client_id, client_secret, storage);
172//! // It is also possible to use the same credentials in other places
173//! // such as API calls by cloning them.
174//! let config = ClientConfig::new_simple(credentials);
175//! // then create your client and use it
176//! # }
177//! ```
178//!
179//! `RefreshingLoginCredentials` needs an implementation of `TokenStorage` that depends on your application, to retrieve
180//! the token or update it. For example, you might put the token in a config file you overwrite, some extra file for
181//! secrets, or a database.
182//!
183//! In order to get started with `RefreshingLoginCredentials`, you need to have initial access and refresh tokens
184//! present in your storage. You can fetch these tokens using the [OAuth authorization code
185//! flow](https://dev.twitch.tv/docs/authentication/getting-tokens-oauth#oauth-authorization-code-flow). There is also a
186//! [`GetAccessTokenResponse`](crate::login::GetAccessTokenResponse) helper struct that allows you to decode the `POST
187//! /oauth2/token` response as part of the authorization process. See the documentation on that type for details on
188//! usage and how to convert the decoded response to a `UserAccessToken` that you can then write to your `TokenStorage`.
189//!
190//! # Close the client
191//!
192//! To close the client, drop all clones of the `TwitchIRCClient` handle. The client will shut down and end the stream
193//! of incoming messages once all processing is done.
194//!
195//! # Feature flags
196//!
197//! This library has these optional feature toggles:
198//! * **`transport-tcp`** enables `PlainTCPTransport`, to connect using a plain-text TLS socket using the normal IRC
199//!   protocol.
200//!     * `transport-tcp-native-tls` enables `SecureTCPTransport` which will then use OS-native TLS functionality to
201//!       make a secure connection. Root certificates are the ones configured in the operating system.
202//!     * `transport-tcp-rustls-native-roots` enables `SecureTCPTransport` using [Rustls][rustls] as the TLS
203//!       implementation, will use the root certificates configured in the operating system via
204//!       `rustls-platform-verifier`.
205//!     * `transport-tcp-rustls-webpki-roots` enables `SecureTCPTransport` using [Rustls][rustls] as the TLS
206//!       implementation, and will statically embed the current [Mozilla root certificates][mozilla-roots] as the
207//!       trusted root certificates.
208//! * **`transport-ws`** enables `PlainWSTransport` to connect using the Twitch-specific websocket method. (Plain-text)
209//!     * `transport-ws-native-tls` further enables `SecureWSTransport` which will then use OS-native TLS functionality
210//!       to make a secure connection. Root certificates are the ones configured in the operating system.
211//!     * `transport-ws-rustls-native-roots` enables `SecureWSTransport` using [Rustls][rustls] as the TLS
212//!       implementation, but will use the root certificates configured in the operating system via
213//!       `rustls-platform-verifier`.
214//!     * `transport-ws-rustls-webpki-roots` enables `SecureWSTransport` using [Rustls][rustls] as the TLS
215//!       implementation, and will statically embed the current [Mozilla root certificates][mozilla-roots] as the
216//!       trusted root certificates.
217//! * Three different feature flags are provided to enable the
218//!   [`RefreshingLoginCredentials`](crate::login::RefreshingLoginCredentials):
219//!     * `refreshing-token-native-tls` enables this feature using the OS-native TLS functionality to make secure
220//!       connections. Root certificates are the ones configured in the operating system.
221//!     * `refreshing-token-rustls-native-roots` enables this feature using [Rustls][rustls] as the TLS implementation,
222//!       but will use the root certificates configured in the operating system via `rustls-platform-verifier`.
223//!     * `refreshing-token-rustls-webpki-roots` enables this feature using [Rustls][rustls] as the TLS implementation,
224//!       and will statically embed the current [Mozilla root certificates][mozilla-roots] as the trusted root
225//!       certificates.
226//! * **`metrics-collection`** enables a set of metrics to be exported from the client. See the documentation on
227//!   [`ClientConfig::metrics_config`] for details. You may also want to see the `metrics` example, which also contains
228//!   further helpful guidelines on a possible setup to make use of this feature, including a [Grafana dashboard
229//!   template](https://grafana.com/grafana/dashboards/20702).
230//! * **`with-serde`** pulls in `serde` v1.0 and adds `#[derive(Serialize, Deserialize)]` to many structs. This feature
231//!   flag is automatically enabled when using any of the `refreshing-token` feature flags.
232//!
233//! By default, `transport-tcp` and `transport-tcp-native-tls` are enabled.
234//!
235//! [rustls]: https://github.com/ctz/rustls
236//! [mozilla-roots]: https://github.com/ctz/webpki-roots
237
238pub mod client;
239mod config;
240mod connection;
241mod error;
242pub mod login;
243pub mod message;
244#[cfg(feature = "metrics-collection")]
245mod metrics;
246pub mod transport;
247pub mod validate;
248
249pub use client::TwitchIRCClient;
250pub use config::ClientConfig;
251#[cfg(feature = "metrics-collection")]
252pub use config::MetricsConfig;
253pub use error::Error;
254
255#[cfg(feature = "transport-tcp")]
256pub use transport::tcp::PlainTCPTransport;
257#[cfg(all(
258    feature = "transport-tcp",
259    any(
260        feature = "transport-tcp-native-tls",
261        feature = "transport-tcp-rustls-native-roots",
262        feature = "transport-tcp-rustls-webpki-roots"
263    )
264))]
265pub use transport::tcp::SecureTCPTransport;
266
267#[cfg(feature = "transport-ws")]
268pub use transport::websocket::PlainWSTransport;
269#[cfg(all(
270    feature = "transport-ws",
271    any(
272        feature = "transport-ws-native-tls",
273        feature = "transport-ws-rustls-native-roots",
274        feature = "transport-ws-rustls-webpki-roots",
275    )
276))]
277pub use transport::websocket::SecureWSTransport;