使用line-bot-sdk-java构建聊天机器人

总结

「LINE Messaging API」在9月29日发布。这个新API与之前的「LINE BOT API Trial Account」相比,更加简洁。为了配合这个API的发布,官方已经提供了各种语言的SDK。我们使用Java版本来创建一个简单的聊天机器人。

我想做的事情 (Wǒ zuò de

当收到文本消息时,会根据消息类型在Twitter上搜索声优村川梨衣的图片并回复。

这里是GitHub

环境

    • jdk1.8

 

    • Spring Boot

 

    Heroku

准备好

setup.PNG

由于Heroku账户设置并不是主要讨论内容,故将其省略不提。

源代码

本次的聊天机器人将使用Spring Boot进行开发。SDK中有专门用于Spring Boot的项目,使用起来非常方便。

build.gradle -> 构建.gradle

dependencies {
    compile('org.springframework.boot:spring-boot-starter-social-twitter')
    runtime('org.springframework.boot:spring-boot-devtools')
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')

    // LINE BOT SDK
    compile group: 'com.linecorp.bot', name: 'line-bot-spring-boot', version: '1.3.0'
}

虽然MVNRepository只有1.1.0版本,但截至2016年11月16日,已经有1.3.0版本了,所以我们选择使用那个版本。
由于使用了Twitter搜索API,我们还将Spring Social Twitter作为依赖关系添加进去。

终结点 jié

import java.io.IOException;

import com.linecorp.bot.model.event.Event;
import com.linecorp.bot.model.event.MessageEvent;
import com.linecorp.bot.model.event.message.TextMessageContent;
import com.linecorp.bot.model.response.BotApiResponse;
import com.linecorp.bot.spring.boot.annotation.EventMapping;
import com.linecorp.bot.spring.boot.annotation.LineMessageHandler;

@LineMessageHandler
public class MessageHandler {

    private final ReplyMessageHandler replyMessageHandler;

    public MessageHandler(ReplyMessageHandler replyMessageHandler) {
        super();
        this.replyMessageHandler = replyMessageHandler;
    }

    @EventMapping
    public void handleTextMessageEvent(MessageEvent<TextMessageContent> event) throws IOException {
        System.out.println("event: " + event);
        BotApiResponse response = replyMessageHandler.reply(event);
        System.out.println("Sent messages: " + response);
    }

    @EventMapping
    public void defaultMessageEvent(Event event) {
        System.out.println("event: " + event);
    }

}

这是API的终端点,不是使用Spring MVC的@Controller + @RequestMapping来定义,而是使用@LineMessageHandler + @EventMapping。

将@LineMessageHandler应用于类,并为每个方法添加@EventMapping。这样,它会根据发送的消息类型自动调用匹配相应参数类型的方法。

例如,在上述代码中,如果发送了文本类型的消息,则会调用handleTextMessageEvent方法,如果是其他类型,则会调用defaultMessageEvent方法。

另外,URL为/callback。

发送回复

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.validation.constraints.NotNull;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.social.twitter.api.SearchParameters;
import org.springframework.social.twitter.api.SearchResults;
import org.springframework.social.twitter.api.Tweet;
import org.springframework.social.twitter.api.Twitter;
import org.springframework.stereotype.Service;

import com.linecorp.bot.client.LineMessagingService;
import com.linecorp.bot.model.ReplyMessage;
import com.linecorp.bot.model.event.MessageEvent;
import com.linecorp.bot.model.event.message.TextMessageContent;
import com.linecorp.bot.model.message.ImageMessage;
import com.linecorp.bot.model.message.Message;
import com.linecorp.bot.model.message.TextMessage;
import com.linecorp.bot.model.response.BotApiResponse;

import github.rshindo.rlb.util.TwitterApiUtils;

@Service
public class ReplyMessageHandler {

    private static final String RIETION = "りえしょん";
    private static final String MURAKAWA_RIE = "村川梨衣";
    private static final String ROBOT = "ロボ";

    @Value("${rietionbot.twitter.robot}")
    @NotNull
    private String robotAccountId; 

    @Value("${rietionbot.twitter.riemagic}")
    @NotNull
    private String riemagicAccountId;

    private final Twitter twitter;
    private final LineMessagingService lineMessagingService;

    public ReplyMessageHandler(Twitter twitter, LineMessagingService lineMessagingService) {
        super();
        this.twitter = twitter;
        this.lineMessagingService = lineMessagingService;
    }

    public BotApiResponse reply(MessageEvent<TextMessageContent> event) throws IOException {

        String receivedMessage = event.getMessage().getText();
        String replyToken = event.getReplyToken();
        List<Message> messages = null;
        switch (receivedMessage) {
            case RIETION:
                messages = searchRietionImages();
                break;
            case MURAKAWA_RIE:
                messages = searchRieMurakawaImages();
                break;
            case ROBOT:
                messages = searchRobotTexts();
                break;
            default:
                messages = searchRiemagicTexts();
                break;
        }
        return lineMessagingService
            .replyMessage(new ReplyMessage(replyToken, messages))
            .execute()
            .body();

    }

    protected List<Message> searchRietionImages() {
        return searchImages(RIETION + TwitterApiUtils.IMAGE_FILTER);
    }

    protected List<Message> searchRieMurakawaImages() {
        return searchImages(MURAKAWA_RIE + TwitterApiUtils.IMAGE_FILTER);
    }

    protected List<Message> searchImages(String searchKey) {
        SearchResults results = twitter.searchOperations().search(
                new SearchParameters(searchKey)
                    .count(10));
        List<Tweet> tweets = results.getTweets();
        List<Message> messages = tweets.stream()
                .flatMap(tweet -> TwitterApiUtils.getImageUrlList(tweet).stream())
                .limit(3)
                .map(url -> new ImageMessage(url, url))
                .collect(Collectors.toList());
        if(messages.isEmpty()) {
            return Arrays.asList(new TextMessage("画像が見つかりませんでした"));
        } else {
            return messages;
        }
    }

    protected List<Message> searchRobotTexts() {
        return searchTexts(robotAccountId);
    }

    protected List<Message> searchRiemagicTexts() {
        return searchTexts(riemagicAccountId);
    }

    protected List<Message> searchTexts(String searchKey) {
        SearchResults results = twitter.searchOperations().search(
                new SearchParameters(searchKey)
                    .count(1));
        List<Message> messages = results.getTweets().stream()
                .map(tweet -> new TextMessage(tweet.getText()))
                .collect(Collectors.toList());
        if(messages.isEmpty()) {
            return Arrays.asList(new TextMessage("ツイートが見つかりません"));
        } else {
            return messages;
        }
    }

}

对于消息的回复,不是使用Spring MVC功能,而是使用SDK的功能。
只需使用LineMessagingService自动装配,按照上述方法调用,就完成了回复的实现。很简单!
但是有一个要点,就是必须将接收到的消息中包含的ReplyToken放入回复中。我相信你不会迷失方向的。。

構築和部署

在这里进入LINE的管理界面。

channel_secret.PNG
channel_token.PNG

首先,请在Webhook URL中设置终端点。
然后,确认Channel Secret和Channel Access Token。尽管我们在这次实现中没有自己实施,但是LINE发送的消息是加密的,所以需要使用Secret和Token进行解密。

Secret和Token分别在“line.bot.channelToken”和“line.bot.channelSecret”中进行设置。在Spring Boot的属性设置中,有以下的一种方法,例如:

    • propertiesファイル

 

    • ymlファイル

 

    • システムプロパティ

 

    環境変数

这次我们在Heroku的系统属性中进行了设置。

执行

由于涉及肖像权问题,我们将不展示实际的LINE界面。

另外,在回复发送时可能出现“请将IP地址添加到白名单”之类的错误。
在这种情况下,需要从管理界面添加到服务器IP白名单中。
但是,对于我个人来说,我遇到了一种奇怪的现象:当我注册一次后,却无法正常工作,但如果将其删除后,就可以回复了…

总结

使用LINE BOT SDK,我们可以轻松实现聊天机器人。它具有许多有趣的应用场景。

bannerAds