feat: Add webhook endpoints for editing and deleting messages (#682)

* feat: ErrorType.CannotDeleteMessage, needed later

Signed-off-by: Taureon <taureon@noreply.codeberg.org>

* feat: webhook edit/delete message endpoints

Signed-off-by: Taureon <taureon@noreply.codeberg.org>

* lol, lmao even

Signed-off-by: Taureon <taureon@noreply.codeberg.org>

* fix contradictory comment

Signed-off-by: Taureon <taureon@noreply.codeberg.org>

---------

Signed-off-by: Taureon <taureon@noreply.codeberg.org>
Co-authored-by: Taureon <taureon@noreply.codeberg.org>
This commit is contained in:
Taureon
2026-05-09 00:37:00 +02:00
committed by GitHub
parent 23ad135983
commit 6f3441cf4a
6 changed files with 121 additions and 3 deletions

View File

@@ -24,6 +24,7 @@ impl IntoResponse for Error {
ErrorType::UnknownChannel => StatusCode::NOT_FOUND,
ErrorType::UnknownMessage => StatusCode::NOT_FOUND,
ErrorType::UnknownAttachment => StatusCode::BAD_REQUEST,
ErrorType::CannotDeleteMessage => StatusCode::FORBIDDEN,
ErrorType::CannotEditMessage => StatusCode::FORBIDDEN,
ErrorType::CannotJoinCall => StatusCode::BAD_REQUEST,
ErrorType::TooManyAttachments { .. } => StatusCode::BAD_REQUEST,

View File

@@ -78,6 +78,7 @@ pub enum ErrorType {
UnknownChannel,
UnknownAttachment,
UnknownMessage,
CannotDeleteMessage,
CannotEditMessage,
CannotJoinCall,
TooManyAttachments {

View File

@@ -30,6 +30,7 @@ impl<'r> Responder<'r, 'static> for Error {
ErrorType::UnknownChannel => Status::NotFound,
ErrorType::UnknownMessage => Status::NotFound,
ErrorType::UnknownAttachment => Status::BadRequest,
ErrorType::CannotDeleteMessage => Status::Forbidden,
ErrorType::CannotEditMessage => Status::Forbidden,
ErrorType::CannotJoinCall => Status::BadRequest,
ErrorType::TooManyAttachments { .. } => Status::BadRequest,

View File

@@ -1,19 +1,23 @@
use rocket::Route;
use revolt_rocket_okapi::revolt_okapi::openapi3::OpenApi;
use rocket::Route;
mod webhook_delete;
mod webhook_delete_message;
mod webhook_delete_token;
mod webhook_edit;
mod webhook_edit_message;
mod webhook_edit_token;
mod webhook_execute;
mod webhook_fetch_token;
mod webhook_fetch;
mod webhook_execute_github;
mod webhook_fetch;
mod webhook_fetch_token;
pub fn routes() -> (Vec<Route>, OpenApi) {
openapi_get_routes_spec![
webhook_delete_message::webhook_delete_message,
webhook_delete_token::webhook_delete_token,
webhook_delete::webhook_delete,
webhook_edit_message::webhook_edit_message,
webhook_edit_token::webhook_edit_token,
webhook_edit::webhook_edit,
webhook_execute_github::webhook_execute_github,

View File

@@ -0,0 +1,27 @@
use revolt_database::{util::reference::Reference, Database};
use revolt_result::{create_error, Result};
use rocket::State;
use rocket_empty::EmptyResponse;
/// # Deletes a webhook message
///
/// Deletes a message sent by a webhook
#[openapi(tag = "Webhooks")]
#[delete("/<webhook_id>/<token>/<message_id>")]
pub async fn webhook_delete_message(
db: &State<Database>,
webhook_id: Reference<'_>,
token: String,
message_id: Reference<'_>,
) -> Result<EmptyResponse> {
let webhook = webhook_id.as_webhook(db).await?;
webhook.assert_token(&token)?;
let message = message_id.as_message(db).await?;
if message.author != webhook.id {
return Err(create_error!(CannotDeleteMessage));
}
message.delete(db).await.map(|_| EmptyResponse)
}

View File

@@ -0,0 +1,84 @@
use iso8601_timestamp::Timestamp;
use revolt_config::config;
use revolt_database::{
tasks::process_embeds::queue, util::reference::Reference, Database, Message, PartialMessage,
};
use revolt_models::v0::{self, DataEditMessage, Embed};
use revolt_models::validator::Validate;
use revolt_result::{create_error, Result};
use rocket::{serde::json::Json, State};
/// # Edits a webhook message
///
/// Edits a message sent by a webhook
#[openapi(tag = "Webhooks")]
#[patch("/<webhook_id>/<token>/<message_id>", data = "<data>")]
pub async fn webhook_edit_message(
db: &State<Database>,
webhook_id: Reference<'_>,
token: String,
message_id: Reference<'_>,
data: Json<DataEditMessage>,
) -> Result<Json<v0::Message>> {
let edit = data.into_inner();
edit.validate().map_err(|error| {
create_error!(FailedValidation {
error: error.to_string()
})
})?;
Message::validate_sum(
&edit.content,
edit.embeds.as_deref().unwrap_or_default(),
config().await.features.limits.default.message_length,
)?;
let webhook = webhook_id.as_webhook(db).await?;
webhook.assert_token(&token)?;
let mut message = message_id.as_message(db).await?;
if message.author != webhook.id {
return Err(create_error!(CannotEditMessage));
}
message.edited = Some(Timestamp::now_utc());
let mut partial = PartialMessage {
edited: message.edited,
..Default::default()
};
// 1. Handle content update
if let Some(content) = &edit.content {
partial.content = Some(content.clone());
}
// 2. Clear any auto generated embeds
let mut new_embeds = vec![];
if let Some(embeds) = &message.embeds {
for embed in embeds {
if let Embed::Text(embed) = embed {
new_embeds.push(Embed::Text(embed.clone()))
}
}
}
// 3. Replace if we are given new embeds
if let Some(embeds) = edit.embeds {
new_embeds.clear();
for embed in embeds {
new_embeds.push(message.create_embed(db, embed).await?);
}
}
partial.embeds = Some(new_embeds);
message.update(db, partial, vec![]).await?;
// Queue up a task for processing embeds
if let Some(content) = edit.content {
queue(message.channel.to_string(), message.id.to_string(), content).await;
}
Ok(Json(message.into_model(None, None)))
}