generated from frutbits/template
-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add anime search * fix: use discord.js color constants * fix: only show manga in anime command * refactor: move anime embed builder to separate file - add emojis to links * fix: get available color from constants - fix color type cast * fix: use got for api request - replace try catch with sapphire/result - truncate text to conform character limits
- Loading branch information
Showing
8 changed files
with
442 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { ApplyOptions } from "@sapphire/decorators"; | ||
import { ApplicationCommandRegistry, Command, RegisterBehavior, Result } from "@sapphire/framework"; | ||
import { ApplicationCommandOptionType, ActionRowBuilder, StringSelectMenuBuilder } from "discord.js"; | ||
import { toTitleCase } from "@sapphire/utilities"; | ||
import { guildsToRegister } from "../../config"; | ||
import { Anilist } from "../../utils/Anilist"; | ||
import { SelectMenuCustomIds } from "../../constants"; | ||
import { AnimeResponseBuilder } from "../../utils/responseBuilder/AnimeResponseBuilder"; | ||
|
||
@ApplyOptions<Command.Options>({ | ||
aliases: [], | ||
name: "anime", | ||
description: "Search anime information", | ||
detailedDescription: { | ||
usage: "{prefix}anime [text]" | ||
}, | ||
requiredClientPermissions: ["EmbedLinks"] | ||
}) | ||
export class AnimeCommand extends Command { | ||
public override registerApplicationCommands(registry: ApplicationCommandRegistry): void { | ||
registry.registerChatInputCommand({ | ||
name: this.name, | ||
description: this.description, | ||
options: [ | ||
{ | ||
name: "title", | ||
type: ApplicationCommandOptionType.String, | ||
description: "Anime title to find", | ||
required: true | ||
} | ||
] | ||
}, { | ||
guildIds: guildsToRegister, | ||
behaviorWhenNotIdentical: RegisterBehavior.Overwrite, | ||
registerCommandIfMissing: true | ||
}); | ||
} | ||
|
||
// eslint-disable-next-line class-methods-use-this | ||
public async chatInputRun(interaction: Command.ChatInputCommandInteraction): Promise<any> { | ||
const animeTitle = interaction.options.getString("title", true); | ||
await interaction.deferReply(); | ||
const anilist = new Anilist(); | ||
const data = (await Result.fromAsync(anilist.findAnimeByTitle(animeTitle))).unwrap(); | ||
if (!data?.media[0]) return interaction.editReply(`No result found for ${animeTitle}.`); | ||
if (data.media.length === 1) return interaction.editReply({ embeds: AnimeResponseBuilder(data.media[0], interaction.user) }); | ||
|
||
const rowOptions = data.media.map(currentMedia => { | ||
const status = toTitleCase(currentMedia.status?.replaceAll("_", " ") ?? "Unknown"); | ||
const episodes = currentMedia.episodes?.toString(); | ||
const genres = currentMedia.genres?.join(", "); | ||
const startDate = Anilist.parseAnimeDate(currentMedia.startDate); | ||
const description = `${status}, ${startDate ? `${startDate}, ` : ""}${episodes ? `${episodes} episodes, ` : ""}${genres ?? ""}`; | ||
return { | ||
label: Anilist.truncateText(currentMedia.title.romaji, 100), | ||
description: Anilist.truncateText(Anilist.stripHtmlTag(description), 100), | ||
value: currentMedia.id.toString() | ||
}; | ||
}); | ||
const row = new ActionRowBuilder<StringSelectMenuBuilder>() | ||
.addComponents( | ||
new StringSelectMenuBuilder() | ||
.setCustomId(SelectMenuCustomIds.Anime) | ||
.setPlaceholder(`Search result: ${data.media.length}`) | ||
.addOptions(rowOptions) | ||
); | ||
return interaction.editReply({ embeds: AnimeResponseBuilder(data.media[0], interaction.user), components: [row] }); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { InteractionHandler, InteractionHandlerTypes } from "@sapphire/framework"; | ||
import { StringSelectMenuInteraction } from "discord.js"; | ||
import { ApplyOptions } from "@sapphire/decorators"; | ||
import { Anilist, AnilistAnime } from "../../utils/Anilist"; | ||
import { SelectMenuCustomIds } from "../../constants"; | ||
import { AnimeResponseBuilder } from "../../utils/responseBuilder/AnimeResponseBuilder"; | ||
@ApplyOptions<InteractionHandler.Options>({ | ||
interactionHandlerType: InteractionHandlerTypes.SelectMenu | ||
}) | ||
export class AnimeSelectHandler extends InteractionHandler { | ||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type | ||
public override async parse(interaction: StringSelectMenuInteraction) { | ||
if (interaction.customId !== SelectMenuCustomIds.Anime) return this.none(); | ||
|
||
await interaction.deferUpdate(); | ||
const anilist = new Anilist(); | ||
const data = await anilist.getAnimeById(parseInt(interaction.values[0])); | ||
if (!data) return this.none(); | ||
return this.some({ data }); | ||
} | ||
|
||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type, class-methods-use-this | ||
public override async run(interaction: StringSelectMenuInteraction, result: { data: AnilistAnime }) { | ||
return interaction.editReply({ embeds: AnimeResponseBuilder(result.data, interaction.user) }); | ||
} | ||
} |
Oops, something went wrong.