Lag en superteit chattebot med Socket.IO

Lær hvordan du kommuniserer over WebSockets med NodeJS, Express og Socket.IO.

Lær å bygge en veldig enkelt chatbot med Socket.IO, NodeJS, Express og JQuery.
Lær å bygge en veldig enkelt chatbot med Socket.IO, NodeJS, Express og JQuery.Vis mer

Chatteboter er så populære for tida, atte hjælp. Firmaer ansetter egne robot-trenere, og våre eiere i SOL har utviklet en prisvinnende Google Voice-app, som i prinsippet bare er en fancy chattebot.

Om chatteboter har noe for seg, eller bare er en ny hype? Tja, det er diskutabelt. Det som derimot har noe for seg, er å se på hvordan de funker.

Det morsomme med denne teknologien er at den kan brukes til enkle ting som chattebotter og avanserte systemer som live-paneler og nettverkskommunikasjon i Unity-spill. Du finner sikkert egne måter å utvide eksempelet under, men et sted må man jo starte.

Og vi starter med en chattebot, med WebSockets gjennom rammeverket Sockets.IO.

Prøv chattebotten vi skal bygge under - spør for eksempel hva den beste byen i Norge er, eller om den kan fortelle en vits:

Hva er Websockets?

Her er greia, vi skal bygge en enkel applikasjon hvor en bruker kan stille et spørsmål, og få et svar fra vår chattebot, som egentlig er en enkel webserver.

Det vi ønsker er å ha kontinuerlig kommunikasjon mellom brukeren vår i nettleseren, og serveren som tolker spørsmålene og gir et svar. Når serveren mottar et spørsmål, skal svaret gå tilbake til brukeren i nettleseren så fort som mulig.

Så hvorfor kan vi ikke bare ha et API-endepunkt som tar i mot spørsmålet og gir et svar tilbake, spør du kanskje?

Joda, du har et godt poeng, Sherlock. Men la oss si at chattebotten vår bruker lang tid på sende en respons? Eller at boten vår trenger å sende informasjon i flere omganger?

Si at du spør "Hvor er det best mat i Porsgrunn?". Da vil botten vår kanskje først svare "Jeg skal undersøke". Før den gjør et kall til en bakenforliggende tjeneste og gir et mer fyldig svar, ala "Du bør prøve Michel Seylmagers Hus, og unngå Lilly May". Vi kunne sikkert lagd en liten algoritme som spurte API-endepunktet om den har noe nytt å melde kontinuerlig, om og om igjen, men det hadde ført til bombardering av serveren.

Alternativet er WebSockets, en standard som ble lansert med HTML5 rundt 2011, og støttes av alle de store nettleserne.

WebSockets lar brukeren i nettleseren oppretteholde en kontinuerlig tilkobling til serveren, som gjør at serveren selv kan fortelle når den har ny data å gi brukeren.

Hvorfor Socket.IO?

Men alt dette er gruelig komplisert, så det er flott at noen har bygd et rammeverk som abstraherer vekk alt det vanskelige, og gir oss enkle verktøy å jobbe med.

Socket.IO er en av de lengstlevende rammeverkene for kommunikasjon over Websockets, og en av de absolutt enkleste å komme i gang med. Socket.IOo har riktignok en trøblete historie, spesielt grunnet måten den har håndtert tilbakefall om brukeren ikke har hatt WebSockets. Men i 2019 er Socket.IO tatt inn i varmen, og brukes av alt fra store selskap som Microsoft og Trello, til små startups.

Socket.IO fungerer såre enkelt:

  1. Du spinner opp en Socket.IO-server med NodeJS.
  2. Socket.IO tilbyr automatisk et klient-script på et eget endepunkt i serveren, som du inkluderer med script-tag på nettsiden din.
  3. En tilkobling blir automatisk startet når brukeren åpner nettsiden.
  4. Både server og klient kan nå lytte til og sende beskjeder kontinuerlig.
  5. Du velger selv om beskjeder skal sendes kun til én bruker, et utvalg brukere, eller alle brukere som er tilkoblet.

Server-implementasjon

Er du utålmodig og vil bare dra ned det ferdige prosjektet? Du finner det her.

Det er ikke mye som kreves for å komme i gang.

Først trenger du NodeJS og pakketjenesten Yarn installert på maskinen din. Opprett en mappe for koden din, gå i din favoritt-terminal, og skriv yarn init. Trykk enter på alle valgene. Så skriver du yarn add express socket.io for å legge til bibliotekene du trenger. De legger seg forøvrig under mappen \node_modules i kodemappen din.

Så oppretter du filen index.js, og limer inn koden under:

var express = require("express");
var app = express();
var path = require("path");
var http = require("http").Server(app);
var io = require("socket.io")(http);

app.use(express.static(path.join(__dirname, "client"))); // gjør statiske filer som stilarket tilgjengelig

app.get("*", (req, res) => {
  // gjør index.html tilgjengelig på alt av server-urler
  res.sendFile(path.join(__dirname + "/client/index.html"));
});

// verdens dummeste "AI"
function parseQuestion(question) {
  if (question.indexOf("beste by") > -1) {
    return "Porsgrunn er Norges beste by";
  }
  if (question.indexOf("er du") > -1) {
    return "Jeg er en chattebot";
  }
  if (question.indexOf("vits") > -1) {
    return "Hva får du hvis du kloner en sjørøver? Svar: En piratkopi";
  }
  if (question.indexOf("addresse") > -1) {
    return "kode24.no, seffern";
  }
  return "Aner ikke..";
}
// alle brukere kobler til og får en egen socket id og objekt
io.on("connection", function(socket) {
  // lytt etter beskjeden "chat message"
  socket.on("chat message", function(message) {
    setTimeout(() => {
      var reply = parseQuestion(message);

      //send ut en beskjed til brukeren
      socket.emit("chat reply", reply);
    }, 1000); // lat som vi bruker et sekund på å svare
  });
});

