mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-19 00:38:54 +00:00
Composed framework (#43)
Composed poise and serenity into a composed framework. This will help the migration towards slash-commands in the future.
This commit is contained in:
parent
f3c062f417
commit
a2f0684509
6 changed files with 201 additions and 2 deletions
89
Cargo.lock
generated
89
Cargo.lock
generated
|
@ -440,6 +440,41 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.20.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "5.5.3"
|
version = "5.5.3"
|
||||||
|
@ -481,6 +516,17 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derivative"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
|
@ -1013,6 +1059,12 @@ dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "idna"
|
name = "idna"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1543,6 +1595,35 @@ version = "0.3.29"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
|
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poise"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1819d5a45e3590ef33754abce46432570c54a120798bdbf893112b4211fa09a6"
|
||||||
|
dependencies = [
|
||||||
|
"async-trait",
|
||||||
|
"derivative",
|
||||||
|
"futures-util",
|
||||||
|
"parking_lot",
|
||||||
|
"poise_macros",
|
||||||
|
"regex",
|
||||||
|
"serenity",
|
||||||
|
"tokio",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poise_macros"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fa2c123c961e78315cd3deac7663177f12be4460f5440dbf62a7ed37b1effea"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.48",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "powerfmt"
|
name = "powerfmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
@ -2346,6 +2427,12 @@ dependencies = [
|
||||||
"unicode-normalization",
|
"unicode-normalization",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "subtle"
|
name = "subtle"
|
||||||
version = "2.5.0"
|
version = "2.5.0"
|
||||||
|
@ -3106,6 +3193,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"poise",
|
||||||
"serenity",
|
"serenity",
|
||||||
"tokio",
|
"tokio",
|
||||||
"youmubot-cf",
|
"youmubot-cf",
|
||||||
|
@ -3209,6 +3297,7 @@ dependencies = [
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"flume 0.10.14",
|
"flume 0.10.14",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"poise",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serenity",
|
"serenity",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
|
|
@ -18,6 +18,7 @@ chrono = "0.4.19"
|
||||||
flume = "0.10.13"
|
flume = "0.10.13"
|
||||||
dashmap = "5.3.4"
|
dashmap = "5.3.4"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
poise = "0.6"
|
||||||
|
|
||||||
[dependencies.serenity]
|
[dependencies.serenity]
|
||||||
version = "0.12"
|
version = "0.12"
|
||||||
|
|
|
@ -33,6 +33,9 @@ pub mod table_format;
|
||||||
/// The global app data.
|
/// The global app data.
|
||||||
pub type AppData = Arc<RwLock<TypeMap>>;
|
pub type AppData = Arc<RwLock<TypeMap>>;
|
||||||
|
|
||||||
|
/// The global context type for app commands
|
||||||
|
pub type CmdContext<'a, Env> = poise::Context<'a, Env, anyhow::Error>;
|
||||||
|
|
||||||
/// The HTTP client.
|
/// The HTTP client.
|
||||||
pub struct HTTPClient;
|
pub struct HTTPClient;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ codeforces = ["youmubot-cf"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serenity = "0.12"
|
serenity = "0.12"
|
||||||
|
poise = "0.6"
|
||||||
tokio = { version = "1.19.2", features = ["rt-multi-thread"] }
|
tokio = { version = "1.19.2", features = ["rt-multi-thread"] }
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
|
|
54
youmubot/src/compose_framework.rs
Normal file
54
youmubot/src/compose_framework.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use std::{future::Future, pin::Pin};
|
||||||
|
|
||||||
|
use serenity::{client::FullEvent, framework::Framework};
|
||||||
|
|
||||||
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
|
/// A Framework to compose other frameworks.
|
||||||
|
pub(crate) struct ComposedFramework {
|
||||||
|
frameworks: Box<[Box<dyn Framework>]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComposedFramework {
|
||||||
|
/// Create a new composed framework.
|
||||||
|
pub fn new(frameworks: Vec<Box<dyn Framework>>) -> Self {
|
||||||
|
Self {
|
||||||
|
frameworks: frameworks.into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Framework for ComposedFramework {
|
||||||
|
async fn dispatch(&self, ctx: Context, msg: FullEvent) -> () {
|
||||||
|
if !self.frameworks.is_empty() {
|
||||||
|
self.dispatch_loop(self.frameworks.len() - 1, ctx, msg)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn init(&mut self, client: &Client) {
|
||||||
|
for f in self.frameworks.iter_mut() {
|
||||||
|
f.init(&client).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ComposedFramework {
|
||||||
|
/// Dispatch to all inner frameworks in a loop. Returns a `Pin<Box<Future>>` because rust.
|
||||||
|
fn dispatch_loop<'a>(
|
||||||
|
&'a self,
|
||||||
|
index: usize,
|
||||||
|
ctx: Context,
|
||||||
|
msg: FullEvent,
|
||||||
|
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||||
|
Box::pin(async move {
|
||||||
|
if index == 0 {
|
||||||
|
self.frameworks[index].dispatch(ctx, msg).await
|
||||||
|
} else {
|
||||||
|
self.frameworks[index]
|
||||||
|
.dispatch(ctx.clone(), msg.clone())
|
||||||
|
.await;
|
||||||
|
self.dispatch_loop(index - 1, ctx, msg).await
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,10 @@ use serenity::{
|
||||||
use youmubot_prelude::announcer::AnnouncerHandler;
|
use youmubot_prelude::announcer::AnnouncerHandler;
|
||||||
use youmubot_prelude::*;
|
use youmubot_prelude::*;
|
||||||
|
|
||||||
|
use crate::compose_framework::ComposedFramework;
|
||||||
|
|
||||||
|
mod compose_framework;
|
||||||
|
|
||||||
struct Handler {
|
struct Handler {
|
||||||
hooks: Vec<RwLock<Box<dyn Hook>>>,
|
hooks: Vec<RwLock<Box<dyn Hook>>>,
|
||||||
ready_hooks: Vec<fn(&Context) -> CommandResult>,
|
ready_hooks: Vec<fn(&Context) -> CommandResult>,
|
||||||
|
@ -172,7 +176,7 @@ async fn main() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
data.insert::<Env>(env);
|
data.insert::<Env>(env.clone());
|
||||||
|
|
||||||
#[cfg(feature = "core")]
|
#[cfg(feature = "core")]
|
||||||
println!("Core enabled.");
|
println!("Core enabled.");
|
||||||
|
@ -184,6 +188,41 @@ async fn main() {
|
||||||
// Set up base framework
|
// Set up base framework
|
||||||
let fw = setup_framework(&token[..]).await;
|
let fw = setup_framework(&token[..]).await;
|
||||||
|
|
||||||
|
// Poise for application commands
|
||||||
|
let poise_fw = poise::Framework::builder()
|
||||||
|
.setup(|_, _, _| Box::pin(async { Ok(env) as Result<_> }))
|
||||||
|
.options(poise::FrameworkOptions {
|
||||||
|
prefix_options: poise::PrefixFrameworkOptions {
|
||||||
|
prefix: None,
|
||||||
|
mention_as_prefix: true,
|
||||||
|
execute_untracked_edits: true,
|
||||||
|
execute_self_messages: false,
|
||||||
|
ignore_thread_creation: true,
|
||||||
|
case_insensitive_commands: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
on_error: |err| {
|
||||||
|
Box::pin(async move {
|
||||||
|
if let poise::FrameworkError::Command { error, ctx, .. } = err {
|
||||||
|
let reply = format!(
|
||||||
|
"Command '{}' returned error {:?}",
|
||||||
|
ctx.invoked_command_name(),
|
||||||
|
error
|
||||||
|
);
|
||||||
|
ctx.reply(&reply).await.pls_ok();
|
||||||
|
println!("{}", reply)
|
||||||
|
} else {
|
||||||
|
eprintln!("Poise error: {:?}", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
commands: vec![poise_register()],
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let composed = ComposedFramework::new(vec![Box::new(fw), Box::new(poise_fw)]);
|
||||||
|
|
||||||
// Sets up a client
|
// Sets up a client
|
||||||
let mut client = {
|
let mut client = {
|
||||||
// Attempt to connect and set up a framework
|
// Attempt to connect and set up a framework
|
||||||
|
@ -198,7 +237,7 @@ async fn main() {
|
||||||
| GatewayIntents::DIRECT_MESSAGE_REACTIONS;
|
| GatewayIntents::DIRECT_MESSAGE_REACTIONS;
|
||||||
Client::builder(token, intents)
|
Client::builder(token, intents)
|
||||||
.type_map(data)
|
.type_map(data)
|
||||||
.framework(fw)
|
.framework(composed)
|
||||||
.event_handler(handler)
|
.event_handler(handler)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -276,6 +315,18 @@ async fn setup_framework(token: &str) -> StandardFramework {
|
||||||
fw
|
fw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Poise command to register
|
||||||
|
#[poise::command(
|
||||||
|
prefix_command,
|
||||||
|
rename = "register",
|
||||||
|
required_permissions = "MANAGE_GUILD"
|
||||||
|
)]
|
||||||
|
async fn poise_register(ctx: CmdContext<'_, Env>) -> Result<()> {
|
||||||
|
// TODO: make this work for guild owners too
|
||||||
|
poise::builtins::register_application_commands_buttons(ctx).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Hooks!
|
// Hooks!
|
||||||
|
|
||||||
#[hook]
|
#[hook]
|
||||||
|
|
Loading…
Add table
Reference in a new issue