diff --git a/commands/interaction/slash/utils/dictionary.js b/commands/interaction/slash/utils/dictionary.js index e5a1afe..df7a64c 100644 --- a/commands/interaction/slash/utils/dictionary.js +++ b/commands/interaction/slash/utils/dictionary.js @@ -4,10 +4,11 @@ const {PERMISSION_GROUPS} = require("#constants"); const { createEmbed, page } = require('#utils/embed'); const { acknowledge } = require('#utils/interactions'); -const { link, smallPill, icon, pill } = require('#utils/markdown') +const { link, smallPill, icon } = require('#utils/markdown') const { editOrReply } = require('#utils/message') const { ApplicationCommandOptionTypes, InteractionContextTypes, ApplicationIntegrationTypes } = require('detritus-client/lib/constants'); +const {InteractiveComponentTypes, ResolveCallbackTypes} = require("#cardstack/constants"); // TODO(unity): single constant const LABELS = { @@ -16,21 +17,21 @@ const LABELS = { } // TODO(unity) -function renderDictionaryEntry(context, result, definition, language) { +function renderDictionaryEntry(context, result, definition, resultIndex, definitionIndex) { let card = createEmbed("default", context, { - description: `### ${result.word}\n-# ${result.phonetic} • ${definition.type}\n\n`, + description: `### ${result.word}\n-# ${result.phonetic ? `${result.phonetic} • ` : ""}${definition.type}\n\n`, url: `https://en.wiktionary.org/wiki/${encodeURIComponent(result.word)}`, fields: [] }) - if(definition.labels?.filter((l)=>LABELS[l]).length >= 1) card.description += `-# ⚠ ${definition.labels.map((l)=>LABELS[l]).join(" ⚠ ")}\n`; + if(definition.labels?.filter((l)=>LABELS[l]).filter((e)=>e!==undefined).length >= 1) card.description += `-# ⚠ ${definition.labels.map((l)=>LABELS[l]).filter((e)=>e!==undefined).join(" ⚠ ")}\n`; let defs = []; let i = 1; for(const d of definition.definitions){ let def = `${i++}. ${d.definition}`; - if(d.labels?.filter((l)=>LABELS[l]).length >= 1) def = `-# ⚠ ${d.labels.map((l)=>LABELS[l]).join(" ⚠ ")}\n` + def; + if(d.labels?.filter((l)=>LABELS[l]).filter((e)=>e!==undefined).length >= 1) def = `-# ⚠ ${d.labels.map((l)=>LABELS[l]).filter((e)=>e!==undefined).join(" ⚠ ")}\n` + def; if(d.example) def += `\n - *${d.example}*` let nyms = []; @@ -56,7 +57,17 @@ function renderDictionaryEntry(context, result, definition, language) { } card.description += defs.join("\n\n") - return page(card); + return page(card, {}, { + usage_examples: (definition.definitions.map((s)=>s.additional_examples).flat().filter((e)=>e!==undefined).length >= 1), + similar: ( + definition.definitions.map((s)=>s.synonyms).flat().filter((e)=>e!==undefined).length >= 1 || + definition.definitions.map((s)=>s.antonyms).flat().filter((e)=>e!==undefined).length >= 1 + ), + origin: (result.origin !== undefined), + result: resultIndex, + definition: definitionIndex, + term: result.word + }); } module.exports = { @@ -96,14 +107,122 @@ module.exports = { let pages = [] - for(const r of search.body.results){ - for(const d of r.entries){ - pages.push(renderDictionaryEntry(context, r, d, "en")) + let ri = 0; + let di = 0; + for (const r of search.body.results) { + di = 0; + for (const d of r.entries) { + pages.push(renderDictionaryEntry(context, r, d, ri, di)) + di++; } + ri++; } return await createDynamicCardStack(context, { - cards: pages + cards: pages, + interactive: { + usage_examples: { + type: InteractiveComponentTypes.BUTTON, + label: "Usage Examples", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("usage_examples") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")] + let definition = result.entries[pg.getState("definition")]; + + let usageCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n`, + }) + + let additional = definition.definitions.map((s)=>s.additional_examples).flat(2).filter((e)=>e!==undefined); + + if (additional.length >= 1) usageCard.description += "- " + + additional.map(value => ({value, sort: Math.random()})) + .sort((a, b) => a.sort - b.sort) + .map(({value}) => value) + .splice(0, 7) + .join('\n- '); + + if(search.body.trends){ + usageCard.description += `\n\n-# Usage over time` + usageCard.image = { url: search.body.trends.image }; + } + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(usageCard) + ] + }; + } + }, + similar_and_opposite: { + type: InteractiveComponentTypes.BUTTON, + label: "Similar and Opposite Words", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("similar") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")] + let definition = result.entries[pg.getState("definition")]; + + let similarCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n-# ${definition.type}\n\n`, + }) + + let synonyms = definition.definitions.map((d)=>d.synonyms).flat(2).filter((e)=>e!==undefined); + let antonyms = definition.definitions.map((d)=>d.antonyms).flat(2).filter((e)=>e!==undefined); + + if(synonyms.length >= 1){ + similarCard.description += `**Similar**\n-# ${synonyms.map((s)=>smallPill(s)).join(" ")}\n\n` + } + + if(antonyms.length >= 1){ + similarCard.description += `**Opposite**\n-# ${antonyms.map((s)=>smallPill(s)).join(" ")}\n\n` + } + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(similarCard) + ] + }; + } + }, + term_origin: { + type: InteractiveComponentTypes.BUTTON, + label: "Origin", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("origin") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")]; + + let originCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n${result.origin.description}`, + }) + + if(result.origin.image) originCard.image = { url: result.origin.image }; + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(originCard) + ] + }; + } + } + } }); }catch(e){ if(e.response?.body?.status && e.response.body.status === 2) return editOrReply(context, createEmbed("warning", context, e.response.body.message)) diff --git a/commands/message/utils/dictionary.js b/commands/message/utils/dictionary.js index b59f6fc..81adbcb 100644 --- a/commands/message/utils/dictionary.js +++ b/commands/message/utils/dictionary.js @@ -1,12 +1,13 @@ -const { dictionary } = require('#api'); +const {dictionary} = require('#api'); const {createDynamicCardStack} = require("#cardstack/index"); const {PERMISSION_GROUPS} = require("#constants"); -const { createEmbed, page } = require('#utils/embed'); -const { acknowledge } = require('#utils/interactions'); -const { link, smallPill, icon, pill } = require('#utils/markdown') -const { editOrReply } = require('#utils/message') -const { dictionaryGetCodeFromAny } = require('#utils/translate'); +const {createEmbed, page} = require('#utils/embed'); +const {acknowledge} = require('#utils/interactions'); +const {link, smallPill, icon} = require('#utils/markdown') +const {editOrReply} = require('#utils/message') +const {dictionaryGetCodeFromAny} = require('#utils/translate'); +const {InteractiveComponentTypes, ResolveCallbackTypes} = require("#cardstack/constants"); // TODO(unity): single constant const LABELS = { @@ -15,33 +16,33 @@ const LABELS = { } // TODO(unity) -function renderDictionaryEntry(context, result, definition, language) { +function renderDictionaryEntry(context, result, definition, resultIndex, definitionIndex) { let card = createEmbed("default", context, { - description: `### ${result.word}\n-# ${result.phonetic} • ${definition.type}\n\n`, + description: `### ${result.word}\n-# ${result.phonetic ? `${result.phonetic} • ` : ""}${definition.type}\n\n`, url: `https://en.wiktionary.org/wiki/${encodeURIComponent(result.word)}`, fields: [] }) - if(definition.labels?.filter((l)=>LABELS[l]).length >= 1) card.description += `-# ⚠ ${definition.labels.map((l)=>LABELS[l]).join(" ⚠ ")}\n`; + if (definition.labels?.filter((l) => LABELS[l]).filter((e)=>e!==undefined).length >= 1) card.description += `-# ⚠ ${definition.labels.map((l) => LABELS[l]).filter((e)=>e!==undefined).join(" ⚠ ")}\n`; let defs = []; let i = 1; - for(const d of definition.definitions){ - let def = `${i++}. ${d.definition}`; + for (const d of definition.definitions) { + let def = `${i++}. ${d.definition}`; - if(d.labels?.filter((l)=>LABELS[l]).length >= 1) def = `-# ⚠ ${d.labels.map((l)=>LABELS[l]).join(" ⚠ ")}\n` + def; - if(d.example) def += `\n - *${d.example}*` + if (d.labels?.filter((l) => LABELS[l]).filter((e)=>e!==undefined).length >= 1) def = `-# ⚠ ${d.labels.map((l) => LABELS[l]).filter((e)=>e!==undefined).join(" ⚠ ")}\n` + def; + if (d.example) def += `\n - *${d.example}*` let nyms = []; - if(d.synonyms) nyms = nyms.concat(d.synonyms.map((sd)=>smallPill(sd))) + if (d.synonyms) nyms = nyms.concat(d.synonyms.map((sd) => smallPill(sd))) // Display up to 6 random synonyms/antonyms - if(nyms.length >= 1) { - nyms = nyms.splice(0 ,6) - .map(value => ({ value, sort: Math.random() })) - .sort((a, b) => a.sort - b.sort) - .map(({ value }) => value) + if (nyms.length >= 1) { + nyms = nyms.splice(0, 6) + .map(value => ({value, sort: Math.random()})) + .sort((a, b) => a.sort - b.sort) + .map(({value}) => value) def += `\n-# ${nyms.join(" ")}` } @@ -49,13 +50,23 @@ function renderDictionaryEntry(context, result, definition, language) { defs.push(def); } - if(defs.length > 5){ + if (defs.length > 5) { defs = defs.splice(0, 5); defs.push(link(`https://www.google.com/search?q=define+${encodeURIComponent(result.word)}`, `More Definitions ${icon("open_in_new")}`)) } card.description += defs.join("\n\n") - return page(card); + return page(card, {}, { + usage_examples: (definition.definitions.map((s)=>s.additional_examples).flat().filter((e)=>e!==undefined).length >= 1), + similar: ( + definition.definitions.map((s)=>s.synonyms).flat().filter((e)=>e!==undefined).length >= 1 || + definition.definitions.map((s)=>s.antonyms).flat().filter((e)=>e!==undefined).length >= 1 + ), + origin: (result.origin !== undefined), + result: resultIndex, + definition: definitionIndex, + term: result.word + }); } module.exports = { @@ -76,31 +87,138 @@ module.exports = { permissionsClient: [...PERMISSION_GROUPS.baseline], run: async (context, args) => { await acknowledge(context); - let language = dictionaryGetCodeFromAny(args.lang); - if(!language) return editOrReply(context, createEmbed("warning", context, "Invalid Language")) + if (!language) return editOrReply(context, createEmbed("warning", context, "Invalid Language")) - try{ + try { let search = await dictionary(context, args.query, language) search = search.response - - if(search.body.status === 1) return editOrReply(context, createEmbed("warning", context, search.body.message)) + + if (search.body.status === 1) return editOrReply(context, createEmbed("warning", context, search.body.message)) let pages = [] - for(const r of search.body.results){ - for(const d of r.entries){ - pages.push(renderDictionaryEntry(context, r, d, language)) + let ri = 0; + let di = 0; + for (const r of search.body.results) { + di = 0; + for (const d of r.entries) { + pages.push(renderDictionaryEntry(context, r, d, ri, di)) + di++; } + ri++; } return await createDynamicCardStack(context, { - cards: pages + cards: pages, + interactive: { + usage_examples: { + type: InteractiveComponentTypes.BUTTON, + label: "Usage Examples", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("usage_examples") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")] + let definition = result.entries[pg.getState("definition")]; + + let usageCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n`, + }) + + let additional = definition.definitions.map((s)=>s.additional_examples).flat(2).filter((e)=>e!==undefined); + + if (additional.length >= 1) usageCard.description += "- " + + additional.map(value => ({value, sort: Math.random()})) + .sort((a, b) => a.sort - b.sort) + .map(({value}) => value) + .splice(0, 7) + .join('\n- '); + + if(search.body.trends){ + usageCard.description += `\n\n-# Usage over time` + usageCard.image = { url: search.body.trends.image }; + } + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(usageCard) + ] + }; + } + }, + similar_and_opposite: { + type: InteractiveComponentTypes.BUTTON, + label: "Similar and Opposite Words", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("similar") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")] + let definition = result.entries[pg.getState("definition")]; + + let similarCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n-# ${definition.type}\n\n`, + }) + + let synonyms = definition.definitions.map((d)=>d.synonyms).flat(2).filter((e)=>e!==undefined); + let antonyms = definition.definitions.map((d)=>d.antonyms).flat(2).filter((e)=>e!==undefined); + + if(synonyms.length >= 1){ + similarCard.description += `**Similar**\n-# ${synonyms.map((s)=>smallPill(s)).join(" ")}\n\n` + } + + if(antonyms.length >= 1){ + similarCard.description += `**Opposite**\n-# ${antonyms.map((s)=>smallPill(s)).join(" ")}\n\n` + } + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(similarCard) + ] + }; + } + }, + term_origin: { + type: InteractiveComponentTypes.BUTTON, + label: "Origin", + inline: true, + visible: true, + instantResult: true, + condition: (page) => { + return page.getState("origin") + }, + resolvePage: async (pg) => { + let result = search.body.results[pg.getState("result")]; + + let originCard = createEmbed("default", context, { + description: `### ${pg.getState("term")}\n${result.origin.description}`, + }) + + if(result.origin.image) originCard.image = { url: result.origin.image }; + + return { + type: ResolveCallbackTypes.SUBSTACK, + cards: [ + page(originCard) + ] + }; + } + } + } }); - }catch(e){ - if(e.response?.body?.status && e.response.body.status === 2) return editOrReply(context, createEmbed("warning", context, e.response.body.message)) + } catch (e) { + if (e.response?.body?.status && e.response.body.status === 2) return editOrReply(context, createEmbed("warning", context, e.response.body.message)) console.log(e) return editOrReply(context, createEmbed("error", context, `Unable to perform dictionary lookup.`)) }