Skip to content

Commit

Permalink
Changed post mission to modal
Browse files Browse the repository at this point in the history
  • Loading branch information
coinmoles committed Dec 21, 2023
1 parent 8be16e5 commit 1408172
Show file tree
Hide file tree
Showing 14 changed files with 216 additions and 96 deletions.
17 changes: 4 additions & 13 deletions src/commands/missionGive/postMission/builders.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,15 @@
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, MessageActionRowComponentBuilder } from "discord.js"
import { ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, MessageActionRowComponentBuilder, ModalBuilder, TextInputBuilder, TextInputStyle } from "discord.js"
import { cancelColor, normalColor, successColor } from "../../utils/colors.js"

const commandId = "post-mission"

const replyEmbedPrototype = new EmbedBuilder().setTitle("승격조건을 추가합니까?").setColor(normalColor)
const successEmbedPrototype = new EmbedBuilder().setTitle("승격조건을 추가했습니다").setColor(successColor)
const cancelEmbedPrototype = new EmbedBuilder().setTitle("승격조건 추가를 취소했습니다").setColor(cancelColor)

const confirmButtonId = "post-mission-confirm"
const confirmButton = new ButtonBuilder().setCustomId(confirmButtonId).setLabel("확인").setStyle(ButtonStyle.Success)
const cancelButtonId = "post-mission-cancel"
const cancelButton = new ButtonBuilder().setCustomId(cancelButtonId).setLabel("취소").setStyle(ButtonStyle.Danger)
const actionRow = new ActionRowBuilder<MessageActionRowComponentBuilder>().addComponents(confirmButton, cancelButton)

export default {
commandId,
replyEmbedPrototype,
successEmbedPrototype,
cancelEmbedPrototype,

confirmButtonId,
confirmButton,
cancelButtonId,
cancelButton,
actionRow
}
6 changes: 1 addition & 5 deletions src/commands/missionGive/postMission/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,7 @@ const postMission: SlashCommandSubcommandContainer = {
builder: new SlashCommandSubcommandBuilder()
.setName(name)
.setDescription(description)
.addUserOption(option => option.setName("대상").setDescription("추가할 승격조건 대상, 공통 조건을 추가하는 경우 자기 자신을 입력").setRequired(true))
.addStringOption(option => option.setName("내용").setDescription("추가할 승격조건 내용").setRequired(true))
.addStringOption(option => option.setName("카테고리").setDescription("추가할 승격조건 카테고리"))
.addStringOption(option => option.setName("비고").setDescription("추가할 승격조건 비고"))
.addNumberOption(option => option.setName("점수").setDescription("추가할 승격조건 점수, 명시하지 않을 경우 1점")),
.addUserOption(option => option.setName("대상").setDescription("추가할 승격조건 대상, 공통 조건을 추가하는 경우 자기 자신을 입력").setRequired(true)),
callback: reply
}

Expand Down
109 changes: 60 additions & 49 deletions src/commands/missionGive/postMission/operations.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,95 @@
import builders from "./builders.js"
import { ButtonInteraction, ChatInputCommandInteraction, ComponentType, EmbedBuilder, InteractionResponse, Message, User } from "discord.js"
import { ButtonInteraction, ChatInputCommandInteraction, ComponentType, EmbedBuilder, InteractionResponse, Message, ModalSubmitInteraction, User } from "discord.js"
import { postMission } from "../../../db/actions/missionActions.js"
import { Mission } from "../../../interfaces/models/Mission.js"
import { errorEmbed } from "../../utils/errorEmbeds.js"
import { createMissionPreviewString, createMissionPreviewTitle } from "../../utils/createString/createMissionPreviewString.js"
import { checkAssociate } from "../../utils/checks/checkAssociate.js"
import { confirmCollect, confirmFilter, confirmTimeout, createConfirmActionRow } from "../../utils/components/confirmActionRow.js"
import { createMissionModal, createMissionModalId, getMissionModalValue } from "../../utils/components/missionModal.js"
import { createMissionPreviewString, createMissionPreviewTitle } from "../../utils/createString/createMissionPreviewString.js"
import { errorEmbed } from "../../utils/errorEmbeds.js"
import { getQuarterDataFooter } from "../../utils/quarterData/getQuarterData.js"
import builders from "./builders.js"

const {
commandId,
replyEmbedPrototype,
successEmbedPrototype,
cancelEmbedPrototype,
confirmButtonId,
cancelButtonId,
actionRow
} = builders

