如何使用 setTimeout 多次更新 discord.js 的交互?
How to update multiple time a discord.js's interaction using setTimeout?
我目前正在使用打字稿开发一个 discordjs v13.6 机器人,当使用 /day
交互命令时,它 post 一个带有日期的嵌入对象。
简而言之:我制作了 /day
并且我的机器人 post 嵌入了当前日期、天气等...
它工作正常,我在嵌入下添加了 3 个按钮:
- “重新加载”按钮:它只会更新嵌入(包含当前天气预报)。
- “上一个”按钮:将嵌入的交互更新到前一天。
- “下一步”按钮:将嵌入的交互更新到第二天。
我的代码对于附加到我的嵌入的 3 个按钮来说应该是这样工作的:
import { MessageActionRow, MessageButton } from 'discord.js';
// All customId property are formated like: `{method}@{date}`, ie: `reload@2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev@${date}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload@${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`next@${date}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
)
对于逻辑:
import { ButtonInteraction } from 'discord.js';
import { createEmbed } from "../utils/embed";
import { createNavigationButtons } from "../utils/buttons";
import * as year from "../../resources/year.json";
import * as moment from 'moment';
import { setTimeout as wait } from 'node:timers/promises';
// This function is called in the on('interactionCreate') event, when interaction.isButton() is true
export const button = async (interaction: ButtonInteraction): Promise<void> => {
const [action, date]: string[] = interaction.customId?.split('@');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
}
如我所愿。但是,每个人都可以使用这些按钮(我不想发送 /day
的答案是短暂的,我希望每个人都能看到回复)。因此,如果我们使用 /day 2022-03-10
2022 年 3 月 10 日的嵌入。但是如果 author 或 someone else (我不请注意)使用按钮,嵌入将更新为另一个日期(我没问题!)。但是我想在按下按钮几秒/分钟后将我的嵌入回滚到原始日期。
我尝试了一些像这样的 setTimeout
的原始方法:
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
// In this test, button's customId are formated like {method}@{date}@{date's origin} (where 'origin' is the original requested's date, optional)
const [action, date, origin]: string[] = interaction.customId?.split('@');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
// Here is my setTimeout who is supposed to recursively recall this function with a "reload" and the original date
setTimeout(async () => {
interaction.customId = `reload@${origin ?? date}`;
console.log(interaction.customId);
await button(interaction, config);
}, 5000);
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
};
当我用这个按下我的按钮时,它会正确更新,但是 5 秒(setTimeout
的值)之后它会出现错误消息:
reload@2022-03-10
/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
^
Error [INTERACTION_ALREADY_REPLIED]: The reply to this interaction has already been sent or deferred.
at ButtonInteraction.deferUpdate (/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180:46)
at button (/home/toto/tata/src/services/button.ts:12:21)
at Timeout._onTimeout (/home/toto/tata/src/services/button.ts:28:21)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7) {
[Symbol(code)]: 'INTERACTION_ALREADY_REPLIED'
}
我知道我似乎无法像这样使用相同的令牌重新更新我的交互,那么我应该如何实现我的目标呢?可能 setTimeout
不是一个合适的解决方案(但实现起来非常简单,所以我首先尝试了)。有什么想法吗?
我这样成功达到了 objective:
// All customId property are formated like: `{method}@{date}@{origin}`, ie: `reload@2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date: string, mode?: boolean) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev@${date}${!mode ? `@${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload@${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`remind@${date}`)
.setStyle('PRIMARY')
.setEmoji("946192601806155806"),
new MessageButton()
.setCustomId(`next@${date}${!mode ? `@${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
);
export const createButtons = (date, mode?: boolean) => ({
components: [ createNavigationButtons(date, mode) ]
});
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
const [action, date, origin]: string[] = interaction.customId?.split('@');
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
origin && !interaction.deferred && setTimeout(async () => {
await interaction.editReply({
embeds: [await createEmbed(year[origin], config.server)],
...createButtons(origin)
});
}, 120000);
!interaction.deferred && await interaction.deferUpdate();
await interaction.editReply({
embeds: [await createEmbed(year[newDate], config.server)],
...createButtons(newDate, true)
});
};
简而言之,当我第一次创建嵌入时,我放置了一个 @{origin}
(谁是约会对象),当我使用我的按钮导航和更新我的嵌入时,我不发送来源(仅 {action}@{date}
,而不是 {action}@{date}@origin
通过将 true
传递给我的 createButtons
方法。
我目前正在使用打字稿开发一个 discordjs v13.6 机器人,当使用 /day
交互命令时,它 post 一个带有日期的嵌入对象。
简而言之:我制作了 /day
并且我的机器人 post 嵌入了当前日期、天气等...
它工作正常,我在嵌入下添加了 3 个按钮:
- “重新加载”按钮:它只会更新嵌入(包含当前天气预报)。
- “上一个”按钮:将嵌入的交互更新到前一天。
- “下一步”按钮:将嵌入的交互更新到第二天。
我的代码对于附加到我的嵌入的 3 个按钮来说应该是这样工作的:
import { MessageActionRow, MessageButton } from 'discord.js';
// All customId property are formated like: `{method}@{date}`, ie: `reload@2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev@${date}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload@${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`next@${date}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
)
对于逻辑:
import { ButtonInteraction } from 'discord.js';
import { createEmbed } from "../utils/embed";
import { createNavigationButtons } from "../utils/buttons";
import * as year from "../../resources/year.json";
import * as moment from 'moment';
import { setTimeout as wait } from 'node:timers/promises';
// This function is called in the on('interactionCreate') event, when interaction.isButton() is true
export const button = async (interaction: ButtonInteraction): Promise<void> => {
const [action, date]: string[] = interaction.customId?.split('@');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
}
如我所愿。但是,每个人都可以使用这些按钮(我不想发送 /day
的答案是短暂的,我希望每个人都能看到回复)。因此,如果我们使用 /day 2022-03-10
2022 年 3 月 10 日的嵌入。但是如果 author 或 someone else (我不请注意)使用按钮,嵌入将更新为另一个日期(我没问题!)。但是我想在按下按钮几秒/分钟后将我的嵌入回滚到原始日期。
我尝试了一些像这样的 setTimeout
的原始方法:
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
// In this test, button's customId are formated like {method}@{date}@{date's origin} (where 'origin' is the original requested's date, optional)
const [action, date, origin]: string[] = interaction.customId?.split('@');
await interaction.deferUpdate();
await wait(1000);
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
// Here is my setTimeout who is supposed to recursively recall this function with a "reload" and the original date
setTimeout(async () => {
interaction.customId = `reload@${origin ?? date}`;
console.log(interaction.customId);
await button(interaction, config);
}, 5000);
await interaction.editReply({
embeds: [await createEmbed(year[newDate])],
components: [createNavigationButtons(newDate)]
});
};
当我用这个按下我的按钮时,它会正确更新,但是 5 秒(setTimeout
的值)之后它会出现错误消息:
reload@2022-03-10
/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180
if (this.deferred || this.replied) throw new Error('INTERACTION_ALREADY_REPLIED');
^
Error [INTERACTION_ALREADY_REPLIED]: The reply to this interaction has already been sent or deferred.
at ButtonInteraction.deferUpdate (/home/toto/tata/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:180:46)
at button (/home/toto/tata/src/services/button.ts:12:21)
at Timeout._onTimeout (/home/toto/tata/src/services/button.ts:28:21)
at listOnTimeout (node:internal/timers:559:17)
at processTimers (node:internal/timers:502:7) {
[Symbol(code)]: 'INTERACTION_ALREADY_REPLIED'
}
我知道我似乎无法像这样使用相同的令牌重新更新我的交互,那么我应该如何实现我的目标呢?可能 setTimeout
不是一个合适的解决方案(但实现起来非常简单,所以我首先尝试了)。有什么想法吗?
我这样成功达到了 objective:
// All customId property are formated like: `{method}@{date}@{origin}`, ie: `reload@2022-03-10` is the button who'll reload the embed to date 03/10/2022.
export const createNavigationButtons = (date: string, mode?: boolean) => (
new MessageActionRow().addComponents([
new MessageButton()
.setCustomId(`prev@${date}${!mode ? `@${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186554517389332"),
new MessageButton()
.setCustomId(`reload@${date}`)
.setStyle('SUCCESS')
.setEmoji("946190012154794055"),
new MessageButton()
.setCustomId(`remind@${date}`)
.setStyle('PRIMARY')
.setEmoji("946192601806155806"),
new MessageButton()
.setCustomId(`next@${date}${!mode ? `@${date}` : ''}`)
.setStyle('SECONDARY')
.setEmoji("946186699745161296")
])
);
export const createButtons = (date, mode?: boolean) => ({
components: [ createNavigationButtons(date, mode) ]
});
export const button = async (interaction: ButtonInteraction, config: any): Promise<void> => {
const [action, date, origin]: string[] = interaction.customId?.split('@');
const newDate: string = {
prev: moment(date, "YYYY-MM-DD").subtract(1, "day").format("YYYY-MM-DD"),
next: moment(date, "YYYY-MM-DD").add( 1, "day").format("YYYY-MM-DD"),
reload: date
}[action];
origin && !interaction.deferred && setTimeout(async () => {
await interaction.editReply({
embeds: [await createEmbed(year[origin], config.server)],
...createButtons(origin)
});
}, 120000);
!interaction.deferred && await interaction.deferUpdate();
await interaction.editReply({
embeds: [await createEmbed(year[newDate], config.server)],
...createButtons(newDate, true)
});
};
简而言之,当我第一次创建嵌入时,我放置了一个 @{origin}
(谁是约会对象),当我使用我的按钮导航和更新我的嵌入时,我不发送来源(仅 {action}@{date}
,而不是 {action}@{date}@origin
通过将 true
传递给我的 createButtons
方法。