Merge branch 'user-register' into dev
commit
fe171fdc8e
|
@ -24,3 +24,4 @@ rust-project.json
|
|||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/rust,rust-analyzer
|
||||
|
||||
*.db*
|
||||
|
|
|
@ -35,6 +35,8 @@ dependencies = [
|
|||
"affluences-api",
|
||||
"async-minecraft-ping",
|
||||
"chrono",
|
||||
"diesel",
|
||||
"diesel_migrations",
|
||||
"poise",
|
||||
"tokio",
|
||||
"uuid",
|
||||
|
@ -413,6 +415,40 @@ dependencies = [
|
|||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel"
|
||||
version = "2.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72eb77396836a4505da85bae0712fa324b74acfe1876d7c2f7e694ef3d0ee373"
|
||||
dependencies = [
|
||||
"diesel_derives",
|
||||
"libsqlite3-sys",
|
||||
"r2d2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_derives"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ad74fdcf086be3d4fdd142f67937678fe60ed431c3b2f08599e7687269410c4"
|
||||
dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diesel_migrations"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e9ae22beef5e9d6fab9225ddb073c1c6c1a7a6ded5019d5da11d1e5c5adc34e2"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"migrations_internals",
|
||||
"migrations_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.6"
|
||||
|
@ -793,6 +829,16 @@ version = "0.2.144"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
|
||||
|
||||
[[package]]
|
||||
name = "libsqlite3-sys"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afc22eff61b133b115c6e8c74e818c628d6d5e7a502afea6f64dee076dd94326"
|
||||
dependencies = [
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "link-cplusplus"
|
||||
version = "1.0.8"
|
||||
|
@ -833,6 +879,27 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "migrations_internals"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c493c09323068c01e54c685f7da41a9ccf9219735c3766fbfd6099806ea08fbc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "migrations_macros"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a8ff27a350511de30cdabb77147501c36ef02e0451d957abea2f30caffb2b58"
|
||||
dependencies = [
|
||||
"migrations_internals",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.17"
|
||||
|
@ -955,6 +1022,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
|
||||
|
||||
[[package]]
|
||||
name = "poise"
|
||||
version = "0.5.5"
|
||||
|
@ -992,6 +1065,30 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.109",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
|
@ -1010,6 +1107,17 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r2d2"
|
||||
version = "0.8.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51de85fb3fb6524929c8a2eb85e6b6d363de4e8c48f9e2c2eac4944abc181c93"
|
||||
dependencies = [
|
||||
"log",
|
||||
"parking_lot",
|
||||
"scheduled-thread-pool",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
@ -1168,6 +1276,15 @@ version = "1.0.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cbc66816425a074528352f5789333ecff06ca41b36b0b0efdfbb29edc391a19"
|
||||
dependencies = [
|
||||
"parking_lot",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
@ -1491,6 +1608,15 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
|
@ -1644,6 +1770,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
|
|
@ -22,3 +22,5 @@ chrono = "*"
|
|||
uuid = "*"
|
||||
poise = "0.5.5"
|
||||
async-minecraft-ping = "0.8.0"
|
||||
diesel = { version = "2.0.4", features = ["sqlite", "returning_clauses_for_sqlite_3_35", "r2d2"] }
|
||||
diesel_migrations = { version = "2.0.0", features = [ "sqlite" ] }
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/db/schema.rs"
|
||||
|
||||
[migrations_directory]
|
||||
dir = "migrations"
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE users;
|
|
@ -0,0 +1,11 @@
|
|||
-- Your SQL goes here
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
discord_id UNSIGNED BIG INT NOT NULL,
|
||||
guild_id UNSIGNED BIG INT NOT NULL,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
first_name TEXT NOT NULL,
|
||||
last_name TEXT NOT NULL,
|
||||
|
||||
UNIQUE(discord_id, guild_id)
|
||||
);
|
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
}
|
|
@ -1,12 +1,19 @@
|
|||
mod affluence;
|
||||
mod minecraft;
|
||||
mod users;
|
||||
|
||||
use crate::{Context, Data, Error};
|
||||
|
||||
type EmbedField = (String, String, bool);
|
||||
|
||||
pub fn commands() -> Vec<poise::structs::Command<Data, Error>> {
|
||||
vec![affluence::available(), minecraft::mc(), help()]
|
||||
vec![
|
||||
help(),
|
||||
affluence::available(),
|
||||
minecraft::mc(),
|
||||
users::register(),
|
||||
users::registered(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Show this help menu
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
use crate::db::users::{NewUser, User};
|
||||
use crate::{Context, Error};
|
||||
use diesel::RunQueryDsl;
|
||||
|
||||
#[poise::command(prefix_command, slash_command)]
|
||||
pub async fn register(
|
||||
ctx: Context<'_>,
|
||||
first_name: String,
|
||||
last_name: String,
|
||||
email: String,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(guild_id) = ctx.guild_id() {
|
||||
let discord_id = ctx.author().id.0 as i64;
|
||||
|
||||
{
|
||||
let mut conn = ctx.data().pool.get()?;
|
||||
|
||||
if User::get(&mut conn, guild_id.into(), discord_id)?.is_some() {
|
||||
ctx.say("You've already been registered.").await?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let new_user = NewUser {
|
||||
discord_id,
|
||||
guild_id: guild_id.into(),
|
||||
first_name,
|
||||
last_name,
|
||||
email,
|
||||
};
|
||||
|
||||
new_user.insert(&mut conn)?;
|
||||
}
|
||||
|
||||
ctx.say("You have been registered.").await?;
|
||||
} else {
|
||||
ctx.say("You have to send this message from a guild.")
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[poise::command(prefix_command, slash_command)]
|
||||
pub async fn registered(ctx: Context<'_>) -> Result<(), Error> {
|
||||
if let Some(guild_id) = ctx.guild_id() {
|
||||
let users = {
|
||||
let mut conn = ctx.data().pool.get()?;
|
||||
User::by_guild_id(guild_id.into()).load(&mut conn)?
|
||||
};
|
||||
|
||||
ctx.send(|f| {
|
||||
f.embed(|e| {
|
||||
e.description("Registered users").fields(
|
||||
users
|
||||
.into_iter()
|
||||
.map(|u| (format!("{} {}", u.first_name, u.last_name), u.email, false)),
|
||||
)
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.say("You are not in a guild.").await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
mod schema;
|
||||
pub mod users;
|
||||
|
||||
use diesel::connection::SimpleConnection;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use diesel::sqlite::{Sqlite, SqliteConnection};
|
||||
use std::error::Error;
|
||||
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
|
||||
|
||||
type DbError = Box<dyn Error + Send + Sync + 'static>;
|
||||
|
||||
fn run_migrations(connection: &mut impl MigrationHarness<Sqlite>) -> Result<(), DbError> {
|
||||
// This will run the necessary migrations.
|
||||
//
|
||||
// See the documentation for `MigrationHarness` for
|
||||
// all available methods.
|
||||
connection.run_pending_migrations(MIGRATIONS)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize_db(conn: &mut SqliteConnection) -> Result<(), DbError> {
|
||||
// Enable WAL mode and enforce foreign keys
|
||||
conn.batch_execute(
|
||||
"PRAGMA journal_mode = WAL; PRAGMA synchronous = NORMAL; PRAGMA foreign_keys = ON;",
|
||||
)?;
|
||||
run_migrations(conn)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn initialize_pool(url: &str) -> Result<Pool<ConnectionManager<SqliteConnection>>, DbError> {
|
||||
let manager = ConnectionManager::new(url);
|
||||
|
||||
let pool = Pool::builder().test_on_check_out(true).build(manager)?;
|
||||
|
||||
let mut conn = pool.get()?;
|
||||
initialize_db(&mut conn)?;
|
||||
|
||||
Ok(pool)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
users (id) {
|
||||
id -> Integer,
|
||||
discord_id -> BigInt,
|
||||
guild_id -> BigInt,
|
||||
email -> Text,
|
||||
first_name -> Text,
|
||||
last_name -> Text,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
use super::schema::users::{self, dsl::*};
|
||||
use diesel::dsl::Eq;
|
||||
use diesel::dsl::{AsSelect, Select};
|
||||
use diesel::helper_types::Filter;
|
||||
use diesel::prelude::*;
|
||||
use diesel::sqlite::Sqlite;
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
|
||||
#[derive(Queryable, Selectable, AsChangeset)]
|
||||
#[diesel(table_name = users)]
|
||||
pub struct User {
|
||||
pub id: i32,
|
||||
pub discord_id: i64,
|
||||
pub guild_id: i64,
|
||||
pub email: String,
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = users)]
|
||||
pub struct NewUser {
|
||||
pub discord_id: i64,
|
||||
pub guild_id: i64,
|
||||
pub email: String,
|
||||
pub first_name: String,
|
||||
pub last_name: String,
|
||||
}
|
||||
|
||||
type All = Select<users::table, AsSelect<User, Sqlite>>;
|
||||
type WithGuild<T> = Eq<guild_id, T>;
|
||||
type ByGuild<T> = Filter<All, WithGuild<T>>;
|
||||
|
||||
impl User {
|
||||
pub fn all() -> All {
|
||||
users::table.select(User::as_select())
|
||||
}
|
||||
|
||||
// pub fn by_guild<T>(guild_id_: T) -> ByGuild<T>
|
||||
// where T: AsExpression<BigInt>
|
||||
// {
|
||||
// Self::all().filter(guild_id.eq(guild_id_))
|
||||
// }
|
||||
|
||||
pub fn by_guild_id(guild_id_: i64) -> ByGuild<i64> {
|
||||
Self::all().filter(guild_id.eq(guild_id_))
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
conn: &mut SqliteConnection,
|
||||
guild_id_: i64,
|
||||
discord_id_: i64,
|
||||
) -> Result<Option<User>, diesel::result::Error> {
|
||||
Self::all()
|
||||
.filter(guild_id.eq(guild_id_))
|
||||
.filter(discord_id.eq(discord_id_))
|
||||
.first(conn)
|
||||
.optional()
|
||||
}
|
||||
|
||||
pub fn get_by_id(
|
||||
conn: &mut SqliteConnection,
|
||||
id_: i32,
|
||||
) -> Result<Option<User>, diesel::result::Error> {
|
||||
Self::all().find(id_).first(conn).optional()
|
||||
}
|
||||
|
||||
pub fn update(&self, conn: &mut SqliteConnection) -> Result<usize, diesel::result::Error> {
|
||||
diesel::update(users::table).set(self).execute(conn)
|
||||
}
|
||||
}
|
||||
|
||||
impl NewUser {
|
||||
pub fn insert(&self, conn: &mut SqliteConnection) -> Result<User, diesel::result::Error> {
|
||||
diesel::insert_into(users::table)
|
||||
.values(self)
|
||||
.get_result(conn)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
mod commands;
|
||||
mod db;
|
||||
|
||||
use affluences_api::AffluencesClient;
|
||||
use diesel::r2d2::{ConnectionManager, Pool};
|
||||
use diesel::sqlite::SqliteConnection;
|
||||
use poise::serenity_prelude as serenity;
|
||||
use std::{env::var, time::Duration};
|
||||
|
||||
|
@ -11,6 +14,7 @@ type Context<'a> = poise::Context<'a, Data, Error>;
|
|||
// Custom user data passed to all command functions
|
||||
pub struct Data {
|
||||
client: AffluencesClient,
|
||||
pool: Pool<ConnectionManager<SqliteConnection>>,
|
||||
}
|
||||
|
||||
async fn on_error(error: poise::FrameworkError<'_, Data, Error>) {
|
||||
|
@ -80,6 +84,8 @@ async fn main() {
|
|||
..Default::default()
|
||||
};
|
||||
|
||||
let pool = db::initialize_pool("affy.db").expect("Failed to initialize database.");
|
||||
|
||||
poise::Framework::builder()
|
||||
.token(
|
||||
var("DISCORD_TOKEN")
|
||||
|
@ -91,6 +97,7 @@ async fn main() {
|
|||
poise::builtins::register_globally(ctx, &framework.options().commands).await?;
|
||||
Ok(Data {
|
||||
client: AffluencesClient::new(),
|
||||
pool,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue