esp8266でSlackBotを作ってIFTTTからのメッセージを待ち受けする

# esp8266のSlackBotをIFTTT経由で操作する

## 背景
GoogleHomeからesp8266を制御しようとしたときに、IFTTT-Slack経由が一番ラクにできそうだったのでトライしてみた。

## 用意するもの
esp8266(WROOM02)

## 参考サイトをみてSlackBotをつくる
[Arduino(ESP8266)にSlack Botを乗せてみた](http://shift.hateblo.jp/entry/2016/11/21/112611)

## ちょっとだけトラブル発生
参考サイト通りにやると人間の投稿には反応できるけど、IFTTTからのメッセージには反応してくれない。
シリアルログをみるとIFTTTからのメッセージは添付扱いになっているため、すこし階層が深くなっている模様。

## 対策版
かなりやっつけだけど「root["attachments"][0]["pretext"]」でIFTTTからのメッセージを参照できた。
ソースコードは以下。

```C
// https://github.com/urish/arduino-slack-bot

#include <Arduino.h>
#include <Hash.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
#include <Servo.h>

#define WIFI_SSID "あなたのSSID"
#define WIFI_PASSWORD "あなたのWIFIパスワード"

#define SLACK_SSL_FINGERPRINT "AC 95 5A 58 B8 4E 0B CD B3 97 D2 88 68 F5 CA C1 0A 81 E3 6E" // If Slack changes their SSL fingerprint, you would need to update this
#define SLACK_BOT_TOKEN "スラックボットのTOKEN"
#define DEFAULT_CHANNEL "チャンネルID?"

ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;

long nextCmdId = 1;
bool connected = false;
bool helloSaid = false;

void webSocketEvent(WStype_t type, uint8_t *payload, size_t len) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.printf("[WebSocket] Disconnected :-( \n");
      connected = false;
      break;

    case WStype_CONNECTED:
      Serial.printf("[WebSocket] Connected to: %s\n", payload);
      break;

    case WStype_TEXT:
      Serial.printf("[WebSocket] Message: %s\n", payload);
      processSlackMessage((char*)payload);
      break;
  }
}

void processSlackMessage(char *payload) {
  StaticJsonBuffer<2000> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(payload);
  if (!root.success()) {
    Serial.println("[Json] Parse failed");
    return;
  }

  if(root.containsKey("type")) {
    if(strcmp("hello", root["type"]) == 0) {
      helloSaid = true;
    } else if (strcmp("message", root["type"]) == 0) {
      if(root.containsKey("attachments")) {
        if(strcmp("IFTTT経由メッセージのキーワード", root["attachments"][0]["pretext"]) == 0) {
          // キーワードを見つけたときの処理
        }
      }else if(root.containsKey("text")) {
        if(strcmp("通常メッセージのキーワード", root["text"]) == 0) {
          // キーワードを見つけたときの処理
        }
      }
    }
  }
}

bool connectToSlack() {
  // Step 1: Find WebSocket address via RTM API (https://api.slack.com/methods/rtm.start)
  HTTPClient http;
  http.begin("https://slack.com/api/rtm.start?token=" SLACK_BOT_TOKEN, SLACK_SSL_FINGERPRINT);
  int httpCode = http.GET();
  if (httpCode != HTTP_CODE_OK) {
    Serial.printf("HTTP GET failed with code %d\n", httpCode);
    return false;
  }
  
  WiFiClient *client = http.getStreamPtr();
  client->find("wss:\\/\\/");
  String host = client->readStringUntil('\\');
  String path = client->readStringUntil('"');
  path.replace("\\/", "/");

  // Step 2: Open WebSocket connection and register event handler
  Serial.println("WebSocket Host=" + host + " Path=" + path);
  webSocket.beginSSL(host, 443, path, "", "");
  webSocket.onEvent(webSocketEvent);
  
  return true;
}

void sendPing() {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["type"] = "ping";
  root["id"] = nextCmdId++;
  String json;
  root.printTo(json);
  webSocket.sendTXT(json);
}

void sendMessage() {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["type"] = "message";
  root["channel"] = DEFAULT_CHANNEL;
  root["text"] = "fuga";
  String json;
  root.printTo(json);
  webSocket.sendTXT(json);
}

void setup() {
  servo.attach(servoPin);
  servo.write(angle);
  Serial.begin(9600);
  while (!Serial) {
    delay(100);
  }
  Serial.setDebugOutput(true);

  WiFiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);
  while (WiFiMulti.run() != WL_CONNECTED) {
    delay(100);
  }

  configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
}

unsigned long lastPing = 0;

void loop() {
  webSocket.loop();

  if (connected) {
    // Send ping every 5 seconds, to keep the connection alive
    if (millis() - lastPing > 5000) {
      if(helloSaid) {
        sendPing();
      }
      lastPing = millis();
    }
  } else {
    // Try to connect / reconnect to slack
    connected = connectToSlack();
    if (!connected) {
      delay(3000);
    }
    lastPing = millis();
  }
}
```