diff --git a/Cargo.lock b/Cargo.lock index 50c2877..fa88476 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1183,6 +1183,9 @@ name = "pudoxd" version = "0.1.0" dependencies = [ "axum", + "reqwest 0.12.28", + "serde", + "serde_json", "tokio", "tracing", "tracing-subscriber", diff --git a/bin/pudoxd/Cargo.toml b/bin/pudoxd/Cargo.toml index 21cbb72..8f4bf2f 100644 --- a/bin/pudoxd/Cargo.toml +++ b/bin/pudoxd/Cargo.toml @@ -12,3 +12,6 @@ axum = "0.7" tokio = { version = "1", features = ["full"] } tracing = "0.1.44" tracing-subscriber = "0.3.23" +reqwest = { version = "0.12", features = ["json"] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/bin/pudoxd/src/linear.rs b/bin/pudoxd/src/linear.rs new file mode 100644 index 0000000..6e26032 --- /dev/null +++ b/bin/pudoxd/src/linear.rs @@ -0,0 +1,48 @@ +use serde::Deserialize; + +#[derive(Clone)] +pub struct Linear { + client: reqwest::Client, + access_token: String, +} + +impl Linear { + pub fn new() -> Self { + let client = reqwest::Client::new(); + let access_token = std::env::var("PUDOX_DEVELOPER_TOKEN").unwrap(); + + Self { + client, + access_token, + } + } + + pub async fn send(&self, json: serde_json::Value) -> Result { + self.client + .post("https://api.linear.app/graphql") + .header("Authorization", format!("Bearer {}", self.access_token)) + .json(&json) + .send() + .await? + .json() + .await + } +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +#[allow(dead_code)] +pub struct WebhookPayload { + #[serde(rename = "type")] + pub event_type: String, + pub action: String, + #[serde(rename = "createdAt")] + pub created_at: String, + pub agent_session: AgentSession, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct AgentSession { + pub id: String, +} diff --git a/bin/pudoxd/src/main.rs b/bin/pudoxd/src/main.rs index 4fc4c05..a946be4 100644 --- a/bin/pudoxd/src/main.rs +++ b/bin/pudoxd/src/main.rs @@ -1,13 +1,20 @@ -use axum::{routing::post, Router}; +use axum::{extract::State, routing::post, Router}; use std::net::SocketAddr; +mod linear; +use crate::linear::{Linear, WebhookPayload}; + #[tokio::main] async fn main() -> Result<(), Box> { tracing_subscriber::fmt::init(); tracing::info!("Starting pudoxd..."); - let app = Router::new().route("/webhook", post(webhook_handler)); + let linear = Linear::new(); + + let app = Router::new() + .route("/webhook", post(webhook_handler)) + .with_state(linear); let addr = SocketAddr::from(([0, 0, 0, 0], 7790)); tracing::info!("Listening on {}", addr); @@ -17,7 +24,39 @@ async fn main() -> Result<(), Box> { Ok(()) } -async fn webhook_handler() -> &'static str { +async fn webhook_handler( + State(linear): State, + axum::Json(payload): axum::Json, +) -> &'static str { tracing::info!("Webhook received"); - "Webhook received" + + let res = linear + .send(serde_json::json!({ + "query": r#" + mutation AgentActivityCreate($input: AgentActivityCreateInput!) { + agentActivityCreate(input: $input) { + success + } + } + "#.to_string(), + "variables": { + "input": { + "agentSessionId": payload.agent_session.id, + "content": { + "type": "thought", + "body": "Received" + } + } + } + })) + .await; + + match res { + Ok(r) => { + tracing::info!("Mutation response: {:}", r); + } + Err(e) => tracing::error!("Failed to send mutation: {}", e), + } + + "Webhook processed" }