chatbuss_chat — SDK de chat in-app ChatBuss (Flutter)

Permet à n'importe quelle application mobile d'offrir un support client (messagerie + IA + agents) via ChatBuss — l'équivalent du widget web livechat, mais pour les apps.

Comment ça marche

Le SDK parle à la façade mobile, /api/v1/app-chat/v1/* — distincte de celle du widget web, et versionnée à dessein : une application publiée met des jours à passer les stores, là où un site se redéploie en une minute.

Étape Endpoint
Ouvrir/reprendre la session POST /session (renvoie un user_id persisté)
Envoyer un message POST /messages
Recevoir les réponses agent WS /api/v1/ws/app-chat/v1, avec relecture de GET /messages/{user_id} en filet

Chaque appel porte l'en-tête X-Chatbuss-SDK : le serveur sait ainsi quelle génération d'application lui parle, et peut répondre 426 à une version périmée plutôt que de deviner. Le user_id est stocké (SharedPreferences, clé chatbuss_visitor_<channel_id>) → la conversation reprend au relancement de l'app.

Prérequis côté ChatBuss

Le canal doit être de type app_chat, pas livechat. Les deux servent le même noyau, mais par des façades séparées : /api/v1/app-chat/v1/* pour les applications, /api/v1/livechat/* pour le widget web. Un site se redéploie en une minute, une application publiée met des jours à passer les stores et une partie du parc ne se met jamais à jour — les deux ne peuvent pas partager un contrat. Un identifiant de canal livechat est refusé par le SDK.

  1. Créer un canal de type app_chat dans la console ChatBuss.
  2. Récupérer son channel_id (UUID public).
  3. Le donner à l'app hôte (avec l'URL du serveur).

Intégration (côté app cliente)

flutter pub add chatbuss_chat
# ou à la main, dans le pubspec.yaml de l'app hôte
dependencies:
  chatbuss_chat: ^1.0.0
import 'package:chatbuss_chat/chatbuss_chat.dart';

// Ouvrir le support depuis un bouton "Aide" :
Navigator.push(context, MaterialPageRoute(
  builder: (_) => ChatbussChatScreen(
    serverUrl: 'https://chatbuss-server.h.gandyam.com',
    channelId: 'VOTRE-CHANNEL-ID',
    // L'app connaît déjà son utilisateur → pré-identification, pas de formulaire :
    user: ChatUser(name: 'Awa Diop', email: 'awa@exemple.com'),
  ),
));

C'est tout. L'utilisateur discute avec le support sans quitter l'app.

Tester l'intégration (app d'exemple)

cd chatbuss_chat_sdk/example
# 1) Éditer lib/main.dart : mettre kServerUrl et kChannelId
flutter pub get
flutter run          # sur un émulateur ou un téléphone branché

Puis, côté ChatBuss (console agent), réponds au message : il apparaît dans l'app (polling ~3 s).

Checklist de validation :

  • Le chat s'ouvre et affiche le message d'accueil.
  • L'envoi d'un message → il apparaît + arrive dans la console agent ChatBuss (canal app_chat).
  • La réponse de l'agent apparaît dans l'app en quelques secondes.
  • Fermer/rouvrir l'app → la conversation est reprise (même visitor_id).

API du SDK

  • ChatbussChatScreen(...) — écran de chat clé en main.
  • ChatbussClient(...) — client bas niveau si tu veux ta propre UI :
    • init() · sendMessage(text) · fetchMessages() · getConfig()
    • sendMedia(bytes:, filename:, mimeType:) — pièce jointe
    • answerSurvey(surveyId:, rating:, comment:) — note de satisfaction

Enquête de satisfaction (CSAT)

Rien à câbler : si le canal a csat_enabled, l'enquête arrive dans le fil comme un message et ChatbussChatScreen l'affiche en étoiles cliquables. Pour une interface sur mesure, ChatMessage.isSurvey et ChatMessage.survey portent le barème, et ChatbussClient.answerSurvey enregistre la note.

Pièces jointes

Le bouton trombone est présent par défaut et s'appuie sur file_picker. Formats acceptés : JPEG, PNG, WebP, GIF, PDF, 10 Mo maximum — le serveur vérifie la signature réelle du fichier, pas seulement son extension.

Si ton application possède déjà un sélecteur, branche-le pour ne pas en avoir deux :

ChatbussChatScreen(
  serverUrl: kServerUrl,
  channelId: kChannelId,
  pickAttachment: () async {
    final image = await ImagePicker().pickImage(source: ImageSource.gallery);
    if (image == null) return null; // annulé
    return ChatPickedFile(
      bytes: await image.readAsBytes(),
      filename: image.name,
      // Le serveur refuse application/octet-stream : déduis le type du nom.
      mimeType: mimeDepuisNom(image.name),
    );
  },
)

Pour une interface sur mesure, ChatbussClient.sendMedia ne demande que des octets — aucun sélecteur imposé.

À savoir : une pièce jointe dont l'envoi échoue ne survit pas à la fermeture de l'application. Le texte, lui, est conservé et rejoué au redémarrage.

Notifications push (Phase 2 — disponible)

Quand l'app est fermée, le polling ne suffit pas. Le SDK enregistre le jeton push de l'appareil ; quand un agent répond, chatbuss_server envoie une notification push.

Principe : l'app hôte possède déjà Firebase → elle obtient son jeton FCM et le passe au SDK. Le SDK ne gère pas l'init Firebase (pour ne pas entrer en conflit).

// L'app hôte récupère son jeton (elle a déjà firebase_messaging configuré) :
final fcmToken = await FirebaseMessaging.instance.getToken();

Navigator.push(context, MaterialPageRoute(builder: (_) => ChatbussChatScreen(
  serverUrl: 'https://ton-serveur',
  channelId: 'TON-CHANNEL-ID',
  user: ChatUser(name: 'Awa', email: 'awa@exemple.com'),
  deviceToken: fcmToken,          // ← active le push
  platform: 'android',            // ou 'ios'
)));

Côté serveur, c'est déjà branché :

  • POST /api/v1/app-chat/v1/device-token — enregistre le jeton (stocké sur le Client) ; /device-token/unregister le retire à la déconnexion de l'utilisateur.
  • Tâche send_visitor_push — déclenchée automatiquement à chaque réponse agent/bot (hook central notify_new_message). Réutilise l'infra FCM existante (fcm_service). No-op si FCM_ENABLED est faux.

Prérequis : FCM_CREDENTIALS_JSON configuré côté serveur, et Firebase configuré dans l'app hôte (google-services.json / GoogleService-Info.plist).

Vérification de l'identité (recommandé)

Par défaut, l'identité transmise dans ChatUser est crue sur parole. Connaître l'email d'un utilisateur suffit alors à ouvrir une session en son nom et à lire son historique de support. Tant que l'option est désactivée, le comportement reste celui-ci.

Pour la fermer, active « Exiger une identité signée » sur le canal, dans la console ChatBuss, et renseigne un secret partagé.

Ce que ton backend doit calculer

import hmac, hashlib

user_hash = hmac.new(
    CHATBUSS_IDENTITY_SECRET.encode(),
    str(user.id).encode(),        # le sujet — voir l'ordre ci-dessous
    hashlib.sha256,
).hexdigest()

Le sujet signé suit un ordre figé, qui fait partie du contrat : external_user_id, sinon email, sinon phone. Autrement dit, dès que tu fournis externalUserId, c'est lui qui doit être signé.

Ce que l'application fait de la signature

ChatbussChatScreen(
  serverUrl: 'https://<api>',
  channelId: '<channel_id>',
  user: ChatUser(
    externalUserId: me.id,      // pivot d'identité, stable même si l'email change
    name: me.name,
    email: me.email,
    userHash: me.chatbussHash,  // récupéré auprès de TON backend
  ),
)

Le secret ne doit jamais être embarqué dans l'application. Un APK se décompile : quiconque en extrait le secret peut signer l'identité de n'importe quel client. L'application demande la signature à son propre serveur, au moment de la connexion de l'utilisateur.

Erreurs possibles

Situation Réponse
Signature valide 200
Signature absente alors que le canal l'exige 403 Identité non vérifiée.
Signature d'un autre utilisateur 403
Canal en vérification sans secret configuré 403 — échec fermé, volontaire
Aucune identité fournie (visiteur anonyme) 200, rien à vérifier

Limites connues

  • Le SDK ne fait pas l'init Firebase : c'est à l'app hôte de fournir le jeton.
  • Une pièce jointe dont l'envoi échoue ne survit pas à la fermeture de l'application. Le texte, lui, est conservé et rejoué au redémarrage.

Temps réel

Le WebSocket est utilisé quand il est disponible, et la relecture périodique de l'historique reste le filet — elle passe simplement de 3 s à 20 s une fois la connexion établie. Aucun réglage n'est nécessaire : un réseau qui bloque le WebSocket dégrade le délai, jamais la réception.

Libraries

chatbuss_chat
SDK de chat in-app ChatBuss.