test
This commit is contained in:
parent
616a2cc7b5
commit
6273c03431
1
index.js
1
index.js
@ -52,6 +52,7 @@ client.once('clientReady', async () => {
|
||||
|
||||
deploy(process.env.DISCORD_TOKEN);
|
||||
clean(client);
|
||||
await update(client);
|
||||
});
|
||||
|
||||
client.on(Events.InteractionCreate, async interaction => {
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
const { EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder } = require('discord.js');
|
||||
const { EmbedBuilder, ActionRowBuilder, StringSelectMenuBuilder, AttachmentBuilder } = require('discord.js');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const PINBOARD_SELECT_CUSTOM_ID = 'pinboard-image-select';
|
||||
const selectedImageIndexByMessage = new Map();
|
||||
const preloadedImagesByUrl = new Map();
|
||||
let lastKnownStatus = null;
|
||||
|
||||
const getCacheBustValue = () => {
|
||||
const bucketSeconds = Number.parseInt(process.env.PINBOARD_CACHE_BUST_SECONDS || '60', 10);
|
||||
@ -95,12 +97,71 @@ const clampIndex = (index, length) => {
|
||||
return index;
|
||||
};
|
||||
|
||||
const buildSelectedImageEmbed = (item, index, total) => (
|
||||
new EmbedBuilder()
|
||||
const sanitizeAttachmentName = (name) => (
|
||||
(name || 'pinboard-image')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9._-]/g, '-')
|
||||
.replace(/-+/g, '-')
|
||||
.slice(0, 64)
|
||||
);
|
||||
|
||||
const guessFileExtensionFromContentType = (contentType) => {
|
||||
if (!contentType) return 'jpg';
|
||||
if (contentType.includes('png')) return 'png';
|
||||
if (contentType.includes('webp')) return 'webp';
|
||||
if (contentType.includes('gif')) return 'gif';
|
||||
if (contentType.includes('jpeg') || contentType.includes('jpg')) return 'jpg';
|
||||
return 'jpg';
|
||||
};
|
||||
|
||||
const preloadPinboardImages = async (items) => {
|
||||
const uniqueUrls = [...new Set(items.map(item => item.url))];
|
||||
|
||||
await Promise.all(uniqueUrls.map(async (url, index) => {
|
||||
try {
|
||||
const response = await fetch(toCacheBustedUrl(url));
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
}
|
||||
|
||||
const arrayBuffer = await response.arrayBuffer();
|
||||
const buffer = Buffer.from(arrayBuffer);
|
||||
const contentType = response.headers.get('content-type') || '';
|
||||
const ext = guessFileExtensionFromContentType(contentType.toLowerCase());
|
||||
const attachmentName = `${sanitizeAttachmentName(`pinboard-${index + 1}`)}.${ext}`;
|
||||
|
||||
preloadedImagesByUrl.set(url, {
|
||||
buffer,
|
||||
attachmentName,
|
||||
loadedAt: Date.now()
|
||||
});
|
||||
} catch (error) {
|
||||
// Keep previous successful cache entry if refresh fails.
|
||||
if (!preloadedImagesByUrl.has(url)) {
|
||||
preloadedImagesByUrl.set(url, null);
|
||||
}
|
||||
console.log(`⚠️ Preload image impossible (${url}): ${error.message}`);
|
||||
}
|
||||
}));
|
||||
|
||||
const knownUrls = new Set(uniqueUrls);
|
||||
for (const cachedUrl of preloadedImagesByUrl.keys()) {
|
||||
if (!knownUrls.has(cachedUrl)) {
|
||||
preloadedImagesByUrl.delete(cachedUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const buildSelectedImageEmbed = (item, imageReference) => {
|
||||
const imageUrl = imageReference?.attachmentName
|
||||
? `attachment://${imageReference.attachmentName}`
|
||||
: toCacheBustedUrl(item.url);
|
||||
|
||||
return new EmbedBuilder()
|
||||
.setColor('#f59e0b')
|
||||
.setTitle(item.name)
|
||||
.setImage(toCacheBustedUrl(item.url))
|
||||
);
|
||||
.setImage(imageUrl);
|
||||
};
|
||||
|
||||
const buildImageSelectorRow = (items, selectedIndex) => {
|
||||
const options = items.slice(0, 25).map((item, index) => ({
|
||||
@ -159,24 +220,31 @@ const buildPanelEmbed = (status) => {
|
||||
return message;
|
||||
};
|
||||
|
||||
const buildPanelPayload = (status, messageId) => {
|
||||
const buildPanelPayload = (status, messageId, pinboardItems = null) => {
|
||||
const panelEmbed = buildPanelEmbed(status);
|
||||
const pinboardItems = parsePinboardImageItems().slice(0, 25);
|
||||
const items = (pinboardItems || parsePinboardImageItems()).slice(0, 25);
|
||||
|
||||
if (pinboardItems.length === 0) {
|
||||
if (items.length === 0) {
|
||||
return { content: '', embeds: [panelEmbed], components: [] };
|
||||
}
|
||||
|
||||
const currentIndex = clampIndex(selectedImageIndexByMessage.get(messageId) ?? 0, pinboardItems.length);
|
||||
const currentIndex = clampIndex(selectedImageIndexByMessage.get(messageId) ?? 0, items.length);
|
||||
selectedImageIndexByMessage.set(messageId, currentIndex);
|
||||
|
||||
const selectedImageEmbed = buildSelectedImageEmbed(pinboardItems[currentIndex], currentIndex, pinboardItems.length);
|
||||
const selectorRow = buildImageSelectorRow(pinboardItems, currentIndex);
|
||||
const selectedItem = items[currentIndex];
|
||||
const cachedImage = preloadedImagesByUrl.get(selectedItem.url);
|
||||
const selectedImageEmbed = buildSelectedImageEmbed(selectedItem, cachedImage);
|
||||
const selectorRow = buildImageSelectorRow(items, currentIndex);
|
||||
|
||||
const files = cachedImage?.buffer
|
||||
? [new AttachmentBuilder(cachedImage.buffer, { name: cachedImage.attachmentName })]
|
||||
: [];
|
||||
|
||||
return {
|
||||
content: '',
|
||||
embeds: [panelEmbed, selectedImageEmbed],
|
||||
components: [selectorRow]
|
||||
components: [selectorRow],
|
||||
files
|
||||
};
|
||||
};
|
||||
|
||||
@ -215,10 +283,15 @@ const update = async (client) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const status = await getMinecraftStatus();
|
||||
const pinboardItems = parsePinboardImageItems().slice(0, 25);
|
||||
const [status] = await Promise.all([
|
||||
getMinecraftStatus(),
|
||||
preloadPinboardImages(pinboardItems)
|
||||
]);
|
||||
lastKnownStatus = status;
|
||||
|
||||
const { message } = await resolvePanelMessage(client, channelId, messageId);
|
||||
const payload = buildPanelPayload(status, message.id);
|
||||
const payload = buildPanelPayload(status, message.id, pinboardItems);
|
||||
|
||||
await message.edit(payload);
|
||||
|
||||
@ -245,8 +318,8 @@ const handlePinboardSelection = async (interaction) => {
|
||||
const selectedIndex = clampIndex(selectedRaw, pinboardItems.length);
|
||||
selectedImageIndexByMessage.set(interaction.message.id, selectedIndex);
|
||||
|
||||
const status = await getMinecraftStatus();
|
||||
const payload = buildPanelPayload(status, interaction.message.id);
|
||||
const status = lastKnownStatus || await getMinecraftStatus();
|
||||
const payload = buildPanelPayload(status, interaction.message.id, pinboardItems);
|
||||
await interaction.update(payload);
|
||||
return true;
|
||||
} catch (error) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user