Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement action row buttons to delete the original message if replying, to embed in manual mode #3

Merged
merged 17 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
config.json
config.json
embetter.db
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,7 @@ dist
.yarn/install-state.gz
.pnp.*

config.json
config.json
config.development.json
config.production.json
embetter.db
3 changes: 2 additions & 1 deletion config.example.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"token": "",
"clientId": "",
"guildId": ""
"guildId": "",
"commandId": ""
}
18 changes: 18 additions & 0 deletions delete-commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { REST, Routes } = require('discord.js');
const { clientId, commandId, guildId, token } = require('./config.json');

const rest = new REST().setToken(token);

(async () => {
try {
console.log(`Started deleting ${commandId} application (/) command.`);
const route = process.argv[2] === 'develop'
? Routes.applicationGuildCommand(clientId, guildId, commandId)
: Routes.applicationCommand(clientId, commandId);
await rest.delete(route);
console.log(`Successfully deleted ${commandId} application (/) command.`);
}
catch (error) {
console.error(error);
}
})();
75 changes: 46 additions & 29 deletions deploy-commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,56 @@ const { REST, Routes } = require('discord.js');
const { clientId, guildId, token } = require('./config.json');
const fs = require('node:fs');
const path = require('node:path');
const { SlashCommandBuilder, ContextMenuCommandBuilder } = require('discord.js');

const commands = [];
const foldersPath = path.join(__dirname, 'src/commands');
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));

for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
commands.push(command.data.toJSON());
}
else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
const contextCommands = [];
const slashCommands = [];
const commandsDir = path.join(__dirname, 'src/commands');

function loadCommands(dir) {
const files = fs.readdirSync(dir);

for (let file of files) {
let fullPath = path.join(dir, file);

// Check if path is directory
if (fs.lstatSync(fullPath).isDirectory()) {
// Skip node_modules directory
if (file === 'node_modules') continue;
loadCommands(fullPath); // Recurse into directories
} else if (file.endsWith('.js')) { // Check if file is a JavaScript file
const command = require(fullPath);
if ('data' in command && 'execute' in command) {
if (command.data instanceof SlashCommandBuilder) {
slashCommands.push(command.data.toJSON());
} else if (command.data instanceof ContextMenuCommandBuilder) {
contextCommands.push(command.data.toJSON());
}
} else {
console.log(`[WARNING] The command at ${fullPath} is missing a required "data" or "execute" property.`);
}
}
}
}

// Start recursion from root directories
loadCommands(commandsDir);

const rest = new REST().setToken(token);

const allCommands = [...slashCommands, ...contextCommands];

