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);
|
deploy(process.env.DISCORD_TOKEN);
|
||||||
clean(client);
|
clean(client);
|
||||||
|
await update(client);
|
||||||
});
|
});
|
||||||
|
|
||||||
client.on(Events.InteractionCreate, async interaction => {
|
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 fs = require('node:fs');
|
||||||
const path = require('node:path');
|
const path = require('node:path');
|
||||||
|
|
||||||
const PINBOARD_SELECT_CUSTOM_ID = 'pinboard-image-select';
|
const PINBOARD_SELECT_CUSTOM_ID = 'pinboard-image-select';
|
||||||
const selectedImageIndexByMessage = new Map();
|
const selectedImageIndexByMessage = new Map();
|
||||||
|
const preloadedImagesByUrl = new Map();
|
||||||
|
let lastKnownStatus = null;
|
||||||
|
|
||||||
const getCacheBustValue = () => {
|
const getCacheBustValue = () => {
|
||||||
const bucketSeconds = Number.parseInt(process.env.PINBOARD_CACHE_BUST_SECONDS || '60', 10);
|
const bucketSeconds = Number.parseInt(process.env.PINBOARD_CACHE_BUST_SECONDS || '60', 10);
|
||||||
@ -95,12 +97,71 @@ const clampIndex = (index, length) => {
|
|||||||
return index;
|
return index;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildSelectedImageEmbed = (item, index, total) => (
|
const sanitizeAttachmentName = (name) => (
|
||||||
new EmbedBuilder()
|
(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')
|
.setColor('#f59e0b')
|
||||||
.setTitle(item.name)
|
.setTitle(item.name)
|
||||||
.setImage(toCacheBustedUrl(item.url))
|
.setImage(imageUrl);
|
||||||
);
|
};
|
||||||
|
|
||||||
const buildImageSelectorRow = (items, selectedIndex) => {
|
const buildImageSelectorRow = (items, selectedIndex) => {
|
||||||
const options = items.slice(0, 25).map((item, index) => ({
|
const options = items.slice(0, 25).map((item, index) => ({
|
||||||
@ -159,24 +220,31 @@ const buildPanelEmbed = (status) => {
|
|||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildPanelPayload = (status, messageId) => {
|
const buildPanelPayload = (status, messageId, pinboardItems = null) => {
|
||||||
const panelEmbed = buildPanelEmbed(status);
|
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: [] };
|
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);
|
selectedImageIndexByMessage.set(messageId, currentIndex);
|
||||||
|
|
||||||
const selectedImageEmbed = buildSelectedImageEmbed(pinboardItems[currentIndex], currentIndex, pinboardItems.length);
|
const selectedItem = items[currentIndex];
|
||||||
const selectorRow = buildImageSelectorRow(pinboardItems, 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 {
|
return {
|
||||||
content: '',
|
content: '',
|
||||||
embeds: [panelEmbed, selectedImageEmbed],
|
embeds: [panelEmbed, selectedImageEmbed],
|
||||||
components: [selectorRow]
|
components: [selectorRow],
|
||||||
|
files
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -215,10 +283,15 @@ const update = async (client) => {
|
|||||||
return;
|
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 { message } = await resolvePanelMessage(client, channelId, messageId);
|
||||||
const payload = buildPanelPayload(status, message.id);
|
const payload = buildPanelPayload(status, message.id, pinboardItems);
|
||||||
|
|
||||||
await message.edit(payload);
|
await message.edit(payload);
|
||||||
|
|
||||||
@ -245,8 +318,8 @@ const handlePinboardSelection = async (interaction) => {
|
|||||||
const selectedIndex = clampIndex(selectedRaw, pinboardItems.length);
|
const selectedIndex = clampIndex(selectedRaw, pinboardItems.length);
|
||||||
selectedImageIndexByMessage.set(interaction.message.id, selectedIndex);
|
selectedImageIndexByMessage.set(interaction.message.id, selectedIndex);
|
||||||
|
|
||||||
const status = await getMinecraftStatus();
|
const status = lastKnownStatus || await getMinecraftStatus();
|
||||||
const payload = buildPanelPayload(status, interaction.message.id);
|
const payload = buildPanelPayload(status, interaction.message.id, pinboardItems);
|
||||||
await interaction.update(payload);
|
await interaction.update(payload);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user