First commit for dscrdbt

This commit is contained in:
Ignacio PS 2025-04-15 16:15:56 -06:00
commit 11a100eacf
13 changed files with 482 additions and 0 deletions

50
.gitignore vendored Normal file
View File

@ -0,0 +1,50 @@
# Dependencies
node_modules/
package-lock.json
yarn.lock
# Environment variables
.env
.env.local
.env.*.local
# Build output
dist/
build/
out/
# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*.swn
.DS_Store
# Runtime data
pids/
*.pid
*.seed
*.pid.lock
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity

View File

@ -0,0 +1,59 @@
const { SlashCommandBuilder, ActionRowBuilder, ModalBuilder, TextInputBuilder, TextInputStyle } = require('discord.js');
const fs = require('fs');
const path = require('path');
// Configuration
const ALLOWED_CHANNEL_ID = '1354951205180150004';
module.exports = {
data: new SlashCommandBuilder()
.setName('aipost')
.setDescription('Generate an Instagram post from a news article'),
async execute(interaction) {
// Check if the command is used in the allowed channel
if (interaction.channelId !== ALLOWED_CHANNEL_ID) {
return await interaction.reply({
content: 'This command can only be used in the designated channel.',
ephemeral: true,
});
}
// Modal creation
const modal = new ModalBuilder()
.setCustomId('aipostModal')
.setTitle('News Link');
// Text input component
const newsLinkInput = new TextInputBuilder()
.setCustomId('newsLinkInput')
.setLabel('Please provide the news article URL')
.setStyle(TextInputStyle.Short)
.setPlaceholder('https://example.com/news-article')
.setRequired(true);
// Add text input to the modal
const firstActionRow = new ActionRowBuilder().addComponents(newsLinkInput);
modal.addComponents(firstActionRow);
// Make the modal visible to the user
await interaction.showModal(modal);
},
// Handle the modal submission
async modalSubmit(interaction) {
if (interaction.customId === 'aipostModal') {
// Acknowledge the interaction
await interaction.deferReply();
// Placeholder for AI post generation
await interaction.editReply({
content: 'Here is your generated Instagram post:\n\n' +
'📸 *[Generated Image Placeholder]*\n\n' +
'📝 **Generated Caption:**\n' +
'Exciting news in tech! 🚀\n' +
'Stay updated with the latest trends!\n\n' +
'#tech #innovation #news #trending #instagram',
});
}
},
};

13
commands/utility/ping.js Normal file
View File

@ -0,0 +1,13 @@
const { SlashCommandBuilder, MessageFlags } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('ping')
.setDescription('Replies with Pong!'),
async execute(interaction) {
await interaction.reply({
content: 'Secret Pong!',
flags: MessageFlags.Ephemeral
});
},
};

View File

@ -0,0 +1,11 @@
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('server')
.setDescription('Provides information about the server.'),
async execute(interaction) {
await interaction.reply(`This server is ${interaction.guild.name} and has
${interaction.guild.memberCount} members.`);
},
};

11
commands/utility/user.js Normal file
View File

@ -0,0 +1,11 @@
const { SlashCommandBuilder } = require('discord.js');
module.exports = {
data: new SlashCommandBuilder()
.setName('user')
.setDescription('Provides information about the user.'),
async execute(interaction) {
await interaction.reply(`This command was run by ${interaction.user.username},
who joined on ${interaction.member.joinedAt}.`);
},
};

39
deploy-commands.js Normal file
View File

@ -0,0 +1,39 @@
const { REST, Routes } = require('discord.js');
require('dotenv').config();
const fs = require('fs');
const path = require('path');
const commands = [];
const foldersPath = path.join(__dirname, '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 rest = new REST().setToken(process.env.DISCORD_TOKEN);
(async () => {
try {
console.log(`Started refreshing ${commands.length} application (/) commands`);
const data = await rest.put(
Routes.applicationGuildCommands(process.env.clientId, process.env.guildId),
{ body: commands },
);
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
} catch (error) {
console.error(error);
}
})();

50
eslint.config.js Normal file
View File

@ -0,0 +1,50 @@
const js = require('@eslint/js');
module.exports = [
js.configs.recommended,
{
languageOptions: {
ecmaVersion: 'latest',
},
rules: {
'arrow-spacing': ['warn', { before: true, after: true }],
'brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
'comma-dangle': ['error', 'always-multiline'],
'comma-spacing': 'error',
'comma-style': 'error',
curly: ['error', 'multi-line', 'consistent'],
'dot-location': ['error', 'property'],
'handle-callback-err': 'off',
indent: ['error', 'tab'],
'keyword-spacing': 'error',
'max-nested-callbacks': ['error', { max: 4 }],
'max-statements-per-line': ['error', { max: 2 }],
'no-console': 'off',
'no-empty-function': 'error',
'no-floating-decimal': 'error',
'no-inline-comments': 'error',
'no-lonely-if': 'error',
'no-multi-spaces': 'error',
'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1, maxBOF: 0 }],
'no-shadow': ['error', { allow: ['err', 'resolve', 'reject'] }],
'no-trailing-spaces': ['error'],
'no-var': 'error',
'no-undef': 'off',
'object-curly-spacing': ['error', 'always'],
'prefer-const': 'error',
quotes: ['error', 'single'],
semi: ['error', 'always'],
'space-before-blocks': 'error',
'space-before-function-paren': ['error', {
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
}],
'space-in-parens': 'error',
'space-infix-ops': 'error',
'space-unary-ops': 'error',
'spaced-comment': 'error',
yoda: 'error',
},
},
];

View File

@ -0,0 +1,59 @@
const { Events } = require('discord.js');
const { generateCaption, generateImage, generatePostImage } = require('../utils/newsProcessor');
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (!interaction.isModalSubmit()) return;
if (interaction.customId !== 'aipostModal') return;
try {
const newsLink = interaction.fields.getTextInputValue('newsLinkInput');
await interaction.deferReply();
// Process the article with status updates
// await interaction.editReply('📰 Reading article...');
// const articleData = await scrapeArticle(newsLink);
await interaction.editReply('✍️ Generating image copy...');
const imageText = await generateImage(newsLink);
await interaction.editReply('🎨 Creating AI image...');
const imageUrl = await generatePostImage(imageText);
await interaction.editReply('📝 Generating Instagram post...');
const postText = await generateCaption(newsLink);
// First message with the AI-generated image and its copy
await interaction.editReply({
content: `📸 **Generated Image Copy:**\n${imageText}`,
files: [imageUrl],
});
// Second message with the Instagram post copy
await interaction.followUp({
content: `📱 **Generated Instagram Post:**\n${postText}`,
});
}
catch (error) {
console.error('Error in modal submission:', error);
const errorMessage = error.message.includes('Failed to')
? error.message
: 'An unexpected error occurred while generating the post.';
if (interaction.deferred) {
await interaction.editReply({
content: `${errorMessage}\nPlease try again or use a different article.`,
ephemeral: true,
});
}
else {
await interaction.reply({
content: `${errorMessage}\nPlease try again or use a different article.`,
ephemeral: true,
});
}
}
},
};

