diff --git a/commands/interaction/slash/search/anime.js b/commands/interaction/slash/search/anime.js new file mode 100644 index 0000000..6082190 --- /dev/null +++ b/commands/interaction/slash/search/anime.js @@ -0,0 +1,162 @@ +const { anime } = require('#api'); +const { paginator } = require('#client'); +const { PERMISSION_GROUPS, OMNI_ANIME_FORMAT_TYPES } = require('#constants'); + +const { createEmbed, formatPaginationEmbeds, page, hexToEmbedColor } = require('#utils/embed'); +const { acknowledge } = require('#utils/interactions'); +const { smallPill, link, pill } = require('#utils/markdown'); +const { editOrReply } = require('#utils/message'); +const { InteractionContextTypes, ApplicationIntegrationTypes, ApplicationCommandOptionTypes } = require('detritus-client/lib/constants'); + +function renderAnimeResultsPage(context, res){ + let result = createEmbed("default", context, { + author: { + name: res.title, + url: res.url + }, + description: ``, + fields: [] + }) + + // Add Metadata to Title + if(res.dates){ + if(res.dates.start){ + if(res.dates.end && new Date(res.dates.start).getFullYear() !== new Date(res.dates.end).getFullYear()) result.author.name += ` (${new Date(res.dates.start).getFullYear()} - ${new Date(res.dates.end).getFullYear()})` + else result.author.name += ` (${new Date(res.dates.start).getFullYear()})` + } + } + + // Render Description + if(res.subtitle) result.description += `-# ${res.subtitle}\n\n`; + if(res.subtype) result.description += pill(OMNI_ANIME_FORMAT_TYPES[res.subtype]) + " " + if(res.genres) result.description += res.genres.map((r)=>smallPill(r)).join(" ") + "\n\n"; + if(res.description) result.description += res.description; + if(res.attribution?.description) result.description += `\n\n-# Source • ${res.attribution.description}` + + // Render Images + if(res.cover) result.thumbnail = { url: res.cover }; + if(res.image) result.image = { url: res.image }; + + // Render Color + if(res.color) result.color = hexToEmbedColor(res.color); + + // Render Episode Metadata + if(res.episodes) { + result.fields.push({ + name: "Episodes", + value: `${res.episodes} ${res.episode_length ? `@ ${res.episode_length}` : ""}`, + inline: true + }) + } + + if(res.links){ + result.fields.push({ + name: "Links", + value: res.links.map((l)=>`${link(l.url, l.label)}`).join("\n"), + inline: true + }) + } + + return page(result, {}, { + episodes_key: res.supplemental.episodes + }); +} + + +module.exports = { + name: 'anime', + description: 'Search for Anime.', + contexts: [ + InteractionContextTypes.GUILD, + InteractionContextTypes.PRIVATE_CHANNEL, + InteractionContextTypes.BOT_DM + ], + integrationTypes: [ + ApplicationIntegrationTypes.USER_INSTALL + ], + options: [ + { + name: 'query', + description: 'Search query.', + type: ApplicationCommandOptionTypes.TEXT, + required: true + }, + { + name: 'incognito', + description: 'Makes the response only visible to you.', + type: ApplicationCommandOptionTypes.BOOLEAN, + required: false, + default: false + } + ], + run: async (context, args) => { + await acknowledge(context, args.incognito, [...PERMISSION_GROUPS.baseline_slash]); + + if(!args.query) return editOrReply(context, createEmbed("warning", context, `Missing Parameter (query).`)) + try{ + let search = await anime(context, args.query, context.channel.nsfw) + search = search.response + + if(search.body.status == 2) return editOrReply(context, createEmbed("error", context, search.body.message)) + + let pages = [] + for(const res of search.body.results){ + pages.push(renderAnimeResultsPage(context, res)) + } + + if(!pages.length) return editOrReply(context, createEmbed("warning", context, `No results found.`)) + + await paginator.createPaginator({ + context, + pages: formatPaginationEmbeds(pages), + interactive: { + episodes_button: { + // Button Label + label: "Episodes", + // Next to pagination or new row + inline: false, + visible: true, + condition: (page)=>{ + return (page.getState("episodes_key") !== null) + }, + // Will resolve all conditions at paginator creation time + precompute_conditions: true, + resolvePage: (page)=>{ + // If an interactive button for this index hasn't been + // resolved yet, run this code + page.getState("episodes_key"); // -> supplemental key + + /* Resolve supplemental key via api */ + + return [...cards]; + } + }, + characters_button: { + // Button Label + label: "Characters", + // Next to pagination or new row + inline: false, + condition: (page)=>{ + return (page.getState("characters_key") !== null) + }, + resolvePage: (page)=>{ + // If an interactive button for this index hasn't been + // resolved yet, run this code + page.getState("characters_key"); // -> supplemental key + + /* Resolve supplemental key via api */ + + return [...cards]; + } + } + } + }); + }catch(e){ + if(e.response?.body?.status == 1) return editOrReply(context, createEmbed("warning", context, e.response?.body?.message)) + if(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 anime search.`)) + } + }, +}; \ No newline at end of file diff --git a/commands/interaction/slash/search/movie.js b/commands/interaction/slash/search/movie.js new file mode 100644 index 0000000..d41038d --- /dev/null +++ b/commands/interaction/slash/search/movie.js @@ -0,0 +1,92 @@ +const { movie } = require('#api'); +const { paginator } = require('#client'); +const { PERMISSION_GROUPS, OMNI_ANIME_FORMAT_TYPES, OMNI_MOVIE_TYPES } = require('#constants'); + +const { createEmbed, formatPaginationEmbeds, page, hexToEmbedColor } = require('#utils/embed'); +const { acknowledge } = require('#utils/interactions'); +const { smallPill, pill } = require('#utils/markdown'); +const { editOrReply } = require('#utils/message'); +const { ApplicationCommandOptionTypes, ApplicationIntegrationTypes, InteractionContextTypes } = require('detritus-client/lib/constants'); + +function renderMovieResultsPage(context, res){ + let result = createEmbed("default", context, { + author: { + name: res.title, + url: res.url + }, + description: ``, + fields: [] + }) + + // Render Description + if(res.subtitle) result.description += `-# ${res.subtitle}\n\n`; + if(res.type) result.description += `${pill(OMNI_MOVIE_TYPES[res.type])} ` + if(res.genres) result.description += res.genres.map((r)=>smallPill(r)).join(" ") + "\n\n"; + if(res.description) result.description += res.description; + + // Render Images + if(res.cover) result.thumbnail = { url: res.cover }; + if(res.image) result.image = { url: res.image }; + + // Render Color + if(res.color) result.color = hexToEmbedColor(res.color); + + return page(result); +} + +module.exports = { + name: 'movie', + description: 'Search for Movies & TV Shows.', + contexts: [ + InteractionContextTypes.GUILD, + InteractionContextTypes.PRIVATE_CHANNEL, + InteractionContextTypes.BOT_DM + ], + integrationTypes: [ + ApplicationIntegrationTypes.USER_INSTALL + ], + options: [ + { + name: 'query', + description: 'Search query.', + type: ApplicationCommandOptionTypes.TEXT, + required: true + }, + { + name: 'incognito', + description: 'Makes the response only visible to you.', + type: ApplicationCommandOptionTypes.BOOLEAN, + required: false, + default: false + } + ], + run: async (context, args) => { + await acknowledge(context, args.incognito, [...PERMISSION_GROUPS.baseline_slash]); + + if(!args.query) return editOrReply(context, createEmbed("warning", context, `Missing Parameter (query).`)) + try{ + let search = await movie(context, args.query, context.channel.nsfw) + search = search.response + + if(search.body.status == 2) return editOrReply(context, createEmbed("error", context, search.body.message)) + + let pages = [] + for(const res of search.body.results){ + pages.push(renderMovieResultsPage(context, res)) + } + + if(!pages.length) return editOrReply(context, createEmbed("warning", context, `No results found.`)) + + await paginator.createPaginator({ + context, + pages: formatPaginationEmbeds(pages) + }); + }catch(e){ + if(e.response?.body?.status == 1) return editOrReply(context, createEmbed("warning", context, e.response?.body?.message)) + if(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 anime search.`)) + } + }, +}; \ No newline at end of file