Discord.js v13 邀请跟踪器

Kas*_*spr 1 discord.js

终于抽出时间将我的机器人从 v12 更新到 v13,但遇到了一个我似乎无法解决的问题。我有一个邀请跟踪器,可以在 v12 上完美运行,但在 v13 上无法运行。我已经包含了下面的代码,注释行是 v12 代码,我知道需要针对 v13 进行更改。

// const Discord = require('discord.js')
const {
    Client,
    Intents
} = require('discord.js')

// const client = new Discord.Client({
//     partials: ['MESSAGE', 'CHANNEL', 'REACTION']
// })
const client = new Client({
    intents: [Intents.FLAGS.GUILDS, Intents.FLAGS.GUILD_MEMBERS, Intents.FLAGS.GUILD_BANS, Intents.FLAGS.GUILD_EMOJIS_AND_STICKERS, Intents.FLAGS.GUILD_INTEGRATIONS, Intents.FLAGS.GUILD_WEBHOOKS, Intents.FLAGS.GUILD_INVITES, Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_PRESENCES, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILD_MESSAGE_REACTIONS, Intents.FLAGS.GUILD_MESSAGE_TYPING, Intents.FLAGS.DIRECT_MESSAGES, Intents.FLAGS.DIRECT_MESSAGE_REACTIONS, Intents.FLAGS.DIRECT_MESSAGE_TYPING],
    partials: ['MESSAGE', 'CHANNEL', 'REACTION']
})
const guildInvites = new Map()

// client.on('inviteCreate', async invite => guildInvites.set(invite.guild.id, await invite.guild.fetchInvites()))
client.on('inviteCreate', async invite => guildInvites.set(invite.guild.id, await invite.guild.invites.fetch()))

client.once('ready', () => {
    client.guilds.cache.forEach(guild => {
        //guild.fetchInvites()
        guild.invites.fetch()
            .then(invites => guildInvites.set(guild.id, invites))
            .catch(err => {
                console.log(err)
                console.log(guildInvites)
                client.channels.cache.get('channelId').send({
                    content: `${err}`
                })
            })
    })
})

client.on('guildMemberAdd', async member => {
    const cachedInvites = guildInvites.get(member.guild.id)
    // const newInvites = await member.guild.fetchInvites()
    const newInvites = await member.guild.invites.fetch()
    guildInvites.set(member.guild.id, newInvites)
    try {
        const usedInvite = newInvites.find(inv => cachedInvites.get(inv.code).uses < inv.uses)
        console.log(cachedInvites)
        console.log(newInvites)
        console.log(usedInvite)
        console.log(`The code ${usedInvite.code} was just used by ${member.user.username}.`)
    } catch (err) {
        console.log(err)
        client.channels.cache.get('channelId').send({
            content: `${err}`
        })
    }
})
Run Code Online (Sandbox Code Playgroud)

这是控制台日志结果:

console.log(cachedInvites) => 返回如下

