👏

Server Actionsを利用して、Slackのチャンネルに通知を飛ばす

2023/07/29に公開

はじめに

  • Slack のチャンネルに通知を飛ばす方法をまとめます。
  • Incoming Webhooksを利用し通知を飛ばします。
  • Server Actions を利用し、サーバサイドから Slack のチャンネルに通知を飛ばします。

https://api.slack.com/messaging/webhooks

今回作成するプログラムです。

作業したコードはこちらにあります。

https://github.com/hayato94087/nextjs-slack-incoming-webhook-sample

Incoming Webhooksの設定手順

Incoming Webhooks を Slack に設定する手順を紹介します。詳細はこちらに記載されています。
https://api.slack.com/messaging/webhooks

ステップ1:Slackのワークスペースを作成

以下を参考にワークスペースを作成します。

https://slack.com/intl/ja-jp/help/articles/206845317-Slack-ワークスペースを作成する

ステップ2:Slack Appの作成

Slack App を作成します。

https://api.slack.com/messaging/webhooks の「Create your Slack app」をクリックします。

「From scratch」をクリックします。

アプリ名を入力し、ワークスペースに通知を飛ばすワークスペースを選択し「Create App」をクリックします。

これで Slack App の作成が完了しました。

ステップ3:Incoming Webhookの有効化

「Incoming Webhook」を選択します。

チェックボックスをクリックし有効化します。

これで、Incoming Webhook が有効化されました。

ステップ4:Webhookをワークスペースに追加

作成した Incoming Webhook をワークスペースに追加します。

「Add New Webhook to Workspace」をクリックします。

チャンネルを選択し「許可する」をクリックします。

これで Incoming Webook がワークスペースに連携されました。先程設定したチャンネルに Incoming Webhook とチャンネルが連携された趣旨のメッセージが見えるはずです。

設定画面に戻ると、先程まで出ていなかった Webhook URL が表示されています。

以下のようなフォーマットの URL が表示されているはずです。

https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

ステップ5:Incoming Webhook URLを利用しメッセージを投稿

Incoming Webhook URL を利用しメッセージを投稿し、Slack チャンネルにメッセージを飛ばします。

ターミナルに以下のように入力し実行します。

curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/XXXXXXXXXXXX/XXXXXXXXXXX/XXXXXXXXXXXXXXXXX

チャンネルに通知が飛んでいれば成功です。

Next.jsから通知を飛ばす

それでは、以降は、Next.js のプロジェクトを作り、ウェブアプリから Slack のチャンネルに通知を飛ばします。

実施内容は以下になります。

  • ボタンを配置します。
  • ボタンをクリックすると、Slack に通知が飛びます。
  • Slack への通知処理は、Server Actions を利用し、サーバサイドで実行されます。

Next.jsプロジェクトの新規作成

作業するプロジェクトを新規に作成していきます。

長いので、折りたたんでおきます。

新規プロジェクト作成と初期環境構築の手順詳細
$ pnpm create next-app@latest nextjs-slack-incoming-webhook-sample --typescript --eslint --import-alias "@/*" --src-dir --use-pnpm --tailwind --app
$ cd nextjs-slack-incoming-webhook-sample

以下の通り不要な設定を削除し、プロジェクトの初期環境を構築します。

$ mkdir src/styles
$ mv src/app/globals.css src/styles/globals.css
src/styles/globals.css
@tailwind base;
@tailwind components;
@tailwind utilities;
src/app/page.tsx
export default function Home() {
  return (
    <main className="text-lg">
      テストページ
    </main>
  )
}
src/app/layout.tsx
import '@/styles/globals.css'

export const metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ja">
      <body className="">{children}</body>
    </html>
  );
}
tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  plugins: [],
};
tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
+   "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  "exclude": ["node_modules"]
}

コミットします。

$ pnpm build
$ git add .
$ git commit -m "新規にプロジェクトを作成し, 作業環境を構築"

パッケージの追加

Slack へ通知するために必要なパッケージを追加します。

$ pnpm add @slack/webhook 

環境変数を設定

環境変数を設定します。サーバサイドで利用するため、NEXT_PUBLIC_ は不要です。

$ touch .env.local
.env.local
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX

この環境変数は Slack へ通知を出すときに利用します。

Server Actionsを導入

続いて、Server Actions を導入します。Server Actions を利用して、サーバサイドから Slack へ通知を飛ばします。

Server Actions を利用するには、next.config.jsexperimental.serverActions を追加します。Server Actions を利用するには、App Router を利用する必要があります。

