twitchchat
This crate provides a way to interact with Twitch's chat.
Along with parse messages as Rust types, it provides methods for sending messages.
Demonstration
See examples/demo.rs for a larger example
Configuration features
Feature | Description |
---|---|
default | enables async and tokio_native_tls (the default) |
async | enables tokio support (and all of the async methods/types) |
tokio_native_tls | uses native_tls (OpenSSL, SChannel, SecureTransport) for TLS |
tokio_rustls | uses rusttls for TLS |
serde | enables serde Serialize/Deserialize on most of the types |
Connecting to Twitch
This crate allows you connect to Twitch with a TLS stream, or without one.
With TLS
NOTE the async blocks are here so the doctests will work, you'll likely have it in a 'larger' async context
Connect with a
UserConfig
:
// make a user config, builder lets you configure it.
let user_config = builder.build.unwrap;
// the conn type is an tokio::io::{AsyncRead + AsyncWrite}
let conn = async ;
Connect with default capabilities using just a username and oauth token:
let my_oauth = var.unwrap;
// the conn type is an tokio::io::{AsyncRead + AsyncWrite}
let conn = async ;
Without TLS (an unsecure plain-text connection)
Connect with a UserConfig:
// make a user config, builder lets you configure it.
let user_config = builder.build.unwrap;
// the conn type is an tokio::io::{AsyncRead+AsyncWrite}
let conn = async ;
Connect with default capabilities using just a username and oauth token:
let my_oauth = var.unwrap;
// the conn type is an tokio::io::{AsyncRead+AsyncWrite}
let conn = async ;
You can even connect with an anonymous users that doesn't require an OAuth token.
This will let you join and read messages from channels, but not write them. Also you'd be limited in what sort of metadata you can receive.
With TLS
let = ANONYMOUS_LOGIN;
let conn = async ;
Without TLS
let = ANONYMOUS_LOGIN;
let conn = async ;
A synchronous 'connect' is provided for completionist sake.
This crate is intended to be used with async types, but the Encoder
and decode
methods will work without them.
Disabling async
will only give you these types.
See twitchchat::sync
for synchronous types.
// make a user config, builder lets you configure it.
let user_config = builder.build.unwrap;
let conn: TcpStream = connect.unwrap;
Or
let my_oauth = var.unwrap;
let conn: TcpStream = connect_easy.unwrap;
Parsing messages
Parsing is done with the decode(&str)
or decode_one(&str)
methods. Twitch (IRC) messages are delimited by CRLF [0xD, 0xA]
Parsing potentially many messages.
let input = "@badge-info=subscriber/8;color=#59517B;tmi-sent-ts=1580932171144;user-type= :tmi.twitch.tv USERNOTICE #justinfan1234\r\n";
// decode takes in a string and returns an Iterator of Result<Message<'a>, Error>
// the flatten here just 'unwraps' the Results safely
for msg in decode.flatten
Parsing a single message
let input =
":tmi.twitch.tv PING 1234567\r\n:museun!museun@museun.tmi.twitch.tv JOIN #museun\r\n";
// parse one message at a time
// this returns the index of the start of the possible next message
let = decode_one.unwrap;
assert!;
assert_eq!;
// use the new index
let = decode_one.unwrap;
assert_eq!;
assert_eq!;
Parsing a
decode::Message<'a>
into a subtype
// Enables parsing a decode::Message<'a> into some subtype (e.g. messages::Privmsg<'a>).
use Parse as _;
use ;
let input = ":museun!museun@museun.tmi.twitch.tv JOIN #some_test_channel\r\n";
let msg: Message = decode.next.unwrap.unwrap;
// this borrows from the 'decode::Message' so its super cheap.
let join: Join = parse.unwrap;
assert_eq!;
assert_eq!;
Parsing a
decode::Message<'a>
into an enum of all possible messages
// Enables parsing a decode::Message<'a> into some subtype (e.g. messages::Privmsg<'a>).
use Parse as _;
use ;
let input = ":museun!museun@museun.tmi.twitch.tv JOIN #some_test_channel\r\n";
let msg: Message = decode.next.unwrap.unwrap;
// this borrows from the 'decode::Message' so its super cheap.
match parse.unwrap
Taking ownership of a parsed message
// Enables parsing a decode::Message<'a> into some subtype (e.g. messages::Privmsg<'a>).
use Parse as _;
// Enables converting a Message<'a> to a Message<'static> (or any of the subtypes).
use AsOwned as _;
use ;
let input = ":museun!museun@museun.tmi.twitch.tv JOIN #some_test_channel\r\n";
let msg: Message = decode.next.unwrap.unwrap;
// can be used to take ownership of a 'Message<'a>'
let owned: Message = msg.as_owned;
assert_eq!;
let join = parse.unwrap;
// or even a subtype
let join_owned: Join = join.as_owned;
assert_eq!;
assert_eq!;
Getting data out of the tags
use Parse as _;
use ;
let input = "@badge-info=subscriber/8;color=#59517B;tmi-sent-ts=1580932171144;user-type= :tmi.twitch.tv USERNOTICE #justinfan1234\r\n";
let msg = decode.next.unwrap.unwrap;
let user_notice = parse.unwrap;
// the tags are parsed and are accessible as methods
// colors can be parsed into rgb/named types
assert_eq!;
// you can manually get tags from the message
let ts = user_notice.tags.get.unwrap;
assert_eq!;
// or as a type
let ts = user_notice
.tags
.
.unwrap;
assert_eq!;
Event dispatching/streams
Along with connecting to twitch and parsing strings, this crate can do that for you and provide you typed asynchronous Streams for events you're interested in.
use ;
// for working with streams, the futures::stream::StreamExt trait will also work.
use StreamExt as _;
/// make a new event dispatcher
let dispatcher = new;
// you can subscribe to an event
// this'll return a Stream which'll produce a `messages` type
// for example, events::Join will produce messages::Join<'static>
let mut joins = dispatcher.;
// you can subscribe to the same event multiple times
let mut more_joins = dispatcher.;
// you can subscribe to 'All' to get an enum of all possible events
let mut all = dispatcher.;
// and you can subscribe to 'Raw' to get the 'raw' decode::Message type
let mut raw = dispatcher.;
// dropping these streams will 'unsubscribe' them
let fut = async move ;
// lets be fancy and use a select over stream
let fut = async move ;
Finally, writing (encoding) messages.
// you probably want to keep a dispatcher around so you can read from the conn
let = new;
// the control type is also clonable
let mut ctrl_clone = control.clone;
// the Control type has a way to get a &mut borrow to a writer
let writer = control.writer;
// async block is here for the test, you'll likely have a larger async context
async move ;
// you can also clone the writer and send across tasks/threads
let writer = control.writer;
let mut w1 = writer.clone;
let mut w2 = w1.clone;
async move ;
You can use the AsyncEncoder
and Encoder
types to wrap io types.
Async
use Cursor;
use AsyncEncoder;
// AsyncEncoder wraps a tokio::io::AsyncWrite type and provides a 'typed' way of writing messages.
async ;
Sync
use Cursor;
// or get it from twitchchat:sync::Encoder;
use Encoder;
// Encoder wraps a std::io::Write type and provides a 'typed' way of writing messages.
// cursor implements Write
let cursor = new;
let mut encoder = new;
encoder.privmsg.unwrap;
// get a reference to the inner type
// get a mutable reference to the inner type
// convert the encoder back into the wrapped type
let cursor: = encoder.into_inner;
Putting it together, a simple "bot"
use StreamExt as _;
use ;
async
License
twitchchat
is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0).
See LICENSE-APACHE and LICENSE-MIT for details.