2ヶ月ぶりの投稿、そして第五作目のプログラム。最近はもっぱらN予備校で勉強をしていました。いやーあのサービスは素晴らしいです。クオリティが高く、値段が安い。僕のような独学でプログラミングを勉強している方にはお薦めです。さて、今回はN予備校で習ったNode.jsとHubotを使って、TwitterのBotを作成してみました。
仕様
- フォローバック
- Tweetを監視し、キーワードでユーザーをフォロー
- メンションに対し、リプライを送る
リプライにはdocomoの雑談対話APIを利用しました。これは「ユーザの発話テキストを受け付け、その入力に対して自然な会話となる雑談を提供」というものです。TwitterのAPIを触るのに利用したのはNode.jsのTwitというライブラリです。
このBot、対話APIを活用して日本語学習者向けの会話練習用Botにしようと思ったのですが、会話のクオリティがちょっとそこまで望めずに断念しました。なので、本来はHerokuで公開しようと思ったのですが、それはやめています。アンフォロー機能や日本語関連情報の自動ツイートとかも実装しようと思ったのですがそれも中止。ローカルサーバーで動かして、Twitter上での動作は確認済みです。
コード
'use strict';
const config = require('./config');
const Twit = require('twit');
const request = require('request');
const fs = require('fs');
let log = new Map();
const fileName = './scripts/log.json';
try {
const update = fs.readFileSync(fileName, 'utf8');
log = new Map(JSON.parse(update));
} catch (ignore) {
console.log('There is no prior conversation.');
};
const T = new Twit({
consumer_key: config.key,
consumer_secret: config.secret,
access_token: config.token,
access_token_secret: config.token_secret,
timeout_ms: 60*1000
});
const userStream = T.stream('user');
const keywordStream = T.stream('statuses/filter', { track: ['learn Japanese', 'learning Japanese', 'Japanese lessons', 'Japanese grammar', 'study Japanese', config.userName]});
// Follow Back
userStream.on('follow', function(data) {
const param = { user_id: data.source.id_str };
if (data.source.id_str === config.ownerID) return;
T.post('friendships/create', param, function(err, data, resp){});
});
// Follow Based on Keywords
keywordStream.on('tweet', function(data){
const param = { user_id: data.user.id_str };
T.post('friendships/create', param, function(err, data, resp){});
});
// Reply with conversation API
function save() {fs.writeFileSync(fileName, JSON.stringify(Array.from(log)), 'utf8');}
const record = {};
keywordStream.on('tweet', function(data){
const textToString = data.text.toString();
const target = data.user.screen_name.toString();
if (textToString.includes(config.userName) && data.source.id_str !== config.ownerID) {
record.utt = textToString;
record.nickname = target;
if (log.has(target)){record.context = log.get(target)};
const param = { body: JSON.stringify(record)};
request.post(config.url + config.API_key, param, function(err, res, data) {
const body = JSON.parse(data);
T.post('statuses/update', {status: '@' + target + ' ' + body.utt});
log.set(target, body.context);
save();
});
};
});
日本語学習者向けを想定していたので、キーワードフォローは'learning Japanese', 'Japanese lessons', 'Japanese grammar'とかになっています。自分の学習記録用にQiitaを書いているので、コードの解説は詳しくしませんが、雑談対話APIで一貫性のある会話をするには、contextを残しておくことが必要。そこで、Mapオブジェクトでcontextを管理し、saveファンクションで永続的に同期できるようにしました。これでこんな感じで会話が成り立ちます(と言ってもBotの返信しか写っていませんが)。
AIの進化で、こういうBotによる会話の精度も今後は上がっていくのだろうか…。現状はやはりまだまだというところです。しかし、少し凝ったものが作れるようになってきて、プログラミングが徐々に楽しくなってきました。引き続き頑張ります!