Slack - 通知の送信

にメンテナンス済み

kintone と Slack を連携させることで、レコードの作成・更新時に Slack チャンネルへ自動通知を送ることができます。kintone 標準の Slack 連携では通知内容や送信先を細かく制御できないため、任意のチャンネルへの通知やメッセージ内容の組み立てを行いたい場合は、JavaScript カスタマイズが有効です。

この記事では、Slack の Incoming Webhook と kintone.proxy を使って、kintone から Slack に通知を送る方法を解説します。

事前準備

Slack Incoming Webhook の設定

  1. Slack API のアプリページ にアクセスし、新しいアプリを作成(「Create New App」→「From scratch」)するか、既存のアプリを選択します
  2. 「Incoming Webhooks」を有効にします
  3. 「Add New Webhook to Workspace」から通知先のチャンネルを選択します
  4. 生成された Webhook URL をコピーします

Webhook URL は以下のような形式です。

https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
チェック

Webhook URL を知っていれば誰でもそのチャンネルに投稿できます。この記事のサンプルコードは説明のため URL を直書きしていますが、実運用ではソースコード上にハードコーディングせず、アクセス権を絞った設定用アプリなどで管理してください(後述)。

基本的な通知の送信

シンプルなテキスト通知

最もシンプルな Slack 通知の実装です。

slack-simple.js
(() => {
  'use strict';

  /**
   * Slack の Incoming Webhook にメッセージを送信する
   * @param { Object } params
   * @param { string } params.webhookUrl - Slack Webhook URL
   * @param { string } params.message - 送信するメッセージ
   * @returns { Promise<[string, number, Object]> } kintone.proxy のレスポンス
   */
  const sendSlackMessage = (params) => {
    const { webhookUrl, message } = params;

    return kintone.proxy(
      webhookUrl,
      'POST',
      { 'Content-Type': 'application/json' },
      JSON.stringify({ text: message })
    );
  };

  // 使用例
  const WEBHOOK_URL = 'https://hooks.slack.com/services/xxx/yyy/zzz';

  sendSlackMessage({
    webhookUrl: WEBHOOK_URL,
    message: 'kintone からのテスト通知です',
  }).then(([body, statusCode]) => {
    console.log('送信結果:', statusCode);
  });
})();
kintone.proxyの使い方
kintone JavaScript カスタマイズから外部 API を呼び出す際に必要な kintone.proxy の使い方を解説します。GET/POST/PUT/DELETE の各メソッドの使い方、リクエストヘッダーの設定、レスポンスの

レコード保存時に自動通知する

レコードの作成・更新が成功したタイミングで Slack に自動通知を送る実装です。

slack-on-submit.js
(() => {
  'use strict';

  const WEBHOOK_URL = 'https://hooks.slack.com/services/xxx/yyy/zzz';

  /**
   * Slack に通知を送信する
   * @param { string } message - メッセージ
   */
  const notifySlack = async (message) => {
    const [, statusCode] = await kintone.proxy(
      WEBHOOK_URL,
      'POST',
      { 'Content-Type': 'application/json' },
      JSON.stringify({ text: message })
    );

    if (statusCode !== 200) {
      console.error('Slack通知に失敗しました:', statusCode);
    }
  };

  // レコード作成成功時に通知
  kintone.events.on('app.record.create.submit.success', async (event) => {
    const record = event.record;
    const recordUrl = `${location.origin}/k/${kintone.app.getId()}/show#record=${event.recordId}`;

    const message = [
      '📝 新しいレコードが作成されました',
      `*タイトル:* ${record['タイトル'].value}`,
      `*作成者:* ${record['作成者'].value.name}`,
      `*リンク:* ${recordUrl}`,
    ].join('\n');

    await notifySlack(message);

    return event;
  });

  // レコード更新成功時に通知
  kintone.events.on('app.record.edit.submit.success', async (event) => {
    const record = event.record;
    const recordUrl = `${location.origin}/k/${kintone.app.getId()}/show#record=${event.recordId}`;

    const message = [
      '✏️ レコードが更新されました',
      `*タイトル:* ${record['タイトル'].value}`,
      `*更新者:* ${record['更新者'].value.name}`,
      `*リンク:* ${recordUrl}`,
    ].join('\n');

    await notifySlack(message);

    return event;
  });
})();
チェック

submit.success イベントは、レコードの保存が成功した後に発火します。このタイミングで通知を送ることで、保存に失敗した場合に不要な通知が送られるのを防げます。

リッチなメッセージの送信

Slack のメッセージフォーマットを使って、より見やすい通知を送ることができます。

Block Kit を使ったリッチメッセージ

slack-blocks.js
/**
 * Block Kit を使ったリッチな Slack 通知を送信する
 * @param { Object } params
 * @param { string } params.webhookUrl - Webhook URL
 * @param { Object } params.record - kintone レコード
 * @param { string } params.recordUrl - レコードのURL
 */
