Flutterの通報をSlackに通知し、Firestoreのデータを削除する
Flutter×Firebaseの構成のSNSモバイルアプリを開発中です。その中で通報機能を作成したのですが通報内容をボタン付きでSlackに送信して削除ボタンを押したら通報された投稿を削除しに行く一連の流れを構成し、通報対応を簡易かつプログラミングの知識なしで行うようにしました。使用する言語はDartとNode.jsです
1、FlutterでSlack APIにPOSTリクエストし、削除ボタン付きのメッセージを送信
2、削除ボタンをクリックし、Slackがそのイベント内容をPOSTリクエストでCloudFunctionsで生成したエンドポイントに対して送信
3、CloudFunctionsでリクエストを受け取りFirestoreのデータを削除しに行く
今回のアーキテクチャは以下の通りです
・Slack APIについて以前も取り上げたのでこちら参考までに
FlutterからSlackにボタン付きメッセージを送信
以下の画面にてSlackワークスペースと連携してURLを生成しましょう(これはのちに使います)
サンプルを見ればわかると思うのですがHTTP POSTリクエストで生成したURLに対してjsonタイプのデータを送信するとSlackにメッセージを送信することできます。
DartでHTTPリクエストを送ります。ライブラリはhttpを使います。
通報をSlackに通知するメソッド
static Future<void> slack(PostModel postData, String reasonValue) async {
const String slackToken =
'<取得したToken>';
Uri slackUri = Uri.parse("https://slack.com/api/chat.postMessage");
Map<String, Object> data = {
"channel": "<送信したいチャンネルID>",
"text": postData.title,
"attachments": [
{
"fallback": "fallback string",
"title": "このスレッドを削除しますか?",
"callback_id": "callback_id value",
"color": "#FF0000",
"attachment_type": "default",
"actions": [
{
"name": "削除",
"text": "削除",
"type": "button",
"style": "danger",
"value": postData.postId,
"confirm": {
"title": "本当に削除しますか?",
"text": "このスレッドは削除されてしまいます",
"ok_text": "Yes",
"dismiss_text": "No"
}
}
]
}
]
};
final http.Response response = await http.post(
slackUri,
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer $slackToken",
},
body: json.encode(data),
);
print(response);
}
送信できるとこのような感じで表示されます。
SlackボタンアクションイベントをCloudFunctionsに送信
Slack APIのInteractivityはクリックイベントの内容を設定したエンドポイントに対してHTTP POSTリクエストを送信します。ここでCloudFunctionsのHTTPトリガのURLを貼り付けます。
CloudFunctionsでFirestoreのデータを削除
CloudFunctionsでSlackからのHTTP POSTリクエストをイベントを元にFirestoreのデータを削除します。今回はpostコレクションとそのサブコレクションも削除します。
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
// 通報されたスレッド削除
exports.deletePost = functions.region("asia-northeast1")
.https.onRequest(async function(req, res) {
const payload = JSON.parse(req.body.payload);
// ドキュメントIDを受け取る
const postId = payload["actions"][0]["value"];
const deleteDocumentRecursively = async function(postId) {
const collections = await postId.listCollections();
if (collections.length > 0) {
for (const collection of collections) {
const snapshot = await collection.get();
for (const doc of snapshot.docs) {
await deleteDocumentRecursively(doc.ref);
}
}
}
await postId.delete();
};
await deleteDocumentRecursively(admin.firestore().collection("post").doc(postId));
res.status(200).send("削除成功!");
});
補足説明
application/x-www-form-urlencoded について
SlackからのHTTP POSTリクエストのContent-typeはapplication/jsonではなくapplication/x-www-form-urlencodedでした。エンコードされたurlでデータが受送信されます。(ex. a=1&b=1 → %20a%3D1%26b%3D1)なのでリクエストを受け取った時にデコードする必要があります。
Node.jsの場合
const payload = JSON.parse(req.body.payload);
私は主にPythonに書いているので実験用で今回はAWS Lambda、CloudFunctionsでも試しました。
AWS Lambdaの場合
import json
import urllib.request
import urllib.parse
def lambda_handler(event, context):
data = event["body"]
payload = urllib.parse.unquote(data)
payload = payload.replace("payload=", "")
params = json.loads(payload)
CloudFunctionsの場合
import json
import urllib.request
import urllib.parse
def hello_world(request):
# get_data()はPOSTされたデータをそのまま受け取れる
data = request.get_data()
payload = urllib.parse.unquote(data)
payload = payload.replace("payload=", "")
params = json.loads(payload)
参考文献
・slack APIについて
・cloud functionsについて
Discussion