Collection(2) [Map] {
    '11111111' => Invite {
      guild: Guild {
        id: 'xxxx',
        name: "Testing Server",
        icon: null,
        features: [Array],
        commands: [GuildApplicationCommandManager],
        members: [GuildMemberManager],
        channels: [GuildChannelManager],
        bans: [GuildBanManager],
        roles: [RoleManager],
        presences: PresenceManager {},
        voiceStates: [VoiceStateManager],
        stageInstances: [StageInstanceManager],
        invites: [GuildInviteManager],
        deleted: false,
        available: true,
        shardId: 0,
        splash: null,
        banner: null,
        description: null,
        verificationLevel: 'LOW',
        vanityURLCode: null,
        nsfwLevel: 'DEFAULT',
        discoverySplash: null,
        memberCount: 3,
        large: false,
        applicationId: null,
        afkTimeout: 900,
        afkChannelId: 'xxxx',
        systemChannelId: 'xxxx',
        premiumTier: 'NONE',
        premiumSubscriptionCount: 0,
        explicitContentFilter: 'ALL_MEMBERS',
        mfaLevel: 'NONE',
        joinedTimestamp: 1633812225872,
        defaultMessageNotifications: 'ONLY_MENTIONS',
        systemChannelFlags: [SystemChannelFlags],
        maximumMembers: 250000,
        maximumPresences: null,
        approximateMemberCount: null,
        approximatePresenceCount: null,
        vanityURLUses: null,
        rulesChannelId: 'xxxx',
        publicUpdatesChannelId: 'xxxx',
        preferredLocale: 'en-US',
        ownerId: 'xxxx',
        emojis: [GuildEmojiManager],
        stickers: [GuildStickerManager]
      },
      code: '11111111',
      presenceCount: null,
      memberCount: null,
      temporary: false,
      maxAge: 604800,
      uses: 40,
      maxUses: 0,
      inviter: User {
        id: 'xxxx',
        bot: false,
        system: false,
        flags: [UserFlags],
        username: 'xxxx',
        discriminator: 'xxxx',
        avatar: 'xxxx',
        banner: undefined,
        accentColor: undefined
      },
      targetUser: null,
      targetApplication: null,
      targetType: null,
      channel: TextChannel {
        type: 'GUILD_TEXT',
        deleted: false,
        guild: [Guild],
        guildId: 'xxxx',
        parentId: 'xxxx',
        permissionOverwrites: [PermissionOverwriteManager],
        messages: [MessageManager],
        threads: [ThreadManager],
        nsfw: false,
        id: 'xxxx',
        name: 'welcome',
        rawPosition: 3,
        topic: null,
        lastMessageId: 'xxxx',
        rateLimitPerUser: 0
      },
      createdTimestamp: 1633922331801,
      _expiresTimestamp: null,
      stageInstance: null
    },
    '22222222' => Invite {
      guild: Guild {
        id: 'xxxx',
        name: "Testing Server",
        icon: null,
        features: [Array],
        commands: [GuildApplicationCommandManager],
        members: [GuildMemberManager],
        channels: [GuildChannelManager],
        bans: [GuildBanManager],
        roles: [RoleManager],
        presences: PresenceManager {},
        voiceStates: [VoiceStateManager],
        stageInstances: [StageInstanceManager],
        invites: [GuildInviteManager],
        deleted: false,
        available: true,
        shardId: 0,
        splash: null,
        banner: null,
        description: null,
        verificationLevel: 'LOW',
        vanityURLCode: null,
        nsfwLevel: 'DEFAULT',
        discoverySplash: null,
        memberCount: 3,
        large: false,
        applicationId: null,
        afkTimeout: 900,
        afkChannelId: 'xxxx',
        systemChannelId: 'xxxx',
        premiumTier: 'NONE',
        premiumSubscriptionCount: 0,
        explicitContentFilter: 'ALL_MEMBERS',
        mfaLevel: 'NONE',
        joinedTimestamp: 1633812225872,
        defaultMessageNotifications: 'ONLY_MENTIONS',
        systemChannelFlags: [SystemChannelFlags],
        maximumMembers: 250000,
        maximumPresences: null,
        approximateMemberCount: null,
        approximatePresenceCount: null,
        vanityURLUses: null,
        rulesChannelId: 'xxxx',
        publicUpdatesChannelId: 'xxxx',
        preferredLocale: 'en-US',
        ownerId: 'xxxx',
        emojis: [GuildEmojiManager],
        stickers: [GuildStickerManager]
      },
      code: '22222222',
      presenceCount: null,
      memberCount: null,
      temporary: false,
      maxAge: 604800,
      uses: 21,
      maxUses: 0,
      inviter: User {
        id: 'xxxx',
        bot: false,
        system: false,
        flags: [UserFlags],
        username: 'xxxx',
        discriminator: 'xxxx',
        avatar: 'xxx',
        banner: undefined,
        accentColor: undefined
      },
      targetUser: null,
      targetApplication: null,
      targetType: null,
      channel: TextChannel {
        type: 'GUILD_TEXT',
        deleted: false,
        guild: [Guild],
        guildId: 'xxxxx',
        parentId: 'xxxx',
        permissionOverwrites: [PermissionOverwriteManager],
        messages: [MessageManager],
        threads: [ThreadManager],
        nsfw: false,
        id: 'xxxx',
        name: 'guest-chat',
        rawPosition: 48,
        topic: 'Guest Invitation Link',
        lastMessageId: null,
        rateLimitPerUser: 0
      },
      createdTimestamp: 1633969178889,
      _expiresTimestamp: null,
      stageInstance: null
    }
  }
Run Code Online (Sandbox Code Playgroud)

console.log(newInvites) => 返回如下

