JSPM

  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q43640F
  • License MIT

oneki framework for discord.js

Package Exports

  • offdjs
  • offdjs/lib/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (offdjs) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme


OFFDJS


Discord server npm version npm downloads

About

offdjs is a small framework that uses discord.js created to solve the needs and simplify the development of the Oneki bot made public so that anyone can create their own bot in a few lines of code

  • little
  • fast
  • easy to use

Installation

Node.js 16.9.0 or newer is required.

npm install offdjs

Example usage

Install discord.js:

npm i offdjs

create a script in your package.json that runs offdjs:

{
    "scripts": {
        "start": "offdjs"
    }
}

Set your token in an .env file at the root of the project (according to discord.js specs)

DISCORD_TOKEN=your_discord_token

At this point you can run your script and the bot will turn on without requiring any configuration

npm start

Events

To load events you just need to create a folder in the root called events and offdjs will read all the files whose name has the following structure:

eventName.event.js

offdjs can read subfolders within the events folder for easy event management

example:

.
├── events
│   ├── ready.event.js
│   └── guild
│       ├── guildMemberAdd.event.js
│       └── guildMemberRemove.event.js
├── node_modules
│   └── ...
├── .env
└── package.json

The script should export by default a function which will be executed with the parameters of said event

export default function ready(client) {
    console.log('ready')
}

Commands

To load commands you just need to create a folder in the root called commands and offdjs will read all the files whose name has the following structure:

commandName.command.js

offdjs can read subfolders within the commands folder to make managing commands easier

example:

.
├── commands
│   ├── ping.command.js
│   └── actions
│       ├── punch.command.js
│       └── kiss.command.js
├── node_modules
│   └── ...
├── .env
└── package.json

The script must export a default class that extends from the Command class provided by offdjs which is initialized in the super with at least the name that must match the name put in the file and a description, both properties are objects with properties named as local Discord API, en-US at least (this will change in the future)

The Comand class has an ChatInputCommandInteraction method with a parameter of type ChatInputCommandInteraction<'cached'> which is executed every time it receives a command interaction with the same name set which you can override to execute and respond to said interaction

example:

import { Command } from 'offdjs'

export default class Ping extends Command {
    constructor() {
        super({
            name: {
                'en-US': 'ping'
            },
            description: {
                'en-US': 'Ping the bot'
            },
            global: false
        })
    }

    async ChatInputCommandInteraction(interaction) {
        interaction.reply('Pong!')
    }
}

If you want to abstract the functionality of the command you can create a folder called interactions and export a function chatInputCommandInteraction in a file with the name of the command, it will receive the interaction as a parameter of type ChatInputCommandInteraction<'cached'>

example:

.
├── interactions
│   └── ping.js
├── node_modules
│   └── ...
├── .env
└── package.json
//ping.js

export function chatInputCommandInteraction(interaction) {
    interaction.reply('Pong!')
}

If you use subcommands you can create folders to use them as if the command name were a path; /test ping => interactions/test/ping.js

example:

.
├── interactions
│   └── test
│       └── ping.js
├── node_modules
│   └── ...
├── .env
└── package.json
//ping.js

export function chatInputCommandInteraction(interaction) {
    interaction.reply('Pong!')
}

Buttons

To receive button interactions you can create a folder called interactions and export a function buttonInteraction in a file with the name of the button id, it will receive the interaction as a parameter of type ButtonInteraction<'cached'>

example:

.
├── interactions
│   └── custom_id.js
├── node_modules
│   └── ...
├── .env
└── package.json
//ping.js

export function buttonInteraction(interaction) {
    interaction.reply('you clicked the button')
}

if you need to use arguments in the button, you can pass them by the customId as a string separating them with : (you can custom interactionSplit). This also allows you to create subinteractions with the commands that like the chatInputCommandInteraction will split the logic into different files for example interaction.customId = 'test:yes' => interactions/test/yes.js. The arguments passed by id will be received along with the interaction

example:

.
├── interactions
│   ├── test.js
│   └── test
│       └── yes.js
├── node_modules
│   └── ...
├── .env
└── package.json
//test.js

export function buttonInteraction(interaction, _, choice) {
    // this is executed in case the interaction.customId
    // is 'test:yes' or 'test:no'
    interaction.reply('you selected ' + choice)
    // choice contains a 'yes' or 'no'
}
//test/yes.js

