use anyhow::{Context, Result};
use serde_json::json;
const BLOCK_CHAR_LIMIT: usize = 2900;
pub async fn send_message(webhook_url: &str, title: &str, body: &str) -> Result<()> {
let client = reqwest::Client::new();
let text = md_to_mrkdwn(body);
let mut blocks = vec![json!({
"type": "header",
"text": { "type": "plain_text", "text": title, "emoji": false }
})];
let mut remaining = text.as_str();
while !remaining.is_empty() {
let cut = char_boundary_at(remaining, BLOCK_CHAR_LIMIT);
blocks.push(json!({
"type": "section",
"text": { "type": "mrkdwn", "text": &remaining[..cut] }
}));
remaining = &remaining[cut..];
}
let payload = json!({ "blocks": blocks });
let resp = client
.post(webhook_url)
.json(&payload)
.send()
.await
.context("Erro conectando ao Slack")?;
if !resp.status().is_success() {
let status = resp.status();
let body = resp.text().await.unwrap_or_default();
anyhow::bail!("Slack webhook erro {status}: {body}");
}
Ok(())
}
fn md_to_mrkdwn(text: &str) -> String {
text.replace("**", "*")
}
fn char_boundary_at(s: &str, max_bytes: usize) -> usize {
if s.len() <= max_bytes {
return s.len();
}
let mut idx = max_bytes;
while !s.is_char_boundary(idx) {
idx -= 1;
}
idx
}