Skip to content

Add ability to synchronize commands #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/commands/moderation/clear.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ pub async fn run(
interaction.options.get("amount"), CommandOptionValue::Integer
);

let member = get_option!(
interaction.options.get("member"), CommandOptionValue::User
let author = get_option!(
interaction.options.get("author"), CommandOptionValue::User
).copied();

let filter = get_option!(
interaction.options.get("member"), CommandOptionValue::String
interaction.options.get("filter"), CommandOptionValue::String
);

if !(&2..=&600).contains(&amount) {
Expand Down Expand Up @@ -61,7 +61,7 @@ pub async fn run(

last = messages.last().map(|msg| msg.id);

if let Some(member) = member {
if let Some(member) = author {
messages = messages.iter()
.filter(|msg| msg.author.id == member)
.cloned().collect::<Vec<Message>>();
Expand Down
6 changes: 5 additions & 1 deletion src/context.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use twilight_model::id::Id;
use twilight_model::id::marker::ApplicationMarker;
use crate::{
all_macro,
env_unwrap,
Expand All @@ -19,10 +21,11 @@ pub struct Context {
pub scam_domains: ScamLinks,
#[cfg(feature = "gateway")]
pub bucket: Bucket,
pub application_id: Id<ApplicationMarker>,
}

impl Context {
pub async fn new() -> Self {
pub async fn new(application_id: Id<ApplicationMarker>) -> Self {
let mongodb_uri = env_unwrap!("MONGODB_URI");
let redis_url = env_unwrap!("REDIS_URL");

Expand All @@ -49,6 +52,7 @@ impl Context {
#[cfg(feature = "gateway")]
bucket,
application,
application_id
}
}
}
22 changes: 22 additions & 0 deletions src/database/redis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use tokio::sync::mpsc::UnboundedSender;
use tokio::sync::mpsc::error::SendError;
use tracing::info;
use crate::database::mongodb::MongoDBConnection;
use crate::models::config::GuildConfig;
use crate::server::guild::commands::bitfield::get_enabled_bitfield;

#[derive(Serialize, Deserialize, Debug)]
pub struct PartialGuild {
Expand Down Expand Up @@ -124,6 +126,26 @@ impl RedisConnection {
connection.zincr(path, user_id.to_string(), count).await
}

pub async fn set_commands_as_synced(&self, config: &GuildConfig) -> Result<(), RedisError> {
let mut connection = connection!(self)?;
let bitfield = get_enabled_bitfield(&config);
let _: () = connection
.set(
format!("commands.{}", config.guild_id),
bitfield.to_string()
).await?;

Ok(())
}

pub async fn are_commands_synced(&self, config: &GuildConfig) -> Result<bool, RedisError> {
let mut connection = connection!(self)?;
let bitfield = get_enabled_bitfield(&config);
let response: Option<String> = connection.get(format!("commands.{}", config.guild_id)).await?;

Ok(response.map_or_else(|| false, |v| v == bitfield.to_string()))
}

pub async fn watch_config_updates(
&self,
mongodb: &MongoDBConnection
Expand Down
49 changes: 26 additions & 23 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use dotenv::dotenv;
use tokio::task::JoinHandle;
use tracing::info;
use twilight_http::Client;
use crate::gateway::clients::{DiscordClients, LoadDiscordClients};

all_macro!(
cfg(feature = "gateway");
Expand Down Expand Up @@ -34,36 +35,38 @@ async fn main() {
tracing_init::init();
info!("starting app");

let context = Arc::new(Context::new().await);

let discord_token = env_unwrap!("DISCORD_TOKEN");
let main_http = Arc::new(Client::new(discord_token.to_owned()));
let current_user = main_http
.current_user()
.await
.unwrap()
.model()
.await
.unwrap();
info!("current user fetched: {}#{}", current_user.name, current_user.discriminator);
let application_id = current_user.id.cast();

let context = Arc::new(Context::new(application_id).await);
let cloned_context = context.clone();
tokio::spawn(async move {
let context = cloned_context;
context.redis.watch_config_updates(&context.mongodb).await.unwrap();
});

let discord_token = env_unwrap!("DISCORD_TOKEN");
let main_http = Arc::new(Client::new(discord_token.to_owned()));

let mut threads: Vec<JoinHandle<()>> = vec![];

#[cfg(any(feature = "custom-clients", feature = "tasks"))]
{
use crate::gateway::clients::{DiscordClients, LoadDiscordClients};
let discord_clients = DiscordClients::load(&context.mongodb).await.unwrap();

#[cfg(feature = "tasks")]
{
threads.push(tasks::run(
context.mongodb.to_owned(),
discord_clients.to_owned(),
main_http.to_owned()
));
}

#[cfg(feature = "custom-clients")]
threads.append(&mut discord_clients.start(context.to_owned()));
}
let discord_clients = DiscordClients::load(&context.mongodb).await.unwrap();

#[cfg(feature = "tasks")]
threads.push(tasks::run(
context.mongodb.to_owned(),
discord_clients.to_owned(),
main_http.to_owned()
));

#[cfg(any(feature = "custom-clients"))]
threads.append(&mut discord_clients.start(context.to_owned()));

#[cfg(feature = "gateway")]
{
Expand Down Expand Up @@ -95,7 +98,7 @@ async fn main() {
};

let run = tokio::spawn(crate::server::listen(
80, context, main_http, #[cfg(feature = "http-interactions")] public_key
80, context, main_http, discord_clients, #[cfg(feature = "http-interactions")] public_key
));
threads.push(run);
}
Expand Down
23 changes: 23 additions & 0 deletions src/models/config/commands.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use serde::{Deserialize, Serialize};

/// Disabled commands indicates what commands should not be registered as guild commands
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Commands {
pub case_details: bool,
pub case_edit: bool,
pub case_last: bool,
pub case_list: bool,
pub case_remove: bool,

pub clear: bool,
pub ban: bool,
pub kick: bool,
pub mute: bool,
pub timeout: bool,
pub warn: bool,

pub top_day_all: bool,
pub top_day_me: bool,
pub top_week_all: bool,
pub top_week_me: bool
}
4 changes: 4 additions & 0 deletions src/models/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@ use serde::{Serialize, Deserialize};
use twilight_model::id::Id;
use twilight_model::id::marker::{ApplicationMarker, GuildMarker};
use crate::models::config::activity::{Levels, Top};
use crate::models::config::commands::Commands;
use crate::models::config::moderation::Moderation;

use self::automod::actions::BucketAction;

pub mod moderation;
pub mod activity;
pub mod automod;
pub mod commands;

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct GuildConfig {
pub guild_id: Id<GuildMarker>,
pub application_id: Option<Id<ApplicationMarker>>,
pub enabled_commands: Commands,
pub moderation: Option<Moderation>,
pub premium: bool,
pub levels: Option<Levels>,
Expand All @@ -25,6 +28,7 @@ impl GuildConfig {
Self {
guild_id,
application_id: None,
enabled_commands: Default::default(),
moderation: None,
premium: false,
levels: None,
Expand Down
3 changes: 2 additions & 1 deletion src/models/config/moderation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@ pub struct Moderation {
pub mute_role: Option<Id<RoleMarker>>,
pub native_support: bool,
pub logs_channel: Option<Id<ChannelMarker>>,
pub dm_case: bool
pub dm_case: bool,
pub context_menu: bool
}
85 changes: 85 additions & 0 deletions src/server/guild/commands/activity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use crate::models::config::GuildConfig;
use crate::server::guild::commands::{default_option, defaults_command, if_enabled};
use twilight_model::application::command::{Command, CommandOption, CommandOptionType};

macro_rules! subcommand {
($name: expr, $description: expr) => {
CommandOption {
name: $name.to_string(),
description: $description.to_string(),
kind: CommandOptionType::SubCommand,
..default_option()
}
};
}

macro_rules! group {
($name: expr, $subcommands: expr) => {
CommandOption {
name: $name.to_string(),
description: "-".to_string(),
kind: CommandOptionType::SubCommandGroup,
options: Some($subcommands),
..default_option()
}
};
}

pub(super) fn add_activity_commands(config: &GuildConfig, commands: &mut Vec<Command>) {
if let Some(top) = &config.top {
let mut subcommand_groups = vec![];

if top.day {
let mut subcommands = vec![];

if_enabled!(
config,
top_day_all,
subcommands,
subcommand!("all", "Shows top 3 active users")
);

if_enabled!(
config,
top_day_me,
subcommands,
subcommand!("me", "Shows your position on the leaderboard")
);

if !subcommands.is_empty() {
subcommand_groups.push(group!("day", subcommands));
}
}

if top.week {
let mut subcommands = vec![];

if_enabled!(
config,
top_week_all,
subcommands,
subcommand!("all", "Shows top 3 active users")
);

if_enabled!(
config,
top_week_me,
subcommands,
subcommand!("me", "Shows your position on the leaderboard")
);

if !subcommands.is_empty() {
subcommand_groups.push(group!("week", subcommands));
}
}

if !subcommand_groups.is_empty() {
commands.push(Command {
name: "top".to_string(),
description: "-".to_string(),
options: subcommand_groups,
..defaults_command()
})
}
}
}
Loading