diff --git a/src/commands/membership/index.ts b/src/commands/membership/index.ts index 17d5ae8..f94eef9 100644 --- a/src/commands/membership/index.ts +++ b/src/commands/membership/index.ts @@ -2,6 +2,9 @@ import { SlashCommandBuilder } from "discord.js"; import type { Command } from "../types.js"; import executeJoinSubcommand from "./join.js"; import executeNotificationConfigSubcommand from "./notificationConfig.js"; +import executeRequestsList from "./requestsList.js"; +import executeRequestsAccept from "./requestsAccept.js"; +import executeRequestsReject from "./requestsReject.js"; const command: Command = { data: new SlashCommandBuilder() @@ -22,6 +25,37 @@ const command: Command = { .setName("身份組") .setDescription("可以接收通知的身份組"), ), + ) + .addSubcommandGroup((subcommandGroup) => subcommandGroup + .setName("請求") + .setDescription("管理社員加入請求") + .addSubcommand((subcommand) => subcommand + .setName("查看") + .setDescription("查看正在等待幹部確認的請求"), + ) + .addSubcommand((subcommand) => subcommand + .setName("接受") + .setDescription("接受請求") + .addUserOption((option) => option + .setName("使用者") + .setDescription("要接受的使用者") + .setRequired(true), + ), + ) + .addSubcommand((subcommand) => subcommand + .setName("拒絕") + .setDescription("拒絕請求") + .addUserOption((option) => option + .setName("使用者") + .setDescription("要拒絕的使用者") + .setRequired(true), + ) + .addStringOption((option) => option + .setName("原因") + .setDescription("完整的拒絕理由,這會傳送給請求加入者。他會回到「填寫基本資料」步驟。") + .setRequired(true), + ), + ), ), async execute(interaction) { @@ -36,6 +70,15 @@ const command: Command = { case "設定通知頻道": await executeNotificationConfigSubcommand(interaction); break; + case "查看": + await executeRequestsList(interaction); + break; + case "接受": + await executeRequestsAccept(interaction); + break; + case "拒絕": + await executeRequestsReject(interaction); + break; } }, }; diff --git a/src/commands/membership/requestsAccept.ts b/src/commands/membership/requestsAccept.ts new file mode 100644 index 0000000..46c2c3d --- /dev/null +++ b/src/commands/membership/requestsAccept.ts @@ -0,0 +1,38 @@ +import { RegistrationStep } from "@prisma/client"; +import config, { messages } from "../../config.js"; +import { prisma } from "../../main.js"; +import type { Subcommand } from "../types.js"; + +const executeRequestsAccept: Subcommand = async (interaction) => { + if (!interaction.inGuild()) { + await interaction.reply("請在伺服器內使用此指令"); + return; + } + + // requestUser is a User, but we need a GuildMember to add the role. + // Find the GuildMember by the User's ID. + const requestUser = interaction.options.getUser("使用者", true); + const requester = interaction.guild!.members.cache.get(requestUser.id)!; + const discordId = BigInt(requester.id); + + const member = await prisma.member.findUnique({ where: { discordId } }); + if (!member) { + await interaction.reply(messages.error.generic); + return; + } + if (member.registrationStep !== RegistrationStep.COMMITTEE_CONFIRMATION) { + await interaction.reply("這個使用者並沒有等待幹部確認的加入請求"); + return; + } + + await prisma.member.update({ + data: { registrationStep: RegistrationStep.COMPLETE }, + where: { discordId }, + }); + const membershipRole = interaction.guild!.roles.cache.get(config.membershipRoleId)!; + await requester.roles.add(membershipRole); + await requester.send(messages.join.accept); + await interaction.reply(`已接受 <@${requester.id}> 的加入請求。`); +}; + +export default executeRequestsAccept; diff --git a/src/commands/membership/requestsList.ts b/src/commands/membership/requestsList.ts new file mode 100644 index 0000000..9f4d071 --- /dev/null +++ b/src/commands/membership/requestsList.ts @@ -0,0 +1,28 @@ +import { RegistrationStep } from "@prisma/client"; +import { prisma } from "../../main.js"; +import type { Subcommand } from "../types.js"; + +const executeRequestsList: Subcommand = async (interaction) => { + if (!interaction.inGuild()) { + await interaction.reply("請在伺服器內使用此指令"); + return; + } + + const requests = await prisma.member.findMany({ + where: { registrationStep: RegistrationStep.COMMITTEE_CONFIRMATION }, + }); + + const requestList = requests.map((request) => { + const relativeNotificationDate = ``; + const { discordId, email, name, studentId } = request; + return `<@${discordId}> ${relativeNotificationDate}: \`${email}\`, \`${name}\`, \`${studentId}\``; + }); + + await interaction.reply( + `目前有 ${requests.length} 位使用者正在等待幹部確認${requests.length ? ":" : "。"} +${requests.length ? "(`電子郵件`, `姓名`, `學號`)" : ""} +${requestList.join("\n")}`, + ); +}; + +export default executeRequestsList; diff --git a/src/commands/membership/requestsReject.ts b/src/commands/membership/requestsReject.ts new file mode 100644 index 0000000..acbd224 --- /dev/null +++ b/src/commands/membership/requestsReject.ts @@ -0,0 +1,34 @@ +import { RegistrationStep } from "@prisma/client"; +import { prisma } from "../../main.js"; +import type { Subcommand } from "../types.js"; +import { messages } from "../../config.js"; + +const executeRequestsReject: Subcommand = async (interaction) => { + if (!interaction.inGuild()) { + await interaction.reply("請在伺服器內使用此指令"); + return; + } + + const requester = interaction.options.getUser("使用者", true); + const reason = interaction.options.getString("原因", true); + const discordId = BigInt(requester.id); + + const member = await prisma.member.findUnique({ where: { discordId } }); + if (!member) { + await interaction.reply({ content: messages.error.notInDatabase, ephemeral: true }); + return; + } + if (member?.registrationStep !== RegistrationStep.COMMITTEE_CONFIRMATION) { + await interaction.reply({ content: messages.error.notAwaitingConfirmation, ephemeral: true }); + return; + } + + await prisma.member.update({ + data: { registrationStep: RegistrationStep.BASIC_INFORMATION }, + where: { discordId }, + }); + await requester.send(messages.join.reject(reason)); + await interaction.reply(`已拒絕 <@${requester.id}> 的加入請求,理由:${reason}。`); +}; + +export default executeRequestsReject;