はじめに
2014年12月頃、SlackのHubot Integrationの仕様が変わりまして、Slack上でhubotが動いているURLを設定する必要がなくなりました。このおかげで、RaspberryPi上のhubotも簡単にSlackと連携させることができます。
というわけで、世の中のIoTブームに乗っかってSlackを現実世界とつなげてみましょう。
作ったもの
今回実装したのは、Slackのあるコマンドに反応してRasPiが写真を撮影、自動アップロードするという機能です。
なぜこれを実装したかと言うと、これが欲しかったんです。画像データを自動で飛ばしてくれるホワイトボード。でもお高いですよね。普通はこんなお高いやつを何台も買えないので、ミーティングのときにはiPhoneで写真を撮って、Slackにアップロードして共有しています。
これ、自動化できればお高いホワイトボード買わなくていいんじゃね?ということで作ってみました。
実演
こんな感じでセッティングして...
slack上でcameraと入力すると...
10数秒後の画像がポストされます!Slack APIのtokenが自分のものなので自分が投稿しているように見えますが、完全に自動で画像がアップロードされています。
RasPi設定
若干覚えていないところがあるのですが覚えている範囲で...Raspbianのインストールまでは完了している前提で。
nvmインストール
curl https://raw.githubusercontent.com/creationix/nvm/v0.11.1/install.sh | bash
source ~/.profile
nvm install 0.10
npmインストール
追記:nvmインストールしたらnpm付いてきますね...不要です
git clone git://github.com/isaacs/npm.git
cd npm/scripts
chmod +x install.sh
sudo ./install.sh
Webカメラ設定
sudo apt-get install fswebcam
hubotスクリプト
コードはGithubに置いていますので、cloneしたら使えるはず。スクリプトはこんな感じ。今回は市販のWebカメラを使用しているのでfswebcam
コマンドで撮影しています。RasPi用カメラモジュールを使う場合はここをraspistill
に置き換えたら動くはず。
node.jsからmultipartのリクエストを投げるのがどうしても出来なかったので、しょうがなくcurl
コマンドを叩いて画像をアップロードしています。どなたかいい感じの書き方をご存知の方、コメントなりPullRequestなりお願いします。
module.exports = (robot) ->
exec = require('child_process').exec
Slack = require 'slack-node'
slack = new Slack process.env.SLACK_API_TOKEN
DIR = '/tmp/'
robot.hear /camera/i, (msg) ->
channel = msg.message.room
fileName = "#{getNow()}.jpg"
console.log "rcv"
exec "fswebcam -S 50 -r 1920x1080 --no-banner #{DIR}#{fileName}", (err, stdout, stderr) ->
if err
return msg.send "Failed " + err
postSlack channel, fileName, (err, res) ->
if err
return msg.send "Failed " + err
msg.send "OK"
postSlack = (channel, fileName, callback) ->
getChannelFromName channel, (err, id) ->
if err
return callback err
exec "curl -F file=@#{DIR}#{fileName} -F channels=#{id} -F token=#{process.env.SLACK_API_TOKEN} https://slack.com/api/files.upload", (err, stdout, stderr) ->
exec "rm -f #{DIR}#{fileName}", (err, stdout, stderr) ->
if err
console.log "failed to delete file"
if err
return callback err
callback null, 'OK'
getChannelFromName = (channelName, callback) ->
slack.api "channels.list", exclude_archived: 1, (err, response) ->
if err
return callback err
if !response.ok
return callback response.error
for val, i in response.channels
if val.name == channelName
return callback null, val.id
return callback err
getNow = () ->
date = new Date
y = date.getFullYear()
m = date.getMonth() + 1
d = date.getDate()
h = date.getHours()
M = date.getMinutes()
s = date.getSeconds()
return "#{y}#{m}#{d}#{h}#{M}#{s}"
おわりに
IoT連携まで来るといよいよSlackはただのチャットツールじゃないなと。チャットであり、コマンドラインであり、すべてのインタフェースに成り得る。自分の想像力でプロジェクトをもっと面白いものにしていきましょう!