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
|
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"
|
sysinfo = "0.30.7"
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
tokio = { version = "1.36.0", features = ["macros", "signal", "rt-multi-thread"] }
|
tokio = { version = "1.36.0", features = ["macros", "signal", "rt-multi-thread"] }
|
||||||
|
tokio-postgres = "0.7.10"
|
||||||
uptime_lib = "0.3.0"
|
uptime_lib = "0.3.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
@ -2,8 +2,22 @@ version: '3.8'
|
|||||||
services:
|
services:
|
||||||
bot:
|
bot:
|
||||||
container_name: rustbot
|
container_name: rustbot
|
||||||
image: 'git.toast-server.net/toast/rustbot:main'
|
#image: 'git.toast-server.net/toast/rustbot:main'
|
||||||
build: .
|
build: .
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
restart: unless-stopped
|
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
|
#!/bin/bash
|
||||||
|
|
||||||
export $(cat .env | xargs) && cargo run
|
export $(grep -v '^#' .env | xargs) && cargo run
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
pub mod ping;
|
pub mod ping;
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
pub mod uptime;
|
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 commands;
|
||||||
|
mod controllers;
|
||||||
|
mod models;
|
||||||
mod internals;
|
mod internals;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -58,13 +60,15 @@ async fn on_ready(
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let token = var("DISCORD_TOKEN").expect("Expected a \"DISCORD_TOKEN\" in the envvar but none was found");
|
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()
|
let framework = poise::Framework::builder()
|
||||||
.options(poise::FrameworkOptions {
|
.options(poise::FrameworkOptions {
|
||||||
commands: vec![
|
commands: vec![
|
||||||
commands::ping::ping(),
|
commands::ping::ping(),
|
||||||
commands::eval::eval(),
|
commands::eval::eval(),
|
||||||
commands::uptime::uptime()
|
commands::uptime::uptime(),
|
||||||
|
commands::sample::sample()
|
||||||
],
|
],
|
||||||
pre_command: |ctx| Box::pin(async move {
|
pre_command: |ctx| Box::pin(async move {
|
||||||
let get_guild_name = match ctx.guild() {
|
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)))
|
.setup(|ctx, ready, framework| Box::pin(on_ready(ctx, ready, framework)))
|
||||||
.build();
|
.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 {
|
if let Err(why) = client.start().await {
|
||||||
println!("Client error: {:?}", why);
|
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