「都会にはこんなに人がいるのにぼくは独りぼっちだ…」というときのために、Twitterボットにダイレクトメッセージ機能をつけ、雑談できるようにしてみました。
Twitterボットの雛形は、@hkusuさんのNode.js(Express) と Heroku で Twitter ボットを作る - Qiitaという記事を参考に作りました。
環境
- OS : Windows 10
- Node.js : 0.12.7
- Express : 4.13.3
Twitterアプリケーションのセットアップ
まずはTwitter Application Managementでボットのセットアップを行い、各種トークンを取得します。
ボットにダイレクトメッセージを行わせたい場合、PermissionsにRead, Write and Access direct messages
を設定しておく必要があります。
もしセットアップ済みのボットでPermissions設定を変更した場合、Access TokenとAccess Token Secretを再生成する必要があるので注意してください。(古いTokenのままだとDMが送受信できません。軽くハマりました。。)
TwitterのStreaming API
参考元の記事と同じように、Twitter用ライブラリとしてtwitを使用します。options
はExpressアプリに設定しておいた環境変数が取れるようになっているので、ここから各種Tokenを取得してtwitに渡します。
twitからTwitterのStreaming APIからのイベントを受信するためにstream
を取得します。
var app = require('../app')
var Twit = require('twit')
var options = app.get('options')
var twitter = new Twit( {
consumer_key: options.key,
consumer_secret: options.secret,
access_token: options.token,
access_token_secret: options.token_secret
})
// イベント受信用のstream取得
var stream = twitter.stream('user')
ここで指定しているuser
はStreamingの種類(endpoint)で、各ユーザ向けイベントを取得するのに使います。他にpublic
とsite
があります。
フォロー返し
ダイレクトメッセージのやりとりは、相互フォロー状態が前提条件です。
※2016/1/18追記
いつの間にか、相互フォローがなくてもダイレクトメッセージのやり取りができるようになっているようですが、記事はそのままにしておきます。
自分のTwitterアカウントとボットのアカウントを手動で相互フォローにしてもよいのですが、せっかくなのでボットにフォロー返し機能を付けることにしました。
フォローはfollow
イベントで検知します。
フォローの実行はfriendships/create
のPOSTですね。
// フォローイベントのハンドリング
stream.on('follow', function(data) {
var param = { user_id: data.source.id_str }
// 自分発のフォローは処理しない
if (data.source.id_str === options.id) return
// フォローされた相手をフォローする
twitter.post('friendships/create', param, function(err, data, resp){})
})
follow
イベントは「自分がフォローされた」場合も、「自分がフォローした」場合も発火してしまうので、素朴に作ると「自分による自分のフォロー」をしようとし、無限ループな予感がします(やってみると途中で止まるんですけど)。
いい感じにフィルタリングしてから受信できればいいのですが方法が見つからないので、受信後にフォローの実行元IDが自分かどうかでガードするようにしてみました。(コード中のoption.id
は、環境変数で与えたTwitterボット用のIDです)
ダイレクトメッセージ受信+エコー返信
とりあえずダイレクトメッセージを受信したら、そのままオウム返しする機能を付けてみます。
ダイレクトメッセージを送受信はdirect_message
イベントで検知し、送信の実行はdirect_messages/new
のPOSTで行います。
// ダイレクトメッセージハンドリング
stream.on('direct_message', function(data) {
var message = data.direct_message
// 自分が送信したダイレクトメッセージは処理しない
if (message.sender_id_str === options.id) return
// エコーで返信
var reply = { user_id: message.sender_id_str, text: message.text }
twitter.post('direct_messages/new', reply, function(err, data, resp) {})
})
フォローの時と同じように、送信元が自分かどうかで返信処理に進むかどうか判定させています。
これで以下のようにやりとりできます。
右側が人間、左側がボットです。
雑談で返信
対話は、ドコモの雑談対話APIというのを使うとお手軽に実現できます。使用時はTwitter APIと同じようにアプリケーション登録し、Tokenを取得する必要があります。
APIを叩くのを楽にするために、Requestをインストールしておきます。
npm install request --save
次にエコー返信していたコードを書き換えます。
Requestを使って雑談対話APIに会話内容をPOSTし、返ってきた回答をダイレクトメッセージに受け渡しています。
var request = require('request').defaults({strictSSL: false})
// docomo雑談対話API
var api = "https://api.apigw.smt.docomo.ne.jp/dialogue/v1/dialogue?APIKEY="
var token = options.dialog_token
// context保存用
var status = {}
// ダイレクトメッセージハンドリング
stream.on('direct_message', function(data) {
var message = data.direct_message
// 自分が送信したダイレクトメッセージは処理しない
if (message.sender_id_str === options.id) return
// 雑談対話API呼び出し
status.utt = message.text
status.nickname = message.sender_screen_name
var param = { body: JSON.stringify(status) }
request.post(api + token, param, function(err, res, data) {
body = JSON.parse(data)
// 雑談対話の文脈保存
status.context = body.context
status.mode = body.mode
// 雑談対話APIで得られた回答内容でダイレクトメッセージに返信
var reply = { user_id: message.sender_id_str, text: body.utt }
twitter.post('direct_messages/new', reply, function(err, data, res) {})
})
})
コード中のstatus
は、雑談時の文脈(context)を保存するのに使っています。同じ文脈で会話すると、応答が前回までの対話を考慮したものになるようです。それほど精度の高いものではなさそうですが…
本来は話しかけられたID全ての分をデータストアに保存する必要があると思いますが、とりあえずはメモリ上に1つだけに保存するように作ってみました。
これでボットに話しかけると、それっぽい回答が返ってくるようになりました。
ローカル実行時の環境変数埋め込み
参考にしたTwitterボットの作成記事では、ローカル環境での起動のためにシェルスクリプトlocal_run.sh
を作成していました。
私はWindows環境で開発していますので、以下のようなスクリプトlocal_run.cmd
を作成しました。
rem Twitterボットのトークン
set BOT_TWITTER_KEY=XXXX
set BOT_TWITTER_SECRET=XXXX
set BOT_TWITTER_TOKEN=XXXX
set BOT_TWITTER_TOKEN_SECRET=XXXX
rem TwitterボットのアカウントのID
set BOT_TWITTER_ID=XXXX
rem 雑談API用トークン
set BOT_DOCOMO_TOKEN=XXXX
npm start
後記
雑談対話API、結構面白いです。ついつい長話してしまう。超時間の無駄だけど…
ボットフレームワークであるHubotに組み込んで、チャットルーム内でしゃべらせるなんて使い方をしてる方もいますし、それ以外にも色々使い道ありそうですね。キュレーション自動化とかできそう。