Maak je eigen WhatsApp bot met Python

In het dagelijks leven ben ik als Java programmeur voornamelijk bezig met zware enterprise applicaties in een complex landschap. Het is dan makkelijk om het zicht te verliezen op de compleet andere kant van het code-spectrum, de flexibele en lichtgewicht scripts. Juist vanwege deze tegenstelling ben ik eens in Python-scripting gedoken om dit eens uit te diepen.

Het idee is om een WhatsApp bot te maken die je verschillende vragen kan stellen, terwijl het script op de achtergrond deze informatie bij elkaar zoekt. In dit voorbeeld heb ik 4 “vragen” opgezet:

  • “quote”: stuurt een willekeurige quote
  • “kat”: stuurt een willekeurige foto van een kat
  • “wiki” + <input>: geeft de wikipagina terug van je zoekterm
  • “vacatures”: geeft de beschikbare vacatures terug van Conspect

Met bovenstaande vragen oefen je met het verwerken van respectievelijk: tekst, media, user input en HTML scraping (bepaalde informatie uit een HTML-pagina selecteren)
Als kers op de taart heb ik alle code laten reviewen door ChatGTP in de rol van senior Python ontwikkelaar.

Vereiste onderdelen:

  • Een Twilio account met een gekoppelde telefoon
  • Python 3.9 of nieuwer
  • Je favoriete IDE
  • Flask: voor het opzetten van een web app dat de berichten verwerkt
  • Ngrok: om de flask app door te routeren naar het open internet

Stap 1: Voorbereiding

Zorg dat Python 3.9+ beschikbaar is via de command line.
Twilio is de link tussen WhatsApp en wat er uiteindelijk naar je script gestuurd wordt via de ngrok server.
Maak een account aan op https://www.twilio.com/whatsapp en koppel je telefoon:

Messaging / Try it out / Send WA message.

Stap 2: Installatie

Maak een Python environment aan met daarin de complete omgeving de je nodig hebt om te app te runnen.
Dit is een best-practice vanwege de gedwongen isolatie van de verschillende frameworks binnen je projecten.

Maak een omgeving (env) aan: mkdir python-bot && cd python-bot

Draai en activeer de env: python3 -m venv python-bot-env && source python-bot-env/bin/activate

Installeer alle benodigde libraries: pip3 install twilio flask requests beautifulsoup4 ngrok

Stap 3: Scripting

Maak een nieuw Python script aan op hetzelfde niveau als python-bot-env genaamd bot.py.

Begin eerst met het importeren van alle benodigde libraries:

				
					from flask import Flask, request
import urllib.request
from bs4 import BeautifulSoup
import requests
from twilio.twiml.messaging_response import MessagingResponse
				
			

Voeg vervolgens de basis toe voor de Flask app:

				
					app = Flask(__name__)

@app.route('/', methods=['POST'])

				
			

Dan kunnen we nu beginnen met de functie waarin het allemaal moet gaan gebeuren.
Het functie genaamd bot() begint met het bericht dat Flask uit WhatsApp haalt genaamd incoming_msg.
Vervolgens wordt de lege response alvast aangemaakt die we straks gaan vullen.
Als laatste hebben we een Boolean die bijhoudt of we het request al hebben afgehandeld.

				
					def bot():
    incoming_msg = request.values.get('Body', '').lower()
    response = MessagingResponse()
    message = response.message()
    responded = False
				
			

Nu is het tijd om de eerste vraag te implementeren, de quotes. Aan de hand van zoekwoord “quote” wordt onderstaande API aangesproken.
Bij een geldige response wordt de JSON response verwerkt en in de message.body gezet. Bij een ongeldige response komt er een foutmelding terug. Als laatste wodt de flag responded = True gezet zodat het duidelijk is dat er geen andere check meer gedaan hoeven worden.

				
					if 'quote' in incoming_msg:
    # return a quote
    r = requests.get('https://api.quotable.io/random')
    if r.status_code == 200:
        data = r.json()
        quote = f'{data["content"]} ({data["author"]})'
    else:
        quote = 'Ik kan geen quotes vinden op dit moment'
    message.body(quote)
    responded = True

				
			