Collection(2) [Map] {
    '11111111' => Invite {
      guild: Guild {
        id: 'xxxx',
        name: "Testing Server",
        icon: null,
        features: [Array],
        commands: [GuildApplicationCommandManager],
        members: [GuildMemberManager],
        channels: [GuildChannelManager],
        bans: [GuildBanManager],
        roles: [RoleManager],
        presences: PresenceManager {},
        voiceStates: [VoiceStateManager],
        stageInstances: [StageInstanceManager],
        invites: [GuildInviteManager],
        deleted: false,
        available: true,
        shardId: 0,
        splash: null,
        banner: null,
        description: null,
        verificationLevel: 'LOW',
        vanityURLCode: null,
        nsfwLevel: 'DEFAULT',
        discoverySplash: null,
        memberCount: 3,
        large: false,
        applicationId: null,
        afkTimeout: 900,
        afkChannelId: 'xxxx',
        systemChannelId: 'xxxx',
        premiumTier: 'NONE',
        premiumSubscriptionCount: 0,
        explicitContentFilter: 'ALL_MEMBERS',
        mfaLevel: 'NONE',
        joinedTimestamp: 1633812225872,
        defaultMessageNotifications: 'ONLY_MENTIONS',
        systemChannelFlags: [SystemChannelFlags],
        maximumMembers: 250000,
        maximumPresences: null,
        approximateMemberCount: null,
        approximatePresenceCount: null,
        vanityURLUses: null,
        rulesChannelId: 'xxxx',
        publicUpdatesChannelId: 'xxxx',
        preferredLocale: 'en-US',
        ownerId: 'xxxx',
        emojis: [GuildEmojiManager],
        stickers: [GuildStickerManager]
      },
      code: '11111111',
      presenceCount: null,
      memberCount: null,
      temporary: false,
      maxAge: 604800,
      uses: 40,
      maxUses: 0,
      inviter: User {
        id: 'xxxx',
        bot: false,
        system: false,
        flags: [UserFlags],
        username: 'xxxx',
        discriminator: 'xxxx',
        avatar: 'xxxx',
        banner: undefined,
        accentColor: undefined
      },
      targetUser: null,
      targetApplication: null,
      targetType: null,
      channel: TextChannel {
        type: 'GUILD_TEXT',
        deleted: false,
        guild: [Guild],
        guildId: 'xxxx',
        parentId: 'xxxx',
        permissionOverwrites: [PermissionOverwriteManager],
        messages: [MessageManager],
        threads: [ThreadManager],
        nsfw: false,
        id: 'xxxx',
        name: 'welcome',
        rawPosition: 3,
        topic: null,
        lastMessageId: 'xxxx',
        rateLimitPerUser: 0
      },
      createdTimestamp: 1633922331801,
      _expiresTimestamp: null,
      stageInstance: null
    },
    '22222222' => Invite {
      guild: Guild {
        id: '878285237082271744',
        name: "Testing Server",
        icon: null,
        features: [Array],
        commands: [GuildApplicationCommandManager],
        members: [GuildMemberManager],
        channels: [GuildChannelManager],
        bans: [GuildBanManager],
        roles: [RoleManager],
        presences: PresenceManager {},
        voiceStates: [VoiceStateManager],
        stageInstances: [StageInstanceManager],
        invites: [GuildInviteManager],
        deleted: false,
        available: true,
        shardId: 0,
        splash: null,
        banner: null,
        description: null,
        verificationLevel: 'LOW',
        vanityURLCode: null,
        nsfwLevel: 'DEFAULT',
        discoverySplash: null,
        memberCount: 3,
        large: false,
        applicationId: null,
        afkTimeout: 900,
        afkChannelId: 'xxxx',
        systemChannelId: 'xxxx',
        premiumTier: 'NONE',
        premiumSubscriptionCount: 0,
        explicitContentFilter: 'ALL_MEMBERS',
        mfaLevel: 'NONE',
        joinedTimestamp: 1633812225872,
        defaultMessageNotifications: 'ONLY_MENTIONS',
        systemChannelFlags: [SystemChannelFlags],
        maximumMembers: 250000,
        maximumPresences: null,
        approximateMemberCount: null,
        approximatePresenceCount: null,
        vanityURLUses: null,
        rulesChannelId: 'xxxx',
        publicUpdatesChannelId: 'xxxx',
        preferredLocale: 'en-US',
        ownerId: 'xxxx',
        emojis: [GuildEmojiManager],
        stickers: [GuildStickerManager]
      },
      code: '22222222',
      presenceCount: null,
      memberCount: null,
      temporary: false,
      maxAge: 604800,
      uses: 22,
      maxUses: 0,
      inviter: User {
        id: 'xxxx',
        bot: false,
        system: false,
        flags: [UserFlags],
        username: 'xxxx',
        discriminator: 'xxxx',
        avatar: 'xxxx',
        banner: undefined,
        accentColor: undefined
      },
      targetUser: null,
      targetApplication: null,
      targetType: null,
      channel: TextChannel {
        type: 'GUILD_TEXT',
        deleted: false,
        guild: [Guild],
        guildId: 'xxxx',
        parentId: 'xxxx',
        permissionOverwrites: [PermissionOverwriteManager],
        messages: [MessageManager],
        threads: [ThreadManager],
        nsfw: false,
        id: 'xxxx',
        name: 'guest-chat',
        rawPosition: 48,
        topic: 'Guest Invitation Link',
        lastMessageId: null,
        rateLimitPerUser: 0
      },
      createdTimestamp: 1633969178889,
      _expiresTimestamp: null,
      stageInstance: null
    }
  }
