xorgxx/warp-messenger-bundle

WarpMessengerBundle is a modular, warp multi-channel messaging bundle for Symfony.

Installs: 4

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Type:symfony-bundle

pkg:composer/xorgxx/warp-messenger-bundle

dev-master 2025-11-06 12:51 UTC

This package is auto-updated.

Last update: 2025-11-06 12:51:55 UTC


README

Adaptateur SMS Personnalisé

Présentation

Ce projet implémente un adaptateur SMS personnalisé pour le bundle WarpMessengerBundle. L'adaptateur utilise une API SMS externe pour envoyer des messages texte.

Configuration

L'adaptateur SMS est configuré dans src/Service/Sms/MonAdapterSmsProvider.php et utilise les clés d'API directement codées :

$apiKey = 'votre_cle_api'; // SMS_API_KEY
$apiSecret = 'votre_secret_api'; // SMS_API_SECRET

Utilisation

Pour envoyer un SMS :

$monAdapterSmsProvider->send('+33612345678', 'Votre message ici');

Fonctionnalités supportées

  • Support Unicode
  • Concaténation de SMS longs
  • Rapports de livraison

Dépendances

  • GuzzleHTTP pour les appels API
  • WarpMessengerBundle pour l'intégration avec Symfony Latest Version Build Status License

WarpMessengerBundle est une solution complète et moderne pour l'envoi de messages multi-canaux dans les applications Symfony. Il permet d'envoyer facilement des messages via différents canaux (email, SMS, Discord, etc.) avec une API fluide et une architecture extensible.

Note: Les notifications en temps réel via Mercure sont optionnelles. Pour les activer, installez simplement le bundle Mercure avec composer require symfony/mercure-bundle.

Documentation Avancée

Pour approfondir les fonctionnalités avancées, consultez les guides détaillés suivants :

Installation

composer require xorgxx/warp-messenger-bundle

Consultez le guide d'installation détaillé pour plus d'informations sur la configuration et les fonctionnalités optionnelles.

Caractéristiques

API fluide intuitive - API en chaîne de méthodes (fluent API) pour une expérience développeur optimale

Multi-canal - Envoi de messages via différents canaux (email, SMS, Discord) avec une seule configuration

Extensible - Architecture modulaire permettant d'ajouter facilement de nouveaux canaux

Templating Twig - Utilisation de templates Twig pour le formatage des messages

Gestion intelligente des SMS - Concaténation automatique, détection Unicode, optimisation de la longueur

Canaux de secours - Envoi automatique via des canaux alternatifs en cas d'échec

Résultats détaillés - Informations précises sur le statut d'envoi et les erreurs

Intégration Symfony - Compatibilité totale avec les composants Symfony (Messenger, Mailer, EventDispatcher)

Installation

Via Composer

composer require neoxx/warp-messenger-bundle

Configuration du bundle

Activez le bundle dans config/bundles.php :

return [
    // ...
    Warp\MessengerBundle\WarpMessengerBundle::class => ['all' => true],
];

Créez un fichier de configuration config/packages/warp_messenger.yaml :

warp_messenger:
    # Templates par défaut pour chaque canal
    templates:
        email: 'emails/default.html.twig'
        sms: 'sms/default.txt.twig'
        discord: 'discord/default.txt.twig'

    # Configuration spécifique SMS
    sms:
        enable_concat: true    # Autoriser la concaténation des SMS longs
        enable_truncate: false # Autoriser la troncature des SMS longs

    # Expéditeurs par défaut
    default_from:
        email: 'noreply@example.com'
        sms: 'WARP'

    # Canaux de secours en cas d'échec
    fallback_channels:
        enabled: false
        default: ['email']

    # Configuration générale
    log_messages: true      # Journaliser les messages
    enable_retries: false   # Activer les nouvelles tentatives

Utilisation

Injection du service

use Warp\MessengerBundle\Service\WarpMessenger;

class NotificationService 
{
    public function __construct(
        private WarpMessenger $messenger
    ) {}

    // ...
}

Envoi d'un message simple

