Slackはいわゆるチャットツールなのですが、他のサービスと比べると外部連携に力を入れているようで(個人の感想です)、APIが充実しているので触っていて面白いです。
この記事ではAPIの中でもreaction関連の部分にフォーカスしてみます。
この記事でやること
- reactionの特徴を考える
- reactionをスイッチとして使ってみる
その前にreactionとは何か?
reactionというのは、いわゆるFacebookの「いいね!」ボタンみたいなものを、各メッセージに後付でつけたり消したりすることができる機能です。
画像で説明すると、メッセージの下の「親指上げる絵文字」のところがreactionです。
詳しい説明はこちら。Slackで、投稿に対して絵文字で「いいね!」などのリアクションが返せるようになった
reactionはどう使うと効果的か
reactionは、他のメンバーから各記事に「いいね」をもらったり投票したりするのに使うのが一般的です。それ以外の用途に使おうと思った場合、まずreactionの特徴から考えるのがよさそうです。
- ボタンを押すだけなので、意思表示しやすい(メッセージを打たなくて良い)
- 一度押すと+1で、もう一度押すと-1(マイナス1)になる
- SlackのReal Time Messaging APIだと、リアルタイムにreaction ON/OFFのイベントがあがってくるので、BOTがすばやく反応できる
- そのチャンネルの議論とは異なるメッセージのやりとりを、reaction側に寄せることができる
- BOTもメッセージ投稿せずにreactionをつけたり外したりできる
- 最初にメッセージにreactionを付与するのが結構面倒
- 一度つけられたreactionへの操作は超楽
上記を元に考えると、
- 何かのON/OFFスイッチとしてreactionを利用する
- reactionはなんらかのメッセージをキーに、BOTに付与してもらう
というアイデアを思いつきました。
SlackでLEDチカチカ
何をON/OFFさせるのか考えたのですが、リアルタイムON/OFFといえば自作電子回路のLEDをチカチカさせるのが便利さが伝わりやすそうです。
便利さを強調するために、iPhone上のSlackから、BOT経由で、小型Wifiモジュール ESP-WROOM-02を利用した電子回路のLEDをチカチカさせてみます。両方場所選ばないので、どこでもLチカが簡単に実現できます(今回は電源部分だけ有線です)
準備(Slack BOT側)
SlackのBOTのプログラムは、slappyのv0.6.0を利用しました。
参考記事:Slappy - 簡単にslackのbotを作れるgemを作った
BOT用のスクリプトを本体から独立したDSLで記述できるところと、Slack RTMイベントのフックがサポートされているところが超便利です。
今回は、SlackのRTMイベントのreaction_added event, reaction_removed eventをフックして、+1されたらLED点灯、-1されたらLED消灯としています。
一部そのままでは動かなかった部分(lib/slappy/listeners/concerns/validatable.rb)と手抜きしたかった部分(lib/slappy/event.rb)を改造しています。
(2015/12/4追記:v0.6.1で以下の2項目について採用されましたので、v0.6.1を利用する場合は下記パッチは不要です。コメント欄参照)
module Slappy
module Listener
module Validatable
include Slappy::Debuggable
attr_accessor :pattern
def valid?(event)
unless time_valid?(event)
Debug.log 'Event happend in before start time'
Debug.log event.ts
Debug.log Slappy.client.start_time
return false
end
target = event.send(target_element)
unless target
Debug.log 'Target is nil'
return false
end
event.matches = target.match pattern
unless event.matches
Debug.log "Target is not match pattern(#{pattern})"
return false
end
true
end
private
def time_valid?(event)
# eventによってはtsがない場合があるのでその場合は無視
return true unless event.has_key?(:ts)
event.ts > Slappy.client.start_time
end
end
end
end
module Slappy
class Event
extend Forwardable
include Debuggable
attr_accessor :matches
def_delegators :@data, :method_missing, :respond_to_missing?
def initialize(data)
@data = Hashie::Mash.new data
end
def text
@data['text'].to_s
end
def channel
SlackAPI::Channel.find(id: @data['channel']) ||
SlackAPI::Group.find(id: @data['channel']) ||
SlackAPI::Direct.find(id: @data['channel'])
end
def user
SlackAPI::User.find(id: @data['user'])
end
def ts
Time.at(@data['ts'].to_f)
end
def reply(text, options = {})
options[:text] = text
options[:channel] = channel
Messenger.new(options).message
end
def reaction(emoji)
result = ::Slack.reactions_add name: emoji, channel: @data['channel'], timestamp: @data['ts']
Debug.log "Reaction response: #{result}"
end
def bot_message?
@data['subtype'] && @data['subtype'] == 'bot_message'
end
# @dataの内容を外で直接扱いたい(手抜きしたい)
def data
@data
end
end
end
BOTスクリプトは、slappy new
した時に作成される、slappy-scripts/example.rbを直接書き換えました。
ESPSERVERのところは、次に立てるWifiモジュールのIPアドレスを入れてください。
ここではローカルにあるPCからSlackにアクセスして、同じくローカルネットワーク内にあるWifiモジュールとプライベートIPで接続する想定にしていますが、グローバルIPがあれば世界中どこでもつながります。
BOT_USERIDのところは、BOTがreactionを付与する際にもイベントがあがるので、BOTだったらLチカさせないようにする必要があるのですが、BOTかどうかの判定がイベントであがってこないので、BOTのID(xoxx-~のIDではなくて、UxxxxxxxというBOTのユーザIDとして付与されるID)を調べて入れてください。
(イベントあがってきたときのeventオブジェクトの内容をp event
等でコンソールにdumpすると出てくるuserパラメータの値になります。)
require 'net/http'
require 'uri'
ESPSERVER = ##YOUR WIFI WEBSERVER IP##
BOT_USERID = ##YOUR BOT USERID##
BUTTON1 = 'red_circle'
BUTTON2 = 'large_blue_circle'
hello do
puts 'successfly connected'
end
hear 'Lチカ' do |event|
event.reaction BUTTON1
event.reaction BUTTON2
end
monitor 'reaction_added' do |event|
next if event.data["user"] == BOT_USERID
button =
case event.data["reaction"]
when BUTTON1; 1
when BUTTON2; 2
else 0
end
Net::HTTP.get(URI.parse(ESPSERVER + button.to_s + '/on')) if button>0
end
monitor 'reaction_removed' do |event|
next if event.data["user"] == BOT_USERID
button =
case event.data["reaction"]
when BUTTON1; 1
when BUTTON2; 2
else 0
end
Net::HTTP.get(URI.parse(ESPSERVER + button.to_s + '/off')) if button>0
end
準備(Wifi WEB Server側)
Arduino IDEにESP8266用のデータをダウンロードすると、スケッチの例として、ESP8266WebServer/HelloServerというプログラムを読み込むことが出来ます。
参考記事:技適済み格安高性能Wi-FiモジュールESP8266をArduinoIDEを使ってIoT開発する為の環境準備を10分でやる方法
今回は、HelloServerのプログラムを改造しました。http://##ipaddr##/1/on
でLED1が点灯、http://##ipaddr##/2/on
でLED2が点灯、onをoffに変えると消灯するプログラムになります。
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
const char* ssid = ##YOUR SSID##;
const char* password = ##YOUR Wifi PASSWORD##;
ESP8266WebServer server(80);
const int led1 = 13;
const int led2 = 12;
void setup(void){
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
digitalWrite(led1, 0);
digitalWrite(led2, 0);
Serial.begin(115200);
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP());
server.on("/1/on", [](){
digitalWrite(led1, 1);
server.send(200, "text/plain", "led1 on");
});
server.on("/2/on", [](){
digitalWrite(led2, 1);
server.send(200, "text/plain", "led2 on");
});
server.on("/1/off", [](){
digitalWrite(led1, 0);
server.send(200, "text/plain", "led1 off");
});
server.on("/2/off", [](){
digitalWrite(led2, 0);
server.send(200, "text/plain", "led2 off");
});
server.begin();
Serial.println("HTTP server started");
}
void loop(void){
server.handleClient();
}
Arduino IDEから、ESP-WROOM-02にプログラムを転送します。シリアルモニタを立ち上げて、WEBServerが立ち上がったことと、IPアドレスを確認してください。(IPアドレスは、準備(Slack BOT側)のBOTスクリプトに埋め込む必要があります)
動かそう
iPhoneとWifiモジュールに回路を組み込んだものを重ねてみました。
画像だと伝わりにくいですが、「Lチカ」とメッセージを書くとbotがreactionを2つつけてくれます。そのreactionをそれぞれ押すと、下の回路の対応する赤LEDと青LEDが光る/消えるとなります。
動画だとこういう感じです。
「Lチカ」とメッセージを送った後、reactionが出てくるのが遅いのは、RTM APIではなくて、Slack WEB APIを使わないといけないため、1APIにつき1秒のリミットがあるからです。
reactionを押すと、押した直後にLEDが点灯していますね。これぐらいだとわりと実用的に使えるのではないでしょうか。
まとめ
SlackとIoTは親和性高いんじゃないかと思いました。スマホから簡単に使えるので、中途半端なインターフェース作るくらいなら、Slackでまずはプロトタイプというのも選択肢に入りそうです。