diff --git a/apps/bot/src/bot/help.rs b/apps/bot/src/bot/help.rs new file mode 100644 index 0000000..539847e --- /dev/null +++ b/apps/bot/src/bot/help.rs @@ -0,0 +1,36 @@ +use crate::bot::{Context, Error}; +use poise::samples::HelpConfiguration; +use tracing::instrument; + +/// Show help message +#[instrument(skip(ctx), level = "info",fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))] +#[poise::command(prefix_command, slash_command, track_edits, category = "Utility")] +pub async fn help( + ctx: Context<'_>, + #[description = "Command to get help for"] + #[rest] + mut command: Option, +) -> Result<(), Error> { + // This makes it possible to just make `help` a subcommand of any command + // `/fruit help` turns into `/help fruit` + // `/fruit help apple` turns into `/help fruit apple` + if ctx.invoked_command_name() != "help" { + command = match command { + Some(c) => Some(format!("{} {}", ctx.invoked_command_name(), c)), + None => Some(ctx.invoked_command_name().to_string()), + }; + } + let extra_text_at_bottom = "\ +Provided by Mak with ❤️ and too much ☕"; + + let config = HelpConfiguration { + show_subcommands: true, + show_context_menu_commands: false, + ephemeral: true, + extra_text_at_bottom, + + ..Default::default() + }; + poise::builtins::help(ctx, command.as_deref(), config).await?; + Ok(()) +} diff --git a/apps/bot/src/bot/mod.rs b/apps/bot/src/bot/mod.rs index 0f2bb0b..df4456b 100644 --- a/apps/bot/src/bot/mod.rs +++ b/apps/bot/src/bot/mod.rs @@ -1,12 +1,15 @@ use std::sync::Arc; use clickhouse_pool::pool_manager::PoolManager; +use help::help; use poise::serenity_prelude as serenity; use poise::serenity_prelude::GatewayIntents; use tracing::{info, instrument}; use crate::config::Config; +pub mod help; + /// Displays your or another user's account creation date #[instrument(skip(ctx), level = "info", fields(channel_id = ctx.channel_id().get() , guild_id = ?ctx.guild_id(), user_id = ?ctx.author().id.get(), user_name = ctx.author().name))] #[poise::command(slash_command, prefix_command)] @@ -44,7 +47,7 @@ pub async fn start_bot(config: Config, datalake_config: Arc) { let framework = poise::Framework::builder() .options(poise::FrameworkOptions { - commands: vec![age()], + commands: vec![age(), help()], prefix_options: poise::PrefixFrameworkOptions { prefix: Some(prefix), ..Default::default() diff --git a/libs/database/src/guild.rs b/libs/database/src/guild.rs new file mode 100644 index 0000000..b1830d0 --- /dev/null +++ b/libs/database/src/guild.rs @@ -0,0 +1,135 @@ +use std::collections::HashMap; + +use chrono::{DateTime, Utc}; +use clickhouse::Row; +use clickhouse_pool::traits::Model; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, PartialEq, Row, Serialize, Deserialize)] +pub struct Guild { + pub id: u64, + pub bot_name: String, + pub enrolled: bool, + pub prefix: String, + pub moderator: Vec, + + pub feature_flags: HashMap, + + #[serde(with = "clickhouse::serde::chrono::datetime64::millis")] + pub created_at: DateTime, + #[serde(with = "clickhouse::serde::chrono::datetime64::millis")] + pub updated_at: DateTime, + pub creator_id: u64, + pub updater_id: u64, +} + +impl Model for Guild { + type T = Guild; + + fn table_name() -> &'static str { + "guild" + } + + fn create_table_sql() -> &'static str { + r#" + CREATE TABLE IF NOT EXISTS guild ( + id UInt64 PRIMARY KEY, + bot_name String, + enrolled Bool, + prefix String, + moderator Array(UInt64), + feature_flags Map(String, Bool), + created_at DateTime64(3), + updated_at DateTime64(3), + creator_id UInt64, + updater_id UInt64 + ) ENGINE = MergeTree() + ORDER BY (id) + "# + .trim() + } + + fn column_names() -> Vec<&'static str> { + vec![ + "id", + "bot_name", + "enrolled", + "prefix", + "moderator", + "feature_flags", + "created_at", + "updated_at", + "creator_id", + "updater_id", + ] + } + + fn to_row(&self) -> (Vec<&'static str>, Vec) { + ( + Self::column_names(), + vec![ + self.id.to_string(), + self.bot_name.clone(), + self.enrolled.to_string(), + self.prefix.clone(), + format!("{:?}", self.moderator), + format!("{:?}", self.feature_flags), + self.created_at.to_rfc3339(), + self.updated_at.to_rfc3339(), + self.creator_id.to_string(), + self.updater_id.to_string(), + ], + ) + } + + fn insert_query(&self) -> String { + let (columns, values) = self.to_row(); + let columns_str = columns.join(", "); + let values_str = values.join(", "); + + format!( + "INSERT INTO {} ({}) VALUES ({})", + Self::table_name(), + columns_str, + values_str + ) + } + + fn batch_insert_query(items: &[Self::T]) -> String { + let mut queries = Vec::new(); + for item in items { + let (columns, values) = item.to_row(); + let columns_str = columns.join(", "); + let values_str = values.join(", "); + queries.push(format!( + "INSERT INTO {} ({}) VALUES ({})", + Self::table_name(), + columns_str, + values_str + )); + } + queries.join("; ") + } + + fn build_select_query( + where_clause: Option<&str>, + limit: Option, + offset: Option, + ) -> String { + let mut query = format!( + "SELECT {} FROM {}", + Self::column_names().join(", "), + Self::table_name() + ); + if let Some(where_clause) = where_clause { + query.push_str(&format!(" WHERE {}", where_clause)); + } + if let Some(limit) = limit { + query.push_str(&format!(" LIMIT {}", limit)); + } + if let Some(offset) = offset { + query.push_str(&format!(" OFFSET {}", offset)); + } + query + } +} diff --git a/libs/database/src/lib.rs b/libs/database/src/lib.rs index 045e4f0..8624ad1 100644 --- a/libs/database/src/lib.rs +++ b/libs/database/src/lib.rs @@ -2,6 +2,7 @@ pub mod config; use std::{error::Error, sync::Arc}; +pub mod guild; pub mod trivial; pub mod trivial_point; pub mod trivial_question; @@ -12,6 +13,7 @@ use clickhouse_pool::{ pool_manager::PoolManager, traits::Model, }; +use guild::Guild; use tracing::{error, info, instrument}; use trivial::Trivial; use trivial_point::TrivialPoint; @@ -75,6 +77,13 @@ pub async fn create_manager_and_init( } }; + manager = match create_table::(manager).await { + Ok(manager) => manager, + Err(e) => { + return Err(e); + } + }; + info!("All tables created successfully"); Ok(Arc::new(manager))