121 lines
3.6 KiB
Rust
121 lines
3.6 KiB
Rust
use std::{fmt::Debug, sync::Arc};
|
|
|
|
use futures::future::BoxFuture;
|
|
use opendal::Operator;
|
|
use patricia_tree::StringPatriciaMap;
|
|
use songbird::{
|
|
Songbird,
|
|
driver::{Channels, SampleRate},
|
|
};
|
|
use tokio_util::sync::CancellationToken;
|
|
use twilight_model::{
|
|
application::{command::Command, interaction::Interaction},
|
|
id::{
|
|
Id,
|
|
marker::{ApplicationMarker, CommandMarker, UserMarker},
|
|
},
|
|
};
|
|
|
|
use crate::{BotDataManager, GuildVoiceChannelToTextChannel, UserDataManager, VCsWatcher};
|
|
|
|
pub mod info;
|
|
pub mod join;
|
|
pub mod leave;
|
|
pub mod opt_in;
|
|
pub mod opt_out;
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct State {
|
|
pub audio_channels: Channels,
|
|
pub audio_sample_rate: SampleRate,
|
|
pub bot_data_manager: BotDataManager,
|
|
pub cancellation_token: CancellationToken,
|
|
pub discord_application_id: Id<ApplicationMarker>,
|
|
pub discord_bot_owner_user_id: Id<UserMarker>,
|
|
pub discord_client: Arc<twilight_http::Client>,
|
|
pub discord_info_command_id: Id<CommandMarker>,
|
|
pub discord_info_command_name: Arc<str>,
|
|
pub discord_opt_in_command_id: Id<CommandMarker>,
|
|
pub discord_opt_in_command_name: Arc<str>,
|
|
pub discord_opt_out_command_id: Id<CommandMarker>,
|
|
pub discord_opt_out_command_name: Arc<str>,
|
|
pub discord_user_id: Id<UserMarker>,
|
|
pub discord_voice_channel_corresponding_text_channel: Arc<GuildVoiceChannelToTextChannel>,
|
|
pub recording_data: Operator,
|
|
pub songbird: Arc<Songbird>,
|
|
pub user_data_manager: UserDataManager,
|
|
pub vcs_watcher: VCsWatcher,
|
|
}
|
|
|
|
type Return = ();
|
|
type BoxedHandler = Box<dyn Send + Sync + Fn(State, Interaction) -> BoxFuture<'static, Return>>;
|
|
|
|
fn box_handler<Handler, Fut>(handler: Handler) -> BoxedHandler
|
|
where
|
|
Fut: Future<Output = Return> + Send + 'static,
|
|
Handler: Send + Sync + Fn(State, Interaction) -> Fut + 'static,
|
|
{
|
|
Box::new(move |state, interaction| Box::pin(handler(state, interaction)))
|
|
}
|
|
|
|
pub fn all() -> Vec<(&'static Command, BoxedHandler)> {
|
|
vec![
|
|
(&info::COMMAND, box_handler(info::handle)),
|
|
(&join::COMMAND, box_handler(join::handle)),
|
|
(&leave::COMMAND, box_handler(leave::handle)),
|
|
(&opt_in::COMMAND, box_handler(opt_in::handle)),
|
|
(&opt_out::COMMAND, box_handler(opt_out::handle)),
|
|
]
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct Router {
|
|
map: StringPatriciaMap<BoxedHandler>,
|
|
}
|
|
|
|
impl Router {
|
|
pub fn add_route<Fut, Handler>(&mut self, name: &str, handler: Handler)
|
|
where
|
|
Fut: Future<Output = Return> + Send + 'static,
|
|
Handler: Send + Sync + Fn(State, Interaction) -> Fut + 'static,
|
|
{
|
|
self.add_route_already_boxed(name, box_handler(handler));
|
|
}
|
|
|
|
fn add_route_already_boxed(&mut self, name: &str, boxed_handler: BoxedHandler) {
|
|
self.map.insert(name, boxed_handler);
|
|
}
|
|
|
|
pub async fn handle(
|
|
&self,
|
|
state: State,
|
|
command_name: &str,
|
|
interaction: Interaction,
|
|
) -> Option<Return> {
|
|
let handler = self.map.get(command_name)?;
|
|
|
|
Some(handler(state, interaction).await)
|
|
}
|
|
}
|
|
|
|
impl<'a> FromIterator<(&'a Command, BoxedHandler)> for Router {
|
|
#[inline]
|
|
fn from_iter<T: IntoIterator<Item = (&'a Command, BoxedHandler)>>(iter: T) -> Self {
|
|
let mut this = Self::default();
|
|
|
|
this.extend(iter);
|
|
|
|
this
|
|
}
|
|
}
|
|
|
|
impl<'a> Extend<(&'a Command, BoxedHandler)> for Router {
|
|
#[inline]
|
|
fn extend<T: IntoIterator<Item = (&'a Command, BoxedHandler)>>(&mut self, iter: T) {
|
|
for (command, boxed_handler) in iter {
|
|
let name = &command.name;
|
|
self.add_route_already_boxed(name, boxed_handler);
|
|
}
|
|
}
|
|
}
|