i create a discord bot in js and its my first bot ever. i add 5 slash command but when i add my bot to my server there is not all of my slash command. i retry a lot of time to add my bot to my server (delete and re-invite) but its always the same :
- there is 2,3 or 4 commands (never 5).
- its never the same.
i use Glitch but i dont thinks it's from this.
app.js :
import pg from "pg";
import {
InteractionType,
InteractionResponseType,
InteractionResponseFlags,
MessageComponentTypes,
ButtonStyleTypes,
} from "discord-interactions";
import {
VerifyDiscordRequest,
getRandomEmoji,
DiscordRequest,
} from "./utils.js";
import {
ADDSTUFF_COMMAND,
MYSTUFF_COMMAND,
MYCREATORID_COMMAND,
FROMCREATORID_COMMAND,
STUFFBYCLASSE_COMMAND,
HasGuildCommands,
} from "./commands.js";
// Create an express app
const app = express();
// Get port, or default to 3000
const PORT = process.env.PORT || 3000;
// Parse request body and verifies incoming requests using discord-interactions package
app.use(express.json({ verify: VerifyDiscordRequest(process.env.PUBLIC_KEY) }));
// Store for in-progress games. In production, you'd want to use a DB
const activeGames = {};
// Create a connection pool using the connection information provided on bit.io.
const client = new pg.Client({
user: "delete for stack",
host: "delete for stack",
database: "delete for stack", // public database
password: "", // key from bit.io database page connect menu
port: 5432,
ssl: true,
});
/**
* Interactions endpoint URL where Discord will send HTTP requests
*/
app.post("/interactions", async function (req, res) {
// Interaction type and data
const { type, id, data } = req.body;
/**
* Handle verification requests
*/
if (type === InteractionType.PING) {
return res.send({ type: InteractionResponseType.PONG });
}
/**
* Handle slash command requests
* See https://discord.com/developers/docs/interactions/application-commands#slash-commands
*/
if (type === InteractionType.APPLICATION_COMMAND) {
const { name } = data;
// "test" guild command
if (name === "mystuff") {
const userId = req.body.member.user.id;
var info = await mystuff(userId);
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
// Fetches a random emoji to send from a helper function
content: "stuff --> "+ info + getRandomEmoji(),
},
});
}
if (name === "mycreatorid") {
const userId = req.body.member.user.id;
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
// Fetches a random emoji to send from a helper function
content: "Ton creator id est --> "+ userId,
},
});
}
if (name === "fromcreator") {
const friendId = data.options[0].value;
var info = await mystuff(friendId);
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content: "From " + friendId + "--> "+ info + getRandomEmoji(),
},
});
}
if (name === "stuffbyclasse") {
const classe = data.options[0].value;
console.log(classe);
var info = await findbyclasse(classe);
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content: "equipement trouvé pour " + classe + "--> "+ info + getRandomEmoji(),
},
});
}
if (name === "addstuff") {
const userId = req.body.member.user.id;
const stuff_link = data.options[0].value;
const classe = data.options[1].value;
const note = data.options[2].value;
addstuff(stuff_link,classe,note,userId);
// Send a message into the channel where command was triggered from
return res.send({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
// Fetches a random emoji to send from a helper function
content:
"stuff {" + stuff_link + "} est dans la base ! " + getRandomEmoji(),
},
});
}
}
});
async function findbyclasse(classe){
await client.connect();
var retval;
var res = await client.query("SELECT stuff, classe, note FROM dsstable WHERE classe='" + classe +"';");
res.rows.forEach((row) => {
if (row.stuff !== undefined){
retval += " stuff : ";
retval += row.stuff;
retval += " classe : ";
retval += row.classe;
retval += " note : ";
retval += row.note;
retval += " ";
retval += " // ";
}
});
var retvalpur = retval.replace("undefined","");
await client.end();
return retvalpur;
}
async function mystuff(userId) {
await client.connect();
var retval;
var res = await client.query("SELECT stuff, classe, note FROM dsstable WHERE creator='" + userId +"';");
res.rows.forEach((row) => {
if (row.stuff !== undefined){
retval += " stuff : ";
retval += row.stuff;
retval += " classe : ";
retval += row.classe;
retval += " note : ";
retval += row.note;
retval += " ";
retval += " // ";
}
});
var retvalpur = retval.replace("undefined","");
await client.end();
return retvalpur;
}
async function addstuff(stufflink, classe, note,userId) {
await client.connect();
var res = await client.query(
"INSERT INTO DSSTable (stuff,classe,note, creator) VALUES ('" + stufflink + "','"+ classe +"','"+ note +"','"+ userId +"');");
res.rows.forEach((row) => {
console.log(row);
});
await client.end();
}
app.listen(PORT, () => {
console.log("Listening on port", PORT);
// Check if guild commands from commands.json are installed (if not, install them)
HasGuildCommands(process.env.APP_ID, process.env.GUILD_ID, [MYSTUFF_COMMAND,ADDSTUFF_COMMAND,MYCREATORID_COMMAND,FROMCREATORID_COMMAND,STUFFBYCLASSE_COMMAND,]);
});
command.js :
import { capitalize, DiscordRequest } from './utils.js';
export async function HasGuildCommands(appId, guildId, commands) {
if (guildId === '' || appId === '') return;
commands.forEach((c) => HasGuildCommand(appId, guildId, c));
}
// Checks for a command
async function HasGuildCommand(appId, guildId, command) {
// API endpoint to get and post guild commands
const endpoint = `applications/${appId}/guilds/${guildId}/commands`;
try {
const res = await DiscordRequest(endpoint, { method: 'GET' });
const data = await res.json();
if (data) {
const installedNames = data.map((c) => c['name']);
// This is just matching on the name, so it's not good for updates
if (!installedNames.includes(command['name'])) {
console.log(`Installing "${command['name']}"`);
InstallGuildCommand(appId, guildId, command);
} else {
console.log(`"${command['name']}" command already installed`);
}
}
} catch (err) {
console.error(err);
}
}
// Installs a command
export async function InstallGuildCommand(appId, guildId, command) {
// API endpoint to get and post guild commands
const endpoint = `applications/${appId}/guilds/${guildId}/commands`;
// install command
try {
console.log("command is -> " + command['name']);
await DiscordRequest(endpoint, { method: 'POST', body: command });
} catch (err) {
console.error(err);
}
}
// Simple test command
export const MYSTUFF_COMMAND = {
name: 'mystuff',
description: 'get my stuff',
type: 1,
};
export const MYCREATORID_COMMAND = {
name: 'mycreatorid',
description: 'get my creator id !',
type: 1,
};
export const STUFFBYCLASSE_COMMAND = {
name: 'stuffbyclasse',
description: 'Trouve les stuffs pour une classe précise !',
type: 1,
"options": [
{
"name": "classe",
"description": "j'espère que tu joues hupper !",
"type": 3,
"required": true,
"choices" : [
{
"name": "huppermage",
"value": "huppermage"
},
{
"name": "iop",
"value": "iop"
},
{
"name": "cra",
"value": "cra"
},
{
"name": "sram",
"value": "sram"
},
{
"name": "steamer",
"value": "steamer"
},
{
"name": "enutrof",
"value": "enutrof"
},
{
"name": "roublar",
"value": "roublar"
},
{
"name": "xelor",
"value": "xelor"
},
{
"name": "eniripsa",
"value": "eniripsa"
},
{
"name": "ouginak",
"value": "ouginak"
},
{
"name": "sadida",
"value": "sadida"
},
{
"name": "ecaflip",
"value": "ecaflip"
},
{
"name": "eliotrop",
"value": "eliotrop"
},
{
"name": "osamodas",
"value": "osamodas"
},
{
"name": "panda",
"value": "panda"
},
{
"name": "sacrieur",
"value": "sacrieur"
},
{
"name": "zobal",
"value": "zobal"
},
{
"name": "feca",
"value": "feca"
},
{
"name": "all",
"value": "all"
}
]
},
]
};
export const FROMCREATORID_COMMAND = {
name: 'fromcreator',
description: 'get creator stuff',
type: 1,
"options": [
{
"name": "creatorid",
"description": "l'id du createur du stuf !",
"type": 3,
"required": true,
},
]
};
// Command containing options
export const ADDSTUFF_COMMAND = {
name: 'addstuff',
"type": 1,
"description": "Send a random adorable animal photo",
"options": [
{
"name": "stuff_link",
"description": "dofus book ici !",
"type": 3,
"required": true,
},
{
"name": "classe",
"description": "j'espère que tu joues hupper !",
"type": 3,
"required": true,
"choices" : [
{
"name": "huppermage",
"value": "huppermage"
},
{
"name": "iop",
"value": "iop"
},
{
"name": "cra",
"value": "cra"
},
{
"name": "sram",
"value": "sram"
},
{
"name": "steamer",
"value": "steamer"
},
{
"name": "enutrof",
"value": "enutrof"
},
{
"name": "roublar",
"value": "roublar"
},
{
"name": "xelor",
"value": "xelor"
},
{
"name": "eniripsa",
"value": "eniripsa"
},
{
"name": "ouginak",
"value": "ouginak"
},
{
"name": "sadida",
"value": "sadida"
},
{
"name": "ecaflip",
"value": "ecaflip"
},
{
"name": "eliotrop",
"value": "eliotrop"
},
{
"name": "osamodas",
"value": "osamodas"
},
{
"name": "panda",
"value": "panda"
},
{
"name": "sacrieur",
"value": "sacrieur"
},
{
"name": "zobal",
"value": "zobal"
},
{
"name": "feca",
"value": "feca"
},
{
"name": "all",
"value": "all"
}
]
},
{
"name": "note",
"description": " Donne un peu de detail !",
"type": 3,
"required": true,
},
]
};
utils.js :
import fetch from 'node-fetch';
import { verifyKey } from 'discord-interactions';
export function VerifyDiscordRequest(clientKey) {
return function (req, res, buf, encoding) {
const signature = req.get('X-Signature-Ed25519');
const timestamp = req.get('X-Signature-Timestamp');
const isValidRequest = verifyKey(buf, signature, timestamp, clientKey);
if (!isValidRequest) {
res.status(401).send('Bad request signature');
throw new Error('Bad request signature');
}
};
}
export async function DiscordRequest(endpoint, options) {
// append endpoint to root API URL
const url = 'https://discord.com/api/v10/' + endpoint;
// Stringify payloads
if (options.body) options.body = JSON.stringify(options.body);
// Use node-fetch to make requests
const res = await fetch(url, {
headers: {
Authorization: `Bot ${process.env.DISCORD_TOKEN}`,
'Content-Type': 'application/json; charset=UTF-8',
'User-Agent': 'DiscordBot (https://github.com/discord/discord-example-app, 1.0.0)'
},
...options
});
// throw API errors
if (!res.ok) {
const data = await res.json();
throw new Error(JSON.stringify(data));
}
// return original response
return res;
}
// Simple method that returns a random emoji from list
export function getRandomEmoji() {
const emojiList = ['','','','','','','','️','','','','','','✨'];
return emojiList[Math.floor(Math.random() * emojiList.length)];
}
export function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}