aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Lee <andrew@alee14.me>2025-01-11 18:02:05 -0500
committerAndrew Lee <andrew@alee14.me>2025-01-11 18:02:05 -0500
commit7e65ae0e135098acad76b8081f34478b4efc077f (patch)
treeb7efc060fca4257a875a5a3e525e2733b6579040
parentf5de90ba89146008af78c16e798e216efccf0c50 (diff)
downloadAleeBot-7e65ae0e135098acad76b8081f34478b4efc077f.tar.gz
AleeBot-7e65ae0e135098acad76b8081f34478b4efc077f.tar.bz2
AleeBot-7e65ae0e135098acad76b8081f34478b4efc077f.zip
Cleaned up some code, author image now support attachments
-rw-r--r--api/routes/quotes.js58
-rw-r--r--api/server.js56
-rw-r--r--commands/addquote.js87
-rw-r--r--commands/quote.js4
-rw-r--r--commands/setup.js3
-rw-r--r--models/quote.js20
-rw-r--r--storage/activities.js1
-rw-r--r--web/astro.config.mjs2
-rw-r--r--web/src/components/Quotes.jsx1
-rw-r--r--web/src/layouts/Layout.astro2
-rw-r--r--web/src/pages/index.astro19
11 files changed, 159 insertions, 94 deletions
diff --git a/api/routes/quotes.js b/api/routes/quotes.js
new file mode 100644
index 0000000..39aba7b
--- /dev/null
+++ b/api/routes/quotes.js
@@ -0,0 +1,58 @@
+const express = require('express');
+const quoteDB = require('../../models/quote.js');
+
+const router = express.Router();
+
+const pendingQuote = quoteDB.pendingQuote;
+const approvedQuote = quoteDB.quote;
+
+router.get('/pending-quotes', async (req, res) => {
+ try {
+ const quotes = await pendingQuote.findAll();
+ res.json(quotes);
+ } catch (error) {
+ console.error('Error fetching quotes:', error);
+ res.status(500).send('Internal Server Error');
+ }
+});
+
+router.post('/approve-quote', async (req, res) => {
+ const { id } = req.body;
+ try {
+ const quote = await pendingQuote.findByPk(id);
+ if (quote) {
+ await approvedQuote.create({
+ author: quote.author,
+ authorImage: quote.authorImage,
+ quote: quote.quote,
+ year: quote.year,
+ submitter: quote.submitterID
+ });
+ await pendingQuote.destroy({ where: { id } });
+ res.status(200).send('Quote approved');
+ } else {
+ res.status(404).send('Quote not found');
+ }
+ } catch (error) {
+ console.error('Error approving quote:', error);
+ res.status(500).send('Internal Server Error');
+ }
+});
+
+router.post('/reject-quote', async (req, res) => {
+ const { id } = req.body;
+ try {
+ const quote = await pendingQuote.findByPk(id);
+ if (quote) {
+ await pendingQuote.destroy({ where: { id } });
+ res.status(200).send('Quote rejected');
+ } else {
+ res.status(404).send('Quote not found');
+ }
+ } catch (error) {
+ console.error('Error rejecting quote:', error);
+ res.status(500).send('Internal Server Error');
+ }
+});
+
+module.exports = router;
diff --git a/api/server.js b/api/server.js
index a918308..a6cb48c 100644
--- a/api/server.js
+++ b/api/server.js
@@ -1,6 +1,6 @@
const express = require('express');
const cors = require('cors');
-const { pendingQuote, quote: approvedQuote } = require('../models/quote.js');
+const quotesRouter = require('./routes/quotes');
const app = express();
const PORT = 3000;
@@ -9,54 +9,7 @@ const createServer = () => {
app.use(cors()); // Allow cross-origin requests
app.use(express.json());
- // Endpoint to get all pending quotes
- app.get('/api/pending-quotes', async (req, res) => {
- try {
- const quotes = await pendingQuote.findAll();
- res.json(quotes);
- } catch (error) {
- console.error('Error fetching quotes:', error);
- res.status(500).send('Internal Server Error');
- }
- });
-
- app.post('/api/approve-quote', async (req, res) => {
- const { id } = req.body;
- try {
- const quote = await pendingQuote.findByPk(id);
- if (quote) {
- await approvedQuote.create({
- author: quote.author,
- quote: quote.quote,
- year: quote.year,
- authorImage: quote.authorImage
- });
- await pendingQuote.destroy({ where: { id } });
- res.status(200).send('Quote approved');
- } else {
- res.status(404).send('Quote not found');
- }
- } catch (error) {
- console.error('Error approving quote:', error);
- res.status(500).send('Internal Server Error');
- }
- });
-
- app.post('/api/reject-quote', async (req, res) => {
- const { id } = req.body;
- try {
- const quote = await pendingQuote.findByPk(id);
- if (quote) {
- await pendingQuote.destroy({ where: { id } });
- res.status(200).send('Quote rejected');
- } else {
- res.status(404).send('Quote not found');
- }
- } catch (error) {
- console.error('Error rejecting quote:', error);
- res.status(500).send('Internal Server Error');
- }
- });
+ app.use('/api', quotesRouter);
app.get('/api/version', (req, res) => {
const { abVersion } = require('../storage/settings.json');
@@ -64,11 +17,6 @@ const createServer = () => {
});
- app.get('/' , (req, res) => {
- res.send('API for AleeBot');
- // Most likely going to redirect to the frontend
- });
-
// Start the server
app.listen(PORT, () => {
console.log(`Server is running on http://localhost:${PORT}`);
diff --git a/commands/addquote.js b/commands/addquote.js
index 52f7b21..f13d3c9 100644
--- a/commands/addquote.js
+++ b/commands/addquote.js
@@ -20,14 +20,21 @@
const { pendingQuote } = require('../models/quote');
const { MessageEmbed } = require("discord.js");
+const setupUsers = new Set();
+
module.exports.run = async (client, message) => {
try {
let newAuthor, newAuthorImage, newQuote, newYear;
- let isSetupRunning = false;
+
+ if (setupUsers.has(message.author.id)) {
+ return await message.reply('You are already setting up a quote.');
+ }
+
+ setupUsers.add(message.author.id);
const setupProcess = [
'Provide the name of the author:',
- 'Submit the image of the author:\nYou need to use a picture link that ends in .jpg or .png (like those from IMGUR or Google Images), and the picture should be either 128x128 pixels or 512x512 pixels in size.',
+ 'Submit the image of the author:\nYou can use an attachment or a link that ends in .jpg/.jpeg or .png (like those from IMGUR or Google Images), and the picture should be either 128x128 pixels or 512x512 pixels in size.',
'Enter the quote:',
'Specify the year from which the quote originates:'
];
@@ -38,23 +45,38 @@ module.exports.run = async (client, message) => {
authorImage: newAuthorImage,
quote: newQuote,
year: newYear,
+ submitterAuthor: message.author.username,
+ submitterID: message.author.id
});
}
+ async function imageCheck(message) {
+ const attachment = message.attachments.first();
+ if (attachment) {
+ const fileExtension = attachment.name.split('.').pop().toLowerCase();
+ if (['jpg', 'png', 'jpeg'].includes(fileExtension)) {
+ newAuthorImage = attachment.url.toString(); // Use the attachment's URL directly
+ } else {
+ await dmChannel.send('Invalid file type. Please attach a .jpg or .png image.');
+ return await imageCheck(message);
+ }
+ } else if (msg.content.startsWith('http') && (message.content.endsWith('.jpg') || message.content.endsWith('.jpeg') || message.content.endsWith('.png'))) {
+ newAuthorImage = msg.content; // Use the provided URL
+ } else {
+ await dmChannel.send('Invalid input. Please provide an image URL or attach an image file.');
+ return await imageCheck(message);
+ }
+ }
+
let setupMessage = "Welcome to the AleeBot Quote Setup!\n";
setupMessage += "Please follow these rules when submitting quotes:\n";
- setupMessage += "```1. Do not use profanity or offensive language.\n";
+ setupMessage += "```1. No offensive content (NSFW, Racism, etc).\n";
setupMessage += "2. Do not send any personal information.\n";
setupMessage += "3. Only send noteworthy quotes.```\n";
setupMessage += "We reserve the right to reject any quotes that do not meet our criteria.\n";
- if (isSetupRunning) {
- return await message.reply('You are already setting up a quote.');
- }
-
const filter = (m) => m.author.id === message.author.id;
- isSetupRunning = true;
await message.reply(':arrow_left: Check DMs to continue.');
const dmChannel = await message.author.createDM();
@@ -65,10 +87,14 @@ module.exports.run = async (client, message) => {
const collector = dmChannel.createMessageCollector({
filter,
max: setupProcess.length,
- time: 1000 * 120
+ time: 1000 * 1200
});
- collector.on('collect', async () => {
+ collector.on('collect', async (msg) => {
+ if (counter === 2) { // Collecting author image
+ await imageCheck(msg);
+ }
+
if (counter < setupProcess.length) {
await dmChannel.send(setupProcess[counter++]);
}
@@ -77,20 +103,23 @@ module.exports.run = async (client, message) => {
collector.on('end', async (collected) => {
if (collected.size < setupProcess.length) {
dmChannel.send('Quote setup was not completed. Please rerun the command.');
+ setupUsers.delete(message.author.id);
} else {
const quoteContent = collected.map((m) => m.content);
newAuthor = quoteContent[0];
- newAuthorImage = quoteContent[1];
+ if (!newAuthorImage) {
+ newAuthorImage = quoteContent[1] || 'N/A';
+ }
newQuote = quoteContent[2];
newYear = quoteContent[3];
const setupEmbed = new MessageEmbed()
.setAuthor('AleeBot Quote Setup', client.user.avatarURL())
- .setDescription('Are you happy with this quote?\nThis quote will be sent for manual approval automatically in 2 minutes.')
- .addField('Author', newAuthor)
- .addField('Author Image (URL)', newAuthorImage)
- .addField('Quote', newQuote)
- .addField('Year', newYear)
+ .setDescription('Are you happy with this quote?\nThis quote will be sent for manual approval automatically in 20 minutes.')
+ .addField('Author', newAuthor || 'N/A')
+ .addField('Author Image (URL)', newAuthorImage || 'N/A')
+ .addField('Quote', newQuote || 'N/A')
+ .addField('Year', newYear || 'N/A')
.setColor('#1fd619');
let messageReact = await dmChannel.send({embeds: [setupEmbed]});
@@ -107,7 +136,7 @@ module.exports.run = async (client, message) => {
const reactionCollector = messageReact.createReactionCollector({
filter: reactionFilter,
- time: 1000 * 120
+ time: 1000 * 1200
});
reactionCollector.on('collect', async (reaction) => {
@@ -119,10 +148,10 @@ module.exports.run = async (client, message) => {
await dmChannel.send('Updated author name.');
break;
case '📷':
- await dmChannel.send('You selected the author image. Please provide the image URL.');
+ await dmChannel.send('You selected the author image. Please provide the image URL or attach an image file.');
const imageResponse = await dmChannel.awaitMessages({ filter, max: 1, time: 60000 });
- if (imageResponse.size) newAuthorImage = imageResponse.first().content;
- await dmChannel.send('Updated author URL.');
+ await imageCheck(imageResponse.first());
+ await dmChannel.send('Updated author image.');
break;
case '🖋️':
await dmChannel.send('You selected the quote. Please provide the quote.');
@@ -144,36 +173,26 @@ module.exports.run = async (client, message) => {
break;
}
- const updatedEmbed = new MessageEmbed()
- .setAuthor('AleeBot Quote Setup', client.user.avatarURL())
- .setDescription('Are you happy with this quote?\nThis quote will be sent for manual approval automatically in 2 minutes.')
- .addField('Author', newAuthor)
- .addField('Author Image (URL)', newAuthorImage)
- .addField('Quote', newQuote)
- .addField('Year', newYear)
- .setColor('#1fd619');
-
- await messageReact.edit({embeds: [updatedEmbed]});
+ await messageReact.edit({embeds: [setupEmbed]});
});
reactionCollector.on('end', async (collected, reason) => {
if (reason === 'cancelled') {
- isSetupRunning = false;
dmChannel.send('Cancelling quote setup.');
} else if (reason === 'completed') {
dmChannel.send('Sending this quote for manual approval.');
- isSetupRunning = false;
await createQuote();
} else {
dmChannel.send('You have not responded. Sending this quote for manual approval.');
- isSetupRunning = false;
await createQuote();
}
+ setupUsers.delete(message.author.id);
});
-
}
});
} catch (error) {
+ message.author.send('An error occurred while setting up the quote. Please try again.');
+ setupUsers.delete(message.author.id);
console.error(error);
}
};
diff --git a/commands/quote.js b/commands/quote.js
index 02699cd..0d67b66 100644
--- a/commands/quote.js
+++ b/commands/quote.js
@@ -38,6 +38,10 @@ module.exports.run = async (client, message, args) => {
.setColor('#1fd619')
.setFooter('- ' + quote.year);
+ if (quote.url) {
+ quoteEmbed.setURL(quote.url);
+ }
+
await message.reply({ embeds: [quoteEmbed] })
} else {
message.reply('Cannot find quote, specify the correct quote id.');
diff --git a/commands/setup.js b/commands/setup.js
index 380e85e..c797f7d 100644
--- a/commands/setup.js
+++ b/commands/setup.js
@@ -27,7 +27,8 @@ module.exports.run = async (client, message) => {
.setDescription('Select the options')
.addField('Logging', 'channelid', true)
.addField('Broadcast', 'placeholder', true)
- .addField('Broadcast', 'placeholder', true);
+ .addField('Quote of the Day', 'placeholder', true)
+ .addField('QOTD Channel', 'channelid', true);
message.author.send({embeds: [setupEmbed]});
};
diff --git a/models/quote.js b/models/quote.js
index eab4f57..b68dfbc 100644
--- a/models/quote.js
+++ b/models/quote.js
@@ -22,6 +22,14 @@ const quote = sequelize.define('quotes', {
year: {
type: Sequelize.STRING,
allowNull: false
+ },
+ url: {
+ type: Sequelize.STRING,
+ allowNull: true
+ },
+ submitter: {
+ type: Sequelize.STRING,
+ allowNull: false
}
})
@@ -47,6 +55,18 @@ const pendingQuote = sequelize.define('pending-quotes', {
year: {
type: Sequelize.STRING,
allowNull: false
+ },
+ url: {
+ type: Sequelize.STRING,
+ allowNull: true
+ },
+ submitterAuthor: {
+ type: Sequelize.STRING,
+ allowNull: false
+ },
+ submitterID: {
+ type: Sequelize.STRING,
+ allowNull: false
}
})
diff --git a/storage/activities.js b/storage/activities.js
index 5d0161e..be28e58 100644
--- a/storage/activities.js
+++ b/storage/activities.js
@@ -91,7 +91,6 @@ const activities = [
'What is Web3?',
'GNU\'s NOT UNIX!',
'Linux, but actually GNU/Linux',
- 'Praise RMS! (dont)',
'Debloating my ThinkPad',
'Turbotastic!',
`Now running on Discord.JS ${version}!`
diff --git a/web/astro.config.mjs b/web/astro.config.mjs
index 53e49de..515ddf0 100644
--- a/web/astro.config.mjs
+++ b/web/astro.config.mjs
@@ -6,4 +6,4 @@ import react from '@astrojs/react';
// https://astro.build/config
export default defineConfig({
integrations: [react()]
-}); \ No newline at end of file
+});
diff --git a/web/src/components/Quotes.jsx b/web/src/components/Quotes.jsx
index 1d563e7..831408d 100644
--- a/web/src/components/Quotes.jsx
+++ b/web/src/components/Quotes.jsx
@@ -72,6 +72,7 @@ export function PendingQuotes() {
</div>
<p className="quoteText">{quote.quote}</p>
<small>- {quote.year}</small>
+ <small>Submitted by {quote.submitterAuthor} ({quote.submitterID})</small>
</div>
<button onClick={() => approveQuote(quote.id)}>Approve</button>
<button onClick={() => rejectQuote(quote.id)}>Reject</button>
diff --git a/web/src/layouts/Layout.astro b/web/src/layouts/Layout.astro
index e455c61..2f6032d 100644
--- a/web/src/layouts/Layout.astro
+++ b/web/src/layouts/Layout.astro
@@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="generator" content={Astro.generator} />
- <title>Astro Basics</title>
+ <title>AleeBot Web Interface</title>
</head>
<body>
<slot />
diff --git a/web/src/pages/index.astro b/web/src/pages/index.astro
index b5c607d..8136402 100644
--- a/web/src/pages/index.astro
+++ b/web/src/pages/index.astro
@@ -2,12 +2,11 @@
import Layout from '../layouts/Layout.astro';
import { PendingQuotes } from '../components/Quotes';
-const version = await fetch('http://localhost:3000/api/version').then(res => res.json());
---
<Layout>
<div class="container">
- <h1>AleeBot {version}</h1>
+ <h1 id="version">AleeBot</h1>
<PendingQuotes client:load />
</div>
</Layout>
@@ -27,3 +26,19 @@ const version = await fetch('http://localhost:3000/api/version').then(res => res
}
</style>
+
+<script>
+ document.addEventListener('DOMContentLoaded', async () => {
+ try {
+ const version = await fetch('http://localhost:3000/api/version').then((res) => res.json());
+ const versionElement = document.getElementById('version');
+ if (versionElement) {
+ versionElement.textContent = `AleeBot ${version}`;
+ } else {
+ console.error('Element with ID "version" not found.');
+ }
+ } catch (e) {
+ console.error('Failed to fetch version:', e);
+ }
+ });
+</script>