+1

Chatbot Trò chơi Địa Lý - Đoán hình lá cờ quốc gia

Mayfest2023

Hoàn cảnh

Thời học cấp 3, Địa Lý có phải môn học yêu thích của bạn không? Đối với mình thì KHÔNG PHẢI, vì nó khó vch và cái gì khó quá thì mình không thích. Đó là suy nghĩ thời cấp 3, còn bây giờ thì mình có thích Địa Lý, vì mình thích đi đây đi đó, thích đi phượt ngắm cảnh mọi nơi. Cũng đi được một vài nơi trên Việt Nam mình rồi như Hà Giang, Cao Bằng, Lai Châu, Phú Thọ, Hải Phòng, Nam Định, Ninh Bình, Sài Gòn, Vũng Tàu, ... Sau mỗi chuyến đi thì mình có thêm những kỷ niệm, những người bạn, những kiến thức thú vị về mỗi vùng miền.

Nhưng ngoài trên Việt Nam, mình chưa đi được các nước nào trên Thế Giới, và vì thế, mình bắt đầu tìm hiểu Địa Lý các nước trên Thế Giới. Ôi, nhiều quốc gia vch (khoảng 232 quốc gia)

Và mình có ý tưởng làm 1 chatbot về các quốc gia trên thế giới. Mục đích sẽ là giúp mình hỗ trợ về tìm hiểu thông tin các quốc gia. Mình gắn với 1 trò chơi đó là đoán cờ các quốc gia để hứng thú hơn.

Ok, cùng mình tìm hiểu nào =))

Cách thực hiện

Có 2 vẫn đề cần xử lý: Crawl dữ liệu các quốc gia (nguồn từ Wikipedia) và xử lý luồng Chatbot đối với chức năng đoán cờ quốc gia

Crawl dữ liệu quốc gia

Dữ liệu sẽ lấy từ nguồn Wikipedia, sau khi lấy dữ liệu về thì sẽ được lưu ở mysql để Chatbot có thể lấy ra xử lý được

Đầu tiên, danh sách các quốc gia thì lấy tại link: https://vi.wikipedia.org/wiki/Danh_sách_quốc_gia_theo_dân_số

Từ link danh sách trên, mình lấy được link chi tiết của từng quốc gia, và sẽ lấy được dữ liệu chi tiết của các quốc gia đó.

Công cụ sử dụng mình để crawl dữ liệu ở đây mình dùng python + BeautifulSoup

Lấy danh sách các quốc gia:

Như ảnh trên, ta thấy mỗi quốc gia được đặt trong thẻ tr trong table có class là wikitable. Nên ta có đoạn code dưới đây để lấy ra list các quốc gia:

link_list_country = 'https://vi.wikipedia.org/wiki/Danh_s%C3%A1ch_qu%E1%BB%91c_gia_theo_d%C3%A2n_s%E1%BB%91'
res = requests.get(link_list_country)
data = res.text

soup = BeautifulSoup(data, features="lxml")
countries = soup.find('table', {"class": "wikitable"}).findAll('tr')

Lấy các thông tin chi tiết của một quốc gia: Trong ứng dụng này mình chỉ làm trò chơi đoán cờ quốc gia, nhưng mình muốn lấy hết các thông tin cơ bản của một quốc gia để phục vụ mục đích có thể phát triển thêm các chức năng sau này.

Các thông tin mình sẽ lấy:

  • Quốc kỳ
  • Quốc huy
  • Location trên thế giới
  • Diện tích
  • Dân số
  • Thủ đô
  • Timezone

def get_detail_country(link_country):
	res = requests.get(link_country)
	html = res.text
	soup = BeautifulSoup(html, features="html.parser")

	table_info = soup.find('table', {"class": "infobox"})
	

	find_flag = table_info.find('div', {"style": "display:table; width:100%;"})

	if find_flag is None:
		find_flag = table_info.find('table', {"style": "width: 100%; background-color: none; text-align: center"})
	
	list_img = table_info.findAll('img')

	link_flag = 'https:' + list_img[0]['src']
	link_coat_of_arms = 'https:' + list_img[1]['src']
	link_location = 'https:' + list_img[2]['src']

	trs = table_info.findAll('tr')

	area = ''
	population = ''
	capital = ''
	timezone = ''
	for tr in trs:
		if tr.find('th'):
			th = tr.find('th').getText()
			th = th.replace(' ', ' ').replace('• ', '')
			# print(th)
			if tr.find('td') and tr.find('td').getText() != None:
				td = tr.find('td').getText()
				if th == 'Diện tích' or th == 'Tổng cộng':
					split_td = td.split(' km2')
					split_td = split_td[0].split(' km²')
					area = split_td[0]

				if 'Điều tra' in th or 'Dân số ước lượng' in th:
					split_td = td.split(' người')
					split_td = split_td[0].split('[')
					population = split_td[0].strip()

				if th == 'Thủ đô':
					if tr.find('td').find('a'):
						capital = tr.find('td').find('a').getText()
					else:
						capital = tr.find('td').getText()

				if th == 'Múi giờ':
					timezone = td

	return (link_country, link_flag, link_coat_of_arms, link_location, area, population, capital, timezone)

Các dữ liệu được nằm trong table có class là infobox Và mỗi trường dữ liệu được đặt trong mỗi dòng của table trên. Mình dùng nội dung của thẻ th để check xem dữ liệu đó của trường gì. VD: th == 'Thủ đô' --> dòng này là dữ liệu về Thủ đô

