aboutsummaryrefslogtreecommitdiffstats
path: root/src/database
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 01:09:10 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 01:09:10 +0100
commitceb96326ccf3daff0472cafdaba3e223d3a20724 (patch)
tree8bd84f484ce714bd10f1c9f25f612ab16fbd8eae /src/database
parentcron: scope jobs to the guild that created them (diff)
downloadsenator-sperma-ceb96326ccf3daff0472cafdaba3e223d3a20724.tar.gz
senator-sperma-ceb96326ccf3daff0472cafdaba3e223d3a20724.zip
triggers: scope groups to the guild that created them
Diffstat (limited to 'src/database')
-rw-r--r--src/database/mod.rs59
1 files changed, 43 insertions, 16 deletions
diff --git a/src/database/mod.rs b/src/database/mod.rs
index 9679811..e81e448 100644
--- a/src/database/mod.rs
+++ b/src/database/mod.rs
@@ -75,14 +75,24 @@ async fn run_migrations(pool: &DbPool) -> Result<()> {
75 sqlx::query( 75 sqlx::query(
76 "CREATE TABLE IF NOT EXISTS triggers ( 76 "CREATE TABLE IF NOT EXISTS triggers (
77 id INTEGER PRIMARY KEY AUTOINCREMENT, 77 id INTEGER PRIMARY KEY AUTOINCREMENT,
78 name TEXT UNIQUE NOT NULL, 78 guild_id INTEGER NOT NULL DEFAULT 0,
79 name TEXT NOT NULL,
79 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, 80 created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
80 last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP 81 last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
82 UNIQUE(guild_id, name)
81 )", 83 )",
82 ) 84 )
83 .execute(pool) 85 .execute(pool)
84 .await?; 86 .await?;
85 87
88 // Add guild_id for pre-existing trigger tables. (The old table also kept a
89 // global UNIQUE(name), so existing databases still require unique trigger
90 // names across guilds; new databases use UNIQUE(guild_id, name).)
91 sqlx::query("ALTER TABLE triggers ADD COLUMN guild_id INTEGER NOT NULL DEFAULT 0")
92 .execute(pool)
93 .await
94 .ok();
95
86 sqlx::query( 96 sqlx::query(
87 "CREATE TABLE IF NOT EXISTS trigger_keywords ( 97 "CREATE TABLE IF NOT EXISTS trigger_keywords (
88 id INTEGER PRIMARY KEY AUTOINCREMENT, 98 id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -416,6 +426,7 @@ pub async fn set_cron_job_active(
416#[derive(Debug, Clone, FromRow)] 426#[derive(Debug, Clone, FromRow)]
417pub struct Trigger { 427pub struct Trigger {
418 pub id: i64, 428 pub id: i64,
429 pub guild_id: i64,
419 pub name: String, 430 pub name: String,
420 pub created_at: Option<DateTime<Utc>>, 431 pub created_at: Option<DateTime<Utc>>,
421 pub last_updated: Option<DateTime<Utc>>, 432 pub last_updated: Option<DateTime<Utc>>,
@@ -437,33 +448,45 @@ pub struct TriggerResponse {
437 pub response_text: String, 448 pub response_text: String,
438} 449}
439 450
440pub async fn create_trigger(pool: &DbPool, name: &str) -> Result<i64> { 451pub async fn create_trigger(pool: &DbPool, guild_id: i64, name: &str) -> Result<i64> {
441 let result = sqlx::query("INSERT INTO triggers (name) VALUES (?)") 452 let result = sqlx::query("INSERT INTO triggers (guild_id, name) VALUES (?, ?)")
453 .bind(guild_id)
442 .bind(name) 454 .bind(name)
443 .execute(pool) 455 .execute(pool)
444 .await?; 456 .await?;
445 Ok(result.last_insert_rowid()) 457 Ok(result.last_insert_rowid())
446} 458}
447 459
448pub async fn delete_trigger(pool: &DbPool, id: i64) -> Result<bool> { 460pub async fn delete_trigger(pool: &DbPool, id: i64, guild_id: i64) -> Result<bool> {
449 let result = sqlx::query("DELETE FROM triggers WHERE id = ?") 461 let result = sqlx::query("DELETE FROM triggers WHERE id = ? AND guild_id = ?")
450 .bind(id) 462 .bind(id)
463 .bind(guild_id)
451 .execute(pool) 464 .execute(pool)
452 .await?; 465 .await?;
453 Ok(result.rows_affected() > 0) 466 Ok(result.rows_affected() > 0)
454} 467}
455 468
456pub async fn get_trigger_by_name(pool: &DbPool, name: &str) -> Result<Option<Trigger>> { 469pub async fn get_trigger_by_name(
457 Ok(sqlx::query_as("SELECT * FROM triggers WHERE name = ?") 470 pool: &DbPool,
458 .bind(name) 471 guild_id: i64,
459 .fetch_optional(pool) 472 name: &str,
460 .await?) 473) -> Result<Option<Trigger>> {
474 Ok(
475 sqlx::query_as("SELECT * FROM triggers WHERE guild_id = ? AND name = ?")
476 .bind(guild_id)
477 .bind(name)
478 .fetch_optional(pool)
479 .await?,
480 )
461} 481}
462 482
463pub async fn list_triggers(pool: &DbPool) -> Result<Vec<Trigger>> { 483pub async fn list_triggers(pool: &DbPool, guild_id: i64) -> Result<Vec<Trigger>> {
464 Ok(sqlx::query_as("SELECT * FROM triggers ORDER BY name") 484 Ok(
465 .fetch_all(pool) 485 sqlx::query_as("SELECT * FROM triggers WHERE guild_id = ? ORDER BY name")
466 .await?) 486 .bind(guild_id)
487 .fetch_all(pool)
488 .await?,
489 )
467} 490}
468 491
469pub async fn add_keyword(pool: &DbPool, trigger_id: i64, keyword: &str) -> Result<()> { 492pub async fn add_keyword(pool: &DbPool, trigger_id: i64, keyword: &str) -> Result<()> {
@@ -526,7 +549,11 @@ pub async fn get_responses(pool: &DbPool, trigger_id: i64) -> Result<Vec<String>
526pub async fn load_all_triggers_with_keywords_and_responses( 549pub async fn load_all_triggers_with_keywords_and_responses(
527 pool: &DbPool, 550 pool: &DbPool,
528) -> Result<Vec<(Trigger, Vec<String>, Vec<String>)>> { 551) -> Result<Vec<(Trigger, Vec<String>, Vec<String>)>> {
529 let triggers = list_triggers(pool).await?; 552 // The event cache needs every guild's triggers; `/trigger list` and
553 // mutations use the guild-scoped helpers above.
554 let triggers: Vec<Trigger> = sqlx::query_as("SELECT * FROM triggers ORDER BY name")
555 .fetch_all(pool)
556 .await?;
530 let mut result = Vec::new(); 557 let mut result = Vec::new();
531 for t in triggers { 558 for t in triggers {
532 let keywords = get_keywords(pool, t.id).await?; 559 let keywords = get_keywords(pool, t.id).await?;