Weave Code
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

Zulip Notifier Laravel Package

symfony/zulip-notifier

Symfony Notifier integration for Zulip. Configure via a zulip:// DSN using your Zulip email, token, host, and default channel, then send notifications to Zulip streams through Symfony’s notifier system.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the Package Add the package to your composer.json:

    composer require symfony/zulip-notifier
    

    For Laravel, ensure compatibility by using a Symfony-compatible HTTP client (e.g., guzzlehttp/guzzle or symfony/http-client).

  2. Configure the DSN Add the Zulip DSN to your .env:

    ZULIP_DSN=zulip://your-email@example.com:API_TOKEN@your-zulip-host.com?channel=your-channel
    

    Or define it in config/services.php:

    'zulip' => [
        'dsn' => env('ZULIP_DSN', 'zulip://default:token@host?channel=default'),
    ],
    
  3. First Notification Use the notifier in a Laravel controller or command:

    use Symfony\Component\Notifier\Notifier;
    use Symfony\Component\Notifier\Message\ChatMessage;
    
    public function sendZulipNotification()
    {
        $notifier = new Notifier(
            new ZulipTransport(config('zulip.dsn'))
        );
    
        $message = new ChatMessage('Hello from Laravel!');
        $notifier->send($message);
    }
    
  4. Trigger via Events Dispatch a Laravel event and listen for it:

    // In a service or controller
    event(new DeploymentFailed('Server crashed!'));
    
    // In EventServiceProvider
    protected $listen = [
        DeploymentFailed::class => [ZulipEventListener::class],
    ];
    

Implementation Patterns

Core Workflows

  1. Event-Driven Notifications

    • Pattern: Use Laravel’s event system to trigger Zulip messages.
    • Example:
      // Listen to a custom event
      public function handle(DeploymentFailed $event)
      {
          $notifier = app(Notifier::class);
          $message = new ChatMessage($event->message);
          $notifier->send($message);
      }
      
  2. Queue-Based Delays

    • Pattern: Dispatch notifications to Laravel’s queue for async processing.
    • Example:
      Queue::push(function () {
          $notifier = app(Notifier::class);
          $notifier->send(new ChatMessage('Scheduled task completed!'));
      });
      
  3. Dynamic Channel Routing

    • Pattern: Route messages to different Zulip channels based on context.
    • Example:
      $channel = $user->role === 'admin' ? 'admin-alerts' : 'user-notifications';
      $dsn = "zulip://email:token@host?channel={$channel}";
      $notifier->send(new ChatMessage('Alert!', $dsn));
      
  4. Rich Message Formatting

    • Pattern: Use Zulip’s markdown and emoji support.
    • Example:
      $message = new ChatMessage(
          "🚨 **Deployment Failed**\n```\n{$event->error}\n```",
          config('zulip.dsn')
      );
      

Integration Tips

  • Symfony Notifier Bridge: Extend Laravel’s Illuminate\Contracts\Queue\ShouldQueue for async notifications:

    use Symfony\Component\Notifier\Message\ChatMessage;
    use Symfony\Component\Notifier\Notifier;
    
    class ZulipNotification implements ShouldQueue
    {
        public function handle()
        {
            $notifier = new Notifier(new ZulipTransport(config('zulip.dsn')));
            $notifier->send(new ChatMessage($this->message));
        }
    }
    
  • Webhook Listeners: For incoming Zulip webhooks (e.g., reactions), create a Laravel route:

    Route::post('/zulip/webhook', function (Request $request) {
        // Parse Zulip payload and trigger Laravel logic
    });
    
  • Testing: Mock the ZulipTransport in PHPUnit:

    $transport = $this->createMock(ZulipTransport::class);
    $transport->expects($this->once())->method('send');
    $notifier = new Notifier($transport);
    

Gotchas and Tips

Pitfalls

  1. Symfony Dependency Conflicts

    • Issue: The package assumes Symfony’s HttpClient or OptionsResolver.
    • Fix: Use a wrapper or replace dependencies:
      // Use Guzzle instead of Symfony's HttpClient
      $client = new Client(['base_uri' => 'https://your-zulip-host.com']);
      $transport = new ZulipTransport($client, config('zulip.dsn'));
      
  2. DSN Parsing Errors

    • Issue: Invalid DSN format (e.g., missing channel query param).
    • Fix: Validate the DSN in a service provider:
      if (!str_starts_with(config('zulip.dsn'), 'zulip://')) {
          throw new \RuntimeException('Invalid Zulip DSN');
      }
      
  3. Rate Limiting

    • Issue: Zulip’s API limits (200 requests/10 minutes) may throttle notifications.
    • Fix: Implement retries with Laravel’s Illuminate\Queue\Retryable:
      class ZulipNotification implements ShouldQueue
      {
          public function retryUntil()
          {
              return now()->addMinutes(10);
          }
      }
      
  4. Authentication Failures

    • Issue: Invalid API token or email.
    • Fix: Log errors and notify admins:
      try {
          $notifier->send($message);
      } catch (\Exception $e) {
          Log::error("Zulip notification failed: {$e->getMessage()}");
          // Send fallback email
      }
      

Debugging Tips

  • Enable Debug Mode: Configure the transport to log requests:

    $transport = new ZulipTransport($client, config('zulip.dsn'), [
        'debug' => true,
    ]);
    
  • Check Zulip API Status: Verify Zulip’s API endpoint (/api/version) is reachable:

    $response = $client->request('GET', '/api/version');
    if ($response->getStatusCode() !== 200) {
        throw new \RuntimeException('Zulip API unavailable');
    }
    

Extension Points

  1. Custom Message Types Extend ChatMessage for Zulip-specific features (e.g., topics, emoji):

    class ZulipMessage extends ChatMessage
    {
        public function __construct(string $content, string $dsn, ?string $topic = null)
        {
            parent::__construct($content, $dsn);
            $this->topic = $topic;
        }
    }
    
  2. Transport Decorators Add pre/post-processing to messages:

    class LoggingZulipTransport implements TransportInterface
    {
        private $transport;
    
        public function __construct(ZulipTransport $transport)
        {
            $this->transport = $transport;
        }
    
        public function send(MessageInterface $message)
        {
            Log::info("Sending to Zulip: {$message->getContent()}");
            $this->transport->send($message);
        }
    }
    
  3. Multi-Channel Support Dynamically switch channels based on user roles:

    $dsn = str_replace(
        '?channel=default',
        "?channel={$user->zulipChannel}",
        config('zulip.dsn')
    );
    

Configuration Quirks

  • Environment Variables: Use Laravel’s env() helper to load the DSN:

    'zulip' => [
        'dsn' => env('ZULIP_DSN', 'zulip://default:token@host'),
    ],
    
  • Channel Validation: Ensure the channel exists in Zulip before sending:

    $response = $client->request('GET', '/api/channels');
    $channels = json_decode($response->getContent(), true);
    if (!in_array($channel, array_column($channels, 'name'))) {
        throw new \RuntimeException("Channel {$channel} does not exist");
    }
    
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor