Tạo ChatBot cho ChatWork với API Simsimi và Java

Đầu năm 2016, microsoft đã giới thiệu về Bot framework. Một công cụ sẽ giúp các nhà phát triển những chatbot cho riêng mình. Microsoft cho biết, trong tương lai con người sẽ nói chuyện với chatbot. Vậy chatbot là gì ? tại sao chúng ta sẽ nói chuyện với chatbot ?

Không dài dòng nữa, chúng ta sẽ bắt tay luôn vào việc tìm hiểu Bot và tạo Bot cho ChatWork, thứ mà chúng ta thường ngày vẫn hay dùng để trao đổi trong công việc. Trong bài này mình xin hướng dẫn tạo con Bot với API Simsimi (Chắc hẳn nhiều người biết đến con Simsimi mất dạy này rồi =)) )

1. Bot là gì?

Theo tính chất kế thừa thì không dại gì viết lại khái niệm này nữa ahihi, nên mọi người có thể tìm nó hiểu trong bài này: Tìm hiểu về Bot Framework

2. Các bước để tạo ra một con Bot đơn giản cho ChatWork

Cũng chẳng có gì cao siêu ở đây, tóm tắt thì chỉ là việc hướng dẫn sử dụng 2 cái API tương tác chúng nó với nhau thông qua java (Vì mình là dev java ^^! )

2.1 Đầu tiên ta sẽ cần phải đăng ký sử dụng ChatWork API (nên tạo một cái account ChatWork mới vì nó dùng để làm con Bot luôn): tại đây

Sau khi đăng ký thường thì sẽ được chấp nhận sau khoảng 4-5h (Có thể lâu hơn). Khi đã nhận được một email thông báo, ta có thể bắt đầu sinh ra API Token để sử dụng trong ứng dụng của mình. Vào mục Personal Settings -> API, sau đó nhập password để hiển thị Token

2.2 Sau khi có API Token của ChatWork rồi thì tạm để đó, tiếp tục đăng ký sử dụng Simsimi API (Cái này là nhân tố chính :v )

Nói sơ về API Simsimi này thì nó có 2 bản TrialPaid, bản Paid thì phải trả tiền nó sẽ có nhiều tính năng hơn và thông minh hơn... trong bài này chúng ta sẽ dùng bản Trial vì nó free =)) cái gì thì cũng có cái giá của nó, dùng đồ chùa thì phải chấp nhận một số yếu tố sau:

  1. Key chỉ dùng được trong 7 ngày
  2. Hạn chế số "tin nhắn" gửi đi trong 1 ngày, cụ thể bao nhiêu thì mình quên rồi 😄

Để đăng ký thì vào đây và làm theo các bước sau: Click Signup for Trial key sau đó điền các thông tin vào... Đến đây thì vào mail mình đăng ký sau đó click vào link nó gửi để xác nhận, sau khi xác nhận thì sẽ nhận được thông báo như sau Click Sing In với account chúng ta vừa đăng ký Click Get a 7 days Trial Key thì nó sẽ hiện lên 1 thông báo thì điền bậy vào cũng được và click Save Click View Details / Extra function edit để lấy key Tiếp đến nó sẽ chuyển sang trang API Configuration thì các bạn chọn Enable All cho 3 loại Teach, Keywork, Filtering rối nhấn save nhé

2.3 Sau khi có API Token của ChatWork và API Key của Simsimi, thì việc tiếp theo của chúng ta là sử dụng nó như thế nào trong Java? (Ở đây mình sử dụng project maven)

2.3.1 Tạo class Account (Để lấy thông tin người chat từ ChatWork)

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * Created by ThanhD
 */
@JsonIgnoreProperties(ignoreUnknown=true)
public class Account {

    @JsonProperty("account_id")
    public String accountId;

    @JsonProperty("name")
    public String name;
}

2.3.2 Tạo class Message (Để lấy các tin nhắn từ ChatWork)

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;

/**
 * Created by ThanhD
 */
@JsonIgnoreProperties(ignoreUnknown=true)
public class Message {

    @JsonProperty("account")
    public Account account;

    @JsonProperty("body")
    public String body;
}

2.3.3 Tạo class BotStatus (Để lấy số lượng tin nhắn được "TO" cho con Bot)

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.builder.HashCodeBuilder;

/**
 * Created by ThanhD
 */
@JsonIgnoreProperties(ignoreUnknown=true)
public class BotStatus {

    @JsonProperty("mention_num")
    public int mentionRoomNum;
}

2.3.4 Tạo class ChatworkClient (Tinh hoa nằm hết trong này nên mọi người chịu khó đọc code nhé, mình có comment =)))))

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Created by ThanhD
 */
public class ChatworkClient {

    /**
     * PARAM FOR CHAT WOR API
     * DOCS: http://download.chatwork.com/ChatWork_API_Documentation.pdf
     */
    private static final String CW_API_URL = "https://api.chatwork.com/v2";
    private static final String CW_API_TOKEN = "xxxxxxxxxxxxxxxx"; //Replace your ChatWork API Token
    private static final String CW_HEADER_NAME = "X-ChatWorkToken";

    /**
     * PARAM FOR CHAT BOT API
     * DOCS: http://developer.simsimi.com/
     */
    private static final String CB_API_KEY = "34753d61-bc50-4125-bb3b-eed11b76e5c3";  //Replace your Simsimi API Key
    private static final String CB_API_URL = "http://sandbox.api.simsimi.com/request.p?key=%KEY%&lc=en&ft=0.0&text=%TEXT%";
    private static final String BOT_ID = "[To:XXXXX]"; //Replace XXXXX to ID ChatWork of Bot
    private static final String CB_HEADER_NAME = "X-ChatBotToken";

    private boolean breakFlag = false;

    HttpClient httpClient = new HttpClient();
    ObjectMapper mapper = new ObjectMapper();

    /**
     * BOT (API SIMSIMI)
     *
     * @param roomId
     * @throws Exception
     */
    public void startChatBot(String roomId) throws Exception {
        System.out.println("**********BOT STARTED**********");

        // Notify to room
        sendMessage(roomId, "TO ALL >>> BOT STARTED!");

        StringBuilder messReply = new StringBuilder();
        int mentionRoomNum = 0;
        while (!breakFlag) {
            try {
                // Check the number of messages mention BOT
                BotStatus status = mapper.readValue(
                        get(CW_API_URL.concat("/rooms/").concat(roomId), CW_HEADER_NAME, CW_API_TOKEN),
                        new TypeReference<BotStatus>() {});

                mentionRoomNum = status.mentionRoomNum;
            } catch (Exception e) {
                mentionRoomNum = 0;
            }

            if (mentionRoomNum > 0) {
                getMessages(roomId).forEach(message -> {
                    if (message.body.startsWith(BOT_ID)) {
                        // Get message from room of ChatWork
                        String messSend = message.body.substring(message.body.indexOf("\n") + 1);

                        // Check request BOT stop?
                        if ("STOP".equals(messSend)) {
                            // Notify to room
                            sendMessage(roomId, "TO ALL >>> BOT STOPPED!");
                            breakFlag = true;
                            return;
                        }

                        // Make new message from BOT
                        messReply.append("\n")
                                .append("[To:")
                                .append(message.account.accountId)
                                .append("] ")
                                .append(message.account.name)
                                .append("\n")
                                .append(getMessageFromBot(messSend));

                        // Send message of Bot to the previous sender
                        sendMessage(roomId, messReply.toString());

                        // Clear messReply
                        messReply.setLength(0);
                    }
                });
            }

            Thread.sleep(1500);
        }
        System.out.println("**********BOT STOPPED**********");
    }

