Discord.js 机器人的反应角色嵌入在机器人重启后不起作用

问题描述 投票:0回答:1

如何在机器人会话之间保持机器人的反应角色帖子正常工作?

我正在使用 Discord.js 创建一个嵌入,其中包含来自机器人的一些自动自我反应,这些反应与嵌入中识别的可自我分配的角色相对应。这部分效果很好。

然后我使用 sqlite3 将反应数据存储在数据库中,以便我可以在机器人重新启动时获取数据。从理论上讲,这应该可以使反应角色的嵌入保持正常工作。如下所述,这效果不太好。

下面是从初始启动开始记录的所有内容(来自最多 3 个现有嵌入的大量记录信息供您忽略),通过新的

/roles
命令创建新嵌入,对该嵌入做出反应,关闭机器人,重新打开机器人,删除我最初对嵌入给出的反应(未记录,如上所述),然后重新添加相同的反应(已记录但无效,因为该角色从未从反应的删除中删除,如上所述) ,然后——为了更好的措施——再次删除并重新添加反应,以显示所有记录都正确(并且我可以保证它在这些操作中也能正确运行)。

PS C:\Users\snake\OneDrive\Documents\discord\R1N> node .
Connection to SQLite database has been established successfully.
...Zee-zee... R1N is now On-line!
[ready.js] Fetched all members for all guilds.
Executing (default): SELECT `id`, `messageId`, `channelId`, `embedTitle`, `embedDescription`, `embedFooter`, `embedThumbnail`, `embedImage`, `embedIcon`, `embedColor`, `maxReactions`, `fieldValue`, `createdAt`, `updatedAt` FROM `Embeds` AS `Embed`;
[ready.js] Setting up collectors for message 1253058917680808068
[ready.js] Collectors set up for message 1253058917680808068
[syncInitialReactions] Syncing initial reactions for message 1253058917680808068
[syncInitialReactions] Checking reaction 1252855872892440666 from user daggerbyte on message 1253058917680808068
Executing (default): SELECT `id`, `embedId`, `userId`, `emoji`, `roleId`, `createdAt`, `updatedAt` FROM `Reactions` AS `Reaction` WHERE `Reaction`.`embedId` = 14 AND `Reaction`.`userId` = '590630085607030784' AND `Reaction`.`emoji` = '1252855872892440666' LIMIT 1;
[syncInitialReactions] Reaction 1252855872892440666 from user daggerbyte on message 1253058917680808068 already recorded in database
[ready.js] Setting up collectors for message 1253487005900476486
[ready.js] Collectors set up for message 1253487005900476486
[syncInitialReactions] Syncing initial reactions for message 1253487005900476486
[ready.js] Setting up collectors for message 1253487126411219106
[ready.js] Collectors set up for message 1253487126411219106
[syncInitialReactions] Syncing initial reactions for message 1253487126411219106
[syncInitialReactions] Checking reaction 1252855872892440666 from user daggerbyte on message 1253487126411219106
Executing (default): SELECT `id`, `embedId`, `userId`, `emoji`, `roleId`, `createdAt`, `updatedAt` FROM `Reactions` AS `Reaction` WHERE `Reaction`.`embedId` = 16 AND `Reaction`.`userId` = '590630085607030784' AND `Reaction`.`emoji` = '1252855872892440666' LIMIT 1;
[syncInitialReactions] Database does not contain reaction 1252855872892440666 for user daggerbyte on message 1253487126411219106. Adding to database and assigning role.
Executing (default): INSERT INTO `Reactions` (`id`,`embedId`,`userId`,`emoji`,`roleId`,`createdAt`,`updatedAt`) VALUES (NULL,$1,$2,$3,$4,$5,$6);
[ready.js] Reaction collected on message 1253487126411219106 by user daggerbyte
[ready.js] Reaction removed on message 1253487126411219106 by user daggerbyte
[handleRoleAssignment] Role @⚜️ NOBLESSE OBLIGE removed from user daggerbyte for reaction <:noblesse:12528558728924406666>.
[ready.js] Reaction collected on message 1253487126411219106 by user daggerbyte
[handleRoleAssignment] Role @⚜️ NOBLESSE OBLIGE added to user daggerbyte for reaction <:noblesse:1252855872892440666>.

