Discord.js v13 Пригласительный трекер

#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}`
        })
    }
})
 

Это результат журнала консоли:

console.log(кэшированные приглашения) => возвращает ниже

 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
    }
  }
 

console.log(новые приглашения) => возвращает ниже

 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
    }
  }
 

console.log(usedInvite) => возвращает неопределенное значение

Сообщение об ошибке, которое я получаю, очевидно,:

TypeError: Cannot read property 'code' of undefined

Комментарии:

1. В фактическом коде «Идентификатор канала» — это число, просто чтобы я не указывал на это.

2. Можете ли вы обновить свой вопрос, чтобы включить соответствующие данные (например, использование и коды приглашений-не включайте фактические коды, просто покажите, какие из них соответствуют двум коллекциям) в каждой из коллекций cachedInvites и newInvites ?

3. @Канницид, который был добавлен.

Ответ №1:

Проблема, с которой вы столкнулись, скорее всего, связана с тем фактом, что вы сохраняете весь Invites объект коллекции на своей guildInvites карте. Помните, что в Javascript, когда вы сохраняете один объект в нескольких разных местах, каждое место по-прежнему указывает на один и тот же объект. В принципе, когда вы сохраняете invites объект или invite.guild.invites.fetch() на свою guildInvites карту, он ссылается на тот же объект, newInvites что и Уилл. Другими словами, cachedInvites и newInvites то и другое относится к одному и тому же объекту; из-за этого они будут содержать одинаковые значения. Всякий раз, когда приглашение newInvites обновляется, оно также автоматически обновляет приглашение cachedInvites . Это может произойти только сейчас из-за некоторых изменений в discord.js’ код для управления приглашениями в версии 13.

Однако я не смотрел на 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
}
 

И вот мое полное решение, использующее эту новую, более простую структуру для кэшированных приглашений. Я протестировал его, и он работает:

 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);
});