Lấy được các giá trị rồi thì chỉ việc lưu lại và database thôi.

Full code ở đây nhé: https://github.com/south1907/knowledge-chatbot/blob/master/python/country/main.py

Xử lý Chatbot

Mình có khProjectung (PHP - Laravel) xử lý tương tác với Facebook Messenger rồi https://github.com/south1907/knowledge-chatbot Project này đã xử lý việc nhận, trả lời tin nhắn Facebook Messenger. Vậy trong bài viết này, mình sẽ chỉ giới thiệu phần tích hợp Trò chơi Địa Lý - Đoán cờ quốc gia này.

Dữ liệu được lưu ở bảng countries

database.png

Khi người dùng chat "start" thì hiểu là bắt đầu trò chơi

if ($query['type'] == 'text') {
    $message = mb_strtolower($query['content']);

    if (strpos($message, 'start') !== false) {
        $result = self::getStart();
    }
}
private static function getStart(): array
{
    $result = [];
    $result[] = [
        'id'	=>	null,
        'type'	=>	'text',
        'message'	=>	'Hello, I am Dialy. Try guess flag of countries'
    ];
    $numberResult = 4;
    $findRandom = Country::get()->random($numberResult);

    if ($findRandom) {
        $rand = rand(0, $numberResult - 1);
        $item = $findRandom[$rand];

        $result[] = [
            'id'	=>	null,
            'type'	=>	'image',
            'url'	=>	$item['link_flag']
        ];
        $listReply = [];
        foreach ($findRandom as $country) {
            $flagCheck = "FALSE";
            if ($country['id'] == $item['id']) {
                $flagCheck = "TRUE";
            }
            $listReply[] = [
                "content_type"		=> "text",
                "title"		=> $country["name"],
                "payload"	=> "DIALY::answer|" . $flagCheck . "|" . $item['id']
            ];
        }
        $result[] = [
            'id'	=>	null,
            'type'	=>	'quick_reply',
            'message'	=>	'What is the flag of country?',
            'quick_replies' => json_encode($listReply)
        ];

    }
    return $result;
}

Logic xử lý sẽ là lấy radom ra 4 quốc gia bất kỳ, sau đó thì lấy 1 quốc gia trong 4 quốc gia đó là Đáp án, các phương án sẽ dùng 4 quốc gia đó để tạo.

Mỗi phương án trả lời sẽ có gắn 1 giá trị payload kiểu DIALY::answer|TRUE|1 để có thể nhận biết User đã click vào câu trả lời nào, câu trả lời đấy đúng hay sai (TRUE), câu trả lời đó của quốc gia có id nào (1)

Khi User click trả lời sẽ có payload DIALY::answer|TRUE|1, tách các phần ra và so sánh để đưa ra quyết định:

  • Nếu TRUE --> đáp án đó đúng
  • Nếu FALSE --> đáp án đó sai
if (strpos($payload, 'DIALY::answer') !== false) {
    $checkAnswer = explode("|", $payload)[1];
    $idCountryCorrect = explode("|", $payload)[2];
    $country = Country::find($idCountryCorrect);
    if ($checkAnswer == 'TRUE') {
        $rand = array_rand($random_string_correct);
        $mes = $random_string_correct[$rand];
        $result = [
            [
                'id'	=>	null,
                'type'	=>	'text',
                'message'	=>	$mes
            ]
        ];
    } else {
        $rand = array_rand($random_string_not_correct);
        $mes = $random_string_not_correct[$rand];
        $result = [
            [
                'id'	=>	null,
                'type'	=>	'text',
                'message'	=>	$mes
            ]
        ];
        $result[] = [
            'id'	=>	null,
            'type'	=>	'text',
            'message'	=>	'Đáp án đúng là ' . $country['name']
        ];
    }

    $result[] = [
        'id'	=>	null,
        'type'	=>	'button',
        'message'	=>	'More information',
        'buttons' => json_encode([
            [
                "type"		=> "web_url",
                "title"		=> "View more",
                "url"	=> $country['link_country']
            ],
            [
                "type"		=> "postback",
                "title"		=> "New game",
                "payload"	=> "DIALY::start"
            ]
        ])
    ];
}

Ez phải không 😃)

Link full phần xử lý Chatbot: https://github.com/south1907/knowledge-chatbot/blob/master/app/Helpers/DialyHelper.php

Demo

Video demo: https://youtu.be/4ItkYR8mAmI

Link Bot: https://www.facebook.com/messages/t/116626974651353

Kết luận

Trên đây, mình mới chỉ phát triển chức năng đoán lá cờ quốc gia thôi, hướng phát triển sẽ làm các chức năng xoay quanh các dữ liệu thu thập được như: hỏi quốc huy, vị trí này trên bản đồ thế giới thì là nước nào, hỏi thủ đô,...

Các bạn có góp ý thêm gì mình không =)) để lại comment nhé ♥️♥️♥️

À, 1 phút dành cho quảng cáo: Mình nhận làm các tool liên quan đến:

  • Crawl dữ liệu
  • Tool auto marketing: tự động post bài, tự động comment, like, chat,...
  • Tool auto cho các thao tác thực hiện lặp đi lặp lại trên website: quản lý thêm xóa sử, đặt hàng,...

Liên hệ qua facebook: Nam

Link source code: https://github.com/south1907/knowledge-chatbot


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.