0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TL;DR

  • まずはこの記事の方法でSlackBotを作ります。
  • n8nにWebhookのWorkflowを作成します。
  • Slack の Socket Mode を使って、メッセージを中継します。

方法

Slack Botの作成

上記の記事は詳しく作成方法を書いています。読みたくない場合は、 このリンク をクリックし、 「From a manifest」 を選び、下記の設定ファイルを 「YAML」タブ に貼り付ければOKです。
image.png
image.png

display_information:
  name: Slack-Bot-Demo
features:
  bot_user:
    display_name: Slack-Bot-Demo
    always_online: false
oauth_config:
  scopes:
    bot:
      - channels:history
      - groups:history
      - im:history
      - mpim:history
      - chat:write
      - app_mentions:read
settings:
  event_subscriptions:
    bot_events:
      - message.channels
      - message.groups
      - message.im
      - message.mpim
      - app_mention
  interactivity:
    is_enabled: true
  org_deploy_enabled: false
  socket_mode_enabled: true
  token_rotation_enabled: false

n8n Workflowの作成

下記のような簡単な時報AIを作りました。

image.png

Slack-Local-Demo.json
Slack-Local-Demo.json
{
  "name": "Slack Local Demo",
  "nodes": [
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ $json.body.text }}",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 3,
      "position": [
        208,
        0
      ],
      "id": "c48e2a7b-4d27-4fb7-95b5-a1b95bb5c746",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatGoogleGemini",
      "typeVersion": 1,
      "position": [
        208,
        176
      ],
      "id": "1b78944e-db02-4a10-bc31-a86472472312",
      "name": "Google Gemini Chat Model",
      "credentials": {
        "googlePalmApi": {
          "id": "bNX3Ng40XeUKUnU8",
          "name": "Google Gemini(PaLM) Api account"
        }
      }
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "eb43abea-835e-4356-bbae-eb2b15cb5439",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        -48,
        0
      ],
      "id": "920d4473-f7bb-44fd-aacf-3db32dd810bb",
      "name": "Webhook",
      "webhookId": "eb43abea-835e-4356-bbae-eb2b15cb5439"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={\n  \"text\": \"{{$json.output}}\",\n  \"thread\": {\n    \"channelId\": \"{{ $('Webhook').item.json.body.thread.channelId }}\",\n    \"threadId\": \"{{ $('Webhook').item.json.body.thread.threadId }}\"\n  }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.4,
      "position": [
        560,
        0
      ],
      "id": "5b559af6-d383-45a8-bb9b-88ed22a895f1",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "options": {}
      },
      "type": "n8n-nodes-base.dateTimeTool",
      "typeVersion": 2,
      "position": [
        352,
        192
      ],
      "id": "04352eeb-d01a-4d6b-8d56-3d95e1344066",
      "name": "Date & Time"
    }
  ],
  "pinData": {},
  "connections": {
    "Google Gemini Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Webhook": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Date & Time": {
      "ai_tool": [
        [
          {
            "node": "AI Agent",
            "type": "ai_tool",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "807ed4c6-05b3-4e0c-89a3-1d8fa69c27d6",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "733f4543cc2716ce03aee7a2723a7a38c8dd841e95dddcbe1403304743cd54e1"
  },
  "id": "0spOf0unGMovJr9v",
  "tags": []
}

API テスト

下記のような感じでAPIの動作を確認します。

POST http://localhost:5678/webhook-test/eb43abea-835e-4356-bbae-eb2b15cb5439
Content-Type: application/json

{
  "text": "Hello, What's the time now!",
  "thread": {
    "channelId": "TEST-CHANNEL",
    "threadId": "TEST-THREAD"
  }
}

こんな感じでレスポンスが来たらOKです。

{
  "text": "The current date and time is 2025-12-26T02:10:40.580-05:00.",
  "thread": {
    "channelId": "TEST-CHANNEL",
    "threadId": "TEST-THREAD"
  }
}

注意すべきのは、必ず channelIdthreadId (threadTs)をメッセージに持たせることです。
今回は簡易的な同期的実装にしていましたが、将来非同期的な実装をしようとする時、チャネルやスレッド情報を持っていると便利です。

中継 Scriptの作成

同期的な実装にしていますが、ExpressなどでAPIサーバーを立てれば、非同期的な実装も可能です。

import bolt from '@slack/bolt';
import { config } from 'dotenv';

config({ quiet: true });

const { App } = bolt;
const slackApp = new App({
  token: process.env.SLACK_BOT_TOKEN, // xoxb-XXXXX
  appToken: process.env.SLACK_APP_TOKEN, // xapp-XXXXX
  socketMode: true,
});

await slackApp.start();
console.log('⚡️ Bolt app started');

const n8nUrl = `http://localhost:5678/webhook/eb43abea-835e-4356-bbae-eb2b15cb5439`;

slackApp.event('app_mention', async ({ event, say }) => {
  console.log('App mentioned:', event);
  const text = removeSlackMention(removeSlackUrlTag(event.text));
  const threadId = event.thread_ts || event.ts;

  const response = await sendToN8n(n8nUrl, text, { channelId: event.channel, threadId });

  // スレッドを作成して返信する
  await say({
    thread_ts: event.ts,
    text: `<@${event.user}> ${response.text}`,
  });
});

async function sendToN8n(url, text, thread = { channelId: null, threadId: null }) {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ text, thread }),
  });
  const data = await response.json();
  console.log(data);

  return data;
}


// SlackのURLタグをプレーンのURLに還元する
function removeSlackUrlTag(text) {
  return text.replace(/<https?:\/\/[^|]+?\|([^>]+)>/g, '$1').replace(/<(https?:\/\/[^>]+)>/g, '$1');
}

// Slackのメンションタグを削除
function removeSlackMention(text) {
  return text.replace(/<@[^>]+>/g, '').trim();
}

結果

image.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?