<commit_before>const discord = require('discord.js');
const { stripIndents, oneLine } = require('common-tags');
const Command = require('./base');
const FriendlyError = require('../errors/friendly');
const CommandFormatError = require('../errors/command-format');
const { permissions } = require('../util');

/** A container for a message that triggers a command, that command, and methods to respond */
class CommandMessage {
	/**
	 * @param {Message} message - Message that triggers the 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)
	 */
	constructor(message, command = null, argString = null, patternMatches = null) {
		/**
		 * Client that the message was sent from
		 * @name CommandMessage#client
		 * @type {CommandoClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: message.client });

		/**
		 * Message that triggers the command
		 * @type {Message}
		 */
		this.message = message;

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

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

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

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

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

	/**
	 * Creates a usage string for the message's command
	 * @param {string} [argString] - A string of arguments for the command
	 * @param {string} [prefix=this.message.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.message.guild) prefix = this.message.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.message.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.message.guild) prefix = this.message.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 (ugly-ass if statement ahead)
		if(this.message.channel.type === 'text' && !this.message.guild.members.has(this.message.author.id) &&
			!this.message.webhookID) {
			await this.message.guild.members.fetch(this.message.author);
		}

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

		// Make sure the command is usable in this context
		if(this.command.guildOnly && !this.message.guild) {
			/**
			 * Emitted when a command is prevented from running
			 * @event CommandoClient#commandBlocked
			 * @param {CommandMessage} message - Command message that the command is running from
			 * @param {string} reason - Reason that the command was blocked
			 * (built-in reasons are `guildOnly`, `permission`, and `throttling`)
			 */
			this.client.emit('commandBlocked', this, 'guildOnly');
			return this.reply(`The \`${this.command.name}\` command must be used in a server channel.`);
		}

		if(this.command.nsfw && !this.message.channel.nsfw) {
			this.client.emit('commandBlocked', this, 'nsfw');
			return this.reply(`The \`${this.command.name}\` command can only be used in NSFW channels.`);
		}

		// Ensure the user has permission to use the command
		const hasPermission = this.command.hasPermission(this);
		if(!hasPermission || typeof hasPermission === 'string') {<commit_msg>Add nsfw reason to commandBlocked event docs<commit_after>const discord = require('discord.js');
const { stripIndents, oneLine } = require('common-tags');
const Command = require('./base');
const FriendlyError = require('../errors/friendly');
const CommandFormatError = require('../errors/command-format');
const { permissions } = require('../util');

/** A container for a message that triggers a command, that command, and methods to respond */
class CommandMessage {
	/**
	 * @param {Message} message - Message that triggers the 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)
	 */
	constructor(message, command = null, argString = null, patternMatches = null) {
		/**
		 * Client that the message was sent from
		 * @name CommandMessage#client
		 * @type {CommandoClient}
		 * @readonly
		 */
		Object.defineProperty(this, 'client', { value: message.client });

		/**
		 * Message that triggers the command
		 * @type {Message}
		 */
		this.message = message;

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

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

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

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

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

	/**
	 * Creates a usage string for the message's command
	 * @param {string} [argString] - A string of arguments for the command
	 * @param {string} [prefix=this.message.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.message.guild) prefix = this.message.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.message.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.message.guild) prefix = this.message.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 (ugly-ass if statement ahead)
		if(this.message.channel.type === 'text' && !this.message.guild.members.has(this.message.author.id) &&
			!this.message.webhookID) {
			await this.message.guild.members.fetch(this.message.author);
		}

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

		// Make sure the command is usable in this context
		if(this.command.guildOnly && !this.message.guild) {
			/**
			 * Emitted when a command is prevented from running
			 * @event CommandoClient#commandBlocked
			 * @param {CommandMessage} 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`, and `throttling`)
			 */
			this.client.emit('commandBlocked', this, 'guildOnly');
			return this.reply(`The \`${this.command.name}\` command must be used in a server channel.`);
		}

		if(this.command.nsfw && !this.message.channel.nsfw) {
			this.client.emit('commandBlocked', this, 'nsfw');
			return this.reply(`The \`${this.command.name}\` command can only be used in NSFW channels.`);
		}

		// Ensure the user has permission to use the command
		const hasPermission = this.command.hasPermission(this);
		if(!hasPermission || typeof hasPermission === 'string') {
{'fraction_non_alphanumeric': 0.14014612392067557, 'fraction_numerical': 0.000379542651105418, 'mean_word_length': 3.651368049426302, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '777484', 'n_tokens_mistral': 3486, 'n_tokens_neox': 3094, 'n_words': 937}