{"id":21521773,"url":"https://github.com/greensky-gs/amethystjs","last_synced_at":"2025-09-07T16:38:56.685Z","repository":{"id":63526753,"uuid":"564882026","full_name":"Greensky-gs/AmethystJS","owner":"Greensky-gs","description":"Framework for Discord.js v14 in TypeScript","archived":false,"fork":false,"pushed_at":"2024-04-29T09:19:12.000Z","size":451,"stargazers_count":6,"open_issues_count":2,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-05-01T17:53:00.773Z","etag":null,"topics":["discord","discord-framework","discordjs","framework"],"latest_commit_sha":null,"homepage":"https://npmjs.org/package/amethystjs","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Greensky-gs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-11T18:16:00.000Z","updated_at":"2024-05-03T22:32:06.018Z","dependencies_parsed_at":"2023-02-19T06:31:15.284Z","dependency_job_id":"cdb69701-b278-4317-af43-7cb6dad296c1","html_url":"https://github.com/Greensky-gs/AmethystJS","commit_stats":null,"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Greensky-gs%2FAmethystJS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Greensky-gs%2FAmethystJS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Greensky-gs%2FAmethystJS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Greensky-gs%2FAmethystJS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Greensky-gs","download_url":"https://codeload.github.com/Greensky-gs/AmethystJS/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248120562,"owners_count":21050972,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["discord","discord-framework","discordjs","framework"],"created_at":"2024-11-24T01:08:17.204Z","updated_at":"2025-09-07T16:38:56.658Z","avatar_url":"https://github.com/Greensky-gs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"X\"\u003e\n    \u003cimg src=\"https://github.com/Greensky-gs/AmethystJS/blob/master/logo.png\" alt=\"Logo\"\u003e\n  \u003c/a\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://discord.gg/NjsmxpnyXg\"\u003e\n    \u003cimg src=\"https://img.shields.io/discord/974703720380133457?color=5865F2\u0026logo=discord\u0026logoColor=white\" alt=\"Discord Server\"\u003e\n  \u003c/a\u003e\n \u003ca href=\"https://www.npmjs.com/package/amethystjs\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/amethystjs\" alt=\"Package NPM\"\u003e\n  \u003c/a\u003e\n \u003ca href=\"https://github.com/Greensky-gs/amethystjs/releases\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/downloads/Greensky-gs/amethystjs/total\" alt=\"Download\"\u003e\n\u003c/a\u003e\n\n## Installation\n\n```yml\nnpm : npm install amethystjs\nyarn : yarn add amethystjs\npnpm : pnpm add amethystjs\n```\n\n## Features\n\nWith this powerful framework you can :\n\n* [Create commands](#create-a-command)\n* [Handle errors](#handle-errors)\n* [Create a precondition](#record-your-own-preconditions)\n* [Use events](#registering-events)\n* [Handle autocomplete interactions](#autocomplete-listeners)\n* [Wait for messages](#wait-for-messages)\n* [Wait for interactions](#wait-for-interactions)\n* [Handle buttons](#button-handler)\n* [Handle modals](#modal-handlers)\n* [Make a control panel](#make-a-control-panel)\n* [Set custom prefixes](#register-custom-prefixes)\n* [Wait a little time](#wait)\n* [Paginate embeds](#paginator)\n* [log4js](#log4js)\n\nSee an example [right here](#examples)\n\n## Create Amythyst Client\n\nImport the client\n\n```ts\nimport { AmethystClient } from 'amethystjs';\n\nconst client = new AmethystClient({\n    // Discord.js client options\n}, {\n    token: process.env.token,\n    debug: true,\n    commandsFolder: './commands',\n    buttonsFolder: './button',\n    eventsFolder: './events',\n    modalHandlersFolder: './modals',\n    autocompleteListenersFolder: './autocompletes',\n    preconditionsFolder: './preconditions',\n    prefix: '!!',\n    strictPrefix: false,\n    botName: 'Amethyst',\n    botNameWorksAsPrefix: true,\n    mentionWorksAsPrefix: true,\n    defaultCooldownTime: 10,\n    waitForDefaultReplies: {\n        user: ({ user, interaction }) =\u003e ({ content: \"You cannot interact with this message\" }),\n        everyone: ({ user, interaction }) =\u003e ({ content: \"You cannot interact with this message\" })\n    },\n    commandsArchitecture: 'simple',\n    eventsArchitecture: 'simple',\n    commandLocalizationsUsedAsNames: true,\n    defaultWhoCanReact: 'useronly',\n    activity: {\n       name: 'Amethyst',\n       type: 'Playing',\n       url: 'https://github.com/Greensky-gs/amethystjs'\n    },\n    runMessageCommandsOnMessageEdit: true,\n\n});\nclient.start({\n    // All are optionnal and true by default\n    loadCommands: true, // Load commands\n    loadEvents: true, // Load events\n    loadPreconditions: true, // Load preconditions\n    loadAutocompleteListeners: true, // Load autocomplete listeners\n    loadModals: true // Load modals handlers\n});\n```\n\n## Create a command\n\nGo in your commands folder, and create a new file.\n\n### Architecture\n\nThe emplacement of the file depends of your architecture ( `commandsArchitecture`,  defined when [creating the client](#create-amythyst-client) )\n\nIf it is simple, it looks like this :\n\n```cmd\n|_commands\n  |_info.ts/js\n  |_ban.ts/js\n```\n\nIf you configured it to double, it looks like this :\n\n```cmd\n|_commands\n  |_moderation\n    |_ban.ts/js\n    |_info.ts/js\n```\n\n### Command creation\n\nImport AmethystCommand and exports it\n\n```ts\nimport { AmethystCommand } from 'amethystjs';\n\nexport default new AmethystCommands({\n    name: 'command name', // Command name\n    description: \"Description of the command\", // Description - required\n    cooldown: 5, // Cooldown time\n    permissions: [ 'Administrator' ], // Permissions for the user - optionnal\n    clientPermissions: [ 'ManageChannels' ], // Permissions for the bot - optionnal\n    preconditions: [  ], // Preconditions for the command - optionnal\n    messageInputChannelTypes: [], // Channel types allowed for message input running - optionnal\n    aliases: ['alias 1', 'alias 2', '...'], // Command aliases - optionnal\n    messageInputDescription: \"Description of the message command (optionnal)\", // Message description - optionnal\n    userContextName: \"name of the user context command\", // Name of the user context command - optionnal\n    messageContextName: \"Name of the message context command\" // Name of the message context command - optionnal\n})\n.setMessageRun((options) =\u003e {\n    // Write code for message commands (optionnal)\n})\n.setChatInputRun((options) =\u003e {\n    // Write code for slash commands (optionnal)\n}).setUserContextMenuRun((options) =\u003e {\n    // Write code for user context menu command (optionnal)\n})\n.setMessageContextMenuRun((options) =\u003e {\n    // Write code for message context menu command (optionnal)\n})\n```\n\n## Handle errors\n\nUse the `commandDenied` event to handle command denietions\n\nUse the `commandError` event to handle command errors\n\n## Record your own preconditions\n\nAmethyst JS allows you to create your own preconditions (because it's fun 🙂)\n\n### Before you start\n\nBefore you start creating your preconditions, know that there are defaults preconditions you can access using the `preconditions` object of the framework, like so :\n\n```ts\nimport { preconditions, AmethystCommand } from 'amethystjs';\n\nexport default new AmethystCommand({\n    name: 'name',\n    preconditions: [ preconditions.OwnerOnly ] // Add the precondition you want\n});\n```\n\n### Create a precondition\n\nFirst, import the Precondition from Amethyst\n\n```ts\nimport { Precondition } from 'amethystjs';\n\nexport default new Precondition(\"Your precondition's name\")\n.setChatInputRun((options) =\u003e {\n    // Run your precondition here for slash commands\n    // You have to return something like this :\n    return {\n        ok: true,\n        message: 'Message in case of fail',\n        metadata: {/* some more datas */},\n        interaction: options.interaction,\n        type: 'chatInput'\n    }\n})\n.setMessageRun((options) =\u003e {\n    // Run your precondition here for message commands\n    // Return something like this\n    return {\n        ok: true,\n        message: \"Message in case of fail\",\n        metadata: { /* some extra datas */ },\n        type: 'message',\n        channelMessage: options.message\n    }\n}).setModalRun((options) =\u003e {\n    // Run your modal precondition here\n    // Return something like so\n    return {\n        ok: true,\n        message: \"Message in case of fail\",\n        metadata: { /* some data here */ },\n        type: 'modal',\n        modal: options.modal\n    }\n}).setUserContextMenuRun((options) =\u003e {\n    // Run your precondition for user context command here\n    // Return something like this\n    return {\n        ok: true,\n        message: \"Message in case of fail\",\n        metadata: { /* Some more datas */ },\n        type: 'userContextMenu',\n        contextMenu: options.interaction\n    }\n}).setMessageContextMenuRun((options) =\u003e {\n    // Run your precondition for message context command here\n    // Return something like this\n    return {\n        ok: true,\n        message: \"Message in case of fail\",\n        metadata: { /* Some more datas */ },\n        type: 'messageContextMenu',\n        contextMenu: options.interaction\n    }\n})\n```\n\nTo use a custom precondition in a command, use it like so :\n\n```ts\nimport yourPrecondition from 'your precondition file';\nimport { AmethystCommand } from 'amethystjs';\n\nexport default new AmethystCommand({\n    name: 'name',\n    preconditions: [ yourPrecondition ]\n})\n```\n\n## Registering events\n\nGo in your events folder and add a new file\n\n### Architecture of events\n\nThe emplacement of the file depends of your architecture ( `eventsArchitecture`, defined when [creating the client](#create-amythyst-client) )\n\nIf it is simple, it looks like this :\n\n```cmd\n|_events\n  |_ready.ts/js\n  |_commandDenied.ts/js\n```\n\nIf you configured it to double, it looks like this :\n\n```cmd\n|_events\n  |_bot\n    |_ready.ts/js\n    |_guildCreate.ts/js\n  |_users\n    |_commandDenied.ts/js\n```\n\n```ts\nimport { AmethystEvent } from 'amethystjs';\n\nexport default new AmethystEvent('eventName', (/* event options */) =\u003e {\n    // Run your event\n})\n```\n\n### Events list\n\nAmethyst JS adds events to the Discord Client. Here is the list of the events you can resiter via [Events handler](#registering-events) :\n\n| Event | When its activated | arguments list | Types |\n| :-----|:------------------:|----------------| ----: |\n| `amethystDebug` | When the client debugs something | message | [string](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/String) |\n| `commandDenied` | When a precondition stops a command | command, reason | [commandDeniedPayload](./src/typings/Command.ts#L197), [deniedReason](./src/typings/Client.ts#L339) |\n| `commandError` | When an error occurs | command, reason | [commandDeniedPayload](./src/typings/Command.ts#L197), [errorReason](./src/typings/Client.ts#L310) |\n| `buttonInteraction` | When a button is pressed | interaction, message | [ButtonInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/ButtonInteraction), [Message](https://old.discordjs.dev/#/docs/discord.js/main/class/Message) |\n| `modalSubmit` | When a modal interaction is created | interaction | [ModalSubmitInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/ModalSubmitInteraction) |\n| `buttonDenied` | When a button is stopped because of a precondition | button | [buttonDenied](./src/typings/ButtonHandler.ts#L71) |\n| `selectMenuInteraction` | When any select menu is interacted | selector | [AnySelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/AnySelectMenuInteraction) |\n| `stringSelectInteraction` | When a string select menu is interacted | selector | [StringSelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/StringSelectMenuInteraction) |\n| `roleSelectMenuInteraction` | When a role select menu is interacted | selector | [RoleSelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/RoleSelectMenuInteraction) |\n| `userSelectInteraction` | When an user select menu is interacted | selector | [UserSelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/UserSelectMenuInteraction) |\n| `channelSelectInteraction` | When a channel select menu is interacted | selector | [ChannelSelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/ChannelSelectMenuInteraction) |\n| `mentionableSelectInteraction` | When a mentionable select menu is interacted | selector | [MentionableSelectMenuInteraction](https://old.discordjs.dev/#/docs/discord.js/main/class/MentionableSelectMenuInteraction) |\n| `modalRejected` | When a modal is stopped because of a precondition | reason | [ModalDenied](./src/typings/ModalHandler.ts#L63) |\n\n## Autocomplete listeners\n\nAutocomplete listeners are things that replies to an autocomplete interaction (interaction options with a lot of choices)\n\nGo in your autocomplete listeners and create a new file\n\n```ts\nimport { AutocompleteListener } from 'amethystjs';\n\nexport default new AutocompleteListener({\n    commandName: [ { commandName: 'command name here' }, { commandName: 'another command name here', optionName: 'optionnal option name in the command here' } ],\n    run: (options) =\u003e {\n        // Make your choice here are return :\n        return [ {name: 'Name', value: 'value', nameLocalizations: {} } ]\n    }\n});\n```\n\nAs you've maybe noticed, `commandName` is an array containing a commandName and a potential optionName.\n\nIt 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.\n\n## Button handler\n\nAmethyst JS can handle button interactions for you.\n\nGo to your buttons folder and create a new file\n\n```ts\nimport { ButtonHandler } from 'amethystjs';\n\nexport default new ButtonHandler({\n    customId: 'buttonCustomId',\n    permissions: ['Permissions required for the user'],\n    clientPermissions: [\"Permissions required for the client\"],\n    identifiers: [ 'optionnal array of more button custom identifiers' ]\n})\n.setRun((options) =\u003e {\n    // Execute your code here\n})\n```\n\nIf you specify permissions, you have to handle it in case of error.\n\n\u003e The customId and identifiers propreties are button custom ID\n\nTo handle it, [create a new event](#registering-events) and record for the `buttonDenied` event.\n\n## Wait for messages\n\nYou can wait for messages using amethyst JS.\n\nYou'll use `waitForMessage()` function.\n\n```ts\nimport { waitForMessage } from 'amethystjs';\n\n// Important : this works only in an async function\n// For exemple, I'll do a simple client.on('messageCreate') to show you how to use it\nclient.on('messageCreate', async(message) =\u003e {\n    if (message.content === '!ping') {\n        await message.channel.send(`Would you like me to reply ?\\nReply by \\`yes\\` or \\`no\\``);\n        const reply = await waitForMessage({\n            user: message.author,\n            whoCanReact: 'useronly',\n            channel: message.channel,\n            time: 120000\n        });\n\n        if (!reply) message.channel.send(`You haven't replied :/`);\n        if (reply.content === 'yes') message.reply(\"Pong !\");\n    }\n})\n```\n\n## Wait for interactions\n\nAmethyst JS allows you to wait for interaction responses, like a select menu or a button click\n\n```ts\nimport { waitForInteraction } from 'amethystjs';\nimport { ButtonBuilder, ActionRowBuilder, componentType, Message } from 'discord.js';\n\n// This function works only in an async function.\n// Here i'm gonna show you in a very simple async function.\n// In this exemple, interaction is already defined\n\n(async() =\u003e {\n    const msg = await interaction.reply({\n        message: \"Yes or no\",\n        components: [ new ActionRowBuilder({\n            components: [\n                new ButtonBuilder({ label: 'Yes', style: ButtonStyle.Success, customId: 'yes' }),\n                new ButtonBuilder({ label: 'No', style: ButtonStyle.Danger, customId: 'no' })\n            ]\n        }) as ActionRowBuilder\u003cButtonBuilder\u003e]\n    }) as Message\u003ctrue\u003e;\n\n    const reply = await waitForInteraction({\n        message: msg,\n        time: 120000,\n        whoCanReact = 'useronly',\n        user: interaction.user,\n        componentType: componentType.Button\n    });\n\n    if (!reply || reply.customId === 'no') return interaction.editReply(\"Ok, no\");\n    interaction.editReply(\"Yes !\");\n})()\n```\n\n## Register custom prefixes\n\nThe [Amethyst client](#create-amythyst-client) has a `prefixesManager` proprety that allows you to set different prefixes for different servers\n\n: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\n\n### Summary\n\nHere is the summary of the [prefixes manager](#register-custom-prefixes)\n\n| Proprety | Type |\n|----------|------|\n| [`setPrefix`](#setprefix) | Method |\n| [`getPrefix`](#getprefix) | Method |\n| [`samePrefix`](#same-prefix) | Method |\n| [`list`](#prefixes-list) | Proprety |\n| [`json`](#prefixes-json) | Proprety |\n\n#### setPrefix\n\nSet a prefix for a server.\n\n:warning: Use it when you load your bot, in a ready [event](#registering-events), for example.\n\n```js\nconst prefixes = [\n    { guildId: '1324', prefix: '!' },\n    { guildId: '4321', prefix: '!!' },\n    { guildId: '1324', prefix: '??' }\n];\n\nfor (const data of prefixes) {\n    client.prefixesManager.setPrefix({\n        guildId: data.guildId,\n        prefix: data.prefix\n    });\n}\n```\n\n#### getPrefix\n\nGet the custom prefix of a server.\n\nReturns default prefix if the server has no custom prefix\n\n```js\nclient.prefixesManager.getPrefix('guild ID')\n```\n\n#### Same prefix\n\nReturn all the servers with the same prefix\n\nThe method returns an array of objects with 2 propreties :\n\n```ts\n{\n    guildId: string;\n    prefix: string\n}\n```\n\n```js\nclient.prefixesManager.samePrefix('!')\n```\n\n#### prefixes list\n\nYou can get the prefixes data to manage it if you want.\n\nThis method will return a [map](https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Array/map). If you want to get it as an array, use [json method](#prefixes-json) instead\n\nThe map has 2 propreties :\n\n```ts\nMap\u003c\n    string, // Corresponding to the guild ID\n    string // Corresponding to the prefix\n\u003e\n```\n\n```js\nclient.prefixesManager.list;\n```\n\n#### Prefixes json\n\nYou can get the prefixes data to manage it if you want.\n\nThis method will return an array. Use [map method](#prefixes-list) to get it as a map if you need.\n\nThe array has 2 propreties :\n\n```ts\n{\n    guildId: string;\n    prefix: string;\n}[];\n```\n\n```js\nclient.prefixesManager.json;\n```\n\n## Wait\n\nYou can wait for a certain time before executing something else with Amethyst JS. Here is how to use the wait method\n\n```ts\n// Import wait\nimport { wait } from 'amethystjs'\n\n// Use it in an async function, it wont work otherwise\n(async() =\u003e {\n    await wait(1000); // Wait 1s\n    await wait(1, 's'); // Wait 1s\n    await wait(1, 'm'); // Wait 1 minute\n})();\n```\n\n## Modal Handlers\n\nYou can use modal handlers with Amethyst JS, it can handle modals trought an object inside the modals handler folder\n\n```ts\nimport { ModalHandler } from 'amethystjs';\n\nexport default new ModalHandler({\n    modalId: ['identifiers of modals to be handled'],\n    name: \"Name of the handler\"\n}).setRun((opts) =\u003e {\n    opts.modal;\n    opts.user;\n});\n```\n\n## Make a control panel\n\nYou can make a control panel for your bot\n\n![image](https://media.discordapp.net/attachments/1012743337754771486/1146382088434630696/image.png?width=2052\u0026height=636)\n\nYou need to have the [client](#create-amythyst-client) created\n\n### Creation\n\nHere's how to create a control panel\n\n```ts\nimport { AmethystClient, ControlPanel } from 'amethystjs';\nimport rebootHandler from './buttons/reboot';\nimport const { panelEmbed }  from './contents/embeds';\n\nconst client = new AmethystClient({\n    intents: ['Guilds']\n}, {\n    token: 'token',\n    debug: true,\n    buttonsFolder: './dist/buttons'\n})\n\nclient.start({}) // You can start it after the panel is created if you want\n\nconst panel = new ControlPanel({\n    client: client,\n    channelID: 'Id of the channel where the panel will be',\n    deleteMessages: true, // Delete the other messages in the channel - optional\n    pin: true, // Pin the panel - optional\n    content: { \n        content: \"Control panel\",\n        embeds: [panelEmbed]\n    } // Content of the message - optional\n});\n```\n\n### Register a button\n\nNow you surely want to register your buttons\n\n```ts\nimport rebootHandler from './buttons/reboot';\n\npanel.registerButton({\n    label: 'Disconnect voice',\n    handler: 'panel.disconnect', // Use the handler that handles 'panel.disconnect'\n    style: 'Primary'\n})\n.registerButton({\n    label: 'Reboot',\n    style: 'Danger',\n    handler: rebootHandler // Use the imported handler\n})\n```\n\nOnce it's done, don't forget to start the panel with `panel.start()` method\n\n## Log4JS\n\nAmethyst JS allows you to log things in a log file with `log4js`\n\nThis object has two methods\n\n### Trace\n\nThe `trace()` method allows you to write something in the log file\n\n```js\nconst { log4js } = require('amethystjs');\n\nlog4js.trace(\"Something just happened\");\n```\n\n### Config\n\nThe `config()` method configs log4js.\n\nFor now, you can config :\n\n* Trigger the time displaying\n* Time displaying format\n* log file\n* Add a `onLog` event\n* Object indentation\n\n```js\nconst { log4js } = require('amethystjs');\n\nlog4js.config('displayTime', true);\nlog4js.config('onLog', (message) =\u003e {\n    console.log(`Log4JS traced something`)\n})\nlog4js.config('file', 'logs.txt')\nlog4js.config('objectIndentation', 1)\nlog4js.config('displayTimeFormat', (time) =\u003e `[${time.getDay()}/${time.getMonth()}/${time.getFullYear()}:${time.getHours()}:${time.getMinutes()}:${time.getSeconds()}]`)\n```\n\n## Paginator\n\nThe [Greensky's paginator](https://npmjs.com/package/dsc-pagination) under the name of `AmethystPaginator`. The method is the exact same than the package.\n\n## Examples\n\nHere are some repositories that use Amethyst JS :\n\n* [Lofi Girl](https://github.com/Greensky-gs/lofi-girl) by [Greensky](https://github.com/Greensky-gs/) (Amethyst JS v1.7.1)\n* [Draver Bot](https://github.com/DraverBot/DraverBot) by [Draver Industries](https://github.com/DraverBot) (Amethyst JS v1.7.2)\n\n# Contributing\n\nBefore creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the documentation.\n\n\u003ca href=\"https://github.com/Greensky-gs/AmethystJS/graphs/contributors\"\u003e\n  \u003cimg src=\"https://contrib.rocks/image?repo=Greensky-gs/AmethystJS\" /\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreensky-gs%2Famethystjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgreensky-gs%2Famethystjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgreensky-gs%2Famethystjs/lists"}