Skip to content

prompts

IRONM00N edited this page Oct 11, 2021 · 1 revision

Argument Prompting

Please Try Again

You may notice prompting for arguments in other bots (Tatsumaki) or bot frameworks (Commando).
Akairo has a flexible way for you to do them too!
It allows you to set the following properties:

  • How many times the user can retry.
  • How long they can stall the prompt for.
  • The input to use to cancel a prompt (default is cancel).
  • Whether or not the prompt is optional.
  • The message to send on start, on retry, on timeout, on maximum retries, and on cancel.
    • There can be embeds or files too!
    • Or you can have no message at all!

Let's start with a basic prompt.
We will be reusing this command:

import { Command } from "discord-akairo";
import { GuildMember, Message } from "discord.js";

export default class HighestRoleCommand extends Command {
  constructor() {
    super("highestRole", {
      aliases: ["highestRole"],
      args: [
        {
          id: "member",
          type: "member",
          default: (message: Message) => message.member
        }
      ],
      channel: "guild"
    });
  }

  exec(message: Message, args: { member: GuildMember }): Promise<Message> {
    return message.reply(args.member.roles.highest.name);
  }
}

First, remove the default.
Since prompting will have the user retry until it is finished, default won't do anything.
Now, add the prompt property with the options you want.

import { Command } from "discord-akairo";
import { GuildMember, Message } from "discord.js";

export default class HighestRoleCommand extends Command {
  constructor() {
    super("highestRole", {
      aliases: ["highestRole"],
      args: [
        {
          id: "member",
          type: "member",
          prompt: {
            start: "Who would you like to get the highest role of?",
            retry: "That's not a valid member! Try again."
          }
        }
      ],
      channel: "guild"
    });
  }

  exec(message: Message, args: { member: GuildMember }): Promise<Message> {
    return message.reply(args.member.roles.highest.name);
  }
}

Simple as that, you have a prompt.
Guess what, you can use a function for those messages too!

args = [
  {
    /* ... */
    prompt: {
      start: (message: Message) => `Hey ${message.author}, who would you like to get the highest role of?`,
      retry: (message: Message) => `That\'s not a valid member! Try again, ${message.author}.`
    }
  }
];

More complex structures can also be returned as well.
This includes embeds, attachments, anything that can be sent.

args = [
  {
    /* ... */
    prompt: {
      start: (message: Message) => {
        const embed = new MessageEmbed().setDescription("Please input a member!");
        const content = "Please!";
        return { embed, content };
      };
    }
  }
]

Cascading

Prompts can also "cascade" from three places: the command handler, then the command, then the argument.
For the command handler or the command, we would set the argumentDefaults option.

this.commandHandler = new CommandHandler(this, {
  directory: "./commands/",
  prefix: "?",
  argumentDefaults: {
    prompt: {
      timeout: "Time ran out, command has been cancelled.",
      ended: "Too many retries, command has been cancelled.",
      cancel: "Command has been cancelled.",
      retries: 4,
      time: 30000
    }
  }
});

Those prompt options would now be applied to all prompts that do not have those options already.
Or, with a command with similar arguments:

import { Command } from "discord-akairo";
import { Message } from "discord.js";

export default class AddCommand extends Command {
  constructor() {
    super("add", {
      aliases: ["add"],
      args: [
        {
          id: "numOne",
          type: "number",
          prompt: true
        },
        {
          id: "numTwo",
          type: "number",
          prompt: true
        },
        {
          id: "numThree",
          type: "number",
          prompt: true
        }
      ],
      defaultPrompt: {
        start: "Please input a number!",
        retry: "Please input a number!"
      }
    });
  }

  exec(message: Message, args: { numOne: number; numTwo: number; numThree: number }): Promise<Message> {
    const sum = args.numOne + args.numTwo + args.numThree;
    return message.reply(`The sum is ${sum}!`);
  }
}

Rather than repeating the text for all three arguments, there is a default prompt that applies to all three.
Their prompt property still has to be truthy in order to actually prompt, of course.

Modifying

Prompts can then be modified with a modify function.
It is most useful inside the argumentDefaults option, such as on the command handler.

this.commandHandler = new CommandHandler(this, {
  /* ... */
  argumentDefaults: {
    prompt: {
      modifyStart: (message: Message, text: string) => `${text}\nType cancel to cancel this command.`,
      modifyRetry: (message: Message, text: string) => `${text}\nType cancel to cancel this command.`,
      timeout: 'Time ran out, command has been cancelled.',
      ended: 'Too many retries, command has been cancelled.',
      cancel: 'Command has been cancelled.',
      retries: 4,
      time: 30000
    }
  }
}

The options modifyStart, modifyRetry, etc. are used to modify those types of prompts.
With the above options, all start and retry prompts will have "Type cancel to cancel this command." appended after it.