Integrate Postgres database and add sample model
This commit is contained in:
parent
a77f3c84ea
commit
cdee302f4f
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +1,2 @@
|
||||
target
|
||||
.env
|
||||
.env*
|
||||
|
647
Cargo.lock
generated
647
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@ poise = "0.6.1"
|
||||
sysinfo = "0.30.7"
|
||||
tempfile = "3.10.1"
|
||||
tokio = { version = "1.36.0", features = ["macros", "signal", "rt-multi-thread"] }
|
||||
tokio-postgres = "0.7.10"
|
||||
uptime_lib = "0.3.0"
|
||||
|
||||
[[bin]]
|
||||
|
@ -2,8 +2,22 @@ version: '3.8'
|
||||
services:
|
||||
bot:
|
||||
container_name: rustbot
|
||||
image: 'git.toast-server.net/toast/rustbot:main'
|
||||
#image: 'git.toast-server.net/toast/rustbot:main'
|
||||
build: .
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
- db
|
||||
db:
|
||||
container_name: rustbot-database
|
||||
image: postgres:16.2-alpine3.19
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 37931:5432/tcp
|
||||
volumes:
|
||||
- /var/lib/docker/volumes/rustbot-database:/var/lib/postgresql/data:rw
|
||||
environment:
|
||||
POSTGRES_USER: ${POSTGRES_USER}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||
POSTGRES_DB: ${POSTGRES_DB}
|
||||
|
2
run.sh
2
run.sh
@ -1,3 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
export $(cat .env | xargs) && cargo run
|
||||
export $(grep -v '^#' .env | xargs) && cargo run
|
||||
|
@ -1,3 +1,4 @@
|
||||
pub mod ping;
|
||||
pub mod eval;
|
||||
pub mod uptime;
|
||||
pub mod sample;
|
||||
|
83
src/commands/sample.rs
Normal file
83
src/commands/sample.rs
Normal file
@ -0,0 +1,83 @@
|
||||
use crate::{
|
||||
Error,
|
||||
models::sample::SampleData
|
||||
};
|
||||
|
||||
use poise::CreateReply;
|
||||
|
||||
/// Perform sample CRUD operations in database
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
subcommands("list", "create", "update", "delete"),
|
||||
subcommand_required
|
||||
)]
|
||||
pub async fn sample(_: poise::Context<'_, (), Error>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List sample data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn list(
|
||||
ctx: poise::Context<'_, (), Error>,
|
||||
#[description = "ID of the sample data"] id: u64
|
||||
) -> Result<(), Error> {
|
||||
let samples = SampleData::list_data(id).await?;
|
||||
|
||||
let mut response = String::new();
|
||||
for sample in samples {
|
||||
response.push_str(&format!("ID: {}\n", sample.id));
|
||||
response.push_str(&format!("Text: {}\n", sample.text_val));
|
||||
response.push_str(&format!("Int: {}\n", sample.int_val));
|
||||
response.push_str(&format!("Boolean: {}\n\n", sample.boolean_val));
|
||||
}
|
||||
|
||||
ctx.send(CreateReply::default()
|
||||
.content(response)
|
||||
).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create sample data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn create(
|
||||
ctx: poise::Context<'_, (), Error>,
|
||||
#[description = "Text value"] text: String,
|
||||
#[description = "Int value"] int: u64,
|
||||
#[description = "Boolean value"] boolean: bool
|
||||
) -> Result<(), Error> {
|
||||
SampleData::create_data(text, int as i64, boolean).await?;
|
||||
|
||||
ctx.send(CreateReply::default().content("Done!")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Update sample data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn update(
|
||||
ctx: poise::Context<'_, (), Error>,
|
||||
#[description = "ID of the sample data"] id: u64,
|
||||
#[description = "Text value"] text: String,
|
||||
#[description = "Int value"] int: u64,
|
||||
#[description = "Boolean value"] boolean: bool
|
||||
) -> Result<(), Error> {
|
||||
SampleData::update_data(id, text, int as i64, boolean).await?;
|
||||
|
||||
ctx.send(CreateReply::default().content("Done!")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete sample data
|
||||
#[poise::command(slash_command)]
|
||||
pub async fn delete(
|
||||
ctx: poise::Context<'_, (), Error>,
|
||||
#[description = "ID of the sample data"] id: u64
|
||||
) -> Result<(), Error> {
|
||||
SampleData::delete_data(id).await?;
|
||||
|
||||
ctx.send(CreateReply::default().content("Done!")).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
35
src/controllers/database.rs
Normal file
35
src/controllers/database.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use poise::serenity_prelude::prelude::TypeMapKey;
|
||||
use tokio_postgres::{Client, NoTls, Error};
|
||||
|
||||
pub struct DatabaseController {
|
||||
pub client: Client
|
||||
}
|
||||
|
||||
impl TypeMapKey for DatabaseController {
|
||||
type Value = DatabaseController;
|
||||
}
|
||||
|
||||
impl DatabaseController {
|
||||
pub async fn new() -> Result<DatabaseController, Error> {
|
||||
let db_uri = std::env::var("DATABASE_URI").expect("Expected a \"DATABASE_URI\" in the envvar but none was found");
|
||||
let (client, connection) = tokio_postgres::connect(&db_uri, NoTls).await?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = connection.await {
|
||||
eprintln!("Connection error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// Sample model
|
||||
client.batch_execute("
|
||||
CREATE TABLE IF NOT EXISTS sample (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
text_val VARCHAR(255) NOT NULL,
|
||||
int_val BIGINT NOT NULL,
|
||||
boolean_val BOOLEAN NOT NULL
|
||||
);
|
||||
").await?;
|
||||
|
||||
Ok(DatabaseController { client })
|
||||
}
|
||||
}
|
1
src/controllers/mod.rs
Normal file
1
src/controllers/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod database;
|
15
src/main.rs
15
src/main.rs
@ -1,4 +1,6 @@
|
||||
mod commands;
|
||||
mod controllers;
|
||||
mod models;
|
||||
mod internals;
|
||||
|
||||
use std::{
|
||||
@ -58,13 +60,15 @@ async fn on_ready(
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let token = var("DISCORD_TOKEN").expect("Expected a \"DISCORD_TOKEN\" in the envvar but none was found");
|
||||
let db = controllers::database::DatabaseController::new().await.expect("Failed to connect to database");
|
||||
|
||||
let framework = poise::Framework::builder()
|
||||
.options(poise::FrameworkOptions {
|
||||
commands: vec![
|
||||
commands::ping::ping(),
|
||||
commands::eval::eval(),
|
||||
commands::uptime::uptime()
|
||||
commands::uptime::uptime(),
|
||||
commands::sample::sample()
|
||||
],
|
||||
pre_command: |ctx| Box::pin(async move {
|
||||
let get_guild_name = match ctx.guild() {
|
||||
@ -87,7 +91,14 @@ async fn main() {
|
||||
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework)))
|
||||
.build();
|
||||
|
||||
let mut client = ClientBuilder::new(token, GatewayIntents::GUILDS).framework(framework).await.expect("Error creating client");
|
||||
let mut client = ClientBuilder::new(token, GatewayIntents::GUILDS)
|
||||
.framework(framework)
|
||||
.await.expect("Error creating client");
|
||||
|
||||
{
|
||||
let mut data = client.data.write().await;
|
||||
data.insert::<controllers::database::DatabaseController>(db);
|
||||
}
|
||||
|
||||
if let Err(why) = client.start().await {
|
||||
println!("Client error: {:?}", why);
|
||||
|
1
src/models/mod.rs
Normal file
1
src/models/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod sample;
|
70
src/models/sample.rs
Normal file
70
src/models/sample.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use crate::controllers::database::DatabaseController;
|
||||
|
||||
pub struct SampleData {
|
||||
pub id: i64,
|
||||
pub text_val: String,
|
||||
pub int_val: i64,
|
||||
pub boolean_val: bool
|
||||
}
|
||||
|
||||
impl SampleData {
|
||||
pub async fn list_data(id: u64) -> Result<Vec<Self>, tokio_postgres::Error> {
|
||||
let client = DatabaseController::new().await?.client;
|
||||
let rows = client.query("
|
||||
SELECT * FROM sample
|
||||
WHERE id = $1
|
||||
", &[&(id as i64)]).await?;
|
||||
|
||||
let mut data = Vec::new();
|
||||
for row in rows {
|
||||
data.push(Self {
|
||||
id: row.get("id"),
|
||||
text_val: row.get("text_val"),
|
||||
int_val: row.get("int_val"),
|
||||
boolean_val: row.get("boolean_val")
|
||||
});
|
||||
}
|
||||
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
pub async fn create_data(
|
||||
text: String,
|
||||
int: i64,
|
||||
boolean: bool
|
||||
) -> Result<(), tokio_postgres::Error> {
|
||||
let client = DatabaseController::new().await?.client;
|
||||
client.execute("
|
||||
INSERT INTO sample (text_val, int_val, boolean_val)
|
||||
VALUES ($1, $2, $3)
|
||||
", &[&text, &int, &boolean]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_data(
|
||||
id: u64,
|
||||
text: String,
|
||||
int: i64,
|
||||
boolean: bool
|
||||
) -> Result<(), tokio_postgres::Error> {
|
||||
let client = DatabaseController::new().await?.client;
|
||||
client.execute("
|
||||
UPDATE sample
|
||||
SET text_val = $1, int_val = $2, boolean_val = $3
|
||||
WHERE id = $4
|
||||
", &[&text, &int, &boolean, &(id as i64)]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn delete_data(id: u64) -> Result<(), tokio_postgres::Error> {
|
||||
let client = DatabaseController::new().await?.client;
|
||||
client.execute("
|
||||
DELETE FROM sample
|
||||
WHERE id = $1
|
||||
", &[&(id as i64)]).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user