Apex コードで Slack API を実行してみよう

この記事では、Salesforce プラットフォームのプログラミング言語である [Apex](https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_intro_what_is_apex.htm) を使って Slack API を実行する方法を説明します。

使うのはブラウザだけです。以下のサンプルをコピペしたらそのまま動くと思いますので、気軽に試してみてください！

### Salesforce プラットフォームを使ったことがない方へ

この記事を読んでいる方の中には Salesforce プラットフォームを使ったことがない人も多いかもしれません。実は基本的な機能であれば、誰でも簡単に試すことができるのです。

学習のプラットフォームである [Trailhead](https://trailhead.salesforce.com/) のアカウントを作ってみましょう。**すると「Trailhead Playground」と呼ばれる Trailhead でのハンズオン学習用のサンドボックス環境を利用できるようになります。**この記事で紹介する内容も Playground を使っています。

ちなみに、この Trailhead には Salesforce に関するものだけでなく、リーダーシップスキルなどの非常に学びのあるコンテンツもたくさんあります。私は個人的にこのサービスがとても好きで、この記事時点で [148 のバッジをとっています](https://trailblazer.me/id/seratch)。これを機に興味のあるコンテンツを探してみてはいかがでしょうか？

### Apex コードで Slack Web API を実行する

さて、本題です。この記事では Slack チャンネルや DM へのメッセージ投稿のための API である [chat.postMessage](https://api.slack.com/methods/chat.postMessage) API メソッドを実行してみましょう。引数の仕様などは、リンク先の API ドキュメントを参考にしてください（日本語はまだありません :bow: ）。

Playground 環境にアクセスしたら、画面の右上にある歯車をクリックしてみてください。この **Setup** をクリックすると **Developer Console** を開くことができます。今回は、作業にこのコンソールだけを使います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19163/557116c5-6136-f160-b4c0-478f757bd410.png" width=300 />

そして **File > New > Apex Class** で新しいクラスをつくります。名前は `WebClient` にしましょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19163/0d97eaae-e286-7f4d-0780-c96fdbc8e519.png" width=400 />

以下が Apex での API 呼び出しのコード例です。

```apex
public class WebClient {
    private final String token;
    private final String baseUrl;
    private Http http = new Http();

    public WebClient(String token) {
        this(token, 'https://www.slack.com/api/');
    }

    public WebClient(String token, String baseUrl) {
        this.token = token;
        this.baseUrl = baseUrl;
    }
    
    public class Response {
        public HttpResponse httpResponse;
        public Map<String, Object> body;
        public String rawBody;
        public Response(HttpResponse httpResponse) {
            this.httpResponse = httpResponse;
            this.rawBody = httpResponse.getBody();
            this.body = (Map<String, Object>) JSON.deserializeUntyped(this.rawBody);
        }
    }

    public Response callApi(
        String apiMethodName,
        Map<String, Object> params
    ) {
        HttpRequest request = new HttpRequest();
        request.setMethod('POST');
        request.setEndpoint(this.baseUrl + apiMethodName);
        request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        request.setHeader('Authorization', 'Bearer ' + this.token);
        Map<String, String> encodedParams = new Map<String, String>();
        String body = '';
        for (String name : params.keySet()) {
            if (params.get(name) != null) {
                String value = params.get(name).toString();
                String regExp = '%(?![0-9a-fA-F]{2})';
                value = value.replaceAll(regExp, '%25');
                body += '&' + name + '=' + EncodingUtil.urlDecode(value, 'utf-8');
            }
        }
        request.setBody(body.substring(1));
        return new Response(this.http.send(request));
    }
}
```

Slack の Web API はドキュメントを見ると、HTTP メソッドで GET を推奨していたり、POST を推奨していたりしますが、実は全て POST で実行しても問題なく呼び出すことができます。

送信形式が違う [files.upload](https://api.slack.com/methods/files.upload) API とバイナリファイルデータを応答する [admin.analytics.getFile](https://api.slack.com/methods/admin.analytics.getFile) API 以外の API であれば全てこれで実行できるはずです。

また `Content-Type: application/json; charset=utf-8` で送った方がやりやすい API も一部あります。そのようなものについては、このコードを少しカスタマイズして JSON データを作って送るとよいでしょう。

それでは早速実行してみましょう。**Developer Console** のメニューの **Debug** > **Open Execute Anonymous Window** をクリックすると **Execute Anonymous Window** が **Developer Console** 内にポップアップで開きます。ここに実行したいコードを書きます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19163/b01cc0a9-66f0-01d5-70c1-aa04be5b2a73.png" width=400 />

以下のようなコードを実行してみましょう。

```apex
// 実際のトークンで差し替えてください
String token = 'xoxb-';
WebClient client = new WebClient(token);

WebClient.Response response = client.callApi(
    'chat.postMessage',
    new Map<String, Object> {
        'channel' => '#random', // 本当は ID の方がよいです
        'text' =>  'Hello World!' // blocks を使うともっといろいろできますが、ここではシンプルに
    }
);
if (response.body.get('ok') == true) {
    System.debug(response.body.get('message'));
} else {
    System.debug(response.body.get('error'));
}
```

`#random` チャンネルが存在していることに依存しているので、もし存在しない場合は、テストに使ってよい public channel に書き換えてください。

もし、まだ Slack のボットトークンを発行していなければ https://api.slack.com/apps にアクセスして

* Slack アプリを新規作成
* **Features** > **OAuth & Permissions** ページで **Bot Token Scopes** に `chat:write.public` という権限を追加（`chat:write` も追加するよ？と聞かれますが Yes で OK です）
* **Settings** > **Install App** からワークスペースにインストール

の手順で発行することができます。 `xoxb-` で始まっている文字列であれば正しい値です。

全ての準備ができたら、この **Enter Apex Code** というウィンドウにコードを貼り、下にある **Execute** ボタンを押します。ログをすぐに見たければ **Open Log** にチェックを入れておくとよいでしょう。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19163/52c06598-438a-afdb-5204-27915d0b1387.png" width=400 />
うまくいったでしょうか？成功の場合は `#random` チャンネル（あるいは指定のチャンネル）に「こんにちは！」メッセージが投稿され、**Developer Console** 側のログには、メッセージのメタ情報が出力されているはずです。

失敗した場合は `invalid_auth` や `channel_not_found` などのエラーコードが表示されていると思います。
エラーコードの意味は、[API ドキュメント](https://api.slack.com/methods/chat.postMessage#errors)に記述があるので、確認してみてください。

### Slack は 230 以上の公開 API が利用可能です

このように Slack API は非常にシンプルで扱いやすいものです。この chat.postMessage だけでなく 230 以上の利用可能な API の一覧を[こちらのページ](https://api.slack.com/methods)で確認できます。

ぜひいろいろと活用してみてください :wave: