本文へスキップ

2026年6月23日

Slackスラッシュコマンドで自社コマンドを作る — Slack API実装ガイド

CommunitySlackEngineeringAI

スラッシュコマンドで「Slackを操作パネル」にする

Slack Bot・APIで何ができるのかで概観したように、Slack運用の自動化はメッセージ操作・インタラクション・ワークフローの3層から成ります。その入口として最も使いやすいのがスラッシュコマンドです。

/help でFAQが表示される。/report @ユーザー でモデレーターに通知される。/standup でその日のスタンドアップを収集する——こうした仕組みは、参加者が特別なUIを覚えなくても「コマンドを打つだけ」で使えます。Discordと同様、スラッシュコマンドはコミュニティBotの最も重要なインターフェースです。

仕組み:コマンドが実行されると何が起きるか

ユーザーが /hello と入力して送信
 → Slack がリクエストURLにPOSTリクエスト(application/x-www-form-urlencoded)
 → サーバーがSlack署名を検証
 → 3秒以内にレスポンスを返す(または ack())
 → 必要ならresponse_urlや非同期処理で追加投稿

Slack APIはDiscordと違い、コマンドの引数を text フィールドの生テキストとして受け取ります。構造化した入力を受け取りたい場合はモーダル(Block Kitのinput要素)を組み合わせます。

実装:Bolt for JavaScript(Node.js)で作る

Slack公式のBoltフレームワークを使うと、署名検証・イベントルーティング・応答まわりの定型コードを大幅に省けます。

インストール

npm install @slack/bolt

最小構成のスラッシュコマンド

// src/index.ts
import { App } from '@slack/bolt';

const app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  signingSecret: process.env.SLACK_SIGNING_SECRET,
  socketMode: true, // Socket Modeを使う場合
  appToken: process.env.SLACK_APP_TOKEN,
});

// /hello コマンドに応答
app.command('/hello', async ({ command, ack, respond }) => {
  await ack(); // 3秒以内にack(確認)を返す

  await respond({
    text: `こんにちは、<@${command.user_id}> さん!`,
    response_type: 'in_channel', // チャンネルに表示('ephemeral'にすると本人のみ)
  });
});

(async () => {
  await app.start();
  console.log('Bolt app is running!');
})();

response_type: 'in_channel' にするとチャンネル全体に見える形で返答し、'ephemeral' にすると実行したユーザーのみに見える形で返します。通知・確認系は ephemeral、共有情報は in_channel が基本の使い分けです。

モデレーション通知コマンドの実装例

運営でよく使う「メンバー通報コマンド」の実装例です。

app.command('/report', async ({ command, ack, client, respond }) => {
  await ack();

  const text = command.text.trim(); // "/report @ユーザー 理由" の "@ユーザー 理由" 部分

  // モデレーターチャンネルへ通知
  await client.chat.postMessage({
    channel: process.env.MODERATOR_CHANNEL_ID!, // #moderatorsチャンネルのID
    blocks: [
      {
        type: 'section',
        text: {
          type: 'mrkdwn',
          text: `🚨 *新しい通報*\n*通報者:* <@${command.user_id}>\n*内容:* ${text || '(理由なし)'}`,
        },
      },
      {
        type: 'context',
        elements: [
          {
            type: 'mrkdwn',
            text: `チャンネル: <#${command.channel_id}> | 受信: <!date^${Math.floor(Date.now() / 1000)}^{date_pretty} {time}|${new Date().toISOString()}>`,
          },
        ],
      },
    ],
  });

  // 通報者にだけ確認を返す
  await respond({
    text: '通報を受け付けました。モデレーターが確認します。',
    response_type: 'ephemeral',
  });
});

モーダルで構造化された入力を受け取る

/report の引数を自由テキストにすると入力がバラバラになります。モーダルを使うと、フォーム形式で構造化された入力を強制できます。

app.command('/report', async ({ command, ack, client }) => {
  await ack();

  // モーダルを開く
  await client.views.open({
    trigger_id: command.trigger_id,
    view: {
      type: 'modal',
      callback_id: 'report_modal',
      title: { type: 'plain_text', text: 'メンバーを通報' },
      submit: { type: 'plain_text', text: '送信' },
      blocks: [
        {
          type: 'input',
          block_id: 'user_block',
          element: {
            type: 'users_select',
            action_id: 'user',
            placeholder: { type: 'plain_text', text: '通報するメンバーを選ぶ' },
          },
          label: { type: 'plain_text', text: '対象メンバー' },
        },
        {
          type: 'input',
          block_id: 'reason_block',
          element: {
            type: 'plain_text_input',
            action_id: 'reason',
            multiline: true,
            placeholder: { type: 'plain_text', text: '状況を詳しく教えてください' },
          },
          label: { type: 'plain_text', text: '理由・状況' },
          optional: true,
        },
      ],
    },
  });
});

