From 6f09f9a7f3f127dd7bba41f0d9ad89c19b11a96c Mon Sep 17 00:00:00 2001 From: flyingtoasters Date: Thu, 3 Apr 2025 12:41:18 -0400 Subject: [PATCH] started work on extractIcons --- toyobot/package-lock.json | 172 ++++++++++++++++++++++++++++++++++-- toyobot/package.json | 1 + toyobot/src/commands.js | 39 +++++++- toyobot/src/register.js | 10 ++- toyobot/src/server.js | 26 ++++++ toyobot/src/yotoplaylist.js | 31 +++++++ 6 files changed, 266 insertions(+), 13 deletions(-) diff --git a/toyobot/package-lock.json b/toyobot/package-lock.json index a6cfc5c..4a65d68 100644 --- a/toyobot/package-lock.json +++ b/toyobot/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { + "@discordjs/rest": "^2.4.3", "axios": "^1.8.4", "discord-interactions": "^4.0.0", "itty-router": "^5.0.9" @@ -182,6 +183,62 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.3.tgz", + "integrity": "sha512-+SO4RKvWsM+y8uFHgYQrcTl/3+cY02uQOH7/7bKbVZsTfrfpoE62o5p+mmV+s7FVhTX82/kQUGGbu4YlV60RtA==", + "license": "Apache-2.0", + "dependencies": { + "@discordjs/collection": "^2.1.1", + "@discordjs/util": "^1.1.1", + "@sapphire/async-queue": "^1.5.3", + "@sapphire/snowflake": "^3.5.3", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.37.119", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.3", + "undici": "6.21.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, + "node_modules/@discordjs/rest/node_modules/undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==", + "license": "MIT", + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@discordjs/util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/discordjs/discord.js?sponsor" + } + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -1309,6 +1366,26 @@ "url": "https://opencollective.com/unts" } }, + "node_modules/@sapphire/async-queue": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, + "node_modules/@sapphire/snowflake": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz", + "integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -1435,6 +1512,16 @@ "@types/node": "*" } }, + "node_modules/@vladfrangu/async_event_emitter": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==", + "license": "MIT", + "engines": { + "node": ">=v14.0.0", + "npm": ">=7.0.0" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -2071,6 +2158,12 @@ "node": ">=0.3.1" } }, + "node_modules/discord-api-types": { + "version": "0.37.119", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz", + "integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==", + "license": "MIT" + }, "node_modules/discord-interactions": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/discord-interactions/-/discord-interactions-4.0.0.tgz", @@ -3299,6 +3392,12 @@ "node": ">=8" } }, + "node_modules/magic-bytes.js": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==", + "license": "MIT" + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -4277,10 +4376,10 @@ } }, "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -4701,6 +4800,39 @@ } } }, + "@discordjs/collection": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/collection/-/collection-2.1.1.tgz", + "integrity": "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg==" + }, + "@discordjs/rest": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/@discordjs/rest/-/rest-2.4.3.tgz", + "integrity": "sha512-+SO4RKvWsM+y8uFHgYQrcTl/3+cY02uQOH7/7bKbVZsTfrfpoE62o5p+mmV+s7FVhTX82/kQUGGbu4YlV60RtA==", + "requires": { + "@discordjs/collection": "^2.1.1", + "@discordjs/util": "^1.1.1", + "@sapphire/async-queue": "^1.5.3", + "@sapphire/snowflake": "^3.5.3", + "@vladfrangu/async_event_emitter": "^2.4.6", + "discord-api-types": "^0.37.119", + "magic-bytes.js": "^1.10.0", + "tslib": "^2.6.3", + "undici": "6.21.1" + }, + "dependencies": { + "undici": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.1.tgz", + "integrity": "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ==" + } + } + }, + "@discordjs/util": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@discordjs/util/-/util-1.1.1.tgz", + "integrity": "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g==" + }, "@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -5259,6 +5391,16 @@ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true }, + "@sapphire/async-queue": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/async-queue/-/async-queue-1.5.5.tgz", + "integrity": "sha512-cvGzxbba6sav2zZkH8GPf2oGk9yYoD5qrNWdu9fRehifgnFZJMV+nuy2nON2roRO4yQQ+v7MK/Pktl/HgfsUXg==" + }, + "@sapphire/snowflake": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@sapphire/snowflake/-/snowflake-3.5.5.tgz", + "integrity": "sha512-xzvBr1Q1c4lCe7i6sRnrofxeO1QTP/LKQ6A6qy0iB4x5yfiSfARMEQEghojzTNALDTcv8En04qYNIco9/K9eZQ==" + }, "@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -5375,6 +5517,11 @@ "@types/node": "*" } }, + "@vladfrangu/async_event_emitter": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vladfrangu/async_event_emitter/-/async_event_emitter-2.4.6.tgz", + "integrity": "sha512-RaI5qZo6D2CVS6sTHFKg1v5Ohq/+Bo2LZ5gzUEwZ/WkHhwtGTCB/sVLw8ijOkAUxasZ+WshN/Rzj4ywsABJ5ZA==" + }, "acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -5836,6 +5983,11 @@ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true }, + "discord-api-types": { + "version": "0.37.119", + "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.37.119.tgz", + "integrity": "sha512-WasbGFXEB+VQWXlo6IpW3oUv73Yuau1Ig4AZF/m13tXcTKnMpc/mHjpztIlz4+BM9FG9BHQkEXiPto3bKduQUg==" + }, "discord-interactions": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/discord-interactions/-/discord-interactions-4.0.0.tgz", @@ -6709,6 +6861,11 @@ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true }, + "magic-bytes.js": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/magic-bytes.js/-/magic-bytes.js-1.10.0.tgz", + "integrity": "sha512-/k20Lg2q8LE5xiaaSkMXk4sfvI+9EGEykFS4b0CHHGWqDYU0bGUFSwchNOMA56D7TCs9GwVTkqe9als1/ns8UQ==" + }, "make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -7392,10 +7549,9 @@ } }, "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, "type-check": { "version": "0.4.0", diff --git a/toyobot/package.json b/toyobot/package.json index d856b33..9ad8bc5 100644 --- a/toyobot/package.json +++ b/toyobot/package.json @@ -18,6 +18,7 @@ "author": "Justin Beckwith ", "license": "MIT", "dependencies": { + "@discordjs/rest": "^2.4.3", "axios": "^1.8.4", "discord-interactions": "^4.0.0", "itty-router": "^5.0.9" diff --git a/toyobot/src/commands.js b/toyobot/src/commands.js index c2e83dc..95bc3ce 100644 --- a/toyobot/src/commands.js +++ b/toyobot/src/commands.js @@ -45,6 +45,7 @@ export function INVITE_EXEC(request, env, interaction) { }); }; + /******************************************* * /ping *******************************************/ @@ -61,6 +62,7 @@ export function PING_EXEC(request, env, interaction) { }) }; + /******************************************* * /server *******************************************/ @@ -81,6 +83,7 @@ Verification level: ${interaction.guild.verificationLevel}`, }) }; + /******************************************* * /user *******************************************/ @@ -100,6 +103,7 @@ nickname: ${interaction.member.nick}`, }) }; + /******************************************* * /yoto-store * /yoto-store url: https://us.yotoplay.com/products/paw-patrol-pup-pack @@ -129,6 +133,7 @@ export async function YOTO_STORE_EXEC(request, env, interaction) { }); } + /******************************************* * /yoto-playlist * /yoto-playlist url: https://yoto.io/hMkni?84brH2BNuhyl=e79sopPfwKnBL @@ -212,9 +217,41 @@ export async function EXTRACT_AUDIO_EXEC(request, env, interaction) { const markdown = formatDataAsMarkdown(data); return new JsonResponse({ type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, - //flags: InteractionResponseFlags.EPHEMERAL, //only show the message to the user who invoked the command + flags: InteractionResponseFlags.EPHEMERAL, //only show the message to the user who invoked the command data: { content: markdown, } }); }; + + +/******************************************* + * /extract-icons + * TODO -- this is incomplete + *******************************************/ +import { GetIconURLs } from './yotoplaylist.js'; +export const EXTRACT_ICONS_COMMAND = { + name: 'extract-icons', + description: 'Get icon files from a playlist URL.\n this is incomplete.', + options: [ + { + name: 'url', + description: 'URL of the playlist page. e.g.: https://yoto.io/hMkni?84brH2BNuhyl=e79sopPfwKnBL', + required: true, + type: 3, + } + ], +}; +export async function EXTRACT_ICONS_EXEC(request, env, interaction) { + const url = interaction.data.options[0].value; + const data = await GetIconURLs(url); + const markdown = formatDataAsMarkdown(data); + return new JsonResponse({ + type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE, + flags: InteractionResponseFlags.EPHEMERAL, //only show the message to the user who invoked the command + data: { + content: markdown, + } + }); +}; + diff --git a/toyobot/src/register.js b/toyobot/src/register.js index 864992f..3a8862d 100644 --- a/toyobot/src/register.js +++ b/toyobot/src/register.js @@ -1,6 +1,8 @@ -import { AWWWW_COMMAND, INVITE_COMMAND, PING_COMMAND, SERVER_COMMAND, USER_COMMAND, - YOTO_STORE_COMMAND, YOTO_PLAYLIST_COMMAND, - EXTRACT_AUDIO_COMMAND, +import { AWWWW_COMMAND, INVITE_COMMAND, SERVER_COMMAND, USER_COMMAND, //non-registered commands + PING_COMMAND, //generic + YOTO_STORE_COMMAND, //public store commands + EXTRACT_AUDIO_COMMAND, YOTO_PLAYLIST_COMMAND, //private playlist commands + EXTRACT_ICONS_COMMAND, //public playlist commands } from './commands.js'; import dotenv from 'dotenv'; import process from 'node:process'; @@ -33,7 +35,7 @@ const url = `https://discord.com/api/applications/${applicationId}/commands`; const reg_command = JSON.stringify([PING_COMMAND, YOTO_STORE_COMMAND, YOTO_PLAYLIST_COMMAND, - EXTRACT_AUDIO_COMMAND, + EXTRACT_AUDIO_COMMAND, EXTRACT_ICONS_COMMAND, ]); const del_command = JSON.stringify([]); diff --git a/toyobot/src/server.js b/toyobot/src/server.js index fa7a4c0..473c6fb 100644 --- a/toyobot/src/server.js +++ b/toyobot/src/server.js @@ -18,6 +18,7 @@ import { USER_COMMAND, USER_EXEC } from './commands.js'; import { YOTO_PLAYLIST_COMMAND, YOTO_PLAYLIST_EXEC } from './commands.js'; import { YOTO_STORE_COMMAND, YOTO_STORE_EXEC } from './commands.js'; import { EXTRACT_AUDIO_COMMAND, EXTRACT_AUDIO_EXEC } from './commands.js'; +import { EXTRACT_ICONS_COMMAND, EXTRACT_ICONS_EXEC } from './commands.js'; // Import other local requirements import { JsonResponse } from './jsonresponse.js'; @@ -58,6 +59,10 @@ router.get('/extract-audio', (request, env) => { return EXTRACT_AUDIO_EXEC(request, env, "webget"); }); +router.get('/extract-icons', (request, env) => { + return EXTRACT_ICONS_EXEC(request, env, "webget"); +}); + /** * A simple :wave: hello page to verify the worker is working. */ @@ -114,11 +119,32 @@ router.post('/', async (request, env) => { case EXTRACT_AUDIO_COMMAND.name.toLowerCase():{ return EXTRACT_AUDIO_EXEC(request, env, interaction); } + case EXTRACT_ICONS_COMMAND.name.toLowerCase():{ + return EXTRACT_ICONS_EXEC(request, env, interaction); + } default: + console.error('Unknown Command\n\n'); + console.log('Interaction:', interaction); + console.log('Interaction Data:', interaction.data); + console.log('Request', request); return new JsonResponse({ error: 'Unknown Type' }, { status: 400 }); } } + if (interaction.type === InteractionType.MESSAGE_COMPONENT) { + // The `MESSAGE_COMPONENT` message is used for all button and select interactions. + console.log('Message Component Interaction:', interaction); + switch (interaction.data.custom_id.toLowerCase()) { + default: + console.error('Unknown Message Component\n\n'); + console.log('Interaction:', interaction); + console.log('Interaction Data:', interaction.data); + console.log('Request', request); + return new JsonResponse({ error: 'Unknown Type' }, { status: 400 }); + } + + } + console.error('Unknown Type'); return new JsonResponse({ error: 'Unknown Type' }, { status: 400 }); }); diff --git a/toyobot/src/yotoplaylist.js b/toyobot/src/yotoplaylist.js index 3a0790e..61336f4 100644 --- a/toyobot/src/yotoplaylist.js +++ b/toyobot/src/yotoplaylist.js @@ -54,6 +54,37 @@ export async function GetTrackURLs(url){ } } +export async function GetIconURLs(url){ + try{ + // Fetch the content from the provided URL + const response = await axios.get(url); + const contentType = response.headers['content-type']; + + let card = response.data?.card; + return extractIcons(card); + } catch (e) { + return { error: e.message || "Error fetching data" }; + } +} + +function extractIcons(card) { + const data = []; // Declare the data array + //"props.pageProps.card.content.chapters[0].display.icon16x16" + const chapters = card.content?.chapters || []; + chapters.forEach((chapter, chapterIndex) => { + const chapterDisplayInfo = chapter.display; // Get the icon for the chapter + if (chapterDisplayInfo) { + chapter.tracks?.forEach((track, trackIndex) => { + const trackDisplayInfo = track.display; // Look for an icon for the track + const iconUrl = trackDisplayInfo?.icon16x16 || chapterDisplayInfo.icon16x16; // Fallback to chapter icon + // Push the formatted entry into the data collection + data.push(`${chapterIndex}-${trackIndex}: <${track.trackUrl}>`); + }); // Close forEach + } + }); + return data; // Return the data array +} + /** * Extracts track URLs from the card data and formats them as index: url. *