J'ai créé signal-sdk parce que je voulais utiliser Signal dans mes propres outils sans réécrire un script fragile à chaque changement de signal-cli. Mon objectif était simple : garder la logique métier en Node.js et laisser un outil spécialisé gérer la connexion au protocole Signal.
Dans ce tutoriel, je vais te montrer comment je m'en sers pour envoyer une notification, écouter un message entrant et construire un petit bot à commandes. Je vais aussi te donner les précautions que je prends quand le processus tourne sur un serveur.
Si tu arrives depuis mon premier guide, garde-le sous la main : j'ai conservé sa page et ajouté un changelog des exemples mis à jour pour éviter que tu retombes sur l'ancienne API.
Pourquoi j'ai construit signal-sdk
Signal ne propose pas une API REST officielle avec une clé à copier dans un fichier .env. Pour automatiser un compte, il faut passer par un appareil lié et par un client capable de parler le protocole. C'est le rôle de signal-cli.
Je n'ai pas voulu réimplémenter la cryptographie de Signal. signal-sdk pilote signal-cli et ajoute une interface TypeScript qui ressemble davantage à une application Node.js classique :
signal-cliconserve l'état du compte, les clés et le protocole ;signal-sdkgère le processus, le JSON-RPC, les événements et les tentatives ;- ton code contient uniquement la logique de notification ou de bot.
Le SDK expose deux portes d'entrée. SignalCli te donne le contrôle direct sur les messages et les événements. SignalBot ajoute les commandes, les permissions et une file d'actions pour les bots.
Installer le SDK
installe le paquet dans ton projet :
npm install signal-sdk
Je cible Node.js 18 ou plus récent. Le paquet installe automatiquement signal-cli. Sur Linux, il utilise le binaire natif. Sur macOS et Windows, tu dois aussi avoir Java 25 ou plus récent :
node --version java -version # macOS et Windows uniquement
Si tu veux regarder le code ou contribuer, le dépôt est disponible sur GitHub :
git clone https://github.com/benoitpetit/signal-sdk.git cd signal-sdk npm install npm run build
Lier un appareil Signal
Je te conseille d'utiliser un compte Signal dédié à l'automatisation. Ça évite qu'un mauvais destinataire ou une boucle d'envoi touche tes conversations personnelles.
Depuis le projet qui a installé le SDK, lance l'assistant :
npx signal-sdk connect "Serveur de notifications"
Un QR code apparaît dans le terminal. Dans Signal, ouvre Paramètres → Appareils liés → Lier un nouvel appareil, puis scanne-le.
Tu ne devras normalement faire cette étape qu'une fois. En revanche, tu dois conserver le dossier de données de signal-cli : il contient l'identité et les clés du compte. Je monte toujours ce dossier dans un volume persistant lorsque je déploie le bot dans Docker.
Envoyer un premier message
Je garde les numéros dans l'environnement et je les écris au format international E.164 :
SIGNAL_PHONE_NUMBER=+33600000000 SIGNAL_RECIPIENT=+33700000000
Voici le script minimal que j'utilise pour une notification ponctuelle :
import 'dotenv/config'; import { SignalCli } from 'signal-sdk'; const phoneNumber = process.env.SIGNAL_PHONE_NUMBER; const recipient = process.env.SIGNAL_RECIPIENT; if (!phoneNumber || !recipient) { throw new Error('SIGNAL_PHONE_NUMBER et SIGNAL_RECIPIENT sont requis'); } const signal = new SignalCli(phoneNumber, undefined, { maxRetries: 3, retryDelay: 1_000, minRequestInterval: 500, requestTimeout: 60_000, }); try { await signal.connect(); await signal.sendMessage( recipient, `Sauvegarde terminée à ${new Date().toLocaleString('fr-FR')}`, ); console.log('Notification envoyée'); } finally { await signal.gracefulShutdown(); }
Je garde le finally même dans un exemple aussi court. signal-cli est un processus enfant ; le fermer proprement évite de laisser un daemon tourner après l'envoi.
Pour une notification récurrente, je laisse cron ou systemd s'occuper de la planification et je garde le script focalisé sur l'envoi :
*/15 * * * * cd /opt/signal-notifier && /usr/bin/node send-message.mjs >> /var/log/signal-notifier.log 2>&1
Écouter les messages entrants
Quand j'ai besoin de réagir à un message, j'écoute l'événement message. Une enveloppe Signal peut contenir autre chose qu'un texte, donc je vérifie toujours les champs avant de répondre :
signal.on('message', (message) => { const text = message.dataMessage?.message; const source = message.source; if (!text || !source) return; console.log(`Message reçu de ${source} : ${text}`); if (text.trim().toLowerCase() === 'ping') { void signal.sendMessage(source, 'pong'); } });
Pour quelques règles simples, cet écouteur suffit. Dès que les commandes se multiplient, je passe à SignalBot.
Construire un bot à commandes
SignalBot m'évite de mélanger le parsing des commandes, les permissions et la réponse dans un seul écouteur. Voici un bot réduit à une commande réservée à l'administrateur :
import 'dotenv/config'; import { SignalBot } from 'signal-sdk'; const phoneNumber = process.env.SIGNAL_PHONE_NUMBER; const admin = process.env.SIGNAL_ADMIN_NUMBER; if (!phoneNumber || !admin) { throw new Error('SIGNAL_PHONE_NUMBER et SIGNAL_ADMIN_NUMBER sont requis'); } const bot = new SignalBot({ phoneNumber, admins: [admin], settings: { commandPrefix: '/', cooldownSeconds: 3, logMessages: false, }, }); bot.addCommand({ name: 'statut', description: 'Retourne le statut du service', adminOnly: true, handler: async () => `Service disponible depuis ${Math.round(process.uptime())} secondes.`, }); bot.on('error', (error) => { console.error('Erreur du bot Signal :', error); }); await bot.start();
Le bot fournit déjà /help, /ping, /stats et /info. J'ai désactivé logMessages dans l'exemple : le contenu de tes conversations n'a rien à faire dans un fichier de log général.
Les garde-fous que j'utilise en production
Le premier message part vite. La fiabilité se joue surtout autour du processus.
Je conserve les données du compte
Les clés et l'identité du compte résident dans les données de signal-cli. Si tu reconstruis ton conteneur sans volume persistant, tu risques de générer une nouvelle identité et de déclencher un changement de numéro de sécurité chez tes correspondants.
Je limite les envois
Le SDK possède un limiteur de débit et une politique de retry. Je rajoute tout de même une liste de destinataires autorisés, un quota par heure et un arrêt automatique après plusieurs erreurs. Une alerte doit rester rare et attendue.
Je ne mets aucun secret dans le dépôt
Ne versionne ni .env, ni le dossier de données Signal, ni des logs remplis de numéros. J'utilise un compte Unix dédié, des permissions minimales et une rotation des journaux.
Je teste les mises à jour
signal-sdk suit signal-cli, mais les deux projets ne sortent pas au même rythme. J'épingle les versions, je teste l'envoi vers un compte de test et je surveille les événements error et daemon-closed avant toute mise à jour en production.
Je reste dans un usage raisonnable
Ce montage repose sur un outil communautaire et un appareil lié. Il ne donne pas accès à une API officielle de Signal. Respecte les règles du service, le consentement des destinataires et les contraintes légales de ton contexte. Le fait que l'envoi soit possible ne veut pas dire qu'il est souhaitable.
Pour finir
J'ai construit signal-sdk pour enlever le bruit autour du transport JSON-RPC et te laisser écrire une vraie application Node.js : une notification, un assistant d'équipe ou un bot privé.
Tu peux commencer petit : un compte dédié, un seul scénario, des logs sobres et une limite d'envoi claire. Le code et les tests sont disponibles sur GitHub, et le paquet s'installe avec npm install signal-sdk.
Article co-écrit avec l'IA · Images générées avec l'IA.