View File

@ -0,0 +1,33 @@
const { Events } = require('discord.js');
module.exports = {
name: Events.InteractionCreate,
async execute(interaction) {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
}
catch (error) {
console.error(error);
if (interaction.replied || interaction.deferred) {
await interaction.followUp({
content: 'There was an error while executing this command',
ephemeral: true,
});
}
else {
await interaction.reply({
content: 'There was an error while executing this command!',
ephemeral: true,
});
}
}
},
};

9
events/ready.js Normal file
View File

@ -0,0 +1,9 @@
const { Events } = require('discord.js');
module.exports = {
name: Events.ClientReady,
once: true,
execute(client) {
console.log(`Ready! Logged in as ${client.user.tag}`);
},
};

47
index.js Normal file
View File

@ -0,0 +1,47 @@
const { Client, GatewayIntentBits, Collection } = require('discord.js');
const fs = require('fs');
const path = require('path');
require('dotenv').config();
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
client.commands = new Collection();
const foldersPath = path.join(__dirname, '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.`);
}
}
}
const eventsPath = path.join(__dirname, 'events');
const eventFiles = fs.readdirSync(eventsPath).filter(file => file.endsWith('.js'));
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));
}
}
client.login(process.env.DISCORD_TOKEN);

21
package.json Normal file
View File

@ -0,0 +1,21 @@
{
"name": "dscrdbt",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node src/index.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"discord.js": "^14.18.0",
"dotenv": "^16.4.7",
"openai": "^4.90.0"
},
"devDependencies": {
"@eslint/js": "^9.23.0",
"eslint": "^9.23.0"
}
}

80
utils/newsProcessor.js Normal file
View File

@ -0,0 +1,80 @@
const OpenAI = require('openai');
// Initialize OpenAI client
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
// Function to generate Instagram caption
async function generateCaption(articleData) {
try {
const prompt = `Act as a community manager for a technology and software community. Your job is to create content for the community's social media.
Give me the copy for the Instagram post.
Here is the information:
URL: ${articleData}
The copy for the Instagram post should be informative and educational, written in a friendly and approachable tone that invites conversation and interaction.
Try to relate the information to technology and software.
Make sure to include hashtags following Instagram and marketing best practices.`;
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: prompt }],
max_tokens: 500,
temperature: 0.7,
});
return completion.choices[0].message.content.trim();
}
catch (error) {
throw new Error(`Failed to generate caption: ${error.message}`);
}
}
// Function to generate image copy
async function generateImage(articleData) {
try {
const prompt = `Act as a community manager for a technology and software community. Your job is to create content for the community's social media.
Give me the copy for the image.
Here is the information:
URL: ${articleData}
The copy for the image should be engaging and attention-grabbing to encourage people to click and read the post.`;
const completion = await openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: prompt }],
max_tokens: 100,
temperature: 0.7,
});
return completion.choices[0].message.content.trim();
}
catch (error) {
throw new Error(`Failed to generate image copy: ${error.message}`);
}
}
// Function to generate image using DALL-E
async function generatePostImage(imageText) {
try {
const prompt = `Create a modern, professional social media image that represents: ${imageText}. The image should be suitable for a technology and software community, with a clean and engaging design.`;
const response = await openai.images.generate({
model: 'dall-e-3',
prompt: prompt,
n: 1,
quality: 'standard',
size: '1024x1024',
});
return response.data[0].url;
}
catch (error) {
throw new Error(`Failed to generate image with DALL-E: ${error.message}`);
}
}
module.exports = {
generateCaption,
generateImage,
generatePostImage,
};