Skip to content
Merged
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ config.toml
config.toml.backup
node_modules
.env
db/
/db/
.idea
.vscode/
package-lock.json
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions migrations/20251125000000_create_snippets.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-- Add migration script here
CREATE TABLE IF NOT EXISTS "snippets" (
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
"key" TEXT NOT NULL UNIQUE,
"content" TEXT NOT NULL,
"created_by" TEXT NOT NULL,
"created_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX IF NOT EXISTS "idx_snippets_key" ON "snippets"("key");
1 change: 1 addition & 0 deletions rustmail/src/bot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub async fn run_bot(
registry.register_command(TakeCommand);
registry.register_command(ReleaseCommand);
registry.register_command(PingCommand);
registry.register_command(SnippetCommand);

let registry = Arc::new(registry);

Expand Down
35 changes: 20 additions & 15 deletions rustmail/src/commands/anonreply/text_command/anonreply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,29 @@ pub async fn anonreply(
.as_ref()
.ok_or_else(database_connection_failed)?;

let content = extract_reply_content(&msg.content, &config.command.prefix, &["anonreply", "ar"]);
let mut content =
extract_reply_content(&msg.content, &config.command.prefix, &["anonreply", "ar"]);

if let Some(text) = &content {
if let Some(stripped) = text.strip_prefix("{{").and_then(|s| s.strip_suffix("}}")) {
let snippet_key = stripped.trim();
match get_snippet_by_key(snippet_key, db_pool).await? {
Some(snippet) => {
content = Some(snippet.content);
}
None => {
return Err(ModmailError::Command(CommandError::SnippetNotFound(
snippet_key.to_string(),
)));
}
}
}
}

let intent = extract_intent(content, &msg.attachments).await;

let Some(intent) = intent else {
MessageBuilder::system_message(&ctx, config)
.translated_content(
"reply.missing_content",
None,
Some(msg.author.id),
msg.guild_id.map(|g| g.get()),
)
.await
.color(0xFF0000)
.reply_to(msg.clone())
.send_and_forget()
.await;

return Err(validation_failed("Missing content"));
return Err(ModmailError::Message(MessageError::MessageEmpty));
};

let thread = fetch_thread(db_pool, &msg.channel_id.to_string()).await?;
Expand Down
2 changes: 2 additions & 0 deletions rustmail/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod release;
pub mod remove_reminder;
pub mod remove_staff;
pub mod reply;
pub mod snippet;
pub mod take;

pub use add_reminder::*;
Expand All @@ -49,6 +50,7 @@ pub use release::*;
pub use remove_reminder::*;
pub use remove_staff::*;
pub use reply::*;
pub use snippet::*;
pub use take::*;

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
Expand Down
36 changes: 35 additions & 1 deletion rustmail/src/commands/reply/slash_command/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ impl RegistrableCommand for ReplyCommand {
None,
)
.await;
let snippet_desc = get_translated_message(
&config,
"slash_command.reply_snippet_argument_description",
None,
None,
None,
None,
)
.await;
let anonymous_desc = get_translated_message(
&config,
"slash_command.reply_anonymous_argument_description",
Expand All @@ -79,7 +88,15 @@ impl RegistrableCommand for ReplyCommand {
"message",
message_desc,
)
.required(true),
.required(false),
)
.add_option(
CreateCommandOption::new(
CommandOptionType::String,
"snippet",
snippet_desc,
)
.required(false),
)
.add_option(
CreateCommandOption::new(
Expand Down Expand Up @@ -119,6 +136,7 @@ impl RegistrableCommand for ReplyCommand {
defer_response(&ctx, &command).await?;

let mut content: Option<String> = None;
let mut snippet_key: Option<String> = None;
let mut attachments: Vec<Attachment> = Vec::new();
let mut anonymous: bool = false;

Expand All @@ -127,6 +145,9 @@ impl RegistrableCommand for ReplyCommand {
CommandDataOptionValue::String(val) if option.name == "message" => {
content = Some(val.clone());
}
CommandDataOptionValue::String(val) if option.name == "snippet" => {
snippet_key = Some(val.clone());
}
CommandDataOptionValue::Attachment(att_id) if option.name == "attachment" => {
if let Some(att) = command.data.resolved.attachments.get(att_id) {
attachments.push(att.clone());
Expand All @@ -138,6 +159,19 @@ impl RegistrableCommand for ReplyCommand {
_ => {}
}
}

if let Some(key) = snippet_key {
match get_snippet_by_key(&key, db_pool).await? {
Some(snippet) => {
content = Some(snippet.content);
}
None => {
return Err(ModmailError::Command(CommandError::SnippetNotFound(
key.to_string(),
)));
}
}
}

let intent = extract_intent(content, &attachments).await;

Expand Down
19 changes: 18 additions & 1 deletion rustmail/src/commands/reply/text_command/reply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,24 @@ pub async fn reply(
.as_ref()
.ok_or_else(database_connection_failed)?;

let content = extract_reply_content(&msg.content, &config.command.prefix, &["reply", "r"]);
let mut content = extract_reply_content(&msg.content, &config.command.prefix, &["reply", "r"]);

if let Some(text) = &content {
if let Some(stripped) = text.strip_prefix("{{").and_then(|s| s.strip_suffix("}}")) {
let snippet_key = stripped.trim();
match get_snippet_by_key(snippet_key, db_pool).await? {
Some(snippet) => {
content = Some(snippet.content);
}
None => {
return Err(ModmailError::Command(CommandError::SnippetNotFound(
snippet_key.to_string(),
)));
}
}
}
}

let intent = extract_intent(content, &msg.attachments).await;

let Some(intent) = intent else {
Expand Down
5 changes: 5 additions & 0 deletions rustmail/src/commands/snippet/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pub mod slash_command;
pub mod text_command;

pub use slash_command::*;
pub use text_command::*;
3 changes: 3 additions & 0 deletions rustmail/src/commands/snippet/slash_command/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod snippet;

pub use snippet::*;
Loading