enh pyramid + chat module
This commit is contained in:
parent
5faf982485
commit
8eaa56dd0c
19 changed files with 455 additions and 176 deletions
forcebot_core
moderator_reactor/src
readme.mdsimple_command_bot/src
simple_module_example/src
|
@ -5,6 +5,7 @@ edition = "2021"
|
||||||
default-run = "fun_bot"
|
default-run = "fun_bot"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
# async-recursion = "1.1.1" /* has issues when used */
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
tokio = { version = "1.33.0", features = ["full"] }
|
tokio = { version = "1.33.0", features = ["full"] }
|
||||||
|
|
|
@ -18,8 +18,7 @@
|
||||||
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
|
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
|
||||||
//! - More Info - <https://dev.twitch.tv/docs/authentication>
|
//! - More Info - <https://dev.twitch.tv/docs/authentication>
|
||||||
|
|
||||||
// use forcebot_rs_v2::{custom_mods::{guest_badge, pyramid}, Bot};
|
use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
|
||||||
use forcebot_core::{custom_mods::{debug, guest_badge, pyramid, quiet}, Bot};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -31,12 +30,10 @@ pub async fn main() {
|
||||||
|
|
||||||
/* 1. Load the module into the bot */
|
/* 1. Load the module into the bot */
|
||||||
bot.load_module(funbot_objs::create_module()).await;
|
bot.load_module(funbot_objs::create_module()).await;
|
||||||
|
|
||||||
/* 2. Load Custom Modules */
|
|
||||||
bot.load_module(guest_badge::create_module()).await;
|
bot.load_module(guest_badge::create_module()).await;
|
||||||
bot.load_module(pyramid::create_module()).await;
|
bot.load_module(pyramid::create_module()).await;
|
||||||
bot.load_module(debug::create_module()).await;
|
bot.load_module(debug::create_module()).await;
|
||||||
bot.load_module(quiet::create_module()).await;
|
|
||||||
|
|
||||||
/* 3. Run the bot */
|
/* 3. Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
@ -69,7 +66,7 @@ pub mod funbot_objs {
|
||||||
|
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg, "annytfYandere he's mine".to_string()).await;
|
&msg, "annytfYandere he's mine".to_string()).await;
|
||||||
return Result::Ok("Success".to_string());
|
return Result::Ok("Success".to_string());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
//! Example simple Binary crate that creates & runs bot based on `.env`
|
|
||||||
//! Be sure the followig is defined in `.env`
|
|
||||||
//! - login_name
|
|
||||||
//! - access_token
|
|
||||||
//! - bot_channels
|
|
||||||
//! - prefix
|
|
||||||
//! - bot_admins
|
|
||||||
//!
|
|
||||||
//! Bot access tokens be generated here -
|
|
||||||
//! - Get a Bot Chat Token here - <https://twitchtokengenerator.com>
|
|
||||||
//! - More Info - <https://dev.twitch.tv/docs/authentication>
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use forcebot_core::{execution_async, Bot, Listener};
|
|
||||||
use twitch_irc::message::ServerMessage;
|
|
||||||
|
|
||||||
#[tokio::main]
|
|
||||||
pub async fn main() {
|
|
||||||
|
|
||||||
/* 1. Create the bot using env */
|
|
||||||
let bot = Bot::new().await;
|
|
||||||
|
|
||||||
/* 2a. Create a new blank Listener */
|
|
||||||
let mut listener = Listener::new();
|
|
||||||
|
|
||||||
/* 2b. Set a trigger condition function for listener */
|
|
||||||
listener.set_trigger_cond_fn(
|
|
||||||
|_:Arc<Bot>,_:ServerMessage| true
|
|
||||||
);
|
|
||||||
|
|
||||||
/* 2c. Define an async fn callback execution */
|
|
||||||
async fn execbody(_:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
|
||||||
dbg!(message); /* outputs message to debug */
|
|
||||||
Result::Ok("Success".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2d. Set and Store the execution body using `async_box()` */
|
|
||||||
listener.set_exec_fn(execution_async(execbody));
|
|
||||||
|
|
||||||
/* 3. Load the listener into the bot */
|
|
||||||
bot.load_listener(listener).await;
|
|
||||||
|
|
||||||
/* 4. Run the bot */
|
|
||||||
bot.run().await;
|
|
||||||
|
|
||||||
}
|
|
|
@ -63,7 +63,7 @@ pub mod custom_mod {
|
||||||
/* 2. Define exec callback */
|
/* 2. Define exec callback */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg, "test return".to_string()).await;
|
&msg, "test return".to_string()).await;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
pub mod bot;
|
pub mod bot;
|
||||||
pub mod bot_objects;
|
pub mod bot_objects;
|
||||||
pub mod modules;
|
pub mod modules;
|
||||||
|
pub mod built_in_mods;
|
||||||
|
pub mod chat;
|
|
@ -1,14 +1,15 @@
|
||||||
|
|
||||||
|
|
||||||
|
// use async_recursion::async_recursion;
|
||||||
use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
|
use tokio::sync::{mpsc::UnboundedReceiver, Mutex};
|
||||||
use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
|
use twitch_irc::{login::StaticLoginCredentials, message::{PrivmsgMessage, ServerMessage}, SecureTCPTransport, TwitchIRCClient};
|
||||||
use dotenv::dotenv;
|
use dotenv::dotenv;
|
||||||
use std::{env, sync::{Arc, RwLock}, time::{Duration, Instant}};
|
use std::{env, sync::{Arc, RwLock}, time::{Duration, Instant}};
|
||||||
|
|
||||||
// use crate::{Badge, Command, Listener, Module};
|
// use crate::{Badge, Command, Listener, Module};
|
||||||
use super::bot_objects::command::Command;
|
use super::{bot_objects::command::Command, built_in_mods, chat::Chat};
|
||||||
|
|
||||||
use crate::botcore::bot_objects::Badge;
|
use crate::botcore::{bot_objects::Badge, chat};
|
||||||
use crate::botcore::bot_objects::listener::Listener;
|
use crate::botcore::bot_objects::listener::Listener;
|
||||||
use super::super::botcore::modules::Module;
|
use super::super::botcore::modules::Module;
|
||||||
// use super::
|
// use super::
|
||||||
|
@ -25,6 +26,8 @@ pub struct Bot
|
||||||
incoming_msgs: Mutex<UnboundedReceiver<ServerMessage>>,
|
incoming_msgs: Mutex<UnboundedReceiver<ServerMessage>>,
|
||||||
/// outbound chat client msg stream
|
/// outbound chat client msg stream
|
||||||
pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
|
pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
|
||||||
|
/// *preferred* bot enforced outbound chat client msg stream
|
||||||
|
pub chat : Mutex<Chat>,
|
||||||
/// joined channels
|
/// joined channels
|
||||||
botchannels: Vec<String>,
|
botchannels: Vec<String>,
|
||||||
/// admin chatters
|
/// admin chatters
|
||||||
|
@ -41,8 +44,8 @@ pub struct Bot
|
||||||
chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
|
chatter_guest_badges: Mutex<Vec<(String,String,Badge,Instant,Duration)>>,
|
||||||
/// Message cache
|
/// Message cache
|
||||||
message_cache: Mutex<Vec<PrivmsgMessage>>,
|
message_cache: Mutex<Vec<PrivmsgMessage>>,
|
||||||
/// channel_quiet
|
// /// channel_quiet
|
||||||
channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
|
// channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +60,8 @@ impl Bot
|
||||||
/// - bot_channels
|
/// - bot_channels
|
||||||
/// - prefix
|
/// - prefix
|
||||||
/// - bot_admins
|
/// - bot_admins
|
||||||
pub async fn new() -> Bot {
|
// #[async_recursion]
|
||||||
|
pub async fn new() -> Arc<Bot> {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -82,7 +86,10 @@ impl Bot
|
||||||
/// Creates a new `Bot` using bot information
|
/// Creates a new `Bot` using bot information
|
||||||
///
|
///
|
||||||
/// Bot will join `botchannels` argument
|
/// Bot will join `botchannels` argument
|
||||||
pub async fn new_from(bot_login_name:String,oauth_token:String,prefix:String,botchannels:Vec<String>) -> Bot {
|
pub async fn new_from(bot_login_name:String,
|
||||||
|
oauth_token:String,
|
||||||
|
prefix:String,
|
||||||
|
botchannels:Vec<String>) -> Arc<Bot> {
|
||||||
|
|
||||||
dotenv().ok();
|
dotenv().ok();
|
||||||
let bot_login_name = bot_login_name;
|
let bot_login_name = bot_login_name;
|
||||||
|
@ -111,7 +118,8 @@ impl Bot
|
||||||
let bot = Bot {
|
let bot = Bot {
|
||||||
prefix,
|
prefix,
|
||||||
incoming_msgs : Mutex::new(incoming_messages),
|
incoming_msgs : Mutex::new(incoming_messages),
|
||||||
client,
|
client : client.clone(),
|
||||||
|
chat : Mutex::new(Chat::new(client).await),
|
||||||
botchannels : botchannels_all,
|
botchannels : botchannels_all,
|
||||||
listeners : Mutex::new(vec![]),
|
listeners : Mutex::new(vec![]),
|
||||||
commands : Mutex::new(vec![]),
|
commands : Mutex::new(vec![]),
|
||||||
|
@ -120,24 +128,53 @@ impl Bot
|
||||||
channel_module_status: RwLock::new(vec![]),
|
channel_module_status: RwLock::new(vec![]),
|
||||||
chatter_guest_badges: Mutex::new(vec![]),
|
chatter_guest_badges: Mutex::new(vec![]),
|
||||||
message_cache : Mutex::new(vec![]),
|
message_cache : Mutex::new(vec![]),
|
||||||
channel_quiet_yn : RwLock::new(vec![]),
|
// channel_quiet_yn : RwLock::new(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async fn load_modules(bot:Bot) -> Bot {
|
||||||
|
// let mut bot1 = bot;
|
||||||
|
|
||||||
|
// bot1.chat = Some(Chat::new(client, bot1));
|
||||||
|
|
||||||
for cmd in built_in_objects::create_commands() {
|
for cmd in built_in_objects::create_commands() {
|
||||||
bot.load_command(cmd).await;
|
bot.load_command(cmd).await;
|
||||||
}
|
}
|
||||||
|
built_in_mods::load_built_in_mods(&bot).await;
|
||||||
|
|
||||||
bot
|
bot
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let bot = load_modules(bot).await;
|
||||||
|
|
||||||
|
|
||||||
|
let bot = Arc::new(bot);
|
||||||
|
|
||||||
|
// let lock = bot.chat.lock().await;
|
||||||
|
|
||||||
|
// *lock = Some(Chat::new(chat, bot.clone()));
|
||||||
|
|
||||||
|
// let cht = Chat::new(chat).await;
|
||||||
|
|
||||||
|
// bot.chat.lock()
|
||||||
|
|
||||||
|
// lock.set_parent_bot(bot.clone());
|
||||||
|
|
||||||
|
println!("Joined - {:?}",bot.botchannels);
|
||||||
|
|
||||||
|
|
||||||
|
bot.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the bot
|
/// Runs the bot
|
||||||
pub async fn run(self) {
|
pub async fn run(self:Arc<Self>) {
|
||||||
|
|
||||||
for chnl in &self.botchannels {
|
for chnl in &self.botchannels {
|
||||||
self.client.join(chnl.to_owned()).unwrap();
|
self.client.join(chnl.to_owned()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let bot = Arc::new(self);
|
// let bot = Arc::new(self);
|
||||||
|
let bot = self;
|
||||||
|
|
||||||
let join_handle = tokio::spawn(async move {
|
let join_handle = tokio::spawn(async move {
|
||||||
|
|
||||||
|
@ -145,6 +182,7 @@ impl Bot
|
||||||
let mut in_msgs_lock = a.incoming_msgs.lock().await;
|
let mut in_msgs_lock = a.incoming_msgs.lock().await;
|
||||||
|
|
||||||
while let Some(message) = in_msgs_lock.recv().await {
|
while let Some(message) = in_msgs_lock.recv().await {
|
||||||
|
// dbg!(message.clone()) ;
|
||||||
|
|
||||||
let bot_listener_lock = bot.listeners.lock().await;
|
let bot_listener_lock = bot.listeners.lock().await;
|
||||||
for listener in bot_listener_lock.iter() {
|
for listener in bot_listener_lock.iter() {
|
||||||
|
@ -429,39 +467,39 @@ impl Bot
|
||||||
rslt
|
rslt
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the quiet status of a channel
|
// /// Get the quiet status of a channel
|
||||||
pub fn get_channel_quiet(&self,channel:String) -> bool {
|
// pub fn get_channel_quiet(&self,channel:String) -> bool {
|
||||||
for a in self.channel_quiet_yn.read().unwrap().iter() {
|
// for a in self.channel_quiet_yn.read().unwrap().iter() {
|
||||||
if a.0 == channel {
|
// if a.0 == channel {
|
||||||
return a.1.read().unwrap().clone();
|
// return a.1.read().unwrap().clone();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// Get the quiet status of a channel
|
// /// Get the quiet status of a channel
|
||||||
pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
|
// pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
|
||||||
let mut found = false;
|
// let mut found = false;
|
||||||
|
|
||||||
let chnlquiet = self.channel_quiet_yn.read().unwrap();
|
// let chnlquiet = self.channel_quiet_yn.read().unwrap();
|
||||||
for rec in chnlquiet.iter() {
|
// for rec in chnlquiet.iter() {
|
||||||
if rec.0 == channel {
|
// if rec.0 == channel {
|
||||||
found = true;
|
// found = true;
|
||||||
let mut status = rec.1.write().unwrap();
|
// let mut status = rec.1.write().unwrap();
|
||||||
*status = quiet_on;
|
// *status = quiet_on;
|
||||||
drop(status);
|
// drop(status);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
drop(chnlquiet);
|
// drop(chnlquiet);
|
||||||
|
|
||||||
if !found {
|
// if !found {
|
||||||
// dbg!("set chn quiet > !found channel quiet status");
|
// // dbg!("set chn quiet > !found channel quiet status");
|
||||||
let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
|
// let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
|
||||||
chnlquiet.push((channel,RwLock::new(quiet_on)));
|
// chnlquiet.push((channel,RwLock::new(quiet_on)));
|
||||||
drop(chnlquiet);
|
// drop(chnlquiet);
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ pub enum Badge {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub type ExecBody = Box<
|
pub type ExecBody = Box<
|
||||||
dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
|
dyn Fn(Arc<Bot>,ServerMessage) -> Pin<Box<dyn Future<Output = Result<String,String>> + Send>> + Send + Sync,
|
||||||
>;
|
>;
|
||||||
|
@ -108,14 +109,6 @@ pub mod built_in_objects {
|
||||||
|
|
||||||
use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
|
use super::{execution_async,command::Command,Bot,Badge,super::modules::Status};
|
||||||
|
|
||||||
// use super::execution_async;
|
|
||||||
// use super::command::Command;
|
|
||||||
// use super::Bot;
|
|
||||||
// use super::Badge;
|
|
||||||
// use super::super::modules::Status;
|
|
||||||
// ::{execution_async, modules::Status, Badge, Bot, Command};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// create a vector of command build in objects
|
/// create a vector of command build in objects
|
||||||
pub fn create_commands() -> Vec<Command>
|
pub fn create_commands() -> Vec<Command>
|
||||||
|
@ -148,7 +141,7 @@ pub mod built_in_objects {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if action_taken {
|
if action_taken {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
@ -199,7 +192,7 @@ pub mod built_in_objects {
|
||||||
bot_message = bot_message[..250].to_string();
|
bot_message = bot_message[..250].to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = bot.client.say_in_reply_to(&msg,
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg,
|
||||||
format!("Enabled! {}", bot_message)
|
format!("Enabled! {}", bot_message)
|
||||||
).await ;
|
).await ;
|
||||||
|
|
||||||
|
@ -257,7 +250,7 @@ pub mod built_in_objects {
|
||||||
msg.channel_login.clone(),
|
msg.channel_login.clone(),
|
||||||
Badge::Moderator, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
Badge::Moderator, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
||||||
|
|
||||||
let _ = bot.client.say_in_reply_to(&msg,
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg,
|
||||||
format!("Temp {:?} issued for {:?} minutes",Badge::Moderator,TEMP_BADGE_DUR_MIN)
|
format!("Temp {:?} issued for {:?} minutes",Badge::Moderator,TEMP_BADGE_DUR_MIN)
|
||||||
).await ;
|
).await ;
|
||||||
}
|
}
|
||||||
|
@ -279,8 +272,8 @@ pub mod built_in_objects {
|
||||||
msg.channel_login.clone(),
|
msg.channel_login.clone(),
|
||||||
Badge::Vip, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
Badge::Vip, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
||||||
|
|
||||||
let _ = bot.client.say_in_reply_to(&msg,
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg,
|
||||||
format!("Temp {:?} issued for {:?} minutes",Badge::Vip,TEMP_BADGE_DUR_MIN)
|
format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Vip,TEMP_BADGE_DUR_MIN)
|
||||||
).await ;
|
).await ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,14 +293,14 @@ pub mod built_in_objects {
|
||||||
msg.channel_login.clone(),
|
msg.channel_login.clone(),
|
||||||
Badge::Broadcaster, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
Badge::Broadcaster, Instant::now(), Duration::from_secs(60*TEMP_BADGE_DUR_MIN)).await;
|
||||||
|
|
||||||
let _ = bot.client.say_in_reply_to(&msg,
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg,
|
||||||
format!("Temp {:?} issued for {:?} minutes",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
|
format!("Temp {:?} issued for {:?} minutes for the bot admin",Badge::Broadcaster,TEMP_BADGE_DUR_MIN)
|
||||||
).await ;
|
).await ;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// let _ = bot.client.say_in_reply_to(&msg, String::from("Disabled!")).await ;
|
// let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("Disabled!")).await ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use twitch_irc::message::{PrivmsgMessage, ServerMessage};
|
use twitch_irc::message::{PrivmsgMessage, ServerMessage};
|
||||||
|
|
||||||
// use crate::{botcore::bot::Bot, command_condition_async, execution_async, Badge};
|
|
||||||
use super::{execution_async,Bot,Badge,command_condition_async};
|
use super::{execution_async,Bot,Badge,command_condition_async};
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,17 +188,18 @@ impl Command
|
||||||
(cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
|
(cmd.custom_cond_fn)(bot.clone(),message.clone()) && (cmd.custom_cond_async)(bot,message).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
|
// async fn quiet_off_ok(cmd:&Command,bot:Arc<Bot>,message:PrivmsgMessage) -> bool {
|
||||||
!bot.get_channel_quiet(message.channel_login.clone())
|
// !bot.chat.lock().await.get_channel_quiet(message.channel_login.clone())
|
||||||
|| bot.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
|
// || bot.chat.lock().await.get_channel_quiet(message.channel_login.clone()) && cmd.commands.contains(&("quiet off".to_string()))
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
cmd_called(self, bot.clone(), msg.clone()) &&
|
cmd_called(self, bot.clone(), msg.clone()) &&
|
||||||
caller_badge_ok(self, bot.clone(), msg.clone()).await &&
|
caller_badge_ok(self, bot.clone(), msg.clone()).await &&
|
||||||
admin_only_ok(self, bot.clone(), msg.clone()) &&
|
admin_only_ok(self, bot.clone(), msg.clone()) &&
|
||||||
custom_cond_ok(self, bot.clone(), msg.clone()).await &&
|
custom_cond_ok(self, bot.clone(), msg.clone()).await
|
||||||
quiet_off_ok(self, bot, msg)
|
// &&
|
||||||
|
// quiet_off_ok(self, bot, msg).await
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
// use tokio::sync::Mutex;
|
|
||||||
use twitch_irc::message::ServerMessage;
|
use twitch_irc::message::ServerMessage;
|
||||||
|
|
||||||
use crate::Module;
|
use crate::Module;
|
||||||
|
@ -116,20 +116,21 @@ impl Listener
|
||||||
(list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
|
(list.trigger_cond_fn)(bot.clone(),msg.clone()) && (list.trigger_cond_async)(bot,msg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
|
// async fn quiet_off_ok(list:Arc<Listener>,bot:Arc<Bot>,message:ServerMessage) -> bool {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
// if let ServerMessage::Privmsg(msg) = message {
|
||||||
|
|
||||||
if let Some(parent_mod) = &*list.parent_module {
|
// if let Some(parent_mod) = &*list.parent_module {
|
||||||
return !bot.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
|
// return !bot.chat.lock().await.get_channel_quiet(msg.channel_login) || parent_mod.get_names().contains(&"debug".to_string());
|
||||||
}
|
// }
|
||||||
|
|
||||||
return !bot.get_channel_quiet(msg.channel_login) ;
|
// return !bot.chat.lock().await.get_channel_quiet(msg.channel_login) ;
|
||||||
}
|
// }
|
||||||
return true; /* quiet is off for non chat msgs */
|
// return true; /* quiet is off for non chat msgs */
|
||||||
}
|
// }
|
||||||
|
|
||||||
defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await &&
|
defined_conditions_ok(list.clone(), bot.clone(), msg.clone()).await
|
||||||
quiet_off_ok(list, bot, msg)
|
// &&
|
||||||
|
// quiet_off_ok(list, bot, msg).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// executes the listeners executon body
|
/// executes the listeners executon body
|
||||||
|
|
14
forcebot_core/src/botcore/built_in_mods.rs
Normal file
14
forcebot_core/src/botcore/built_in_mods.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::Bot;
|
||||||
|
|
||||||
|
pub mod quiet;
|
||||||
|
|
||||||
|
|
||||||
|
/// used to internally load internal modules
|
||||||
|
pub async fn load_built_in_mods(bot:&Bot){
|
||||||
|
|
||||||
|
bot.load_module(quiet::create_module()).await;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -44,8 +44,15 @@ fn cmd_quiet_on() -> Command {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
|
|
||||||
// dbg!("quiet on called");
|
// dbg!("quiet on called");
|
||||||
bot.set_channel_quiet(msg.channel_login.clone(), true);
|
|
||||||
|
let chatlock = bot.chat.lock().await;
|
||||||
|
let _=chatlock.say_in_reply_to(&msg, "Shush ".to_string()).await;
|
||||||
|
|
||||||
|
chatlock.set_channel_quiet(msg.channel_login.clone(), true);
|
||||||
println!("channel {} set quiet true",msg.channel_login);
|
println!("channel {} set quiet true",msg.channel_login);
|
||||||
|
|
||||||
|
|
||||||
|
return Result::Ok("Success".to_string());
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
}
|
}
|
||||||
|
@ -68,7 +75,12 @@ fn cmd_quiet_off() -> Command {
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
|
|
||||||
bot.set_channel_quiet(msg.channel_login.clone(), false);
|
let chatlock = bot.chat.lock().await;
|
||||||
|
|
||||||
|
chatlock.set_channel_quiet(msg.channel_login.clone(), false);
|
||||||
|
let _=chatlock.say_in_reply_to(&msg, "GoodGirl I'll be good for u chat rar ".to_string()).await;
|
||||||
|
|
||||||
|
|
||||||
println!("channel {} set quiet false",msg.channel_login);
|
println!("channel {} set quiet false",msg.channel_login);
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
151
forcebot_core/src/botcore/chat.rs
Normal file
151
forcebot_core/src/botcore/chat.rs
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
use std::{fmt::Error, ops::Mul, rc::Rc, sync::{Arc, Mutex, RwLock}};
|
||||||
|
|
||||||
|
use twitch_irc::{login::StaticLoginCredentials, message::ReplyToMessage, SecureTCPTransport, TwitchIRCClient};
|
||||||
|
|
||||||
|
use crate::Bot;
|
||||||
|
|
||||||
|
/// Bot API to send messages to send messages to chat
|
||||||
|
///
|
||||||
|
/// Uses TwitchIRCClient say_in_reply_to() but enforces controls like quiet
|
||||||
|
///
|
||||||
|
///
|
||||||
|
|
||||||
|
pub struct Chat
|
||||||
|
{
|
||||||
|
/// outbound chat client msg stream
|
||||||
|
pub client: TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>,
|
||||||
|
/// channel_quiet
|
||||||
|
channel_quiet_yn: RwLock<Vec<(String,RwLock<bool>)>>,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Chat {
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn new(client:TwitchIRCClient<SecureTCPTransport,StaticLoginCredentials>)
|
||||||
|
-> Chat {
|
||||||
|
Chat {
|
||||||
|
client,
|
||||||
|
// parent_bot : Mutex::new(Bot::new().await) ,
|
||||||
|
channel_quiet_yn : RwLock::new(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn set_parent_bot(&self,parent_bot_in:Arc<Bot>)
|
||||||
|
// {
|
||||||
|
// let mut lock = self.parent_bot.lock().unwrap();
|
||||||
|
// *lock = parent_bot_in;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// helper
|
||||||
|
fn ok_to_send(&self,channel_login: String) -> bool {
|
||||||
|
|
||||||
|
fn not_quiet_ok(chat:&Chat,channel_login:String) -> bool {
|
||||||
|
// let lock = chat.parent_bot.lock().unwrap();
|
||||||
|
// let a = lock.as_ref();
|
||||||
|
// if let Some(bot) = &*lock {
|
||||||
|
return !chat.get_channel_quiet(channel_login);
|
||||||
|
// }
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
not_quiet_ok(self, channel_login)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get the quiet status of a channel
|
||||||
|
pub fn get_channel_quiet(&self,channel:String) -> bool {
|
||||||
|
for a in self.channel_quiet_yn.read().unwrap().iter() {
|
||||||
|
if a.0 == channel {
|
||||||
|
return a.1.read().unwrap().clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the quiet status of a channel
|
||||||
|
pub fn set_channel_quiet(&self,channel:String,quiet_on:bool) {
|
||||||
|
let mut found = false;
|
||||||
|
|
||||||
|
let chnlquiet = self.channel_quiet_yn.read().unwrap();
|
||||||
|
for rec in chnlquiet.iter() {
|
||||||
|
if rec.0 == channel {
|
||||||
|
found = true;
|
||||||
|
let mut status = rec.1.write().unwrap();
|
||||||
|
*status = quiet_on;
|
||||||
|
drop(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drop(chnlquiet);
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
// dbg!("set chn quiet > !found channel quiet status");
|
||||||
|
let mut chnlquiet = self.channel_quiet_yn.write().unwrap();
|
||||||
|
chnlquiet.push((channel,RwLock::new(quiet_on)));
|
||||||
|
drop(chnlquiet);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub async fn say_in_reply_to(&self,
|
||||||
|
reply_to: &impl ReplyToMessage,
|
||||||
|
message: String
|
||||||
|
) -> Result<(),()> {
|
||||||
|
// reply_to.channel_login()
|
||||||
|
if self.ok_to_send(reply_to.channel_login().to_string()) {
|
||||||
|
match self.client.say_in_reply_to(reply_to, message).await {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn say(&self,
|
||||||
|
channel_login: String,
|
||||||
|
message: String,
|
||||||
|
) -> Result<(),()> {
|
||||||
|
if self.ok_to_send(channel_login.to_string()) {
|
||||||
|
match self.client.say(channel_login, message).await {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn me(&self,
|
||||||
|
channel_login: String,
|
||||||
|
message: String,
|
||||||
|
) -> Result<(),()> {
|
||||||
|
if self.ok_to_send(channel_login.to_string()) {
|
||||||
|
match self.client.me(channel_login, message).await {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn me_in_reply_to(&self,
|
||||||
|
reply_to: &impl ReplyToMessage,
|
||||||
|
message: String
|
||||||
|
) -> Result<(),()> {
|
||||||
|
if self.ok_to_send(reply_to.channel_login().to_string()) {
|
||||||
|
match self.client.me_in_reply_to(reply_to, message).await {
|
||||||
|
Ok(_) => return Ok(()),
|
||||||
|
Err(_) => return Err(()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
pub mod guest_badge;
|
pub mod guest_badge;
|
||||||
pub mod pyramid;
|
pub mod pyramid;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
pub mod quiet;
|
// pub mod quiet;
|
|
@ -79,7 +79,7 @@ fn create_cmd_vip() -> Command {
|
||||||
if badges_issued {
|
if badges_issued {
|
||||||
|
|
||||||
|
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg.clone(), format!("Guest badges issued for {} min",guest_dur_min)).await;
|
&msg.clone(), format!("Guest badges issued for {} min",guest_dur_min)).await;
|
||||||
return Result::Ok("Success".to_string());
|
return Result::Ok("Success".to_string());
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ fn create_cmd_mod() -> Command {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if badges_issued {
|
if badges_issued {
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg, format!("Guest badges issued for {} min",MOD_GIVEN_DUR_MIN)).await;
|
&msg, format!("Guest badges issued for {} min",MOD_GIVEN_DUR_MIN)).await;
|
||||||
return Result::Ok("Success".to_string());
|
return Result::Ok("Success".to_string());
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ fn create_pyramid_detector() -> Listener {
|
||||||
/* 2. Define an async trigger condition callback */
|
/* 2. Define an async trigger condition callback */
|
||||||
async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
|
async fn condition01(bot:Arc<Bot>,message:ServerMessage) -> bool {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
|
if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await && get_pyramid_size(msg.channel_login) > 3 {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,11 +65,33 @@ fn create_pyramid_detector() -> Listener {
|
||||||
/* 4. Define an async fn callback execution */
|
/* 4. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
|
dbg!("enter pyramid listener execution - after pyramid complete");
|
||||||
// if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
|
// if detect_pyramid_complete_ok(bot.clone(), msg.clone()).await {
|
||||||
|
|
||||||
let _ = bot.client.say_in_reply_to(&msg, "Clap".to_string()).await ;
|
// let _ = bot.chat.lock().await.
|
||||||
|
dbg!("> get start pattern");
|
||||||
|
let pattern = get_start_pattern(msg.channel_login.clone());
|
||||||
|
let mut outmsg ;
|
||||||
|
|
||||||
set_pyramid_started(msg.channel_login,false);
|
/* Prefer emote before pattern in case pattern is command */
|
||||||
|
if pattern.len() < 50 {
|
||||||
|
outmsg = format!("Clap {}",pattern);
|
||||||
|
} else {
|
||||||
|
outmsg = "Clap".to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!(get_pyramid_size(msg.channel_login.clone()));
|
||||||
|
if get_pyramid_size(msg.channel_login.clone()) < 4 {
|
||||||
|
outmsg = format!("{} annytfMagniGlass",outmsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
dbg!("> start pattern :",pattern);
|
||||||
|
|
||||||
|
dbg!("> say_in_reply_to completed :",outmsg.clone());
|
||||||
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, outmsg).await ;
|
||||||
|
|
||||||
|
dbg!("> set pyramid started - false");
|
||||||
|
set_pyramid_started(msg.channel_login.clone(),false);
|
||||||
|
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
// }
|
// }
|
||||||
|
@ -84,18 +106,23 @@ fn create_pyramid_detector() -> Listener {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// detect pyramid based on latest message and channel
|
||||||
|
///
|
||||||
|
///
|
||||||
async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
|
async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
|
||||||
|
dbg!("enter detect_pyramid_complete()");
|
||||||
|
|
||||||
let msgtext = msg.message_text.replace("\u{e0000}","").trim().to_string();
|
let msgtext = msg.message_text.replace("","").replace("\u{e0000}","").trim().to_string();
|
||||||
let msgchannel = msg.channel_login;
|
let msgchannel = msg.channel_login;
|
||||||
let msgchatter = msg.sender.login;
|
let msgchatter = msg.sender.login;
|
||||||
|
|
||||||
// 1. Check if Pyramid started in chat > and recognize pyramid started
|
// 1. Check if Pyramid started in chat > and recognize pyramid started
|
||||||
if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
|
if !is_pyramid_started(msgchannel.clone()) & check_start_pyramid(msgchannel.clone(),msgtext.clone(),) {
|
||||||
|
dbg!("> set pyramid started - true");
|
||||||
set_pyramid_started(msgchannel.clone(),true);
|
set_pyramid_started(msgchannel.clone(),true);
|
||||||
push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
|
push_to_compare(msgchannel.clone(),msgchatter.clone(),get_start_pattern(msgchannel.clone()));
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if is_pyramid_started(msgchannel.clone()) {
|
if is_pyramid_started(msgchannel.clone()) {
|
||||||
|
@ -109,15 +136,29 @@ async fn detect_pyramid_complete_ok(_bot:Arc<Bot>,msg:PrivmsgMessage) -> bool {
|
||||||
|
|
||||||
// 2b. If Pyramid is Started, and the latest message is the pattern, check for
|
// 2b. If Pyramid is Started, and the latest message is the pattern, check for
|
||||||
// symmetry to determine pyramid
|
// symmetry to determine pyramid
|
||||||
|
|
||||||
if is_pyramid_started(msgchannel.clone()) && msgtext.clone() == get_start_pattern(msgchannel.clone()) {
|
if is_pyramid_started(msgchannel.clone()) && msgtext.clone() == get_start_pattern(msgchannel.clone()) {
|
||||||
if symmetry_ok(msgchannel.clone()) {
|
if symmetry_ok(msgchannel.clone()) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
dbg!("> set pyramid started - false");
|
||||||
|
set_pyramid_started(msgchannel,false);
|
||||||
|
|
||||||
return false ;
|
return false ;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2c. if Pyramid is strted but latest message does not ontain pattern
|
||||||
|
if is_pyramid_started(msgchannel.clone()) && !msgtext.clone().contains( get_start_pattern(msgchannel.clone()).as_str()) {
|
||||||
|
|
||||||
|
dbg!("> set pyramid started - false");
|
||||||
|
set_pyramid_started(msgchannel,false);
|
||||||
|
|
||||||
|
return false ;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,6 +171,8 @@ lazy_static!{
|
||||||
pub static ref PYRAMID_STARTED_PER_CHNL: Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
|
pub static ref PYRAMID_STARTED_PER_CHNL: Mutex<Vec<(String,Mutex<bool>)>> = Mutex::new(vec![]);
|
||||||
/// Start patterns per channel (channel:String,pattern:String)
|
/// Start patterns per channel (channel:String,pattern:String)
|
||||||
pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
|
pub static ref START_PATTERNS_PER_CHNL: Mutex<Vec<(String,Mutex<String>)>> = Mutex::new(vec![]);
|
||||||
|
/// Pyramid sze per channel (channel:String,started:bool)
|
||||||
|
pub static ref PYRAMID_SIZE_PER_CHNL: Mutex<Vec<(String,Mutex<i32>)>> = Mutex::new(vec![]);
|
||||||
/// temp message stack checker
|
/// temp message stack checker
|
||||||
pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
|
pub static ref TEMP_MSG_STACK: Mutex<Vec<String>> = Mutex::new(vec![]);
|
||||||
|
|
||||||
|
@ -249,6 +292,11 @@ fn push_to_compare(channel:String,chatter:String,message:String) {
|
||||||
/// checks latest and next latest messages for potential start
|
/// checks latest and next latest messages for potential start
|
||||||
fn check_start_pyramid(channel:String,msgtext: String) -> bool {
|
fn check_start_pyramid(channel:String,msgtext: String) -> bool {
|
||||||
msgtext == format!("{} {}",get_start_pattern(channel.clone()),get_start_pattern(channel.clone()))
|
msgtext == format!("{} {}",get_start_pattern(channel.clone()),get_start_pattern(channel.clone()))
|
||||||
|
// msgtext == format!("{} {} {}",
|
||||||
|
// get_start_pattern(channel.clone()),
|
||||||
|
// get_start_pattern(channel.clone()),
|
||||||
|
// get_start_pattern(channel.clone())
|
||||||
|
// )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -259,13 +307,17 @@ fn symmetry_ok(channel:String) -> bool {
|
||||||
if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
|
if !(read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut pyramid_size = 0;
|
||||||
loop {
|
loop {
|
||||||
|
|
||||||
if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {
|
if !checking_started && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1 == get_start_pattern(channel.clone()) {
|
||||||
checking_started = true;
|
checking_started = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
|
if temp_stack.last().is_none() || read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() > temp_stack.last().unwrap_or(&"".to_string()).len() {
|
||||||
temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
|
temp_stack.push(pop_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1);
|
||||||
|
pyramid_size += 1;
|
||||||
|
|
||||||
} else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
|
} else if temp_stack.last().is_some() && read_top_of_compare(channel.clone()).unwrap_or(("".to_string(),"".to_string())).1.len() < temp_stack.last().unwrap_or(&"".to_string()).len() {
|
||||||
|
|
||||||
|
@ -276,13 +328,20 @@ fn symmetry_ok(channel:String) -> bool {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
set_pyramid_size(channel.clone(), 0);
|
||||||
temp_stack.clear();
|
temp_stack.clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else { return false; }
|
} else {
|
||||||
|
set_pyramid_size(channel.clone(), 0);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {
|
if checking_started && read_top_of_compare(channel.clone()).unwrap().1 == get_start_pattern(channel.clone()) {
|
||||||
|
|
||||||
|
/* leave pyramid size set for exection */
|
||||||
|
set_pyramid_size(channel.clone(), pyramid_size*2-1);
|
||||||
temp_stack.clear();
|
temp_stack.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -293,6 +352,32 @@ fn symmetry_ok(channel:String) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn set_pyramid_size(channel:String,size:i32) {
|
||||||
|
let mut size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
|
||||||
|
let mut found = false;
|
||||||
|
for rec in size_perchnl.iter() {
|
||||||
|
if rec.0 == channel {
|
||||||
|
found = true;
|
||||||
|
let mut rec_started = rec.1.lock().unwrap();
|
||||||
|
*rec_started = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
size_perchnl.push((channel,Mutex::new(size)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_pyramid_size(channel:String) -> i32 {
|
||||||
|
let size_perchnl = PYRAMID_SIZE_PER_CHNL.lock().unwrap();
|
||||||
|
for rec in size_perchnl.iter() {
|
||||||
|
if rec.0 == channel {
|
||||||
|
let rec_started = rec.1.lock().unwrap();
|
||||||
|
return *rec_started;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
/// #todo
|
/// #todo
|
||||||
///
|
///
|
||||||
/// pyramid interruptor
|
/// pyramid interruptor
|
||||||
|
@ -316,7 +401,7 @@ fn _create_interruptor_cmd() -> Command {
|
||||||
/* 2. Define an async fn callback execution */
|
/* 2. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub async fn main() {
|
||||||
/* 3. Define an async fn callback execution */
|
/* 3. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
|
71
readme.md
71
readme.md
|
@ -27,18 +27,36 @@ cargo run -p forcebot_core
|
||||||
|
|
||||||
# Features
|
# Features
|
||||||
|
|
||||||
- Quick Start to use full feature set bot
|
## Built In Chat Commands
|
||||||
- Moderators & Broadcasters can `disable` or `enable` `Modules` of bot functionality through chat `Commands`
|
|
||||||
- Full Feature Set `forcebot_core` bot has the following modules loaded
|
- `quiet on` / `quiet off` - Moderators & Broadcasters can quiet the bot
|
||||||
|
|
||||||
|
- `enable $module$` / `disable $module$` - Moderators & Broadcasters can enable or disable `Modules` of bot functionality through chat `Commands`
|
||||||
|
|
||||||
|
|
||||||
|
## Custom Modules can be coded to load additional functionality
|
||||||
|
|
||||||
|
Developers an create Modules that add more bot functionality
|
||||||
|
|
||||||
|
The main `forcebot_core` Binary crate includes the following Custom `Modules`
|
||||||
|
|
||||||
- `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off`
|
- `debug` - outputs to console messages from the channel where it was enabled. Toggle debug with the Commands `debug on` or `debug off`
|
||||||
- `guest_badge` - Temporary badges can be issued to chatters
|
- `guest_badge` - Temporary badges can be issued to chatters
|
||||||
- `besty` - Tomfoolery
|
- `besty` - Tomfoolery
|
||||||
- `pyramid` - for detecting & handling pyramids
|
- `pyramid` - for detecting & handling pyramids
|
||||||
|
|
||||||
|
|
||||||
|
## `forcebot_core` Bot Library
|
||||||
|
|
||||||
- `forcebot_core` library API provides Custom package developers a way to add functionality by adding `Modules` that contain Bot Objects like `Commands` and `Listeners`
|
- `forcebot_core` library API provides Custom package developers a way to add functionality by adding `Modules` that contain Bot Objects like `Commands` and `Listeners`
|
||||||
- `Listeners` and `Commands` listen for a defined callback trigger condition and run an defined execution callback
|
- `Listeners` and `Commands` listen for a defined callback trigger condition and run an defined execution callback
|
||||||
- `Commands` are similar to `Listeners` with refined trigger conditions including using bot `prefix` with the `Command` , triggers based on `Badge` , and more
|
- `Commands` are similar to `Listeners` with refined trigger conditions including using bot `prefix` with the `Command` , triggers based on `Badge` , and more
|
||||||
- Workspace for package developers to independently code their own `Modules`
|
- Workspace for package developers to independently code their own `Modules`
|
||||||
- Workspace comes with binary crates with working or example bots that use `forcebot_core` library
|
|
||||||
|
## Workspaces
|
||||||
|
|
||||||
|
|
||||||
|
Workspace comes with binary crates with working or example bots that use `forcebot_core` library
|
||||||
- `moderator_reactor` - bot kneels to all moderator messages
|
- `moderator_reactor` - bot kneels to all moderator messages
|
||||||
- `simple_module_example` - bot has a `test` `Module` with a `test` `Command` . Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
|
- `simple_module_example` - bot has a `test` `Module` with a `test` `Command` . Moderators & Broadcasters can manage the `Module` in chat with `enable` / `disable` `Commands`
|
||||||
- `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
|
- `new_empty_bot` - while empty, has `disable` and `enable` chat `Commands` . This is an example of the bot without any loaded modules
|
||||||
|
@ -150,7 +168,7 @@ use forcebot_core::Bot;
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* 1. Create the bot using env */
|
/* 1. Create the bot using env */
|
||||||
let bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
/* 2. Run the bot */
|
/* 2. Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
@ -165,18 +183,24 @@ A `Module` is a group of bot objects (eg `Command`) that elevated users can mana
|
||||||
Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
|
Custom `Modules` can be loaded into a new bot with minimum coding : just load the modules and run the bot
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use forcebot_core::{custom_mods::{guest_badge, pyramid}, Bot};
|
use forcebot_core::{custom_mods::{debug, guest_badge, pyramid}, Bot};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* 1. Create the bot using env */
|
/* Create the bot using env */
|
||||||
let mut bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
|
/* 1. Load the module into the bot */
|
||||||
|
bot.load_module(funbot_objs::create_module()).await;
|
||||||
|
|
||||||
/* 2. Load Custom Modules */
|
/* 2. Load Custom Modules */
|
||||||
bot.load_module(guest_badge::create_module()).await;
|
bot.load_module(guest_badge::create_module()).await;
|
||||||
bot.load_module(pyramid::create_module()).await;
|
bot.load_module(pyramid::create_module()).await;
|
||||||
|
bot.load_module(debug::create_module()).await;
|
||||||
|
|
||||||
|
|
||||||
/* 3. Run the bot */
|
/* 3. Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
@ -195,13 +219,14 @@ Create a custom `Module` by :
|
||||||
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
||||||
use forcebot_core::Bot;
|
use forcebot_core::Bot;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* Create the bot using env */
|
/* Create the bot using env */
|
||||||
let mut bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
/* load the Module */
|
/* load the Module */
|
||||||
bot.load_module(custom_mod::new()).await;
|
bot.load_module(custom_mod::new()).await;
|
||||||
|
@ -211,6 +236,7 @@ pub async fn main() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub mod custom_mod {
|
pub mod custom_mod {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -240,7 +266,7 @@ pub mod custom_mod {
|
||||||
/* 2. Define exec callback */
|
/* 2. Define exec callback */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg, "test return".to_string()).await;
|
&msg, "test return".to_string()).await;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
@ -249,7 +275,7 @@ pub mod custom_mod {
|
||||||
/* 3. Set Command flags */
|
/* 3. Set Command flags */
|
||||||
cmd.set_exec_fn(execution_async(execbody));
|
cmd.set_exec_fn(execution_async(execbody));
|
||||||
cmd.set_admin_only(false);
|
cmd.set_admin_only(false);
|
||||||
cmd.set_min_badge(Badge::Moderator);
|
cmd.set_min_badge(Badge::Vip);
|
||||||
|
|
||||||
cmd
|
cmd
|
||||||
}
|
}
|
||||||
|
@ -260,6 +286,7 @@ pub mod custom_mod {
|
||||||
Bot with a simple listener that listens for all messages and prints in output
|
Bot with a simple listener that listens for all messages and prints in output
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use forcebot_core::{execution_async, Bot, Listener};
|
use forcebot_core::{execution_async, Bot, Listener};
|
||||||
|
@ -269,7 +296,7 @@ use twitch_irc::message::ServerMessage;
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* 1. Create the bot using env */
|
/* 1. Create the bot using env */
|
||||||
let mut bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
/* 2a. Create a new blank Listener */
|
/* 2a. Create a new blank Listener */
|
||||||
let mut listener = Listener::new();
|
let mut listener = Listener::new();
|
||||||
|
@ -289,12 +316,13 @@ pub async fn main() {
|
||||||
listener.set_exec_fn(execution_async(execbody));
|
listener.set_exec_fn(execution_async(execbody));
|
||||||
|
|
||||||
/* 3. Load the listener into the bot */
|
/* 3. Load the listener into the bot */
|
||||||
bot.load_listener(listener);
|
bot.load_listener(listener).await;
|
||||||
|
|
||||||
/* 4. Run the bot */
|
/* 4. Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Moderator Reactor
|
## Moderator Reactor
|
||||||
|
@ -302,6 +330,7 @@ pub async fn main() {
|
||||||
Example listener listens for a moderator badge and reply in chat
|
Example listener listens for a moderator badge and reply in chat
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use forcebot_core::Bot;
|
use forcebot_core::Bot;
|
||||||
|
@ -314,7 +343,7 @@ use twitch_irc::message::ServerMessage;
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* Create the bot using env */
|
/* Create the bot using env */
|
||||||
let mut bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
/* 1. Create a new blank Listener */
|
/* 1. Create a new blank Listener */
|
||||||
let mut listener = Listener::new();
|
let mut listener = Listener::new();
|
||||||
|
@ -324,7 +353,6 @@ pub async fn main() {
|
||||||
listener.set_trigger_cond_fn(
|
listener.set_trigger_cond_fn(
|
||||||
|_:Arc<Bot>,message:ServerMessage|
|
|_:Arc<Bot>,message:ServerMessage|
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
|
|
||||||
for badge in msg.badges {
|
for badge in msg.badges {
|
||||||
if matches!(badge, x if x.name == "moderator") {
|
if matches!(badge, x if x.name == "moderator") {
|
||||||
// dbg!("moderator found");
|
// dbg!("moderator found");
|
||||||
|
@ -338,7 +366,7 @@ pub async fn main() {
|
||||||
/* 3. Define an async fn callback execution */
|
/* 3. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, "pepeKneel".to_string()).await ;
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
@ -348,12 +376,13 @@ pub async fn main() {
|
||||||
listener.set_exec_fn(execution_async(execbody));
|
listener.set_exec_fn(execution_async(execbody));
|
||||||
|
|
||||||
/* 5. Load the listener into the bot */
|
/* 5. Load the listener into the bot */
|
||||||
bot.load_listener(listener);
|
bot.load_listener(listener).await;
|
||||||
|
|
||||||
/* Run the bot */
|
/* Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Simple Test Command
|
## Simple Test Command
|
||||||
|
@ -367,11 +396,12 @@ use forcebot_core::execution_async;
|
||||||
use forcebot_core::Command;
|
use forcebot_core::Command;
|
||||||
use twitch_irc::message::ServerMessage;
|
use twitch_irc::message::ServerMessage;
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() {
|
pub async fn main() {
|
||||||
|
|
||||||
/* Create the bot using env */
|
/* Create the bot using env */
|
||||||
let mut bot = Bot::new();
|
let bot = Bot::new().await;
|
||||||
|
|
||||||
/* 1. Create a new blank cmd */
|
/* 1. Create a new blank cmd */
|
||||||
let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
|
let mut cmd = Command::new(vec!["test".to_string()],"".to_string());
|
||||||
|
@ -379,7 +409,7 @@ pub async fn main() {
|
||||||
/* 2. Define an async fn callback execution */
|
/* 2. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
@ -395,12 +425,13 @@ pub async fn main() {
|
||||||
cmd.set_min_badge(Badge::Moderator);
|
cmd.set_min_badge(Badge::Moderator);
|
||||||
|
|
||||||
/* 6. Load the cmd into the bot */
|
/* 6. Load the cmd into the bot */
|
||||||
bot.load_command(cmd);
|
bot.load_command(cmd).await;
|
||||||
|
|
||||||
/* Run the bot */
|
/* Run the bot */
|
||||||
bot.run().await;
|
bot.run().await;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Crate Rust API Documentation
|
# Crate Rust API Documentation
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub async fn main() {
|
||||||
/* 2. Define an async fn callback execution */
|
/* 2. Define an async fn callback execution */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _ = bot.client.say_in_reply_to(&msg, String::from("test success")).await;
|
let _ = bot.chat.lock().await.say_in_reply_to(&msg, String::from("test success")).await;
|
||||||
return Result::Ok("Success".to_string()) ;
|
return Result::Ok("Success".to_string()) ;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub mod custom_mod {
|
||||||
/* 2. Define exec callback */
|
/* 2. Define exec callback */
|
||||||
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
async fn execbody(bot:Arc<Bot>,message:ServerMessage) -> Result<String,String> {
|
||||||
if let ServerMessage::Privmsg(msg) = message {
|
if let ServerMessage::Privmsg(msg) = message {
|
||||||
let _= bot.client.say_in_reply_to(
|
let _= bot.chat.lock().await.say_in_reply_to(
|
||||||
&msg, "test return".to_string()).await;
|
&msg, "test return".to_string()).await;
|
||||||
}
|
}
|
||||||
Result::Err("Not Valid message type".to_string())
|
Result::Err("Not Valid message type".to_string())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue