Ứng dụng Python

Simple chat bot facebook với aiohttp

Đăng bởi - Ngày 19-05-2018

Chào các bạn, trong bài hôm nay chúng ta sẽ tạo một chat bot đơn giản, đây là chat bot mình sử dụng trên site Taekwondo Tân Phú . Chat bot của chúng ta sẽ sử dụng aiohttp và facebook api để trả lời những câu hỏi với keywords chúng ta viết trong chương trình.

Chuẩn bị

Bạn hãy tạo một virtual env và cài đặt package sau

pip install aiohttp

Coding

Tiếp theo hãy tạo một file app.py với nội dung như sau

import json
import aiohttp
from os import environ
from aiohttp import web

# fanpage token
PAGE_ACCESS_TOKEN = ''
# verify token
VERIFY_TOKEN = ''

class BotControl(web.View):

    async def get(self):
        query = self.request.rel_url.query
        if(query.get('hub.mode') == "subscribe" and query.get("hub.challenge")):
            if not query.get("hub.verify_token") == VERIFY_TOKEN:
                return web.Response(text='Verification token mismatch', status=403)
            return web.Response(text=query.get("hub.challenge"))
        return web.Response(text='Forbidden', status=403)

    async def post(self):
        data = await self.request.json()
        if data.get("object") == "page":
            await self.send_greeting("Chào bạn. Mình là bot demo của học python.")

            for entry in data.get("entry"):
                for messaging_event in entry.get("messaging"):
                    if messaging_event.get("message"):
                        sender_id = messaging_event["sender"]["id"]
                        message_text = messaging_event["message"]["text"]

                        if any(["chào" in message_text.lower(), "hi " in message_text.lower(),
                                "hello" in message_text.lower(), "có ai" in message_text.lower(),
                                "có ở đó" in message_text.lower(), "hi" == message_text.lower()]):
                            await self.send_message(sender_id, "chào đằng ấy :)")
                        elif any(["bạn tên" in message_text.lower(), "mày tên" in message_text.lower(),
                                "your name" in message_text.lower(), "cậu tên" in message_text.lower()]):
                            await self.send_message(sender_id, "mình tên là bot demo aiohttp nha")
                        elif any(["tác giả" in message_text.lower(), "người viết" in message_text.lower(),
                                "ai viết" in message_text.lower(), "ba mày" in message_text.lower(), "cha mày" in message_text.lower()
                                     , "bố mày" in message_text.lower(), "tía mày" in message_text.lower()]):
                            await self.send_message(sender_id, "ahihi bạn vào đây để xem ai là người tạo ra mình nha :3 https://www.hocpython.com")
                        else:
                            await self.send_message(sender_id, "Bạn dễ thương gì ấy ơi, ghé https://www.hocpython.com để ủng hộ ba mình nha :3 ")
                            await self.send_message(sender_id,
                                              "mình nghe ba mình nói nếu mình được 100 like sẽ chia sẻ với các bạn thêm tính năng mới của mình đó :3")

        return web.Response(text='ok', status=200)

    async def send_greeting(self, message_text):
        params = {
            "access_token": PAGE_ACCESS_TOKEN
        }
        headers = {
            "Content-Type": "application/json"
        }
        data = json.dumps({
            "setting_type": "greeting",
            "greeting": {
                "text": message_text
            }
        })
        async with aiohttp.ClientSession() as session:
            await session.post("https://graph.facebook.com/v3.0/me/thread_settings", params=params, headers=headers, data=data)

    async def send_message(self, sender_id, message_text):

        params = {
            "access_token": PAGE_ACCESS_TOKEN
        }
        headers = {
            "Content-Type": "application/json"
        }
        data = json.dumps({
            "recipient": {
                "id": sender_id
            },
            "message": {
                "text": message_text
            }
        })

        async with aiohttp.ClientSession() as session:
            await session.post("https://graph.facebook.com/v3.0/me/messages", params=params, headers=headers, data=data)



