Salesforce→Slackに通知を送る

Salesforce Tech

2019-10-17: Slack本家からとても使いやすいアプリが発表されました。
これはこれでとても使いやすく、ChatterではなくSlackとSalesforceとをシームレスに使えるできるのはとても良さそう。
然しながら今回はレコードの作成や更新の通知を確認したいなーと思うので、このプラグインの紹介はまた今度。

Salesforce and Slack: Unlocking collaboration for sales and service | The Official Slack Blog
https://slackhq.com/salesforce-and-slack-for-sales-and-service

目的

レコードの新規作成や更新の通知を受け取りたい。
また、全ての処理をコードで書くのではなくGUI上で変更することも可能にしたい。

あくまでコードを変更する回数を減らすという目的なので、内容によってはApexで書いた方が工数が掛からないケースも十分考えられます。

WIP:attachmentとかBlockで送る

構築

プロセスビルダーで送信する内容を記載・修正できるようにします。
1つApex Classを作成して、送信する内容は数式で生成します。

Salesforce to Slack Integration – Cloud Daemon
https://cloud-daemon.com/salesforce-slack-integration/

とても参考になりました、ありがとうございます。

Apex Classの作成(Sandbox環境で)

こんな感じのApex Classを作成します。
@InvocableVariable はプロセスビルダーから設定できる値になります。
@InvocableMethod はプロセスビルダーでApexを選択するときに表示されるものなので、わかりやすい名前にすると良いです。

public class PostToSlackWebhook {
     
    public class slackRequest {
        @InvocableVariable(label='webhookURL')
        public String webhookURL;
        @InvocableVariable(label='Channel/User to post to' required=true) 
        public String channel;
        @InvocableVariable(label='icon_emoji')
        public String iconEmoji;
        @InvocableVariable(label='投稿者-姓' required=true)
        public String postUserLastName;
        @InvocableVariable(label='投稿者-名')
        // 名はSalesforce側でrequiredではないため、属性は付与しない
        public String postUserFirstName;
        @InvocableVariable(label='Slack Text Message' required=true)
        public String slackTextMessage; 
    }
    
    @InvocableMethod(label='Post to Slack')
    public static void publishToSlack(List<slackRequest> requests) {
        for(slackRequest r:requests){
            JSONGenerator gen = JSON.createGenerator(true);
            gen.writeStartObject(); //Inserts {
            
            if (r.postUserFirstName != null) {
                gen.writeStringField('username',  r.postUserLastName + ' ' + r.postUserFirstName );
            } else {
                gen.writeStringField('username',  r.postUserLastName);
            }
            if (r.channel != null) {
                gen.writeStringField('channel', r.channel);
            }
            if (r.iconEmoji != null) {
                gen.writeStringField('icon_emoji', ':' + r.iconEmoji + ':');
            }
            if (r.slackTextMessage != null) {
                gen.writeStringField('text', r.slackTextMessage);
            }
            gen.writeEndObject(); //Inserts }
            
            if (r.webhookURL == null) {
                r.webhookURL = 'https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxxxxxxxxxxxxxxxxxxxx';
            }
 
            String body = gen.getAsString(); //Translates JSONGenerator to string to be passed to callout
            System.debug(body);
            System.enqueueJob(new qCallOut(r.webhookURL, 'POST', body));
        } 
    }
   
    public class qCallOut implements System.Queueable, Database.AllowsCallouts {
         
        private final String url;
        private final String method;
        private final String body;
         
        public qCallOut(String url, String method, String body) {
            this.url = url;
            this.method = method;
            this.body = body;
        }
         
        public void execute(System.QueueableContext ctx) {
            HttpRequest req = new HttpRequest();
            req.setEndpoint(url);
            req.setMethod(method);
            req.setBody(body);
            Http http = new Http();
            HttpResponse res = http.send(req);
        }
    }
}

こちらはテストクラス。

@isTest
public class testPostToSlackWebhook{
 
    static testmethod void testPublishToSlack(){
        Test.setMock(HttpCalloutMock.class, new mockCallout());
        List<PostToSlackWebhook.slackRequest> requests = new List<PostToSlackWebhook.slackRequest>();
        PostToSlackWebhook.slackRequest r = new PostToSlackWebhook.slackRequest();
        r.webhookURL = 'https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxxxxxxxxxxxxxxxxxxxx';
        r.channel='#general';
        r.iconEmoji = 'v';
        r.postUserLastName = 'APEX';
        r.postUserFirstName = 'TEST';
        r.slackTextMessage = 'THIS IS TEXT MESSAGE'; 
        requests.add(r);
        PostToSlackWebhook.publishToSlack(requests);
    }
    
    public class mockCallout implements HttpCalloutMock
    {
        public HttpResponse respond(HttpRequest request)
        {
            HttpResponse res = new HttpResponse();
            res.setBody( '{"text":"value"}');
            res.setStatusCode(200);
            return res;
        }   
    }
}

プロセスビルダー

新規作成や更新のときに通知を受け取りたいので、最終更新日時が更新されたときに発動するようにします。

Lead
更新日時が更新された時に発動

アクションはApexクラスを選択、デフォルトで表示されるのは先程のApexクラスで @InvocableVariable(required=true) の属性を付与したものだけなので、required になっていないものは “行を追加” から選択してください。

"New Lead" & BR() &
"https://xxxx.lightning.force.com/" & [Lead].Id & BR() &
BR() &
"状況: " & TEXT( [Lead].Status ) & BR() &
"会社名: " & [Lead].Company & BR() &
"氏名 " & [Lead].LastName & " " & [Lead].FirstName 

通知する内容の URLはこれで生成
自サイトのURLはユーザに持っておくと便利じゃないかと思っている。
大抵はベタ打ちで大丈夫だろうけど…。

リモートサイトの設定

SlackのWebhook URLをリモートサイトとして有効にしておかないと、Salesforceから送信ができない。必ず忘れずに対応する。

設定 > セキュリティ > リモートサイトの設定

確認

リードを新規作成・編集したときにSlackに通知が飛んで来ればよし。

飛んだ飛んだ。

良いSalesforce Success ライフを。

自サイトのURLはこの数式で取得できるそうです。

LEFT($Api.Partner_Server_URL_350 ,FIND("/services/Soap/", $Api.Partner_Server_URL_350)) + [Account].Id

絵文字アイコン生成はこちらのサイトが最強に便利です。

絵文字ジェネレーター – Slack 向け絵文字を無料で簡単生成
https://emoji-gen.ninja/