diff --git a/crates/delta/src/routes/webhooks/mod.rs b/crates/delta/src/routes/webhooks/mod.rs index c1013c42..4b730326 100644 --- a/crates/delta/src/routes/webhooks/mod.rs +++ b/crates/delta/src/routes/webhooks/mod.rs @@ -5,6 +5,7 @@ mod webhook_delete; mod webhook_edit; mod webhook_execute; mod webhook_fetch; +mod webhook_execute_github; pub fn routes() -> (Vec, OpenApi) { openapi_get_routes_spec![ @@ -12,5 +13,6 @@ pub fn routes() -> (Vec, OpenApi) { webhook_edit::req, webhook_execute::req, webhook_fetch::req, + webhook_execute_github::req ] } diff --git a/crates/delta/src/routes/webhooks/webhook_execute_github.rs b/crates/delta/src/routes/webhooks/webhook_execute_github.rs new file mode 100644 index 00000000..4ed393de --- /dev/null +++ b/crates/delta/src/routes/webhooks/webhook_execute_github.rs @@ -0,0 +1,75 @@ +use revolt_quark::{Db, Ref, Result, Error, models::{Webhook, Message, message::SendableEmbed}, types::push::MessageAuthor}; +use rocket::{serde::{json::Json, DeserializeOwned}, Request, request::FromRequest, http::Status}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use ulid::Ulid; + + +#[derive(Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Event { + Star { + action: String, + sender: Value + } +} + +struct EventHeader<'r>(pub &'r str); + +#[async_trait] +impl<'r> FromRequest<'r> for EventHeader<'r> { + type Error = Error; + + async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome { + let headers = request.headers(); + let Some(event) = headers.get_one("X-GitHub-Event") else { + return rocket::request::Outcome::Failure((Status::BadRequest, Error::InvalidOperation)) + }; + + rocket::request::Outcome::Success(Self(event)) + } +} + +/// # executes a webhook specific to github +/// +/// executes a webhook specific to github and sends a message containg the relavent info about the event +#[openapi(tag = "Webhooks")] +#[post("///github", data="")] +pub async fn req(db: &Db, target: Ref, token: String, event: EventHeader<'_>, data: String) -> Result<()> { + let webhook = target.as_webhook(db).await?; + + (webhook.token == token) + .then_some(()) + .ok_or(Error::InvalidCredentials)?; + + let channel = db.fetch_channel(&webhook.channel).await?; + + let body = format!(r#"{{"{}": {data}}}"#r, event.0); + + let Ok(event) = serde_json::from_str::(&body) else { + return Err(Error::InvalidOperation) + }; + + let sendable_embed = match event { + Event::Star { action, sender } => SendableEmbed { + title: Some(format!("{action} star")), + ..Default::default() + }, + }; + + let message_id = Ulid::new().to_string(); + + let embed = sendable_embed.into_embed(db, message_id.clone()).await?; + + let message = Message { + id: message_id, + channel: webhook.channel, + embeds: Some(vec![embed]), + webhook: Some(webhook.id.clone()), + ..Default::default() + }; + + message.create(db, &channel, Some(MessageAuthor::Webhook(&webhook))).await; + + Ok(()) +} diff --git a/crates/quark/src/models/channels/message.rs b/crates/quark/src/models/channels/message.rs index cc90e99f..0b3b4adf 100644 --- a/crates/quark/src/models/channels/message.rs +++ b/crates/quark/src/models/channels/message.rs @@ -30,7 +30,7 @@ pub struct Reply { } /// Representation of a text embed before it is sent. -#[derive(Validate, Serialize, Deserialize, JsonSchema, Clone, Debug)] +#[derive(Validate, Serialize, Deserialize, JsonSchema, Clone, Debug, Default)] pub struct SendableEmbed { #[validate(length(min = 1, max = 128))] pub icon_url: Option,