Run Code Online (Sandbox Code Playgroud)

console.log(usedInvite) => 返回未定义

我收到的错误消息显然是:

TypeError: Cannot read property 'code' of undefined

Can*_*ide 5

您遇到的问题很可能源于您将整个InvitesCollection 对象保存在guildInvitesMap 中。请记住,在 Javascript 中,当您将单个对象保存在多个不同位置时,每个位置仍然指向同一个对象。基本上,当您保存invites对象或invite.guild.invites.fetch()保存到guildInvites地图时,它会引用完全相同的对象newInvites。换句话说,cachedInvitesnewInvites都指的是同一个对象;因此,它们将包含完全相同的值。每当更新 中的邀请时newInvites,它也会自动更新 中的邀请cachedInvites。由于 v13 中 Discord.js 的邀请管理代码发生了一些变化,这种情况可能现在才发生。

然而,我还没有查看 new 的 discord.js 源代码InviteManager,因此这里可能存在其他一些类似的问题。然而,这个答案中的解决方案无论如何都是有效的。

由于缓存和新的邀请列表相同,因此缓存的邀请和已使用的邀请之间的使用最终是相等的(导致cachedInvites.get(inv.code).uses < inv.uses不满足条件)。因此,newInvites.find()无法找到您正在寻找的邀请,usedInvite最终是undefined

有多种方法可以解决此问题。我的偏好是不将整个invites集合保存到您的邀请缓存中。由于您只需要知道缓存的邀请代码和旧的使用次数,因此您只需保存该信息。Invite当您只需要每个缓存邀请中的两小段信息时,无需将整个对象保存到缓存中。

这是我建议的新结构cachedInvites。正如您所看到的,它比以前更简单、更高效。它还解决了上述可能的 JS 问题,即单个对象被保存到多个位置,而不是直接将对象保存invites在缓存中。

Collection(2) [Map] {
    '11111111' => 40,
    '22222222' => 21
}
Run Code Online (Sandbox Code Playgroud)

这是我的完整解决方案,使用这种新的、更简单的结构来缓存邀请。我已经测试过了,它有效:

client.on('inviteCreate', async invite => {
    const invites = await invite.guild.invites.fetch();

    const codeUses = new Map();
    invites.each(inv => codeUses.set(inv.code, inv.uses));

    guildInvites.set(invite.guild.id, codeUses);
})

client.once('ready', () => {
    client.guilds.cache.forEach(guild => {
        guild.invites.fetch()
            .then(invites => {
                console.log("INVITES CACHED");
                const codeUses = new Map();
                invites.each(inv => codeUses.set(inv.code, inv.uses));

                guildInvites.set(guild.id, codeUses);
            })
            .catch(err => {
                console.log("OnReady Error:", err)
            })
    })
})

client.on('guildMemberAdd', async member => {
    const cachedInvites = guildInvites.get(member.guild.id)
    const newInvites = await member.guild.invites.fetch();
    try {
        const usedInvite = newInvites.find(inv => cachedInvites.get(inv.code) < inv.uses);
        console.log("Cached", [...cachedInvites.keys()])
        console.log("New", [...newInvites.values()].map(inv => inv.code))
        console.log("Used", usedInvite)
        console.log(`The code ${usedInvite.code} was just used by ${member.user.username}.`)
    } catch (err) {
        console.log("OnGuildMemberAdd Error:", err)
    }

    newInvites.each(inv => cachedInvites.set(inv.code, inv.uses));
    guildInvites.set(member.guild.id, cachedInvites);
});
Run Code Online (Sandbox Code Playgroud)