const sendRichSlackMessage = (params) => {
  const { webhookUrl, record, recordUrl } = params;

  const payload = {
    blocks: [
      {
        type: 'header',
        text: {
          type: 'plain_text',
          text: '📋 新しい申請が登録されました',
        },
      },
      {
        type: 'section',
        fields: [
          {
            type: 'mrkdwn',
            text: `*申請者:*\n${record['申請者'].value.name}`,
          },
          {
            type: 'mrkdwn',
            text: `*カテゴリ:*\n${record['カテゴリ'].value}`,
          },
          {
            type: 'mrkdwn',
            text: `*金額:*\n¥${Number(record['金額'].value).toLocaleString()}`,
          },
          {
            type: 'mrkdwn',
            text: `*優先度:*\n${record['優先度'].value}`,
          },
        ],
      },
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `*内容:*\n${record['申請内容'].value}`,
        },
      },
      {
        type: 'actions',
        elements: [
          {
            type: 'button',
            text: {
              type: 'plain_text',
              text: 'kintone で確認する',
            },
            url: recordUrl,
          },
        ],
      },
    ],
  };

  return kintone.proxy(
    webhookUrl,
    'POST',
    { 'Content-Type': 'application/json' },
    JSON.stringify(payload)
  );
};

条件に応じた通知先の切り替え

フィールドの値に応じて通知先のチャンネル(Webhook URL)を切り替える例です。

slack-conditional-channel.js
(() => {
  'use strict';

  /** カテゴリごとの Webhook URL */
  const WEBHOOK_URLS = {
    営業: 'https://hooks.slack.com/services/xxx/sales/zzz',
    開発: 'https://hooks.slack.com/services/xxx/dev/zzz',
    総務: 'https://hooks.slack.com/services/xxx/general/zzz',
  };

  /** デフォルトの通知先 */
  const DEFAULT_WEBHOOK = 'https://hooks.slack.com/services/xxx/default/zzz';

  kintone.events.on('app.record.create.submit.success', async (event) => {
    const record = event.record;
    const category = record['部署'].value;

    const webhookUrl = WEBHOOK_URLS[category] || DEFAULT_WEBHOOK;

    const message = `新しい申請: ${record['タイトル'].value} (${category})`;

    await kintone.proxy(
      webhookUrl,
      'POST',
      { 'Content-Type': 'application/json' },
      JSON.stringify({ text: message })
    );

    return event;
  });
})();

エラーハンドリング

Slack 通知の送信に失敗しても、レコードの保存処理自体には影響を与えたくない場合のエラーハンドリングパターンです。

slack-error-handling.js
/**
 * Slack 通知を送信する(失敗してもエラーをスローしない)
 * @param { string } webhookUrl - Webhook URL
 * @param { string } message - メッセージ
 * @returns { Promise<boolean> } 送信成功の場合は true
 */
const sendSlackSafe = async (webhookUrl, message) => {
  try {
    const [, statusCode] = await kintone.proxy(
      webhookUrl,
      'POST',
      { 'Content-Type': 'application/json' },
      JSON.stringify({ text: message })
    );

    if (statusCode !== 200) {
      console.warn(`Slack通知に失敗しました (HTTP ${statusCode})`);
      return false;
    }

    return true;
  } catch (error) {
    console.warn('Slack通知でエラーが発生しました:', error);
    return false;
  }
};
チェック

通知の送信失敗がユーザーの業務に影響を与えないよう、try-catch で囲んでエラーを吸収するのが一般的なパターンです。通知は「ベストエフォート」として扱い、失敗してもレコード操作は正常に完了させましょう。

Webhook URL を安全に管理する

kintone のカスタマイズ JavaScript はブラウザに配信されるため、ソースコードに書いた Webhook URL は、アプリを利用できるユーザーであれば開発者ツールなどから確認できます。閲覧権限を限定した設定用アプリに保存して取得する方法を推奨します。

webhook-config.js
/**
 * 設定アプリから Webhook URL を取得する
 * @param { string | number } configAppId - 設定用アプリのID
 * @returns { Promise<string> } Webhook URL
 */
const getWebhookUrl = async (configAppId) => {
  const response = await kintone.api(
    kintone.api.url('/k/v1/records.json', true),
    'GET',
    {
      app: configAppId,
      query: '設定キー = "SLACK_WEBHOOK_URL"',
      fields: ['設定値'],
    }
  );

  if (response.records.length === 0) {
    throw new Error('Webhook URL の設定が見つかりません');
  }

  return response.records[0]['設定値'].value;
};
チェック

設定用アプリに保存した場合も、そのアプリを閲覧できるユーザーは URL を確認できます。閲覧を許可するのは、通知機能を使うユーザーに限定してください。URL を利用者にも見せたくない場合は、kintone の Webhook などを使い、サーバー側から Slack へ送信する構成を検討してください。

定数定義
定数定義は本当に様々な方法で実装可能なため、どのように実装するのがベストなのか、今でもよく悩むところです。今回は、僕が実際に採用してうまくいった方法を紹介したいと思います。

まとめ

  • kintone.proxy と Slack の Incoming Webhook を使って、kintone から Slack に通知を送信できる
  • submit.success イベントを使うことで、レコード保存成功後に自動通知が可能
  • Block Kit を使ってリッチなメッセージフォーマットで通知できる
  • フィールドの値に応じて通知先チャンネルを切り替えることも可能
  • 通知の失敗がレコード操作に影響しないよう、エラーハンドリングを適切に行う
  • Webhook URL はソースコード以外の安全な場所で管理する
#kintone #JavaScript #Slack