mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 11:28:15 +00:00
feat: add validateLength util
This commit is contained in:
@@ -10,6 +10,7 @@ import { PermissionStrings } from "../../types/permissions/permission_strings.ts
|
|||||||
import { endpoints } from "../../util/constants.ts";
|
import { endpoints } from "../../util/constants.ts";
|
||||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||||
import { camelKeysToSnakeCase } from "../../util/utils.ts";
|
import { camelKeysToSnakeCase } from "../../util/utils.ts";
|
||||||
|
import { validateLength } from "../../util/validate_length.ts";
|
||||||
|
|
||||||
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||||
export async function sendMessage(
|
export async function sendMessage(
|
||||||
@@ -48,7 +49,7 @@ export async function sendMessage(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use ... for content length due to unicode characters and js .length handling
|
// Use ... for content length due to unicode characters and js .length handling
|
||||||
if (content.content && [...content.content].length > 2000) {
|
if (content.content && !validateLength(content.content, { max: 2000 })) {
|
||||||
throw new Error(Errors.MESSAGE_MAX_LENGTH);
|
throw new Error(Errors.MESSAGE_MAX_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { DiscordWebhook } from "../../types/webhooks/webhook.ts";
|
|||||||
import { endpoints } from "../../util/constants.ts";
|
import { endpoints } from "../../util/constants.ts";
|
||||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||||
import { snakeKeysToCamelCase, urlToBase64 } from "../../util/utils.ts";
|
import { snakeKeysToCamelCase, urlToBase64 } from "../../util/utils.ts";
|
||||||
|
import { validateLength } from "../../util/validate_length.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
|
* Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
|
||||||
@@ -21,9 +22,7 @@ export async function createWebhook(
|
|||||||
if (
|
if (
|
||||||
// Specific usernames that discord does not allow
|
// Specific usernames that discord does not allow
|
||||||
options.name === "clyde" ||
|
options.name === "clyde" ||
|
||||||
// Character limit checks. [...] checks are because of js unicode length handling
|
!validateLength(options.name, { min: 2, max: 32 })
|
||||||
[...options.name].length < 2 ||
|
|
||||||
[...options.name].length > 32
|
|
||||||
) {
|
) {
|
||||||
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-25
@@ -9,6 +9,7 @@ import { DiscordImageFormat } from "../types/misc/image_format.ts";
|
|||||||
import { DiscordImageSize } from "../types/misc/image_size.ts";
|
import { DiscordImageSize } from "../types/misc/image_size.ts";
|
||||||
import { EditGlobalApplicationCommand } from "../types/mod.ts";
|
import { EditGlobalApplicationCommand } from "../types/mod.ts";
|
||||||
import { SLASH_COMMANDS_NAME_REGEX } from "./constants.ts";
|
import { SLASH_COMMANDS_NAME_REGEX } from "./constants.ts";
|
||||||
|
import { validateLength } from "./validate_length.ts";
|
||||||
|
|
||||||
export async function urlToBase64(url: string) {
|
export async function urlToBase64(url: string) {
|
||||||
const buffer = await fetch(url).then((res) => res.arrayBuffer());
|
const buffer = await fetch(url).then((res) => res.arrayBuffer());
|
||||||
@@ -34,10 +35,11 @@ export function delay(ms: number): Promise<void> {
|
|||||||
export const formatImageURL = (
|
export const formatImageURL = (
|
||||||
url: string,
|
url: string,
|
||||||
size: DiscordImageSize = 128,
|
size: DiscordImageSize = 128,
|
||||||
format?: DiscordImageFormat,
|
format?: DiscordImageFormat
|
||||||
) => {
|
) => {
|
||||||
return `${url}.${format ||
|
return `${url}.${
|
||||||
(url.includes("/a_") ? "gif" : "jpg")}?size=${size}`;
|
format || (url.includes("/a_") ? "gif" : "jpg")
|
||||||
|
}?size=${size}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
function camelToSnakeCase(text: string) {
|
function camelToSnakeCase(text: string) {
|
||||||
@@ -45,22 +47,23 @@ function camelToSnakeCase(text: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function snakeToCamelCase(text: string) {
|
function snakeToCamelCase(text: string) {
|
||||||
return text.replace(
|
return text.replace(/([-_][a-z])/gi, ($1) =>
|
||||||
/([-_][a-z])/gi,
|
$1.toUpperCase().replace("_", "")
|
||||||
($1) => $1.toUpperCase().replace("_", ""),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isConvertableObject(obj: unknown) {
|
function isConvertableObject(obj: unknown) {
|
||||||
return (
|
return (
|
||||||
obj === Object(obj) && !Array.isArray(obj) && typeof obj !== "function" &&
|
obj === Object(obj) &&
|
||||||
|
!Array.isArray(obj) &&
|
||||||
|
typeof obj !== "function" &&
|
||||||
!(obj instanceof Blob)
|
!(obj instanceof Blob)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function camelKeysToSnakeCase<T>(
|
export function camelKeysToSnakeCase<T>(
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
obj: Record<string, any> | Record<string, any>[],
|
obj: Record<string, any> | Record<string, any>[]
|
||||||
): T {
|
): T {
|
||||||
if (isConvertableObject(obj)) {
|
if (isConvertableObject(obj)) {
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
@@ -69,11 +72,11 @@ export function camelKeysToSnakeCase<T>(
|
|||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
eventHandlers.debug?.(
|
eventHandlers.debug?.(
|
||||||
"loop",
|
"loop",
|
||||||
`Running forEach loop in camelKeysToSnakeCase function.`,
|
`Running forEach loop in camelKeysToSnakeCase function.`
|
||||||
);
|
);
|
||||||
convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase(
|
convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase(
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
(obj as Record<string, any>)[key],
|
(obj as Record<string, any>)[key]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,7 +90,7 @@ export function camelKeysToSnakeCase<T>(
|
|||||||
|
|
||||||
export function snakeKeysToCamelCase<T>(
|
export function snakeKeysToCamelCase<T>(
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
obj: Record<string, any> | Record<string, any>[],
|
obj: Record<string, any> | Record<string, any>[]
|
||||||
): T {
|
): T {
|
||||||
if (isConvertableObject(obj)) {
|
if (isConvertableObject(obj)) {
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
@@ -96,11 +99,11 @@ export function snakeKeysToCamelCase<T>(
|
|||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
eventHandlers.debug?.(
|
eventHandlers.debug?.(
|
||||||
"loop",
|
"loop",
|
||||||
`Running forEach loop in snakeKeysToCamelCase function.`,
|
`Running forEach loop in snakeKeysToCamelCase function.`
|
||||||
);
|
);
|
||||||
convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase(
|
convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase(
|
||||||
// deno-lint-ignore no-explicit-any
|
// deno-lint-ignore no-explicit-any
|
||||||
(obj as Record<string, any>)[key],
|
(obj as Record<string, any>)[key]
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -115,14 +118,14 @@ export function snakeKeysToCamelCase<T>(
|
|||||||
/** @private */
|
/** @private */
|
||||||
function validateSlashOptionChoices(
|
function validateSlashOptionChoices(
|
||||||
choices: ApplicationCommandOptionChoice[],
|
choices: ApplicationCommandOptionChoice[],
|
||||||
optionType: DiscordApplicationCommandOptionTypes,
|
optionType: DiscordApplicationCommandOptionTypes
|
||||||
) {
|
) {
|
||||||
for (const choice of choices) {
|
for (const choice of choices) {
|
||||||
eventHandlers.debug?.(
|
eventHandlers.debug?.(
|
||||||
"loop",
|
"loop",
|
||||||
`Running for of loop in validateSlashOptionChoices function.`,
|
`Running for of loop in validateSlashOptionChoices function.`
|
||||||
);
|
);
|
||||||
if ([...choice.name].length < 1 || [...choice.name].length > 100) {
|
if (!validateLength(choice.name, { min: 1, max: 100 })) {
|
||||||
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +147,7 @@ function validateSlashOptions(options: ApplicationCommandOption[]) {
|
|||||||
for (const option of options) {
|
for (const option of options) {
|
||||||
eventHandlers.debug?.(
|
eventHandlers.debug?.(
|
||||||
"loop",
|
"loop",
|
||||||
`Running for of loop in validateSlashOptions function.`,
|
`Running for of loop in validateSlashOptions function.`
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
option.choices?.length &&
|
option.choices?.length &&
|
||||||
@@ -156,10 +159,8 @@ function validateSlashOptions(options: ApplicationCommandOption[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[...option.name].length < 1 ||
|
!validateLength(option.name, { min: 1, max: 32 }) ||
|
||||||
[...option.name].length > 32 ||
|
!validateLength(option.description, { min: 1, max: 100 })
|
||||||
[...option.description].length < 1 ||
|
|
||||||
[...option.description].length > 100
|
|
||||||
) {
|
) {
|
||||||
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
|
||||||
}
|
}
|
||||||
@@ -172,12 +173,12 @@ function validateSlashOptions(options: ApplicationCommandOption[]) {
|
|||||||
|
|
||||||
export function validateSlashCommands(
|
export function validateSlashCommands(
|
||||||
commands: (CreateGlobalApplicationCommand | EditGlobalApplicationCommand)[],
|
commands: (CreateGlobalApplicationCommand | EditGlobalApplicationCommand)[],
|
||||||
create = false,
|
create = false
|
||||||
) {
|
) {
|
||||||
for (const command of commands) {
|
for (const command of commands) {
|
||||||
eventHandlers.debug?.(
|
eventHandlers.debug?.(
|
||||||
"loop",
|
"loop",
|
||||||
`Running for of loop in validateSlashCommands function.`,
|
`Running for of loop in validateSlashCommands function.`
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
(command.name && !SLASH_COMMANDS_NAME_REGEX.test(command.name)) ||
|
(command.name && !SLASH_COMMANDS_NAME_REGEX.test(command.name)) ||
|
||||||
@@ -188,8 +189,7 @@ export function validateSlashCommands(
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
(command.description &&
|
(command.description &&
|
||||||
([...command.description].length < 1 ||
|
!validateLength(command.description, { min: 1, max: 100 })) ||
|
||||||
[...command.description].length > 100)) ||
|
|
||||||
(create && !command.description)
|
(create && !command.description)
|
||||||
) {
|
) {
|
||||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/** Validates the length of a string in JS. Certain characters in JS can have multiple numbers in length in unicode and discords api is in python which treats length differently. */
|
||||||
|
export function validateLength(
|
||||||
|
text: string,
|
||||||
|
options: { max?: number; min?: number }
|
||||||
|
) {
|
||||||
|
const length = [...text].length;
|
||||||
|
|
||||||
|
// Text is too long
|
||||||
|
if (options.max && length > options.max) return false;
|
||||||
|
// Text is too short
|
||||||
|
if (options.min && length < options.min) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
// First complete non-api reliant testing.
|
// First complete non-api reliant testing.
|
||||||
// Don't waste api rate limits if a early test fails.
|
// Don't waste api rate limits if a early test fails.
|
||||||
import "./util/utils.ts";
|
import "./util/utils.ts";
|
||||||
|
import "./util/validate_length.ts";
|
||||||
|
|
||||||
// API TESTING BELOW
|
// API TESTING BELOW
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { validateLength } from "../../src/util/validate_length.ts";
|
||||||
|
import { assertEquals } from "../deps.ts";
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is too low",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { min: 5 }), false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is too high",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { max: 3 }), false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is NOT just right in between.",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { min: 5, max: 3 }), false);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is NOT too low",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { min: 3 }), true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is NOT too high",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { max: 5 }), true);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test({
|
||||||
|
name: "[utils] Validate length is just right in between.",
|
||||||
|
fn() {
|
||||||
|
assertEquals(validateLength("test", { min: 3, max: 6 }), true);
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user