SalesforceからSlackに投稿する仕組みを作ってみた

2022.02.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Salesforceエンジニアの にしたに です。

SalesforceからSlackにメッセージを投稿する仕組みがあったら便利だと思うことはないですか?
この記事では、SalesforceからSlackへの投稿を実現するための方法の一例をご紹介していきます。

背景

弊社では、SalesforceからSlackへの投稿にSalesforce to Slackという未管理パッケージを使っています。

こちらはインストールするとカスタムオブジェクトが作成され、そのオブジェクトにレコードを作成することで内容がSlackに投稿されるという、とてもシンプルで使いやすいパッケージです。

ですがこちら、最後のコミットが2016年となっており特にメンテされていないようでした。
中身を見てみると、Slack Incoming Webhookの方式を使っておりSlack公式によると以下のように非推奨となっています。

These integrations lack newer features and they will be deprecated and possibly removed in the future. We do not recommend their use.

Incoming Webhooks

ということで、Slack Incoming Webhookの新方式を使ってSalesforceからSlackへの投稿を行う仕組みを作ってみた、というのがこの記事の内容になります。

この記事の対象者

SalesforceからSlack投稿する仕組みが欲しい方
かつSalesforce開発者の方

Apexコードは書けないよ!という方はSlack公式が出している以下の記事を参考にしてみてください。
Slack で Salesforce を利用する

パッケージを導入することでフローとプロセスビルダーにてSlackメッセージを投稿することが可能となるようです。
が、Apex ClassからSlackに投稿することは出来ないようでしたので自作することにしました。

ゴール

SlackMessageオブジェクトにレコードが作成されるとトリガが起動し、Incoming Webhookの新方式でSlackに投稿される仕組みを作成する。

Slackの準備

まずはSlackの準備を行います。

手順はSlack公式ページの通りなのですが、一応画像付きで手順を紹介していきます。

  • Slackアプリを作成する

アプリ作成画面に遷移し、From Scratchをクリックします。

アプリ名と投稿したいSlackのワークスペースを入力し、Create Appボタンを押してアプリを作成します。

  • Incoming Webhookを有効にする

作成が完了したら、Incoming WebhooksActivate Incoming WebhooksONにします。

  • Incoming Webhookを作成する

下スクロールしAdd New Webhook to WorkspaceをクリックすることでSlackチャンネルを選択できます。
そこで選択したチャンネルに対し、Webhook URLが発行されます。

このURLにPOSTすることでSlackにメッセージを投稿することができます。
Salesforce側に設定するため控えておいてください。

以上でSlack側の準備は完了です。

Salesforceの準備

次にSalesforce側で準備を行います。

カスタムオブジェクトの作成

まずはSlackに投稿するメッセージを格納するため、SlackMessageオブジェクトを作成します。

以下通りにカスタムオブジェクトとカスタム項目を作成します。

  • SlackMessage (SlackMessage__c)
表示ラベル API参照名 データ型
テキスト Text__c ロングテキストエリア(20000)1
チャンネルID ChannelId__c テキスト(20)2

※オブジェクトを作成した後のレイアウト設定や権限設定、タブの設定などは省略します。

カスタムメタデータの作成

次に、チャンネルIDとWebhook URLを結びつけるためにカスタムメタデータを作成します。
カスタムメタデータの説明は趣旨からずれるので他に譲りますが、非常にざっくり言うとSalesforceのカスタムオブジェクトのような形の設定データです。

カスタムオブジェクトのようにデータ型を持てたり、設定値を変更セットに詰めてリリースできるなどの利点があります。

まずは以下のカスタムメタデータ型とカスタム項目を作成します。

  • SlackWebhookSetting (SlackWebhookSetting__mdt)
表示ラベル API参照名 データ型
チャンネルID ChannelId__c テキスト(20)
WebhookUrl WebhookUrl__c URL(255)

次に、作成したカスタムメタデータにレコード(設定値)を追加します。

  • カスタムメタデータ型の設定画面に戻り、「レコードの管理」アクションをクリックします。

  • 新規ボタンをクリックし、レコードの追加を行います。
    Slack投稿したいチャンネルのチャンネルIDとWebhook URLを入力し、保存します。

リモートサイトの設定

SalesforceからSlack Webhookをコールできるようにするため、リモートサイトの設定を行う必要があります。
新規リモートサイトからリモートサイトの URLhttps://hooks.slack.comを入力し、保存します。

以上で、Apexコードを書く前の準備作業が完了しました。

Apex Class、Triggerの作成

ではいよいよ、コードを書いていきましょう。

