From 803289d7544630df9203f36b0b4decfd8bda0251 Mon Sep 17 00:00:00 2001 From: mzntori <44904735+ElektroEnte@users.noreply.github.com> Date: Sat, 9 Mar 2024 01:36:47 +0100 Subject: [PATCH] funny crate do funny thing --- Cargo.toml | 10 +++++ src/bot.rs | 26 ++++++++----- src/client_queue.rs | 66 +++++++++++++++++++++++++++++++++ src/command.rs | 11 +++--- src/context.rs | 0 src/lib.rs | 12 ++++++ tests/integration_test.rs | 78 +++++++++++++++++++++++++-------------- 7 files changed, 160 insertions(+), 43 deletions(-) create mode 100644 Cargo.toml create mode 100644 src/client_queue.rs create mode 100644 src/context.rs create mode 100644 src/lib.rs diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5207308 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "twitchbot-rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +twitch-irc = "5.0.1" +tokio = { version = "1.33.0", features = ["full"] } diff --git a/src/bot.rs b/src/bot.rs index 93c5559..2b812f4 100644 --- a/src/bot.rs +++ b/src/bot.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::command::Command; -use std::sync::Arc; +use std::sync::{Arc, Mutex as StdMutex}; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::Mutex; @@ -14,7 +14,6 @@ use twitch_irc::{ }; - type IncomingMessages = UnboundedReceiver; pub type Client = TwitchIRCClient; /// rename types wrapped in `Arc>` to their respective name ending with `AM` @@ -22,16 +21,16 @@ pub type Client = TwitchIRCClient; pub type ClientAM = Arc>; -#[derive(Debug)] pub struct Bot<'a, P> { username: &'a str, oauth_token: &'a str, - payload: Arc>, - commands: Arc>> + payload: Arc>, + commands: Arc>>>>, } impl<'a, P> Bot<'a, P> + where P: Send + 'static { /// Return a message stream and a client wrapped in `Arc>` fn incoming_messages_and_client(&self) -> (IncomingMessages, ClientAM) { @@ -53,18 +52,18 @@ impl<'a, P> Bot<'a, P> /// `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 + where + 'b : 'a { Bot { username, oauth_token, - payload: Arc::new(Mutex::new(payload)), + payload: Arc::new(StdMutex::new(payload)), commands: Arc::new(Mutex::new(HashMap::new())), } } - pub async fn add_command(&mut self, identifier: String, command: impl Command) { + pub async fn add_command(&mut self, identifier: String, command: Box>) { let mut commands = self.commands.lock().await; commands.insert(identifier, command); } @@ -72,6 +71,7 @@ impl<'a, P> Bot<'a, P> pub async fn run(&'a self) { // set up client and message stream let (mut incoming_messages, client_am) = self.incoming_messages_and_client(); + let commands = Arc::clone(&self.commands); let payload = Arc::clone(&self.payload); let initial_channel = self.username.to_owned(); @@ -93,7 +93,13 @@ impl<'a, P> Bot<'a, P> ServerMessage::Part(_) => {} ServerMessage::Ping(_) => {} ServerMessage::Pong(_) => {} - ServerMessage::Privmsg(_) => {} + ServerMessage::Privmsg(msg) => { + let mut cmd = commands.lock().await; + if let Some(command) = cmd.get_mut(&msg.message_text) { + let mut queue = command.execute(Arc::clone(&payload), msg); + queue.execute(Arc::clone(&client_am)).await; + } + } ServerMessage::Reconnect(_) => {} ServerMessage::RoomState(_) => {} ServerMessage::UserNotice(_) => {} diff --git a/src/client_queue.rs b/src/client_queue.rs new file mode 100644 index 0000000..8fcb5e2 --- /dev/null +++ b/src/client_queue.rs @@ -0,0 +1,66 @@ +use twitch_irc::message::ReplyToMessage; +use crate::bot::ClientAM; + +pub enum ClientOps { + // channel login + Join(String), + // channel login + Part(String), + // channel login, message + Say(String, String), + // channel login, message + Me(String, String), + // // message + // Reply(String), + // // message + // ReplyMe(String), +} + + +pub struct ClientQueue { + queue: Vec, +} + +impl ClientQueue { + pub fn new() -> Self { + ClientQueue { queue: vec![] } + } + + pub fn join(&mut self, channel: String) { + self.queue.push(ClientOps::Join(channel)); + } + + pub fn part(&mut self, channel: String) { + self.queue.push(ClientOps::Part(channel)); + } + + pub fn say(&mut self, channel: String, message: String) { + self.queue.push(ClientOps::Say(channel, message)); + } + + pub fn me(&mut self, channel: String, message: String) { + self.queue.push(ClientOps::Me(channel, message)); + } + + // pub fn reply(&mut self, message: String) { + // self.queue.push(ClientOps::Reply(message)); + // } + // + // pub fn reply_me(&mut self, message: String) { + // self.queue.push(ClientOps::ReplyMe(message)); + // } + + pub async fn execute(&self, client_am: ClientAM) { + let client = client_am.lock().await; + for op in self.queue.iter() { + match op { + ClientOps::Join(ch) => { client.join(ch.to_string()).unwrap() } + ClientOps::Part(ch) => { client.part(ch.to_string()) } + ClientOps::Say(ch, msg) => { client.say(ch.to_string(), msg.to_string()).await.unwrap() } + ClientOps::Me(ch, msg) => { client.me(ch.to_string(), msg.to_string()).await.unwrap() } + // ClientOps::Reply(ch) => {} + // ClientOps::ReplyMe(msg) => {} + } + } + } +} \ No newline at end of file diff --git a/src/command.rs b/src/command.rs index 52f1c1e..e55e4b0 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,14 +1,13 @@ -use crate::bot::ClientAM; +use crate::ClientQueue; -use std::sync::Arc; +use std::sync::{Arc, Mutex as StdMutex}; -use tokio::sync::Mutex; +use twitch_irc::message::PrivmsgMessage; -pub trait Command { +pub trait Command: Send { type CommandPayLoad; - async fn execute(&self, pl_am: Arc>, client_am: ClientAM); - + fn execute(&self, pl_am: Arc>, ctx: PrivmsgMessage) -> ClientQueue; fn help(&self) -> String; fn info(&self) -> String; } diff --git a/src/context.rs b/src/context.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fb4169e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,12 @@ +pub mod bot; +pub mod command; +pub mod client_queue; +pub mod context; + + +pub use bot::Bot; +pub use command::Command; +pub use client_queue::ClientQueue; + + +pub type Context = twitch_irc::message::PrivmsgMessage; diff --git a/tests/integration_test.rs b/tests/integration_test.rs index f860e92..8589def 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,6 +1,6 @@ -use std::sync::Arc; -use tokio::sync::Mutex; -use twitchbot_rs::{Bot, Command}; +use std::sync::{Arc, Mutex as StdMutex}; + +use twitchbot_rs::{Bot, ClientQueue, Command, Context}; use twitchbot_rs::bot::ClientAM; @@ -9,41 +9,65 @@ pub struct Payload { } -// 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!() -// } -// } +pub struct PingCommand; + +impl Command for PingCommand { + type CommandPayLoad = Payload; + + fn execute(&self, pl_am: Arc>, ctx: Context) -> ClientQueue { + let mut queue = ClientQueue::new(); + + let pl = pl_am.lock().unwrap(); + queue.say("mzntori".to_string(), format!("Pong! {} {}", pl.content, ctx.sender.name)); + println!("Pong executed! {}", pl.content); + + queue + } + + fn help(&self) -> String { + "pongs".to_owned() + } + + fn info(&self) -> String { + "do be ponging".to_owned() + } +} + + +pub struct UrmCommand; + +impl Command for UrmCommand { + type CommandPayLoad = Payload; + + fn execute(&self, pl_am: Arc>, ctx: Context) -> ClientQueue { + let mut pl = pl_am.lock().unwrap(); + pl.content.push('1'); + + ClientQueue::new() + } + + fn help(&self) -> String { + "pongs".to_owned() + } + + fn info(&self) -> String { + "do be ponging".to_owned() + } +} #[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( + let mut bot = Bot::new( login.as_str(), oauth.as_str(), Payload { content: String::new() }, ); - - - // bot.add_command() + bot.add_command("!ping".to_owned(), Box::new(PingCommand {})).await; + bot.add_command("!urm".to_owned(), Box::new(UrmCommand {})).await; bot.run().await; - - println!("hello world!") } \ No newline at end of file