[email protected]
Twitter
LinkedIn
Youtube
GitHub
  • Services
  • Blog
  • Dépôts
  • GitHub
  • CV
  • Contact
Produit a été ajouté à votre panier

Chariot

Chatbot Jfa Whatsapp

Mars 29, 2023Automatisation, Back-End, Outilsjfadev

Avec ça node.js micro-framework utilisant Robot venimeux sous la capuche, vous pouvez facilement créer un Chatbot WhatsApp 🤖 . Vous n'aurez qu'à éditer votre flux de conversation dans un seul fichier.

Commencer

  1. Créer un nouveau référentiel à partir de ce modèle
  2. Installation dans votre environnement de développement
  3. Configurer port(s), crédits, etc
  4. Écrivez votre flux de conversation
  5. Commencer

Installation

Docker

Exigences: docker

Construire et exécuter avec Dockerfile

$ docker build -t wchatbot .
$ docker run --name wchatbot -p 3000:3000 -v /your_project_absolute_path/src:/wchatbot/src wchatbot

ou Construire et exécuter avec Docker Compose

$ docker-compose build
$ docker-compose up

Visitez http://hôte local:3000 et jouez avec votre chatbot!

Machine virtuelle

Exigences: nodejs (Dernière maintenance LTS version), yarn (ou npm), pm2, chrome/chromium

Utilisez un nginx proxy inverse pour exposer publiquement le panneau de contrôle http (exemple de configuration).

$ yarn install

Lancer le chatbot et le panneau de contrôle http

$ yarn start
$ yarn http-ctrl:start

Visitez http://hôte local:3000 et jouez avec votre chatbot!

Machine locale

Exigences: nodejs (Dernière maintenance LTS version), yarn (ou npm), chrome/chromium

$ yarn install

Lancer le chatbot et le panneau de contrôle http

$ yarn http-ctrl:dev:detach
$ yarn dev

Visitez http://hôte local:3000 et jouez avec votre chatbot!

Configuration

Éditer ./src/config.js fichier

Basic

export const chatbotOptions = {
  httpCtrl: {
    port: 3000, // httpCtrl port (http://localhost:3000/)
    username: "admin", // httpCtrl auth login
    password: "chatbot",
  },
};

Advancé

export const venomOptions = {
  ...
  browserArgs: [
    "--no-sandbox", // Will be passed to browser. Use --no-sandbox with Docker
  ],
  puppeteerOptions: { // Will be passed to puppeteer.launch.
    args: ["--no-sandbox"] // Use --no-sandbox with Docker
  },
  ...
};

Commands

Docker

Contrôles du chatbot

$ docker exec wchatbot yarn start
$ docker exec wchatbot yarn stop
$ docker exec wchatbot yarn restart
$ docker exec wchatbot yarn reload

Contrôles du panneau de configuration HTTP

$ docker exec wchatbot yarn http-ctrl:start
$ docker exec wchatbot yarn http-ctrl:stop
$ docker exec wchatbot yarn http-ctrl:restart
$ docker exec wchatbot yarn http-ctrl:reload

Machine virtuelle

Contrôles du chatbot

$ yarn start
$ yarn stop
$ yarn restart
$ yarn reload

Contrôles du panneau de configuration HTTP

$ yarn http-ctrl:start
$ yarn http-ctrl:stop
$ yarn http-ctrl:restart
$ yarn http-ctrl:reload

Machine locale

Directement dans votre OS sans Docker

Chatbot

$ yarn dev
$ yarn dev:detach

Lancer le panneau de configuration HTTP

$ yarn http-ctrl:dev
$ yarn http-ctrl:dev:detach

Séances

Les sessions et les jetons d'authentification sont écrits ./tokens dossier.

Journaux

Les journaux sont écrits ./logs dossier. Attention: console.log et http-ctrl-console.log n'écrivez que dans ./logs dossier avec yarn dev:detach et yarn http-ctrl:dev:detach autrement géré par pm2.

Docker

Chatbot

$ docker exec wchatbot yarn log