Voor “kat” doen we bijna hetzelfde, alleen sturen we de foto via message.media

				
					if 'kat' in incoming_msg and not responded:
    # return a cat pic
    message.media('https://cataas.com/cat')

    responded = True
				
			

Bij de “wiki” verwerk je de user input, waarbij je alleen de relevatie zoekterm behoudt.
Als bonus geven we (waar nodig) een foutmelding terug in de vorm van de relevante HTTP kattenfoto.

				
					if 'wiki' in incoming_msg and not responded:
    # return a corresponding wiki page
    r = requests.get('https://nl.wikipedia.org/wiki/' + incoming_msg[5:])

    if r.status_code == 200:
        text = str(r.url)
    else:
        text = 'Ik kan geen fatsoenlijke wiki vinden op dit moment, alleen een foutmelding'
        message.media('https://http.cat/' + str(r.status_code))
    message.body(text)

    responded = True

				
			

Bij “vacatures” gebruiken we HTML scraping, hierbij moeten we de pagina doorzoeken naar de juiste elementen.
We gebruiken hierbij een andere library genaamd urllib, deze gaat net iets beter om met complexe pagina’s.

Met BeautifulSoup converteren we de HTML tekst naar een soup. Deze laten we vervolgens zoeken naar alle voorkomens van een <div> element met de class name ‘elementor-button-wrapper’.
Dit resulteert in een lijstje van 10 elementen, waarvan we er maar 4 nodig hebben. Hiervan selecteren we de URLs en geven deze terug in losse berichten.

				
					if 'vacatures' in incoming_msg and not responded:
    # search for jobs
    url = 'https://conspect.nl/vacatures'
    req = urllib.request.Request(url)
    text = urllib.request.urlopen(req).read()

    soup = BeautifulSoup(str(text), 'html.parser')
    functies = soup.findAll('div', {'class':'elementor-button-wrapper'})

    message.body('Alle huidige vacatures bij Conspect')
    for item in functies[3:-3]:
        response.message(url + str(item.find('a')['href']))

    responded = True

				
			

Mocht geen van de vragen getriggerd worden, wordt er een foutmelding teruggegeven.

				
					if not responded:
    message.body('Er gaat iets niet helemaal lekker')

return str(response)
				
			

Tot slot is het noodzakelijk dat we aangeven dat dit bestand als script gedraaid moet worden, maar juist niet als een module. Dat doen we op de volgende manier.

				
					if __name__ == '__main__':
    app.run(debug=True)
				
			

Voeg vervolgens de basis toe voor de Flask app:

Stap 4: Maak je app benaderbaar via het internet

Draai in een los cmd venster ngrok http 5000 om de server aan zetten op poort 5000.

Kijk wat voor URL er staat bij Forwarding en kopieer de URL:

				
					Forwarding        https://d796-80-114-236-171.eu.ngrok.io
				
			

Ga naar https://www.twilio.com/console/sms/whatsapp/sandbox en voer deze URL in bij
Sandbox configuration / When a message comes in en klik op Save.

Stap 5: Draaien van het script

Draai in een los cmd venster je script met Python (in mijn geval python3 op een MacBook):

				
					python3 bot.py
				
			

Je app is nu klaar om via WhatsApp bestuurd te worden!

Koos Drost – Java Consultant

Uitgelicht

Retail

Doormiddel van het inzichtelijk maken van datavraagstukken en het inzetten van BI-oplossingen helpen wij klanten in de retail actuele informatie uit de data te halen.

Lees meer »

Joerie: SME Data Engineering & Reports

De interessante gesprekken met de business voeren is wat mij zeer boeit. Helemaal als ik de klant daardoor goed kan helpen om te sturen op hun bedrijfsproces door middel van het creëren van de juiste inzichten. Het BI werkveld is dynamisch, vol in ontwikkeling en kan ook weer totaal verschillen per organisatie. Als BI consultant sta ik dicht bij de bedrijfsvoering van de klant, gaaf!

Lees meer »

Claudia: SME Agile

Agile is een prachtig framework om als organisatie snel software op te leveren en wendbaar te zijn, het grote voordeel laat zich gelden wanneer het

Lees meer »