An open API service indexing awesome lists of open source software.

https://github.com/skaisser/howl

Multi-driver Laravel notifier with Discord-first rich embeds, mentions, and bot-parseable metadata.
https://github.com/skaisser/howl

discord embeds events laravel logging notifications observability webhook

Last synced: 10 days ago
JSON representation

Multi-driver Laravel notifier with Discord-first rich embeds, mentions, and bot-parseable metadata.

Awesome Lists containing this project

README

          

Howl โ€” Multi-driver Laravel notifier

**Multi-driver Laravel notifier โ€” Discord, Slack, and Telegram with rich embeds.**

*When something goes wrong, your app should howl into the night.*


Latest Version
Total Downloads
License


Tests
487 Tests Passing
100% Coverage


PHP 8.3 | 8.4
Laravel 12 | 13
Pest 3 | 4


Discord
Slack
Telegram

**[๐Ÿ“– Full documentation at howl.skaisser.dev โ†’](https://howl.skaisser.dev)**

---

## โœจ Why Howl?

A single driver-agnostic API for Discord, Slack, and Telegram. Drop it into any Laravel 12 or 13 app and start delivering rich, structured notifications in minutes.

- ๐ŸŽฏ **One fluent API for all three drivers** โ€” switch per-call without touching business logic
- ๐ŸŽจ **Rich, native formatting per platform** โ€” Discord embeds, Slack Block Kit, Telegram HTML โ€” with mentions, fields, code blocks, buttons, attachments
- ๐Ÿ›ฐ๏ธ **Channel failover & fan-out** โ€” automatic backup channel dispatch on failure
- ๐Ÿ“ฆ **Seven built-in event templates** โ€” exceptions, deployments, audits, cron heartbeats, job failures, manual ops, generic info
- ๐Ÿงช **HowlFake test helper** โ€” assert notifications without real HTTP calls; per-driver assertions
- โšก **Queue-aware** with exponential backoff and opt-in Redis rate limiting
- โœ… **100% line coverage** across 487 tests, enforced by `pest --coverage --min=100`
- ๐Ÿ“š **Versioned docs** at `howl.skaisser.dev` plus machine-readable `llms.txt` for AI agents

---

## ๐Ÿ› ๏ธ Compatibility

| PHP | Laravel | Pest | PHPUnit | Testbench | Status |
|:---:|:-------:|:----:|:-------:|:---------:|:------:|
| 8.3 | 12.x | 3.x | 11.x | 10.x | โœ… |
| 8.3 | 13.x | 4.x | 12.x | 11.x | โœ… |
| 8.4 | 12.x | 3.x | 11.x | 10.x | โœ… |
| 8.4 | 13.x | 4.x | 12.x | 11.x | โœ… |

Composer constraints support all four combinations. CI validates the latest combo (PHP 8.4 ร— Laravel 13) on every push and PR targeting `main`; the other rows are validated locally before each release.

---

## ๐Ÿ“ฆ Installation

```bash
composer require skaisser/howl
php artisan vendor:publish --tag=howl-config
```

Add your driver credentials to `.env`:

```env
HOWL_DRIVER=discord # discord | slack | telegram
HOWL_DEFAULT_CHANNEL=errors # primary channel name

# Discord
HOWL_DISCORD_DEFAULT=https://discord.com/api/webhooks/...

# Slack (optional โ€” only if you use the slack driver)
HOWL_SLACK_BOT_TOKEN=xoxb-...
HOWL_SLACK_DEFAULT_CHANNEL=C0XXXXXXX

# Telegram (optional โ€” only if you use the telegram driver)
HOWL_TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
HOWL_TELEGRAM_CHAT_ID=-1001234567890
```

---

## ๐Ÿš€ Quick Start

```php
use Skaisser\Howl\Facades\Howl;
use Skaisser\Howl\Events\GenericExceptionEvent;

// Direct severity verbs โ€” use config('howl.driver') by default
Howl::error(new GenericExceptionEvent($exception));
Howl::info('Scheduled job completed');
Howl::audit($auditEvent);

// Channel routing โ€” per-call override beats event default beats config
Howl::on('audits')->audit($event);

// Per-call driver override โ€” same payload, different destination
Howl::driver('slack')->info('Deploy succeeded');
Howl::driver('telegram')->error('Database connection lost');

// Chainable: pick driver + channel + severity in one go
Howl::driver('slack')->channel('deployments')->success('v1.2.0 shipped');
```

### ๐Ÿ“จ Built-in event templates

```php
use Skaisser\Howl\Events\{
GenericExceptionEvent,
DeploymentEvent,
AuditEvent,
CronHeartbeatEvent,
JobRetryExhaustedEvent,
ManualOperationEvent,
GenericInfoEvent,
};

Howl::error(new GenericExceptionEvent($e));
Howl::deployment(new DeploymentEvent(version: 'v1.2.0', env: 'production', commit: 'abc1234'));
Howl::audit(new AuditEvent(actor: $user->email, action: 'role.changed', target: $role));
```

---

## ๐Ÿ›ฐ๏ธ Channel Failover & Fan-Out

Configure a backup channel and pick the mode:

```php
// config/howl.php
'channel' => 'errors',
'channel_backup' => 'errors-backup',
'channel_mode' => 'failover', // try primary; on failure, backup once
// or
'channel_mode' => 'fan_out', // dispatch to BOTH channels in parallel
```

- **`failover`** (default): primary first, backup only on failure. `true` on first success, `false` if both fail.
- **`fan_out`**: dispatch to primary AND backup sequentially. `true` if at least one succeeds. **Doubles rate-limit consumption** โ€” size your `RateLimiter::for()` quota accordingly.

---

## ๐Ÿงช Testing with HowlFake

```php
use Skaisser\Howl\Facades\Howl;

$fake = Howl::fake();

Howl::error('Something broke');
Howl::driver('slack')->info('Deploy started');

// Global assertions
$fake->assertSent(fn ($p) => $p->severity === 'error');
$fake->assertNothingSent(); // negation form
expect($fake->sent())->toHaveCount(2); // count via the sent() accessor

// Per-driver assertions (v1.0+)
$fake->assertSentVia('discord', fn ($p) => $p->severity === 'error');
$fake->assertSentVia('slack', fn ($p) => $p->severity === 'info');
$fake->assertSentViaNothing('telegram');
```

No real HTTP calls. No mocks of HTTP clients. Drop-in replacement.

---

## โšก Queue Mode + Rate Limiting

```env
HOWL_QUEUE=true
HOWL_QUEUE_CONNECTION=redis
HOWL_QUEUE_NAME=default
HOWL_RATE_LIMITER_KEY=howl-discord # opt-in Redis rate limiter
```

```php
// AppServiceProvider::boot()
use Illuminate\Cache\RateLimiter;
use Illuminate\Cache\RateLimiting\Limit;

RateLimiter::for('howl-discord', fn () => Limit::perMinute(28));
```

`SendHowlJob` ships with 3 retries + exponential backoff. Queue-failure events always force sync to avoid recursive loops.

---

## ๐Ÿ“– Documentation

The full docs site at **[howl.skaisser.dev](https://howl.skaisser.dev)** covers everything in depth:

- ๐Ÿšฆ [Installation & Quick Start](https://howl.skaisser.dev/v1.0.0/guide/installation)
- ๐Ÿค– Drivers: [Discord](https://howl.skaisser.dev/v1.0.0/drivers/discord) ยท [Slack](https://howl.skaisser.dev/v1.0.0/drivers/slack) ยท [Telegram](https://howl.skaisser.dev/v1.0.0/drivers/telegram)
- ๐ŸŽ›๏ธ [Configuration reference](https://howl.skaisser.dev/v1.0.0/configuration/reference)
- ๐Ÿงช [HowlFake testing guide](https://howl.skaisser.dev/v1.0.0/testing/howl-fake)
- ๐Ÿ“œ [API reference](https://howl.skaisser.dev/v1.0.0/reference/api)
- โฌ†๏ธ [Upgrade guide v0.x โ†’ v1.0](https://howl.skaisser.dev/v1.0.0/upgrade)
- ๐Ÿ“ [Release notes](https://howl.skaisser.dev/v1.0.0/releases)

**For AI agents:** [llms.txt](https://howl.skaisser.dev/llms.txt) (index) ยท [llms-full.txt](https://howl.skaisser.dev/llms-full.txt) (inline)

---

## ๐Ÿค Contributing

Issues and pull requests welcome at [github.com/skaisser/howl](https://github.com/skaisser/howl).

Before opening a PR, run the full suite locally:

```bash
composer install
vendor/bin/pest --parallel
vendor/bin/pest --coverage --min=100 # enforces 100% line coverage
vendor/bin/pint # code style
```

---

## ๐Ÿ“œ License

MIT โ€” see [LICENSE](LICENSE). Copyright ยฉ Shirleyson Kaisser.
// worktree test commit Wed May 13 03:12:54 -03 2026