Notificaciones push desde Rust
El crate honk-honk es un cliente asíncrono sobre Tokio y reqwest con rustls, así que no necesita OpenSSL. La feature blocking añade un cliente síncrono para scripts y CLI.
- Paquete
-
honk-honkPublicado - Código fuente
- github.com/honk-hq/rust · Licencia MIT
- Requisitos
- Rust 1.85 o posterior.
Instalación
Para código síncrono: cargo add honk-honk --features blocking.
cargo add honk-honk
cargo add tokio --features macros,rt-multi-thread # if you don't have a runtime yet Quien tenga una clave de ingesta (honk_…) puede enviar mensajes a su proyecto. Guárdala en servidores, tareas y secretos de CI, nunca en un navegador ni en una app móvil o de escritorio.
Enviar un evento
O de forma explícita: Honk::new("https://honk-honk.app", key)?. Clonar Honk es barato: crea uno y compártelo, para que las conexiones sigan abiertas.
use honk::Honk;
#[tokio::main]
async fn main() -> honk::Result<()> {
let honk = Honk::from_env()?; // HONK_URL, HONK_KEY (+ HONK_SOURCE, HONK_ENVIRONMENT, HONK_CHANNEL)
honk.beep("Backup finished", "nightly pg_dump took 42 s").await?;
Ok(())
} Receta: una solicitud de cliente en tu teléfono
Un grupo por solicitud y tu propia clave de idempotencia, para que un webhook reintentado nunca suene dos veces.
use honk::{Category, Message, Priority, Severity};
let msg = Message::new(format!("{} asked for a quote: {}", req.name, req.summary))
.title("New quote request")
.severity(Severity::LOUD)
.priority(Priority::High)
.category(Category::Customers)
.group_key(format!("requests/{}", req.id))
.url(format!("https://shop.example.com/admin/requests/{}", req.id)); // https only, "Open link"
honk.honk(&msg, format!("request-{}", req.id).as_str()).await?; La escala Honk y las incidencias
Cada helper devuelve un PendingSend: encadena más campos y después haz .await.
use honk::Priority;
honk.loud("Disk 91% full", "db-1 /var is at 91%")
.group_key("disk/db-1/var")
.priority(Priority::High)
.metadata("used_percent", 91)
.await?;
honk.uhoh("db/backup", "Backup failed", "pg_dump exited with 1").await?; // a long honk by default
honk.phew("db/backup", "Backup is back", "pg_dump finished in 41 s. Nobody needs to know.").await?; // a beep by default El cliente bloqueante
Ejecuta el cliente asíncrono en un runtime privado de un solo hilo, con los mismos reintentos. No lo llames desde dentro de un runtime asíncrono.
// Cargo.toml: honk-honk = { version = "0.4", features = ["blocking"] }
use honk::blocking::Honk;
let honk = Honk::from_env()?;
honk.beep("Backup finished", "nightly pg_dump took 42 s").send()?;
honk.honk(&honk::Message::new("Imported 1 204 rows"), None)?; Errores
Cada variante de Error lleva un Failure con el estado, el código, cada campo no válido y retry_after.
use honk::Error;
match honk.honk(&msg, "order-1042-failed").await {
Ok(_) => {}
Err(Error::Validation(f)) => tracing::error!(?f.fields, "a bug: don't retry"),
Err(e) if e.is_retryable() => queue.retry_later(e.idempotency_key(), e.retry_after()),
Err(e) => return Err(e.into()),
} Reintentos que nunca envían dos veces
- Cada envío lleva una
Idempotency-Key: la tuya o un UUIDv7 nuevo. La misma clave se reutiliza en cada reintento y, durante 24 horas, Honk responde a una repetición con el id original yduplicate: true, así que una respuesta perdida nunca crea un segundo mensaje. - Solo se reintentan los errores de red, los tiempos de espera agotados,
429y5xx, con backoff exponencial y jitter completo, y nunca antes delRetry-Afterdel servidor. Las demás respuestas4xxno se reintentan nunca: hay que corregir la solicitud. - Cada intento se corta a los 5 segundos y todo se detiene a los 30. Una espera que superaría ese plazo, como una cuota diaria que se reinicia a medianoche, falla al momento y te dice cuándo reintentar.
- Los campos se validan antes de enviar, y todos los errores de validación se devuelven juntos.