src/help_command_helper.js

/**
 * Assists in creating a help command.
 * The HelpCommandHelper can be
 * accessed via {@link CommandManager#getHelpCommandHelper}.
 * For an example of how to use the HelpCommandHelper, see the [monochrome demo help command]{@link https://github.com/mistval/monochrome-demo/blob/master/commands/help.js}
 * You can use the demo help command in your bot. Just fill out the configuration section at the top.
 * @hideconstructor
 */
class HelpCommandHelper {
  constructor(commands, settings, persistence) {
    this.settings_ = settings;
    this.persistence_ = persistence;
    this.nonHiddenCommands_ = commands.filter(command => !command.hidden);
  }

  /**
   * Get all commands that do not have their hidden property set to true.
   * @returns {Command[]}
   */
  getNonHiddenCommands() {
    return this.nonHiddenCommands_;
  }

  /**
   * Do a fuzzy search for a command that matches the provided search term. Returns
   * undefined if no match is found.
   * @param {string} searchTerm - The string to find a matching command for.
   * @returns {Command|undefined}
   */
  findCommand(searchTerm, serverId) {
    const prefix = this.persistence_.getPrefixesForServer(serverId)[0];
    const commands = this.getNonHiddenCommands();

    const exactMatch =
      commands.find(command => command.aliases.indexOf(searchTerm) !== -1)
      || commands.find(command => command.aliases.indexOf(`${prefix}${searchTerm}`) !== -1);

    if (exactMatch) {
      return exactMatch;
    }

    let currentCandidateCommand;
    let currentCandidateAlias;
    let prefixedCurrentCandidateAlias = '';
    for (let newCandidateCommand of commands) {
      for (let newCandidateAlias of newCandidateCommand.aliases) {
        const prefixedNewCandidateAlias = `${prefix}${newCandidateAlias}`;
        if (prefixedNewCandidateAlias.indexOf(searchTerm) !== -1) {
          let update = false;
          if (!currentCandidateCommand) {
            update = true;
          } else {
            let currentStartsWithAlias = prefixedCurrentCandidateAlias.startsWith(searchTerm);
            let newStartsWithAlias = prefixedNewCandidateAlias.startsWith(searchTerm);
            let currentContainsAliasNotAtStart = !currentStartsWithAlias || prefixedCurrentCandidateAlias.replace(searchTerm, '').indexOf(searchTerm) !== -1;
            let newContainsAliasNotAtStart = !newStartsWithAlias || prefixedNewCandidateAlias.replace(searchTerm, '').indexOf(searchTerm) !== -1;
            let newIsShorter = prefixedNewCandidateAlias.length < prefixedCurrentCandidateAlias.length;
            if (currentStartsWithAlias && newStartsWithAlias) {
              // They both start with the alias.
              if (currentContainsAliasNotAtStart && newContainsAliasNotAtStart) {
                // They both start with the alias and contain the alias not at start. Update if new is shorter.
                update = newIsShorter;
              } else if (newContainsAliasNotAtStart) {
                // They both start with the alias, only the new one of them contains the alias not at start. Update.
                update = true;
              } else {
                // They both start with the alias, only the old one of them contains the alias not at start. Don't update.
              }
            } else if (currentStartsWithAlias || newStartsWithAlias) {
              // One of them starts with the alias, the other does not. Update if it's the old one.
              update = currentStartsWithAlias;
            } else {
              // They both contain the alias, but not at the start. Update if new is shorter.
              update = prefixedNewCandidateAlias.length < prefixedCurrentCandidateAlias.length;
            }
          }

          if (update) {
            prefixedCurrentCandidateAlias = prefixedNewCandidateAlias;
            currentCandidateCommand = newCandidateCommand;
          }
        }
      }
    }
    return currentCandidateCommand;
  }

  /**
   * Get all non-hidden commands that are enabled where the msg was sent.
   * @param {external:"Eris.Message"} msg - The Eris message that caused the help command to be invoked.
   * @returns {Command[]}
   */
  async getEnabledNonHiddenCommands(msg) {
    const promises = this.nonHiddenCommands_.map(command => {
      return this.settings_.getInternalSettingValue(
        command.getEnabledSettingUniqueId(),
        msg.channel.guild ? msg.channel.guild.id : msg.channel.id,
        msg.channel.id,
        msg.author.id,
      );
    });

    const enabledArray = await Promise.all(promises);
    return this.nonHiddenCommands_.filter((command, i) => enabledArray[i] === true || enabledArray[i] === undefined);
  }
}

module.exports = HelpCommandHelper;