Skip to content

Commit

Permalink
Made the mentions as a database entry
Browse files Browse the repository at this point in the history
  • Loading branch information
1Git2Clone committed Dec 15, 2024
1 parent 0fa6878 commit 329cbc9
Show file tree
Hide file tree
Showing 18 changed files with 196 additions and 109 deletions.
16 changes: 11 additions & 5 deletions migrations/20240304155340_initial_migration.sql
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
-- Add migration script here
CREATE TABLE user_stats (
user_id BIGINT UNSIGNED NOT NULL,
guild_id BIGINT UNSIGNED NOT NULL,
experience_points INTEGER UNSIGNED NOT NULL,
level INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (user_id, guild_id)
user_id BIGINT UNSIGNED NOT NULL,
guild_id BIGINT UNSIGNED NOT NULL,
experience_points INTEGER UNSIGNED NOT NULL,
level INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (user_id, guild_id)
);
CREATE INDEX idx_user_id ON user_stats (user_id);
CREATE INDEX idx_guild_id ON user_stats (guild_id);

CREATE TABLE bot_mentions (
mentions BIGINT UNSIGNED NOT NULL
);
CREATE INDEX idx_mentions ON bot_mentions (mentions);
INSERT INTO bot_mentions (mentions) VALUES (0);
4 changes: 2 additions & 2 deletions src/commands/level_cmds/level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ pub async fn level(
};
let level = level_xp_and_rank_row
.1
.get::<i32, &str>(DATABASE_COLUMNS[&Level]);
.get::<i32, &str>(LEVELS_TABLE[&Level]);
let xp = level_xp_and_rank_row
.1
.get::<i32, &str>(DATABASE_COLUMNS[&ExperiencePoints]);
.get::<i32, &str>(LEVELS_TABLE[&ExperiencePoints]);

