adding more stuff for lua command support
This commit is contained in:
parent
0f40bc98c3
commit
c7b54b0ed5
8 changed files with 282 additions and 99 deletions
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -1517,18 +1517,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
||||
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.61"
|
||||
version = "1.0.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
commands.help = {
|
||||
sys.registerCommand({
|
||||
aliases = { "h" }, -- other strings that you can run the command from
|
||||
pretty_name = "Help", -- the name of the command as it shows up in menues and other commands that use its pretty name
|
||||
name = "help", -- the main required string used to call the command
|
||||
|
@ -9,13 +9,13 @@ commands.help = {
|
|||
permissions = nil, -- which discord permissions they need to run the command
|
||||
|
||||
func = function(context, message, guildid)
|
||||
for k,v in pairs(commands) do
|
||||
for k, v in pairs(commands) do
|
||||
info("Help info for command", k)
|
||||
end
|
||||
end
|
||||
}
|
||||
})
|
||||
|
||||
commands["help"].func()
|
||||
sys.executeCommand("help", "E")
|
||||
|
||||
-- exit(0)
|
||||
-- local seen={}
|
||||
|
@ -37,4 +37,4 @@ commands["help"].func()
|
|||
-- end
|
||||
-- end
|
||||
|
||||
-- dump(_G,"")
|
||||
-- dump(_G,"")
|
||||
|
|
61
src/bot.rs
61
src/bot.rs
|
@ -1,18 +1,19 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
|
||||
use crate::commands::{self, Command};
|
||||
use crate::{constants, tasks, DO_SHUTDOWN};
|
||||
use serenity::all::EventHandler;
|
||||
use crate::commands::{self, COMMANDS};
|
||||
use crate::{constants, tasks, DO_SHUTDOWN, NO_DISCORD};
|
||||
use serenity::all::{ActivityData, Context, GuildId};
|
||||
use serenity::all::{EventHandler, GatewayIntents, ShardManager};
|
||||
use serenity::model::channel::Message;
|
||||
use serenity::model::gateway::Ready;
|
||||
use serenity::Client;
|
||||
use tracing::*;
|
||||
|
||||
pub struct BotHandler {
|
||||
// TODO use data field instead?
|
||||
pub commands: HashMap<String, Command>,
|
||||
}
|
||||
pub static SHARD_MANAGER: OnceLock<Arc<ShardManager>> = OnceLock::new();
|
||||
|
||||
pub struct BotHandler;
|
||||
|
||||
unsafe impl Sync for BotHandler {}
|
||||
unsafe impl Send for BotHandler {}
|
||||
|
@ -61,7 +62,7 @@ impl EventHandler for BotHandler {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
if let Some(command) = self.commands.get(&target_cmd_name) {
|
||||
if let Some(command) = COMMANDS.read().await.get(&target_cmd_name) {
|
||||
if let Some(guild_id) = msg.guild_id {
|
||||
if let Some(run_guild_command) = &command.run_guild_command {
|
||||
match run_guild_command {
|
||||
|
@ -97,3 +98,47 @@ impl EventHandler for BotHandler {
|
|||
info!("Cache ready.");
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start() {
|
||||
// TODO load this at runtime so the key will not be stored in the binary?
|
||||
#[cfg(not(debug_assertions))]
|
||||
let token = {
|
||||
info!("Initializing bot with production token.");
|
||||
include_str!("bot_token.prod")
|
||||
};
|
||||
#[cfg(debug_assertions)]
|
||||
let token = {
|
||||
info!("Initializing bot with development token.");
|
||||
include_str!("bot_token.dev")
|
||||
};
|
||||
|
||||
let intents = GatewayIntents::DIRECT_MESSAGES
|
||||
| GatewayIntents::DIRECT_MESSAGE_REACTIONS
|
||||
| GatewayIntents::GUILDS
|
||||
| GatewayIntents::GUILD_MODERATION
|
||||
// | GatewayIntents::GUILD_EMOJIS_AND_STICKERS
|
||||
| GatewayIntents::GUILD_MEMBERS
|
||||
| GatewayIntents::GUILD_MESSAGE_REACTIONS
|
||||
| GatewayIntents::GUILD_MESSAGES
|
||||
| GatewayIntents::MESSAGE_CONTENT;
|
||||
|
||||
if *NO_DISCORD.get().unwrap() {
|
||||
warn!("ABORTING CONNECTING TO DISCORD, BYE BYE!");
|
||||
} else {
|
||||
let mut client = match Client::builder(&token, intents)
|
||||
.event_handler(BotHandler)
|
||||
.await
|
||||
{
|
||||
Ok(client) => client,
|
||||
Err(err) => panic!("Error starting client connection: `{err}`"),
|
||||
};
|
||||
|
||||
SHARD_MANAGER.set(client.shard_manager.clone()).unwrap();
|
||||
|
||||
if let Err(why) = client.start_shards(2).await {
|
||||
error!("Client error: {why:?}");
|
||||
}
|
||||
}
|
||||
|
||||
warn!("BOT EXITING");
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use std::{fmt::Debug, time::Duration};
|
||||
use std::{collections::HashMap, fmt::Debug, sync::LazyLock, time::Duration};
|
||||
|
||||
use serenity::all::{Context, GuildId, Message, Permissions};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::arguments::{Argument, ArgumentStorage};
|
||||
|
||||
// pub type DmCommand = fn(Context, Message);
|
||||
// pub type GuildCommand = fn(Context, Message, GuildId);
|
||||
pub static COMMANDS: LazyLock<RwLock<HashMap<String, Command>>> =
|
||||
LazyLock::new(|| RwLock::new(HashMap::new()));
|
||||
|
||||
pub enum CommandFnKind {
|
||||
Lua(()),
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
pub const VERSION: u16 = 0;
|
||||
|
||||
pub const CAT_DEV: &'static str = "Dev";
|
||||
pub const CAT_FUN: &'static str = "Fun";
|
||||
pub const CAT_MOD: &'static str = "Moderation";
|
||||
pub const CAT_GEN: &'static str = "General";
|
||||
pub const CAT_INF: &'static str = "Info";
|
||||
pub const CAT_HID: &'static str = "Hidden";
|
||||
|
||||
pub const COMMAND_PREFIX: &'static str = ";";
|
||||
// pub const SHORT_ARGUMENT_PREFIX: &'static str = "-";
|
||||
// pub const LONG_ARGUMENT_PREFIX: &'static str = "--";
|
||||
|
|
185
src/lua.rs
185
src/lua.rs
|
@ -1,7 +1,8 @@
|
|||
use mlua::{Lua, LuaOptions, StdLib, Table, Variadic};
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use tracing::info;
|
||||
|
||||
use crate::constants::{CAT_FUN, CAT_GEN, CAT_INF, CAT_MOD, VERSION};
|
||||
|
||||
use crate::{constants::VERSION, CAT_FUN, CAT_GEN, CAT_HID, CAT_INF, CAT_MOD};
|
||||
|
||||
pub fn initialize() -> Lua {
|
||||
// let mut runtimes = vec![];
|
||||
|
@ -36,15 +37,6 @@ pub fn initialize() -> Lua {
|
|||
|
||||
set_logging(&lua, &globals);
|
||||
|
||||
lua.load(include_str!("../lua/loader.lua"))
|
||||
.set_name("lua init script")
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
lua.sandbox(true).unwrap();
|
||||
|
||||
// return;
|
||||
|
||||
// runtimes.push(lua);
|
||||
// }
|
||||
|
||||
|
@ -71,6 +63,7 @@ fn clean_global(#[cfg(not(debug_assertions))] lua: &Lua, globals: &Table) {
|
|||
// we should be using the logging functions instead of raw print
|
||||
globals.raw_remove("print").unwrap();
|
||||
}
|
||||
|
||||
fn prepare_global(lua: &Lua, globals: &Table) {
|
||||
let table = lua.create_table().unwrap();
|
||||
table.set("info", CAT_INF).unwrap();
|
||||
|
@ -80,7 +73,12 @@ fn prepare_global(lua: &Lua, globals: &Table) {
|
|||
globals.set("commandCategory", table).unwrap();
|
||||
|
||||
let table = lua.create_table().unwrap();
|
||||
globals.set("commands", table).unwrap();
|
||||
#[rustfmt::skip]
|
||||
table.set("registerCommand", lua.create_function(rust_lua_functions::registerCommand).unwrap()).unwrap();
|
||||
#[rustfmt::skip]
|
||||
table.set("executeCommand", lua.create_function(rust_lua_functions::executeCommand).unwrap()).unwrap();
|
||||
|
||||
globals.set("sys", table).unwrap();
|
||||
}
|
||||
|
||||
fn set_logging(lua: &Lua, globals: &Table) {
|
||||
|
@ -97,3 +95,166 @@ fn set_logging(lua: &Lua, globals: &Table) {
|
|||
.unwrap();
|
||||
globals.set("info", log).unwrap();
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
mod rust_lua_functions {
|
||||
use mlua::{ExternalError, Lua, Result, Value, Variadic};
|
||||
use tracing::info;
|
||||
|
||||
pub fn executeCommand(_lua: &Lua, arg_values: Variadic<Value>) -> Result<()> {
|
||||
let mut command_name = String::new();
|
||||
let mut command_args = vec![];
|
||||
|
||||
for (idx, value) in arg_values.iter().enumerate() {
|
||||
match value {
|
||||
Value::Nil => {
|
||||
return Err(
|
||||
"Nil argument provided! executeCommand accepts no nil arguments"
|
||||
.into_lua_err(),
|
||||
)
|
||||
}
|
||||
Value::String(string) => {
|
||||
if idx == 0 {
|
||||
command_name = string.to_string_lossy().to_string();
|
||||
} else {
|
||||
command_args.push(string.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
Value::Table(_) => {
|
||||
return Err("Direct run commands are not supported yet.".into_lua_err())
|
||||
}
|
||||
// Value::Function(_) => todo!(),
|
||||
_ => {
|
||||
return Err("Invalid type used! Only `String` and `Table` are supported by executeCommand".into_lua_err());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
info!(
|
||||
"Running lua command `{:?}` with args `{:?}`",
|
||||
command_name, command_args
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
pub fn registerCommand(_lua: &Lua, arg_values: Variadic<Value>) -> Result<()> {
|
||||
let table = match arg_values.get(0) {
|
||||
Some(Value::Nil) | None => {
|
||||
return Err(
|
||||
"Nil argument provided! registerCommand accepts no nil arguments"
|
||||
.into_lua_err(),
|
||||
)
|
||||
}
|
||||
Some(Value::Table(table)) => table,
|
||||
Some(_) => {
|
||||
return Err(
|
||||
"Invalid type used! Only `Table` is supported by registerCommand"
|
||||
.into_lua_err(),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// pub mod luagen_module {
|
||||
|
||||
// // macro_rules! luafunc {
|
||||
|
||||
// // }
|
||||
|
||||
// // macro_rules! luavalue {
|
||||
// // () => {
|
||||
|
||||
// // };
|
||||
// // }
|
||||
|
||||
// macro_rules! luagen {
|
||||
|
||||
// // $($item_ident:literal = $item_value:expr,)+
|
||||
// ($($item_ident:literal = $item_value:expr,)*) => {
|
||||
// pub fn
|
||||
// };
|
||||
|
||||
// ($($vis:vis $table:ident { $($any:tt)* } )+ ) => {
|
||||
// $($vis mod $table {
|
||||
// luagen!{
|
||||
// $($any)*
|
||||
// }
|
||||
// })+
|
||||
// };
|
||||
|
||||
// ($vis:vis fn $fn_ident:ident() { $($fn_body:tt)* } ) => {
|
||||
// $vis fn $fn_ident(#[allow(unused_variables)] lua: &mlua::Lua, #[allow(unused_variables)] arg_values: mlua::Variadic<mlua::Value>) -> mlua::Result<()> { $($fn_body)* }
|
||||
// };
|
||||
// }
|
||||
|
||||
// luagen! {
|
||||
// commandCategory {
|
||||
// "info" = CAT_INF,
|
||||
// "fun" = CAT_FUN,
|
||||
// "general" = CAT_GEN,
|
||||
// "moderation" = CAT_MOD,
|
||||
// }
|
||||
// pub sys {
|
||||
// pub fn regiserCommand() {
|
||||
|
||||
// dbg!(arg_values);
|
||||
|
||||
// // let table = match arg_values.get(0) {
|
||||
// // Some(Value::Nil) | None => {
|
||||
// // return Err(
|
||||
// // "Nil argument provided! registerCommand accepts no nil arguments"
|
||||
// // .into_lua_err(),
|
||||
// // )
|
||||
// // }
|
||||
// // Some(Value::Table(table)) => table,
|
||||
// // Some(_) => {
|
||||
// // return Err(
|
||||
// // "Invalid type used! Only `Table` is supported by registerCommand"
|
||||
// // .into_lua_err(),
|
||||
// // );
|
||||
// // }
|
||||
// // };
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// // fn executeCommand() {
|
||||
// // let mut command_name = String::new();
|
||||
// // let mut command_args = vec![];
|
||||
|
||||
// // for (idx, value) in arg_values.iter().enumerate() {
|
||||
// // match value {
|
||||
// // Value::Nil => {
|
||||
// // return Err(
|
||||
// // "Nil argument provided! executeCommand accepts no nil arguments"
|
||||
// // .into_lua_err(),
|
||||
// // )
|
||||
// // }
|
||||
// // Value::String(string) => {
|
||||
// // if idx == 0 {
|
||||
// // command_name = string.to_string_lossy().to_string();
|
||||
// // } else {
|
||||
// // command_args.push(string.to_string_lossy().to_string());
|
||||
// // }
|
||||
// // }
|
||||
// // Value::Table(_) => {
|
||||
// // return Err("Direct run commands are not supported yet.".into_lua_err())
|
||||
// // }
|
||||
// // // Value::Function(_) => todo!(),
|
||||
// // _ => {
|
||||
// // return Err("Invalid type used! Only `String` and `Table` are supported by executeCommand".into_lua_err());
|
||||
// // }
|
||||
// // };
|
||||
// // }
|
||||
|
||||
// // info!(
|
||||
// // "Running lua command `{:?}` with args `{:?}`",
|
||||
// // command_name, command_args
|
||||
// // );
|
||||
// // Ok(())
|
||||
// // }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
|
99
src/main.rs
99
src/main.rs
|
@ -11,25 +11,17 @@ mod tasks;
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Condvar, OnceLock};
|
||||
use std::sync::{Condvar, OnceLock};
|
||||
|
||||
use bot::BotHandler;
|
||||
use commands::{Command, CommandFnKind};
|
||||
use commands::{Command, CommandFnKind, COMMANDS};
|
||||
use constants::CAT_HID;
|
||||
use metadata::LevelFilter;
|
||||
use serenity::all::GatewayIntents;
|
||||
use serenity::all::{Context, GuildId, ShardManager};
|
||||
use serenity::all::{Context, GuildId};
|
||||
use serenity::model::channel::Message;
|
||||
use serenity::Client;
|
||||
use tracing::*;
|
||||
|
||||
// pub const CAT_DEV: &'static str = "Dev";
|
||||
pub const CAT_FUN: &'static str = "Fun";
|
||||
pub const CAT_MOD: &'static str = "Moderation";
|
||||
pub const CAT_GEN: &'static str = "General";
|
||||
pub const CAT_INF: &'static str = "Info";
|
||||
pub const CAT_HID: &'static str = "Hidden";
|
||||
pub static NO_DISCORD: OnceLock<bool> = OnceLock::new();
|
||||
|
||||
static SHARD_MANAGER: OnceLock<Arc<ShardManager>> = OnceLock::new();
|
||||
// static SHARDS_READY: AtomicBool = AtomicBool::new(false);
|
||||
static DO_SHUTDOWN: (AtomicBool, Condvar) = (AtomicBool::new(false), Condvar::new()); // atombool and condvar combo to ensure maximum coverage when the bot needs to power off
|
||||
|
||||
|
@ -41,66 +33,43 @@ async fn main() {
|
|||
.finish(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
info!("Logging initialized.");
|
||||
|
||||
let mut commands = HashMap::default();
|
||||
|
||||
info!("Grabbing commandline input.");
|
||||
let mut nodiscord = false;
|
||||
for i in std::env::args() {
|
||||
match i.as_str() {
|
||||
"nodiscord" => nodiscord = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
NO_DISCORD.set(nodiscord).unwrap();
|
||||
|
||||
// let mut commands = HashMap::default();
|
||||
|
||||
info!("Loading stock commands.");
|
||||
insert_stock(&mut commands);
|
||||
insert_stock().await;
|
||||
info!("Loading rust dynamic commands.");
|
||||
insert_rust(&mut commands);
|
||||
insert_rust().await;
|
||||
|
||||
info!("Initializing Lua runtime.");
|
||||
let mut lua = lua::initialize(); // may not need to be mutable, but its fine for now
|
||||
let lua = lua::initialize(); // may not need to be mutable, but its fine for now
|
||||
|
||||
lua.load(include_str!("../lua/loader.lua"))
|
||||
.set_name("lua init script")
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
info!("Loading lua commandlets.");
|
||||
insert_lua(&mut commands, &mut lua);
|
||||
insert_lua(&lua).await;
|
||||
|
||||
warn!("ABORTING CONNECTING TO DISCORD, BYE BYE!");
|
||||
return;
|
||||
|
||||
// // TODO load this at runtime so the key will not be stored in the binary?
|
||||
// #[cfg(not(debug_assertions))]
|
||||
// let token = {
|
||||
// info!("Initializing bot with production token.");
|
||||
// include_str!("bot_token.prod")
|
||||
// };
|
||||
// #[cfg(debug_assertions)]
|
||||
// let token = {
|
||||
// info!("Initializing bot with development token.");
|
||||
// include_str!("bot_token.dev")
|
||||
// };
|
||||
|
||||
// let intents = GatewayIntents::DIRECT_MESSAGES
|
||||
// | GatewayIntents::DIRECT_MESSAGE_REACTIONS
|
||||
// | GatewayIntents::GUILDS
|
||||
// | GatewayIntents::GUILD_MODERATION
|
||||
// // | GatewayIntents::GUILD_EMOJIS_AND_STICKERS
|
||||
// | GatewayIntents::GUILD_MEMBERS
|
||||
// | GatewayIntents::GUILD_MESSAGE_REACTIONS
|
||||
// | GatewayIntents::GUILD_MESSAGES
|
||||
// | GatewayIntents::MESSAGE_CONTENT;
|
||||
|
||||
// let mut client = match Client::builder(&token, intents)
|
||||
// .event_handler(BotHandler { commands })
|
||||
// .await
|
||||
// {
|
||||
// Ok(client) => client,
|
||||
// Err(err) => panic!("Error starting client connection: `{err}`"),
|
||||
// };
|
||||
|
||||
// SHARD_MANAGER.set(client.shard_manager.clone()).unwrap();
|
||||
|
||||
// if let Err(why) = client.start_shards(2).await {
|
||||
// error!("Client error: {why:?}");
|
||||
// }
|
||||
|
||||
// warn!("MAIN FUNCTION EXITING");
|
||||
lua.sandbox(true).unwrap();
|
||||
|
||||
bot::start().await;
|
||||
}
|
||||
|
||||
/// The last set of commands, these are used by the hoster of the engine.
|
||||
pub fn insert_lua(_commands: &mut HashMap<String, Command>, _lua: &mut mlua::Lua) {
|
||||
pub async fn insert_lua(_lua: &mlua::Lua) {
|
||||
// commands.insert(
|
||||
// "stop".to_owned(),
|
||||
// Command::new("stop".to_owned())
|
||||
|
@ -115,7 +84,7 @@ pub fn insert_lua(_commands: &mut HashMap<String, Command>, _lua: &mut mlua::Lua
|
|||
}
|
||||
|
||||
/// Cannot use any command names of stock commands, but gets to pick before lua commands are loaded.
|
||||
pub fn insert_rust(_commands: &mut HashMap<String, Command>) {
|
||||
pub async fn insert_rust() {
|
||||
// commands.insert(
|
||||
// "stop".to_owned(),
|
||||
// Command::new("stop".to_owned())
|
||||
|
@ -130,8 +99,8 @@ pub fn insert_rust(_commands: &mut HashMap<String, Command>) {
|
|||
}
|
||||
|
||||
/// Will never fail, gets first pick of command names and properties. It is up to the maintainer to make damn sure this works.
|
||||
pub fn insert_stock(commands: &mut HashMap<String, Command>) {
|
||||
commands.insert(
|
||||
pub async fn insert_stock() {
|
||||
COMMANDS.write().await.insert(
|
||||
"stop".to_owned(),
|
||||
Command::new("stop".to_owned())
|
||||
.alias("svs".to_owned())
|
||||
|
@ -156,6 +125,6 @@ fn stop_command(_: Context, msg: Message, _: Option<GuildId>) {
|
|||
let _eg = handle.enter();
|
||||
|
||||
handle.spawn(async {
|
||||
SHARD_MANAGER.get().unwrap().shutdown_all().await;
|
||||
bot::SHARD_MANAGER.get().unwrap().shutdown_all().await;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::constants;
|
||||
use crate::{bot::SHARD_MANAGER, constants};
|
||||
use std::{
|
||||
sync::{Arc, LazyLock},
|
||||
time::Duration,
|
||||
|
@ -27,7 +27,7 @@ pub async fn start_tasks(ctx: Context) {
|
|||
.await
|
||||
.misc
|
||||
.push(tokio::spawn(status_timer(
|
||||
crate::SHARD_MANAGER.get().unwrap().runners.clone(),
|
||||
SHARD_MANAGER.get().unwrap().runners.clone(),
|
||||
ctx.cache,
|
||||
)));
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue