LoginSignup
6
2

More than 1 year has passed since last update.

SORACOM Beam から Cloudflare Workers 経由で Slack へ POST する

Last updated at Posted at 2022-05-14

(2022-05-14 23:10 追記: KV を使ってみたので、最後に追記します)

はじめに

前々から Cloudflare Workers が気になっていたので、SORACOM と繋げられるか試してみました。本当は R2 や D1 と絡めてみたかったのですが、贅沢は言わずにまずは Workers だけで試してみます。

事前準備

SORACOM のデバイスは SORACOM LTE-M Button for Enterprise を使いました。Slack のアカウントも準備しておきます。

手順

Slack の Incoming Webhook の準備

Slack のドキュメント Slack での Incoming Webhook の利用 | Slack を参考に、Incoming Webhook を準備します。

Cloudflare Workers のセットアップ

まずは、https://www.cloudflare.com/ja-jp/ からサインアップをします。

Cloudflare workers を作っていきます。

image.png

サブドメインを決めます。

image.png

無料プランを使います。

image.png

サインアップ時にメール認証をまだしていなかったら認証します。

image.png

できました。

image.png

次に CLI をセットアップしました (参考: Wrangler (command line) · Cloudflare Workers docs)。NodeJS が使える環境で、以下のコマンドでインストールします。

npm install -g wrangler

ログインは以下のコマンドで。

wrangler login

別途ブラウザを開いて OAuth してくれるのがありがたいです。

setup-cli1.png

初期化します。英語ですが、対話形式でセットアップしてくれるのが良かったです。TypeScript のセットアップもしてくれました。

wrangler init my-worker

ローカルで起動します。

cd my-worker
wrangler dev src/index.ts

以後、index.ts ファイルを編集・保存すると自動で更新してくれるのも良かったです。テストの実行は localhost 宛に curl してできます。何もいじっていない状態で、Hello World がちゃんと出てきました。console.log() のデバッグ結果なんかも wrangler dev しているウィンドウで出てきます。

$ curl localhost:8787
Hello World

デプロイも簡単です。

wrangler publish

Cloudflare Workers から Slack への POST

Cloudflare はかなりサンプルが充実していまして、ここから必要なものだけ拾っていけばやりたいことは実現できました。

今回は「ヘッダ認証」「POST で JSON を受け取り」「Slack へ POST」をしたかったので、以下のサンプルを参考にしました。

書き方 (特に TypeScript が久しぶりだったので...) が正しいかはさておき、こんなプログラムを書きました。

const PRESHARED_AUTH_HEADER_KEY = 'X-Custom-PSK';
const PRESHARED_AUTH_HEADER_VALUE = 'mypresharedkey';
const SLACK_URL = '###Slack の Incoming Webhook の準備」で控えた URL###';

export default {
  async fetch(request: Request): Promise<Response> {
    const requestHeaders = request.headers;

    if (requestHeaders.get(PRESHARED_AUTH_HEADER_KEY) != PRESHARED_AUTH_HEADER_VALUE) {
      return new Response("Invalid key", {
        status: 403
      });
    }

    if (request.method != 'POST') {
      return new Response("Method Not Allowed", {
        status: 405
      });
    }

    if (requestHeaders.get('content-type') != 'application/json') {
      return new Response("Unsupported Media Type", {
        status: 415
      });
    }

    const requestInput = JSON.stringify(await request.json());

    const message = JSON.stringify({
      "text": requestInput
    });

    const requestInit: RequestInit = {
      body: message,
      method: 'POST',
      headers: {
        'content-type': 'application/json;charset=UTF-8',
      },
    };

    const url = SLACK_URL;
    const response = await fetch(url, requestInit);

    return new Response(await response.text())

  },
};

実際に Slack へ投稿されるか、まずはローカルで検証します。

curl -X POST -d '{"foo":"bar"}' http://localhost:8787 -H 'X-Custom-PSK:mypresharedkey' -H 'content-type: application/json'

成功すると、こんな感じで出てきます。

image.png

wrangler publish でデプロイして、https://{プロジェクト名}.{サブドメイン}.workers.dev へのリクエストでも成功するか確認します。

curl -X POST -d '{"hoge":"fuga"}' https://{プロジェクト名}.{サブドメイン}.workers.dev -H 'X-Custom-PSK:mypresharedkey' -H 'content-type: application/json'

SORACOM のセットアップ

今回は、SORACOM LTE-M Button for Enterprise (Enterprise Button) を使いました。このデバイスは UDP で SORACOM へデータを送るので、SORACOM Beam の UDP → HTTP/HTTPS エントリポイントを使います。デバイスや SORACOM Beam のセットアップ詳細は、ドキュメントを参照してください。

まず、Enterprise Button を適当なグループへ所属させます。

image.png

次に、グループ名をクリックして、グループ設定画面にて「SORACOM Air for セルラー設定 > バイナリーパーサー」と「SORACOM Beam 設定」を編集・保存します。

「SORACOM Air for セルラー設定 > バイナリーパーサー」ではのフォーマットでは @button とカスタムフォーマットを設定します。

image.png

「SORACOM Beam 設定」では「設定を追加する」をクリックして、以下のように設定します。

設定項目 設定内容 備考
プロトコル HTTPS -
転送先 - ホスト名 https://{プロジェクト名}.{サブドメイン}.workers.dev Cloudflare の設定
ポート番号 443 -
パス 空欄 -
カスタムヘッダ - アクション 追加 -
カスタムヘッダ - ヘッダ名 X-Custom-PSK Cloudflare の設定に合わせます
カスタムヘッダ - 値 mypresharedkey Cloudflare の設定に合わせます

