Skip to content

Commit

Permalink
Support lilac ratings import subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
abbyfour committed Nov 28, 2024
1 parent 1c83336 commit 37c83a6
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 100 deletions.
81 changes: 13 additions & 68 deletions src/commands/Lastfm/RateYourMusic/ImportRatings.ts
Original file line number Diff line number Diff line change
@@ -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<typeof args> {
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,
Expand Down Expand Up @@ -66,61 +54,18 @@ export class ImportRatings extends RateYourMusicChildCommand<typeof args> {
}

private async getRatings(): Promise<string> {
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();
}

return ratings;
}

private async getRatingsFromAttached(): Promise<string> {
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 "";
}
}
22 changes: 0 additions & 22 deletions src/errors/commands/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!");
Expand Down
20 changes: 11 additions & 9 deletions src/lib/context/arguments/argumentTypes/SlashCommandTypes.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -33,7 +34,8 @@ export type SlashCommandOption =
| SlashCommandBooleanOption
| SlashCommandChannelOption
| SlashCommandIntegerOption
| SlashCommandMentionableOption;
| SlashCommandMentionableOption
| SlashCommandAttachmentOption;

export type SlashCommandBuilder = _SlashCommandBuilder;
export type SlashCommandBuilderReturn =
Expand Down
Original file line number Diff line number Diff line change
@@ -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<MessageAttachment>,
IndexableArgumentOptions {}

export class AttachmentArgument<
OptionsT extends Partial<AttachmentArgumentOptions>
> extends BaseArgument<MessageAttachment, AttachmentArgumentOptions, OptionsT> {
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)
);
}
}
4 changes: 3 additions & 1 deletion src/lib/context/arguments/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -27,7 +28,8 @@ export type ImplementedOptions<T extends Record<string, unknown>> =
| EmojisArgument<T>
| DateArgument<T>
| ChannelArgument<T>
| DiscordRoleArgument<T>;
| DiscordRoleArgument<T>
| AttachmentArgument<T>;

export type UnwrapProvidedOptions<T extends BaseArgument<unknown>> =
T extends ImplementedOptions<infer U> ? U : {};
Expand Down

0 comments on commit 37c83a6

Please sign in to comment.