export function buttonInteraction(interaction) {
    // this is executed only in case
    // the interaction.customId is 'test:yes'
    interaction.reply('you selected yes')
}

To receive menu interactions you can create a folder called interactions and export a function selectMenuInteraction in a file with the name of the menu id, it will receive the interaction as a parameter of type SelectMenuInteraction<'cached'>

example:

.
├── interactions
│   └── custom_id.js
├── node_modules
│   └── ...
├── .env
└── package.json
//ping.js

export function selectMenuInteraction(interaction) {
    interaction.reply('you selected: ' + interaction.vaues.join(', '))
}

if you need to use arguments in the menu, you can pass them by the customId as a string separating them with : (you can custom interactionSplit). This also allows you to create subinteractions with the commands that like the chatInputCommandInteraction will split the logic into different files for example interaction.customId = 'test:add' => interactions/test/add.js. The arguments passed by id will be received along with the interaction

example:

.
├── interactions
│   ├── test.js
│   └── test
│       └── add.js
├── node_modules
│   └── ...
├── .env
└── package.json
//test.js

export function selectMenuInteraction(interaction, _, choice) {
    // this is executed in case the interaction.customId
    // is 'test:add' or 'test:remove'
    interaction.reply(`you selected ${choice} this options: ${interaction.values.join(', ')}`)
    // choice contains a 'add' or 'remove'
}
//test/add.js

export function selectMenuInteraction(interaction) {
    // this is executed only in case
    // the interaction.customId is 'test:add'
    interaction.reply('you selected add this options: ' + interaction.values.join(', '))
}

Modals

To receive modal interactions you can create a folder called interactions and export a function modalSubmitInteraction in a file with the name of the modal id, it will receive the interaction as a parameter of type ModalSubmitInteraction<'cached'>

example:

.
├── interactions
│   └── custom_id.js
├── node_modules
│   └── ...
├── .env
└── package.json
//ping.js

export function modalSubmitInteraction(interaction) {
    interaction.reply('you selected: ' + interaction.vaues.join(', '))
}

if you need to use arguments in the modal, you can pass them by the customId as a string separating them with : (you can custom interactionSplit). This also allows you to create subinteractions with the commands that like the modalSubmitInteraction will split the logic into different files for example interaction.customId = 'test:add' => interactions/send/suggest.js. The arguments passed by id will be received along with the interaction

example:

.
├── interactions
│   ├── send.js
│   └── send
│       └── suggest.js
├── node_modules
│   └── ...
├── .env
└── package.json
//send.js

export function modalSubmitInteraction(interaction, _, choice) {
    // this is executed in case the interaction.customId
    // is 'send:suggest' or 'send:issue'
    interaction.reply(choice + ' sent')
    // choice contains a 'suggest' or 'issue'
}
//send/suggest.js

export function modalSubmitInteraction(interaction) {
    // this is executed only in case
    // the interaction.customId is 'send:suggest'
    interaction.reply('suggest sent')
}

Autocomplete

To receive autocomplete interactions you can create a folder called interactions and export a function autocompleteInteraction in a file with the name of the command or the name of the option focused in a folder with the name of the command, it will receive the interaction as a parameter of type AutocompleteInteraction<'cached'>

example:

.
├── interactions
│   └── search
│       └── query.js
├── node_modules
│   └── ...
├── .env
└── package.json
//query.js

export function autocompleteInteraction(interaction) {
    const query = interaction.options.getFocused()
    interaction.respond(autocomplete(query))
}
.
├── interactions
│   └── search.js
├── node_modules
│   └── ...
├── .env
└── package.json
//query.js

export function autocompleteInteraction(interaction) {
    const query = interaction.options.getFocused()
    if (interaction.options.getFocused(true).name === 'query') {
        interaction.respond(autocomplete(query))
    }
}

Message context menu

To receive message context menu interactions you can create a folder called interactions and export a function messageContextMenuCommandInteraction in a file with the name of the command, it will receive the interaction as a parameter of type MessageContextMenuCommandInteraction<'cached'>

example:

.
├── interactions
│   └── translate.js
├── node_modules
│   └── ...
├── .env
└── package.json
//query.js

