Installation
npm : npm install amethystjs
yarn : yarn add amethystjs
pnpm : pnpm add amethystjs
Features
With this powerful framework you can :
See an example right here
Create Amythyst Client
Import the client
import { AmethystClient } from 'amethystjs';
const client = new AmethystClient({
}, {
token: process.env.token,
debug: true,
commandsFolder: './commands',
buttonsFolder: './button',
eventsFolder: './events',
modalHandlersFolder: './modals',
autocompleteListenersFolder: './autocompletes',
preconditionsFolder: './preconditions',
prefix: '!!',
strictPrefix: false,
botName: 'Amethyst',
botNameWorksAsPrefix: true,
mentionWorksAsPrefix: true,
defaultCooldownTime: 10,
waitForDefaultReplies: {
user: ({ user, interaction }) => ({ content: "You cannot interact with this message" }),
everyone: ({ user, interaction }) => ({ content: "You cannot interact with this message" })
},
commandsArchitecture: 'simple',
eventsArchitecture: 'simple',
commandLocalizationsUsedAsNames: true,
defaultWhoCanReact: 'useronly',
activity: {
name: 'Amethyst',
type: 'Playing',
url: 'https://github.com/Greensky-gs/amethystjs'
},
runMessageCommandsOnMessageEdit: true,
});
client.start({
loadCommands: true,
loadEvents: true,
loadPreconditions: true,
loadAutocompleteListeners: true,
loadModals: true
});
Create a command
Go in your commands folder, and create a new file.
Architecture
The emplacement of the file depends of your architecture ( commandsArchitecture, defined when creating the client )
If it is simple, it looks like this :
|_commands
|_info.ts/js
|_ban.ts/js
If you configured it to double, it looks like this :
|_commands
|_moderation
|_ban.ts/js
|_info.ts/js
Command creation
Import AmethystCommand and exports it
import { AmethystCommand } from 'amethystjs';
export default new AmethystCommands({
name: 'command name',
description: "Description of the command",
cooldown: 5,
permissions: [ 'Administrator' ],
clientPermissions: [ 'ManageChannels' ],
preconditions: [ ],
messageInputChannelTypes: [],
aliases: ['alias 1', 'alias 2', '...'],
messageInputDescription: "Description of the message command (optionnal)",
userContextName: "name of the user context command",
messageContextName: "Name of the message context command"
})
.setMessageRun((options) => {
})
.setChatInputRun((options) => {
}).setUserContextMenuRun((options) => {
})
.setMessageContextMenuRun((options) => {
})
Handle errors
Use the commandDenied event to handle command denietions
Use the commandError event to handle command errors
Record your own preconditions
Amethyst JS allows you to create your own preconditions (because it's fun š)
Before you start
Before you start creating your preconditions, know that there are defaults preconditions you can access using the preconditions object of the framework, like so :
import { preconditions, AmethystCommand } from 'amethystjs';
export default new AmethystCommand({
name: 'name',
preconditions: [ preconditions.OwnerOnly ]
});
Create a precondition
First, import the Precondition from Amethyst
import { Precondition } from 'amethystjs';
export default new Precondition("Your precondition's name")
.setChatInputRun((options) => {
return {
ok: true,
message: 'Message in case of fail',
metadata: {},
interaction: options.interaction,
type: 'chatInput'
}
})
.setMessageRun((options) => {
return {
ok: true,
message: "Message in case of fail",
metadata: { },
type: 'message',
channelMessage: options.message
}
}).setModalRun((options) => {
return {
ok: true,
message: "Message in case of fail",
metadata: { },
type: 'modal',
modal: options.modal
}
}).setUserContextMenuRun((options) => {
return {
ok: true,
message: "Message in case of fail",
metadata: { },
type: 'userContextMenu',
contextMenu: options.interaction
}
}).setMessageContextMenuRun((options) => {
return {
ok: true,
message: "Message in case of fail",
metadata: { },
type: 'messageContextMenu',
contextMenu: options.interaction
}
})
To use a custom precondition in a command, use it like so :
import yourPrecondition from 'your precondition file';
import { AmethystCommand } from 'amethystjs';
export default new AmethystCommand({
name: 'name',
preconditions: [ yourPrecondition ]
})
Registering events
Go in your events folder and add a new file
Architecture of events
The emplacement of the file depends of your architecture ( eventsArchitecture, defined when creating the client )
If it is simple, it looks like this :
|_events
|_ready.ts/js
|_commandDenied.ts/js
If you configured it to double, it looks like this :
|_events
|_bot
|_ready.ts/js
|_guildCreate.ts/js
|_users
|_commandDenied.ts/js
import { AmethystEvent } from 'amethystjs';
export default new AmethystEvent('eventName', () => {
})
Events list
Amethyst JS adds events to the Discord Client. Here is the list of the events you can resiter via Events handler :
Autocomplete listeners
Autocomplete listeners are things that replies to an autocomplete interaction (interaction options with a lot of choices)
Go in your autocomplete listeners and create a new file
import { AutocompleteListener } from 'amethystjs';
export default new AutocompleteListener({
commandName: [ { commandName: 'command name here' }, { commandName: 'another command name here', optionName: 'optionnal option name in the command here' } ],
run: (options) => {
return [ {name: 'Name', value: 'value', nameLocalizations: {} } ]
}
});
As you've maybe noticed, commandName is an array containing a commandName and a potential optionName.
It means that the autocomplete will be applied to every command with the name included in the array, and if optionName is specified, it will also check if the option name correspond to the one specified.
Button handler
Amethyst JS can handle button interactions for you.
Go to your buttons folder and create a new file
import { ButtonHandler } from 'amethystjs';
export default new ButtonHandler({
customId: 'buttonCustomId',
permissions: ['Permissions required for the user'],
clientPermissions: ["Permissions required for the client"],
identifiers: [ 'optionnal array of more button custom identifiers' ]
})
.setRun((options) => {
})
If you specify permissions, you have to handle it in case of error.
The customId and identifiers propreties are button custom ID
To handle it, create a new event and record for the buttonDenied event.
Wait for messages
You can wait for messages using amethyst JS.
You'll use waitForMessage() function.
import { waitForMessage } from 'amethystjs';
client.on('messageCreate', async(message) => {
if (message.content === '!ping') {
await message.channel.send(`Would you like me to reply ?\nReply by \`yes\` or \`no\``);
const reply = await waitForMessage({
user: message.author,
whoCanReact: 'useronly',
channel: message.channel,
time: 120000
});
if (!reply) message.channel.send(`You haven't replied :/`);
if (reply.content === 'yes') message.reply("Pong !");
}
})
Wait for interactions
Amethyst JS allows you to wait for interaction responses, like a select menu or a button click
import { waitForInteraction } from 'amethystjs';
import { ButtonBuilder, ActionRowBuilder, componentType, Message } from 'discord.js';
(async() => {
const msg = await interaction.reply({
message: "Yes or no",
components: [ new ActionRowBuilder({
components: [
new ButtonBuilder({ label: 'Yes', style: ButtonStyle.Success, customId: 'yes' }),
new ButtonBuilder({ label: 'No', style: ButtonStyle.Danger, customId: 'no' })
]
}) as ActionRowBuilder<ButtonBuilder>]
}) as Message<true>;
const reply = await waitForInteraction({
message: msg,
time: 120000,
whoCanReact = 'useronly',
user: interaction.user,
componentType: componentType.Button
});
if (!reply || reply.customId === 'no') return interaction.editReply("Ok, no");
interaction.editReply("Yes !");
})()
Register custom prefixes
The Amethyst client has a prefixesManager proprety that allows you to set different prefixes for different servers
:warning: Important The client does not save the prefixes, you have to use a database to save it for your bot. The manager register prefixes only to use it with Amethyst client
Summary
Here is the summary of the prefixes manager
setPrefix
Set a prefix for a server.
:warning: Use it when you load your bot, in a ready event, for example.
const prefixes = [
{ guildId: '1324', prefix: '!' },
{ guildId: '4321', prefix: '!!' },
{ guildId: '1324', prefix: '??' }
];
for (const data of prefixes) {
client.prefixesManager.setPrefix({
guildId: data.guildId,
prefix: data.prefix
});
}
getPrefix
Get the custom prefix of a server.
Returns default prefix if the server has no custom prefix
client.prefixesManager.getPrefix('guild ID')
Same prefix
Return all the servers with the same prefix
The method returns an array of objects with 2 propreties :
{
guildId: string;
prefix: string
}
client.prefixesManager.samePrefix('!')
prefixes list
You can get the prefixes data to manage it if you want.
This method will return a map. If you want to get it as an array, use json method instead
The map has 2 propreties :
Map<
string,
string
>
client.prefixesManager.list;
Prefixes json
You can get the prefixes data to manage it if you want.
This method will return an array. Use map method to get it as a map if you need.
The array has 2 propreties :
{
guildId: string;
prefix: string;
}[];
client.prefixesManager.json;
Wait
You can wait for a certain time before executing something else with Amethyst JS. Here is how to use the wait method
import { wait } from 'amethystjs'
(async() => {
await wait(1000);
await wait(1, 's');
await wait(1, 'm');
})();
Modal Handlers
You can use modal handlers with Amethyst JS, it can handle modals trought an object inside the modals handler folder
import { ModalHandler } from 'amethystjs';
export default new ModalHandler({
modalId: ['identifiers of modals to be handled'],
name: "Name of the handler"
}).setRun((opts) => {
opts.modal;
opts.user;
});
Make a control panel
You can make a control panel for your bot

You need to have the client created
Creation
Here's how to create a control panel
import { AmethystClient, ControlPanel } from 'amethystjs';
import rebootHandler from './buttons/reboot';
import const { panelEmbed } from './contents/embeds';
const client = new AmethystClient({
intents: ['Guilds']
}, {
token: 'token',
debug: true,
buttonsFolder: './dist/buttons'
})
client.start({})
const panel = new ControlPanel({
client: client,
channelID: 'Id of the channel where the panel will be',
deleteMessages: true,
pin: true,
content: {
content: "Control panel",
embeds: [panelEmbed]
}
});
Register a button
Now you surely want to register your buttons
import rebootHandler from './buttons/reboot';
panel.registerButton({
label: 'Disconnect voice',
handler: 'panel.disconnect',
style: 'Primary'
})
.registerButton({
label: 'Reboot',
style: 'Danger',
handler: rebootHandler
})
Once it's done, don't forget to start the panel with panel.start() method
Log4JS
Amethyst JS allows you to log things in a log file with log4js
This object has two methods
Trace
The trace() method allows you to write something in the log file
const { log4js } = require('amethystjs');
log4js.trace("Something just happened");
Config
The config() method configs log4js.
For now, you can config :
- Trigger the time displaying
- Time displaying format
- log file
- Add a
onLog event
- Object indentation
const { log4js } = require('amethystjs');
log4js.config('displayTime', true);
log4js.config('onLog', (message) => {
console.log(`Log4JS traced something`)
})
log4js.config('file', 'logs.txt')
log4js.config('objectIndentation', 1)
log4js.config('displayTimeFormat', (time) => `[${time.getDay()}/${time.getMonth()}/${time.getFullYear()}:${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}]`)
Paginator
The Greensky's paginator under the name of AmethystPaginator. The method is the exact same than the package.
Examples
Here are some repositories that use Amethyst JS :
Contributing
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the documentation.