image.png

image.png

SORACOM LTE-M Button for Enterprise からデータを送信

では試してみましょう...

きました!これだけだと Cloudflare から来ているかわからないのが残念ですが、できました。

まとめ

Cloudflare Workers、個人的には Amazon API Gateway + Lambda という感じでとても気に入りました。さらっと書けるので、Slack や LINE などへの投稿はこれで良いかも。次は KV、 R2 や D1 との組み合わせもやっていきたいと思います。

追記 - KV を使ってみた

KV を使ってみました。別記事に起こすほどではなかったので追記します。ただ、ちょっと使い方が正しいかは怪しいです。。本当は env を使わず、こちらのドキュメント を参照しつつ KVNamespace 型を定義するだけでいけると思ったのですが、想定したように動作しなかったのでいったん env を使っています。

まず、KV を作成します。--preview はローカルテスト用です。

$ wrangler kv:namespace create "TEST_KV"
:namespace create "TEST_KV" --preview ⛅️ wrangler 2.0.5 
-------------------[WARNING] No configured name present, using `worker` as a prefix for the title


🌀 Creating namespace with title "worker-TEST_KV"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "TEST_KV", id = "XXXXXXXXXXXXXXXXXXXXXXXXXXX" }
$ wrangler kv:namespace create "TEST_KV" --preview
 ⛅️ wrangler 2.0.5 
-------------------[WARNING] No configured name present, using `worker` as a prefix for the title


🌀 Creating namespace with title "worker-TEST_KV_preview"
✨ Success!
Add the following to your configuration file in your kv_namespaces array:
{ binding = "TEST_KV", preview_id = "YYYYYYYYYYYYYYYYYYYYYYYYYYYY" }

得られた idpreview_idwrangler.toml に追記します。binding の名前は任意で良いです。

kv_namespaces = [
  { binding = "KV", id = "XXXXXXXXXXXXXXXXXXXXXXXXXXX", preview_id = "YYYYYYYYYYYYYYYYYYYYYYYYYYYY" }
]

KV に Key, Value ペアを追加していきます。先ほど得られた idpreview_id を使って CLI でやるのが楽です。今回は最初の例でグローバル変数にしていたものを KV に入れていきます。

id="XXXXXXXXXXXXXXXXXXXXXXXXXXX"
wrangler kv:key put PRESHARED_KEY "mypresharedkey" --namespace-id ${id}
wrangler kv:key put PRESHARED_KEY_HEADER "X-Custom-PSK" --namespace-id ${id}
wrangler kv:key put SLACK_URL "###Slack の Incoming Webhook の準備」で控えた URL###" --namespace-id ${id}

id="YYYYYYYYYYYYYYYYYYYYYYYYYYYY"
wrangler kv:key put PRESHARED_KEY "mypresharedkey" --namespace-id ${id}
wrangler kv:key put PRESHARED_KEY_HEADER "X-Custom-PSK" --namespace-id ${id}
wrangler kv:key put SLACK_URL "###Slack の Incoming Webhook の準備」で控えた URL###" --namespace-id ${id}

プログラムも、KV を使うように修正します。せっかくなので、ボタンをクリックされた回数をカウントアップするようにしてみました。

export default {
  async fetch(request: Request, env): Promise<Response> {
    // Get parameters from KV
    const preSharedKey: string = await env.KV.get("PRESHARED_KEY");
    const preSharedKeyHeader: string = await env.KV.get("PRESHARED_KEY_HEADER");
    const slackUrl: string = await env.KV.get("SLACK_URL");

    // Confirm KV value is not null
    if (!(preSharedKey && preSharedKeyHeader && slackUrl)) {
      return new Response("Internal Server Error", {
        status: 500,
      });
    }

    const oldCount = await env.KV.get("COUNT");
    let newCount: Number;
    if (!oldCount) {
      newCount = 1;
    } else {
      newCount = Number(oldCount) + 1;
    }
    await env.KV.put("COUNT", newCount);

    // Authenticate with header
    const requestHeaders = request.headers;
    if (requestHeaders.get(preSharedKeyHeader) != preSharedKey) {
      return new Response("Invalid key", {
        status: 403,
      });
    }

    // Only allow POST request
    if (request.method != "POST") {
      return new Response("Method Not Allowed", {
        status: 405,
      });
    }

    // Only allow "application/json" content-type
    if (requestHeaders.get("content-type") != "application/json") {
      return new Response("Unsupported Media Type", {
        status: 415,
      });
    }

    // Post data to Slack using data from client
    const requestInput = JSON.stringify(await request.json());

    const message = JSON.stringify({
      text: "Click count is " + newCount + ". Value is " + requestInput,
    });

    const requestInit: RequestInit = {
      body: message,
      method: "POST",
      headers: {
        "content-type": "application/json;charset=UTF-8",
      },
    };

    const response = await fetch(slackUrl, requestInit);

    return new Response(await response.text());
  },
};

最後にデプロイすれば、KV を使うように変更されます。図のように、カウントアップすることが確認できました。

image.png

ただドキュメントを読んだところ、「一般的に、書き込みは比較的頻繁には行わず、読み取りは迅速かつ頻繁に行う必要があるユースケースに向いている。(DeepL 訳)」なので、複数デバイスからのデータアップロードで同じ KV を書き換えるとかはやめた方が良さそうです。

とはいえ、やはりシンプルに使える良さがあるので、SORACOM との絡め方も今後考えていきたいと思います。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2