laravel-notification-channels/twitter
Send Twitter notifications from Laravel using this notification channel. Integrate tweets and direct messages into Laravel’s notification system via the Twitter API, with a simple channel driver for queued or on-demand notifications.
1. Install and wire up credentials. The package reads from config/services.php, not a dedicated config file:
composer require laravel-notification-channels/twitter:^8.3
// config/services.php
'twitter' => [
'consumer_key' => env('TWITTER_CONSUMER_KEY'),
'consumer_secret' => env('TWITTER_CONSUMER_SECRET'),
'access_token' => env('TWITTER_ACCESS_TOKEN'),
'access_secret' => env('TWITTER_ACCESS_SECRET'),
],
As of v8.3.0 the package no longer supports abraham/twitteroauth 7.x — that line only exists in the v9 branch. So the major you pick is dictated by your OAuth dependency, not by preference: ^8.3 for twitteroauth 6.x (drop-in for existing Laravel apps), ^9.0 if something else in your stack pins twitteroauth 7.x. Check what Composer resolves before writing any message code.
You need an X/Twitter developer app with Read and write permission and an access token generated after that permission was set (see Gotchas — this trips up almost everyone).
2. Write a notification. The channel is just another Laravel notification channel:
use Illuminate\Notifications\Notification;
use NotificationChannels\Twitter\TwitterChannel;
use NotificationChannels\Twitter\TwitterMessage;
class StatusTweet extends Notification
{
public function __construct(public string $text) {}
public function via($notifiable): array
{
return [TwitterChannel::class];
}
public function toTwitter($notifiable): TwitterMessage
{
return TwitterMessage::create($this->text);
}
}
3. Send it. Notification::route() works too, so you don't strictly need a Notifiable model — handy for one-off ops scripts.
Where to look first: open src/TwitterChannel.php and src/TwitterMessage.php before reading the README, then read the require block in composer.json to confirm which twitteroauth major the installed tag expects. The entire public surface is those two classes, one exception type, and a thin wrapper around abraham/twitteroauth. Reading ~150 lines tells you more than the docs, which occasionally lag the released tag.
Best first use case: one-way operational broadcasting to an account you own — deploy notices, incident status, "nightly build failed". This is the shape the package is built for: single account, low volume, write-only. It is not a social-media management tool.
Conditional channel routing. Gate on the presence of a route so notifications don't blow up for users who haven't linked an account:
public function via($notifiable): array
{
return $notifiable->routeNotificationFor('twitter')
? [TwitterChannel::class]
: [];
}
Or, in a fan-out notification, use $this->via() composition with Notification::route() for system events that have no user.
Always queue it. The channel makes a blocking HTTP call. Implement ShouldQueue on every notification that includes TwitterChannel, and handle failure explicitly:
public function failed(Throwable $e): void
{
Log::error('Tweet failed', ['message' => $e->getMessage()]);
}
Media attachments. TwitterMessage supports an image with alt text; because upload is a separate round-trip, the path must be resolvable at send time — queued jobs that reference a temp file will fail. Persist first, then pass Storage::path(...).
Direct messages. The message class exposes a DM factory; the recipient comes from routeNotificationForTwitter() on the notifiable, which must return the numeric user ID (not a @handle). Check TwitterMessage in your installed version for the exact factory name, as it has changed across majors.
Multi-account sending. There is one credential set in config, so a second account requires mutating config immediately before dispatch:
config(['services.twitter' => $credentialsFor($account)]);
$user->notify(new StatusTweet($text));
This is not concurrency-safe. If you genuinely need multiple accounts, resolve the channel from the container per-account ($this->app->make(TwitterChannel::class)) with a scoped config swap, or wrap the channel in a small TwitterClient interface with account-scoped instances. Do the wrapper anyway — it costs an hour and decouples you from the package.
Testing. Notification::fake() + assertSentTo() verifies routing and payload without touching the network. For the channel itself, swap TwitterChannel in the container for a spy (see Gotchas — Http::fake() will not intercept it).
Reading data is out of scope. Timelines, mentions, likes, follower counts — none of it is here. For anything beyond posting and DMs, inject Abraham\TwitterOAuth\TwitterOAuth directly or write your own client. Keep the package for outbound notifications only.
CouldNotSendNotification with the API's message, which is far more informative than the exception class. Log $e->getMessage() verbatim; you'll see "duplicate content", "rate limit exceeded", or "not authorized for this endpoint".strlen(). Truncate defensively rather than trusting a 280-char check.Http::fake() won't catch it. TwitterOAuth uses its own cURL transport. To test the channel in isolation, bind a fake TwitterChannel in the container, or bind a mocked TwitterOAuth instance; do not reach for the HTTP fake client.php artisan config:cache freezes services.php. Never call env() outside config files, and remember runtime config([...]) overrides for multi-account sending don't survive a config cache in the way you might expect within a single request lifecycle.routeNotificationForTwitter() must return the numeric ID. Resolving a handle to an ID is your job, and it's an extra API call you'll want to cache.abraham/twitteroauth 7.x is only available on the package's v9 line; v8.3.0 explicitly dropped it. If a composer update bumps twitteroauth and you're on ^8.3, expect a conflict rather than a resolve — and if you force it, expect method-signature breakage in the transport layer. Run composer show laravel-notification-channels/twitter abraham/twitteroauth and pin both. Snippets from blog posts targeting older majors will silently mislead you; read the README for that tag."^8.3" or "^9.0", never *), keep all TwitterMessage construction inside one class or two, and keep the swap to a hand-rolled TwitterOAuth call within reach — it's about 20 lines for a plain tweet.RateLimited job middleware on queued notifications rather than sprinkling sleeps; retries with exponential backoff will otherwise burn your quota on the same failing message.How can I help you explore Laravel packages today?