Slack Events APIで受講生の名前を自動更新!
投稿日: 2025年04月14日
先日、ShiftB受講生の管理画面上の名前と、Slackの名前が全然違う場合に「この人誰だろう・・」ってなるなぁと思って、ぶべさんにその旨を伝えてみたら「確かに・・茜さん、やりますか?笑」ってありがたいことにブーメラン式に返ってきました。
SlackAPIでできることの調査から実装まで丸っとやらせていただくことになりました。
どんな風に実装したかを共有します。
Slackが提供しているEvents APIを使用しました。
Events APIを使用すると管理画面で設定したエンドポイントに指定したイベント(例:ユーザーが参加した・ユーザー情報を変更した等)が発生したタイミングでHTTPリクエスト(POST)を送信してくれます。
つまりはWebhookですね。
この仕組みを利用したら問題解決できると考えました。
新しい受講生がワークスペースに参加した時
プロフィールを更新した時
にデータを更新したいので、適切なイベントを探したところ、
ワークスペースに参加した時 → team_join
名前を変更した時 → user_change
このイベントが発火した時にprofileテーブルの情報を更新することにしました。
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;
}
}
}
以上です。
管理者のぶべさんに依頼しました。
私が確認しながら実装するときは、個人開発のエラー通知用のワークスペースで試して、イベントオブジェクトの構造を見たり、何の設定を行うことで意図した通りに動くかなど確認してから、まとめて依頼しました。
①OAuth & PermissionsのBot Token Scopesにuser:readとusers:read:emailを追加
②Event SubscriptionsのEnable Eventsにエンドポイント追加
③Subscribe to events on behalf of usersにteam_joinとuser_changeを追加
Slackのスコープを変更した後、必ず「Install App to Workspace」ボタンを押して再インストールしないと反映されません。
user.infoでemailを取得したい場合、user:readだけでは足りず、users:read:emailのスコープも必要です。
詳しく解説されている記事がありましたので参考にしました。
これで完成です!!
既存データに登録する作業をする際はusers.listメソッドを使用してデータ取得してコピペして登録しました。
実際にやってみて、外部サービスとの連携ってハードル高そうに見えても、必要なデータの流れを整理すれば意外とシンプルに組めるなと感じました。
今後も、業務上の「こうなると便利なのに」を見つけたら、どんどん手を動かして仕組み化していきたいです。