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