From c7e48cc8e18d964dfa7ea3c940ec8b3c99036525 Mon Sep 17 00:00:00 2001 From: SamTheKorean Date: Wed, 22 Apr 2026 21:10:38 -0400 Subject: [PATCH] fix : discord logic --- src/discord.ts | 53 +++++++++++++++++++++++++++++++++++--------------- src/index.ts | 32 ++++++++++++++---------------- 2 files changed, 52 insertions(+), 33 deletions(-) diff --git a/src/discord.ts b/src/discord.ts index 170e92c..2c0c1dc 100644 --- a/src/discord.ts +++ b/src/discord.ts @@ -49,28 +49,49 @@ export async function assignRole( } /** - * Discord 채널 메시지 조회 + * Discord 포럼 채널의 포스트(스레드) 목록 및 첫 메시지 조회 */ -export async function getChannelMessages( - channelId: string, +export async function getForumPosts( + guildId: string, + forumChannelId: string, botToken: string, - limit: number = 50, ): Promise { - const response = await fetch( - `https://discord.com/api/v10/channels/${channelId}/messages?limit=${limit}`, - { - headers: { - Authorization: `Bot ${botToken}`, - }, - }, - ); + const headers = { Authorization: `Bot ${botToken}` }; - if (!response.ok) { - const data = await response.json() as any; - throw new Error(`Failed to get channel messages: ${JSON.stringify(data)}`); + const [activeRes, archivedRes] = await Promise.all([ + fetch(`https://discord.com/api/v10/guilds/${guildId}/threads/active`, { headers }), + fetch(`https://discord.com/api/v10/channels/${forumChannelId}/threads/archived/public?limit=50`, { headers }), + ]); + + if (!activeRes.ok) { + const data = await activeRes.json() as any; + throw new Error(`Failed to get active threads: ${JSON.stringify(data)}`); + } + if (!archivedRes.ok) { + const data = await archivedRes.json() as any; + throw new Error(`Failed to get archived threads: ${JSON.stringify(data)}`); } - return response.json() as Promise; + const activeData = await activeRes.json() as any; + const archivedData = await archivedRes.json() as any; + + const activeThreads = (activeData.threads ?? []).filter((t: any) => t.parent_id === forumChannelId); + const archivedThreads = archivedData.threads ?? []; + const allThreads = [...activeThreads, ...archivedThreads]; + + const posts = await Promise.all( + allThreads.map(async (thread: any) => { + const msgRes = await fetch( + `https://discord.com/api/v10/channels/${thread.id}/messages/${thread.id}`, + { headers }, + ); + if (!msgRes.ok) return null; + const msg = await msgRes.json() as any; + return { ...msg, threadId: thread.id }; + }), + ); + + return posts.filter(Boolean); } /** diff --git a/src/index.ts b/src/index.ts index 246c40f..dedfe31 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,6 @@ import type { Env, RoleTeamConfig } from "./types.js"; import { getInstallationToken, checkSponsorship, getTeamCreatedAt, inviteToTeam, getTeamMembership } from "./github.js"; -import { verifySignature, assignRole, getChannelMessages, replyToMessage, addReaction } from "./discord.js"; +import { verifySignature, assignRole, getForumPosts, replyToMessage, addReaction } from "./discord.js"; export default { async fetch( @@ -157,34 +157,32 @@ function parseJoinMessage(content: string): ParsedJoinMessage | null { } async function handleChannelJoin(env: Env): Promise { - const messages = await getChannelMessages(env.STUDY_JOIN_CHANNEL_ID, env.DISCORD_BOT_TOKEN, 50); + const posts = await getForumPosts(env.DISCORD_GUILD_ID, env.STUDY_JOIN_CHANNEL_ID, env.DISCORD_BOT_TOKEN); - const unprocessed = messages.filter((msg) => { + const unprocessed = posts.filter((msg) => { if (msg.author?.bot) return false; - const hasCheckReaction = msg.reactions?.some( - (r: any) => r.emoji.name === "✅" && r.me, - ); - return !hasCheckReaction; + return !msg.reactions?.length; }); - console.log(`[cron] total=${messages.length} unprocessed=${unprocessed.length}`); + console.log(`[cron] total=${posts.length} unprocessed=${unprocessed.length}`); for (const msg of unprocessed) { + const threadId = msg.threadId as string; const parsed = parseJoinMessage(msg.content ?? ""); if (!parsed) { - console.log(`[cron] parse failed messageId=${msg.id}`); + console.log(`[cron] parse failed threadId=${threadId}`); await replyToMessage( - env.STUDY_JOIN_CHANNEL_ID, + threadId, msg.id, "⚠️ 메시지 형식이 올바르지 않습니다. 아래 형식으로 다시 작성해주세요.\n```\ngithub: your_github_username\nteam: team_name\nrole: role_name\n```", env.DISCORD_BOT_TOKEN, ); - await addReaction(env.STUDY_JOIN_CHANNEL_ID, msg.id, "❌", env.DISCORD_BOT_TOKEN); + await addReaction(threadId, msg.id, "❌", env.DISCORD_BOT_TOKEN); continue; } - console.log(`[cron] processing messageId=${msg.id} github=${parsed.githubUsername} team=${parsed.team} role=${parsed.role}`); + console.log(`[cron] processing threadId=${threadId} github=${parsed.githubUsername} team=${parsed.team} role=${parsed.role}`); try { const result = await processVerify( @@ -194,17 +192,17 @@ async function handleChannelJoin(env: Env): Promise { msg.author.id, env, ); - await replyToMessage(env.STUDY_JOIN_CHANNEL_ID, msg.id, result, env.DISCORD_BOT_TOKEN); - await addReaction(env.STUDY_JOIN_CHANNEL_ID, msg.id, "✅", env.DISCORD_BOT_TOKEN); + await replyToMessage(threadId, msg.id, result, env.DISCORD_BOT_TOKEN); + await addReaction(threadId, msg.id, "✅", env.DISCORD_BOT_TOKEN); } catch (err: any) { - console.error(`[cron] error messageId=${msg.id}`, err); + console.error(`[cron] error threadId=${threadId}`, err); await replyToMessage( - env.STUDY_JOIN_CHANNEL_ID, + threadId, msg.id, `⚠️ 오류가 발생했습니다: ${err?.message ?? "알 수 없는 오류"}`, env.DISCORD_BOT_TOKEN, ); - await addReaction(env.STUDY_JOIN_CHANNEL_ID, msg.id, "❌", env.DISCORD_BOT_TOKEN); + await addReaction(threadId, msg.id, "❌", env.DISCORD_BOT_TOKEN); } } }