routes = [
    web.get('/', BotControl, name='verify'),
    web.post('/', BotControl, name='webhook'),
]

app = web.Application()
app.add_routes(routes)

if __name__ == '__main__':
    web.run_app(app, host='0.0.0.0', port=environ.get("PORT", 9090))

Tất cả các function của bot chúng ta sẽ đặt trong class BotControl(web.View).Trong class này chúng ta sẽ có 2 coroutine chính là get và post, và một số function khác hỗ trợ việc gửi tin nhắn đến facebook. Chúng ta sẽ cùng xem qua một lượt các function

Đầu tiên là async def get(): function này có nhiệm vụ là làm verify callback với facebook, khi bạn thực hiện submit url tới webhook, facebook sẽ yêu cầu verify token trùng với VERIFY_TOKEN trong chat bot để xác nhận đủ quyền thực hiện webhook

    async def get(self):
        query = self.request.rel_url.query
        if(query.get('hub.mode') == "subscribe" and query.get("hub.challenge")):
            if not query.get("hub.verify_token") == VERIFY_TOKEN:
                return web.Response(text='Verification token mismatch', status=403)
            return web.Response(text=query.get("hub.challenge"))
        return web.Response(text='Forbidden', status=403)

Tiếp đến là async def post(): function này đảm nhận việc gửi tin nhắn đến người dùng khi có ai đó vào fanpage và gửi tin nhắn đến chat bot. Nếu là người dùng mới hoàn toàn tiếp cận fanpage, chat bot sẽ gửi lời chào đến người dùng. Và khi người dùng nhắn tin hỏi chat bot, nếu đúng các từ khóa đã được lập trình trước, chat bot sẽ trả lời theo ý đã được bạn code

    async def post(self):
        data = await self.request.json()
        if data.get("object") == "page":
            await self.send_greeting("Chào bạn. Mình là bot demo của học python.")

            for entry in data.get("entry"):
                for messaging_event in entry.get("messaging"):
                    if messaging_event.get("message"):
                        sender_id = messaging_event["sender"]["id"]
                        message_text = messaging_event["message"]["text"]

                        if any(["chào" in message_text.lower(), "hi " in message_text.lower(),
                                "hello" in message_text.lower(), "có ai" in message_text.lower(),
                                "có ở đó" in message_text.lower(), "hi" == message_text.lower()]):
                            await self.send_message(sender_id, "chào đằng ấy :)")
                        elif any(["bạn tên" in message_text.lower(), "mày tên" in message_text.lower(),
                                "your name" in message_text.lower(), "cậu tên" in message_text.lower()]):
                            await self.send_message(sender_id, "mình tên là bot demo aiohttp nha")
                        elif any(["tác giả" in message_text.lower(), "người viết" in message_text.lower(),
                                "ai viết" in message_text.lower(), "ba mày" in message_text.lower(), "cha mày" in message_text.lower()
                                     , "bố mày" in message_text.lower(), "tía mày" in message_text.lower()]):
                            await self.send_message(sender_id, "ahihi bạn vào đây để xem ai là người tạo ra mình nha :3 https://www.hocpython.com")
                        else:
                            await self.send_message(sender_id, "Bạn dễ thương gì ấy ơi, ghé https://www.hocpython.com để ủng hộ ba mình nha :3 ")
                            await self.send_message(sender_id,
                                              "mình nghe ba mình nói nếu mình được 100 like sẽ chia sẻ với các bạn thêm tính năng mới của mình đó :3")

        return web.Response(text='ok', status=200)

function async def send_greeting(self, message_text): nhận vào một tham số message_text. message_text chính là lời chào của chat bot khi người dùng mới hoàn toàn bấm mở messenger của fanpage lên

    async def send_greeting(self, message_text):
        params = {
            "access_token": PAGE_ACCESS_TOKEN
        }
        headers = {
            "Content-Type": "application/json"
        }
        data = json.dumps({
            "setting_type": "greeting",
            "greeting": {
                "text": message_text
            }
        })
        async with aiohttp.ClientSession() as session:
            await session.post("https://graph.facebook.com/v3.0/me/thread_settings", params=params, headers=headers, data=data)

function async def send_message(self, sender_id, message_text): function này cũng thực hiện chức năng gửi tin nhắn đến người dùng, nhưng nó nhận vào 2 tham số, sender_id là id của người dùng, và message_text là nội dung mà chat bot sẽ gửi tin nhắn đến người dùng

    async def send_message(self, sender_id, message_text):

        params = {
            "access_token": PAGE_ACCESS_TOKEN
        }
        headers = {
            "Content-Type": "application/json"
        }
        data = json.dumps({
            "recipient": {
                "id": sender_id
            },
            "message": {
                "text": message_text
            }
        })

        async with aiohttp.ClientSession() as session:
            await session.post("https://graph.facebook.com/v3.0/me/messages", params=params, headers=headers, data=data)

Chúng ta sẽ khai báo routes đến aiohttp với 2 coroutine vừa tạo ở class BotControl như sau

routes = [
    web.get('/', BotControl, name='verify'),
    web.post('/', BotControl, name='webhook'),
]

Sau đó, chúng ta sẽ khai báo application và add routes vào

app = web.Application()
app.add_routes(routes)

if __name__ == '__main__':
    web.run_app(app, host='0.0.0.0', port=environ.get("PORT", 9090)) #1
  1. Tại dòng này mình lấy port từ enviroment nếu có hoặc không có mình lấy mặc đinh là port 9090

Upload app lên heroku

Để cấu hình app của chúng ta chạy được trên heroku, bạn hãy tạo file requirements.txt với nội dung như sau

aiohttp==3.1.3
gunicorn

Tiếp theo bạn hãy tạo một file mới tên là Procfile với nội dung sau

web: gunicorn app:app --worker-class aiohttp.worker.GunicornWebWorker

Khi bạn dùng gunicorn thì gunicorn sẽ tự run_app, bạn cần phải comment out 2 dòng bên dưới trong file app.py. Nếu bạn vẫn còn để 2 dòng này trong app.py thì gunicorn sẽ không start được

#if __name__ == '__main__':
#    web.run_app(app, host='0.0.0.0', port=environ.get("PORT", 9090))

Sau đó submit tất cả lên heroku và thực hiện bước cuối là kết nối với facebook

Kết nối với Facebook

Ở thời điểm hiện tại mình viết bài này, facebook chỉ support chat bot cho fanpage. Chúng ta cần tạo một app cho facebook, app này sẽ hook tới chat bot của chúng ta. Nếu bạn chưa biết cách tạo app, hãy đọc bài hướng dẫn tạo app mới nhất 2018.

Sau khi tạo app cho con chat bot, chúng ta cần add product messenger vào app vừa tạo, từ bên cột trái bạn nhấn vào Products + > Messenger > Setup

facebook webhook

Bạn kéo xuống kiếm Token Generation và chọn page cần tích hợp chat bot

facebook webhook

Một cửa sổ popup hiện ra yêu cầu bạn cung cấp quyền cho App được tích hợp vào, lúc này chúng ta chưa cần cho facebook review. Bạn chỉ click "Continue as ..." và "Ok" để hoàn tất việc tạo Token

facebook webhook

facebook webhook

Bạn hãy copy Token này và paste vào PAGE_ACCESS_TOKEN trong app.py

# fanpage token
PAGE_ACCESS_TOKEN = ''