Panneau de configuration HTTP

$ docker exec wchatbot yarn http-ctrl:log

Conversations

$ docker exec wchatbot yarn conversations

Machine virtuelle

Chatbot

$ yarn log

Panneau de configuration HTTP

$ yarn http-ctrl:log

Conversations

$ yarn conversations

Machine locale

Chatbot

$ yarn log:dev

Panneau de configuration HTTP

$ yarn log:http-ctrl:dev

Conversations

$ yarn conversations

Flux de conversations

Éditer ./src/conversations/conversation.js fichier.

Le flux de conversation est un tableau d'objets de réponse ordonnés. Une réponse n'est déclenchée que si son parent (peut être un entier ou un tableau) est égal au id de la réponse précédente.

Replies Relations

Pour indiquer qu'une réponse est la fin de la conversation, ajoutez la propriété suivante:

Propriété Taper Description
end booléen La fin de la conversation

Vous pouvez protéger afin qu'un seul numéro ou une liste de numéros soit répondu avec:

Propriété Taper Description
from Chaîne / Array Ne répondez qu'à ce ou ces chiffres

Une réponse nécessite obligatoirement les propriétés suivantes:

Types de réponses

Envoyer du texte

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
message Chaîne Répondre par SMS

Exemple

[
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match with all text
    message: "Hi I am a Chatbot!",
  }
]

Boutons d'envoi

Attention: Il ne fonctionne pas actuellement!.

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
message Chaîne Répondre par SMS
description Chaîne Sous-titre du texte de la réponse
buttons Array Objet bouton, Regardez l'exemple

Exemple

[
  {
    id: 1,
    parent: 0,
    pattern: /.*/,
    message: "Hello!",
    description: "Can I help with something?",
    buttons: buttons([
      "Website",
      "LinkedIn",
      "Github",
    ]),
  }
]

Envoyer la liste

Attention: Il ne fonctionne pas actuellement!.

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
message Chaîne Répondre par SMS
description Chaîne Sous-titre du texte de la réponse
button Chaîne Texte du bouton de liste
list Array Objet de liste, Regardez l'exemple

Exemple

[
  {
    id: 1,
    parent: 0,
    pattern: /other country/,
    message: "Choice one country",
    description: "Choice one option!",
    button: "Countries list",
    list: list([
      "Argentina",
      "Belize",
      "Bolivia",
    ]),
  },
]

Envoyer un lien

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
message Chaîne Répondre par SMS
link Chaîne URL de l'aperçu du lien généré

Exemple

[
  {
    id: 2,
    parent: 1, // Relation with id: 1
    pattern: /github/,
    message: "Check my Github repositories!",
    link: "https://github.com/jfadev",
  }
]

Envoyer l'image

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
image Chemin / Objet Chemin ou objet renvoyé par remoteImg() fonction

Exemple

[
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    image: remoteImg("https://remote-server.com/menu.jpg"),
    // image: "./images/menu.jpg",
  }
]

Envoyer le son

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
audio Chemin / Objet Chemin ou objet renvoyé par remoteAudio() fonction.

Exemple

[
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    audio: remoteAudio("https://remote-server.com/audio.mp3"),
    // audio: "./audios/audio.mp3",
  }
]

Transférer le message

Propriété Taper Description
id Entier Repondre id est utilisé pour établir un lien avec parent
parent Entier Id du parent de réponse ou du tableau d'ids [2, 3]. S'il n'a pas de parent, c'est 0 par défaut
pattern RegExp Expression régulière à faire correspondre en minuscules
message Chaîne Répondre par SMS
forward Chaîne Numéro où le message est transféré

Exemple

[
    {
    id: 1,
    parent: 0,
    pattern: /forward/,
    message: "Text to forward",
    forward: "[email protected]", // forward this message to this number
  }
]

Aides

