use futures_util::{Future, SinkExt, StreamExt}; use tokio::sync::mpsc::{self, Receiver}; use tokio_tungstenite::{connect_async, tungstenite::Message}; type Result = std::result::Result; #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Failed twitch send text: {0}")] SendWssError(#[from] tokio_tungstenite::tungstenite::Error), } #[derive(Debug)] pub struct TwitchIrcClient<'a> { commands: bool, membership: bool, tags: bool, nick: &'a str, channel: &'a str, access_token: &'a str, client: &'a str, } impl<'a> TwitchIrcClient<'a> { pub fn new(nick: &'a str, channel: &'a str, access_token: &'a str) -> TwitchIrcClient<'a> { TwitchIrcClient { commands: false, membership: false, tags: false, nick, channel, access_token, client: "wss://irc-ws.chat.twitch.tv:443", } } pub fn membership(mut self) -> Self { self.membership = true; self } pub fn commands(mut self) -> Self { self.commands = true; self } pub fn tags(mut self) -> Self { self.tags = true; self } pub async fn run(self) -> Result<(Receiver, impl Future)> { let (tx, rx) = mpsc::channel(1024); let mut capabilities = vec![]; if self.commands { capabilities.push("twitch.tv/commands"); } if self.membership { capabilities.push("twitch.tv/membership"); } if self.tags { capabilities.push("twitch.tv/tags") } let (ws_stream, _) = connect_async(self.client) .await .expect("Failed to connect twitch irc"); let cap = format!("CAP REQ :{}", capabilities.join(" ")); let (mut write, mut read) = ws_stream.split(); let auth_payload = format!("PASS oauth:{}", self.access_token); let nick_payload = format!("NICK {}", self.nick); let join_payload = format!("JOIN #{}", self.channel); write.send(Message::Text(cap)).await?; write.send(Message::Text(auth_payload)).await?; write.send(Message::Text(nick_payload)).await?; write.send(Message::Text(join_payload)).await?; let server = async move { loop { while let Ok(msg) = read.next().await.unwrap() { match msg { Message::Text(msg) => { if let Err(e) = tx.send(msg).await { eprint!("tx send error = {}", e); }; } Message::Ping(msg) => { if let Err(e) = write.send(Message::Ping(msg)).await { eprint!("ping Error : {}", e); }; } Message::Pong(msg) => { println!("Pong {:?}", msg); } Message::Close(msg) => { println!("Close {:?}", msg); } Message::Frame(msg) => { println!("Frame {:?}", msg); } Message::Binary(msg) => { println!("Binary {:?}", msg); } } } } }; Ok((rx, server)) } }