Compare commits
3 Commits
current
...
jellywx/gu
Author | SHA1 | Date | |
---|---|---|---|
b0a04bb289 | |||
eef1f6f3e8 | |||
3d08027325 |
92
migration/05-restructure-guild-table.sql
Normal file
92
migration/05-restructure-guild-table.sql
Normal file
@ -0,0 +1,92 @@
|
||||
SET foreign_key_checks = 0;
|
||||
|
||||
START TRANSACTION;
|
||||
|
||||
-- drop existing constraints
|
||||
ALTER TABLE channels DROP FOREIGN KEY `channels_ibfk_1`;
|
||||
ALTER TABLE command_aliases DROP FOREIGN KEY `command_aliases_ibfk_1`;
|
||||
ALTER TABLE events DROP FOREIGN KEY `events_ibfk_1`;
|
||||
ALTER TABLE guild_users DROP FOREIGN KEY `guild_users_ibfk_1`;
|
||||
ALTER TABLE macro DROP FOREIGN KEY `macro_ibfk_1`;
|
||||
ALTER TABLE roles DROP FOREIGN KEY `roles_ibfk_1`;
|
||||
ALTER TABLE todos DROP FOREIGN KEY `todos_ibfk_2`;
|
||||
ALTER TABLE reminder_template DROP FOREIGN KEY `reminder_template_ibfk_1`;
|
||||
|
||||
-- update foreign key types
|
||||
ALTER TABLE channels MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE command_aliases MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE events MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE guild_users MODIFY `guild` BIGINT UNSIGNED;
|
||||
ALTER TABLE macro MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE roles MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE todos MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
ALTER TABLE reminder_template MODIFY `guild_id` BIGINT UNSIGNED;
|
||||
|
||||
-- update foreign key values
|
||||
UPDATE channels SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE command_aliases SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE events SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE guild_users SET `guild` = (SELECT `guild` FROM guilds WHERE guilds.`id` = guild_users.`guild`);
|
||||
UPDATE macro SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE roles SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE todos SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
UPDATE reminder_template SET `guild_id` = (SELECT `guild` FROM guilds WHERE guilds.`id` = `guild_id`);
|
||||
|
||||
-- update guilds table
|
||||
ALTER TABLE guilds MODIFY `id` BIGINT UNSIGNED NOT NULL;
|
||||
UPDATE guilds SET `id` = `guild`;
|
||||
ALTER TABLE guilds DROP COLUMN `guild`;
|
||||
ALTER TABLE guilds ADD COLUMN `default_channel` BIGINT UNSIGNED;
|
||||
ALTER TABLE guilds ADD CONSTRAINT `default_channel_fk`
|
||||
FOREIGN KEY (`default_channel`)
|
||||
REFERENCES channels(`channel`)
|
||||
ON DELETE SET NULL
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
-- re-add constraints
|
||||
ALTER TABLE channels ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE command_aliases ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE events ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE guild_users ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE macro ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE roles ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE todos ADD CONSTRAINT
|
||||
FOREIGN KEY (`guild_id`)
|
||||
REFERENCES guilds(`id`)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE;
|
||||
|
||||
|
||||
COMMIT;
|
||||
|
||||
SET foreign_key_checks = 1;
|
@ -24,7 +24,7 @@ pub async fn macro_name_autocomplete(ctx: Context<'_>, partial: &str) -> Vec<Str
|
||||
SELECT name
|
||||
FROM macro
|
||||
WHERE
|
||||
guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
||||
guild_id = ?
|
||||
AND name LIKE CONCAT(?, '%')",
|
||||
ctx.guild_id().unwrap().0,
|
||||
partial,
|
||||
|
@ -17,7 +17,7 @@ pub async fn delete_macro(
|
||||
) -> Result<(), Error> {
|
||||
match sqlx::query!(
|
||||
"
|
||||
SELECT id FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
||||
SELECT id FROM macro WHERE guild_id = ? AND name = ?",
|
||||
ctx.guild_id().unwrap().0,
|
||||
name
|
||||
)
|
||||
|
@ -24,7 +24,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
||||
|
||||
let aliases = sqlx::query_as!(
|
||||
Alias,
|
||||
"SELECT name, command FROM command_aliases WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||
"SELECT name, command FROM command_aliases WHERE guild_id = ?",
|
||||
guild_id.0
|
||||
)
|
||||
.fetch_all(&mut transaction)
|
||||
@ -36,7 +36,7 @@ pub async fn migrate_macro(ctx: Context<'_>) -> Result<(), Error> {
|
||||
match parse_text_command(guild_id, alias.name, &alias.command) {
|
||||
Some(cmd_macro) => {
|
||||
sqlx::query!(
|
||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES (?, ?, ?, ?)",
|
||||
cmd_macro.guild_id.0,
|
||||
cmd_macro.name,
|
||||
cmd_macro.description,
|
||||
|
@ -31,7 +31,7 @@ pub async fn record_macro(
|
||||
|
||||
let row = sqlx::query!(
|
||||
"
|
||||
SELECT 1 as _e FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?",
|
||||
SELECT 1 as _e FROM macro WHERE guild_id = ? AND name = ?",
|
||||
guild_id.0,
|
||||
name
|
||||
)
|
||||
@ -121,7 +121,7 @@ pub async fn finish_macro(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let json = serde_json::to_string(&command_macro.commands).unwrap();
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?)",
|
||||
"INSERT INTO macro (guild_id, name, description, commands) VALUES (?, ?, ?, ?)",
|
||||
command_macro.guild_id.0,
|
||||
command_macro.name,
|
||||
command_macro.description,
|
||||
|
@ -2,6 +2,7 @@ use chrono::offset::Utc;
|
||||
use chrono_tz::{Tz, TZ_VARIANTS};
|
||||
use levenshtein::levenshtein;
|
||||
use log::warn;
|
||||
use poise::serenity_prelude::{ChannelId, Mentionable};
|
||||
|
||||
use super::autocomplete::timezone_autocomplete;
|
||||
use crate::{consts::THEME_COLOR, models::CtxData, Context, Error};
|
||||
@ -148,11 +149,51 @@ pub async fn unset_allowed_dm(ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set defaults for commands
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
identifying_name = "default",
|
||||
default_member_permissions = "MANAGE_GUILD"
|
||||
)]
|
||||
pub async fn default(_ctx: Context<'_>) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set a default channel for reminders to be sent to
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
guild_only = true,
|
||||
identifying_name = "default_channel",
|
||||
default_member_permissions = "MANAGE_GUILD"
|
||||
)]
|
||||
pub async fn default_channel(
|
||||
ctx: Context<'_>,
|
||||
#[description = "Channel to send reminders to by default"] channel: Option<ChannelId>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(mut guild_data) = ctx.guild_data().await {
|
||||
guild_data.default_channel = channel.map(|c| c.0);
|
||||
|
||||
guild_data.commit_changes(&ctx.data().database).await?;
|
||||
|
||||
if let Some(channel) = channel {
|
||||
ctx.send(|r| {
|
||||
r.ephemeral(true).content(format!("Default channel set to {}", channel.mention()))
|
||||
})
|
||||
.await?;
|
||||
} else {
|
||||
ctx.send(|r| r.ephemeral(true).content("Default channel unset.")).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// View the webhook being used to send reminders to this channel
|
||||
#[poise::command(
|
||||
slash_command,
|
||||
identifying_name = "webhook_url",
|
||||
required_permissions = "ADMINISTRATOR"
|
||||
required_permissions = "ADMINISTRATOR",
|
||||
default_member_permissions = "ADMINISTRATOR"
|
||||
)]
|
||||
pub async fn webhook(ctx: Context<'_>) -> Result<(), Error> {
|
||||
match ctx.channel_data().await {
|
||||
|
@ -653,7 +653,9 @@ async fn create_reminder(
|
||||
let list = channels.map(|arg| parse_mention_list(&arg)).unwrap_or_default();
|
||||
|
||||
if list.is_empty() {
|
||||
if ctx.guild_id().is_some() {
|
||||
if let Some(channel_id) = ctx.default_channel().await {
|
||||
vec![ReminderScope::Channel(channel_id.0)]
|
||||
} else if ctx.guild_id().is_some() {
|
||||
vec![ReminderScope::Channel(ctx.channel_id().0)]
|
||||
} else {
|
||||
vec![ReminderScope::User(ctx.author().id.0)]
|
||||
|
@ -47,7 +47,7 @@ pub async fn todo_guild_add(
|
||||
) -> Result<(), Error> {
|
||||
sqlx::query!(
|
||||
"INSERT INTO todos (guild_id, value)
|
||||
VALUES ((SELECT id FROM guilds WHERE guild = ?), ?)",
|
||||
VALUES (?, ?)",
|
||||
ctx.guild_id().unwrap().0,
|
||||
task
|
||||
)
|
||||
@ -70,9 +70,7 @@ VALUES ((SELECT id FROM guilds WHERE guild = ?), ?)",
|
||||
)]
|
||||
pub async fn todo_guild_view(ctx: Context<'_>) -> Result<(), Error> {
|
||||
let values = sqlx::query!(
|
||||
"SELECT todos.id, value FROM todos
|
||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||
WHERE guilds.guild = ?",
|
||||
"SELECT todos.id, value FROM todos WHERE guild_id = ?",
|
||||
ctx.guild_id().unwrap().0,
|
||||
)
|
||||
.fetch_all(&ctx.data().database)
|
||||
@ -122,7 +120,7 @@ pub async fn todo_channel_add(
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO todos (guild_id, channel_id, value)
|
||||
VALUES ((SELECT id FROM guilds WHERE guild = ?), (SELECT id FROM channels WHERE channel = ?), ?)",
|
||||
VALUES (?, (SELECT id FROM channels WHERE channel = ?), ?)",
|
||||
ctx.guild_id().unwrap().0,
|
||||
ctx.channel_id().0,
|
||||
task
|
||||
|
@ -222,9 +222,7 @@ WHERE channels.channel = ?",
|
||||
.collect::<Vec<(usize, String)>>()
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"SELECT todos.id, value FROM todos
|
||||
INNER JOIN guilds ON todos.guild_id = guilds.id
|
||||
WHERE guilds.guild = ?",
|
||||
"SELECT todos.id, value FROM todos WHERE guild_id = ?",
|
||||
pager.guild_id,
|
||||
)
|
||||
.fetch_all(&data.database)
|
||||
|
@ -6,7 +6,9 @@ use poise::{
|
||||
serenity_prelude::{model::application::interaction::Interaction, utils::shard_id},
|
||||
};
|
||||
|
||||
use crate::{component_models::ComponentDataModel, Data, Error, THEME_COLOR};
|
||||
use crate::{
|
||||
component_models::ComponentDataModel, models::guild_data::GuildData, Data, Error, THEME_COLOR,
|
||||
};
|
||||
|
||||
pub async fn listener(
|
||||
ctx: &serenity::Context,
|
||||
@ -27,7 +29,7 @@ pub async fn listener(
|
||||
if *is_new {
|
||||
let guild_id = guild.id.as_u64().to_owned();
|
||||
|
||||
sqlx::query!("INSERT IGNORE INTO guilds (guild) VALUES (?)", guild_id)
|
||||
sqlx::query!("INSERT IGNORE INTO guilds (id) VALUES (?)", guild_id)
|
||||
.execute(&data.database)
|
||||
.await?;
|
||||
|
||||
@ -61,16 +63,28 @@ To stay up to date on the latest features and fixes, join our [Discord](https://
|
||||
}
|
||||
}
|
||||
poise::Event::GuildDelete { incomplete, .. } => {
|
||||
let _ = sqlx::query!("DELETE FROM guilds WHERE guild = ?", incomplete.id.0)
|
||||
let _ = sqlx::query!("DELETE FROM guilds WHERE id = ?", incomplete.id.0)
|
||||
.execute(&data.database)
|
||||
.await;
|
||||
}
|
||||
poise::Event::InteractionCreate { interaction } => {
|
||||
if let Interaction::MessageComponent(component) = interaction {
|
||||
let component_model = ComponentDataModel::from_custom_id(&component.data.custom_id);
|
||||
match interaction {
|
||||
Interaction::ApplicationCommand(app_command) => {
|
||||
if let Some(guild_id) = app_command.guild_id {
|
||||
// check database guild exists
|
||||
GuildData::from_guild(guild_id, &data.database).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Interaction::MessageComponent(component) => {
|
||||
let component_model =
|
||||
ComponentDataModel::from_custom_id(&component.data.custom_id);
|
||||
|
||||
component_model.act(ctx, data, component).await;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
@ -120,6 +120,10 @@ async fn _main(tx: Sender<()>) -> Result<(), Box<dyn StdError + Send + Sync>> {
|
||||
],
|
||||
..command_macro::macro_base()
|
||||
},
|
||||
poise::Command {
|
||||
subcommands: vec![moderation_cmds::default_channel()],
|
||||
..moderation_cmds::default()
|
||||
},
|
||||
reminder_cmds::pause(),
|
||||
reminder_cmds::offset(),
|
||||
reminder_cmds::nudge(),
|
||||
|
@ -38,7 +38,7 @@ SELECT id, name, nudge, blacklisted, webhook_id, webhook_token, paused, paused_u
|
||||
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT IGNORE INTO channels (channel, name, guild_id) VALUES (?, ?, (SELECT id FROM guilds WHERE guild = ?))
|
||||
INSERT IGNORE INTO channels (channel, name, guild_id) VALUES (?, ?, ?)
|
||||
",
|
||||
channel_id,
|
||||
channel_name,
|
||||
|
@ -43,7 +43,7 @@ pub async fn guild_command_macro(
|
||||
) -> Option<CommandMacro<Data, Error>> {
|
||||
let row = sqlx::query!(
|
||||
"
|
||||
SELECT * FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?) AND name = ?
|
||||
SELECT * FROM macro WHERE guild_id = ? AND name = ?
|
||||
",
|
||||
ctx.guild_id().unwrap().0,
|
||||
name
|
||||
|
52
src/models/guild_data.rs
Normal file
52
src/models/guild_data.rs
Normal file
@ -0,0 +1,52 @@
|
||||
use sqlx::MySqlPool;
|
||||
|
||||
use crate::GuildId;
|
||||
|
||||
pub struct GuildData {
|
||||
pub id: u64,
|
||||
pub default_channel: Option<u64>,
|
||||
}
|
||||
|
||||
impl GuildData {
|
||||
pub async fn from_guild(guild: GuildId, pool: &MySqlPool) -> Result<Self, sqlx::Error> {
|
||||
let guild_id = guild.0;
|
||||
|
||||
if let Ok(row) = sqlx::query_as_unchecked!(
|
||||
Self,
|
||||
"
|
||||
SELECT id, default_channel FROM guilds WHERE id = ?
|
||||
",
|
||||
guild_id
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
{
|
||||
Ok(row)
|
||||
} else {
|
||||
sqlx::query!(
|
||||
"
|
||||
INSERT IGNORE INTO guilds (id) VALUES (?)
|
||||
",
|
||||
guild_id
|
||||
)
|
||||
.execute(&pool.clone())
|
||||
.await?;
|
||||
|
||||
Ok(Self { id: guild_id, default_channel: None })
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn commit_changes(&self, pool: &MySqlPool) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"
|
||||
UPDATE guilds SET default_channel = ? WHERE id = ?
|
||||
",
|
||||
self.default_channel,
|
||||
self.id
|
||||
)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1,28 +1,28 @@
|
||||
pub mod channel_data;
|
||||
pub mod command_macro;
|
||||
pub mod guild_data;
|
||||
pub mod reminder;
|
||||
pub mod timer;
|
||||
pub mod user_data;
|
||||
|
||||
use chrono_tz::Tz;
|
||||
use poise::serenity_prelude::{async_trait, model::id::UserId};
|
||||
use log::warn;
|
||||
use poise::serenity_prelude::{async_trait, model::id::UserId, ChannelId};
|
||||
|
||||
use crate::{
|
||||
models::{channel_data::ChannelData, user_data::UserData},
|
||||
models::{channel_data::ChannelData, guild_data::GuildData, user_data::UserData},
|
||||
CommandMacro, Context, Data, Error, GuildId,
|
||||
};
|
||||
|
||||
#[async_trait]
|
||||
pub trait CtxData {
|
||||
async fn user_data<U: Into<UserId> + Send>(&self, user_id: U) -> Result<UserData, Error>;
|
||||
|
||||
async fn author_data(&self) -> Result<UserData, Error>;
|
||||
|
||||
async fn timezone(&self) -> Tz;
|
||||
|
||||
async fn channel_data(&self) -> Result<ChannelData, Error>;
|
||||
|
||||
async fn guild_data(&self) -> Option<GuildData>;
|
||||
async fn command_macros(&self) -> Result<Vec<CommandMacro<Data, Error>>, Error>;
|
||||
async fn default_channel(&self) -> Option<ChannelId>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -51,24 +51,55 @@ impl CtxData for Context<'_> {
|
||||
async fn command_macros(&self) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
|
||||
self.data().command_macros(self.guild_id().unwrap()).await
|
||||
}
|
||||
|
||||
async fn default_channel(&self) -> Option<ChannelId> {
|
||||
match self.guild_id() {
|
||||
Some(guild_id) => {
|
||||
let guild_data = GuildData::from_guild(guild_id, &self.data().database).await;
|
||||
|
||||
match guild_data {
|
||||
Ok(data) => data.default_channel.map(|c| ChannelId(c)),
|
||||
|
||||
Err(e) => {
|
||||
warn!("SQL error: {:?}", e);
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
async fn guild_data(&self) -> Option<GuildData> {
|
||||
match self.guild_id() {
|
||||
Some(guild_id) => GuildData::from_guild(guild_id, &self.data().database).await.ok(),
|
||||
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub(crate) async fn command_macros(
|
||||
pub async fn command_macros(
|
||||
&self,
|
||||
guild_id: GuildId,
|
||||
) -> Result<Vec<CommandMacro<Data, Error>>, Error> {
|
||||
let rows = sqlx::query!(
|
||||
"SELECT name, description, commands FROM macro WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||
"SELECT name, description, commands FROM macro WHERE guild_id = ?",
|
||||
guild_id.0
|
||||
)
|
||||
.fetch_all(&self.database)
|
||||
.await?.iter().map(|row| CommandMacro {
|
||||
.await?
|
||||
.iter()
|
||||
.map(|row| CommandMacro {
|
||||
guild_id,
|
||||
name: row.name.clone(),
|
||||
description: row.description.clone(),
|
||||
commands: serde_json::from_str(&row.commands).unwrap(),
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(rows)
|
||||
}
|
||||
|
@ -245,7 +245,7 @@ LEFT JOIN
|
||||
ON
|
||||
reminders.set_by = users.id
|
||||
WHERE
|
||||
channels.guild_id = (SELECT id FROM guilds WHERE guild = ?)
|
||||
channels.guild_id = ?
|
||||
",
|
||||
guild_id.as_u64()
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user