diff --git a/secret/env b/secret/env
index 7e6fd8c1..7f824f9c 100644
--- a/secret/env
+++ b/secret/env
@@ -1,5 +1,8 @@
BOT_TOKEN=BOT_TOKEN_GOES_HERE
+CLIENT_ID=OAuth2 Client ID (Used for registerAppCommands)
+CLIENT_SECRET=OAuth2 Client Secret (Used for registerAppCommands)
+
MYSQL_HOST=localhost
MYSQL_USER=user
MYSQL_PASS=password
diff --git a/src/commands/commandList/battle/ab.js b/src/commands/commandList/battle/ab.js
index 1a2f76bf..42a445bc 100644
--- a/src/commands/commandList/battle/ab.js
+++ b/src/commands/commandList/battle/ab.js
@@ -65,17 +65,25 @@ module.exports = new CommandInterface({
/* Get opponent name */
let sender = result[0][0].sender;
- sender = await p.fetch.getMember(p.msg.channel.guild.id, sender);
+ if (p.msg.channel.guild) {
+ sender = await p.fetch.getMember(p.msg.channel.guild.id, sender);
+ } else {
+ sender = await p.fetch.getUser(sender);
+ }
if (!sender) {
p.errorMsg(', I could not find your opponent!', 3000);
return;
}
+ if (!p.msg.channel.guild) {
+ // Can't seem to edit message after interaction in DMs
+ flags.instant = true;
+ }
const settingOverride = {
friendlyBattle: true,
display: flags.display ? flags.display : 'image',
- speed: flags.log ? 'instant' : 'short',
- instant: flags.log ? true : false,
+ speed: flags.instant || flags.log ? 'instant' : 'short',
+ instant: flags.instant || flags.log ? true : false,
title: this.getName(author) + ' vs ' + this.getName(sender),
showLogs: flags.link ? 'link' : flags.log ? true : false,
};
diff --git a/src/commands/commandList/battle/battle.js b/src/commands/commandList/battle/battle.js
index dfb089d5..1534cde4 100644
--- a/src/commands/commandList/battle/battle.js
+++ b/src/commands/commandList/battle/battle.js
@@ -25,6 +25,23 @@ module.exports = new CommandInterface({
group: ['animals'],
+ appCommands: [
+ {
+ 'name': 'battle',
+ 'type': 1,
+ 'description': 'Fight with your team of animals!',
+ 'options': [
+ {
+ 'type': 6,
+ 'name': 'user',
+ 'description': 'Fight a friend.',
+ },
+ ],
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 15000,
half: 80,
six: 500,
diff --git a/src/commands/commandList/battle/crate.js b/src/commands/commandList/battle/crate.js
index b094af44..e3eff083 100644
--- a/src/commands/commandList/battle/crate.js
+++ b/src/commands/commandList/battle/crate.js
@@ -27,6 +27,23 @@ module.exports = new CommandInterface({
group: ['animals'],
+ appCommands: [
+ {
+ 'name': 'crate',
+ 'type': 1,
+ 'description': 'Open a weapon crate',
+ 'options': [
+ {
+ 'type': 4,
+ 'name': 'count',
+ 'description': 'Number of weapon crates',
+ },
+ ],
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 30000,
half: 100,
six: 500,
diff --git a/src/commands/commandList/memegen/headpat.js b/src/commands/commandList/memegen/headpat.js
index 9bf7a48b..14913a14 100644
--- a/src/commands/commandList/memegen/headpat.js
+++ b/src/commands/commandList/memegen/headpat.js
@@ -7,6 +7,7 @@
const CommandInterface = require('../../CommandInterface.js');
+const commandGroups = require('../../../utils/commandGroups.js');
const request = require('request');
module.exports = new CommandInterface({
@@ -24,26 +25,65 @@ module.exports = new CommandInterface({
group: ['memegeneration'],
+ appCommands: [
+ commandGroups.addOption('headpat', ['gen'], {
+ 'name': 'headpat',
+ 'description': 'Generate a headpat emoji',
+ 'type': 2,
+ 'options': [
+ {
+ 'name': 'user',
+ 'description': "Generate a headpat emoji with a user's avatar",
+ 'type': 1,
+ 'required': false,
+ 'options': [
+ {
+ 'name': 'user',
+ 'description': 'The user to use',
+ 'type': 6,
+ 'required': false,
+ },
+ ],
+ },
+ {
+ 'name': 'emoji',
+ 'description': 'Generate a headpat emoji with an emoji',
+ 'type': 1,
+ 'required': false,
+ 'options': [
+ {
+ 'name': 'emoji',
+ 'description': 'The emoji to use',
+ 'type': 3,
+ 'required': true,
+ },
+ ],
+ },
+ ],
+ }),
+ ],
+
cooldown: 30000,
half: 100,
six: 500,
bot: true,
execute: async function (p) {
- let user = p.getMention(p.args[0]);
+ let user = p.getMention(p.args[0]) || p.options.user;
let link;
let name;
if (user) {
link = user.dynamicAvatarURL('png', 128);
name = user.username;
- } else if (!user && !p.args.length) {
- link = p.msg.author.dynamicAvatarURL('png', 128);
- name = p.getName();
- } else if (!user && p.global.isEmoji(p.args[0])) {
- link = p.args[0].match(/:[0-9]+>/gi)[0];
+ } else if (p.global.isEmoji(p.args[0] || p.options.emoji)) {
+ const emoji = p.args[0] || p.options.emoji;
+ link = emoji.match(/:[0-9]+>/gi)[0];
link = `https://cdn.discordapp.com/emojis/${link.slice(1, link.length - 1)}.png`;
- name = p.args[0].match(/:[\w]+:/gi)[0];
+ name = emoji.match(/:[\w]+:/gi)[0];
name = name.slice(1, name.length - 1);
+ } else if (!p.args.length) {
+ link = p.msg.author.dynamicAvatarURL('png', 128);
+ name = p.getName();
} else {
p.errorMsg(', invalid arguments! Please tag a user or add an emoji!', 3000);
p.setCooldown(5);
@@ -65,41 +105,36 @@ module.exports = new CommandInterface({
async function display(p, url, name) {
const emojiName = `${name.replace(/[^\w]/gi, '')}_pat`;
let embed = createEmbed(p, url, name, emojiName);
- let msg = await p.send({ embed });
-
- // Check if user set stealing
- let sql = `SELECT emoji_steal.guild FROM emoji_steal INNER JOIN user ON emoji_steal.uid = user.uid WHERE id = ${p.msg.author.id};`;
- await p.query(sql);
- let canSteal = (await p.query(sql))[0]?.guild;
-
- // Add reactions
- if (canSteal) await msg.addReaction(p.config.emoji.steal);
+ const components = await p.global.getStealButton(p, true);
+ const content = {
+ embed,
+ components,
+ };
+ let msg = await p.send(content);
- // Create reaction collector
- let filter = (emoji, userId) => emoji.name == p.config.emoji.steal && userId != p.client.user.id;
- const collector = p.reactionCollector.create(msg, filter, { idle: 120000 });
+ // Create interaction collector
+ let filter = (componentName) => componentName === 'steal';
+ let collector = p.interactionCollector.create(msg, filter, { idle: 120000 });
const emojiAdder = new p.EmojiAdder(p, { name: emojiName, url });
- collector.on('collect', async function (emoji, userId) {
+ collector.on('collect', async (component, user, ack) => {
try {
- if (await emojiAdder.addEmoji(userId)) {
- await msg.edit({
- embed: createEmbed(p, url, name, emojiName, emojiAdder),
- });
+ if (await emojiAdder.addEmoji(user.id)) {
+ (content.embed = createEmbed(p, url, name, emojiName, emojiAdder)), ack(content);
}
} catch (err) {
if (!emojiAdder.successCount) {
- await msg.edit({
- embed: createEmbed(p, url, name, emojiName, emojiAdder),
- });
+ (content.embed = createEmbed(p, url, name, emojiName, emojiAdder)), ack(content);
}
}
});
collector.on('end', async function (_collected) {
- const embed = createEmbed(p, url, name, emojiName, emojiAdder);
- embed.color = 6381923;
- await msg.edit({ content: 'This message is now inactive', embed });
+ content.embed = createEmbed(p, url, name, emojiName, emojiAdder);
+ content.embed.color = 6381923;
+ content.content = 'This message is now inactive';
+ content.components[0].components[0].disabled = true;
+ await msg.edit(content);
});
}
diff --git a/src/commands/commandList/memegen/waddle.js b/src/commands/commandList/memegen/waddle.js
index 497d4a65..7c105d90 100644
--- a/src/commands/commandList/memegen/waddle.js
+++ b/src/commands/commandList/memegen/waddle.js
@@ -1,12 +1,13 @@
/*
* OwO Bot for Discord
- * Copyright (C) 2021 Christopher Thai
+ * Copyright (C) 2024 Christopher Thai
* This software is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
* For more information, see README.md and LICENSE
*/
const CommandInterface = require('../../CommandInterface.js');
+const commandGroups = require('../../../utils/commandGroups.js');
const request = require('request');
module.exports = new CommandInterface({
@@ -24,26 +25,65 @@ module.exports = new CommandInterface({
group: ['memegeneration'],
+ appCommands: [
+ commandGroups.addOption('waddle', ['gen'], {
+ 'name': 'waddle',
+ 'description': 'Generate a waddle emoji',
+ 'type': 2,
+ 'options': [
+ {
+ 'name': 'user',
+ 'description': "Generate a waddle emoji with a user's avatar",
+ 'type': 1,
+ 'required': false,
+ 'options': [
+ {
+ 'name': 'user',
+ 'description': 'The user to use',
+ 'type': 6,
+ 'required': false,
+ },
+ ],
+ },
+ {
+ 'name': 'emoji',
+ 'description': 'Generate a waddle emoji with an emoji',
+ 'type': 1,
+ 'required': false,
+ 'options': [
+ {
+ 'name': 'emoji',
+ 'description': 'The emoji to use',
+ 'type': 3,
+ 'required': true,
+ },
+ ],
+ },
+ ],
+ }),
+ ],
+
cooldown: 30000,
half: 100,
six: 500,
bot: true,
execute: async function (p) {
- let user = p.getMention(p.args[0]);
+ let user = p.getMention(p.args[0]) || p.options.user;
let link;
let name;
if (user) {
link = user.dynamicAvatarURL('png', 128);
name = user.username;
- } else if (!user && !p.args.length) {
- link = p.msg.author.dynamicAvatarURL('png', 128);
- name = p.getName();
- } else if (!user && p.global.isEmoji(p.args[0])) {
- link = p.args[0].match(/:[0-9]+>/gi)[0];
+ } else if (p.global.isEmoji(p.args[0] || p.options.emoji)) {
+ const emoji = p.args[0] || p.options.emoji;
+ link = emoji.match(/:[0-9]+>/gi)[0];
link = `https://cdn.discordapp.com/emojis/${link.slice(1, link.length - 1)}.png`;
- name = p.args[0].match(/:[\w]+:/gi)[0];
+ name = emoji.match(/:[\w]+:/gi)[0];
name = name.slice(1, name.length - 1);
+ } else if (!p.args.length) {
+ link = p.msg.author.dynamicAvatarURL('png', 128);
+ name = p.getName();
} else {
p.errorMsg(', invalid arguments! Please tag a user or add an emoji!', 3000);
p.setCooldown(5);
@@ -65,41 +105,36 @@ module.exports = new CommandInterface({
async function display(p, url, name) {
const emojiName = `${name.replace(/[^\w]/gi, '')}_waddle`;
let embed = createEmbed(p, url, name, emojiName);
- let msg = await p.send({ embed });
-
- // Check if user set stealing
- let sql = `SELECT emoji_steal.guild FROM emoji_steal INNER JOIN user ON emoji_steal.uid = user.uid WHERE id = ${p.msg.author.id};`;
- await p.query(sql);
- let canSteal = (await p.query(sql))[0]?.guild;
-
- // Add reactions
- if (canSteal) await msg.addReaction(p.config.emoji.steal);
+ const components = await p.global.getStealButton(p, true);
+ const content = {
+ embed,
+ components,
+ };
+ let msg = await p.send(content);
- // Create reaction collector
- let filter = (emoji, userId) => emoji.name == p.config.emoji.steal && userId != p.client.user.id;
- const collector = p.reactionCollector.create(msg, filter, { idle: 120000 });
+ // Create interaction collector
+ let filter = (componentName) => componentName === 'steal';
+ let collector = p.interactionCollector.create(msg, filter, { idle: 120000 });
const emojiAdder = new p.EmojiAdder(p, { name: emojiName, url });
- collector.on('collect', async function (emoji, userId) {
+ collector.on('collect', async (component, user, ack) => {
try {
- if (await emojiAdder.addEmoji(userId)) {
- await msg.edit({
- embed: createEmbed(p, url, name, emojiName, emojiAdder),
- });
+ if (await emojiAdder.addEmoji(user.id)) {
+ (content.embed = createEmbed(p, url, name, emojiName, emojiAdder)), ack(content);
}
} catch (err) {
if (!emojiAdder.successCount) {
- await msg.edit({
- embed: createEmbed(p, url, name, emojiName, emojiAdder),
- });
+ (content.embed = createEmbed(p, url, name, emojiName, emojiAdder)), ack(content);
}
}
});
collector.on('end', async function (_collected) {
- const embed = createEmbed(p, url, name, emojiName, emojiAdder);
- embed.color = 6381923;
- await msg.edit({ content: 'This message is now inactive', embed });
+ content.embed = createEmbed(p, url, name, emojiName, emojiAdder);
+ content.embed.color = 6381923;
+ content.content = 'This message is now inactive';
+ content.components[0].components[0].disabled = true;
+ await msg.edit(content);
});
}
diff --git a/src/commands/commandList/social/emoji.js b/src/commands/commandList/social/emoji.js
index 83c98acb..001a1a3e 100644
--- a/src/commands/commandList/social/emoji.js
+++ b/src/commands/commandList/social/emoji.js
@@ -9,7 +9,6 @@ const CommandInterface = require('../../CommandInterface.js');
const baseURL = 'https://cdn.discordapp.com/emojis/';
const stickerUrl = 'https://media.discordapp.net/stickers/';
-const stealEmoji = '🕵️';
module.exports = new CommandInterface({
alias: ['emoji', 'enlarge', 'jumbo'],
@@ -26,6 +25,16 @@ module.exports = new CommandInterface({
group: ['social'],
+ appCommands: [
+ {
+ 'type': 3,
+ 'name': 'Grab Emojis',
+ 'dm_permission': true,
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 7000,
half: 100,
six: 500,
@@ -158,7 +167,7 @@ async function display(p, emojis) {
return embed;
};
- const additionalButtons = await getStealButton(p);
+ const additionalButtons = await p.global.getStealButton(p);
const additionalFilter = (componentName, _user) => componentName === 'steal';
const pagedMsg = new p.PagedMessage(p, createEmbed, emojis.length - 1, {
idle: 120000,
@@ -183,25 +192,6 @@ async function display(p, emojis) {
});
}
-async function getStealButton(p) {
- const sql = `SELECT emoji_steal.guild FROM emoji_steal INNER JOIN user ON emoji_steal.uid = user.uid WHERE id = ${p.msg.author.id};`;
- const canSteal = (await p.query(sql))[0]?.guild;
- if (canSteal) {
- return [
- {
- type: 2,
- label: 'Steal',
- style: 1,
- custom_id: 'steal',
- emoji: {
- id: null,
- name: stealEmoji,
- },
- },
- ];
- }
-}
-
async function setServer(p) {
// Check if the user has emoji permissions
if (!p.msg.member.permissions.has('manageEmojis')) {
@@ -228,11 +218,11 @@ async function setServer(p) {
}
}
- p.replyMsg(stealEmoji, ', stolen emojis will now be sent to this server!');
+ p.replyMsg(p.config.emoji.steal, ', stolen emojis will now be sent to this server!');
}
async function unsetServer(p) {
let sql = `DELETE FROM emoji_steal WHERE uid = (SELECT uid FROM user WHERE id = ${p.msg.author.id});`;
await p.query(sql);
- p.replyMsg(stealEmoji, ', your server has been unset for stealing!');
+ p.replyMsg(p.config.emoji.steal, ', your server has been unset for stealing!');
}
diff --git a/src/commands/commandList/utils/help.js b/src/commands/commandList/utils/help.js
index 4f2c6cca..195badeb 100644
--- a/src/commands/commandList/utils/help.js
+++ b/src/commands/commandList/utils/help.js
@@ -33,6 +33,16 @@ module.exports = new CommandInterface({
related: [],
+ appCommands: [
+ {
+ 'name': 'help',
+ 'type': 1,
+ 'description': 'OwO, do you need some help~?',
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 1000,
half: 100,
six: 500,
diff --git a/src/commands/commandList/zoo/catch.js b/src/commands/commandList/zoo/catch.js
index ef4d25cb..5faab51b 100644
--- a/src/commands/commandList/zoo/catch.js
+++ b/src/commands/commandList/zoo/catch.js
@@ -32,6 +32,16 @@ module.exports = new CommandInterface({
group: ['animals'],
+ appCommands: [
+ {
+ 'name': 'hunt',
+ 'type': 1,
+ 'description': 'Hunt for some animals!',
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 15000,
half: 80,
six: 500,
diff --git a/src/commands/commandList/zoo/lootbox.js b/src/commands/commandList/zoo/lootbox.js
index c1fab0d1..f933c51b 100644
--- a/src/commands/commandList/zoo/lootbox.js
+++ b/src/commands/commandList/zoo/lootbox.js
@@ -31,25 +31,51 @@ module.exports = new CommandInterface({
group: ['animals'],
+ appCommands: [
+ {
+ 'name': 'lootbox',
+ 'type': 1,
+ 'description': 'Open a lootbox',
+ 'options': [
+ {
+ 'type': 3,
+ 'name': 'count',
+ 'description': 'Number of lootboxes: [number, "all", "fabled"]',
+ },
+ ],
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+ ],
+
cooldown: 5000,
half: 100,
six: 500,
- execute: async function (p) {
- if (p.args.length > 0 && p.global.isInt(p.args[0])) await openMultiple(p, parseInt(p.args[0]));
- else if (p.args.length > 0 && p.args[0].toLowerCase() == 'all') {
- let sql = `SELECT boxcount FROM lootbox WHERE id = ${p.msg.author.id};`;
- let result = await p.query(sql);
+ execute: async function () {
+ if (this.args.length > 0 && this.global.isInt(this.args[0])) {
+ await openMultiple(this, parseInt(this.args[0]));
+ } else if (this.options.count && this.global.isInt(this.options.count)) {
+ await openMultiple(this, parseInt(this.options.count));
+ } else if (
+ (this.args.length > 0 && this.args[0].toLowerCase() == 'all') ||
+ (this.options.count && this.options.count.toLowerCase() == 'all')
+ ) {
+ let sql = `SELECT boxcount FROM lootbox WHERE id = ${this.msg.author.id};`;
+ let result = await this.query(sql);
if (!result || result[0].boxcount <= 0) {
- p.errorMsg(", you don't have any more lootboxes!");
+ this.errorMsg(", you don't have any more lootboxes!");
return;
}
let boxcount = result[0].boxcount;
if (boxcount > maxBoxes) boxcount = maxBoxes;
- await openMultiple(p, boxcount);
- } else if (p.args.length && ['f', 'fabled'].includes(p.args[0].toLowerCase())) {
- await openFabledBox(p);
- } else await openBox(p);
+ await openMultiple(this, boxcount);
+ } else if (
+ (this.args.length && ['f', 'fabled'].includes(this.args[0].toLowerCase())) ||
+ (this.options.count && ['f', 'fabled'].includes(this.options.count.toLowerCase()))
+ ) {
+ await openFabledBox(this);
+ } else await openBox(this);
},
});
diff --git a/src/data/slash.txt b/src/data/slash.txt
index b5459872..8e1bdd48 100644
--- a/src/data/slash.txt
+++ b/src/data/slash.txt
@@ -1,30 +1,4 @@
-// Battle
-{
- "name": "battle",
- "description": "Fight with your team of animals!",
- "options": [
- {
- "type": 6,
- "name": "user",
- "description": "Fight a friend."
- }
- ]
-}
-
-// Weapon Crate
-{
- "name": "crate",
- "description": "Open a weapon crate",
- "options": [
- {
- "type": 4,
- "name": "count",
- "description": "Number of weapon crates"
- }
- ]
-}
-
-// Team commands
+// Team commands (unused)
{
"name": "team",
"description": "Create a strong team of pets to fight!",
diff --git a/src/eventHandlers/interactionCreate.js b/src/eventHandlers/interactionCreate.js
index f92ae119..91e3eb64 100644
--- a/src/eventHandlers/interactionCreate.js
+++ b/src/eventHandlers/interactionCreate.js
@@ -5,6 +5,8 @@
* For more information, see README.md and LICENSE
*/
+const commandGroups = require('../utils/commandGroups.js');
+
exports.handle = function (interaction) {
switch (interaction.type) {
case 2:
@@ -27,30 +29,38 @@ function handleCommand(interaction) {
async function handleSlash(interaction) {
ackTimer(interaction);
- interaction.command = interaction.data.name;
- interaction.author = interaction.member.user || interaction.user;
+ interaction.command = getSlashCommand(interaction);
+ interaction.author = interaction.member?.user || interaction.user;
interaction.interaction = true;
interaction.acked = false;
- interaction.options = getInteractionArgs(interaction);
+ interaction.options = getInteractionArgs(interaction.data, interaction.data.resolved);
interaction.args = [];
this.command.executeInteraction(interaction);
}
-function getInteractionArgs(interaction) {
- // console.log(interaction.data.options);
- const result = {};
- interaction.data.options?.forEach((option) => {
+function getSlashCommand(interaction) {
+ let command = commandGroups.interactionToCommand(interaction.data);
+ return command || interaction.data.name;
+}
+
+function getInteractionArgs(interaction, resolved, result = {}) {
+ interaction.options?.forEach((option) => {
+ /* eslint-disable no-fallthrough */
switch (option.type) {
// User
case 6:
- result[option.name] = interaction.data.resolved.members.get(option.value);
+ result[option.name] = resolved.users.get(option.value);
break;
// Sub command
case 2:
- // console.log(option);
+ // Command
+ case 1:
+ getInteractionArgs(option, resolved, result);
break;
// Number
case 4:
+ // String
+ case 3:
result[option.name] = option.value;
break;
}
diff --git a/src/eventHandlers/rawWS.js b/src/eventHandlers/rawWS.js
index ceedd23f..ccfc3da8 100644
--- a/src/eventHandlers/rawWS.js
+++ b/src/eventHandlers/rawWS.js
@@ -59,7 +59,7 @@ class Interaction {
id: data.channel_id,
};
- const author = data.member.user;
+ const author = data.member?.user || data.user;
if (author.discriminator !== '0000') {
this.author = bot.users.update(author, bot);
} else {
diff --git a/src/utils/animalInfoUtil.js b/src/utils/animalInfoUtil.js
index a0cb12e9..06c0b724 100644
--- a/src/utils/animalInfoUtil.js
+++ b/src/utils/animalInfoUtil.js
@@ -27,6 +27,13 @@ class AnimalJson {
}
async initialize() {
+ if (!bot) {
+ return new Promise((res) => {
+ setTimeout(() => {
+ res(this.initialize());
+ }, 5000);
+ });
+ }
this.animalNameToKey = {};
this.animals = {};
this.order = [];
diff --git a/src/utils/ban.js b/src/utils/ban.js
index e0a88c0b..235e5acc 100644
--- a/src/utils/ban.js
+++ b/src/utils/ban.js
@@ -13,7 +13,7 @@ const timerEmoji = '⏱';
exports.check = async function (p, command) {
let channel = p.msg.channel.id;
- let guild = p.msg.channel.guild.id;
+ let guild = p.msg.channel.guild?.id || 0;
let author = p.msg.author.id;
if (cooldown[author + command]) return;
diff --git a/src/utils/commandGroups.js b/src/utils/commandGroups.js
new file mode 100644
index 00000000..97235bc4
--- /dev/null
+++ b/src/utils/commandGroups.js
@@ -0,0 +1,54 @@
+/*
+ * OwO Bot for Discord
+ * Copyright (C) 2024 Christopher Thai
+ * This software is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+ * For more information, see README.md and LICENSE
+ */
+
+const commandGroups = [
+ {
+ 'name': 'gen',
+ 'type': 1,
+ 'description': 'Generate some images',
+ 'options': [],
+ 'integration_types': [0, 1],
+ 'contexts': [0, 1, 2],
+ },
+];
+
+const commandMap = {};
+
+exports.addOption = function (commandName, names, options) {
+ const command = JSON.parse(JSON.stringify(findName(names[0], commandGroups)));
+ let innerCommand = command;
+
+ let innerMap = (commandMap[names[0]] = commandMap[names[0]] || {});
+
+ for (let i = 1; i < names.length; i++) {
+ innerCommand = findName(names[i], command.options);
+ innerMap = innerMap[names[i]] = innerMap[names[i]] || {};
+ }
+ innerCommand.options.push(options);
+ innerMap[options.name] = commandName;
+ return command;
+};
+
+exports.interactionToCommand = function (interaction) {
+ return interactionInMap(interaction, commandMap);
+};
+
+function interactionInMap(interaction, map) {
+ if (typeof map === 'string') {
+ return map;
+ }
+ const name = interaction.name;
+ if (map[name]) {
+ return interactionInMap(interaction.options[0], map[name]);
+ } else {
+ return false;
+ }
+}
+
+function findName(name, list) {
+ return list.find((ele) => ele.name === name);
+}
diff --git a/src/utils/global.js b/src/utils/global.js
index 3c7e9998..298ad90c 100644
--- a/src/utils/global.js
+++ b/src/utils/global.js
@@ -221,7 +221,7 @@ exports.cleanString = function (string) {
};
exports.isEmoji = function (string) {
- return /^$/gi.test(string.trim());
+ return /^$/gi.test(string?.trim());
};
exports.parseTime = function (diff) {
@@ -392,3 +392,29 @@ exports.selectRandom = function (array, total) {
}
}
};
+
+exports.getStealButton = async function (p, withComponent) {
+ const sql = `SELECT emoji_steal.guild FROM emoji_steal INNER JOIN user ON emoji_steal.uid = user.uid WHERE id = ${p.msg.author.id};`;
+ const canSteal = (await p.query(sql))[0]?.guild;
+ if (!canSteal) {
+ return;
+ }
+ const components = [
+ {
+ type: 1,
+ components: [
+ {
+ type: 2,
+ label: 'Steal',
+ style: 1,
+ custom_id: 'steal',
+ emoji: {
+ id: null,
+ name: p.config.emoji.steal,
+ },
+ },
+ ],
+ },
+ ];
+ return withComponent ? components : components[0].components;
+};
diff --git a/src/utils/interactionCollector.js b/src/utils/interactionCollector.js
index a504e6bd..6ad87cf8 100644
--- a/src/utils/interactionCollector.js
+++ b/src/utils/interactionCollector.js
@@ -23,11 +23,13 @@ class InteractionCollector {
return iee;
}
- interact({ member, message, data, id, token, entitlements }) {
- member.id = member.user.id;
+ interact({ user, member, message, data, id, token, entitlements }) {
+ if (member) {
+ member.id = member.user.id;
+ }
const listener = this.listeners[message.id] || this.listeners[message.interaction?.id];
if (listener) {
- listener.interact(data, member, id, token, entitlements);
+ listener.interact(data, member || user, id, token, entitlements);
} else {
const url = `https://discord.com/api/v8/interactions/${id}/${token}/callback`;
const content = {
diff --git a/src/utils/logger.js b/src/utils/logger.js
index 31b7536a..63c7ad4f 100644
--- a/src/utils/logger.js
+++ b/src/utils/logger.js
@@ -118,7 +118,7 @@ exports.logstash = function (command, p) {
user: p.msg.author.id,
command: command,
text: p.msg.content,
- guild: p.msg.channel.guild.id,
+ guild: p.msg.channel.guild?.id | 'dm',
};
request(
diff --git a/utils/registerAppCommands.js b/utils/registerAppCommands.js
new file mode 100644
index 00000000..3de269fb
--- /dev/null
+++ b/utils/registerAppCommands.js
@@ -0,0 +1,170 @@
+/*
+ * OwO Bot for Discord
+ * Copyright (C) 2024 Christopher Thai
+ * This software is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
+ * For more information, see README.md and LICENSE
+ */
+
+// Run this file to update all application commands
+require('dotenv').config();
+const axios = require('axios');
+const requireDir = require('require-dir');
+const dir = requireDir('../src/commands/commandList', { recurse: true });
+const CommandInterface = require('../src/commands/CommandInterface.js');
+
+const url = `https://discord.com/api/v8/applications/${process.env.CLIENT_ID}/commands`;
+const headers = {
+ 'Authorization': `Bot ${process.env.CLIENT_SECRET}`,
+};
+
+const newCommands = {};
+const currCommands = {};
+const uniqueCommands = {};
+
+async function run() {
+ let result = await axios({
+ method: 'GET',
+ headers,
+ url,
+ });
+ parseAppCommands(currCommands, result.data);
+
+ for (let key in uniqueCommands) {
+ if (isDiff(newCommands[key], currCommands[key])) {
+ if (!newCommands[key]) {
+ console.log(`Deleting command: ${key}`);
+ await deleteCommand(currCommands[key].id);
+ } else if (!currCommands[key]) {
+ console.log(`Adding command: ${key}`);
+ await addCommand(newCommands[key]);
+ } else {
+ console.log(`Editing command: ${key}`);
+ await editCommand(newCommands[key], currCommands[key].id);
+ }
+ }
+ }
+
+ process.exit();
+}
+
+function init() {
+ for (let key in dir) {
+ if (dir[key] instanceof CommandInterface) {
+ parseAppCommands(newCommands, dir[key].appCommands);
+ } else if (Array.isArray(dir[key])) {
+ dir[key].forEach((val) => {
+ if (val instanceof CommandInterface) {
+ parseAppCommands(newCommands, val.appCommands);
+ }
+ });
+ } else {
+ for (let key2 in dir[key]) {
+ if (dir[key][key2] instanceof CommandInterface) {
+ parseAppCommands(newCommands, dir[key][key2].appCommands);
+ } else if (Array.isArray(dir[key][key2])) {
+ dir[key][key2].forEach((val) => {
+ if (val instanceof CommandInterface) {
+ parseAppCommands(newCommands, val.appCommands);
+ }
+ });
+ }
+ }
+ }
+ }
+}
+
+function parseAppCommands(dict, commands) {
+ commands?.forEach((appCommand) => {
+ if (!appCommand.type) {
+ // Default to slash command
+ appCommand.type = 1;
+ }
+ const key = getKey(appCommand);
+ if (dict[key]) {
+ appendOptions(dict[key], appCommand);
+ } else {
+ dict[key] = appCommand;
+ uniqueCommands[key] = true;
+ }
+ });
+}
+
+function appendOptions(baseCommand, newCommand) {
+ const depth = getDepth(baseCommand, newCommand);
+ let options = baseCommand.options;
+ let append = newCommand.options;
+ for (let i = 1; i < depth; i++) {
+ options = options[0].options;
+ append = append[0].options;
+ }
+ options.push(...append);
+}
+
+function getDepth(baseCommand, newCommand, depth = 0) {
+ if (baseCommand.name === newCommand.name) {
+ depth++;
+ return getDepth(baseCommand.options[0], newCommand.options[0], depth);
+ } else {
+ return depth;
+ }
+}
+
+function getKey(command) {
+ return `${command.name}-${command.type}`;
+}
+
+function isDiff(newCommand, currCommand) {
+ if (typeof newCommand !== typeof currCommand) {
+ return true;
+ } else if (typeof newCommand !== 'object') {
+ if (newCommand !== currCommand) {
+ return true;
+ }
+ } else if (Array.isArray(newCommand)) {
+ if (!Array.isArray(currCommand) || newCommand.length !== currCommand.length) {
+ return true;
+ } else {
+ for (let i = 0; i < newCommand.length; i++) {
+ if (isDiff(newCommand[i], currCommand[i])) {
+ return true;
+ }
+ }
+ }
+ } else {
+ for (const key in newCommand) {
+ if (isDiff(newCommand[key], currCommand[key])) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+async function deleteCommand(commandId) {
+ return axios({
+ method: 'DELETE',
+ headers,
+ url: `${url}/${commandId}`,
+ });
+}
+
+async function addCommand(command) {
+ return axios({
+ method: 'POST',
+ headers,
+ url,
+ data: command,
+ });
+}
+
+async function editCommand(command, commandId) {
+ return axios({
+ method: 'PATCH',
+ headers,
+ url: `${url}/${commandId}`,
+ data: command,
+ });
+}
+
+init();
+run();