Slack APIで任意のチャンネルにメッセージを投稿する

# 結論
任意のチャンネルに投稿するには [chat.postMessage](https://api.slack.com/methods/chat.postMessage) を使う。

# 公式ドキュメント

- [Slack API Developer docs and guides](https://api.slack.com/docs)
    - [Sending messages](https://api.slack.com/messaging/sending)

# メッセージを投稿する方法は複数ある
いずれも[Slack APPの作成](https://api.slack.com/messaging/webhooks#create-app)が前提条件。

参照：https://api.slack.com/messaging/sending#sending_methods
| 方法 | 概要 | 検証対象 |
| --- | --- | --- |
| [chat.postMessage](https://api.slack.com/methods/chat.postMessage)  | [Web API](https://api.slack.com/web)の`chat.postMessage`を使用してメッセージを投稿。 | ✅ |
| [chat.postEphemeral](https://api.slack.com/messaging/managing#ephemeral) |  [Web API](https://api.slack.com/web)の`chat.postEphemeral`を使用してメッセージを投稿。特定の1人にのみ、短時間で消えるメッセージ。「あなたにだけ表示されています」というやつ。 | |
| [Incoming webhooks](https://api.slack.com/messaging/webhooks) | URLを使用して特定のチャンネルにメッセージ投稿 | ✅ |
| [response_url](https://api.slack.com/interactivity/handling#message_responses)| メッセージのやり取りに対する応答をポストするために使用できるコールバックURL。スラッシュコマンドとかショートカットとかの実行の応答結果を取得できるURLぽい。 | |
| [Real Time Messaging API](https://api.slack.com/rtm#sending_messages) | WebSocketベースのAPIで、Slackからイベント(リアクションとか)をリアルタイムで受信し、メッセージをユーザとして送信することができる。"RTM API"というやつ。<br> 現在は[Events API](https://api.slack.com/rtm#sending_messages)が推奨されている。「このAPIは、レイアウトブロックまたは添付ファイルをサポートしていません。 メッセージングには使用しないことをお勧めします」とのこと。 | |
| Broadcast messages within the 'Messages' tab | 「アプリケーションは、ユーザからの返信を聞くことなく、[メッセージ（Messages）]タブでメッセージを送信できます。」とのこと。よく分からない。スレッド返信する時にチャンネルにも投稿する機能のこと？ | |

# 通常のメッセージ投稿をしたいので[chat.postMessage](https://api.slack.com/methods/chat.postMessage)と[Incoming webhooks](https://api.slack.com/messaging/webhooks)を比較&実験

使用言語：Python3.9

| 方法 | 手順 | 任意のチャンネルへの投稿 | 投稿後のメッセージ削除 |
| --- | --- | --- | --- |
| chat.postMessage | Slack SDKのWebClientを使用します。認証にはSlack Appの `Bot Token Scopes` か `User Token Scopes` が必要。必要な権限は[これ](https://api.slack.com/methods/chat.postMessage)の `Required scopes` | ✅ | ✅ |
| Incoming webhooks | Slack Appの`Incoming Webhooks`にあるURLでPOSTする。投稿チャンネルごとにURLが発行される | ー | ー |

**`Incoming webhooks` はシンプルなメッセージ投稿機能なため、複雑なことをする場合は `chat.postMessage`を使うのが良さそう。**

## サンプルコード `chat.postMessage`

```py
import logging
import os
import datetime
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError

client = WebClient(token=os.environ.get("SLACK_BOT_TOKEN")) # Slack Appの `Bot Token Scopes` か `User Token Scopes`
logger = logging.getLogger(__name__)

try:
    result = slackClient.chat_postMessage(
            channel="notify-makimatsun", # DMでも可
            text= f":mega: :new: *TEST*",
            attachments= [{
                "color": "#2eb886",
                "blocks": [
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": "*Title*"
                        }
                    },
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": f":spiral_calendar_pad: *Time* : `{datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}`"
                        }
                    }
                ]
            }]
        )
    logger.info(result)

except SlackApiError as e:
    logger.error(f"Error posting message: {e}")
```

## サンプルコード `Incoming webhooks`

```py
import logging
import os
import datetime
import json
import urllib.request
import urllib.parse

logger = logging.getLogger(__name__)

try:
    data = {
        "text": f":mega: :new: *TEST*",
        "attachments": [{
                "color": "#2eb886",
                "blocks": [
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": "*Title*"
                        }
                    },
                    {
                        "type": "section",
                        "text": {
                            "type": "mrkdwn",
                            "text": f":spiral_calendar_pad: *Time* : `{datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}`"
                        }
                    }
                ]
            }]
    }
    headers = {
        'Content-Type': 'application/json',
    }
    request = urllib.request.Request(
        'https://hooks.slack.com/services/xxxxx/xxxxx/xxxxx', # Slack Appの`Incoming Webhooks`にあるURL
        json.dumps(data).encode(),
        headers
    )
    with urllib.request.urlopen(request) as res:
        print(res)
        body = res.read()
        print(body.decode('utf-8'))

except Exception as e:
    logger.error(f"Error posting message: {e}")

```

## 投稿結果

どちらもこのように投稿されました。

![Screen Shot 2023-03-10 at 19.53.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331389/c0d950c6-f467-8f4f-fe87-f8f91b2b0dcd.png)

ざっくり解説
![Screen Shot 2023-03-10 at 19.48.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/331389/e424b747-503c-48b7-b5a1-4f5fd8458260.png)

## 投稿レイアウトについて

レイアウトの作成には[Block Kit Builder](https://api.slack.com/messaging/composing/layouts)を使用しました。
リッチなメッセージレイアウトを作成できます。

# まとめ
旧`Incoming webhooks`ではチャンネルを指定しての投稿ができたようですが、リニューアル後はできなくなりました。
また、いずれの方法でも、あらかじめ**投稿先のチャンネルへアプリを追加する**必要があります。
私自身はチャンネルを指定して投稿したい場面が多いので、[Web API](https://api.slack.com/web)を活用していきたいと思います。