Slack Events APIで受講生の名前を自動更新!

Slack Events APIで受講生の名前を自動更新!

投稿日: 2025年04月14日

学習振り返り
要約
  • SlackのEvents APIを使用して、受講生の管理画面とSlackの名前を同期する機能を実装した。
  • team_joinやuser_changeイベントに応じて、データベースのプロフィール情報を更新する仕組みを作成した。
  • 外部サービスとの連携はデータの流れを整理することで、意外とシンプルに実現できることを学んだ。
音声で記事を再生

はじめに

先日、ShiftB受講生の管理画面上の名前と、Slackの名前が全然違う場合に「この人誰だろう・・」ってなるなぁと思って、ぶべさんにその旨を伝えてみたら「確かに・・茜さん、やりますか?笑」ってありがたいことにブーメラン式に返ってきました。
SlackAPIでできることの調査から実装まで丸っとやらせていただくことになりました。

どんな風に実装したかを共有します。

Events API

Slackが提供しているEvents APIを使用しました。
Events APIを使用すると管理画面で設定したエンドポイントに指定したイベント(例:ユーザーが参加した・ユーザー情報を変更した等)が発生したタイミングでHTTPリクエスト(POST)を送信してくれます。

つまりはWebhookですね。

この仕組みを利用したら問題解決できると考えました。

実装する前に調査等

イベントの選定

  • 新しい受講生がワークスペースに参加した時

  • プロフィールを更新した時

にデータを更新したいので、適切なイベントを探したところ、

ワークスペースに参加した時 → team_join
名前を変更した時 → user_change

このイベントが発火した時にprofileテーブルの情報を更新することにしました。


DBに追加した項目

DBにはslackIdとslackNameのカラムを追加しました。

考えたこと

team_joinイベントが持っている情報ではDBに存在する誰なのかが分からないので、slackIdを元にusers.infoメソッドを使用して、email(DBではunique制約あり)の取得を行い、profileテーブルのslackIdとslackNameを更新します。

コード

エンドポイント(/api/webhook/slack/route.ts)

import { NextRequest, NextResponse } from "next/server";
import { SlackService } from "@/app/_services/SlackService";
import { prisma } from "@/app/_utils/prisma";

export async function POST(request: NextRequest) {
  const body = await request.json();
  try {
    const {
      type,
      user: {
        id,
        profile: { real_name: realName },
      },
    } = body.event;
    //user_changeは登録済の人だからslackIdで更新
    if (type === "user_change") {
      await prisma.profile.update({
        where: {
          slackId: id,
        },
        data: {
          slackName: realName,
        },
      });
    }
    //初回登録なのでprofileのslackId,slackNameはnull→emailから特定する
    if (type === "team_join") {
      const slack = new SlackService();
      const userData = await slack.userInfo({ id });
      if (!userData) throw new Error("slack userInfo is not found");
      const mail = userData.user?.profile?.email;
      await prisma.profile.update({
        where: {
          mail,
        },
        data: {
          slackId: id,
          slackName: realName,
        },
      });
    }
    return NextResponse.json({ message: "success" }, { status: 200 });
  } catch (error) {
    return NextResponse.json(error);
  }
}

こんな感じです。

次に、userInfoを使用しemailを取得する処理です。
/_services/SlackServise.ts

import { WebClient } from "@slack/web-api";

export class SlackService {
  web: WebClient;

  constructor() {
    this.web = new WebClient(process.env.SLACK_API_TOKEN);
  }

  async userInfo({ id }: { id: string }) {
    try {
      return await this.web.users.info({ user: id });
    } catch (error) {
      console.error(error);
      return null;
    }
  }
}

以上です。

Slack Appの設定

管理者のぶべさんに依頼しました。
私が確認しながら実装するときは、個人開発のエラー通知用のワークスペースで試して、イベントオブジェクトの構造を見たり、何の設定を行うことで意図した通りに動くかなど確認してから、まとめて依頼しました。

OAuth & PermissionsBot Token Scopesuser:readusers:read:emailを追加
Event SubscriptionsEnable Eventsにエンドポイント追加
Subscribe to events on behalf of usersteam_joinuser_changeを追加

Slackのスコープを変更した後、必ず「Install App to Workspace」ボタンを押して再インストールしないと反映されません。

user.infoでemailを取得したい場合、user:readだけでは足りず、users:read:emailのスコープも必要です。

詳しく解説されている記事がありましたので参考にしました。

これで完成です!!

既存データに登録する作業をする際はusers.listメソッドを使用してデータ取得してコピペして登録しました。

おわりに

実際にやってみて、外部サービスとの連携ってハードル高そうに見えても、必要なデータの流れを整理すれば意外とシンプルに組めるなと感じました。

今後も、業務上の「こうなると便利なのに」を見つけたら、どんどん手を動かして仕組み化していきたいです。

シェア!

Threads
user
吉本茜
山口在住/二児の母
Loading...
記事一覧に戻る
Threads
0