From 3826ada0691685f2304919662ef9a177eef9a137 Mon Sep 17 00:00:00 2001 From: mzntori <44904735+ElektroEnte@users.noreply.github.com> Date: Fri, 8 Mar 2024 22:53:26 +0100 Subject: [PATCH] Breaks bc `Command` trait can't be made into an object --- src/bot.rs | 110 ++++++++++++++++++++++++++++++++++++++ src/command.rs | 14 +++++ tests/integration_test.rs | 49 +++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 src/bot.rs create mode 100644 src/command.rs create mode 100644 tests/integration_test.rs diff --git a/src/bot.rs b/src/bot.rs new file mode 100644 index 0000000..93c5559 --- /dev/null +++ b/src/bot.rs @@ -0,0 +1,110 @@ +use std::collections::HashMap; +use crate::command::Command; + +use std::sync::Arc; + +use tokio::sync::mpsc::UnboundedReceiver; +use tokio::sync::Mutex; +use twitch_irc::{ + ClientConfig, + SecureTCPTransport, + TwitchIRCClient, + login::StaticLoginCredentials, + message::ServerMessage, +}; + + + +type IncomingMessages = UnboundedReceiver; +pub type Client = TwitchIRCClient; +/// rename types wrapped in `Arc>` to their respective name ending with `AM` +/// so if you have a type `Anatole` for example wrapped, call it `AnatoleAM` FeelsDankMan +pub type ClientAM = Arc>; + + +#[derive(Debug)] +pub struct Bot<'a, P> { + username: &'a str, + oauth_token: &'a str, + payload: Arc>, + commands: Arc>> +} + + +impl<'a, P> Bot<'a, P> +{ + /// Return a message stream and a client wrapped in `Arc>` + fn incoming_messages_and_client(&self) -> (IncomingMessages, ClientAM) { + let login = self.username.to_owned(); + let oauth = self.oauth_token.to_owned(); + + let config = ClientConfig::new_simple( + StaticLoginCredentials::new(login, Some(oauth)) + ); + + let (mut incoming_messages, client) = + TwitchIRCClient::::new(config); + + let client_am = Arc::new(Mutex::new(client)); + return (incoming_messages, client_am); + } + + /// Creates a `Bot` instance using the given username and oauth-token. + /// `payload` can be any type and will be wrapped to `Arc>` and passed to any command executed, + /// where the user can access the contents mutable. + pub fn new<'b>(username: &'b str, oauth_token: &'b str, payload: P) -> Bot<'a, P> + where + 'b : 'a + { + Bot { + username, + oauth_token, + payload: Arc::new(Mutex::new(payload)), + commands: Arc::new(Mutex::new(HashMap::new())), + } + } + + pub async fn add_command(&mut self, identifier: String, command: impl Command) { + let mut commands = self.commands.lock().await; + commands.insert(identifier, command); + } + + pub async fn run(&'a self) { + // set up client and message stream + let (mut incoming_messages, client_am) = self.incoming_messages_and_client(); + let payload = Arc::clone(&self.payload); + let initial_channel = self.username.to_owned(); + + let msg_processor = tokio::spawn(async move { + { + let client = client_am.lock().await; + client.join(initial_channel).unwrap(); + } + + while let Some(message) = incoming_messages.recv().await { + println!("Received message: {:?}", message); + + match message { + ServerMessage::ClearChat(_) => {} + ServerMessage::ClearMsg(_) => {} + ServerMessage::GlobalUserState(_) => {} + ServerMessage::Join(_) => {} + ServerMessage::Notice(_) => {} + ServerMessage::Part(_) => {} + ServerMessage::Ping(_) => {} + ServerMessage::Pong(_) => {} + ServerMessage::Privmsg(_) => {} + ServerMessage::Reconnect(_) => {} + ServerMessage::RoomState(_) => {} + ServerMessage::UserNotice(_) => {} + ServerMessage::UserState(_) => {} + ServerMessage::Whisper(_) => {} + ServerMessage::Generic(_) => {} + _ => {} + } + } + }); + + msg_processor.await.unwrap(); + } +} \ No newline at end of file diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..52f1c1e --- /dev/null +++ b/src/command.rs @@ -0,0 +1,14 @@ +use crate::bot::ClientAM; + +use std::sync::Arc; + +use tokio::sync::Mutex; + +pub trait Command { + type CommandPayLoad; + + async fn execute(&self, pl_am: Arc>, client_am: ClientAM); + + fn help(&self) -> String; + fn info(&self) -> String; +} diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..f860e92 --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,49 @@ +use std::sync::Arc; +use tokio::sync::Mutex; +use twitchbot_rs::{Bot, Command}; +use twitchbot_rs::bot::ClientAM; + + +pub struct Payload { + content: String, +} + + +// pub struct PingCommand; +// +// impl Command for PingCommand { +// type CommandPayLoad = Payload; +// +// async fn execute(&self, pl_am: Arc>, client_am: ClientAM) { +// let client +// } +// +// fn help(&self) -> String { +// todo!() +// } +// +// fn info(&self) -> String { +// todo!() +// } +// } + + +#[tokio::test] +async fn main() { + // let bot = Bot::new(""); + let login = std::env::var("LOGIN").unwrap(); + let oauth = std::env::var("OAUTH").unwrap(); + let bot = Bot::new( + login.as_str(), + oauth.as_str(), + Payload { content: String::new() }, + ); + + + + // bot.add_command() + + bot.run().await; + + println!("hello world!") +} \ No newline at end of file