Slack Incoming Webhookに投稿するためのHTTP POSTリクエストの中身は以下の通りです。

POST https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX  
Content-type: application/json  
{  
    "text": "Hello, world."  
}

シンプルですね。textのみの場合デフォルトでmrkdwn記法でフォーマットされます。
(ややこしいですが、一般的なMarkdownではないので注意してください。)
試しにPostmanなどのREST APIを投げられるツールを使い、Slack Webhook URLに以下のリクエストを送信してみます。

{  
    "text": "This is test text :smile: *this is bold*, and ~this is crossed out~"  
}

フォーマットされた状態でメッセージが投稿されることが確認できます。

テキストの記法について詳しくはこちらを参照してください。
また、Block Kitという、より高度なメッセージを投稿する仕組みもあります。気になる方は調べてみてください。

では、上記を踏まえてまずはSlackとの通信を行うクラスを作成します。

/**
 * Slack Webhookと通信し、Slackにメッセージを投稿するクラス
 */
public class Slack {
    /**
     * Slackにメッセージを投稿する
     * @param slackMsg SlackMessage__cオブジェクト
     * @param url 投稿先のSlack Webhook URL
     */
    public static void sendMessage(SlackMessage__c slackMsg, String url) {
        // テキストが空の場合、処理スキップ
        if (String.isEmpty(slackMsg.Text__c)) {
            System.debug('slackMsg.Text__c is Empty');
            return;
        }
        Message msg = new Message();
        msg.text = slackMsg.Text__c;

        sendMessage(msg, url);
    }
    
    /**
     * Slackにメッセージを投稿する
     * @param msg Slackに投稿するメッセージが格納されたMessageクラス
     * @param url 投稿先のSlack Webhook URL
     */
    private static void sendMessage(Message msg, String url) {
        sendMessage(JSON.serialize(msg), url);
    }
    
    /**
     * Slackにメッセージを投稿する
     * @param msg Slackに投稿するメッセージ
     * @param url 投稿先のSlack Webhook URL
     */
    @future (callout=true)
    private static void sendMessage(String msg, String url) {
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(url);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/json');
        req.setBody(msg);
        HttpResponse res = http.send(req);
    }
    
    public class Message {
        public String text {get;set;}
    }
}

次に、SlackMessage__cオブジェクトのトリガハンドラを作成します。

/**
 * SlackMessage__cのトリガハンドラ
 */
public with sharing class SlackMessageTriggerHandler {
    /**
     * 作成後処理
     * 作成されたSlackMessageオブジェクトをSlackに投稿する
     */
    public void afterInsert(Map<Id, SlackMessage__c> slackMsgMap) {
        
        // URL情報を全取得
        List<SlackWebhookSetting__mdt> slackWebhookSettingList = [SELECT ChannelId__c, WebhookUrl__c FROM SlackWebhookSetting__mdt];

        // 作成されたレコードごとに処理
        for (Id key : slackMsgMap.keySet()) {
            SlackMessage__c slackMsg = slackMsgMap.get(key);

            for (SlackWebhookSetting__mdt setting : slackWebhookSettingList) {
                // チャンネルIDが合致した場合、Slackに投稿
                if (setting.ChannelId__c == slackMsg.ChannelId__c) {
                    Slack.sendMessage(slackMsg, setting.WebhookUrl__c);
                }
            }
        }
    }
}

最後に、トリガを作成して完了です。

trigger SlackMessageTrigger on SlackMessage__c (after insert) {

    SlackMessageTriggerHandler handler = new SlackMessageTriggerHandler();

    switch on Trigger.operationType {
        when AFTER_INSERT {
            handler.afterInsert(Trigger.newMap);
        }
    }
}

動作確認

では最後にSlackMessageオブジェクトを作成し、Slackチャンネルにメッセージが投稿されることを確認します。

カスタムメタデータに設定したチャンネルIDとメッセージを入力し、保存を押すと…。

投稿されたメッセージが確認できました。 お疲れさまでした!

さいごに

SalesforceからSlackにメッセージを投稿する仕組みのご紹介でした。
今回は例としてシンプルなコードにしていますが、エラーハンドリングの仕組みなど適宜付け足してご活用ください。

どなたかの参考になれば幸いです。


  1. 現在、4万文字を超えると切り捨てられる仕様になっているようです。今後、より短くなってしまうことを考えて2万文字を上限に設定しています。 
  2. 変更される可能性があるチャンネル名ではなく、チャンネルIDをキーに設定します。現在、チャンネルIDの長さは11文字ですが、将来的に長くなる可能性があるということで余裕を持たせています。