    /**
     * Get message from BOT (Call API SIMSIMI)
     *
     * @param text
     * @return message
     */
    public String getMessageFromBot(String text)  {
        String message = StringUtils.EMPTY;
        if (StringUtils.isBlank(text)) {
            return message;
        }

        try {
            String json = get(CB_API_URL.replace("%KEY%", CB_API_KEY)
                                        .replace("%TEXT%", URLEncoder.encode(text, "UTF-8")),
                              CB_HEADER_NAME, CB_API_KEY);

            String[] results = json.split(",");

            message = results[0].split(":")[1];
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        }

        return StringUtils.remove(message, "\"");
    }

    /**
     * Get message from room.
     *
     * @param roomId
     * @return the list of Message
     * @throws IOException
     */
    private List<Message> getMessages(String roomId) {
        try {
            String json = get(CW_API_URL.concat("/rooms/").concat(roomId).concat("/messages"),
                              CW_HEADER_NAME, CW_API_TOKEN);

            if (StringUtils.isEmpty(json)) {
                return Collections.EMPTY_LIST;
            }

            return mapper.readValue(json, new TypeReference<List<Message>>() {});
        } catch (IOException e) {
            e.printStackTrace();
            return Collections.EMPTY_LIST;
        }
    }

    /**
     * Send message to room.
     *
     * @param roomId
     * @param message
     * @throws IOException
     */
    private void sendMessage(String roomId, String message) {
        PostMethod method = null;
        try {
            method = new PostMethod(CW_API_URL.concat("/rooms/").concat(roomId).concat("/messages"));
            method.addRequestHeader("X-ChatWorkToken", CW_API_TOKEN);
            method.addRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
            method.setParameter("body", message);

            int statusCode = httpClient.executeMethod(method);
            if (statusCode != HttpStatus.SC_OK) {
                throw new Exception("Response is not valid. Check your API Key or ChatWork API status. response_code = "
                        + statusCode + ", message =" + method.getResponseBodyAsString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }

    /**
     * Get method of APIs
     *
     * @param path
     * @return json result
     * @throws IOException
     */
    private String get(String path, String headerName, String apiKey) {
        GetMethod method = null;
        try {
            method =  new GetMethod(path);
            method.addRequestHeader(headerName, apiKey);

            int statusCode = httpClient.executeMethod(method);

            if (statusCode != HttpStatus.SC_OK) {
                throw new Exception("Response is not valid. Check your API Key or ChatWork API status. response_code = "
                        + statusCode + ", message =" + method.getResponseBodyAsString());
            }

            return method.getResponseBodyAsString();
        } catch (Exception e) {
            e.printStackTrace();
            return StringUtils.EMPTY;
        } finally {
            if (method != null) {
                method.releaseConnection();
            }
        }
    }
}

2.3.5 Cuối cùng tạo class App và chạy thử thôi (len)

import java.io.IOException;
import java.util.List;

/**
 * Created by ThanhD
 */
public class App {
    public static void main(String[] args) {
        try {
        	String roomId = "xxxxxx" // Replace your roomId
            ChatworkClient chatworkClient = new ChatworkClient();
            chatworkClient.startChatBot(roomId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Ghi chú: Trong code, có 2 biến BOT_IDroomId, thì được lấy như sau: Chúng ta add account của BOT được tạo ở bước đầu tiên vào, sau đó TO đến nó để lấy BOT_IDroomId Vd như trong hình sau sẽ là:

  • BOT_ID = 2589947
  • roomId = 82626197 Lưu ý: Bot ID và Room ID chỉ lấy dãy số thôi nhé

Mình demo văn hóa như ở dưới thôi chứ nó có thể nói tục nữa nhé =)))

3. Kết luận:

Cảm ơn mọi người đã cố gắng đọc đến đây =)) bài viết này hơi dài vì chủ yếu là các hình ảnh hưởng dẫn đăng ký các kiểu đà điểu, ngoài ra vì đây là demo đơn giản thôi nên trong source code nhiều chỗ hard code với code không clear, nên mong mọi người thông cảm và đừng chém em ạ (bow)!

Source code: Demo