let avatar = target_replied_user.face().replace(".webp", ".png");
let username = &target_replied_user.name;
Expand Down
9 changes: 5 additions & 4 deletions src/commands/level_cmds/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
/// dependencies for the commands.
use crate::commands::cmd_utils::get_replied_user;
use crate::data::bot_data::{DATABASE_COLUMNS, DATABASE_FILENAME};
use crate::data::command_data::{Context, Error};
use crate::data::database_interactions::{
connect_to_db, fetch_top_nine_levels_in_guild, fetch_user_level_and_rank,
use crate::data::database::{DATABASE_FILENAME, LEVELS_TABLE};
use crate::database::{
connect_to_db,
level_system::{fetch_top_nine_levels_in_guild, fetch_user_level_and_rank},
};
use crate::enums::schemas::DatabaseSchema::*;
use crate::enums::schemas::LevelsSchema::*;
use ::serenity::futures::future::try_join_all;
use poise::serenity_prelude as serenity;
use rayon::prelude::*;
Expand Down
6 changes: 3 additions & 3 deletions src/commands/level_cmds/toplevels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub async fn toplevels(ctx: Context<'_>) -> Result<(), Error> {
ctx.defer().await?;
let user_ids: Vec<u64> = level_and_xp_rows
.par_iter()
.map(|row| row.get::<i64, &str>(DATABASE_COLUMNS[&UserId]) as u64)
.map(|row| row.get::<i64, &str>(LEVELS_TABLE[&UserId]) as u64)
.collect();
let users = try_join_all(
user_ids
Expand All @@ -38,8 +38,8 @@ pub async fn toplevels(ctx: Context<'_>) -> Result<(), Error> {

for (counter, (row, user)) in level_and_xp_rows.iter().zip(users.iter()).enumerate() {
let (level, xp) = (
row.get::<i32, &str>(DATABASE_COLUMNS[&Level]),
row.get::<i32, &str>(DATABASE_COLUMNS[&ExperiencePoints]),
row.get::<i32, &str>(LEVELS_TABLE[&Level]),
row.get::<i32, &str>(LEVELS_TABLE[&ExperiencePoints]),
);

fields.push((
Expand Down
6 changes: 3 additions & 3 deletions src/commands/level_logic.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::collections::HashMap;

use crate::enums::schemas::DatabaseSchema;
use crate::enums::schemas::LevelsSchema;

/// Set the leveling condition and return the updated level with reset xp if true.
pub async fn update_level(experience: i32, level: i32) -> HashMap<DatabaseSchema, i32> {
use crate::enums::schemas::DatabaseSchema as DbSch;
pub async fn update_level(experience: i32, level: i32) -> HashMap<LevelsSchema, i32> {
use crate::enums::schemas::LevelsSchema as DbSch;
let update_level = if experience >= level * 100 {
level + 1
} else {
Expand Down
19 changes: 1 addition & 18 deletions src/data/bot_data.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
use crate::{
enums::schemas::DatabaseSchema, utils::string_manipulation::upper_lowercase_permutations,
};
use std::collections::HashMap;
use crate::utils::string_manipulation::upper_lowercase_permutations;

use lazy_static::lazy_static;
use regex::Regex;

pub const DATABASE_FILENAME: &str = "database/bot_database.sqlite";
pub const DATABASE_USERS: &str = "user_stats";

pub const DEFAULT_XP: i64 = 0;
pub const DEFAULT_LEVEL: i64 = 1;

Expand All @@ -17,18 +11,7 @@ lazy_static! {
pub(crate) static ref BOT_TOKEN: String =
std::env::var("BOT_TOKEN").expect("Expected a token in the dotenv file.");
pub(crate) static ref START_TIME: std::time::Instant = std::time::Instant::now();
#[derive(Debug)]
pub(crate) static ref DATABASE_COLUMNS: HashMap<DatabaseSchema, &'static str> = {
use crate::enums::schemas::DatabaseSchema as DbSch;

HashMap::from([
(DbSch::UserId, "user_id"),
(DbSch::GuildId, "guild_id"),
(DbSch::ExperiencePoints, "experience_points"),
(DbSch::Level, "level"),
(DbSch::LastQueryTimestamp, "last_query_timestamp")
])
};
pub(crate) static ref XP_COOLDOWN_NUMBER_SECS: i64 = 60;
pub(crate) static ref BOT_PREFIXES: Vec<String> = {
let mut temp = vec![];
Expand Down
2 changes: 0 additions & 2 deletions src/data/command_data.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use poise::serenity_prelude as serenity;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::sync::atomic::AtomicU32;
use std::sync::Arc;

#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Data {
pub hutao_mentions: AtomicU32,
pub bot_user: Arc<serenity::CurrentUser>,
pub bot_avatar: Arc<str>,
}
Expand Down
31 changes: 31 additions & 0 deletions src/data/database.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use crate::enums::schemas::{LevelsSchema, MentionsSchema};
use std::collections::HashMap;

use lazy_static::lazy_static;

pub const DATABASE_FILENAME: &str = "database/bot_database.sqlite";
pub const DATABASE_USERS: &str = "user_stats";
pub const MENTIONS_TABLE_NAME: &str = "bot_mentions";

lazy_static! {
#[derive(Debug)]
pub(crate) static ref LEVELS_TABLE: HashMap<LevelsSchema, &'static str> = {
use crate::enums::schemas::LevelsSchema as DbSch;

HashMap::from([
(DbSch::UserId, "user_id"),
(DbSch::GuildId, "guild_id"),
(DbSch::ExperiencePoints, "experience_points"),
(DbSch::Level, "level"),
(DbSch::LastQueryTimestamp, "last_query_timestamp")
])
};
#[derive(Debug)]
pub(crate) static ref MENTIONS_TABLE: HashMap<MentionsSchema, &'static str> = {
use crate::enums::schemas::MentionsSchema as DbSch;

HashMap::from([
(DbSch::Mentions, "mentions"),
])
};
}
2 changes: 1 addition & 1 deletion src/data/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod bot_data;
pub mod command_data;
pub mod database_interactions;
pub mod database;
pub mod embed_media;
pub mod user_data;
45 changes: 45 additions & 0 deletions src/database/bot_mentions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use crate::data::command_data::Error;
use crate::data::database::{MENTIONS_TABLE, MENTIONS_TABLE_NAME};
use crate::enums::schemas::MentionsSchema;
use sqlx::sqlite::SqliteQueryResult;
use sqlx::Row;
use sqlx::SqlitePool;

pub async fn fetch_mentions(db: &SqlitePool) -> Result<i64, Error> {
let query = format!(
"SELECT `{}` FROM `{}`",
MENTIONS_TABLE[&MentionsSchema::Mentions],
MENTIONS_TABLE_NAME,
);
let sql = sqlx::query(&query).fetch_optional(db).await?;

let row = match sql {
Some(row) => row,
None => return Err(format!("Couldn't find a row to select (SQL: {})", query).into()),
};

let queried_mentions = row.get::<i64, &str>(MENTIONS_TABLE[&MentionsSchema::Mentions]);

Ok(queried_mentions)
}

pub async fn update_mentions(
db: &SqlitePool,
updated_mentions: i64,
) -> Result<SqliteQueryResult, sqlx::Error> {
let query = format!(
"UPDATE `{}` SET `{}` = ?",
MENTIONS_TABLE_NAME,
MENTIONS_TABLE[&MentionsSchema::Mentions]
);

sqlx::query(&query).bind(updated_mentions).execute(db).await
}

pub async fn add_mentions(db: &SqlitePool, n: i64) -> Result<i64, Error> {
let fetched_mentions = fetch_mentions(db).await?;

update_mentions(db, fetched_mentions + n).await?;

fetch_mentions(db).await
}
80 changes: 34 additions & 46 deletions src/data/database_interactions.rs → src/database/level_system.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,21 @@
use crate::commands::level_logic::update_level;
use crate::enums::schemas::DatabaseSchema::*;
use crate::enums::schemas::LevelsSchema::*;
use poise::serenity_prelude as serenity;
use serenity::User;
use sqlx::sqlite::SqliteRow;
use sqlx::Row;
/// https://stackoverflow.com/questions/72763578/how-to-create-a-sqlite-database-with-rust-sqlx
use sqlx::{sqlite::SqliteConnectOptions, sqlite::SqlitePoolOptions, Error, SqlitePool};
use std::{future::Future, path::Path};
use sqlx::{Error, SqlitePool};

use crate::data::bot_data::{DATABASE_COLUMNS, DEFAULT_LEVEL, DEFAULT_XP, XP_COOLDOWN_NUMBER_SECS};
use crate::data::user_data::USER_COOLDOWNS;

use super::bot_data::DATABASE_USERS;
use crate::data::bot_data::{DEFAULT_LEVEL, DEFAULT_XP, XP_COOLDOWN_NUMBER_SECS};
use crate::data::database::{DATABASE_USERS, LEVELS_TABLE};

/// Used to establish the database connection with its predetermined parameters.
pub async fn connect_to_db(
filename: impl AsRef<Path>,
) -> impl Future<Output = Result<SqlitePool, Error>> {
SqlitePoolOptions::new().connect_with(
SqliteConnectOptions::new()
.filename(filename)
.create_if_missing(true),
)
}
use crate::data::user_data::USER_COOLDOWNS;

/// Adds a new database user with the schema from `crate::data:bot_data.rs`.
/// That's the reason why the function isn't public.
async fn add_user_if_not_exists(
db: SqlitePool,
db: &SqlitePool,
user: &User,
guild_id: serenity::GuildId,
) -> Result<(), Error> {
Expand All @@ -39,18 +27,18 @@ async fn add_user_if_not_exists(
"INSERT INTO `{}` (`{}`, `{}`, `{}`, `{}`)
VALUES (?, ?, ?, ?)",
DATABASE_USERS,
DATABASE_COLUMNS[&UserId],
DATABASE_COLUMNS[&GuildId],
DATABASE_COLUMNS[&ExperiencePoints],
DATABASE_COLUMNS[&Level],
LEVELS_TABLE[&UserId],
LEVELS_TABLE[&GuildId],
LEVELS_TABLE[&ExperiencePoints],
LEVELS_TABLE[&Level],
);

sqlx::query(&query)
.bind(user.id.to_string())
.bind(guild_id.to_string())
.bind(DEFAULT_XP)
.bind(DEFAULT_LEVEL)
.execute(&db)
.execute(db)
.await?;

Ok(())
Expand All @@ -66,14 +54,14 @@ pub async fn fetch_user_level(
"SELECT `{}`, `{}`, `{}`
FROM `{}`
WHERE `{}` = ? AND `{}` = ?",
DATABASE_COLUMNS[&UserId],
DATABASE_COLUMNS[&ExperiencePoints],
DATABASE_COLUMNS[&Level],
LEVELS_TABLE[&UserId],
LEVELS_TABLE[&ExperiencePoints],
LEVELS_TABLE[&Level],
//
DATABASE_USERS,
//
DATABASE_COLUMNS[&UserId],
DATABASE_COLUMNS[&GuildId]
LEVELS_TABLE[&UserId],
LEVELS_TABLE[&GuildId]
)
.as_str(),
)
Expand Down Expand Up @@ -126,19 +114,19 @@ pub async fn fetch_top_nine_levels_in_guild(
WHERE `{}` = ?
ORDER BY {} DESC, {} DESC
LIMIT 9",
DATABASE_COLUMNS[&UserId],
DATABASE_COLUMNS[&UserId],
LEVELS_TABLE[&UserId],
LEVELS_TABLE[&UserId],
//
DATABASE_COLUMNS[&ExperiencePoints],
DATABASE_COLUMNS[&ExperiencePoints],
LEVELS_TABLE[&ExperiencePoints],
LEVELS_TABLE[&ExperiencePoints],
//
DATABASE_COLUMNS[&Level],
DATABASE_COLUMNS[&Level],
LEVELS_TABLE[&Level],
LEVELS_TABLE[&Level],
//
DATABASE_USERS,
DATABASE_COLUMNS[&GuildId],
DATABASE_COLUMNS[&Level],
DATABASE_COLUMNS[&ExperiencePoints],
LEVELS_TABLE[&GuildId],
LEVELS_TABLE[&Level],
LEVELS_TABLE[&ExperiencePoints],
)
.as_str(),
)
Expand All @@ -156,7 +144,7 @@ pub async fn fetch_top_nine_levels_in_guild(
/// Additionally, we directly use the guild_id instead of the event as the
/// parameter for add_user_if_not_exists() in order to save computing resources.
pub async fn add_or_update_db_user(
db: SqlitePool,
db: &SqlitePool,
message: &serenity::Message,
ctx: &serenity::Context,
obtained_xp: i32,
Expand Down Expand Up @@ -203,7 +191,7 @@ pub async fn add_or_update_db_user(
}

// First we need to check if there's some user_id+guild_id pair that matches
let level_query: Option<SqliteRow> = fetch_user_level(&db, user, guild_id).await?;
let level_query: Option<SqliteRow> = fetch_user_level(db, user, guild_id).await?;

let query_row = match level_query {
Some(row) => row,
Expand All @@ -214,8 +202,8 @@ pub async fn add_or_update_db_user(
}
};

let queried_level = query_row.get::<i32, &str>(DATABASE_COLUMNS[&Level]);
let queried_experience_points = query_row.get::<i32, &str>(DATABASE_COLUMNS[&ExperiencePoints]);
let queried_level = query_row.get::<i32, &str>(LEVELS_TABLE[&Level]);
let queried_experience_points = query_row.get::<i32, &str>(LEVELS_TABLE[&ExperiencePoints]);
let added_experience_points = queried_experience_points + obtained_xp;

let update = update_level(added_experience_points, queried_level).await;
Expand Down Expand Up @@ -259,19 +247,19 @@ pub async fn add_or_update_db_user(
WHERE `{}` = ? AND `{}` = ?",
DATABASE_USERS,
//
DATABASE_COLUMNS[&ExperiencePoints],
DATABASE_COLUMNS[&Level],
LEVELS_TABLE[&ExperiencePoints],
LEVELS_TABLE[&Level],
//
DATABASE_COLUMNS[&UserId],
DATABASE_COLUMNS[&GuildId],
LEVELS_TABLE[&UserId],
LEVELS_TABLE[&GuildId],
);

sqlx::query(&query)
.bind(updated_experience_points)
.bind(updated_level)
.bind(user.id.to_string())
.bind(guild_id.to_string())
.execute(&db)
.execute(db)
.await?;

Ok(())
Expand Down
Loading

0 comments on commit 329cbc9

Please sign in to comment.