Go
El paquete honk es un cliente compatible con context, hecho solo con la biblioteca estándar y seguro para uso concurrente. El mismo módulo incluye la CLI honk-me para scripts, cron y CI.
- Paquete
-
github.com/honk-me/honk-goPublicado - Código fuente
- github.com/honk-me/honk-go · Licencia MIT
- Requisitos
- Go 1.22 o posterior.
Instalación
La CLI también se publica como binarios precompilados en cada release de GitHub.
go get github.com/honk-me/honk-go # library (Go 1.22+)
go install github.com/honk-me/honk-go/cmd/honk-me@latest # CLI 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(honk.Options{URL: "https://honk-me.app", Key: key}). Un error nil significa que Honk guardó el mensaje, no que se haya entregado la notificación.
import honk "github.com/honk-me/honk-go" // package honk
c, err := honk.FromEnv() // HONK_URL, HONK_KEY (+ optional HONK_SOURCE, HONK_ENVIRONMENT, HONK_CHANNEL)
if err != nil {
log.Fatal(err)
}
_, err = c.Beep(ctx, "Backup finished", "nightly pg_dump took 42 s") Receta: una solicitud de cliente en tu teléfono
Guarda primero la solicitud y envía el aviso sin bloquear la respuesta. Un grupo y una clave de idempotencia por solicitud.
var notifier, _ = honk.FromEnv() // create once, reuse (keep-alive)
func onCustomerRequest(r CustomerRequest) {
// ...save the request first, then notify off the request path:
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, err := notifier.Send(ctx, honk.Message{
Title: truncate("New request: "+r.Subject, 150),
Message: truncate(fmt.Sprintf("%s (%s) asked: %s", r.Name, r.Company, r.Body), 2000),
Priority: honk.PriorityHigh, // push right away
Category: honk.CategoryCustomers,
Channel: "requests",
GroupKey: fmt.Sprintf("requests/%d", r.ID), // one group per request
URL: fmt.Sprintf("https://shop.example.com/admin/requests/%d", r.ID),
Metadata: map[string]any{"request_id": strconv.Itoa(r.ID)},
}, honk.WithIdempotencyKey(fmt.Sprintf("request-%d", r.ID)))
if err != nil {
log.Printf("honk: %v", err) // never fail the customer's request
}
}()
}
func truncate(s string, n int) string { // by runes, never splits UTF-8
if r := []rune(s); len(r) > n {
return string(r[:n-1]) + "…"
}
return s
} La escala Honk e incidentes
Los helpers aceptan opciones como WithGroupKey, WithChannel y WithIdempotencyKey.
c.Loud(ctx, "Disk 91%", "/var on app-01", honk.WithGroupKey("disk/app-01/var"))
c.Light(ctx, "Deploy started", "v4.2.0", honk.WithChannel("deploys"))
c.Problem(ctx, "db/backup", "Backup failed", "pg_dump exited with 1") // a long honk by default
c.Recovery(ctx, "db/backup", "Backup OK", "pg_dump finished in 41 s") // a beep by default Errores
Los errores son *honk.Error y se comparan con centinelas mediante errors.Is. Retryable() indica si conviene reintentar con la misma clave.
_, err := c.Long(ctx, "Payment failed", "Stripe declined order 1042", honk.WithGroupKey("payments/stripe"))
var he *honk.Error
switch {
case err == nil:
case errors.Is(err, honk.ErrValidation):
log.Printf("bug: %v", err) // he.Fields says what to fix
case errors.As(err, &he) && he.Retryable():
requeue(he.IdempotencyKey, he.RetryAfter)
default:
log.Print(err)
} 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.