Skip to content

Commit

Permalink
Simplify errors
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardozgz committed Nov 29, 2024
1 parent a990f5f commit db298dc
Show file tree
Hide file tree
Showing 19 changed files with 184 additions and 205 deletions.
30 changes: 26 additions & 4 deletions apps/bot/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,10 +178,32 @@ interface Resources {
"computeError": "ERROR!"
}
},
"errors": {
"UserError": {
"USER_NOT_FOUND": "User not found."
}
"knownErrors": {
"USER_NOT_FOUND": "User not found.",
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
}
}
Expand Down
10 changes: 2 additions & 8 deletions apps/bot/src/events/interactionCreate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,8 @@ export const interactionCreateEvent = new EventHandler({
description.replaceAll("{{ERROR_ID}}", inlineCode(id)),
);

if (
error instanceof KnownError &&
i18n &&
error.cause.type === "UserError"
) {
embed.setDescription(
i18n.t(`errors.${error.cause.type}.${error.cause.name}`),
);
if (error instanceof KnownError && i18n) {
embed.setDescription(i18n.t(`knownErrors.${error.message}`));
}

logger.error(`Interaction error`, { error, interaction });
Expand Down
5 changes: 1 addition & 4 deletions apps/bot/src/interactions/commands/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,7 @@ export const profileCommand = new Command({
targetUser.id,
).catch((error) => {
if (error instanceof NotFoundError) {
throw new KnownError({
type: "UserError",
name: "USER_NOT_FOUND",
});
throw new KnownError("USER_NOT_FOUND");
}
throw error;
});
Expand Down
7 changes: 3 additions & 4 deletions apps/bot/src/jobs/updateChannels.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,9 @@ async function updateGuildChannels(
await GuildSettingsService.channels.logs
.set(channel.id, {
LastTemplateUpdateDate: new Date(),
LastTemplateComputeError: new KnownError({
type: "DataSourceError",
name: "NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL",
}).message,
LastTemplateComputeError: new KnownError(
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL",
).message,
})
.catch(logger.error);

Expand Down
30 changes: 26 additions & 4 deletions apps/bot/src/locales/en-US/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,31 @@
"computeError": "ERROR!"
}
},
"errors": {
"UserError": {
"USER_NOT_FOUND": "User not found."
}
"knownErrors": {
"USER_NOT_FOUND": "User not found.",
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
}
54 changes: 27 additions & 27 deletions apps/website/src/@types/resources.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -588,33 +588,33 @@ interface Resources {
"forumChannel": "Forum channel",
"mediaChannel": "Media channel"
},
"errors": {
"DataSourceError": {
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
"knownError": "Error",
"knownErrors": {
"USER_NOT_FOUND": "User not found.",
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
},
"dataSourceMetadata": {
Expand Down
30 changes: 30 additions & 0 deletions apps/website/src/hooks/useShowError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { TRPCClientError } from "@trpc/client";
import { useTranslation } from "react-i18next";
import { z } from "zod";

import { KnownErrorsTypeNames } from "@mc/common/KnownError/index";
import { useToast } from "@mc/ui/hooks/use-toast";

export default function useShowError() {
const { t } = useTranslation();
const { toast } = useToast();

const showUnknown = (error: unknown): unknown => {
toast({ title: t("common.unknownError"), variant: "destructive" });
return error;
};

return (error: unknown) => {
if (!(error instanceof TRPCClientError)) throw showUnknown(error);

const errorMessage = z.enum(KnownErrorsTypeNames).safeParse(error.message);

if (!errorMessage.success) throw showUnknown(error);

toast({
title: t(`common.knownError`),
description: t(`common.knownErrors.${errorMessage.data}`),
variant: "destructive",
});
};
}
54 changes: 27 additions & 27 deletions apps/website/src/i18n/locales/en-US/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -587,33 +587,33 @@
"forumChannel": "Forum channel",
"mediaChannel": "Media channel"
},
"errors": {
"DataSourceError": {
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
"knownError": "Error",
"knownErrors": {
"USER_NOT_FOUND": "User not found.",
"UNKNOWN": "An unknown error occurred.",
"UNKNOWN_DATA_SOURCE": "The counter provided is unknown or not recognized.",
"UNKNOWN_EVALUATION_RETURN_TYPE": "The type of value returned from evaluation is unknown.",
"FAILED_TO_RETURN_A_FINAL_STRING": "Failed to generate a final string from the evaluation.",
"DELIMITED_DATA_SOURCE_IS_ILLEGAL_JSON": "The counter is in a unrecognized format",
"DELIMITED_DATA_SOURCE_IS_INVALID": "The counter is invalid.",
"EVALUATION_RESULT_FOR_CHANNEL_NAME_IS_LESS_THAN_2_CHARACTERS": "The result for the channel name is less than 2 characters long.",
"NO_ENOUGH_PERMISSIONS_TO_EDIT_CHANNEL": "The bot does not have sufficient permissions to edit the channel.",
"MEMERATOR_MISSING_USERNAME": "The Memerator counter requires a username, which is missing.",
"REDDIT_MISSING_SUBREDDIT": "A subreddit must be provided for the Reddit coutner.",
"TWITCH_MISSING_USERNAME": "The Twitch counter requires a username to be set.",
"TWITCH_CHANNEL_NOT_FOUND": "The specified Twitch channel could not be found.",
"YOUTUBE_MISSING_CHANNEL_URL": "The YouTube counter requires a channel URL to be set.",
"YOUTUBE_INVALID_CHANNEL_URL": "The provided YouTube channel URL is invalid.",
"HTTP_MISSING_URL": "The HTTP counter requires a URL, which is missing.",
"HTTP_INVALID_RESPONSE_CONTENT_TYPE": "The content type of the HTTP response is invalid (not text/plain or application/json).",
"HTTP_INVALID_RESPONSE_STATUS_CODE": "The HTTP response status code is invalid (not 200).",
"HTTP_DATA_PATH_MANDATORY": "A data path must be specified for HTTP requests whose content type is application/json.",
"GAME_MISSING_ADDRESS": "The game counter requires a server address.",
"GAME_MISSING_PORT": "The game counter requires a server port.",
"GAME_MISSING_GAME_ID": "A game ID must be provided for the game counter.",
"BOT_HAS_NO_ENOUGH_PRIVILEGED_INTENTS": "The bot does not have enough privileged intents to use this counter.",
"BOT_IS_NOT_PREMIUM": "The bot is not a premium version and lacks certain features needed for this counter.",
"MEMBER_COUNT_NOT_AVAILABLE": "The member counts are not available at this moment."
}
},
"dataSourceMetadata": {
Expand Down
5 changes: 1 addition & 4 deletions packages/common/src/KnownError/DataSourceError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,4 @@ export const DataSourceErrorNames = [
"MEMBER_COUNT_NOT_AVAILABLE",
] as const;

export interface DataSourceError {
type: "DataSourceError";
name: (typeof DataSourceErrorNames)[number];
}
export type DataSourceError = (typeof DataSourceErrorNames)[number];
7 changes: 3 additions & 4 deletions packages/common/src/KnownError/UserError.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export interface UserError {
type: "UserError";
name: "USER_NOT_FOUND";
}
export const UserErrorNames = ["USER_NOT_FOUND"] as const;

export type UserError = (typeof UserErrorNames)[number];
17 changes: 12 additions & 5 deletions packages/common/src/KnownError/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import type { DataSourceError } from "./DataSourceError";
import type { UserError } from "./UserError";
import { DataSourceErrorNames } from "./DataSourceError";
import { UserErrorNames } from "./UserError";

export type ErrorCause = DataSourceError | UserError;
export const KnownErrorsTypeNames = [
...DataSourceErrorNames,
...UserErrorNames,
] as const;
export type KnownErrorType = (typeof KnownErrorsTypeNames)[number];

export class KnownError extends Error {
constructor(public cause: ErrorCause) {
super(cause.type, { cause });
constructor(
public message: KnownErrorType,
options?: ErrorOptions,
) {
super(message, options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,7 @@ async function fetchData(
export const memeratorEvaluator = new DataSourceEvaluator({
id: DataSourceId.MEMERATOR,
execute: async ({ options }) => {
assert(
options.username,
new KnownError({
type: "DataSourceError",
name: "MEMERATOR_MISSING_USERNAME",
}),
);
assert(options.username, new KnownError("MEMERATOR_MISSING_USERNAME"));

return Number(await fetchData(options.username, options.return));
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ async function fetchData(
const channel = await client.users.getUserByName(username);

if (!channel) {
throw new KnownError({
type: "DataSourceError",
name: "TWITCH_CHANNEL_NOT_FOUND",
});
throw new KnownError("TWITCH_CHANNEL_NOT_FOUND");
}

const stream = await client.streams.getStreamByUserName(username);
Expand Down Expand Up @@ -107,17 +104,8 @@ export const twitchEvaluator = new DataSourceEvaluator({
id: DataSourceId.TWITCH,
execute: ({ ctx, options }) => {
const { isPremium } = ctx.guild.client.botInstanceOptions;
assert(
isPremium,
new KnownError({ type: "DataSourceError", name: "BOT_IS_NOT_PREMIUM" }),
);
assert(
options.username,
new KnownError({
type: "DataSourceError",
name: "TWITCH_MISSING_USERNAME",
}),
);
assert(isPremium, new KnownError("BOT_IS_NOT_PREMIUM"));
assert(options.username, new KnownError("TWITCH_MISSING_USERNAME"));

return fetchData(options.username, options.return);
},
Expand Down
Loading

0 comments on commit db298dc

Please sign in to comment.