RailsからSlack/GoogleChat/TeamsなどのチャットアプリにDMで通知を送る

## はじめに
サーバーから直接DMで通知を送りたいことがあると思いますが、Webhookを使うと公開チャンネルへの投稿はできるものの、DMには送信できません。
そこで今回は、各チャットツールでDM送信を実現する方法を比較しながら紹介します。

## チャットツール連携の共通的なステップ
### 1. チャットアプリの作成
各チャットツールで、アプリ名、機能、認証時のリダイレクトURLなどを設定します。
設定は`manifest.json`や管理画面から行います。

### 2. アプリのインストール
通常、管理者の承認が必要です。開発時には自分のワークスペースや組織でのみインストール可能です。
他組織へのインストールが必要な場合は、マーケットプレイスへの公開やリンク共有で促します。

### 3. ユーザー認証ボタンの用意
OAuth認証を行うためのボタンを設置し、スコープなどのパラメータを渡します。認証完了後、アクセストークンが発行されます。

### 4. サーバーからのDM送信
取得したアクセストークンでメッセージを送信します。
ユーザー認証によって得られたアクセストークンをりようするか、アプリ認証によって新にアクセストークンを取得します

## 事前知識：認証方法の違い
チャットツールでは、以下2種類の認証方法があり、取得できるスコープや利用できるAPIが異なります。APIリファレンスをよく読み、該当APIがどちらの認証方式で利用できるかを確認する必要があります。

|トークン種類|できること|
|:-|:-|
|ユーザー認証トークン|認証したユーザーになりすまして振る舞う。そのユーザーができること（のうち、認証時にscopeで認可したもの）が実行できる|
|アプリ認証トークン|チャットボットとして振る舞う。アプリインストール時にscopeで認可したものが実行できる|


## チャットアプリ作成
### チャットアプリで定義すべきものの概要
Slackのmanifest.ymlの内容がほぼ全てで、GoogleChatやTeamsでは、その設定箇所が分岐しているというイメージです
```:manifest.yml
display_information:
  name: アプリ名
  description: 説明
  background_color: "#e84141"
features:
  app_home:
    home_tab_enabled: false
    messages_tab_enabled: true
    messages_tab_read_only_enabled: true
  bot_user:
    display_name: bot名
    always_online: true
oauth_config:
  redirect_urls:
    - https://xxxx/auth/callback
  scopes:
    user:
      - identity.basic
    bot:
      - channels:history
      - channels:join
      - channels:read
      - chat:write
      - groups:read
      - im:history
      - im:write
      - users:read
      - users:read.email
      - chat:write.customize
settings:
  event_subscriptions:
    request_url: https://xxx/events
    bot_events:
      - message.channels
      - message.im
  interactivity:
    is_enabled: true
    request_url: https://xxx/interactivity
  org_deploy_enabled: false
  socket_mode_enabled: false
  token_rotation_enabled: false
```