export function messageContextMenuCommandInteraction(interaction) {
    interaction.reply(translate(interaction.targetMessage.content, interaction.locale))
}

User context menu

To receive user context menu interactions you can create a folder called interactions and export a function mserContextMenuCommandInteraction in a file with the name of the command, it will receive the interaction as a parameter of type UserContextMenuCommandInteraction<'cached'>

example:

.
├── interactions
│   └── report.js
├── node_modules
│   └── ...
├── .env
└── package.json
//report.js

export function userContextMenuCommandInteraction(interaction) {
    interaction.reply('User reported')
}

Client

To obtain the client, offdjs exports the client already initialized as default so if it requires. this is initialized by running offdjs

example:

import client, { Command } from 'offdjs'

client.on('ready', () => console.log('ready'))

// client.login() isn't necessary

executing index file

As you may have noticed, there is no index file to run, however, if required, you can easily create an index file to run extra processes like connecting to a database

You only have to import the client and use the login method in said file and you can change your script in the package.json or execute said file

example:

//index.js

// import the client
import client from 'offdjs'

// others imports
import mongoose from 'mongoose'

// login the client (important)
client.login()

// connect to de db or others process
await mongoose.connect('mongodb://localhost/my_database')

If you run index.js it works without problems

node index.js

Config

So far no configuration has been required, however, if you need to touch client configurations you can generate a offdjs.config.js file in the root where you export by default an object with properties to pass to the client, this step is optional

example:

.
├── offdjs.config.js
├── node_modules
│   └── ...
├── .env
└── package.json
// offdjs.config.js
export default {
    intents: [
        IntentsBitField.Flags.Guilds,
        IntentsBitField.Flags.GuildMembers,
        IntentsBitField.Flags.MessageContent,
        IntentsBitField.Flags.DirectMessages
    ]
}

Root Directory

If you are working with typescript it is common to have a build folder

.
├── build
│   └── events
│       └── ...
├── node_modules
│   └── ...
├── .env
├── offdjs.config.js
└── package.json
└── tsc.json

By default offdjs reads the commands and events folders in root, to change root to the build folder you will need to export a root property in the config with the new route

example:

// offdjs.config.js
export default {
    intents: [IntentsBitField.Flags.Guilds],
    root: 'build'
}

i18n

offdjs has i18n integration, you can enable the defaults in offdjs.config.js by setting the i18n property to true or by setting your own config

example:

// offdjs.config.js
export default {
    i18n: true
}

or

// offdjs.config.js
export default {
    i18n: {
        locales: ['en', es],
        directory: join(cwd, 'lang'),
        defaultLocale: 'en',
        retryInDefaultLocale: true
        // more config
    }
}

it also provides a function to create a translator which receives an interaction or message object and returns a function which you can use as a translator. The first parameter of the returned function will be a string with the phrase to translate and the second will be an object with the keywords to replace (optional) as if it were i18n.__n()

example:

// x.command.js
import client, { Command, Translator } from 'offdjs'

export default class Ping extends Command {
    constructor() { super({ ... }) }

    async interaction(interaction) {
        const translate = Translator(interaction)
        await interaction.reply(translate('ping.response'))
        interaction.editReply(translate('ping.other', {
            name: client.user.username
        }))
    }
}

interactions

To receive interactions you can create a folder called interactions and export a default function in a file with the name of the command or the id of the button, it will receive the interaction as a parameter of type Interaction. If the interaction contains a customId, in the parameters you recibe a customId splited for :

Example:

.
├── interactions
│   └── avatar.js
├── node_modules
│   └── ...
├── .env
└── package.json
//avatar.js
import { getToggleButton, getAvatarEmbed } from '../myUtils.js'

export default function (interaction, _, selected = 'user') {
    const button = getToggleButton(selected) // customId = 'avatar:user' | 'avatar:member'

    if (interaction.isChatInputCommand()) {
        const embed = getAvatarEmbed(interaction, selected)

        interaction.reply({
            embeds: [embed],
            components: [button]
        })
    } else if (interaction.isButton()) {
        const embed = getAvatarEmbed(interaction, selected)

        interaction.update({
            embeds: [embed],
            components: [button]
        })
    }
}

if you need recibe a specific interaction see commands, buttons, menus, modals, autocomplete, Message context menu and User context menu