feat: Setup Config question
This commit is contained in:
parent
7f8aebb1e5
commit
d026347897
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -713,6 +713,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"clickhouse",
|
||||
"clickhouse_pool",
|
||||
"poise",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
- [x] [ADMIN] trivial create : Creer une activité Trivial Daily
|
||||
- [x] [ADMIN] trivial list : List les activité
|
||||
- [ ] [ADMIN] trivial config : Modification des paramétre de base d'un event
|
||||
- [x] [ADMIN] trivial config : Modification des paramétre de base d'un event
|
||||
- [ ] [ADMIN] trivial add-question : Ajout de question + réponse ou X réponse
|
||||
- [ ] [ADMIN] trivial del-question : Suppression de question
|
||||
- [ ] [ADMIN] trivial load-question : Charge un fichier/lien de question
|
||||
@ -29,6 +29,7 @@
|
||||
- [ ] trivial score : Score d'une personne par défaut le sien
|
||||
- [ ] trivial top : Top X par défaut 3
|
||||
- [ ] [ADMIN] trivial balance : Modifie le score d'une personne +/-/=
|
||||
- [ ] [ADMIN] trivial status : Return trvial status or change trivial status
|
||||
|
||||
#### TODO
|
||||
|
||||
|
@ -70,3 +70,31 @@ pub async fn is_user_admin_right(ctx: Context<'_>) -> Result<bool, Error> {
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
// pub async fn autocomplete_channel_that_have_trivia(
|
||||
// ctx: Context<'_>,
|
||||
// _partial: &str,
|
||||
// ) -> impl Iterator<Item = String> {
|
||||
// let guild_id = match ctx.guild_id() {
|
||||
// Some(id) => id.get(),
|
||||
// None => {
|
||||
// ctx.say("This command can only be used in a server.").await;
|
||||
// return vec![];
|
||||
// }
|
||||
// };
|
||||
|
||||
// let manager = ctx.data().datalake_config.clone();
|
||||
// let search_query = Trivial::build_select_query(Some(&format!("id = {}", guild_id)), None, None);
|
||||
// let trivials = manager
|
||||
// .execute_select_with_retry::<Trivial>(&search_query)
|
||||
// .await;
|
||||
|
||||
// if trivials.is_empty() {
|
||||
// return vec![];
|
||||
// }
|
||||
|
||||
// trivials
|
||||
// .iter()
|
||||
// .map(|c| ChannelId::from(c.channel_id).to_string())
|
||||
// .collect()
|
||||
// }
|
||||
|
149
apps/bot/src/bot/trivia/config.rs
Normal file
149
apps/bot/src/bot/trivia/config.rs
Normal file
@ -0,0 +1,149 @@
|
||||
use crate::bot::helper::is_user_admin_right;
|
||||
use crate::bot::{Context, Error};
|
||||
use clickhouse_pool::traits::Model;
|
||||
use database::trivial::{Trivial, TrivialRewardKind};
|
||||
use poise::serenity_prelude::{Channel, Role};
|
||||
use tracing::{debug, info, instrument};
|
||||
|
||||
/// Config an existing trivia game
|
||||
#[instrument(name="trivia_config", skip(ctx), level = "info", fields(channel = ctx.channel_id().get(), guild = ?ctx.guild_id().unwrap().get()))]
|
||||
#[poise::command(
|
||||
prefix_command,
|
||||
slash_command,
|
||||
guild_only,
|
||||
category = "Trivia",
|
||||
check = "is_user_admin_right"
|
||||
)]
|
||||
pub async fn config(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Channel of the trivia game, or current"] channel_id: Option<Channel>,
|
||||
#[description = "Name of the trivia game"] name: Option<String>,
|
||||
#[description = "Description of the trivia game"] description: Option<String>,
|
||||
#[description = "Whether to use random questions"] random_question: Option<bool>,
|
||||
#[description = "Role to ping when a question is asked"] role_ping: Option<Role>,
|
||||
#[description = "Whether to enable role ping"] role_ping_enabled: Option<bool>,
|
||||
#[description = "Reward kind for the trivia game"] reward_kind: Option<TrivialRewardKind>,
|
||||
#[description = "Reward amount for the trivia game"] reward_amount: Option<u64>,
|
||||
#[description = "Whether to send an ephemeral message when the answer is taken into account"]
|
||||
taken_into_account: Option<bool>,
|
||||
) -> Result<(), Error> {
|
||||
let guild_id = match ctx.guild_id() {
|
||||
Some(id) => id.get(),
|
||||
None => {
|
||||
ctx.say("This command can only be used in a server.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
let channel_id = match channel_id {
|
||||
Some(c) => c.id().get(),
|
||||
None => {
|
||||
info!("No channel specified, using current channel.");
|
||||
ctx.channel_id().get()
|
||||
}
|
||||
};
|
||||
let manager = ctx.data().datalake_config.clone();
|
||||
let seach_query = Trivial::build_select_query(
|
||||
Some(&format!(
|
||||
"channel_id = {} and guild_id = {}",
|
||||
channel_id, guild_id
|
||||
)),
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let mut trivia_exists: Trivial = match manager
|
||||
.execute_select_with_retry::<Trivial>(&seach_query)
|
||||
.await
|
||||
{
|
||||
Ok(trivia) => {
|
||||
if trivia.is_empty() {
|
||||
ctx.say("No trivia game found in this channel. Please create one first.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
if trivia.len() > 1 {
|
||||
ctx.say("Multiple trivia games found in this channel. Please contact an admin to resolve this.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
trivia[0].clone()
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.say(format!("Error fetching trivia game: {}", e))
|
||||
.await?;
|
||||
return Err(Error::from(e));
|
||||
}
|
||||
};
|
||||
debug!("Found trivia game: {:?}", trivia_exists);
|
||||
let mut inserter = match manager.get_insert::<Trivial>(Trivial::table_name()).await {
|
||||
Ok(inserter) => inserter,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to create inserter for Trivial: {}", e);
|
||||
ctx.say("Failed to update trivia game. Please try again later.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
trivia_exists.sign = -1; // Set sign to -1 to indicate an update
|
||||
inserter.write(&trivia_exists.clone()).await?;
|
||||
// Update the trivia game with provided options
|
||||
if let Some(name) = name {
|
||||
trivia_exists.name = name;
|
||||
}
|
||||
if let Some(description) = description {
|
||||
trivia_exists.description = description;
|
||||
}
|
||||
if let Some(random_question) = random_question {
|
||||
trivia_exists.random_question = random_question;
|
||||
}
|
||||
if let Some(role_ping) = role_ping {
|
||||
trivia_exists.role_ping = role_ping.id.get();
|
||||
}
|
||||
if let Some(role_ping_enabled) = role_ping_enabled {
|
||||
trivia_exists.role_ping_enabled = role_ping_enabled;
|
||||
}
|
||||
if let Some(reward_kind) = reward_kind {
|
||||
trivia_exists.reward_kind = reward_kind;
|
||||
}
|
||||
if let Some(reward_amount) = reward_amount {
|
||||
trivia_exists.reward_amount = reward_amount;
|
||||
}
|
||||
if let Some(taken_into_account) = taken_into_account {
|
||||
trivia_exists.taken_into_account = taken_into_account;
|
||||
}
|
||||
trivia_exists.updated_at = chrono::Utc::now();
|
||||
trivia_exists.updater_id = ctx.author().id.get();
|
||||
debug!("Updated trivia game: {:?}", trivia_exists);
|
||||
// Set sign to 1 to indicate a successful update
|
||||
trivia_exists.sign = 1;
|
||||
// Write the updated trivia game back to the database
|
||||
inserter.write(&trivia_exists).await?;
|
||||
match inserter.end().await {
|
||||
Ok(_) => {
|
||||
info!(
|
||||
"Trivia game '{}' updated successfully in channel <#{}>.",
|
||||
trivia_exists.name, channel_id
|
||||
);
|
||||
ctx.say(format!(
|
||||
"Trivia game '{}' has been successfully updated.",
|
||||
trivia_exists.name
|
||||
))
|
||||
.await?;
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to update trivia game: {}", e);
|
||||
ctx.say("Failed to update trivia game. Please try again later.")
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let optimize_querry = format!("OPTIMIZE TABLE {} FINAL", Trivial::table_name());
|
||||
if let Err(e) = manager.execute_with_retry(&optimize_querry).await {
|
||||
tracing::error!("Failed to optimize trivia table: {}", e);
|
||||
} else {
|
||||
info!("Trivia table optimized successfully.");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
pub mod config;
|
||||
pub mod create;
|
||||
pub mod list;
|
||||
|
||||
use crate::bot::{Context, Error};
|
||||
use config::config;
|
||||
use create::create;
|
||||
use list::list;
|
||||
use tracing::instrument;
|
||||
@ -12,7 +14,7 @@ use tracing::instrument;
|
||||
slash_command,
|
||||
prefix_command,
|
||||
category = "Trivia",
|
||||
subcommands("create", "list"),
|
||||
subcommands("create", "list", "config"),
|
||||
guild_only = true
|
||||
)]
|
||||
pub async fn trivia(ctx: Context<'_>) -> Result<(), Error> {
|
||||
|
@ -61,6 +61,7 @@ impl PoolManager {
|
||||
.get_connection()
|
||||
.await
|
||||
.unwrap()
|
||||
.clone()
|
||||
.insert(table_name)
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ pub trait Model {
|
||||
type T;
|
||||
|
||||
fn create_table_sql() -> &'static str;
|
||||
fn optimize_table_sql() -> &'static str;
|
||||
fn table_name() -> &'static str;
|
||||
fn column_names() -> Vec<&'static str>;
|
||||
fn to_row(&self) -> (Vec<&'static str>, Vec<String>);
|
||||
|
@ -11,6 +11,7 @@ clickhouse_pool = { path = "../clickhouse_pool" }
|
||||
clickhouse = { workspace = true }
|
||||
uuid = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
poise = { workspace = true }
|
||||
serde_repr = "0.1.20"
|
||||
serde_json = { workspace = true }
|
||||
|
||||
|
@ -30,6 +30,13 @@ impl Model for Guild {
|
||||
"guild"
|
||||
}
|
||||
|
||||
fn optimize_table_sql() -> &'static str {
|
||||
r#"
|
||||
OPTIMIZE TABLE guild FINAL
|
||||
"#
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn create_table_sql() -> &'static str {
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS guild (
|
||||
|
@ -5,20 +5,49 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Serialize_repr,
|
||||
Deserialize_repr,
|
||||
poise::ChoiceParameter,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum TrivialRewardKind {
|
||||
#[name = "Only the first one"]
|
||||
OnlyTheFirstOne = 0,
|
||||
#[name = "Top three"]
|
||||
TopThree = 1,
|
||||
#[name = "Top five"]
|
||||
TopFive = 2,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize_repr, Deserialize_repr)]
|
||||
#[derive(
|
||||
Debug,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Serialize_repr,
|
||||
Deserialize_repr,
|
||||
poise::ChoiceParameter,
|
||||
)]
|
||||
#[repr(u8)]
|
||||
pub enum TrivialStatus {
|
||||
#[name = "Init"]
|
||||
Init = 0,
|
||||
#[name = "Started"]
|
||||
Started = 1,
|
||||
#[name = "Finished"]
|
||||
Finished = 2,
|
||||
#[name = "Paused"]
|
||||
Paused = 3,
|
||||
}
|
||||
|
||||
@ -48,6 +77,7 @@ pub struct Trivial {
|
||||
/// Whether or not the bot should send an ephemeral message to the user when their answer is taken into account.
|
||||
pub taken_into_account: bool,
|
||||
pub status: TrivialStatus,
|
||||
pub sign: i8,
|
||||
}
|
||||
|
||||
impl Default for Trivial {
|
||||
@ -69,6 +99,7 @@ impl Default for Trivial {
|
||||
reward_amount: 3,
|
||||
taken_into_account: true,
|
||||
status: TrivialStatus::Init,
|
||||
sign: 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -100,11 +131,14 @@ impl Model for Trivial {
|
||||
fn table_name() -> &'static str {
|
||||
"trivial"
|
||||
}
|
||||
fn optimize_table_sql() -> &'static str {
|
||||
"OPTIMIZE TABLE trivial FINAL"
|
||||
}
|
||||
|
||||
fn create_table_sql() -> &'static str {
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS trivial (
|
||||
id UUID PRIMARY KEY,
|
||||
id UUID,
|
||||
name String,
|
||||
description String,
|
||||
guild_id UInt64,
|
||||
@ -119,9 +153,11 @@ impl Model for Trivial {
|
||||
reward_kind Enum8('OnlyTheFirstOne' = 0, 'TopThree' = 1, 'TopFive' = 2),
|
||||
reward_amount UInt64,
|
||||
taken_into_account Bool,
|
||||
status Enum8('Init' = 0, 'Started' = 1, 'Finished' = 2, 'Paused' = 3)
|
||||
) ENGINE = MergeTree()
|
||||
ORDER BY id
|
||||
status Enum8('Init' = 0, 'Started' = 1, 'Finished' = 2, 'Paused' = 3),
|
||||
sign Int8
|
||||
) ENGINE = CollapsingMergeTree(sign)
|
||||
PRIMARY KEY (guild_id, id)
|
||||
ORDER BY (guild_id, id)
|
||||
"#
|
||||
}
|
||||
|
||||
@ -143,6 +179,7 @@ impl Model for Trivial {
|
||||
"reward_amount",
|
||||
"taken_into_account",
|
||||
"status",
|
||||
"sign",
|
||||
]
|
||||
}
|
||||
|
||||
@ -166,6 +203,7 @@ impl Model for Trivial {
|
||||
self.reward_amount.to_string(),
|
||||
self.taken_into_account.to_string(),
|
||||
format!("'{:?}'", serde_json::to_string(&self.status)),
|
||||
self.sign.to_string(),
|
||||
],
|
||||
)
|
||||
}
|
||||
@ -213,6 +251,7 @@ impl Model for Trivial {
|
||||
if let Some(offset) = offset {
|
||||
query.push_str(&format!(" OFFSET {}", offset));
|
||||
}
|
||||
//query.push_str(" FINAL");
|
||||
query
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,13 @@ impl Model for TrivialPoint {
|
||||
"trivial_point"
|
||||
}
|
||||
|
||||
fn optimize_table_sql() -> &'static str {
|
||||
r#"
|
||||
OPTIMIZE TABLE trivial_point FINAL
|
||||
"#
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn create_table_sql() -> &'static str {
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS trivial_point (
|
||||
|
@ -30,6 +30,13 @@ impl Model for TrivialQuestion {
|
||||
"trivial_question"
|
||||
}
|
||||
|
||||
fn optimize_table_sql() -> &'static str {
|
||||
r#"
|
||||
OPTIMIZE TABLE trivial_question FINAL
|
||||
"#
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn create_table_sql() -> &'static str {
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS trivial_question (
|
||||
|
@ -29,6 +29,13 @@ impl Model for TrivialRound {
|
||||
"trivial_round"
|
||||
}
|
||||
|
||||
fn optimize_table_sql() -> &'static str {
|
||||
r#"
|
||||
OPTIMIZE TABLE trivial_round FINAL
|
||||
"#
|
||||
.trim()
|
||||
}
|
||||
|
||||
fn create_table_sql() -> &'static str {
|
||||
r#"
|
||||
CREATE TABLE IF NOT EXISTS trivial_round (
|
||||
|
Loading…
Reference in New Issue
Block a user