|チャットツール|説明|
|:-|:-|
|Slack|**Slack Appの詳細ページで全て完結。** アプリ名や認証に関するタブが存在しており、個別に編集もできるが、App Manifestによって、一括で編集も可能|
|Google Chat|**Google Cloud Console内だが、設定が分散している。** アプリ名とevent_subscriptionやinteractivityは[Google Chat API](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)の構成タブ。認証のリダイレクトurlなどの設定は認証タブか[認証情報](https://console.cloud.google.com/apis/credentials?project=on1-dev-440809)のOAuth2.0クライアントIDの作成。ロゴや認可のscope設定は[OAuth同意画面](https://console.cloud.google.com/apis/credentials/consent/edit)|
|Teams|**Azureと、Developer Potalに分かれている。** Azureのアプリ登録で、Graph APIを有効化。Developer Portalでチャットアプリとしての表示関連を設定|

### チャットアプリ作成における比較表

|項目名|Slack|Google Chat|Teams|
|:-|:-|:-|:-|
|チャットアプリ名|Slack APP| Google Chatアプリ | Microsoft Teamsアプリ |
|チャットアプリ作成|[Slack Apps](https://api.slack.com/apps)で作成する | [Google Chat API](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)でAPIを有効にして、構成タブに色々入れる | [Azureのアプリの登録](https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)と[TeamsのDeveloper Potal](https://dev.teams.microsoft.com/apps)|
|アプリ名・ロゴの編集|Slack AppのApp Homeページ | [Google Chat API](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)の構成タブ | Teams Developer Potalの基本情報 |
|OAuthのリダイレクトurlの設定|Slack AppのOAuth&Permissionページ|[認証情報](https://console.cloud.google.com/apis/credentials?project=on1-dev-440809)のOAuth2.0クライアントID| Azureアプリ詳細の認証ページ |
|scopeの定義|Slack AppのOAuth&Permissionページ | [GCPのOAuth同意画面](https://console.cloud.google.com/apis/credentials/consent) | Azureアプリ詳細のAPIのアクセス許可ページ |
|event subscriptionの設定|Slack Appの Event Subscriptionsページ|[Google Chat API](https://console.cloud.google.com/apis/api/chat.googleapis.com/hangouts-chat)の構成タブ|[Teams Developer Potalのbotページ](https://dev.teams.microsoft.com/bots/)|
|interactive api urlの設定|Slack Appの Interactivity & Shortcutsページ|同上|同上|
|マーケットプレイス名| Slack App Directory | Google Workspace Marketplace | Teams Store |

※項目名は自分で勝手につけた名前です。一般名称と異なる可能性があります。より良い名前があれば、提案をお願いします


### 補足：認証手順とAPI利用

|チャットツール|説明|ユーザー認証|アプリ認証|その他操作のための認証|
|:-|:-|:-|:-|:-|
|Slack|ユーザーがOAuth認証を行い、ユーザーとしてもアプリとしても動作可能|OAuth|OAuth|なし|
|Google Chat|OAuthでユーザー認証。[認証情報](https://console.cloud.google.com/apis/credentials)のサービスアカウントでアプリ認証|OAuth|[サービスアカウント](https://console.cloud.google.com/apis/credentials)のjsonを使って認証|なし|
|Teams|OAuthでユーザー認証。client_idとsecretでbot/Graph APIの認証|OAuth|client_secretでbot API認証|client_secretでgraph API認証|



## API開発における対応表

|項目名|Slack|Google Chat|Teams|
|:-|:-|:-|:-|
|ruby gem|[slack-ruby-client](https://github.com/slack-ruby/slack-ruby-client)|[google apps chat](https://github.com/googleapis/google-cloud-ruby/tree/main/google-apps-chat)| [msgraph-sdk-ruby](https://github.com/microsoftgraph/msgraph-sdk-ruby)。[botframework-ruby](https://github.com/tachyons/botframework-ruby)。net/httpやjsonで頑張っても良い|
|API リファレンス|[Slack API Reference](https://api.slack.com/messaging/sending) (gemが充実してるのでみなくていい)|エンドポイント：[REST APIリファレンス](https://developers.google.com/workspace/chat/api/reference/rest?hl=ja)。リクエストオブジェクト：[RPCリファレンス](https://developers.google.com/workspace/chat/api/reference/rpc/google.chat.v1?hl=ja)|[graph APIリファレンス](https://learn.microsoft.com/ja-jp/graph/api/overview?view=graph-rest-1.0&preserve-view=true)。[bot APIリファレンス](https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-api-reference?view=azure-bot-service-4.0#create-conversation)|
|rate limit| [Slackのrate limit](https://api.slack.com/apis/rate-limits#events_api)|[Google Chat rate limit](https://developers.google.com/workspace/chat/limits?hl=ja)|[Graph API rate limit](https://learn.microsoft.com/en-us/graph/throttling-limits#microsoft-teams-service-limits)|
|メッセージ・カードビルダー|[Block Kit Builder](https://app.slack.com/block-kit-builder)|[Card Builder](https://addons.gsuite.google.com/uikit/builder?hl=ja)| [AdaptiveCard Designer](https://adaptivecards.io/designer/)|
|DMを投稿する対象のオブジェクト名|user|space|conversation|
|投稿する対象のid形式|UXXXXXXXXXX(Uから始まる9桁の英数字）|spaces/XXXXXXXXXXX（spaces/11桁の英数字）|19:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx@unq.gbl.spaces（数字2桁:xxxxで表現したハイフンで繋いだ英数字@nq.gbl.spaces)|
|ユーザー認可の呼称| userスコープ | ユーザー認証スコープ（OAuth認証スコープ） | Delegated認証スコープ |
|アプリ認可の呼称| botスコープ | アプリ認証スコープ（サービスアカウント認証スコープ） | Application認証スコープ |


## アクセストークンを取得してから、DMを送るまでのステップ
### 管理ユーザーだけが認証
管理ユーザーだけが認証して、他ユーザーにDMを送る流れは、大まかには以下
1. DMを送りたいユーザーのidを取得する
2. 対象ユーザーとbotのDMスペースを作成し、そのidを取得
3. スペースのidにメッセージを送る

※ google chatについては、管理者だけが認証をして他のユーザーにDMを送る方法が見つかっていないので、誰か知ってたら教えてください

|ステップ|Slack|Google Chat|Teams|
|:-|:-|:-|:-|
|DMを送りたいユーザーのidを取得する|bot scopeの[users.list](https://api.slack.com/methods/users.list)で取得|-|graph api Applicationの[users](https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http)|
|ユーザーとbotのDMスペースを作成し、そのidを取得|user_idに対して送れるので不要|-|graph api Delegatedの[installedApps](https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/proactive-bots-and-messages/graph-proactive-bots-and-messages?tabs=dotnet)|
|スペースのidにメッセージを送る|bot scopeの[chat.postMessage](https://api.slack.com/methods/chat.postMessage)|-|bot apiの[send to conversation](https://api.slack.com/methods/chat.postMessage)|

### 利用ユーザーがそれぞれ認証
利用ユーザーがそれぞれ認証して、DMを送る流れは以下
1. 認証ユーザーとbotのDMスペースを作成し、そのidを取得
2. スペースのidにメッセージを送る

|ステップ|Slack|Google Chat|Teams|
|:-|:-|:-|:-|
|ユーザーとbotのDMスペースを作成し、そのidを取得|user scopeの[users.info](https://api.slack.com/methods/users.info)|ユーザー認証スコープで[spaces.setup](https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces/setup?hl=ja)|※まだ試してない|
|スペースのidにメッセージを送る|bot scopeの[chat.postMessage](https://api.slack.com/methods/chat.postMessage)|アプリ認証スコープで[spaces.messages.create](https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/create?hl=ja)|※まだ試してない|

## 最後に
初期段階でわかってないことも多いため、今後も加筆していく予定です。
すでに間違いや、加筆の提案があればぜひお願いします。
この記事によって、少しでも日本のサービスの利便性が上がりますように＞＜