$messenger->createMessage("Contenu du message")
    ->to([
        'email' => 'utilisateur@example.com',
        'sms' => '+33612345678'
    ])
    ->send();

Exemple multichanal complet

/**
 * Envoi d'une notification multichanal pour une commande
 */
public function notifierClient(WarpMessenger $messenger, Order $order, User $client)
{
    // Préparer les données communes pour tous les canaux
    $commonData = [
        'order' => $order,
        'client' => $client,
        'order_number' => $order->getReference(),
        'shipping_date' => $order->getShippingDate()->format('d/m/Y'),
        'tracking_url' => $this->generateTrackingUrl($order),
        'logo_url' => 'https://example.com/logo.png'
    ];

    // Créer un message avec gestion des erreurs et canaux de secours
    $result = $messenger->createMessage()
        // Définir tous les destinataires pour tous les canaux
        ->to([
            // Email principal avec nom complet
            'email' => new Address($client->getEmail(), $client->getFullName()),

            // SMS avec le numéro formaté pour l'international
            'sms' => $client->getFormattedPhoneNumber(),

            // Notification Discord sur le canal approprié
            'discord' => $this->params->get('discord.customer_notifications_webhook'),

            // Pour les clients qui utilisent l'application mobile
            'push' => $client->getPushTokens()
        ])
        // Activer uniquement les canaux que le client a autorisés dans ses préférences
        ->onChannels($client->getEnabledNotificationChannels())

        // Template email avec données spécifiques à l'email
        ->usingTemplate('emails/order_shipped.html.twig', array_merge($commonData, [
            'subject' => 'Votre commande ' . $order->getReference() . ' a été expédiée',
            'cc' => $client->getSecondarEmails(),
            'attachments' => [
                // Ajouter automatiquement la facture PDF
                $this->invoiceGenerator->generatePdfPath($order)
            ]
        ]))

        // Métadonnées pour le suivi et les statistiques
        ->withMetadata([
            'order_id' => $order->getId(),
            'client_id' => $client->getId(),
            'notification_type' => 'order_shipped',
            'priority' => 'high'
        ])

        // Configurer les options avancées
        ->withOptions([
            'track_opens' => true,          // Tracker l'ouverture des emails
            'track_clicks' => true,         // Tracker les clics dans les emails
            'save_history' => true,         // Sauvegarder l'historique des envois
            'retry_on_failure' => true,     // Réessayer en cas d'échec
            'max_retries' => 3,             // Nombre maximum de tentatives
            'retry_delay' => 300            // Délai entre les tentatives (secondes)
        ])

        // Envoyer le message et récupérer le résultat détaillé
        ->send();

    // Analyser les résultats de l'envoi
    if ($result->isCompleteSuccess()) {
        $this->logger->info('Notification envoyée avec succès sur tous les canaux', [
            'order' => $order->getId(),
            'channels' => $result->getSuccessfulChannels()
        ]);
    } elseif ($result->isPartialSuccess()) {
        $this->logger->warning('Notification partiellement envoyée', [
            'order' => $order->getId(),
            'success_channels' => $result->getSuccessfulChannels(),
            'failed_channels' => $result->getFailedChannels()
        ]);

        // Notification interne en cas d'échec partiel
        $this->notifyTeam('Échec partiel de notification client', [
            'order' => $order->getReference(),
            'client' => $client->getFullName(),
            'failed_channels' => $result->getFailedChannels()
        ]);
    } else {
        $this->logger->error('Échec d\'envoi de notification', [
            'order' => $order->getId(),
            'errors' => $this->formatChannelErrors($result)
        ]);

        // Action de repli en cas d'échec total (ex: notification manuelle)
        $this->createManualFollowupTask($order, 'Échec des notifications automatiques');
    }

    return $result;
}

/**
 * Formate les erreurs par canal pour le log
 */
private function formatChannelErrors(MessageResult $result): array
{
    $errors = [];
    foreach ($result->getFailedChannels() as $channelId) {
        $channelResult = $result->getChannelResult($channelId);
        $errors[$channelId] = [
            'message' => $channelResult->getMessage(),
            'exception' => $channelResult->getException() ? $channelResult->getException()->getMessage() : null
        ];
    }
    return $errors;
}