const readOptions = (interaction: ChatInputCommandInteraction) => ({
target: interaction.options.getUser("대상", true),
category: interaction.options.getString("카테고리"),
content: interaction.options.getString("내용", true),
note: interaction.options.getString("비고"),
score: interaction.options.getNumber("점수")
export const readOptions = (interaction: ChatInputCommandInteraction) => ({
target: interaction.options.getUser("대상", true)
})

const doConfirm = async (interaction: ChatInputCommandInteraction, buttonInteraction: ButtonInteraction, target: User, mission: Mission) => {
await buttonInteraction.deferReply({ephemeral: true})
const onConfirm = (modalInteraction: ModalSubmitInteraction, target: User, mission: Mission) =>
async (buttonInteraction: ButtonInteraction) => {
await buttonInteraction.deferReply({ ephemeral: true })

const success = await postMission(mission)
const success = await postMission(mission)

if (success) {
const successEmbed = new EmbedBuilder(successEmbedPrototype.toJSON())
.addFields({ name: createMissionPreviewTitle(mission, target), value: createMissionPreviewString(mission, target) })
if (success) {
const successEmbed = new EmbedBuilder(successEmbedPrototype.toJSON())
.addFields({ name: createMissionPreviewTitle(mission, target), value: createMissionPreviewString(mission, target) })

await buttonInteraction.deleteReply()
await interaction.editReply({ embeds: [successEmbed.setFooter(await getQuarterDataFooter())], components: [] })
await buttonInteraction.deleteReply()
await modalInteraction.editReply({ embeds: [successEmbed.setFooter(await getQuarterDataFooter())], components: [] })
}
else
await buttonInteraction.editReply({ embeds: [errorEmbed] })
}
else
await buttonInteraction.editReply({ embeds: [errorEmbed] })
}

const doCancel = async (interaction: ChatInputCommandInteraction, buttonInteraction: ButtonInteraction, target: User, mission: Mission) => {
await buttonInteraction.deferUpdate()
const onCancel = (modalInteraction: ModalSubmitInteraction, target: User, mission: Mission) =>
async (buttonInteraction: ButtonInteraction) => {
await buttonInteraction.deferUpdate()

const cancelEmbed = new EmbedBuilder(cancelEmbedPrototype.toJSON())
.addFields({ name: createMissionPreviewTitle(mission, target), value: createMissionPreviewString(mission, target) })
const cancelEmbed = new EmbedBuilder(cancelEmbedPrototype.toJSON())
.addFields({ name: createMissionPreviewTitle(mission, target), value: createMissionPreviewString(mission, target) })

await interaction.editReply({ embeds: [cancelEmbed.setFooter(await getQuarterDataFooter())], components: [] })
}
await modalInteraction.editReply({ embeds: [cancelEmbed.setFooter(await getQuarterDataFooter())], components: [] })
}

const addCollector = async (
interaction: ChatInputCommandInteraction, reply: Message<boolean> | InteractionResponse<boolean>,
modalInteraction: ModalSubmitInteraction, reply: Message<boolean> | InteractionResponse<boolean>,
target: User, mission: Mission
) => {
const collector = reply.createMessageComponentCollector({
filter: i => i.user === interaction.user,
componentType: ComponentType.Button
})
collector.on("collect", async buttonInteraction => {
if (buttonInteraction.customId === confirmButtonId)
doConfirm(interaction, buttonInteraction, target, mission)
else if (buttonInteraction.customId === cancelButtonId)
doCancel(interaction, buttonInteraction, target, mission)
filter: confirmFilter(commandId, modalInteraction.user),
componentType: ComponentType.Button, time: 10_000, max: 1
})

collector.on("collect", confirmCollect(commandId, modalInteraction.user,
onConfirm(modalInteraction, target, mission), onCancel(modalInteraction, target, mission)))
collector.on('end', confirmTimeout(modalInteraction));
}

export const doReply = async (interaction: ChatInputCommandInteraction, target: User, isEditing: boolean = false) => {
if (!await checkAssociate(interaction, target.id, true))
return

const doReply = async (interaction: ChatInputCommandInteraction, target: User, mission: Mission, isEditing: boolean = false) => {
if (!isEditing)
await interaction.deferReply({ephemeral: true})
await interaction.showModal(createMissionModal(commandId, interaction.user, target))

if (!await checkAssociate(interaction, target.id, true))
const modalInteraction = await interaction.awaitModalSubmit({
filter: mI => mI.customId === createMissionModalId(commandId, interaction.user),
time: 60_000
}).catch(_ => null)
if (modalInteraction === null)
return

const replyEmbed = new EmbedBuilder(replyEmbedPrototype.toJSON())
const mission = getMissionModalValue(modalInteraction, target)
if (mission === undefined) {
await modalInteraction.reply("점수가 숫자가 아닙니다!")
return
}

const replyEmbed = EmbedBuilder.from(replyEmbedPrototype)
.addFields({ name: createMissionPreviewTitle(mission, target), value: createMissionPreviewString(mission, target) })
const reply = await interaction.editReply({ embeds: [replyEmbed.setFooter(await getQuarterDataFooter())], components: [actionRow] });
.setFooter(await getQuarterDataFooter())

if (!isEditing)
addCollector(interaction, reply, target, mission)
}
const reply = await modalInteraction.reply({
embeds: [replyEmbed],
components: [createConfirmActionRow(commandId, modalInteraction.user)],
ephemeral: true,
fetchReply: true
});

export default { readOptions, doReply }
await addCollector(modalInteraction, reply, target, mission)
}
19 changes: 3 additions & 16 deletions src/commands/missionGive/postMission/reply.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
import assert from "assert"
import { InteractionOperation } from "../../../interfaces/commands/InteractionOperation.js"
import { Mission } from "../../../interfaces/models/Mission.js"
import operations from "./operations.js"

const { readOptions, doReply } = operations
import { doReply, readOptions } from "./operations.js"

const reply: InteractionOperation = async interaction => {
assert(interaction.isChatInputCommand())

const { target, category, content, note, score } = readOptions(interaction)

const mission: Mission = {
category: category ?? (target !== interaction.user ? "개인 조건" : "공통 조건"),
content,
note: note ?? "",
score: score ?? 1,
giverId: interaction.user.id,
targetId: target.id,
completed: []
}
const { target } = readOptions(interaction)

await doReply(interaction, target, mission)
await doReply(interaction, target)
}

export default reply
5 changes: 3 additions & 2 deletions src/commands/utils/checks/checkAssociate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getAssociate } from "../../../db/actions/memberActions.js";
import { ReplyComponents } from "../../../interfaces/ReplyComponents.js";
import { selectNotAssociateEmbed, selectNotAssociateOrSelfEmbed, selectUnknownAssociateEmbed } from "../errorEmbeds.js";
import { getRoleIds } from "../roleId/getRoleIds.js";
import { editOrReply } from "../editOrReply.js";

export const checkAssociate = async (
interaction: ChatInputCommandInteraction,
Expand All @@ -22,11 +23,11 @@ export const checkAssociate = async (
if (allowSelf && interaction.user.id === associateId)
return true
if (!associateMember.roles.cache.has(roleIds.associateRole)) {
await interaction.editReply({ embeds: [allowSelf ? selectNotAssociateOrSelfEmbed : selectNotAssociateEmbed], components })
await editOrReply(interaction, { embeds: [allowSelf ? selectNotAssociateOrSelfEmbed : selectNotAssociateEmbed], components })
return false
}
else if (await getAssociate(associateId) === undefined) {
await interaction.editReply({ embeds: [selectUnknownAssociateEmbed], components })
await editOrReply(interaction, { embeds: [selectUnknownAssociateEmbed], components })
return false
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/utils/checks/checkDeveloper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChatInputCommandInteraction } from "discord.js";
import { ReplyComponents } from "../../../interfaces/ReplyComponents.js";
import { testEmbed } from "../errorEmbeds.js";
import { editOrReply } from "../editOrReply.js";

export const checkDeveloper = async (interaction: ChatInputCommandInteraction, components?: ReplyComponents[]) => {
if (interaction.user.id !== "772050469430099989") {
await interaction.reply({ embeds: [testEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [testEmbed], components, ephemeral: true })
return false
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/utils/checks/checkGuild.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { ChatInputCommandInteraction } from "discord.js"
import { ReplyComponents } from "../../../interfaces/ReplyComponents.js"
import { notInGuildEmbed } from "../errorEmbeds.js"
import { editOrReply } from "../editOrReply.js"

export const checkGuild = async (interaction: ChatInputCommandInteraction, components?: ReplyComponents[]): Promise<boolean> => {
if (interaction.guild === null || interaction.guild.id !== process.env.GUILD_ID) {
await interaction.reply({ embeds: [notInGuildEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [notInGuildEmbed], components, ephemeral: true })
return false
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/utils/checks/checkInitialized.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { ChatInputCommandInteraction } from "discord.js"
import { getRoleIds } from "../roleId/getRoleIds.js"
import { notInitializedEmbed } from "../errorEmbeds.js"
import { editOrReply } from "../editOrReply.js"

export const checkInitialized = async (interaction: ChatInputCommandInteraction): Promise<boolean> => {
const roleIds = await getRoleIds()
if (roleIds.chiefRole === "") {
await interaction.reply({ embeds: [notInitializedEmbed], ephemeral: true })
await editOrReply(interaction, { embeds: [notInitializedEmbed], ephemeral: true })
return false
}

Expand Down
13 changes: 7 additions & 6 deletions src/commands/utils/checks/checkPermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ReplyComponents } from "../../../interfaces/ReplyComponents.js"
import { CommandType } from "../../../interfaces/commands/CommandTypes.js"
import { notAssociateEmbed, notChiefEmbed, notOwnerEmbed, notRegularEmbed, unknownAssociateEmbed, unknownRegularEmbed } from "../errorEmbeds.js"
import { getRoleIds } from "../roleId/getRoleIds.js"
import { editOrReply } from "../editOrReply.js"

export const checkPermission = async (interaction: ChatInputCommandInteraction, type: CommandType, components?: ReplyComponents[]): Promise<boolean> => {
assert(interaction.guild !== null)
Expand All @@ -14,7 +15,7 @@ export const checkPermission = async (interaction: ChatInputCommandInteraction,

if (type === "서버장") {
if (interaction.user.id !== interaction.guild.ownerId) {
await interaction.reply({ embeds: [notOwnerEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [notOwnerEmbed], components, ephemeral: true })
return false
}
return true
Expand All @@ -26,31 +27,31 @@ export const checkPermission = async (interaction: ChatInputCommandInteraction,

if (type === "넬장") {
if (!member.roles.cache.has(roleIds.chiefRole)) {
await interaction.reply({ embeds: [notChiefEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [notChiefEmbed], components, ephemeral: true })
return false
}
return true
}

if (type === "정회원") {
if (!member.roles.cache.has(roleIds.regularRole)) {
await interaction.reply({ embeds: [notRegularEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [notRegularEmbed], components, ephemeral: true })
return false
}
else if (await getRegular(interaction.user.id) === undefined) {
await interaction.reply({ embeds: [unknownRegularEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [unknownRegularEmbed], components, ephemeral: true })
return true
}
return true
}

if (type === "준회원") {
if (!member.roles.cache.has(roleIds.associateRole)) {
await interaction.reply({ embeds: [notAssociateEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [notAssociateEmbed], components, ephemeral: true })
return false
}
else if (await getAssociate(interaction.user.id) === undefined) {
await interaction.reply({ embeds: [unknownAssociateEmbed], components, ephemeral: true })
await editOrReply(interaction, { embeds: [unknownAssociateEmbed], components, ephemeral: true })
return false
}

Expand Down
5 changes: 3 additions & 2 deletions src/commands/utils/checks/checkRegular.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getRegular } from "../../../db/actions/memberActions.js";
import { ReplyComponents } from "../../../interfaces/ReplyComponents.js";
import { selectNotRegularEmbed, selectUnknownRegularEmbed } from "../errorEmbeds.js";
import { getRoleIds } from "../roleId/getRoleIds.js";
import { editOrReply } from "../editOrReply.js";

export const checkRegular = async (interaction: ChatInputCommandInteraction, regularId: string, components?: ReplyComponents[]): Promise<boolean> => {
assert(interaction.guild !== null)
Expand All @@ -15,11 +16,11 @@ export const checkRegular = async (interaction: ChatInputCommandInteraction, reg
assert(regularMember !== undefined)

if (!regularMember.roles.cache.has(roleIds.regularRole)) {
await interaction.editReply({ embeds: [selectNotRegularEmbed], components })
await editOrReply(interaction, { embeds: [selectNotRegularEmbed], components })
return false
}
else if (await getRegular(regularId) === undefined) {
await interaction.editReply({ embeds: [selectUnknownRegularEmbed], components })
await editOrReply(interaction, { embeds: [selectUnknownRegularEmbed], components })
return false
}

Expand Down
Loading

0 comments on commit 1408172

Please sign in to comment.