diff --git a/src/commands/Lastfm/RateYourMusic/ImportRatings.ts b/src/commands/Lastfm/RateYourMusic/ImportRatings.ts index c1e52530..6f21298e 100644 --- a/src/commands/Lastfm/RateYourMusic/ImportRatings.ts +++ b/src/commands/Lastfm/RateYourMusic/ImportRatings.ts @@ -1,42 +1,30 @@ import fetch from "node-fetch"; import streamToString from "stream-to-string"; -import { - NoRatingsFileAttatchedError, - TooManyAttachmentsError, - WrongFileFormatAttachedError, -} from "../../../errors/commands/library"; -import { CannotBeUsedAsASlashCommand } from "../../../errors/errors"; -import { StringArgument } from "../../../lib/context/arguments/argumentTypes/StringArgument"; +import { WrongFileFormatAttachedError } from "../../../errors/commands/library"; +import { AttachmentArgument } from "../../../lib/context/arguments/argumentTypes/discord/AttachmentArgument"; import { ArgumentsMap } from "../../../lib/context/arguments/types"; -import { WarningEmbed } from "../../../lib/ui/embeds/WarningEmbed"; import { RatingsImportProgressView } from "../../../lib/ui/views/RatingsImportProgressView"; import { RateYourMusicChildCommand } from "./RateYourMusicChildCommand"; const args = { - input: new StringArgument({ index: { start: 0 }, slashCommandOption: false }), + ratings: new AttachmentArgument({ + index: 0, + description: "The file containing your RateYourMusic ratings", + required: true, + }), } satisfies ArgumentsMap; export class ImportRatings extends RateYourMusicChildCommand { idSeed = "sonamoo high d"; aliases = ["rymimport", "rymsimport"]; description = - "Import your rateyourmusic ratings. See ryms help for more info on how to import"; + "Import your RateYourMusic ratings. See rym help for more info on how to import"; arguments = args; slashCommand = true; async run() { - if (this.payload.isInteraction()) { - await this.reply( - new WarningEmbed().setDescription( - `As of right now, you cannot import with slash commands.\n\nPlease use the message command \`${this.prefix}rymimport\`` - ) - ); - - return; - } - await this.getMentions({ senderRequired: true, syncedRequired: true, @@ -66,15 +54,13 @@ export class ImportRatings extends RateYourMusicChildCommand { } private async getRatings(): Promise { - let ratings: string; + const ratingsAttachment = this.parsedArguments.ratings; - if (!this.parsedArguments.input) { - ratings = await this.getRatingsFromAttached(); - } else { - ratings = this.getRatingsFromContent(); - } + const file = await fetch(ratingsAttachment.url); - ratings = ratings.trim(); + const fileContent = await streamToString(file.body); + + const ratings = fileContent.trim(); if (!ratings.startsWith("RYM Album,")) { throw new WrongFileFormatAttachedError(); @@ -82,45 +68,4 @@ export class ImportRatings extends RateYourMusicChildCommand { return ratings; } - - private async getRatingsFromAttached(): Promise { - if (this.payload.isInteraction()) { - throw new CannotBeUsedAsASlashCommand(); - } else if (this.payload.isMessage()) { - const attachments = this.payload.source.attachments; - - if (attachments.size > 1) { - throw new TooManyAttachmentsError(); - } - - const attachment = attachments.first(); - - if (!attachment) { - throw new NoRatingsFileAttatchedError(this.prefix); - } - - const file = await fetch(attachment.url); - - const fileContent = await streamToString(file.body); - - return fileContent; - } - - return ""; - } - - private getRatingsFromContent(): string { - if (this.payload.isInteraction()) { - throw new CannotBeUsedAsASlashCommand(); - } else if (this.payload.isMessage()) { - const rawMessageContent = this.gowonService.removeCommandName( - this.ctx, - this.payload.source.content - ); - - return rawMessageContent; - } - - return ""; - } } diff --git a/src/errors/commands/library.ts b/src/errors/commands/library.ts index 547bbf17..b340d454 100644 --- a/src/errors/commands/library.ts +++ b/src/errors/commands/library.ts @@ -70,34 +70,12 @@ export class TooManySearchResultsError extends ClientError { } } -export class UnknownRatingsImportError extends ClientError { - constructor() { - super("Something went wrong when importing your ratings"); - } -} - export class WrongFileFormatAttachedError extends ClientError { constructor() { super("Please attach a file with the correct format"); } } -export class TooManyAttachmentsError extends ClientError { - constructor() { - super( - "Too many attachments! Please attach only one file with your ratings" - ); - } -} - -export class NoRatingsFileAttatchedError extends ClientError { - constructor(prefix: string) { - super( - `Please attach your ratings! (See \`${prefix}rym help\` for more info)` - ); - } -} - export class CouldNotFindRatingError extends ClientError { constructor() { super("Couldn't find that album in your ratings!"); diff --git a/src/lib/context/arguments/argumentTypes/SlashCommandTypes.ts b/src/lib/context/arguments/argumentTypes/SlashCommandTypes.ts index 1f4a4816..9d59f8d0 100644 --- a/src/lib/context/arguments/argumentTypes/SlashCommandTypes.ts +++ b/src/lib/context/arguments/argumentTypes/SlashCommandTypes.ts @@ -1,27 +1,28 @@ import { SlashCommandBuilder as _SlashCommandBuilder, - SlashCommandRoleOption, - SlashCommandUserOption, - SlashCommandNumberOption, - SlashCommandStringOption, + SlashCommandAttachmentOption, SlashCommandBooleanOption, SlashCommandChannelOption, SlashCommandIntegerOption, SlashCommandMentionableOption, + SlashCommandNumberOption, + SlashCommandRoleOption, + SlashCommandStringOption, + SlashCommandUserOption, } from "@discordjs/builders"; // Discord.js doesn't export any interfaces to help deal with building slash commands // so I created them myself export { - SlashCommandRoleOption, - SlashCommandUserOption, - SlashCommandNumberOption, - SlashCommandStringOption, SlashCommandBooleanOption, SlashCommandChannelOption, SlashCommandIntegerOption, SlashCommandMentionableOption, + SlashCommandNumberOption, + SlashCommandRoleOption, + SlashCommandStringOption, + SlashCommandUserOption, }; // Any type of slash command option @@ -33,7 +34,8 @@ export type SlashCommandOption = | SlashCommandBooleanOption | SlashCommandChannelOption | SlashCommandIntegerOption - | SlashCommandMentionableOption; + | SlashCommandMentionableOption + | SlashCommandAttachmentOption; export type SlashCommandBuilder = _SlashCommandBuilder; export type SlashCommandBuilderReturn = diff --git a/src/lib/context/arguments/argumentTypes/discord/AttachmentArgument.ts b/src/lib/context/arguments/argumentTypes/discord/AttachmentArgument.ts new file mode 100644 index 00000000..dbb9daa1 --- /dev/null +++ b/src/lib/context/arguments/argumentTypes/discord/AttachmentArgument.ts @@ -0,0 +1,41 @@ +import { CommandInteraction, Message, MessageAttachment } from "discord.js"; +import { GowonContext } from "../../../Context"; +import { + BaseArgument, + BaseArgumentOptions, + IndexableArgumentOptions, + defaultIndexableOptions, +} from "../BaseArgument"; +import { SlashCommandBuilder } from "../SlashCommandTypes"; + +export interface AttachmentArgumentOptions + extends BaseArgumentOptions, + IndexableArgumentOptions {} + +export class AttachmentArgument< + OptionsT extends Partial +> extends BaseArgument { + constructor(options?: OptionsT) { + super({ ...defaultIndexableOptions, ...(options ?? {}) } as OptionsT); + } + + parseFromMessage(message: Message, _: string): MessageAttachment | undefined { + const attachments = Array.from(message.attachments.values()); + + return this.getElementFromIndex(attachments, this.options.index); + } + + parseFromInteraction( + interaction: CommandInteraction, + _: GowonContext, + argumentName: string + ): MessageAttachment | undefined { + return interaction.options.getAttachment(argumentName) ?? undefined; + } + + addAsOption(slashCommand: SlashCommandBuilder, argumentName: string) { + return slashCommand.addAttachmentOption((option) => + this.baseOption(option, argumentName) + ); + } +} diff --git a/src/lib/context/arguments/types.ts b/src/lib/context/arguments/types.ts index bb410915..7f80d562 100644 --- a/src/lib/context/arguments/types.ts +++ b/src/lib/context/arguments/types.ts @@ -4,6 +4,7 @@ import { Flag } from "./argumentTypes/Flag"; import { NumberArgument } from "./argumentTypes/NumberArgument"; import { StringArgument } from "./argumentTypes/StringArgument"; import { StringArrayArgument } from "./argumentTypes/StringArrayArgument"; +import { AttachmentArgument } from "./argumentTypes/discord/AttachmentArgument"; import { ChannelArgument } from "./argumentTypes/discord/ChannelArgument"; import { DiscordRoleArgument } from "./argumentTypes/discord/DiscordRoleArgument"; import { EmojisArgument } from "./argumentTypes/discord/EmojisArgument"; @@ -27,7 +28,8 @@ export type ImplementedOptions> = | EmojisArgument | DateArgument | ChannelArgument - | DiscordRoleArgument; + | DiscordRoleArgument + | AttachmentArgument; export type UnwrapProvidedOptions> = T extends ImplementedOptions ? U : {};