Assistant Retour Description
buttons(buttonTexts) Array Générer des boutons
remoteTxt(url) Chaîne Renvoyer un fichier TXT distant
remoteJson(url) JSON Renvoyer un fichier JSON distant
remoteImg(url) Objet Renvoyer un fichier image distant
remoteAudio(url) Objet Renvoyer un fichier audio distant
list(listRows) Array Générer la liste
inp(id, parents) Chaîne Renvoyer la chaîne d'entrée par ID de réponse. Utiliser avantRépondre, afterReply et beforeForward
med(id, parents) Médias / nul Retourner le média ({amortir, extension}) par identifiant de réponse. Utiliser avantRépondre, afterReply et beforeForward

Crochets

Propriété Taper Description
beforeReply(from, input, output, parents, media) Fonction Injecter du code personnalisé avant une réponse
afterReply(from, input, parents, media) Fonction Injecter du code personnalisé après une réponse
beforeForward(from, forward, input, parents, media) Fonction Injecter du code personnalisé avant un transfert

Boucles

Propriété Taper Description
goTo(from, input, output, parents, media) Fonction Devrait renvoyer l'identifiant de réponse où sauter
clearParents booléen Effacer les données des parents, utiliser avec goTo()

Panneau de configuration HTTP

Avec le panneau de contrôle, vous pouvez vous connecter, commencer, arrêter ou redémarrer le bot et surveiller les journaux.

Définissez votre username et password pour accéder à votre panneau de contrôle dans le fichier ./src/config.js

export const chatbotOptions = {
  httpCtrl: {
    port: 3000, // httpCtrl port (http://localhost:3000/)
    username: "admin",
    password: "chatbot"
  }
};

Utiliser un proxy inverse nginx pour exposer publiquement le panneau de configuration http (exemple de configuration).

Http Control Panel

Exemples

Modifier votre dossier ./src/conversations/conversation.js et créez votre workflow de conversation personnalisé.

Exemple 1

doc/examples/conversation1.js

import { buttons } from "../helpers";

/**
 * Chatbot conversation flow
 * Example 1
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /hello|hi|howdy|good day|good morning|hey|hi-ya|how are you|how goes it|howdy\-do/,
    message: "Hello! Thank you for contacting me, I am a Chatbot 🤖 , we will gladly assist you.",
    description: "Can I help with something?",
    buttons: buttons([
      "Website",
      "Linkedin",
      "Github",
      "Donate",
      "Leave a Message",
    ]),
  },
  {
    id: 2,
    parent: 1, // Relation with id: 1
    pattern: /website/,
    message: "Visit my website and learn more about me!",
    link: "https://jordifernandes.com/",
    end: true,
  },
  {
    id: 3,
    parent: 1, // Relation with id: 1
    pattern: /linkedin/,
    message: "Visit my LinkedIn profile!",
    link: "https://www.linkedin.com/in/jfadev",
    end: true,
  },
  {
    id: 4,
    parent: 1, // Relation with id: 1
    pattern: /github/,
    message: "Check my Github repositories!",
    link: "https://github.com/jfadev",
    end: true,
  },
  {
    id: 5,
    parent: 1, // Relation with id: 1
    pattern: /donate/,
    message: "A tip is always good!",
    link: "https://jordifernandes.com/donate/",
    end: true,
  },
  {
    id: 6,
    parent: 1, // Relation with id: 1
    pattern: /leave a message/,
    message: "Write your message, I will contact you as soon as possible!",
  },
  {
    id: 7,
    parent: 6, // Relation with id: 6
    pattern: /.*/, // Match with all text
    message: "Thank you very much, your message will be sent to Jordi! Sincerely the Chatbot 🤖 !",
    end: true,
  },
];

Exemple 2

doc/examples/conversation2.js

import { buttons, remoteTxt, remoteJson } from "../helpers";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 2
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/,
    message: "Hello! I am a Delivery Chatbot.",
    description: "Choice one option!",
    buttons: buttons([
      "See today's menu?",
      "Order directly!",
      "Talk to a human!",
    ]),
  },
  {
    id: 2,
    parent: 1, // Relation with id: 1
    pattern: /menu/,
    message: remoteTxt(`${customEndpoint}/menu.txt`),
    // message: remoteJson(`${customEndpoint}/menu.json`)[0].message,
    end: true,
  },
  {
    id: 3,
    parent: 1, // Relation with id: 1
    pattern: /order/,
    message: "Make a order!",
    link: `${customEndpoint}/delivery-order.php`,
    end: true,
  },
  {
    id: 4,
    parent: 1, // Relation with id: 1
    pattern: /human/,
    message: "Please call the following WhatsApp number: +1 206 555 0100",
    end: true,
  },
];