next.config.js を修正します。

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
+  experimental: {
+    serverActions: true,
+  },
};

module.exports = nextConfig;

Server Actions のコードを記述します。

$ mkdir src/server-actions
$ touch src/server-actions/send-slack-message-action.ts
src/server-actions/send-slack-message-action.ts
"use server";

import { IncomingWebhook } from "@slack/webhook";

interface paramType {
  message: string;
}

export async function sendSlackMessage({ message }: paramType) {
  // get time in format yyyy/mm/dd hh:mm:ss in one line
  const date = new Date();
  const time = `${date.getFullYear()}/${
    date.getMonth() + 1
  }/${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;

  // 入力値の検証
  const url = process.env.SLACK_WEBHOOK_URL as string;
  const webhook = new IncomingWebhook(url);
  const payload = {
    blocks: [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: "新規メッセージだよ",
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: `:book:*メッセージ:*\n${message}`,
          },
          {
            type: "mrkdwn",
            text: `:clock1:*日時:*\n${time}`,
          },
        ],
      },
    ],
  };
  await webhook.send(payload);
}

ポイントを説明します。

Slackへの通知

以下のコードで Slack のチェンネルに通知します。

  const webhook = new IncomingWebhook(url);
  const payload = {
    blocks: [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: "新規メッセージだよ",
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: `:book:*メッセージ:*\n${message}`,
          },
          {
            type: "mrkdwn",
            text: `:clock1:*日時:*\n${time}`,
          },
        ],
      },
    ],
  };
  await webhook.send(payload);

メッセージのフォーマット

Slack へのメッセージはフォーマットをあてることができます。payload を以下のように変更することで、メッセージの装飾を変更できます。emoji を追加できます。今回は :book::clock1: の2つの絵文字を利用しています。

  const payload = {
    blocks: [
      {
        type: "header",
        text: {
          type: "plain_text",
          text: "新規メッセージだよ",
          emoji: true,
        },
      },
      {
        type: "section",
        fields: [
          {
            type: "mrkdwn",
            text: `:book:*メッセージ:*\n${message}`,
          },
          {
            type: "mrkdwn",
            text: `:clock1:*日時:*\n${time}`,
          },
        ],
      },
    ],
  };

フォーマットの詳しい情報はこちらを確認ください。
https://api.slack.com/messaging/webhooks

ボタンコンポーネントの作成

単純なボタンコンポーネントを作成します。

  • props で受け取った message を Slack のチャンネルに送信します。
  • Server Actions の useTransition を利用することで、サーバ側で処理中かステータスを判断できる isPending が利用できます。
  • 処理中の場合は、「送信中」と表示させます。
$ mkdir src/components
$ touch src/components/button.tsx
src/components/button.tsx
"use client";

import { sendSlackMessage } from "@/server-actions/send-slack-message-action";
import { FC, useTransition } from "react";

interface ButtonProps {
  message: string;
}

const Button:FC<ButtonProps> = (props) => {
  let [isPending, startTransition] = useTransition();
  return (
    <>
      {isPending ? (
        <button onClick={() => startTransition(() => sendSlackMessage({message:`${props.message}`}))} className="border-2 py-2 px-3 bg-slate-400 text-white rounded-lg w-[250px]" disabled={true}>
        {props.message} を送信中
        </button>
      ) : (
        <button onClick={() => startTransition(() => sendSlackMessage({message:`${props.message}`}))} className="border-2 py-2 px-3 bg-slate-800 text-white rounded-lg w-[250px]">
          {props.message} を送信
        </button>
      )}
    </>
  );
};

export default Button;

ページを修正

ページに先程作成したボタンコンポーネントを追加します。

下記の通り上書きします。

src/app/page.tsx
import Button from "@/components/button";

export default function Home() {
  return (
    <main className="text-lg">
      <Button message="Hello World" />
      <Button message="Hello Universe" />
    </main>
  )
}

動作確認

ローカルサーバで動作確認します。

$ pnpm dev

コミットします。

$ pnpm build
$ git add .
$ git commit -m "ボタンをクリックするとSlackのチャンネルに通知機能を追加"

まとめ

  • Slack のチャンネルに通知を飛ばす方法をまとめました。
  • Incoming Webhooksを利用し通知を飛ばしました。
  • Server Actions を利用し、サーバサイドから Slack のチャンネルに通知を飛ばしました。
  • 作業したコードはこちらにあります。

https://github.com/hayato94087/nextjs-slack-incoming-webhook-sample

Discussion