Utilisation de templates

$messenger->createMessage()
    ->to(['email' => 'client@example.com'])
    ->usingTemplate('emails/facture.html.twig', [
        'subject' => 'Votre facture #123',
        'facture' => $facture,
        'client' => $client,
        'montant' => 99.99
    ])
    ->send();

Envoi spécifique par canal

// SMS uniquement
$messenger->createMessage("Votre code de confirmation est 123456")
    ->to(['sms' => '+33612345678'])
    ->onChannels(['sms'])
    ->send();

// Email avec pièces jointes
$messenger->createMessage()
    ->to(['email' => 'contact@example.com'])
    ->onChannels(['email'])
    ->usingTemplate('emails/document.html.twig', [
        'subject' => 'Documents importants',
        'user' => $user
    ])
    ->withAttachments([
        '/chemin/vers/document.pdf',
        ['path' => '/chemin/vers/image.jpg', 'name' => 'Photo.jpg', 'mime' => 'image/jpeg']
    ])
    ->send();

Envoi groupé

// Préparer plusieurs messages
foreach ($users as $user) {
    $messenger->createMessage()
        ->to(['email' => $user->getEmail()])
        ->usingTemplate('emails/newsletter.html.twig', [
            'subject' => 'Notre newsletter mensuelle',
            'user' => $user
        ])
        ->queueMessage();
}

// Envoyer tous les messages en une fois
$messenger->flush();

Planification des messages

$date = new \DateTime('tomorrow 10:00:00');

$messenger->createMessage("Rappel de rendez-vous")
    ->to(['sms' => '+33612345678'])
    ->delayUntil($date)
    ->send();

Canaux disponibles

Email

$messenger->createMessage()
    ->to(['email' => 'destinataire@example.com'])
    ->usingTemplate('emails/notification.html.twig', [
        'subject' => 'Objet du message',
        'cc' => ['copie@example.com'], // Optionnel
        'bcc' => ['copie.cachee@example.com'], // Optionnel
    ])
    ->send();

Système d'adapters personnalisés

WarpMessengerBundle utilise un système d'adapters extensible qui vous permet de créer facilement vos propres canaux de communication.

Comment fonctionne le système d'adapters

  • Le nom de l'adapter dans votre configuration YAML est utilisé pour « matcher » et charger dynamiquement la classe correspondante.
  • Lorsque vous spécifiez adapter: 'mon_adapter_sms' dans la configuration, le bundle recherche un service avec le tag warp_messenger.channel et le nom correspondant.
  • Tous les adapters personnalisés sont chargés automatiquement s'ils sont correctement enregistrés comme services Symfony avec le tag approprié.

Exemple d'implementation

# config/packages/warp_messenger.yaml
warp_messenger:
  channels:
    sms:
      enabled: true
      adapter: 'mon_adapter_sms'
      options:
        param1: 'valeur1'
        param2: 'valeur2'
// src/WarpMessenger/Channel/MonAdapterSMS.php
namespace App\WarpMessenger\Channel;

use Warp\MessengerBundle\Channel\AbstractChannel;

class MonAdapterSMS extends AbstractChannel
{
    public function send($to, $message, array $options = [])
    {
        // Votre logique d'envoi SMS (API, curl, etc.)
        // Par exemple :
        // $apiKey = $options['param1'];
        // ...
    }
}
# config/services.yaml
services:
  App\WarpMessenger\Channel\MonAdapterSMS:
    tags: ['warp_messenger.channel']
    arguments:
      $name: 'mon_adapter_sms'

Consultez la documentation CUSTOM_CHANNEL.md pour des exemples plus détaillés d'implémentation de canaux personnalisés.

SMS

$messenger->createMessage("Votre code de vérification est 123456")
    ->to(['sms' => '+33612345678'])
    ->send();

Discord