Tiếp tục bạn hãy nhấn vào Setup Webhooks, một cửa sổ mới hiện ra, bạn hãy điền vào như trong hình. Sau đó bạn hãy nhấn Verify and Save

  1. Callback URL: bạn sẽ nhập vào địa chỉ dẫn tới chat bot của bạn. Lưu ý rằng url chat bot của bạn phải là giao thức https để có thể hoạt động. Nếu bạn cài đặt bot ở heroku thì hãy copy url chat bot của heroku vào đây, hoặc bạn cài chat bot ở vps thì copy url vào đây. Trường hợp của mình là cài đặt chatbot ở heroku nên url sẽ là https://chatbotaiohttp.herokuapp.com/
  2. Verify Token: Bạn chú ý là đây không phải là Token lấy ở bước trước. Chuỗi token này là chuỗi bạn tự gõ vào. Bạn có thể gõ bằng tay, hoặc vào trang password generator để tạo token hoặc muốn theo cách python như sau. Một điều khác cần lưu ý là verify_token ở đây và trong code chat bot phải giống nhau, nếu không bạn sẽ không verify được
    Python 3.6.5 (default, May 11 2018, 04:00:52) 
    [GCC 8.1.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import string
    >>> from secrets import choice
    >>> ''.join([choice(string.ascii_lowercase + string.ascii_uppercase + string.digits) for letter in range(32)])
    's3Vip8lgh2GEkt60CnmnaZ1SBUYLixlg'
    >>>​
  3. Subscription Fields: chat bot của chúng ta chỉ xài mặc định là message

Bạn hãy copy Verify Token ở trên vào VERIFY_TOKEN trong app.py

# verify token
VERIFY_TOKEN = ''

Vẫn ở mục Webhooks, bạn hãy tìm đoạn này "Select a page to subscribe your webhook to the page events" vào chọn page mà bạn vừa tạo token ở trên để subscribe fanpage với chat bot. Sau đó upload app.py lên host / heroku và restart lại server. Nếu bước này bạn không upload lên và restart thì bạn sẽ gặp lỗi khi nhấn Verify and Save

facebook webhook

facebook webhook

facebook webhook

Vì hiện tại bạn chưa submission cho facebook review, nên bạn chỉ có thể test chatbot với account của bạn thôi. Bạn hãy chuyển status của app từ public về is developer để test nha

Facebook chat bot aiohttp testing

Để có thể submit bot của bạn cho facebook verify bạn hãy theo các bước tại đây. https://developers.facebook.com/docs/messenger-platform/app-review

Bài viết về chat bot facebook với aiohttp đến đây là kết thúc. Mọi ý kiến góp ý của các bạn mình rất hoan nghênh. Các bạn thắc mắc có thể hỏi mình bằng cách comment tại bài viết này hoặc trên group Python Community Viet Nam

Link github: https://github.com/kikyo2006/facebook-chat-bot-aiohttp

Link page test chat bot: https://www.facebook.com/livestreamingpc/

Các thẻ
Bài viết liên quan
0 nhận xét

    Không có nhận xét nào

Nhận xét mới

bắt buộc

yu.kusanagi
Từ Anh Vũ
Hồ Chí Minh, Việt Nam

Xin chào, tôi tên Từ Anh Vũ và là 1 free lancer developer và ngôn ngữ code yêu thích của tôi là Python và PHP. Công việc chủ yếu là viết các module cho magento, magento2, wordpress, django, flask và các framework khác
Nếu bạn muốn trao đổi với tôi hoặc muốn thuê tôi làm việc cho dự án của bạn, hãy liên hệ với tôi

ĐĂNG KÝ NHẬN BÀI MỚI

Tweets gần đây
Tác giả
Feeds
RSS / Atom
ADVERTISING

Đăng ký nhận bài viết mới tại hocpython.com?

Hãy đăng ký nhận bài viết mới tại hocpython.com để:

  • Không bỏ lỡ các bài tutorials mới tại hocpython.com!
  • Cập nhật các công nghệ mới trong python!

Chỉ cần điền email và họ tên của bạn và nhấn Đăng ký nhận tin!