skaisser/howl
最新稳定版本:v1.0.1
Composer 安装命令:
composer require skaisser/howl
包简介
Multi-driver Laravel notifier with Discord-first rich embeds, mentions, and bot-parseable metadata.
README 文档
README
Multi-driver Laravel notifier — Discord, Slack, and Telegram with rich embeds.
When something goes wrong, your app should howl into the night.
📖 Full documentation at 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.devplus machine-readablellms.txtfor 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
composer require skaisser/howl php artisan vendor:publish --tag=howl-config
Add your driver credentials to .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
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
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:
// 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.trueon first success,falseif both fail.fan_out: dispatch to primary AND backup sequentially.trueif at least one succeeds. Doubles rate-limit consumption — size yourRateLimiter::for()quota accordingly.
🧪 Testing with HowlFake
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
HOWL_QUEUE=true HOWL_QUEUE_CONNECTION=redis HOWL_QUEUE_NAME=default HOWL_RATE_LIMITER_KEY=howl-discord # opt-in Redis rate limiter
// 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 covers everything in depth:
- 🚦 Installation & Quick Start
- 🤖 Drivers: Discord · Slack · Telegram
- 🎛️ Configuration reference
- 🧪 HowlFake testing guide
- 📜 API reference
- ⬆️ Upgrade guide v0.x → v1.0
- 📝 Release notes
For AI agents: llms.txt (index) · llms-full.txt (inline)
🤝 Contributing
Issues and pull requests welcome at github.com/skaisser/howl.
Before opening a PR, run the full suite locally:
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. Copyright © Shirleyson Kaisser.
统计信息
- 总下载量: 15
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 3
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-05-11