$messenger->createMessage("Alerte : nouvel utilisateur inscrit")
    ->to(['discord' => 'webhook_id']) // Optionnel si configuré globalement
    ->usingTemplate('discord/alert.txt.twig', [
        'username' => 'Bot Notification', // Nom d'affichage du bot
        'avatar_url' => 'https://example.com/avatar.png', // Avatar du bot
        'embeds' => [ // Cartes enrichies Discord
            [
                'title' => 'Nouvel utilisateur',
                'description' => 'Un nouvel utilisateur s\'est inscrit',
                'color' => 5814783, // Couleur en décimal
                'fields' => [
                    ['name' => 'Nom', 'value' => 'John Doe', 'inline' => true],
                    ['name' => 'Email', 'value' => 'john@example.com', 'inline' => true]
                ]
            ]
        ]
    ])
    ->send();

Créer un nouveau canal

  1. Créez une classe qui implémente ChannelInterface ou étend AbstractChannel
namespace App\Messenger\Channel;

use Warp\MessengerBundle\Messenger\Channel\AbstractChannel;
use Warp\MessengerBundle\Messenger\Message\WarpMessage;
use Warp\MessengerBundle\Messenger\Result\ChannelResult;

class SlackChannel extends AbstractChannel
{
    protected array $supportedFeatures = ['attachments', 'reactions'];

    public function __construct(
        private readonly string $webhookUrl,
        Environment $twig,
        ?LoggerInterface $logger = null,
        string $defaultTemplate = 'slack/default.txt.twig'
    ) {
        parent::__construct($twig, $logger, $defaultTemplate);
    }

    public function getChannelIdentifier(): string
    {
        return 'slack';
    }

    public function supportsFeature(string $feature): bool
    {
        return in_array($feature, $this->supportedFeatures, true);
    }

    public function send(WarpMessage $message): ChannelResult
    {
        try {
            $content = $this->renderContent($message);

            // Logique d'envoi à Slack
            // ...

            return $this->createSuccessResult('Message Slack envoyé avec succès');
        } catch (\Exception $e) {
            return $this->createFailureResult('Erreur d\'envoi Slack: ' . $e->getMessage(), $e);
        }
    }
}
  1. Déclarez votre canal comme service avec le tag approprié
# config/services.yaml
services:
    App\Messenger\Channel\SlackChannel:
        arguments:
            - '%env(SLACK_WEBHOOK_URL)%'
            - '@twig'
            - '@logger'
            - 'slack/default.txt.twig'
        tags:
            - { name: 'warp_messenger.channel', alias: 'slack' }

Événements

Le bundle déclenche deux événements principaux :

  • warp_messenger.message.pre_send - Avant l'envoi d'un message
  • warp_messenger.message.post_send - Après l'envoi d'un message
namespace App\EventListener;

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Warp\MessengerBundle\Event\MessageEvent;

#[AsEventListener(event: MessageEvent::PRE_SEND)]
class PreSendListener
{
    public function __invoke(MessageEvent $event): void
    {
        $message = $event->getMessage();
        // Modification du message avant envoi
    }
}

#[AsEventListener(event: MessageEvent::POST_SEND)]
class PostSendListener
{
    public function __invoke(MessageEvent $event): void
    {
        if ($event->isSuccess()) {
            // Traitement après envoi réussi
        } else {
            // Gestion des échecs
        }
    }
}

Documentation avancée

Pour une documentation plus détaillée sur toutes les fonctionnalités :

Compatibilité

  • PHP 8.0 ou supérieur
  • Symfony 5.4, 6.x

Licence

Ce bundle est disponible sous licence MIT. Voir le fichier LICENSE pour plus de détails.

Contribuer

Les contributions sont les bienvenues ! N'hésitez pas à soumettre des pull requests ou à ouvrir des issues pour signaler des bugs ou proposer des améliorations.

WarpMessenger

Modular multi-channel messaging bundle for Symfony 7.3 / PHP 8.3.

📚 Table of Contents

📬 WarpMessengerBundle

WarpMessenger is a Symfony 7.3 / PHP 8.3 bundle for sending multi-channel messages (email, SMS, Discord, etc.) using Messenger. It supports Twig templates, delayed delivery, SMS concatenation, and is fully extensible.

