JavaScript
Node.js
Twitter
Express

Node.js(Express)で作ったTwitterボットとダイレクトメッセージで雑談する

More than 1 year has passed since last update.

「都会にはこんなに人がいるのにぼくは独りぼっちだ…」というときのために、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を設定しておく必要があります。

access.png

もしセットアップ済みのボットでPermissions設定を変更した場合、Access TokenとAccess Token Secretを再生成する必要があるので注意してください。(古いTokenのままだとDMが送受信できません。軽くハマりました。。)

TwitterのStreaming API

参考元の記事と同じように、Twitter用ライブラリとしてtwitを使用します。optionsはExpressアプリに設定しておいた環境変数が取れるようになっているので、ここから各種Tokenを取得してtwitに渡します。

twitからTwitterのStreaming APIからのイベントを受信するためにstreamを取得します。

index.js
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)で、各ユーザ向けイベントを取得するのに使います。他にpublicsiteがあります。

フォロー返し

ダイレクトメッセージのやりとりは、相互フォロー状態が前提条件です。

※2016/1/18追記
いつの間にか、相互フォローがなくてもダイレクトメッセージのやり取りができるようになっているようですが、記事はそのままにしておきます。

自分のTwitterアカウントとボットのアカウントを手動で相互フォローにしてもよいのですが、せっかくなのでボットにフォロー返し機能を付けることにしました。

フォローはfollowイベントで検知します。
フォローの実行はfriendships/createのPOSTですね。

index.js
// フォローイベントのハンドリング
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で行います。

index.js
// ダイレクトメッセージハンドリング
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) {})
})

フォローの時と同じように、送信元が自分かどうかで返信処理に進むかどうか判定させています。

これで以下のようにやりとりできます。
右側が人間、左側がボットです。
echo.png

雑談で返信

対話は、ドコモの雑談対話APIというのを使うとお手軽に実現できます。使用時はTwitter APIと同じようにアプリケーション登録し、Tokenを取得する必要があります。

APIを叩くのを楽にするために、Requestをインストールしておきます。

npm install request --save

次にエコー返信していたコードを書き換えます。
Requestを使って雑談対話APIに会話内容をPOSTし、返ってきた回答をダイレクトメッセージに受け渡しています。

index.js
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つだけに保存するように作ってみました。

これでボットに話しかけると、それっぽい回答が返ってくるようになりました。
dialog.png

ローカル実行時の環境変数埋め込み

参考にしたTwitterボットの作成記事では、ローカル環境での起動のためにシェルスクリプトlocal_run.shを作成していました。

私はWindows環境で開発していますので、以下のようなスクリプトlocal_run.cmdを作成しました。

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に組み込んで、チャットルーム内でしゃべらせるなんて使い方をしてる方もいますし、それ以外にも色々使い道ありそうですね。キュレーション自動化とかできそう。

参考資料