複数のWebサイトを運営していて、それぞれにGoogle Formを設置しています。投稿に気づかず対応が遅れることがあったので、GASでSlackに自動通知する仕組みを作りました。
やりたかったこと
- Google Formに回答が来たらSlackに通知
- フォームごとに通知先チャンネルを変えたい
- フォームが増えても同じコードを何度も書きたくない
構成
GASではスクリプトがフォームと1対1で紐づく(「コンテナ」と呼ばれる制約)ので、1つのスクリプトを複数フォームで共有できません。そこで入口と本体を分離しました。
- 入口スクリプト — 各フォームに配置。呼び出すだけの数行
- Parser — 共通ライブラリ。フォームのレスポンスを解析して通知テキストを組み立てる
- Slack送信 — 共通ライブラリ。Slack APIを叩いて投稿する

入口はこれだけです。
function call(form) {
GoogleFormParser.parse(form)
}
フォーム追加時は入口スクリプトを置いて、スクリプトエディタからトリガー(フォーム送信時に call を実行)を設定するだけ。本体の修正は共通ライブラリ側で完結します。
Slack App を使う
Slackへの通知はIncoming Webhook(レガシー)とSlack Appの2択です。
- レガシーWebhook: 設定は簡単だが、Slack公式が非推奨
- Slack App: 初期設定はやや手間だが、Bot Tokenのスコープ制御・チャンネル指定が柔軟
将来使えなくなるリスクを避けてSlack Appを選びました。Incoming Webhookは hooks.slack.com/services/... 形式のURLにPOSTする方式ですが、こちらはSlack Web APIの chat.postMessage をBot Token認証で呼んでいます。
トークンはGASの PropertiesService に保存しています。
const token = PropertiesService.getScriptProperties().getProperty("SLACK_BOT_TOKEN");
function callWebApi(apiMethod, payload) {
const response = UrlFetchApp.fetch(
`https://slack.com/api/${apiMethod}`,
{
method: "post",
contentType: "application/json; charset=utf-8",
headers: { "Authorization": `Bearer ${token}` },
payload: JSON.stringify(payload),
}
);
return response;
}
function sendMessage(textMessage, channel) {
if (!channel) {
channel = "#general";
}
callWebApi("chat.postMessage", {
text: textMessage,
channel: channel
});
} ![Notion の AI でブログ執筆が爆速化する未来 [AI Writing]](/tcard/c888a0e8-a5e2-4110-bcfb-f7e5d952e487/2022-12-17-c888a0e8-a5e2-4110-bcfb-f7e5d952e487.webp)