🚀 Features

  • ✅ Fluent and chainable wrapper
  • 📦 Multi-channel support: Email, SMS, Discord (extensible)
  • 🧩 Twig templates (HTML/TXT) with fallback support
  • ⏱️ Delayed sending via Messenger + RabbitMQ
  • ✂️ Automatic SMS truncation or concatenation
  • 🧪 Includes PestPHP tests
  • 🔧 CLI Command: warp:send

⚙️ Installation

composer require warp/messenger-bundle

🛠️ Configuration

# config/packages/warp_messenger.yaml
parameters:
  warp_messenger.sms.concat: true
  warp_messenger.sms.truncate: false

📦 Usage Example

$warpMessenger
    ->to([
        'email' => 'client@example.com',
        'sms' => '+33612345678'
    ])
    ->onChannels(['email', 'sms'])
    ->usingTemplate('shared/order.twig', [
        'name' => 'Jean',
        'orderId' => 'ABC123'
    ])
    ->delayUntil(new \DateTime('+10 minutes'))
    ->send("Your order ABC123 has been shipped.");

📘 See Mercure browser channel 📘 See Message logging

📱 SMS Behavior

Condition Result
≤ 160 characters ✅ 1 SMS
> 160 + concat = true 📦 Multi-SMS
> 160 + truncate = true ✂️ Truncated
> 160 + both false ❌ Exception

🧪 Run Tests

vendor/bin/pest

🖥️ CLI Command

php bin/console warp:send \
  "email=client@example.com,sms=+33612345678" \
  "Test message" \
  "email,sms" \
  "+5 minutes"

📂 Template Structure

templates/
├─ emails/default.html.twig
├─ sms/default.txt.twig
└─ discord/default.txt.twig

🔌 Add a Custom Channel

Create a class that implements ChannelInterface:

class SlackChannel implements ChannelInterface
{
    public function send(WarpMessage $message): void
    {
        // Send to Slack
    }
}

🔄 Batch Sending with flush()

$warpMessenger
    ->queueMessage()
        ->to([...])
        ->send("First message")
    ->queueMessage()
        ->to([...])
        ->send("Second message")
    ->flush();

📧 Email with Attachments

$warpMessenger
    ->to(['email' => 'client@example.com'])
    ->onChannels(['email'])
    ->usingTemplate('emails/notification.html.twig', [
        'user' => 'Jean',
        'summary' => 'Your order was shipped.'
    ])
    ->withAttachments([
        '/path/to/invoice.pdf',
        '/path/to/manual.pdf'
    ])
    ->send('Your order ABC123 has been shipped.');

📄 Sample Twig Template

<h1>Hello {{ user }}</h1>
<p>{{ summary }}</p>

🔌 Automatic Custom Channel Registration

Steps:

  1. Implement ChannelInterface
  2. Tag the service in services.yaml:
services:
  App\Messenger\Channel\SlackChannel:
    tags: ['warp_messenger.channel']
  1. Use the channel by alias:
$warpMessenger
    ->to(['slack' => 'https://hooks.slack.com/...'])
    ->onChannels(['slack'])
    ->send('This is a custom Slack message!');

🏷️ Custom Channel Aliases

warp_messenger:
  aliases:
    App\Messenger\Channel\SlackChannel: slack
    App\Channel\MatrixChannel: matrix

Usage in code:

$warpMessenger
    ->to(['slack' => 'https://hooks.slack.com/...'])
    ->onChannels(['slack'])
    ->send("Hello from Slack via alias!");

To list aliases:

php bin/console warp:aliases:list

Expected output:

Registered WarpMessenger Channel Aliases
+--------+--------------------------------------------+
| Alias  | Channel Class                              |
+--------+--------------------------------------------+
| slack  | App\\Messenger\\Channel\\SlackChannel      |
| discord| App\\Messenger\\Channel\\DiscordChannel    |
+--------+--------------------------------------------+

📄 License

MIT – WarpMessengerBundle