Skip to content

Commit

Permalink
feat: add anime search (#27)
Browse files Browse the repository at this point in the history
* 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
Plyrs1 authored Mar 29, 2023
1 parent cdac331 commit 9954591
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 7 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
"canvas-constructor": "^7.0.0",
"discord.js": "^14.7.1",
"dotenv": "^16.0.3",
"got": "^12.6.0",
"moment": "^2.29.4",
"pino-pretty": "^9.4.0",
"pretty-ms": "^8.0.0",
Expand Down
126 changes: 126 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions src/commands/general/AnimeCommand.ts
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] });
}
}
8 changes: 7 additions & 1 deletion src/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export enum Roles {

export enum Emojis {
YES = "<:yes:990955540802863104>",
NO = "<:no:990955538831536200>"
NO = "<:no:990955538831536200>",
ANILIST = "<:AW_anilist:931202490584690698>",
MAL = "<:Mal:765147564612911104>"
}

export enum Guild {
Expand All @@ -37,3 +39,7 @@ export enum Images {
INFORMATION = "https://api.frutbits.org/assets/images/information.png",
QUESTION_MARK = "https://api.frutbits.org/assets/images/question_mark.png"
}

export enum SelectMenuCustomIds {
Anime = "anime-select"
}
26 changes: 26 additions & 0 deletions src/interaction-handlers/select/AnimeSelect.ts
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) });
}
}
Loading

0 comments on commit 9954591

Please sign in to comment.