Exemple 3

doc/examples/conversation3.js

import fetch from "sync-fetch";
import { remoteImg } from "../helpers";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 3
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "Hello! I am a Delivery Chatbot. Send a menu item number!",
  },
  {
    id: 2,
    parent: 0, // Same parent (send reply id=1 and id=2)
    pattern: /.*/, // Match all
    image: remoteImg(`${customEndpoint}/menu.jpg`),
  },
  {
    id: 3,
    parent: 1, // Relation with id: 1
    pattern: /\d+/, // Match any number
    message: "You are choice item number $input. How many units do you want?", // Inject input value ($input) in message
  },  
  {
    id: 4,
    parent: 2, // Relation with id: 2
    pattern: /\d+/, // Match any number
    message: "You are choice $input units. How many units do you want?",
    // Inject custom code or overwrite output 'message' property before reply
    beforeReply(from, input, output, parents) {
      // Example check external api and overwrite output 'message'
      const response = fetch(
        `${customEndpoint}/delivery-check-stock.php/?item=${input}&qty=${parents.pop()}`
      ).json();
      return response.stock === 0
        ? "Item number $input is not available in this moment!"
        : output;
    },
    end: true,
  },
];

Exemple 4

doc/examples/conversation4.js

import { remoteImg } from "../helpers";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 4
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "Image local and remote! Send [local] or [remote]",
  },
  {
    id: 2,
    parent: 1,
    pattern: /local/, 
    image: "./images/image1.jpg",
    end: true,
  },
  {
    id: 3,
    parent: 1,
    pattern: /remote/, 
    image: remoteImg(`${customEndpoint}/image1.jpg`),
    end: true,
  },
];

Exemple 5

doc/examples/conversation5.js

import { remoteImg } from "../helpers";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 5
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "Audio local and remote! Send [local] or [remote]",
  },
  {
    id: 2,
    parent: 1,
    pattern: /local/, 
    audio: "./audios/audio1.mp3",
    end: true,
  },
  {
    id: 3,
    parent: 1,
    pattern: /remote/, 
    audio: remoteAudio(`${customEndpoint}/audio1.mp3`),
    end: true,
  },
];

Exemple 6

doc/examples/conversation6.js

import fetch from "sync-fetch";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 6
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "",
    // Inject custom code or overwrite output 'message' property before reply
    beforeReply(from, input, output, parents) {
      // Get reply from external api and overwrite output 'message'
      const response = fetch(`${customEndpoint}/ai-reply.php/?input=${input}`).json();
      return response.message;
    },
    end: true,
  },
];

Exemple 7

doc/examples/conversation7.js

import fetch from "sync-fetch";

const customEndpoint = "https://jordifernandes.com/examples/chatbot";

/**
 * Chatbot conversation flow
 * Example 7
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "Hello!",
    // Inject custom code after reply
    afterReply(from, input, parents) {
      // Send WhatApp number to external api
      const response = fetch(`${customEndpoint}/number-lead.php/`, {
        method: "POST",
        body: JSON.stringify({ number: from }),
        headers: { "Content-Type": "application/json" },
      }).json();
      console.log('response:', response);
    },
    end: true,
  },
];

Exemple 8

doc/examples/conversation8.js

import { buttons, inp } from "../helpers";

/**
 * Chatbot conversation flow
 * Example 8
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/,
    message: "Choice one option",
    description: "choice option:",
    buttons: buttons(["Option 1", "Option 2"]),
  },
  {
    id: 2,
    parent: 1,
    pattern: /.*/,
    message: "We have received your request. Thanks.\n\n",
    beforeReply(from, input, output, parents) {
      output += `Your option: ${inp(2, parents)}`;
      return output;
    },
    forward: "[email protected]", // default number or empty
    beforeForward(from, forward, input, parents) { // Overwrite forward number
      switch (inp(2, parents)) { // Access to replies inputs by id
        case "option 1":
          forward = "[email protected]";
          break;
        case "option 2":
          forward = "[email protected]";
          break;
        default:
          forward = "[email protected]";
          break;
      }
      return forward;
    },
    end: true,
  },
];

