Skip to content

Commit

Permalink
feat: prevent timeout in command response (#984)
Browse files Browse the repository at this point in the history
Co-authored-by: Mogyuchi <[email protected]>
  • Loading branch information
kazukazu123123 and Mogyuchi authored Jul 30, 2024
1 parent ee9e9a8 commit bf28075
Show file tree
Hide file tree
Showing 9 changed files with 77 additions and 26 deletions.
15 changes: 11 additions & 4 deletions src/commands/join.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { guildCtxManager } from '../index.js'
import { type InteractionReplyOptions } from 'discord.js'
import { checkCanJoin, checkUserAlreadyJoined } from '../components/preCheck.js'
import { newGuildTextBasedChannelId, newVoiceBasedChannelId } from '../id.js'
import { getErrorReply } from '../utils.js'
import { deferredReplyOrEdit, getErrorReply } from '../utils.js'

export class JoinCommand extends Command {
public constructor(
Expand Down Expand Up @@ -48,10 +48,17 @@ export class JoinCommand extends Command {
checkCanJoin(voiceChannel)

const guildCtx = guildCtxManager.get(interaction.member.guild)
const readChannelId = newGuildTextBasedChannelId(interaction.channel)
const voiceChannelId = newVoiceBasedChannelId(voiceChannel)

guildCtx.checkAlreadyJoined(voiceChannelId)
guildCtx.connectionManager.checkAlreadyUsedChannel(readChannelId)

await interaction.deferReply()

const worker = await guildCtx.join({
voiceChannelId: newVoiceBasedChannelId(voiceChannel),
readChannelId: newGuildTextBasedChannelId(interaction.channel),
voiceChannelId,
readChannelId,
})

interactionReplyOptions = {
Expand All @@ -73,7 +80,7 @@ export class JoinCommand extends Command {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
21 changes: 16 additions & 5 deletions src/commands/leave.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { checkUserAlreadyJoined } from '../components/preCheck.js'
import type { InteractionReplyOptions } from 'discord.js'
import { LeaveCause } from '../connectionCtx.js'
import { newGuildTextBasedChannelId, newVoiceBasedChannelId } from '../id.js'
import { getErrorReply } from '../utils.js'
import { deferredReplyOrEdit, getErrorReply } from '../utils.js'
import {
HandleInteractionError,
HandleInteractionErrorType,
} from '../errors/index.js'

export class LeaveCommand extends Command {
public constructor(
Expand Down Expand Up @@ -48,11 +52,18 @@ export class LeaveCommand extends Command {
checkUserAlreadyJoined(voiceChannel)

const ctx = guildCtxManager.get(interaction.member.guild)
const textChannelId = ctx.connectionManager.channelMap.get(
const readChannelId = ctx.connectionManager.channelMap.get(
newVoiceBasedChannelId(voiceChannel),
)
if (readChannelId === undefined)
throw new HandleInteractionError(
HandleInteractionErrorType.userNotWithBot,
)

await interaction.deferReply()

const cause =
newGuildTextBasedChannelId(interaction.channel) === textChannelId
newGuildTextBasedChannelId(interaction.channel) === readChannelId
? undefined
: LeaveCause.command
const workerId = await ctx.leave({
Expand All @@ -67,7 +78,7 @@ export class LeaveCommand extends Command {
title: 'ボイスチャンネルから退出しました。',
description: [
`担当BOT: <@${workerId}>`,
`テキストチャンネル: <#${textChannelId}>`,
`テキストチャンネル: <#${readChannelId}>`,
`ボイスチャンネル: ${voiceChannel}`,
'またのご利用をお待ちしております。',
].join('\n'),
Expand All @@ -78,7 +89,7 @@ export class LeaveCommand extends Command {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
7 changes: 5 additions & 2 deletions src/commands/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
} from '../errors/index.js'
import { checkUserAlreadyJoined } from '../components/preCheck.js'
import { newVoiceBasedChannelId } from '../id.js'
import { getErrorReply } from '../utils.js'
import { deferredReplyOrEdit, getErrorReply } from '../utils.js'

export class ReadCommand extends Command {
public constructor(
Expand Down Expand Up @@ -118,6 +118,7 @@ export class ReadCommand extends Command {

try {
checkUserAlreadyJoined(voiceChannel)

const text = interaction.options.getString('text', true)

const connectionCtx = guildCtxManager
Expand All @@ -130,6 +131,8 @@ export class ReadCommand extends Command {
HandleInteractionErrorType.userNotWithBot,
)

await interaction.deferReply()

const convertedMessage = convertContent(
text,
[],
Expand All @@ -152,7 +155,7 @@ export class ReadCommand extends Command {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
7 changes: 5 additions & 2 deletions src/commands/rejoin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import { checkCanJoin, checkUserAlreadyJoined } from '../components/preCheck.js'
import { LeaveCause } from '../connectionCtx.js'
import { newGuildTextBasedChannelId, newVoiceBasedChannelId } from '../id.js'
import { getErrorReply } from '../utils.js'
import { deferredReplyOrEdit, getErrorReply } from '../utils.js'

export class RejoinCommand extends Command {
public constructor(
Expand Down Expand Up @@ -75,6 +75,9 @@ export class RejoinCommand extends Command {
existingJoinConfig.guildId,
existingJoinConfig.channelId ?? '',
)

await interaction.deferReply()

const cause =
interaction.channel.id ===
guildCtx.connectionManager.channelMap.get(voiceChannelId)
Expand Down Expand Up @@ -104,7 +107,7 @@ export class RejoinCommand extends Command {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
5 changes: 3 additions & 2 deletions src/commands/reset.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Command, type ChatInputCommand } from '@sapphire/framework'
import { guildCtxManager } from '../index.js'
import { PermissionFlagsBits, type InteractionReplyOptions } from 'discord.js'
import { getErrorReply } from '../utils.js'
import { deferredReplyOrEdit, getErrorReply } from '../utils.js'

export class ResetCommand extends Command {
public constructor(
Expand Down Expand Up @@ -30,6 +30,7 @@ export class ResetCommand extends Command {
interaction: ChatInputCommand.Interaction,
) {
if (!interaction.inCachedGuild()) return
await interaction.deferReply()

let interactionReplyOptions: InteractionReplyOptions = {
embeds: [
Expand All @@ -55,7 +56,7 @@ export class ResetCommand extends Command {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
9 changes: 7 additions & 2 deletions src/commands/userSettings.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Subcommand } from '@sapphire/plugin-subcommands'
import {
deferredReplyOrEdit,
getErrorReply,
userSettingToString,
userSettingToDiff,
Expand Down Expand Up @@ -124,6 +125,8 @@ export class UserSettingsCommand extends Subcommand {
interaction: Subcommand.ChatInputCommandInteraction,
) {
if (!interaction.inCachedGuild()) return
await interaction.deferReply({ ephemeral: true })

const user = interaction.options.getUser('user')

let interactionReplyOptions: InteractionReplyOptions = {
Expand Down Expand Up @@ -160,14 +163,16 @@ export class UserSettingsCommand extends Subcommand {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}

public async chatInputVoice(
interaction: Subcommand.ChatInputCommandInteraction,
) {
if (!interaction.inCachedGuild()) return
await interaction.deferReply({ ephemeral: true })

const allowedVoiceList = [
'show',
'haruka',
Expand Down Expand Up @@ -280,7 +285,7 @@ export class UserSettingsCommand extends Subcommand {
interactionReplyOptions = getErrorReply(error)
console.error(error)
} finally {
void interaction.reply(interactionReplyOptions)
void deferredReplyOrEdit(interaction, interactionReplyOptions)
}
}
}
17 changes: 11 additions & 6 deletions src/connectionCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ export class ConnectionCtxManager extends Map<
return this.get(this.channelMap.get(voiceChannelId))
}

checkAlreadyUsedChannel(readChannelId: GuildTextBasedChannelId) {
const existingJoinConfig = this.get(readChannelId)?.connection.joinConfig
if (existingJoinConfig !== undefined)
throw new AlreadyUsedChannelError(
existingJoinConfig.guildId,
existingJoinConfig.channelId ?? '',
)
}

connectionJoin({
voiceChannelId,
guildId,
Expand All @@ -190,12 +199,7 @@ export class ConnectionCtxManager extends Map<
skipUser?: Set<UserId>
}) {
if (this.channelMap.has(voiceChannelId)) throw new AlreadyJoinedError()
const existingJoinConfig = this.get(readChannelId)?.connection.joinConfig
if (existingJoinConfig !== undefined)
throw new AlreadyUsedChannelError(
existingJoinConfig.guildId,
existingJoinConfig.channelId ?? '',
)
this.checkAlreadyUsedChannel(readChannelId)
this.channelMap.set(voiceChannelId, readChannelId)
const connection = joinVoiceChannel({
channelId: voiceChannelId,
Expand All @@ -219,6 +223,7 @@ export class ConnectionCtxManager extends Map<
this.set(readChannelId, connectionContext)
return connectionContext
}

async connectionLeave({
voiceChannelId,
cause = undefined,
Expand Down
8 changes: 6 additions & 2 deletions src/guildCtx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ export class GuildContext {
this.connectionManager = new ConnectionCtxManager()
}

checkAlreadyJoined(voiceChannelId: VoiceBasedChannelId) {
if (this.connectionManager.channelMap.has(voiceChannelId))
throw new AlreadyJoinedError()
}

async join({
voiceChannelId,
readChannelId,
Expand All @@ -42,8 +47,7 @@ export class GuildContext {
readChannelId: GuildTextBasedChannelId
skipUser?: Set<UserId>
}) {
if (this.connectionManager.channelMap.has(voiceChannelId))
throw new AlreadyJoinedError()
this.checkAlreadyJoined(voiceChannelId)

const vcArray = (await this.guild.channels.fetch())
.map((v) => {
Expand Down
14 changes: 13 additions & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import debug from 'debug'
const debug__Queue = debug('utils.js:Queue')
import { EventEmitter } from 'events'
import { SpeakerList, type userSetting } from '@prisma/client'
import type { InteractionReplyOptions } from 'discord.js'
import type {
ChatInputCommandInteraction,
InteractionReplyOptions,
} from 'discord.js'
import { PowError } from './errors/PowError.js'

export function getProperty<T, K extends keyof T>(property: K) {
Expand Down Expand Up @@ -118,3 +121,12 @@ export const getErrorReply = (error: unknown): InteractionReplyOptions => {
throw error
}
}

export const deferredReplyOrEdit = (
interaction: ChatInputCommandInteraction,
replyOptions: InteractionReplyOptions,
) => {
return interaction.deferred
? interaction.editReply(replyOptions)
: interaction.reply(replyOptions)
}

0 comments on commit bf28075

Please sign in to comment.