Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Notify Laravel Package

guanguans/notify

Send notifications through dozens of channels—DingTalk, WeChat Work, Feishu, Bark, Slack, Telegram, and more—via one unified, framework-agnostic PHP API. Clean, extensible SDK with Laravel support for fast, consistent integration.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and verify the runtime first. The package targets modern PHP (8.1+) and pulls in a PSR-18 HTTP client (Guzzle by default). Before writing any notification code, confirm your app's PHP version and that a PSR-18 client + PSR-17 factories are resolvable in the container — most "it silently does nothing" reports trace back to a missing/duplicate HTTP client binding, not to the notifier itself.

composer require guanguans/notify

Pick one channel and one credential set. Don't wire up five channels on day one. Choose the one your team already uses (DingTalk, WeCom, Telegram Bot, Bark, Slack, etc.), generate the token/webhook, and put it in .env — never in code.

First send (webhook-style channel):

use Guanguans\Notify\Factory;

$notifier = Factory::dingTalk([
    'token'  => env('DINGTALK_TOKEN'),
    'secret' => env('DINGTALK_SECRET'), // only if your bot uses signed webhooks
]);

$response = $notifier->send([
    'msgtype' => 'text',
    'text'    => ['content' => 'Deploy finished: '.config('app.env')],
]);

Where to look first, in this order:

  1. src/Factory.php — the authoritative list of channel names and the config keys each one accepts. This is your map; the README's channel table is derived from it.
  2. src/{Channel}/ for the channel you picked — look at the Notifier class for the endpoint, required config, and signing logic, and at src/Messages/{Channel}/ for the exact payload shape the API expects.
  3. The README section for that channel — it usually shows a working payload, but treat the source as ground truth when they disagree (channel APIs drift faster than docs).
  4. src/Exceptions/ — know what gets thrown before you write your first try/catch.

Typical first real use case: a deploy/failure alert from a queued job or an Artisan command. That's the lowest-risk integration — no user-facing path, easy to roll back, and it immediately tells you whether credentials, signing, and network egress are all correct.

Implementation Patterns

Ad-hoc sends from commands and jobs. The Factory::channel($config)->send($payload) shape is the workhorse. Keep the config array in config/services.php (or a dedicated config/notify.php) rather than inline, so each channel has one construction site:

// config/notify.php
'channels' => [
    'ops' => [
        'driver' => 'weWork',
        'token'  => env('WEWORK_WEBHOOK'),
    ],
],

Then wrap construction in a small service so you can swap the channel without touching call sites.

Use message objects for anything non-trivial. Raw arrays are a passthrough to the remote API, so a typo in a nested key surfaces as an opaque 400 from the provider. The bundled message classes (src/Messages/{Channel}/…) give you named constructors and per-channel validation, and they're far easier to unit test:

$message = new TextMessage('Build #1234 failed');
$notifier->send($message);

Rule of thumb: arrays for one-off scripts, message objects for anything that lives in app/.

Laravel notification channel for user-facing notifications. If the package's Laravel channel is installed, implement toNotify() on the notification class and return the configured notifier/message. This is the pattern to prefer when the notification is about a user ($notifiable), because you inherit ShouldQueue, via() routing, and per-user channel preferences for free:

public function via($notifiable): array
{
    return [NotifyChannel::class];
}

public function toNotify($notifiable)
{
    return Factory::telegram([
        'token'   => config('notify.channels.telegram.token'),
        'chat_id' => $notifiable->telegram_chat_id,
    ]);
}

public function toNotifyMessage($notifiable)
{
    return new TextMessage("Order #{$this->order->id} shipped");
}

Keep the notifier construction and the message construction separate — that's what makes a notification reusable across channels later.

Fan-out with a fallback. For operational alerts, don't hard-code one provider. Build a tiny composite that iterates channels and swallows per-channel failures after logging, so a broken DingTalk webhook doesn't take down the alerting path:

foreach (config('notify.alert_channels') as $name => $config) {
    try {
        Factory::{$config['driver']}($config)->send($message);
    } catch (Throwable $e) {
        report($e); // try the next channel
    }
}

Test with a fake HTTP client, not by hitting the provider. Bind a fake PSR-18 client in the container (or use Http::fake() if you're on Illuminate and the package resolves through it) and assert on the outgoing request body. This is the only way to cheaply cover the per-channel payload shape, and it keeps CI from burning rate limits.

Centralize credentials and recipient IDs. Webhook tokens and chat IDs are the two things that change between staging and production. Put both in config; never branch on app()->environment() inside a notifier call.

Gotchas and Tips

  • The channel list is wide, but maintenance depth is uneven. With ~35 integrations, the popular channels (DingTalk, WeCom, Telegram, Slack, Bark, ServerChan) get the most attention. Before committing to an exotic one, open its Notifier and check that it still matches the provider's current endpoint and auth scheme — and smoke-test it against a real endpoint, because CI can't.
  • Array payloads have essentially no validation. send(['text' => ...]) will happily dispatch a malformed body and you'll debug a provider-side error message. If a channel is business-critical, use its message class or write a thin DTO of your own.
  • Signing/secrets differ per channel and are the #1 silent failure. DingTalk uses timestamp + HMAC sign, WeCom and Lark have their own schemes, some Bark setups need a device key in the path. If your notifier returns 200 with an error body, suspect the signature before the payload.
  • Provider APIs change without versioning. Pin the package in composer.json (e.g. ^2.0) and treat upgrades as code changes, not routine. Given the release cadence, composer update on a Friday is how alerting quietly breaks.
  • Timeouts and proxies are inherited from the HTTP client, not the notifier. Set a short timeout (2–5s) for fire-and-forget alerts — a hung webhook should never block a request or a queue worker. If you're behind a corporate proxy, configure it on the client, not per-channel.
  • Wrap sends in try/catch(Throwable) for non-critical paths. Exceptions from a notification should almost never fail the business operation that triggered it. For critical paths, the inverse: let it throw and retry via the queue.
  • Rate limits and message size caps are enforced remotely. Telegram caps message length, DingTalk/WeCom throttle per-minute, Slack rate-limits per method. Batch digests instead of looping one send per row.
  • Secrets leak easily into logs. The notifiers commonly accept tokens as config, and debug logging of the config array will expose them. Scrub token/secret/webhook keys from any logging or exception reporting you add.
  • Extension point for a missing or broken channel: don't fork the package. Subclass the relevant Notifier, override the endpoint/payload method, and register it via your own factory or container binding — then upstream the fix if it's generally useful.
  • Verify the Laravel integration against your framework version. The notification-channel glue is the part most sensitive to Laravel majors; if via()/toNotify() don't line up, fall back to calling the notifier directly from a queued listener and skip the channel abstraction.
  • Don't put this on a hot path (per-request, per-row). It's an HTTP SDK: every send() is a blocking network call unless you queue it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource