Slack Botは、Slackアプリにボットユーザーを付け、メンションやDMのイベントを受け取って返信させる仕組みです。この記事では、App Manifestでスコープとイベントを宣言するところから、Bolt for Python 1.30系でメンションにスレッド返信するBotを動かす手順、二重返信やBot同士の応答ループの止め方、常駐プロセスとAWS Lambdaのどちらで動かすかの判断までを、コード付きで整理します。仕様と数値は2026年10月時点の公式ドキュメントとbolt-pythonのソースで確認したものです。
まとめ:Slack Bot作りで最初に決める受け取るイベントと動かす場所と権限
Slack Botの作り方は、コードを書く前の3つの決定でほぼ決まります。どのイベントを受けるか(チャンネルでのメンションならapp_mention、DMならmessage.im)、どこで動かすか(社内向けで常駐できるならSocket Mode、サーバーレスならHTTPとlazy listener)、何の権限を付けるか(app_mentions:read・chat:write・im:historyの3つから始める)です。
実装で詰まりやすいのは、3秒以内に応答しないとSlackが同じイベントを最大3回再送して返信が重複する点と、Boltが無視するのは自分の投稿だけで他のBotとは応答し合ってしまう点の2つです。返信はスレッドに寄せ、重い処理はackの後ろへ回します。
決まった文面の通知だけならIncoming Webhook、申請の受付だけならワークフロービルダーで足ります。Botを自作する価値があるのは、社内システムのデータを引いて返す・会話の文脈で判断するといった、条件分岐をコードで持ちたい場面です。
Slack Botでできることと自作Botが受け取るイベントの種類と前提
メンション・DM・チャンネル投稿の3経路で受けるイベントとスコープ
自作のSlack Botは、Slack側で起きた出来事をイベントとして受け取り、Web APIのchat.postMessageで返信します。受け取る経路ごとに購読するイベントと必要なスコープが違うため、最初にどの経路で話しかけられるBotにするかを決めます。
| 話しかけ方 | 購読するイベント | 必要なスコープ | 向く用途 |
|---|---|---|---|
| チャンネルで@メンション | app_mention |
app_mentions:read |
問い合わせ窓口・調べもの |
| BotへのDM | message.im |
im:history |
個人の照会・申請の下書き |
| チャンネルの全投稿 | message.channels |
channels:history |
キーワード監視・自動分類 |
| 返信(共通) | ― | chat:write |
すべてのBot |
公式のapp_mentionイベントのリファレンスには、DMはメンションの有無にかかわらずapp_mentionでは届かず、DMを受けるにはmessage.imイベント(スコープはim:history)を購読すると書かれています。全投稿を読む経路は権限が広く、社内の承認で止まりやすいため、まずはメンションとDMの2経路から始めるのが安全です。
標準のSlackbotやWebhookとBolt自作Botで任せられる処理の違い
「Slack Bot」と呼ばれるものは3種類あります。Slackに最初から入っている標準のSlackbot、Incoming WebhookのURLへJSONを送るだけの通知、そしてSlackアプリにボットユーザーを付けて自分のコードで応答させる自作Botです。
Webhookは送信専用で、話しかけられても受け取れません。送信先は発行時に選んだ1チャンネルに固定されます。通知だけが目的ならSlack Incoming Webhookの実装手順で足り、Botを作る必要はありません。
自作Botが必要になるのは、受け取った文面を解釈して社内データベースやチケット管理を引き、その結果を返すといった双方向の処理です。Slack APIの全体像やトークンの種類はSlack APIとは?Web API・Events API・Boltの違いと実装手順で整理しているので、この記事ではBotを動かす作業に絞ります。
App Manifestでボットユーザーとスコープを宣言するアプリ作成の手順
bot_user・bot_eventsとSocket Mode用Manifestの設定例
Slackアプリは管理画面のボタン操作でも作れますが、設定をYAMLで書くApp Manifestから作ると、スコープとイベントの一覧をレビューできる形で残せます。App manifest referenceのキー名に合わせた、メンションとDMに応答するBotの最小構成は次のとおりです。
display_information:
name: helpdesk-bot
features:
app_home:
messages_tab_enabled: true
messages_tab_read_only_enabled: false
bot_user:
display_name: helpdesk-bot
always_online: true
oauth_config:
scopes:
bot:
- app_mentions:read
- chat:write
- im:history
settings:
event_subscriptions:
bot_events:
- app_mention
- message.im
socket_mode_enabled: true
api.slack.comのアプリ一覧で「Create New App」から「From a manifest」を選び、上の内容を貼り付けます。features.bot_userを書かないとボットユーザーが作られず、Botとしてチャンネルに招待できません。DMを受けるBotでは、App HomeのMessagesタブ(messages_tab_enabled)を有効にし、読み取り専用(messages_tab_read_only_enabled)を外します。公式のApp Homeの解説にあるとおり、Messagesタブで送られたメッセージがEvents APIの通知としてアプリに届く仕組みです。
インストールでxoxbを取りxappにconnections:writeを付ける順序
Manifestから作ったアプリは、まだワークスペースに入っていません。次の順で2つのトークンを用意します。
- 「Install App」からワークスペースへインストールし、Bot User OAuth Token(xoxb-で始まる)を控える
- 「Basic Information」のApp-Level Tokensで、スコープconnections:writeを付けたトークン(xapp-で始まる)を発行する
- Botを使うチャンネルで「/invite @helpdesk-bot」を実行し、Botをメンバーに加える
xoxbはメッセージ送信などWeb APIの呼び出しに、xappはSocket ModeのWebSocket接続を開くためだけに使います。スコープを後から足した場合は再インストールしないと新しい権限がxoxbに反映されません。トークンの取得画面の詳細や漏洩時の作り直しは、G39の兄弟記事で扱います。
Bolt for PythonでメンションとDMに返信するBotを動かす実装の手順
slack_bolt 1.30.0を入れてSocket Modeで起動するまでのコマンド
BotのコードはSlack公式のフレームワークBoltで書くと、署名検証やイベントの振り分けを自分で実装せずに済みます。PyPIのslack-boltは2026年10月時点で1.30.0(2026年7月15日公開)が最新で、Botの実行環境に必要なPythonのバージョンは3.7以上です。bolt-pythonのREADMEどおり、Socket Modeでは署名シークレットは不要で、2つのトークンを環境変数で渡します。
python3 -m venv .venv
source .venv/bin/activate
pip install "slack_bolt==1.30.0"
export SLACK_BOT_TOKEN="xoxb-your-bot-token"
export SLACK_APP_TOKEN="xapp-your-app-level-token"
python app.py
版を固定しているのは、Botを数年運用すると依存ライブラリの更新でイベントの扱いが変わることがあるためです。更新は検証環境で動作を確かめてから版番号を上げます。
app_mentionを受けてスレッドへ返すコードとthread_tsの扱い
次のapp.pyは、チャンネルでのメンションにはスレッドで、DMにはそのまま返信するBotです。Boltのイベント受信の解説にあるとおり、@app.event(“message”)はsubtypeで絞り込めるため、編集や削除の通知(subtype付き)には反応させません。
import os
import re
from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
app = App(token=os.environ["SLACK_BOT_TOKEN"])
@app.event("app_mention")
def handle_mention(event, say):
# スレッド内のメンションはそのスレッドへ、通常の投稿は元投稿をスレッドの親にする
thread_ts = event.get("thread_ts") or event["ts"]
# 本文先頭の <@U0123ABCD> 形式のメンションを取り除く
text = re.sub(r"<@[A-Z0-9]+>", "", event.get("text", "")).strip()
say(text=f"受け付けました: {text}", thread_ts=thread_ts)
@app.event("message")
def handle_dm(event, say):
# DM以外と、編集・削除などsubtype付きのイベントは無視する
if event.get("channel_type") != "im" or event.get("subtype"):
return
say(text="DMを受け付けました。担当者へ回します。")
if __name__ == "__main__":
SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]).start()
sayはchat.postMessageを呼ぶ薄いラッパーで、thread_tsを渡すとスレッド返信になります。chat.postMessageのリファレンスにあるとおり、thread_tsに指定する値は親メッセージのtsです。スレッド内でメンションされたときにevent[“ts”]を渡すと、スレッドの中の1投稿を親にした別スレッドが作られて会話が散らかるため、thread_tsを優先して拾います。
チャンネル直下へ返すBotは、同じチャンネルで複数人が使うと返信の対応関係が追えなくなります。問い合わせ窓口として置くなら、スレッド返信を既定にしてください。
DMが届かないときに確かめるMessagesタブとchannel_typeの判定
DMに反応しないときは、ほぼ次の3か所のどれかです。Manifestのbot_eventsにmessage.imが無い、スコープim:historyを足した後に再インストールしていない、App HomeのMessagesタブが無効か読み取り専用になっている、の3つです。
コード側では、channel_typeが”im”かどうかで判定します。message.imのリファレンスでも、他のmessage系イベントとの区別に使う条件はchannel_typeが”im”であることです。後からmessage.channelsを購読に足した場合、この判定が無いとチャンネルの全投稿にDM用の返信をしてしまいます。
Slack Botが二重に返信する・反応しない原因と再送とループの防ぎ方
3秒を超えると最大3回再送されて返信が重複する仕組みと対処方法
Events APIの仕様では、アプリは3秒以内にHTTP 2xxを返す必要があり、間に合わないと失敗とみなされて、ほぼ即時・1分後・5分後の最大3回再送されます。再送のリクエストにはx-slack-retry-num(何回目か)とx-slack-retry-reason(http_timeoutなど理由)のヘッダが付きます。
Botが同じ質問に2回、3回と答える症状の多くはこれです。社内データベースの検索や生成AIの呼び出しをリスナーの中で同期的に待つと、3秒を超えて再送が始まり、処理が終わるたびに返信が増えます。
対処は2段構えにします。まず重い処理をackの後ろへ回して3秒以内に2xxを返すこと、次にイベントごとに一意なevent_idを処理済みとして記録し、同じevent_idが来たら返信しないことです。Socket Modeでも、Boltは受信直後にackを返すため、ハンドラが遅いだけなら再送は起きにくくなります。失敗が60分間で95%を超えるとイベント購読が一時停止される規定もあり(1時間あたり1,000件未満のアプリは対象外)、利用者の多いBotほど応答の速さが運用の前提になります。
Boltが無視するのは自分の投稿だけでBot同士が応答し続ける条件
Boltには、自分自身の投稿から発生したイベントを捨てるミドルウェアが既定で入っています。bolt-pythonのソースでは、Appの引数ignoring_self_events_enabledの既定値がTrueで、イベントのuserが自Botのユーザーか、bot_idが自Botのものに一致したときだけ無視する実装です。
裏を返すと、他のBotの投稿は無視されません。message.channelsで全投稿を読むBotを2つ同じチャンネルに置き、どちらも投稿に反応する作りにすると、互いの返信に返信し続けるループになります。チャンネルあたりおおむね1秒1件という投稿のレート制限に当たるまで止まりません。
全投稿を読むBotでは、event.get(“bot_id”)があるイベントを最初に捨てる1行を入れます。メンションとDMだけを受けるBotなら、人がBotをメンションしない限り起動しないため、この事故は起きにくい構成です。
チャンネルに招待していないとメンションが届かない初期の詰まり
作ったばかりのBotで「メンションしても無反応」という相談は、Botがチャンネルのメンバーでないことが原因の場合があります。app_mentionのリファレンスには、アプリが会話に参加しておらず招待もされていない場合、イベントは届かないと書かれています。
プライベートチャンネルでは、Botを招待しない限り何も受け取れません。運用の手順書に「新しいチャンネルで使うときは/inviteで招待する」を入れておくと、問い合わせが減ります。
Slack Botの配置先をSocket ModeとHTTPとLambdaから選ぶ判断基準
常駐プロセスで動かすSocket Modeが向く社内Botの条件と再接続
Socket Modeは、BotからSlackへWebSocketをつなぎに行く方式です。公開URLもSSL証明書も要らず、社内ネットワークの中のサーバーやコンテナから動かせます。社内だけで使うBotなら、最初の選択はこちらです。
Socket Modeの公式解説によると、同時に開ける接続は最大10本で、複数の接続があるときイベントはどれか1本に届きます。接続は数時間おきに張り替えが発生するため、プロセスを落とさず再接続できる作りが前提です。SocketModeHandlerは再接続を自動で行いますが、プロセス自体が止まれば応答も止まるので、systemdやコンテナの再起動設定で常駐させます。
見送る場面もはっきりしています。Socket Modeのアプリは公開のSlack Marketplaceに掲載できません。社外の複数のワークスペースへ配布するBotは、最初からHTTP方式で作ってください。
Lambdaでprocess_before_responseとlazyを使う際の理由
利用が業務時間の一部に偏るBotは、AWS Lambdaのようなサーバーレスで動かすと常駐サーバーの費用がかかりません。ただしLambdaはHTTPレスポンスを返した後にスレッドで処理を続けられないため、Boltの通常の「先にack、後で処理」が成り立ちません。
公式のLazy listenersの解説は、process_before_response=Trueにしたうえで、ackを返す関数と重い処理(lazy)を分けて登録する方法を示しています。lazyの関数は別のLambda呼び出しとして実行されるため、実行ロールにlambda:InvokeFunctionとlambda:GetFunctionの権限が要ります。
from slack_bolt import App
from slack_bolt.adapter.aws_lambda import SlackRequestHandler
# トークンと署名シークレットは環境変数 SLACK_BOT_TOKEN と SLACK_SIGNING_SECRET から読む
app = App(process_before_response=True)
def ack_mention(ack):
ack()
def answer(event, say):
# 3秒を超えてよい重い処理(社内APIの照会や生成AIの呼び出し)をここで行う
thread_ts = event.get("thread_ts") or event["ts"]
say(text="調べた結果をお知らせします。", thread_ts=thread_ts)
app.event("app_mention")(ack=ack_mention, lazy=[answer])
def handler(event, context):
return SlackRequestHandler(app=app).handle(event, context)
HTTP方式ではManifestのsocket_mode_enabledをfalseにし、event_subscriptions.request_urlにLambdaの関数URLやAPI GatewayのURLを設定します。コールドスタートで初回の応答が遅れる点を含め、Lambda自体の性質はAWS Lambdaとは?仕組み・料金体系とコールドスタート対策で確認してから選んでください。
自作を見送り既製アプリやワークフローで済ませる場面の判断基準
Botの自作には、トークンの管理、常駐プロセスの監視、Slackの仕様変更への追従という運用が付いてきます。次のどれかに当たるなら、自作は見送ります。
- 決まった文面を決まったチャンネルへ送るだけ(Incoming Webhookで足りる)
- フォームで申請を受けて承認者へ回すだけ(ワークフロービルダーで足りる)
- 決まった操作をコマンドで呼びたいだけ(スラッシュコマンドの方が受付と応答を作りやすい)
ワークフロービルダーで組める範囲はSlackワークフローの業務別の使い方10例にまとめています。逆に、社内マニュアルや業務システムのデータを根拠に質問へ答えさせたい、会話の流れで担当部署を判断して振り分けたいといった要件がある場合は、Botを自作する理由が明確です。生成AIで回答させる構成を検討する段階なら、AIチャットボット開発として要件の整理から相談できます。
よくある質問
Slack Botの作り方や開発でよく出る質問に、2026年10月時点の公式ドキュメントをもとに答えます。
Slack Botは無料プランのワークスペースでも作れますか?
作れます。Slackアプリの作成とBotのインストールは無料プランでも可能です。ただし無料プランには入れられるアプリ連携の数に上限があるため、既存の連携が多いワークスペースでは先に整理が必要になることがあります。また、ワークスペースの管理者がアプリのインストールに承認を求める設定にしている場合、Install Appの段階で申請が必要です。社内で作る場合は、Manifestのスコープ一覧を添えて申請すると審査が早く進みます。
プログラミングなしでSlack Botは作れますか?
決まった応答や申請の受付であれば、Slackのワークフロービルダーでコードを書かずに組めます。フォームで入力を受け、承認ボタン付きのメッセージを送り、結果をリストへ残すといった流れが代表例です。一方、受け取った文面を解釈して社内システムのデータを引く、会話の文脈で応答を変えるといった処理はワークフロービルダーの範囲を超えるため、Boltなどでのコード実装が必要になります。
Slack BotとIncoming Webhookは何が違いますか?
Incoming Webhookは送信専用で、発行時に選んだ1チャンネルへメッセージを送ることしかできません。Botはボットユーザーを持ち、メンションやDMのイベントを受け取って返信できます。Webhookの応答にはメッセージのtsが返らないため、送った通知の更新やスレッド返信もできません。監視アラートの通知だけならWebhook、話しかけて答えさせたいならBot、と考えると選び分けで迷いません。
Python以外の言語でもSlack Botは作れますか?
作れます。Slack公式のBoltには、Python版のほかにBolt for JavaScriptとBolt for Javaがあり、イベントの受け方やackの考え方は共通です。Node.jsで社内のWebシステムを作っているならJavaScript版、Javaの業務システムと同じ基盤で動かすならJava版を選ぶと、運用の手間をそろえられます。Bolt以外の言語でも、Web APIとEvents APIはHTTPで呼べるため実装は可能ですが、署名検証や再送の扱いを自前で書く必要があります。
Slack Botに生成AIで回答させるにはどう組みますか?
基本の形は、この記事のlazy listenerの構成で、重い処理の部分から生成AIのAPIを呼び、結果をスレッドへ返すものです。社内文書を根拠に答えさせるなら、文書を検索して関連部分をプロンプトに入れるRAGの仕組みが加わります。回答に3秒以上かかるのが普通なので、ackを先に返す作りは必須です。方式の選び方はAIチャットボットの作り方で整理しています。
関連記事
- Claude Tagとは?Slackで@Claudeに仕事を任せる新機能:Botを自作せず既製のAIアプリをSlackに入れる場合の選択肢
- Slack MCPサーバーの接続手順と権限設計:AIエージェントからSlackを読み書きさせる逆向きの連携
- Discord Botの作り方|アプリ登録から24時間稼働までを手順どおりに:Discordで同じ種類のBotを作る場合の手順と常駐の考え方
- Cloud Runとは?GCPのサーバーレスコンテナの仕組み:HTTP方式のBotをコンテナのままサーバーレスで動かす場合の基盤