TypeError: Cannot read properties of undefined (reading 'extend')

Erreur "Cannot read properties of undefined (reading 'extend')" sur discord.js

a marqué ce sujet comme résolu.

Je fais un Discord Bot avec la librairie discord.js et discord.js-commando. Mon code fonctionne mais j’ai cette erreur : TypeError: Cannot read properties of undefined (lecture 'extend’)

Voici mon code dans "node_modules\discord.js-commando\src\extensions\message.js"

const { Structures, escapeMarkdown, splitMessage, resolveString } = require('discord.js');
const { oneLine } = require('common-tags');
const Command = require('../commands/base');
const FriendlyError = require('../errors/friendly');
const CommandFormatError = require('../errors/command-format');

module.exports = Structures.extend('Message', Message => {
    /**
     * An extension of the base Discord.js Message class to add command-related functionality.
     * @extends Message
     */
    class CommandoMessage extends Message {
        constructor(...args) {
            super(...args);

            /**
             * Whether the message contains a command (even an unknown one)
             * @type {boolean}
             */
            this.isCommand = false;

            /**
             * Command that the message triggers, if any
             * @type {?Command}
             */
            this.command = null;

            /**
             * Argument string for the command
             * @type {?string}
             */
            this.argString = null;

            /**
             * Pattern matches (if from a pattern trigger)
             * @type {?string[]}
             */
            this.patternMatches = null;

            /**
             * Response messages sent, mapped by channel ID (set by the dispatcher after running the command)
             * @type {?Object}
             */
            this.responses = null;

            /**
             * Index of the current response that will be edited, mapped by channel ID
             * @type {?Object}
             */
            this.responsePositions = null;
        }

        /**
         * Initialises the message for a command
         * @param {Command} [command] - Command the message triggers
         * @param {string} [argString] - Argument string for the command
         * @param {?Array<string>} [patternMatches] - Command pattern matches (if from a pattern trigger)
         * @return {Message} This message
         * @private
         */
        initCommand(command, argString, patternMatches) {
            this.isCommand = true;
            this.command = command;
            this.argString = argString;
            this.patternMatches = patternMatches;
            return this;
        }

        /**
         * Creates a usage string for the message's command
         * @param {string} [argString] - A string of arguments for the command
         * @param {string} [prefix=this.guild.commandPrefix || this.client.commandPrefix] - Prefix to use for the
         * prefixed command format
         * @param {User} [user=this.client.user] - User to use for the mention command format
         * @return {string}
         */
        usage(argString, prefix, user = this.client.user) {
            if(typeof prefix === 'undefined') {
                if(this.guild) prefix = this.guild.commandPrefix;
                else prefix = this.client.commandPrefix;
            }
            return this.command.usage(argString, prefix, user);
        }

        /**
         * Creates a usage string for any command
         * @param {string} [command] - A command + arg string
         * @param {string} [prefix=this.guild.commandPrefix || this.client.commandPrefix] - Prefix to use for the
         * prefixed command format
         * @param {User} [user=this.client.user] - User to use for the mention command format
         * @return {string}
         */
        anyUsage(command, prefix, user = this.client.user) {
            if(typeof prefix === 'undefined') {
                if(this.guild) prefix = this.guild.commandPrefix;
                else prefix = this.client.commandPrefix;
            }
            return Command.usage(command, prefix, user);
        }

        /**
         * Parses the argString into usable arguments, based on the argsType and argsCount of the command
         * @return {string|string[]}
         * @see {@link Command#run}
         */
        parseArgs() {
            switch(this.command.argsType) {
                case 'single':
                    return this.argString.trim().replace(
                        this.command.argsSingleQuotes ? /^("|')([^]*)\1$/g : /^(")([^]*)"$/g, '$2'
                    );
                case 'multiple':
                    return this.constructor.parseArgs(this.argString, this.command.argsCount, this.command.argsSingleQuotes);
                default:
                    throw new RangeError(`Unknown argsType "${this.argsType}".`);
            }
        }

        /**
         * Runs the command
         * @return {Promise<?Message|?Array<Message>>}
         */
        async run() { // eslint-disable-line complexity
            // Obtain the member if we don't have it
            if(this.channel.type === 'text' && !this.guild.members.cache.has(this.author.id) && !this.webhookID) {
                this.member = await this.guild.members.fetch(this.author);
            }

            // Obtain the member for the ClientUser if it doesn't already exist
            if(this.channel.type === 'text' && !this.guild.members.cache.has(this.client.user.id)) {
                await this.guild.members.fetch(this.client.user.id);
            }

            // Make sure the command is usable in this context
            if(this.command.guildOnly && !this.guild) {
                /**
                 * Emitted when a command is prevented from running
                 * @event CommandoClient#commandBlock
                 * @param {CommandoMessage} message - Command message that the command is running from
                 * @param {string} reason - Reason that the command was blocked
                 * (built-in reasons are `guildOnly`, `nsfw`, `permission`, `throttling`, and `clientPermissions`)
                 * @param {Object} [data] - Additional data associated with the block. Built-in reason data properties:
                 * - guildOnly: none
                 * - nsfw: none
                 * - permission: `response` ({@link string}) to send
                 * - throttling: `throttle` ({@link Object}), `remaining` ({@link number}) time in seconds
                 * - clientPermissions: `missing` ({@link Array}<{@link string}>) permission names
                 */
                this.client.emit('commandBlock', this, 'guildOnly');
                return this.command.onBlock(this, 'guildOnly');
            }

            // Ensure the channel is a NSFW one if required
            if(this.command.nsfw && !this.channel.nsfw) {
                this.client.emit('commandBlock', this, 'nsfw');
                return this.command.onBlock(this, 'nsfw');
            }

            // Ensure the user has permission to use the command
            const hasPermission = this.command.hasPermission(this);
            if(!hasPermission || typeof hasPermission === 'string') {
                const data = { response: typeof hasPermission === 'string' ? hasPermission : undefined };
                this.client.emit('commandBlock', this, 'permission', data);
                return this.command.onBlock(this, 'permission', data);
            }

            // Ensure the client user has the required permissions
            if(this.channel.type === 'text' && this.command.clientPermissions) {
                const missing = this.channel.permissionsFor(this.client.user).missing(this.command.clientPermissions);
                if(missing.length > 0) {
                    const data = { missing };
                    this.client.emit('commandBlock', this, 'clientPermissions', data);
                    return this.command.onBlock(this, 'clientPermissions', data);
                }
            }

            // Throttle the command
            const throttle = this.command.throttle(this.author.id);
            if(throttle && throttle.usages + 1 > this.command.throttling.usages) {
                const remaining = (throttle.start + (this.command.throttling.duration * 1000) - Date.now()) / 1000;
                const data = { throttle, remaining };
                this.client.emit('commandBlock', this, 'throttling', data);
                return this.command.onBlock(this, 'throttling', data);
            }

            // Figure out the command arguments
            let args = this.patternMatches;
            let collResult = null;
            if(!args && this.command.argsCollector) {
                const collArgs = this.command.argsCollector.args;
                const count = collArgs[collArgs.length - 1].infinite ? Infinity : collArgs.length;
                const provided = this.constructor.parseArgs(this.argString.trim(), count, this.command.argsSingleQuotes);

                collResult = await this.command.argsCollector.obtain(this, provided);
                if(collResult.cancelled) {
                    if(collResult.prompts.length === 0 || collResult.cancelled === 'promptLimit') {
                        const err = new CommandFormatError(this);
                        return this.reply(err.message);
                    }
                    /**
                     * Emitted when a command is cancelled (either by typing 'cancel' or not responding in time)
                     * @event CommandoClient#commandCancel
                     * @param {Command} command - Command that was cancelled
                     * @param {string} reason - Reason for the command being cancelled
                     * @param {CommandoMessage} message - Command message that the command ran from (see {@link Command#run})
                     * @param {?ArgumentCollectorResult} result - Result from obtaining the arguments from the collector
                     * (if applicable - see {@link Command#run})
                     */
                    this.client.emit('commandCancel', this.command, collResult.cancelled, this, collResult);
                    return this.reply('Cancelled command.');
                }
                args = collResult.values;
            }
            if(!args) args = this.parseArgs();
            const fromPattern = Boolean(this.patternMatches);

            // Run the command
            if(throttle) throttle.usages++;
            const typingCount = this.channel.typingCount;
            try {
                this.client.emit('debug', `Running command ${this.command.groupID}:${this.command.memberName}.`);
                const promise = this.command.run(this, args, fromPattern, collResult);
                /**
                 * Emitted when running a command
                 * @event CommandoClient#commandRun
                 * @param {Command} command - Command that is being run
                 * @param {Promise} promise - Promise for the command result
                 * @param {CommandoMessage} message - Command message that the command is running from (see {@link Command#run})
                 * @param {Object|string|string[]} args - Arguments for the command (see {@link Command#run})
                 * @param {boolean} fromPattern - Whether the args are pattern matches (see {@link Command#run})
                 * @param {?ArgumentCollectorResult} result - Result from obtaining the arguments from the collector
                 * (if applicable - see {@link Command#run})
                 */
                this.client.emit('commandRun', this.command, promise, this, args, fromPattern, collResult);
                const retVal = await promise;
                if(!(retVal instanceof Message || retVal instanceof Array || retVal === null || retVal === undefined)) {
                    throw new TypeError(oneLine`
                        Command ${this.command.name}'s run() resolved with an unknown type
                        (${retVal !== null ? retVal && retVal.constructor ? retVal.constructor.name : typeof retVal : null}).
                        Command run methods must return a Promise that resolve with a Message, Array of Messages, or null/undefined.
                    `);
                }
                return retVal;
            } catch(err) {
                /**
                 * Emitted when a command produces an error while running
                 * @event CommandoClient#commandError
                 * @param {Command} command - Command that produced an error
                 * @param {Error} err - Error that was thrown
                 * @param {CommandoMessage} message - Command message that the command is running from (see {@link Command#run})
                 * @param {Object|string|string[]} args - Arguments for the command (see {@link Command#run})
                 * @param {boolean} fromPattern - Whether the args are pattern matches (see {@link Command#run})
                 * @param {?ArgumentCollectorResult} result - Result from obtaining the arguments from the collector
                 * (if applicable - see {@link Command#run})
                 */
                this.client.emit('commandError', this.command, err, this, args, fromPattern, collResult);
                if(this.channel.typingCount > typingCount) this.channel.stopTyping();
                if(err instanceof FriendlyError) {
                    return this.reply(err.message);
                } else {
                    return this.command.onError(err, this, args, fromPattern, collResult);
                }
            }
        }

        /**
         * Responds to the command message
         * @param {Object} [options] - Options for the response
         * @return {Message|Message[]}
         * @private
         */
        respond({ type = 'reply', content, options, lang, fromEdit = false }) {
            const shouldEdit = this.responses && !fromEdit;
            if(shouldEdit) {
                if(options && options.split && typeof options.split !== 'object') options.split = {};
            }

            if(type === 'reply' && this.channel.type === 'dm') type = 'plain';
            if(type !== 'direct') {
                if(this.guild && !this.channel.permissionsFor(this.client.user).has('SEND_MESSAGES')) {
                    type = 'direct';
                }
            }

            content = resolveString(content);

            switch(type) {
                case 'plain':
                    if(!shouldEdit) return this.channel.send(content, options);
                    return this.editCurrentResponse(channelIDOrDM(this.channel), { type, content, options });
                case 'reply':
                    if(!shouldEdit) return super.reply(content, options);
                    if(options && options.split && !options.split.prepend) options.split.prepend = `${this.author}, `;
                    return this.editCurrentResponse(channelIDOrDM(this.channel), { type, content, options });
                case 'direct':
                    if(!shouldEdit) return this.author.send(content, options);
                    return this.editCurrentResponse('dm', { type, content, options });
                case 'code':
                    if(!shouldEdit) return this.channel.send(content, options);
                    if(options && options.split) {
                        if(!options.split.prepend) options.split.prepend = `\`\`\`${lang || ''}\n`;
                        if(!options.split.append) options.split.append = '\n```';
                    }
                    content = `\`\`\`${lang || ''}\n${escapeMarkdown(content, true)}\n\`\`\``;
                    return this.editCurrentResponse(channelIDOrDM(this.channel), { type, content, options });
                default:
                    throw new RangeError(`Unknown response type "${type}".`);
            }
        }

        /**
         * Edits a response to the command message
         * @param {Message|Message[]} response - The response message(s) to edit
         * @param {Object} [options] - Options for the response
         * @return {Promise<Message|Message[]>}
         * @private
         */
        editResponse(response, { type, content, options }) {
            if(!response) return this.respond({ type, content, options, fromEdit: true });
            if(options && options.split) content = splitMessage(content, options.split);

            let prepend = '';
            if(type === 'reply') prepend = `${this.author}, `;

            if(content instanceof Array) {
                const promises = [];
                if(response instanceof Array) {
                    for(let i = 0; i < content.length; i++) {
                        if(response.length > i) promises.push(response[i].edit(`${prepend}${content[i]}`, options));
                        else promises.push(response[0].channel.send(`${prepend}${content[i]}`));
                    }
                } else {
                    promises.push(response.edit(`${prepend}${content[0]}`, options));
                    for(let i = 1; i < content.length; i++) {
                        promises.push(response.channel.send(`${prepend}${content[i]}`));
                    }
                }
                return Promise.all(promises);
            } else {
                if(response instanceof Array) { // eslint-disable-line no-lonely-if
                    for(let i = response.length - 1; i > 0; i--) response[i].delete();
                    return response[0].edit(`${prepend}${content}`, options);
                } else {
                    return response.edit(`${prepend}${content}`, options);
                }
            }
        }

        /**
         * Edits the current response
         * @param {string} id - The ID of the channel the response is in ("DM" for direct messages)
         * @param {Object} [options] - Options for the response
         * @return {Promise<Message|Message[]>}
         * @private
         */
        editCurrentResponse(id, options) {
            if(typeof this.responses[id] === 'undefined') this.responses[id] = [];
            if(typeof this.responsePositions[id] === 'undefined') this.responsePositions[id] = -1;
            this.responsePositions[id]++;
            return this.editResponse(this.responses[id][this.responsePositions[id]], options);
        }

        /**
         * Responds with a plain message
         * @param {StringResolvable} content - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        say(content, options) {
            if(!options && typeof content === 'object' && !(content instanceof Array)) {
                options = content;
                content = '';
            }
            return this.respond({ type: 'plain', content, options });
        }

        /**
         * Responds with a reply message
         * @param {StringResolvable} content - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        reply(content, options) {
            if(!options && typeof content === 'object' && !(content instanceof Array)) {
                options = content;
                content = '';
            }
            return this.respond({ type: 'reply', content, options });
        }

        /**
         * Responds with a direct message
         * @param {StringResolvable} content - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        direct(content, options) {
            if(!options && typeof content === 'object' && !(content instanceof Array)) {
                options = content;
                content = '';
            }
            return this.respond({ type: 'direct', content, options });
        }

        /**
         * Responds with a code message
         * @param {string} lang - Language for the code block
         * @param {StringResolvable} content - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        code(lang, content, options) {
            if(!options && typeof content === 'object' && !(content instanceof Array)) {
                options = content;
                content = '';
            }
            if(typeof options !== 'object') options = {};
            options.code = lang;
            return this.respond({ type: 'code', content, options });
        }

        /**
         * Responds with an embed
         * @param {RichEmbed|Object} embed - Embed to send
         * @param {StringResolvable} [content] - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        embed(embed, content = '', options) {
            if(typeof options !== 'object') options = {};
            options.embed = embed;
            return this.respond({ type: 'plain', content, options });
        }

        /**
         * Responds with a mention + embed
         * @param {RichEmbed|Object} embed - Embed to send
         * @param {StringResolvable} [content] - Content for the message
         * @param {MessageOptions} [options] - Options for the message
         * @return {Promise<Message|Message[]>}
         */
        replyEmbed(embed, content = '', options) {
            if(typeof options !== 'object') options = {};
            options.embed = embed;
            return this.respond({ type: 'reply', content, options });
        }

        /**
         * Finalizes the command message by setting the responses and deleting any remaining prior ones
         * @param {?Array<Message|Message[]>} responses - Responses to the message
         * @private
         */
        finalize(responses) {
            if(this.responses) this.deleteRemainingResponses();
            this.responses = {};
            this.responsePositions = {};

            if(responses instanceof Array) {
                for(const response of responses) {
                    const channel = (response instanceof Array ? response[0] : response).channel;
                    const id = channelIDOrDM(channel);
                    if(!this.responses[id]) {
                        this.responses[id] = [];
                        this.responsePositions[id] = -1;
                    }
                    this.responses[id].push(response);
                }
            } else if(responses) {
                const id = channelIDOrDM(responses.channel);
                this.responses[id] = [responses];
                this.responsePositions[id] = -1;
            }
        }

        /**
         * Deletes any prior responses that haven't been updated
         * @private
         */
        deleteRemainingResponses() {
            for(const id of Object.keys(this.responses)) {
                const responses = this.responses[id];
                for(let i = this.responsePositions[id] + 1; i < responses.length; i++) {
                    const response = responses[i];
                    if(response instanceof Array) {
                        for(const resp of response) resp.delete();
                    } else {
                        response.delete();
                    }
                }
            }
        }

        /**
         * Parses an argument string into an array of arguments
         * @param {string} argString - The argument string to parse
         * @param {number} [argCount] - The number of arguments to extract from the string
         * @param {boolean} [allowSingleQuote=true] - Whether or not single quotes should be allowed to wrap arguments,
         * in addition to double quotes
         * @return {string[]} The array of arguments
         */
        static parseArgs(argString, argCount, allowSingleQuote = true) {
            const argStringModified = removeSmartQuotes(argString, allowSingleQuote);
            const re = allowSingleQuote ? /\s*(?:("|')([^]*?)\1|(\S+))\s*/g : /\s*(?:(")([^]*?)"|(\S+))\s*/g;
            const result = [];
            let match = [];
            // Large enough to get all items
            argCount = argCount || argStringModified.length;
            // Get match and push the capture group that is not null to the result
            while(--argCount && (match = re.exec(argStringModified))) result.push(match[2] || match[3]);
            // If text remains, push it to the array as-is (except for wrapping quotes, which are removed)
            if(match && re.lastIndex < argStringModified.length) {
                const re2 = allowSingleQuote ? /^("|')([^]*)\1$/g : /^(")([^]*)"$/g;
                result.push(argStringModified.substr(re.lastIndex).replace(re2, '$2'));
            }
            return result;
        }
    }

    return CommandoMessage;
});

function removeSmartQuotes(argString, allowSingleQuote = true) {
    let replacementArgString = argString;
    const singleSmartQuote = /[‘’]/g;
    const doubleSmartQuote = /[“”]/g;
    if(allowSingleQuote) replacementArgString = argString.replace(singleSmartQuote, '\'');
    return replacementArgString
    .replace(doubleSmartQuote, '"');
}

function channelIDOrDM(channel) {
    if(channel.type !== 'dm') return channel.id;
    return 'dm';
}

Voici mon package.json :

{
  "dependencies": {
    "discord.js": "^14.7.1",
    "discord.js-commando": "^0.12.3"
  }
}

Je n’ai jamais eu cette erreur auparavant. J’ai déjà désinstallé et installé les node_modules mais rien ne se passe. J’ai déjà essayé de changer la version de discord.js mais rien ne se passe aussi.

Quelqu’un pourrait m’aider ?

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte