cordless is a simple wrapper for discord.js that allows you to create extensive and extensible Discord bots.
yarn add cordless
npm i cordless
Follow this guide to create a Discord bot in the Discord developer portal.
TypeScript:
import { init, BotFunction } from 'cordless'
const ping: BotFunction = {
condition: (msg) => msg.content === 'ping',
callback: (msg) => msg.reply('pong'),
}
init({ functions: [ping] }).login('your.bot.token')JavaScript:
const cordless = require('cordless')
const ping = {
condition: (msg) => msg.content === 'ping',
callback: (msg) => msg.reply('pong'),
}
cordless.init({ functions: [ping] }).login('your.bot.token')By default, bot functions respond to messageCreate events, like in the ping example above.
You can create functions that subscribe to other events by passing an event key.
For example, this function reacts to a new channel being created:
const channelGreeter: BotFunction<'channelCreate'> = {
event: 'channelCreate',
condition: () => true,
callback: (channel) => {
if (channel.isText()) {
channel.send(`Hello world! This is ${channel.name}`)
}
},
}Note: certain events, like guildMemberAdd, may require additional Gateway Intents to work. See: Overriding the default Gateway Intents
Auto-generate a help function for your bot by passing a helpCommand.
Make sure you give your functions a name and description if you want to use them with the generated help function.
For example, let's generate a !help command:
const ping: BotFunction = {
name: 'ping',
description: 'Responds to your ping with a pong!\n\nUsage: ping',
condition: (msg) => msg.content === 'ping',
callback: (msg) => msg.reply('pong'),
}
const client = init({
functions: [ping],
helpCommand: '!help',
})
client.login('your.bot.token')Now your bot can respond to !help:
You can share business logic and state between your different functions using context. By default, the context contains the discord.js client and the current list of functions.
For example, here is a function that uses context to display the number of functions available:
const numberOfFunctions: BotFunction = {
condition: (msg) => msg.content === 'How many functions?',
callback: (msg, context) => {
msg.reply(`There are ${context.functions.length} functions.`)
},
}You can also extend the context with your own custom context.
Here's a basic implementation of state management - the count will be shared between function calls and its value will be persisted:
TypeScript:
const state = {
count: 0,
increment: () => {
state.count++
},
}
type MyCustomContext = {
state: {
count: number
increment: () => void
}
}
const counter: BotFunction<'messageCreate', MyCustomContext> = {
condition: (msg) => msg.content === 'count',
callback: (msg, context) => {
context.state.increment()
msg.reply(`The count is ${context.state.count}`)
},
}
const client = init<MyCustomContext>({
functions: [counter],
context: { state },
})JavaScript:
const state = {
count: 0,
increment: () => {
state.count++
},
}
const counter = {
condition: (msg) => msg.content === 'count',
callback: (msg, context) => {
context.state.increment()
msg.reply(`The count is ${context.state.count}`)
},
}
const client = cordless.init({
functions: [counter],
context: { state },
})The init method returns a discord.js Client.
Read the discord.js documentation for more information about using the client.
const client = init({
// ...
})
client.on('ready', () => {
console.log(`Logged in as ${client.user?.tag}!`)
})
client.on('messageCreate', console.log)
client.login('your.bot.token')By default, cordless initializes the discord.js client with the Gateway Intents [GUILDS, GUILD_MESSAGES]. This should be sufficient for most bots that simply need to receive messages and do something in response. You can provide your own list of intents if you need additional functionality.
For example, let's say we have a function that should log something when a guild invite is created, in addition to handling messages as usual. We will have to pass the GUILD_INVITES intent:
import { Intents } from 'discord.js'
const inviteLogger: BotFunction<'inviteCreate'> = {
event: 'inviteCreate',
condition: () => true,
callback: (invite) => {
console.log(
`Invite created by ${invite.inviter?.username}. Invite url: ${invite.url}`,
)
},
}
init({
functions: [
inviteLogger,
// ...
],
intents: [
Intents.FLAGS.GUILDS,
Intents.FLAGS.GUILD_MESSAGES,
Intents.FLAGS.GUILD_INVITES,
],
}).login('your.bot.token')For more information about Gateway Intents, see:
- https://discord.com/developers/docs/topics/gateway#gateway-intents
- https://discordjs.guide/popular-topics/intents.html
Clone and install the dependencies:
git clone https://github.com/TomerRon/cordless.git
cd cordless
yarn
We recommend installing yalc. Publish your changes locally with:
yalc publish
You can then test your changes in a local app using:
yalc add cordless
Run the unit tests:
yarn test
You must first create two bots and add them to a Discord server. One of the bots will run the cordless client, and the other bot will pretend to be a normal user.
You'll need the tokens for both of the bots, and the channel ID of a channel where the bots can send messages.
Copy the .env file and edit it:
cp .example.env .env
# .env
E2E_CLIENT_TOKEN=some.discord.token
E2E_USER_TOKEN=some.discord.token
E2E_CHANNEL_ID=12345678
Run the e2e tests:
yarn e2e
Huge shoutout to fivenp (@fivenp) for the amazing visual assets. Go check out his work!
This project is licensed under the ISC License - see the LICENSE file for details.