(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands.`);
const route = process.argv[2] === 'develop'
? Routes.applicationGuildCommands(clientId, guildId)
: Routes.applicationCommands(clientId);
const data = await rest.put(route, { body: commands });
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
}
catch (error) {
console.error(error);
}
try {
console.log(`Started refreshing ${slashCommands.length} application (/) commands and ${contextCommands.length} context commands.`);
const route = process.argv[2] === 'develop'
? Routes.applicationGuildCommands(clientId, guildId)
: Routes.applicationCommands(clientId);
const responseData = await rest.put(route, { body: allCommands });
console.log(`Successfully reloaded ${responseData.length} application (/) commands and context commands.`);
}
catch (error) {
console.error(error);
}
})();

54 changes: 27 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,36 @@ const client = new Client({ intents: [
] });

client.commands = new Collection();
const foldersPath = path.join(__dirname, 'src/commands');
const commandFolders = fs.readdirSync(foldersPath);

for (const folder of commandFolders) {
const commandsPath = path.join(foldersPath, folder);
const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js'));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
if ('data' in command && 'execute' in command) {
client.commands.set(command.data.name, command);
}
else {
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
}
}
}
// General function to load items (commands or events)
function loadItems(dir, type) {
const files = fs.readdirSync(dir);

const eventsPath = path.join(__dirname, 'src/events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
for (let file of files) {
let fullPath = path.join(dir, file);

for (const file of eventFiles) {
const filePath = path.join(eventsPath, file);
const event = require(filePath);
if (event.once) {
client.once(event.name, (...args) => event.execute(...args));
}
else {
client.on(event.name, (...args) => event.execute(...args));
// Check if path is directory
if (fs.lstatSync(fullPath).isDirectory()) {
loadItems(fullPath, type); // Recurse into directories
} else if (file.endsWith('.js')) { // Check if file is a JavaScript file
const item = require(fullPath);
if (type === 'commands' && 'data' in item && 'execute' in item) {
client.commands.set(item.data.name, item);
} else if (type === 'events') {
if (item.once) {
client.once(item.name, (...args) => item.execute(...args));
} else {
client.on(item.name, (...args) => item.execute(...args));
}
} else {
console.log(`[WARNING] The ${type} at ${fullPath} is missing required properties.`);
}
}
}
}

client.login(token);
// Start recursion from root directories
loadItems(path.join(__dirname, 'src/commands'), 'commands');
loadItems(path.join(__dirname, 'src/events'), 'events');

client.login(token);
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@
"description": "Embed better with embetter. Replaces Instagram, TikTok, and Twitter links on Discord with embed-friendly options.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node .",
"lint": "eslint ."
},
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node .",
"lint": "eslint ."
},
"repository": "https://github.com/everettsouthwick/embetter.git",
"author": "Everett Southwick <everett@southwick.dev>",
"license": "GPL-3.0",
"dependencies": {
"discord.js": "^14.11.0"
"discord.js": "^14.11.0",
"sqlite3": "^5.1.6"
},
"devDependencies": {
"eslint": "^8.44.0"
Expand Down
18 changes: 18 additions & 0 deletions src/commands/context/utility/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { ContextMenuCommandBuilder, ApplicationCommandType } = require('discord.js');
const replaceLink = require('../../../utils/linkReplacer.js');

module.exports = {
data: new ContextMenuCommandBuilder()
.setName('Embed')
.setType(ApplicationCommandType.Message),
async execute(interaction) {
const { links } = replaceLink(interaction.targetMessage.content);
if (links.length > 0) {
// If there are multiple links, join them with a newline for readability
interaction.reply(links.join('\n'));
}
else {
interaction.reply('No valid link was found.');
}
},
};
10 changes: 0 additions & 10 deletions src/commands/fun/ping.js

This file was deleted.

23 changes: 23 additions & 0 deletions src/commands/slash/utility/embed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const { SlashCommandBuilder } = require('discord.js');
const replaceLink = require('../../../utils/linkReplacer.js');

module.exports = {
data: new SlashCommandBuilder()
.setName('embed')
.setDescription('Attempts to embed the provided link(s).')
.addStringOption(option => option
.setName('link')
.setDescription('Space separated list of link(s) to embed.')
.setRequired(true)),
async execute(interaction) {
// interaction.guild is the object representing the Guild in which the command was run
const { links } = replaceLink(interaction.options.getString('link'));
if (links.length > 0) {
// If there are multiple links, join them with a newline for readability
interaction.reply(links.join('\n'));
}
else {
interaction.reply('No valid link was found.');
}
},
};
25 changes: 25 additions & 0 deletions src/commands/slash/utility/embedMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { SlashCommandBuilder, PermissionFlagsBits } = require('discord.js');
const { setGuildMode } = require('../../../utils/db.js');
const EmbedMode = require('../../../utils/embedMode.js');

module.exports = {
data: new SlashCommandBuilder()
.setName('embedmode')
.setDescription('Sets the mode for embeds. By default, this is set to reply.')
.addIntegerOption(option => option
.setName('mode')
.setDescription('Replace: Delete and resend, Reply: Keep and send, Manual: Do nothing.')
.setRequired(true)
.addChoices(
{ name: 'Replace', value: EmbedMode.REPLACE },
{ name: 'Reply', value: EmbedMode.REPLY },
{ name: 'Manual', value: EmbedMode.MANUAL },
))
.setDefaultMemberPermissions(PermissionFlagsBits.ManageGuild)
.setDMPermission(false),
async execute(interaction) {
// interaction.guild is the object representing the Guild in which the command was run
setGuildMode(interaction.guild.id, interaction.options.getInteger('mode'));
await interaction.reply({ content: `You have selected ${EmbedMode.getModeName(interaction.options.getInteger('mode'))}.`, ephemeral: true });
},
};
11 changes: 0 additions & 11 deletions src/commands/utility/server.js

This file was deleted.

12 changes: 0 additions & 12 deletions src/commands/utility/user.js

This file was deleted.

11 changes: 11 additions & 0 deletions src/events/guildCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const { Events } = require('discord.js');
const EmbedMode = require('../utils/embedMode.js');
const { setGuildMode } = require('../utils/db.js');


module.exports = {
name: Events.GuildCreate,
execute(guild) {
setGuildMode(guild.id, EmbedMode.REPLY);
},
};
26 changes: 13 additions & 13 deletions src/events/interactionCreate.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ const { Events } = require('discord.js');
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
if (interaction.isChatInputCommand() || interaction.isContextMenuCommand()) {
const command = interaction.client.commands.get(interaction.commandName);

const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}

if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}

try {
await command.execute(interaction);
}
catch (error) {
console.error(`Error executing ${interaction.commandName}`);
console.error(error);
try {
await command.execute(interaction);
}
catch (error) {
console.error(`Error executing ${interaction.commandName}`);
console.error(error);
}
}
},
};
12 changes: 4 additions & 8 deletions src/events/messageCreate.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
const { Events } = require('discord.js');
const replaceLink = require('../utils/linkReplacer.js');
const handleLinkMessage = require('../utils/handleLinkMessage.js');

module.exports = {
name: Events.MessageCreate,
execute(message) {
async execute(message) {
if (!message.author.bot) {
const newMessage = replaceLink(message.content);
if (newMessage != message.content) {
message.delete();
message.channel.send(`${message.author}: ${newMessage}`);
}
handleLinkMessage(message);
}
},
};
};
Loading
Loading