offdjs
v2.7.5
Published
oneki framework for discord.js
Downloads
112
Maintainers
Readme
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
- 0 config
Installation
Node.js 18.12 or newer is required.
npm install offdjs
Example usage
Set your token in an .env file at the root of the project (according to discord.js specs)
OFFDJS_TOKEN="your_discord_token"
At this point you can run your script from the root of your project and the bot will turn on without requiring any configuration or installing anything at all.
npx offdjs
You can also install offdjs locally and add it to your scripts in the
package.json
. (recomended)
{
"scripts": {
"start": "offdjs"
}
}
Intents
intents can be declared in an environment variable DISCORD_INTENTS
or as an
argument -i
in the command, its value will be the number of intents that can
be calculated in the
intents calculator, or the
name of the item in its
upper snake case
or
pascal case version.
An example with .env
OFFDJS_INTENTS="1 GUILD_MEMBERS GuildBans"
An example with cli
npx offdjs -i 1 GUILD_MEMBERS GuildBans
DJS components
offdjs exports all the discord.js library in 'offdjs/djs'. example:
import { Events } from 'offdjs/djs'
Events
To load events you just need to create a folder in the root called events
and
offdjs will read all the .js
files; (custom events also work). offdjs
can read subfolders inside the events
folder for easy event organisation.
offdjs will register the file name as event name.
example:
.
├── events
│ ├── ready.js
│ └── guild
│ ├── guildMemberAdd.js
│ └── guildMemberRemove.js
├── node_modules
│ └── ...
├── .env
└── package.json
The script must export a handler
function to be executed with the parameters
of such an event
// events/ready.js
export function handler(client) {
console.log('ready')
}
Optionally, you can export an once
constant with true
or false
if you
require it to be recorded as a once event. If you need to register several
handlers for the same event you also have the option of exporting a name
constant where you will explicitly declare the name of the event you need to
subscribe to and offdjs will omit the file name and replace it with that
constant.
// events/other_ready.js
import { Events } from 'offdjs/djs'
export const name = Events.ClientReady
export const once = true
export function handler(client) {
console.log('ready again')
}
Interactions
Receiving interactions is similar to events. You need to export a handler
function that will receive the interaction as a parameter. You also need to
export a constant name
which can be of type string
or RegExp
; this
constant, similar to events, will be compared with the id of the interaction or
name of the command. If the interaction has a custom id, the handler receives as
parameters the id separated by :
. The name of the folder depends on the type
of interaction y al igual que los eventos, los scripts pueden estar dentro de
subcarpetas. Here are examples of the different types:
Buttons
For buttons, the folder name is buttons
.
.
├── buttons
│ └── confirm_ban.js
├── node_modules
│ └── ...
├── .env
└── package.json
// confirm_ban.js
export const name = /ban:\d{18,19}/
/**
* @param {ButtonInteraction} interaction - djs interaction object
* @param {'ban'} _ - in this case always is 'ban'
* @param {string} id - the id of the member to ban
*/
export function handler(interaction, _, id) {
// example code
const user = interaction.guild?.members.ban(id)
interaction.reply({
content: `User ${user} banned successfully`,
})
}
Select Menus
For selection menus, the folder name is menus
, they receive any selectable
menu, whether string, channel, role, user and mentionable menus.
.
├── menus
│ └── config
│ └── welcome_channel.js
├── node_modules
│ └── ...
├── .env
└── package.json
// welcome_channel.js
export const name = 'config:welcome_channel'
/**
* @param {AnySelectMenuInteraction} interaction - djs interaction object
*/
export function handler(interaction) {
// example code
if (!interaction.isChannelSelectMenu()) return
const channel = interaction.channels.first()
interaction.client.configWelcomeChannel(channel)
interaction.reply({
content: `welcomes are now displayed in ${channel}`,
})
}
Modals
For modals it is very similar to buttons and the name of the folder
is surprisingly modals
.
.
├── modals
│ └── secret.js
├── node_modules
│ └── ...
├── .env
└── package.json
// secret.js
export const name = 'secret'
/**
* @param {ModalSubmitInteraction} interaction - djs interaction object
*/
export function handler(interaction) {
// example code
const secret = interaction.fields.getTextInputValue('secret')
interaction.client.publishSecret(secret)
interaction.reply({
content: 'Your secret was published anonymously',
ephemeral: true,
})
}
Context Menus
For context menu commands the folder shall be named context
and shall receive
both user context interactions and message context interactions.
.
├── contexts
│ └── warn.js
├── node_modules
│ └── ...
├── .env
└── package.json
// warn.js
export const name = 'warn'
/**
* @param {ContextMenuCommandInteraction} interaction - djs interaction object
*/
export function handler(interaction) {
// example code
interaction.showModal(
new ModalBuilder()
.setTitle(`Warn ${interaction.target.displayName}`)
.setCustomId(`warn:${interaction.target.id}`)
.setComponents(
new ActionRowBuilder<TextInputBuilder>().setComponents(
new TextInputBuilder()
.setCustomId('reason')
.setLabel('Reason')
.setRequired()
.setStyle(TextInputStyle.Paragraph)
)
)
)
}
Commands
For commands the folder is called commands
, these can export a third property
called command
of type ApplicationCommandDataResolvable
which will be the
command that the client will automatically register in each of the guilds. The
command
property is optional. The command name, subcommand and group shall
also be received in the function parameters as a custom id. If the command
contains subcommands and/or groups, the name
property separates them with :
as if they were buttons.
Offdjs will register the command for execution in the following order of priority:
- The name constant exported
- The name of the constant
command
exported. - The name of the file
This means that the export const name
is optional, plus, if you need to, you
can name the file poll:create.js
to listen only to the /poll create
subcommand.
By default the commands are displayed globally, however, you can export a
guilds
array with the ID's of the guilds where you want the command to be
displayed. If the array is empty, the display will be uniform in all available
guilds.
.
├── commands
│ └── poll.js
├── node_modules
│ └── ...
├── .env
└── package.json
// secret.js
export const name = /poll:(create|finish)/
export const guilds = []
export const command =
new SlashCommandBuilder()
.setName('poll')
.setDescription('Create or finish a poll')
.addSubcommand(subcommand =>
subcommand
.setName('create')
.setDescription('Create a poll')
.addStringOption(option =>
option
.setName('question')
.setDescription('The question to ask')
.setRequired(true)
)
)
.addSubcommand(subcommand =>
subcommand
.setName('finish')
.setDescription('Finish a poll')
.addStringOption(option =>
option
.setName('poll')
.setDescription('The id of the poll to finish')
.setRequired(true)
.setAutocomplete(true)
)
)
/**
* @param {ChatInputCommandInteraction} interaction - djs interaction object
* @param {'poll'} _ - in this case always is 'poll'
* @param {'create' | 'finish'} subcommand - the id of the member to ban
*/
export function handler(interaction, _, subcommand) {
// example code
if (subcommand === 'create') return interaction.client.newPoll(interaction)
const pollId = interaction.options.getString('poll')
interaction.client.finishPoll(pollId)
interaction.reply({
'Poll finished'
})
}
Autocompletes
For autocompletes the folder is called autocompletes
and they behave similar
to commands except that they do not export the command
property. If the
command contains subcommands and/or groups, the name
property separates them
with :
as if they were buttons. The name of the command, subcommand and group
will also be received in the function parameters as a custom id.
.
├── commands
│ └── poll.js
├── node_modules
│ └── ...
├── .env
└── package.json
// secret.js
export const name = 'poll:finish'
/**
* @param {AutocompleteInteraction} interaction - djs interaction object
*/
export function handler(interaction) {
// example code
const polls = interaction.client.polls
.search(interaction.options.getFocused())
.map(p => ({ value: p.id, name: p.name }))
interaction.respond(polls)
}
Root Directory
You can specify the root of your project by passing it as a parameter in the
command (-root build
) or by setting the environment variable OFFDJS_ROOT
.
Example .env
OFFDJS_ROOT="build"
Example cli
npx offdjs -r build
Main process
With cli
If you have extra processes, such as a database connection, you can indicate it to the cli as the first argument and it will execute it.
Example:
npx offdjs ./index.js
// index.js
import server from './server.js'
import client from 'offdjs'
import mydb from './db.js'
// your custom process
await mydb.connect()
await server.listen(process.env.PORT ?? 3000)
server.send(client.user.username + ' ready')
remember not to initialize the client in that file, offdjs already initializes it for you and you can import it with
import client from 'offdjs'
. the clien exported is an initialized clientClient<true>
.
Without cli
It is also possible to run the framework without the cli by importing the client
into your index.js file and executing client.login()
. To configure the
offdjs
options, such as intents, or root you can set them in client.options
Note:
import { client } from 'offdjs'
export an uninitialized clientClient<false>
Example
// index.js
import { Events, GatewayIntentBits } from 'offdjs/djs'
import server from './server.js'
import mydb from './db.js'
import { client } 'offdjs'
// your custom process
await mydb.connect()
await server.listen(process.env.PORT ?? 3000)
client.on(Events.ClientReady, () => {
server.send(client.user.username + ' ready')
})
// config framework
// intents
client.options.intents = new IntentsBitField([
GatewayIntentBits.Guilds,
GatewayIntentBits.Members
])
// root (from the cwd)
client.options.root = 'build'
await client.login()
TypeScript Support
You can type your event handlers
import { EventHandler } from 'offdjs'
import { Events } from 'offdjs/djs'
export const handler: EventHandler<Events.ClientReady> = async client => {
console.log('ready again')
}