どうも天城です。最近は戸籍について悩んでます。
きょうは英単語を覚えるためのSlack botを作ったので紹介します。
Slackの #dictionary
に英単語とその意味をメモしておくと、トランプ大統領が定期的にクイズを出題してくれる。
もくじ
前回までのあらすじ
TOEICではいい点数とれたけど、まだまだボキャブラリーが無いのでもっと勉強したい。
我が家では以前からSlackを利用しており、Hubotでを洗濯やゴミ出しのリマインダーを作っていた。
このSlackに #dictionary
に単語をメモしていたのだが、このbotを利用して単語クイズができればいいな〜ということを思いついた。
単語学習サービスは既にAnkiなどがあるが、様々なデバイスでデータを共有するのが大変そうなのと、新たにツールを導入するのは面倒なので、既にあるHubotを利用することにした。
仕組み
HerokuでDBといえばPostgresだけど、無料だと10000行までという制約がある。 実際困る事はないかもしれないが、なんとなく制限のことを考えるのがめんどい。 HerokuのAdd-onは他もだいたい無料だと制限があるので見送り。
というわけで、今回はGoogle Spreadsheetを使うことにした。 Google Spreadsheetなら200万セルまでらしいので、1単語に5セル使っても400000単語まで記録できる。
API周りの処理は、node-google-spreadsheetを利用した。 Google謹製のgoogleapisというのもあるけど、前者のほうが認証周りがシンプルそうだった。
事前にSpreadsheetで以下のようなシートを作成しておき、botが記録/閲覧できるようにしておく。
サービスアカウント作成
botがSpreadsheetにアクセスするためには、Googleのダッシュボード上でサービスアカウントを作成する必要がある。 詳しい手順はこの辺を参照。
https://github.com/theoephraim/node-google-spreadsheet#service-account-recommended-method
サービスアカウントを作成できたら認証用データの入ったJSONがダウンロードされる。 JSON内のメールアドレス、プライベートキー等は、Herokuの設定画面などから環境変数に入れておくとよい。
記録部分
特定の形式のメッセージが来たらスプレッドシートに記録する。 今回は以下の形式を採用。
*america* アメリカ
Hubotレポジトリの /scripts
に以下のスクリプトを保存すると良い。
doc.addRow()
を呼ぶだけで行を追加できて便利。
const GoogleSpreadsheet = require('google-spreadsheet'); const p = require('pify'); // promisify // 認証周りのデータ const doc = new GoogleSpreadsheet(process.env.SPREADSHEET_KEY); const creds = { client_email: process.env.SPREADSHEET_CLIENT_EMAIL, private_key: process.env.SPREADSHEET_PRIVATE_KEY, }; // メッセージにマッチする正規表現 const re = /\*?([^\n\*]+)\*?\n(.+)/; module.exports = (robot) => { robot.hear(re, async (res) => { let m = res.message.rawText.match(re); if (m) { // メッセージから英単語と意味を抽出 const english = m[1]; const japanese = m[2]; // 認証 await p(doc.useServiceAccountAuth)(creds); // スプレッドシートに保存 p(doc.addRow)(1, { english, japanese }) .then(() => res.send('OK, saved.')) .catch((e) => res.send('ERROR! Try again.')); } }); };
クイズ部分
こういう感じでSpreadsheetから単語を取得して
const GoogleSpreadsheet = require('google-spreadsheet'); const p = require('pify'); // promisify const doc = new GoogleSpreadsheet(process.env.SPREADSHEET_KEY); const creds = { client_email: process.env.SPREADSHEET_CLIENT_EMAIL, private_key: process.env.SPREADSHEET_PRIVATE_KEY, }; // 認証 await p(doc.useServiceAccountAuth)(creds); // 行を取得 const info = await p(doc.getInfo)(); const rows = p(info.worksheets[0].getRows)({})); // セルのデータを取得 const words = rows.map(r => ({ english: r.english, japanese: r.japanese, }));
こういうのでSlackに発言すればよい。
send.trump
はメッセージ送信部分のラッパー。
module.exports = async (robot, room) => { send.trump(robot, room, '@channel 今から皆さんに、英語に関するクイズを出すピィ〜♪\n'); // 単語を取得 let rows = await sheet.getRows(); rows = shuffle(rows); for (let i = 0; i < limit; i++) { const r = rows[i]; send.trump(robot, room, `*${r.english}* は何という意味でしょう?`) await sleep(5000); send.trump(robot, room, `正解は \`${r.japanese}\` でした!!\n.\n`); await sleep(5000); } await sleep(1000); send.trump(robot, room, 'お疲れ様!!今日も一日頑張ろうヾ(๑╹◡╹)ノ"'); };
実際には、同じ単語は3回までしかクイズに出ないようにしたり、一日の出題数を制限したりしてる。 大量にあると疲れてしまうので……
今回のbotは単語帳サービスみたいなものなので、英語に限らず何でも記録出来ます。 ことわざとか技術用語の学習に使ってもいいかも。