http.listen(process.env.PORT || 3000, function() {
  console.log("listening on port 3000");
});

Det fungerer sånn her:

  • Du legger kanskje merke til at vi importerer både Express og Socket.io på de første linjene. Det er en enkel grunn til det. Vi skal bruke Express som webrammeverk, og da må vi fortelle Express at den skal tilgjengeliggjøre websiden vår, og eventuelle filer som stilark. Deretter hekter vi Socket.IO på, slik at begge kan kjøre på samme server og port.
  • linje 7 sier vi at vi skal ha en mappe som heter client, og inni den mappa skal det ligge noen statiske filer som brukeren trenger å få tilgang til, i vårt tilfelle stilarket til nettsiden vår.
  • linje 9 spesifiserer vi at alle HTTP-forespørseler til serveren (*) skal føre til websiden vår index.html - om ikke en annen statisk fil er funnet på adressen. Det vil si at hvis brukeren går til localhost:3000/blablabal treffer hun fortsatt på index.html.
  • linje 31-41 er socket.io-implementasjonen vår. Alle brukere som kobler seg til ramler inn her, med et socket-objekt tilknyttet. Serveren vår lytter på beskjeden "chat message", og sender beskjeden "chat reply" tilbake. En Socket.IO-beskjed består av en beskjed, og en variabel, som kan være et objekt, en streng, array eller hva slags data du ønsker å sende. I vårt tilfelle sender vi bare en enkel streng. Svaret på spørsmålene fra brukeren får vi ved å sende spørsmålet til funksjonen parseQuestion, som bare returnerer en svarstreng.
  • Til slutt starter vi serveren vår på linje 43-45. process.env.PORT || 3000, betyr rett og slett at vi skal starte serveren på port 3000 hvis ikke noe annet er spesifisert på kommandolinjen. Tjenester som Heroku dikterer selv hvilken port serveren skal kjøres på i deres tjeneste, derfor er denne linjen nødvendig.

Frontend-implementasjon

I frontend-implementasjonen gjør vi det superenkelt og bruker jQuery. Jada, vi vet at jQuery ikke er så kult lenger. Men til små kodeeksempler funker det fortsatt ganske bra. Opprett mappen client i prosjektmappen din, og opprett filene index.html og style.css.

I index.html limer du inn denne koden:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="x-ua-compatible" content="ie=edge, chrome=1" />
    <title>Example client</title>
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
    <div id="chat-example">
      <div id="messages"></div>
      <form id="input-form">
        <input
          type="text"
          id="input-text"
          name="input-text"
          placeholder="Spør om noe?"
        /><button id="input-form-button">
          Spør
        </button>
      </form>
    </div>

    <script src="https://code.jquery.com/jquery-3.4.1.js"></script>
    <script src="/socket.io/socket.io.js"></script>

    <script>
      $(function() {
        var socket = io();
        $("#input-form").submit(function(e) {
          e.preventDefault(); // stopp formen fra å laste siden på nytt
          // inputfeltet skal ikke være tomt
          if ($("#input-text").val()) {
            socket.emit("chat message", $("#input-text").val()); // send beskjed
            addMessage($("#input-text").val()); // legg til beskjede i chatboksen
            $("#input-text").val(""); // nullstill input-felt
            return false;
          }
        });

        socket.on("chat reply", function(message) {
          addReply(message);
        });

        function addMessage(message) {
          // legg til vår beskjed som et element
          $(
            "#messages"
          ).append(`<div class="message-container me"><div class="message">
              <p class="message-text">${message}</p>
            </div></div>`);
        }

        function addReply(message) {
          // legg til svaret fra serveren som et element
          $(
            "#messages"
          ).append(`<div class="message-container reply"><div class="message reply">
              <p class="message-text">${message}</p>
            </div></div>`);
        }
      });
    </script>
  </body>
</html>
  • linje 25 henter vi frontend-scriptet til socket.io, fra serveren vår.
  • Med jQuery lytter vi på om noen sender inn skjemaet fra siden vår, altså trykker på spør-knappen. Via socket-objektet vårt sender vi en beskjed til serveren. Legg merke til at beskjeden socket.emit("chat message" korresponderer med socket.on("chat message" linje 33 i serveren vår.
  • linje 41 lytter vi til svar fra serveren vår med beskjeden "chat reply". Dessuten har vi to funksjoner for å legge til svarene i nettsiden vår.

Og det var det. Eneste som mangler er litt stiling, som du finner her.

Start serveren

Det eneste som gjenstår nå er å starte serveren. Her anbefaler vi å åpne filen package.json, som ble opprettet i prosjektet ditt da du skrev yarn init. legg inn scripts-koden, slik som i kodesnutten under:

{
  "name": "kode24-chattebot",
  "version": "1.0.0",
  "main": "index.js",
  "repository": "git@github.com:kode24-kode/kode24-chattebot.git",
  "author": "Jørgen Jacobsen <jorgen@kode24.no>",
  "license": "MIT",
  "dependencies": {
    "express": "^4.16.4",
    "socket.io": "^2.2.0"
  },
  "scripts": {
    "start": "node index.js"
  }
}

Til slutt er det bare å hoppe tilbake i terminalen din, skrive yarn start, og så besøke ditt nye underverk i nettleseren på http://localhost:3000.

Har du ideer til andre enkle guider? Har du et forslag til endring i denne guiden? Ta kontakt!

Og ikke minst, vil du laste ned hele kildekoden går du til kode24 sin Github-konto.