这是

roles.js
代码:

// ./commands/roles.js
const { SlashCommandBuilder, PermissionFlagsBits, EmbedBuilder } = require('discord.js');
const commandConfig = require('../config/config');
const Embed = require('../models/embed');
const Reaction = require('../models/reaction');
const { handleRoleAssignment } = require('../utils/roleManager');

const EMBED_LIMIT = 2; // Limit to 2 embeds

module.exports = {
    data: new SlashCommandBuilder()
        .setName('roles')
        .setDescription('Create an embed used for self-selected reaction roles.')
        .setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
        .addStringOption(option =>
            option.setName('reactions')
                .setDescription('Comma-delimited list of reaction emojis and role names. Ex: "🔴 Red, 🟢 Green, 🔵 Blue"')
                .setRequired(true))
        .addStringOption(option =>
            option.setName('title')
                .setDescription('Title at the top of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('description')
                .setDescription('Text for instructions near the top of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('footer')
                .setDescription('Plain text footer at the bottom of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('thumbnail')
                .setDescription('URL or @username of the image for the thumbnail in the top right corner of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('image')
                .setDescription('URL or @username of the image for the main picture below the description of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('icon')
                .setDescription('URL or @username of the image for the footer icon in the bottom left corner of the embed')
                .setRequired(false))
        .addStringOption(option =>
            option.setName('color')
                .setDescription('Hex code or named color for the embed. Ex: "#64ff00" or "Green"')
                .setRequired(false))
        .addIntegerOption(option =>
            option.setName('max')
                .setDescription('Max number of reaction roles allowed')
                .setRequired(false)),

    async execute(interaction) {
        const { client, options } = interaction;

        const reactionsInput = options.getString('reactions');
        const description = options.getString('description') || '';
        const maxReactions = options.getInteger('max');
        const thumbnailInput = options.getString('thumbnail');
        const imageInput = options.getString('image');
        const iconInput = options.getString('icon');
        const footer = options.getString('footer') || '';
        const title = options.getString('title') || '';
        const color = options.getString('color') || '#4e5058';

        const thumbnail = await getImageUrl(thumbnailInput, client);
        const image = await getImageUrl(imageInput, client);
        const icon = await getImageUrl(iconInput, client);

        const embed = new EmbedBuilder()
            .setColor(color)
            .setThumbnail(thumbnail || client.user.displayAvatarURL());

        if (title.length > 0) {
            embed.setTitle(title);
        }

        if (description.length > 0) {
            embed.setDescription(description);
        }

        if (image.length > 0) {
            embed.setImage(image);
        }

        if (footer.length > 0) {
            embed.setFooter({ text: footer, iconURL: icon || client.user.displayAvatarURL() });
        }

        const reactions = reactionsInput.split(',').map(str => str.trim());

        let fieldValue = '';

        try {
            for (const reaction of reactions) {
                const match = reaction.match(/(<a?:\w+:\d+>|[^ ]+)\s+(.+)/u);
                if (!match) {
                    throw new Error(`Invalid reaction format: ${reaction}`);
                }
                const [, emoji, roleName] = match;
                const role = interaction.guild.roles.cache.find(r => r.name.toLowerCase() === roleName.toLowerCase());
                if (!role) {
                    throw new Error(`Role not found: ${roleName}`);
                }
                fieldValue += `${emoji} <@&${role.id}>\n`;
            }

            embed.addFields({ name: '_ _', value: fieldValue.trim() + '\n_ _' });

            const channel = commandConfig.rolesChannel
                ? interaction.guild.channels.cache.get(commandConfig.rolesChannel)
                : interaction.channel;

            if (!channel) {
                throw new Error('Roles channel not found or not set.');
            }

            const message = await channel.send({ embeds: [embed] });

            for (const reaction of reactions) {
                const [emoji] = reaction.match(/(<a?:\w+:\d+>|[^ ]+)/u);
                await message.react(emoji);
            }

            await interaction.reply({ content: 'Reaction roles embed created!', ephemeral: true });

            // Manage embed limit
            await manageEmbedLimit(client);

            // Save new embed
            const savedEmbed = await Embed.create({
                messageId: message.id,
                channelId: message.channel.id,
                embedTitle: title,
                embedDescription: description,
                embedFooter: footer,
                embedThumbnail: thumbnailInput || '',
                embedImage: imageInput || '',
                embedIcon: iconInput || '',
                embedColor: color,
                maxReactions: maxReactions || 0,
                fieldValue: fieldValue
            });

            const filter = (reaction, user) => !user.bot;
            const collector = message.createReactionCollector({ filter, dispose: true });

            collector.on('collect', async (reaction, user) => {
                await handleRoleAssignment(reaction, user, 'add', client, maxReactions, message.id);
            });

            collector.on('remove', async (reaction, user) => {
                await handleRoleAssignment(reaction, user, 'remove', client, maxReactions, message.id);
            });

        } catch (error) {
            console.error('Error creating reaction roles embed:', error);
            if (!interaction.replied && !interaction.deferred) {
                await interaction.reply({ content: `There was an error: ${error.message}`, ephemeral: true });
            }
        }
    },
};

async function manageEmbedLimit(client) {
    const savedEmbeds = await Embed.findAll({ order: [['createdAt', 'ASC']] });
    while (savedEmbeds.length > EMBED_LIMIT) {
        const oldestEmbed = savedEmbeds.shift();
        const channel = await client.channels.fetch(oldestEmbed.channelId);
        if (channel) {
            const message = await channel.messages.fetch(oldestEmbed.messageId);
            if (message) {
                await message.delete();
            }
        }
        await Embed.destroy({ where: { id: oldestEmbed.id } });
    }
}

async function getImageUrl(input, client) {
    if (!input) return '';

    const customEmojiMatch = input.match(/<a?:\w+:(\d+)>/);
    if (customEmojiMatch) {
        const emojiId = customEmojiMatch[1];
        const emoji = client.emojis.cache.get(emojiId);
        if (emoji) {
            return emoji.url;
        }
    }

    const userMentionMatch = input.match(/<@!?(\d+)>/);
    if (userMentionMatch) {
        const userId = userMentionMatch[1];
        const user = await client.users.fetch(userId);
        if (user) {
            return user.displayAvatarURL({ dynamic: true });
        }
    }

    return input;
}

// my command, which functions well up until the problem detailed above...

// my interaction events handler...

// my ready event, which plays a role in this mess, I'm sure...

// my role handler, which also probably plays a role in the mess, I'm sure...

javascript node.js discord.js bots node-sqlite3
1个回答
0
投票

在 Discord.JS 收集器上,就像您使用过的那样

message.createReactionCollector
存储在执行内存中,因此当您重新启动代码时,它们将不再起作用。 如果您希望事件处理程序即使在重新启动时也能工作,那么我建议您使用

messageReactionAdd

messageReactionRemove
客户端事件。供您使用,代码可能是这样的
client.on("messageReactionAdd", async (reaction, user) => {
  await handleRoleAssignment(reaction, user, 'add', client, maxReactions, message.id);
})
client.on("messageReactionRemove", async (reaction, user) => {
  await handleRoleAssignment(reaction, user, 'remove', client, maxReactions, message.id);
})

注意:这仅适用于缓存的消息!!

© www.soinside.com 2019 - 2024. All rights reserved.