Exemple 9

doc/examples/conversation9.js

/**
 * Chatbot conversation flow
 * Example 9
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /.*/, // Match all
    message: "",
    // Inject custom code or overwrite output 'message' property before reply
    beforeReply(from, input, output, parents, media) {
      if (media) {
        console.log("media buffer", media.buffer);
        return `You send file with .${media.extension} extension!`;
      } else {
        return "Send a picture please!";
      }
    },
    end: true,
  },
];

Exemple 10

doc/examples/conversation10.js

import { promises as fs } from "fs";

/**
 * Chatbot conversation flow
 * Example 10
 */
 export default [
  {
    id: 1,
    parent: 0,
    pattern: /\b(?!photo\b)\w+/, // different to photo
    message: `Write "photo" for starting.`,
  },
  {
    id: 2,
    parent: [0, 1],
    pattern: /photo/,
    message: `Hi I'm a Chatbot, send a photo(s)`,
  },
  {
    id: 3,
    parent: 2,
    pattern: /\b(?!finalize\b)\w+/, // different to finalize
    message: "",
    async beforeReply(from, input, output, parents, media) {
      const uniqId =  (new Date()).getTime();
      // Download media
      if (media) {
        const dirName = "./downloads";
        const fileName = `${uniqId}.${media.extension}`;
        const filePath = `${dirName}/${fileName}`;
        await fs.mkdir(dirName, { recursive: true });
        await fs.writeFile(filePath, await media.buffer);
        return `Photo download successfully! Send another or write "finalize".`;
      } else {
        return `Try send again or write "finalize".`;
      }
    },
    goTo(from, input, output, parents, media) {
      return 3; // return to id = 3
    },
  },
  {
    id: 4,
    parent: 2,
    pattern: /finalize/,
    message: "Thank's you!",
    end: true,
  },
];

Exemple 11

doc/examples/conversation11.js

import { inp, med } from "../helpers";
import { promises as fs } from "fs";

const menu = "Menu:\n\n" +
  "1. Send Text\n" +
  "2. Send Image\n";

/**
 * Chatbot conversation flow
 * Example 11
 */
export default [
  {
    id: 1,
    parent: 0,
    pattern: /\/admin/,
    from: "[email protected]", // only respond to this number
    message: menu
  },
  {
    id: 2,
    parent: [1, 5],
    pattern: /.*/,
    message: "",
    async beforeReply(from, input, output, parents, media) {
      switch (input) {
        case "1":
          return `Write your text:`;
        case "2":
          return `Send your image:`;
      }
    },
  },
  {
    id: 3,
    parent: 2,
    pattern: /.*/,
    message: `Write "/save" to save or cancel with "/cancel".`,
  },
  {
    id: 4,
    parent: 3,
    pattern: /\/save/,
    message: "",
    async beforeReply(from, input, output, parents, media) {
      let txt = "";
      let img = null;
      let filePath = null;
      const type = inp(2, parents);
      if (type === "1") {
        txt = inp(3, parents);
      } else if (type === "2") {
        img = med(3, parents); // media from parent replies
      }
      if (img) {
        const uniqId = new Date().getTime();
        const dirName = ".";
        const fileName = `${uniqId}.${img.extension}`;
        filePath = `${dirName}/${fileName}`;
        await fs.writeFile(filePath, await img.buffer);
      } else {
        const uniqId = new Date().getTime();
        const dirName = ".";
        const fileName = `${uniqId}.txt`;
        await fs.writeFile(filePath, txt);
      }
      return `Ok, text or image saved. Thank you very much!`;
    },
    end: true,
  },
  {
    id: 5,
    parent: 3,
    pattern: /\/cancel/,
    message: menu,
    goTo(from, input, output, parents, media) {
      return 2;
    },
    clearParents: true, // reset parents
  },
];

Advancé

Plusieurs flux de conversation

Éditer ./src/main.js fichier.

import { session } from "./core";
import info from "./conversations/info";
import delivery from "./conversations/delivery";

session("chatbotSession", info);
session("chatbotSession", delivery);

Comptes multiples

Éditer ./src/main.js fichier.

import { session } from "./core";
import commercial from "./conversations/commercial";
import delivery from "./conversations/delivery";

session("commercial_1", commercial);
session("commercial_2", commercial);
session("delivery", delivery);

Éditer ./src/httpCtrl.js fichier.

import { httpCtrl } from "./core";

httpCtrl("commercial_1", 3000);
httpCtrl("commercial_2", 3001);
httpCtrl("delivery", 3002);

Accès au client Venom

Éditer ./src/main.js fichier.

import { session } from "./core";
import conversation from "./conversations/conversation";

// Run conversation flow and return a Venom client
const chatbot = await session("chatbotSession", conversation);

Planifier des tâches

Éditer ./src/main.js fichier.

import schedule from "node-schedule"; // Add node-schedule in your project
import { session, log } from "./core";
import { jobsOptions } from "./config";
import conversation from "./conversations/conversation";

// Run conversation flow and return a Venom client
const chatbot = await session("chatbotSession", conversation);

const job1 = schedule.scheduleJob(
  jobsOptions.job1.rule, // "*/15 * * * *"
  async () => {
    // custom logic example
    await chatbot.sendText("[email protected]", "test");
  }
);

Essai

Les tests unitaires écrivent avec Est

$ yarn test

Testez votre structure de tableau de flux de conversation avec conversation.test.js fichier comme exemple.

$ yarn test src/conversations/conversation  

Dépannage

Attention: Ne vous connectez pas à WhatsApp Web avec le même compte que celui utilisé par le chatbot. Cela empêchera le chatbot d'entendre les messages.

Attention: Vous avez besoin d'un compte WhatsApp pour le chatbot et d'un compte différent pour pouvoir lui parler.

Dépôt

🤖 Avec ce micro framework node.js utilisant Venom Bot sous le capot, vous pouvez facilement créer un Chatbot WhatsApp. Vous n'aurez qu'à éditer votre flux de conversation dans un seul fichier.
https://github.com/jfadev/jfa-whatsapp-chatbot
8 fourches.
39 étoiles.
1 questions ouvertes.

commits récents:
  • Améliorez le venin 5.0.1, jfadev
  • Améliorez le venin 5.0.0, jfadev
  • fic documentaire, jfadev
  • Ajouter med à partir des propriétés et des correctifs clearParents, jfadev
  • Réparer, jfadev

: Robot, Chatbot, Docker, Framework, JS, Node.js, WhatsApp

Services

  • Excel2chatGPT $10.00
  • Bot Tok $45.00
  • Correction de bogues dans votre application PHP Symfony $70.00 / heure
  • Correction de bogues de votre site Wordpress $70.00 / heure
  • Automatisation des tâches à l'aide de Node.js $70.00 / heure

Blog

  • Comment payer avec une carte bancaire sur Cryptomus
  • Guide complet du débutant sur le tok bot: Commandes de terminal expliquées
  • Meilleur site pour gagner des vues sur TikTok
  • Chatbot Jfa Whatsapp
  • Bot pour TikTok

Explorer

  • Gratuit 10 J’aime Tiktok
  • Vues TikTok 2K gratuites
  • Gratuit 100 Favoris TikTok
  • Gratuit 300 Partages TikTok
  • Acheter des vues TikTok
  • Gratuit 100 J'aime Instagram
Twitter
LinkedIn
Youtube
GitHub

© 2013-2025 Jordi Fernandes Alves (@jfadev)