moleculer-discord
Discord Gateway for Moleculer.js framework.
Features |
---|
1. Configurations |
2. Slash Commands |
2.1 Slash Commands Options |
2.2 Slash Commands Responses |
3.2 Message Commands |
4 Actions |
Configuration
This gateway uses Discord.js lib, so to setup a Discord Bot its recommended to see this tutorial Setup Discord Bot , after setting bot lets begin to Code:
const DiscordGateway = require("moleculer-discord");
module.exports = {
name: "discord",
mixins: [DiscordGateway],
settings: {
token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
clientId: "xxxxxxxxxxxxxxx",
guildId: "xxxxxxxxxxxxxxx",
clientOpts: {intents: ["GUILDS", "GUILD_MESSAGES", "GUILD_MESSAGE_REACTIONS"]},
},
};
Settings | Type | Required | Description |
---|---|---|---|
token | String | X | Access Token of Discord Bot obtained in OAuth2 page |
clientId | String | X | Client of Discord Bot obtained in OAuth2 page |
guildId | String, String[] | GuildId represents in which Servers the bot will work, all servers if undefined | |
clientOpts | Object | Options to Create DiscordBot Instance, More Info |
Slash Commands
To register Commands that Start with Slash(/), we need to create a object that represents all options of these commands.
SlashCommands | Type | Required | Description |
---|---|---|---|
name | String | X | Name of SlashCommand(used as Key of SlashCommand) |
description | String | X | Description of Command that appears in discord |
action | String, Function | X | Action which moleculer will call |
deferred | Boolean, Object | If you need hadle a action who takes more than 3 seconds(doc) | |
options | Object | Input options of Command(check below) |
Slash Commands Options
SlashCommands Options | Type | Required | Description |
---|---|---|---|
name | String | X | Name of SlashCommand Option(used as Key of SlashCommand Options) |
description | String | X | Description of Command that appears in discord |
type | String | X | Input type, "integer", "string", "channel", "user", all list in doc |
require | Boolean |
SlashCommands Registration Example:
module.exports = {
name: "discord",
mixins: [DiscordGateway],
settings: {
clientOpts: {},
token: "xxxxx",
clientId: "xxxx",
},
slashes: {
ping: {
action: "users.ping",
description: "Replies with pong!",
},
clear: {
action: "users.clear",
description: "Clean Messages",
options: {
amount: {type: "integer", description: "How many messages to deletee", required: true,},
channel: {type: "channel", description: "Channel that will delete messages"}
},
},
}
};
Slash Commands Responses
Have some standardized ways to return response back to response back to Discord gate way with
1) String
Returning a string in called action, the gateway will response current Chat
ping: {
handler(ctx){
return "pong"
}
}
2) Multiple Messages
Returning a array of strings in called action, the gateway will response a series of messages at current Chat
ping: {
handler(ctx) {
return ["pong", "ping"]
}
}
3) Embed Message
To return Embed Message just return a object with key "embed" and value true and set the rest of configurations, see docs
ping: {
handler(ctx) {
return {type: "embed", title: "foo"}
}
}
4) Ephemeral Message
You may not always want everyone who has access to the channel to see a slash command's response, so set ephemeral as true in resposne, see docs
ping: {
handler(ctx) {
return { content: 'Pong!', ephemeral: true }
}
}
5) Deferred Message
Some actions take more than 3 seconds, so you need set actions as deferred, to say to Discord give you more time(up to 15 minutes) to process and give a response, so in slash command declaration you need to set deffered as true. See docs
//Slash Command declaration
slashes: {
ping: {
action: "users.ping",
description: "Replies with pong!",
deferred: true
}
}
//Action
ping: {
async handler(ctx) {
await (new Promise((resolve, reject) => { setTimeout(resolve, 10000)}));
return 'pong';
}
}
6) Deferred + Ephemeral Message
If you need a long task with ephemeral mode, just set ephemeral in slash command declation with object with ephemeral.
//Slash Command declaration
slashes: {
ping: {
action: "users.ping",
description: "Replies with pong!",
deferred: { ephemeral: true }
}
}
//Action
ping: {
async handler(ctx) {
await (new Promise((resolve, reject) => { setTimeout(resolve, 10000)}));
return 'pong';
}
}
7) Delete Timer Message
If you need to delete message after some time just add delete attribute and the time in milliseconds.
//Action
ping: {
async handler(ctx) {
return {content: 'pong', delete: 10000};
}
}
If response is ephemeral, use a object in delete, to edit message after timeout.
//Action
ping: {
async handler(ctx) {
return {content: 'pong', delete: {timeout: 10000, content: "timeout!!"}, ephemeral: true};
}
}
8) components
If you need to show some buttons or selects for the user, just return component attribute just like the doc, the on difference is that you need to pass a action to be called in component, and if you wanna pass a user filter, just add a userId in attribute, and if you wanna pass some param to button handler, add params attribute, its util to persist some date between actions, if you wanna set timeout for your actions just set time attribute in milliseconds.
//Action
buttons: {
async handler() {
return {
content: "*`Esta mensagem será deletada em 30 segundos`*",
components: [
{
type: 1,
params: { foo: "bar" },
time: 15000,
components: [{
type: 2,
label: "Verify",
style: 3,
custom_id: "verify",
action: "users.acceptButton",
}, {
type: 2,
label: "Cancel",
style: 4,
custom_id: "cancel",
action: "users.cancelButton",
}
]
}
]
};
}
}
9) update
If you need current interaction just user "update" type in response(used to update buttons ).
//Action
buttonHandler: {
async handler(ctx) {
return {type: "update", content: "OK", components: []}
}
}
Message Commands
It's possible to register commands in text messages like MeeSix make with commands "!play " or "!clean ", to set a message command just create a object like slashes commands:
module.exports = {
name: "discord",
mixins: [DiscordGateway],
settings: {
clientOpts: {},
token: "xxxxxxxx",
guildId: "xxxxxxxxx",
clientId: "xxxxxx",
},
messages: {
"!ping": {action: "users.ping"},
"!say {message}": {action: "users.say"},
},
}
In this example, all messages that starts with "!ping" will call "users.ping" action, in the second message command is declared a param named message(message params is always a string), so if we send "!say 12345" to discord, the gateway will call "users.say" action and send {message: "12345"} as params
Actions
In Discord Gateway has some actions that could be very helpful to control messages, channels, guilds, and users.
guilds
List all Guilds(or discord servers) connected to your Discord Bot, if you pass a "guildId" in params, the action will search with this Id.
let guilds = await ctx.call("discord.guilds");
let guild = await ctx.call("discord.guilds", {guildId: "xxxxxxxxx"});
channels
List all Channels connect to your Discord Bot, if you pass a "channelId" in params, the action will search with this Id.
let channels = await ctx.call("discord.channels");
let channel = await ctx.call("discord.channels", {channelId: "xxxxxxxxx"});
send
Send message in channel by channelId or user param;
await ctx.call("discord.send", {channelId: "xxxxxxxxx", message: "ping"});
await ctx.call("discord.send", {userId: "xxxxxxxxx", message: "ping"});
await ctx.call("discord.send", {channelId: "xxxxxxxxx", message: {type: "embed", title: "foo"}})
clear
Clear amount of messages by channelId param;
await ctx.call("discord.clear", {channelId: "xxxxxxxx", amount: 5});
Life Cycle Events
It's possible to register some events before and after call action, or for error handling. just create a Method in Discord Gateway with event name(onBeforeCall, onAfterCall, authorize, onError), all these methods comes with a ctx and a interaction object
onBeforeCall
OnBeforeCall it's useful to put author of message and the channel which message comes.
methods: {
onBeforeCall(ctx, interaction) {
ctx.meta.author = interaction.user;
ctx.meta.channel = interaction.channel;
},
authorize(ctx, interaction) {
if(interaction.user.hasRole('123445555')) {
throw new MoleculerClientError("UNAUTHORIZED");
}
ctx.meta.user = await ctx.call("dbUser.get", {where: {discordId: interaction.user.id}});
}
onError(ctx, interaction, error){
if(error.type === "UNAUTHORIZED"){
return "You dont have Permission"
}
}
}