The first case I was handed since arriving at NCKU was a LINE chatbot. It was the third (and final) year of the project and it seems like it has been written/handed down by multiple people, and collaborated with many people. So, it's kind of messy. Since I'm personally interested in creating conversational agents, I though I might as well write down some notes in the development process and probably the technologies behind it.

Basic LINE bot Framework

The basic structure of LINE bot is basically using LINE as an interactive platform to connect to your users. Basically, our framework would be using the LINE's messaging API to send the user's request to our web server.

line_messaging_api

LINE Developer

Of course the first part of creating a LINE bot is to have a LINE developer account. For simplicity reasons, we could just register using our LINE account by logging in here. (You could also turn your account to a business account if needed).

create_channel

After logging in, you'll be taken to the providers page. Which takes us to the next step, creating a provider (project) and messaging API channel (chatbot). Do take note of the Channel Secret (Webhook ID), issue a Channel Access Token (API Token) and enable Use Webhooks in the Channel Settings.

Flask - Web App Framework

Flask is a lightweight, easy-to-use Python web framework. Since our framework is based on a web server, using Flask as the backend of the server makes things easier if you're familiar with Python.

Install Dependencies

pip install flask line-bot-sdk

After installing our dependencies, we could start writing our bot. Of course if you're new to Python, you could start of with the example code provided on LINE's GitHub. We won't be explaining the code line-by-line, but only the main concept of the control flow. The below is the code with some small tweaks I made. First thing to take note is to insert the CHANNEL_ACCESS_TOKEN and CHANNEL_SECRET.

# Import 3rd-Party Dependencies
from flask import Flask, escape, request

from linebot import (
    LineBotApi, WebhookHandler
)
from linebot.exceptions import (
    InvalidSignatureError
)
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

# Import system modules
import sys, os

# Initialize Flask
app = Flask(__name__)

# Channel API & Webhook
line_bot_api = LineBotApi('CHANNEL_ACCESS_TOKEN') 
handler = WebhookHandler('CHANNEL_SECRET')

# Listen to all POST requests from HOST/callback
@app.route("/callback", methods=['POST'])
def callback():
    # get X-Line-Signature header value
    signature = request.headers['X-Line-Signature']
    # get request body as text
    body = request.get_data(as_text=True)
    app.logger.info("Request body: " + body)
    # handle webhook body
    try:
        handler.handle(body, signature)
    except InvalidSignatureError:
        print("Invalid signature. Please check your channel access token/channel secret.")
        abort(400)
    return 'OK'
    
# Text message handler (ECHO)
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    # Print event metadata
    print(event)

    # Retreive user metadata
    userid = event.source.user_id
    usermsg = event.message.text
    
    # Log user metadata on terminal
    print(f'User: {userid}')
    print(f'Message: {usermsg}')

    # Reply user
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text)
    )
    
if __name__ == "__main__":
    # Run web service on server port
    port = int(os.environ.get('PORT', 8080))
    app.run(host='0.0.0.0', port=port)

Basically, the most important part of the code here is the # Text message handler section. This is function handles all TextMessages received from the client. There are few types of messages such as StickerMessage, ImageMessage, LocationMessage etc. Once our web server receives a TextMessage event, it would then trigger the following function. The event is basically dictionary (JSON obj) that looks something like this:

{
    "message": {![ngrok](/content/images/2019/09/ngrok.png)
        "id": "10648174705250",
        "text": "This is the message from the client",
        "type": "text"
    },
    "replyToken": "4cf5fbfa805e4bacbfde5d60b2d8f8b6",
    "source": {
        "type": "user",
        "userId": "CLIENT_USER_ID"
    },
    "timestamp": 1569675030774,
    "type": "message"
}

So, we could actually process the message from our client using event.message.text, and use some keywords to trigger another function. Finally, the event.reply_token is used by the API to determine which client to reply, and we could simply reply them with the line_bot_api.reply_message() function, and TextSendMessage() them. Since we are an echo bot, we would just reply them the event.message.text.

ngrok - Webhooks

One final step before we could finally test our bot is to connect our LINE api to our hosting server. Of course, if you have a server available for deployment, or a cloud platform like Heroku, you're basically good to go. But if you need an domain name to connect to, you could use ngrok.

ngrok

Ngrok creates a secured tunnel from your device and gives you a URL for you to connect to your server even if you're not within the local network. Besides that, it is actually very easy to setup. Open the corresponding port and insert the callback link in the Messaging Settings.

webhook