// モーダル送信時の処理
app.view('report_modal', async ({ ack, view, body, client }) => {
  await ack();

  const userId = view.state.values.user_block.user.selected_user;
  const reason = view.state.values.reason_block.reason.value ?? '(理由なし)';

  await client.chat.postMessage({
    channel: process.env.MODERATOR_CHANNEL_ID!,
    text: `🚨 通報: <@${userId}> | 理由: ${reason} | 通報者: <@${body.user.id}>`,
  });
});

バイブコーディングで実装する

上記の実装をAIコーディング(Claude等)で生成させる場合のプロンプト例です。

Slack BotのスラッシュコマンドをTypeScriptで作りたい。
フレームワーク: Bolt for JavaScript + Socket Mode

コマンド名: /feedback
動作:
  - モーダルを開く(テキストエリア1つ:フィードバック内容・必須)
  - モーダル送信後、#feedback チャンネルにBlock Kitで投稿
    (送信者名・内容・タイムスタンプ)
  - 送信者にはephemeralで「受け付けました」と返す

package.jsonのscriptsと.envの変数名も含めて生成してください。

受け取ったコードで必ず確認すること:

  • SLACK_SIGNING_SECRET 検証(なりすまし防止のための署名検証)
  • 3秒以内の ack()(処理が遅い場合の非同期パターン)
  • エラーハンドリング(チャンネルが見つからない、権限不足など)

よく使うコマンドパターン一覧

コマンド用途返答形式
/helpFAQや使い方の案内Ephemeral(本人のみ)
/report通報・問題申告Ephemeral確認 + Moderatorに通知
/announce管理者が告知文を投稿in_channel
/standupスタンドアップ収集(Modal)チャンネルに集計
/tag @userメンバーにタグ付与(権限管理)Ephemeral確認
/digest過去N日の投稿サマリ(LLM連携)Ephemeral

コミュニティ運用のコードとしての発想全体は「コミュニティ運用をコードで表現する — Slackで実装する3要素」で解説しています。

まとめ

  • Slackスラッシュコマンドはapi.slack.comでコマンド名とリクエストURLを登録して使う
  • Boltフレームワークで署名検証・ルーティング・応答の定型コードを省ける
  • ack() は3秒以内必須。重い処理は非同期で respond()chat.postMessage で後送り
  • モーダルを組み合わせると構造化された入力を受け取れ、通報フォームなどに使える
  • バイブコーディングでは「コマンド名・動作・ライブラリ・.env変数名」を具体的に伝える

スラッシュコマンドが動くと、次は定期投稿・リアクション自動化・LLM連携へと自動化の幅が広がります。


コミュニティのSlack Bot設計・実装の伴走支援をお探しの方は、六瀬のコミュニティ開発サービスもご覧ください。

関連記事

Contact · 六瀬合同会社

コミュニティの話を、続けませんか。

読みながら「自分のところはどうなんだろう」と思った瞬間があれば、その問いをそのまま送ってください。整理されていなくても、相談の入口を一緒に言葉にしていきます。

よくある質問

Q. Slackのスラッシュコマンドはどこで設定しますか?
A. api.slack.com/apps からアプリを作成し、「Slash Commands」メニューで新しいコマンドを追加します。コマンド名(/hello等)・リクエストURL(コマンド実行時にSlackがPOSTするURL)・説明文を設定します。リクエストURLには自作サーバーやCloudflare WorkersのURLを入力します。
Q. スラッシュコマンドの応答は3秒以内に返さなければいけないのですか?
A. はい、Slackはコマンド実行から3秒以内の応答を要求します。3秒以内に処理が終わらない場合は「200 OK」とともに空レスポンス(またはack())を即時返し、処理完了後にresponse_urlへPOSTするか、Bolt/Socketモードでsay()を使って非同期に投稿します。重い処理(LLM呼び出し・DB検索等)はほぼ必ず非同期処理が必要です。
Q. スラッシュコマンドに引数を渡せますか?
A. はい、コマンドの後ろにテキストを入力すると text パラメータとして受け取れます(例:/report @田中 ルール違反)。Discordと異なり、Slack標準のスラッシュコマンドには型付き引数がないため、text の文字列を自分でパースするか、モーダルを開いて構造化された入力を受け取るのが一般的です。Bolt v4からBlock KitのSuggestionsで入力補完も実装できます。
Q. 既存のSlack Botアプリにスラッシュコマンドを追加できますか?
A. できます。api.slack.com/apps から対象アプリを選び、「Slash Commands」から追加するだけです。ただしコマンドを追加した後、ワークスペースにインストールし直す(再認証)か、管理者が承認する手順が必要な場合があります。また、コマンドを処理する実装(リクエストURLのエンドポイント)も追加してください。