※2022/06/13 追記
強引ではない方法が分かったので、別に記事を書きました!
概要
- Bolt.js と GAE とスプレッドシートを組み合わせてアプリを作ろうとした。
- slack からイベントが発行されて 3 秒以内に返さないとリトライされてしまうという所謂 3 秒ルールにぶち当たった。
- つまり、3 秒以内に応答しない場合に、同じ返信を複数回実行してしまう状態になった。
- 試行錯誤を繰り返したが上手く行かず、結局自前で用意した Express でリクエストを捌いて解決した。
- あんまりきれいなやり方では無いです。
- 追記: twitter で Slack の開発者からよりきれいな解決方法を教えてもらいました。 https://twitter.com/seratch_ja/status/1533922381550731264?s=20
結論
import { App, ExpressReceiver } from "@slack/bolt";
import express from "express";
// アプリの設定など
const expressReceiver = new ExpressReceiver({
signingSecret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
endpoints: "/events",
processBeforeResponse: true,
});
const app = new App({
receiver: expressReceiver,
token: "xoxb-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
processBeforeResponse: true,
});
// 処理
app.event("message", async ({ say }) => {
say("Hello world!");
});
// bolt.js とは別にインストールしておいた Express でエンドポイントを定義
const expressApp = express();
expressApp.post("/events", (req, res)=> {
// リクエストヘッダーでリトライかどうかを判断してリトライの場合は握りつぶす。
if (req.headers["x-slack-retry-num"] || req.headers["X-Slack-Retry-Num"]) {
res.send(JSON.stringify({ message: "No need to resend" }));
return;
}
expressReceiver.app(req, res);
})
// express を起動しておく
expressApp.listen(8080, () => {
console.log("⚡Slack Bolt is running!");
})
こうせざるを得なかった理由
- スプレッドシートとの通信が遅く、かなりの確率でリトライが発生し、無駄にアプリが返信してしまう。
- Bolt.js は Express をラップしているが、リクエストヘッダーをみてフィルターする、のような処理を注入できない。
- これができれば理想だった。
- カスタムの Receiver を作るという方法もあったが、実装コストがかかりすぎると感じたため断念。
- socket モードも検討したが、GAE のインスタンスは常駐できないので不可能だった。
参考
※2022/06/7 追記
Bolt.js の開発者が親切にも解決方法を教えてくれました!
比較的最近追加された機能でドキュメントにちゃんと書いてないのが申し訳ないのですが customPropertiesExtractor というのを使えば request header も context に渡せます。 https://t.co/YVJW8vnvAp #SlackDevJP
— Kazuhiro Sera (瀬良) (@seratch_ja) June 6, 2022