feat(helpers): add stickers helpers (#2466)

* helpers: add stickers helpers

* deno fmt

* undo move getNitroStickerPack to stickers

* helpers/stickers: fix jsdocs

* plugins/permissions: check stickers permissions

* plugins/validations: validate stickers stuff

* tests: stickers unit tests

* helpers/stickers: add support for reason

* plugins/permissions: MANAGE_EMOJIS -> MANAGE_EMOJIS_AND_STICKERS

* tests/stickers: delete sticker after test
createGuildSticker: add send sticker test
getGuildStickers: create another sticker then test > 1 stickers
This commit is contained in:
LTS20050703
2022-09-22 11:31:41 -04:00
committed by GitHub
parent f0f9791ccd
commit 17132aaf61
25 changed files with 412 additions and 3 deletions
+8 -1
View File
@@ -3,7 +3,14 @@ import { StickerPack } from "../../transformers/sticker.ts";
import { DiscordStickerPack } from "../../types/discord.ts";
import { Collection } from "../../util/collection.ts";
/** Returns the list of sticker packs available to Nitro subscribers. */
/**
* Returns the list of sticker packs available to Nitro subscribers.
*
* @param bot The bot instance to use to make the request.
* @returns A collection of {@link StickerPack} objects assorted by sticker ID.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#list-nitro-sticker-packs}
*/
export async function getNitroStickerPacks(bot: Bot): Promise<Collection<bigint, StickerPack>> {
const results = await bot.rest.runMethod<DiscordStickerPack[]>(
bot.rest,
+1
View File
@@ -6,5 +6,6 @@ export * from "./members/mod.ts";
export * from "./messages/mod.ts";
export * from "./misc/mod.ts";
export * from "./roles/mod.ts";
export * from "./stickers/mod.ts";
export * from "./templates/mod.ts";
export * from "./webhooks/mod.ts";
+44
View File
@@ -0,0 +1,44 @@
import { Bot } from "../../bot.ts";
import { Sticker } from "../../transformers/sticker.ts";
import { FileContent, WithReason } from "../../types/discordeno.ts";
/**
* Create a new sticker for the guild.
*
* @param bot The bot instance to use to make the request.
* @param guildId The ID of the guild to get
* @return A {@link Sticker}
*
* @remarks
* Requires the `MANAGE_EMOJIS_AND_STICKERS` permission.
* Fires a Guild Stickers Update Gateway event.
* Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots.
* Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` guild feature.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#create-guild-sticker}
*/
export async function createGuildSticker(
bot: Bot,
guildId: bigint,
options: CreateGuildStickerOptions,
): Promise<Sticker> {
const result = await bot.rest.runMethod(bot.rest, "POST", bot.constants.routes.GUILD_STICKERS(guildId), {
name: options.name,
description: options.description,
tags: options.tags,
file: options.file,
reason: options.reason,
});
return bot.transformers.sticker(bot, result);
}
export interface CreateGuildStickerOptions extends WithReason {
/** Name of the sticker (2-30 characters) */
name: string;
/** Description of the sticker (empty or 2-100 characters) */
description: string;
/** Autocomplete/suggestion tags for the sticker (max 200 characters) */
tags: string;
/** The sticker file to upload, must be a PNG, APNG, or Lottie JSON file, max 500 KB */
file: FileContent;
}
+26
View File
@@ -0,0 +1,26 @@
import { Bot } from "../../bot.ts";
import { Sticker } from "../../transformers/sticker.ts";
/**
* Delete a new sticker for the guild.
*
* @param bot The bot instance to use to make the request.
* @param guildId The ID of the guild to get
* @return A {@link Sticker}
*
* @remarks
* Requires the `MANAGE_EMOJIS_AND_STICKERS` permission.
* Fires a Guild Stickers Update Gateway event.
* Every guilds has five free sticker slots by default, and each Boost level will grant access to more slots.
* Lottie stickers can only be uploaded on guilds that have either the `VERIFIED` and/or the `PARTNERED` guild feature.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#delete-guild-sticker}
*/
export async function deleteGuildSticker(bot: Bot, guildId: bigint, stickerId: bigint, reason?: string): Promise<void> {
return await bot.rest.runMethod<void>(
bot.rest,
"DELETE",
bot.constants.routes.GUILD_STICKER(guildId, stickerId),
reason ? { reason } : undefined,
);
}
+41
View File
@@ -0,0 +1,41 @@
import { Bot } from "../../bot.ts";
import { Sticker } from "../../transformers/sticker.ts";
import { WithReason } from "../../types/discordeno.ts";
import { AtLeastOne } from "../../types/shared.ts";
/**
* Edit the given sticker.
*
* @param bot The bot instance to use to make the request.
* @param guildId The ID of the guild to get
* @return A {@link Sticker}
*
* @remarks
* Requires the `MANAGE_EMOJIS_AND_STICKERS` permission.
* Fires a Guild Stickers Update Gateway event.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#modify-guild-sticker}
*/
export async function editGuildSticker(
bot: Bot,
guildId: bigint,
stickerId: bigint,
options: AtLeastOne<EditGuildStickerOptions>,
): Promise<Sticker> {
const result = await bot.rest.runMethod(bot.rest, "PATCH", bot.constants.routes.GUILD_STICKER(guildId, stickerId), {
name: options.name,
description: options.description,
tags: options.tags,
reason: options.reason,
});
return bot.transformers.sticker(bot, result);
}
export interface EditGuildStickerOptions extends WithReason {
/** Name of the sticker (2-30 characters) */
name?: string;
/** Description of the sticker (empty or 2-100 characters) */
description?: string | null;
/** Autocomplete/suggestion tags for the sticker (max 200 characters) */
tags?: string;
}
+18
View File
@@ -0,0 +1,18 @@
import { Bot, Sticker } from "../../mod.ts";
/**
* Returns a sticker object for the given guild and sticker IDs.
*
* @param bot The bot instance to use to make the request.
* @param guildId The ID of the guild to get
* @param stickerId The ID of the sticker to get
* @return A {@link Sticker}
*
* @remarks Includes the user field if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#get-guild-sticker}
*/
export async function getGuildSticker(bot: Bot, guildId: bigint, stickerId: bigint): Promise<Sticker> {
const result = await bot.rest.runMethod(bot.rest, "GET", bot.constants.routes.GUILD_STICKER(guildId, stickerId));
return bot.transformers.sticker(bot, result);
}
+29
View File
@@ -0,0 +1,29 @@
import { Bot } from "../../bot.ts";
import { Collection, Sticker } from "../../mod.ts";
import { DiscordSticker } from "../../types/discord.ts";
/**
* Returns an array of sticker objects for the given guild.
*
* @param bot The bot instance to use to make the request.
* @param guildId The ID of the guild to get
* @returns A collection of {@link Sticker} objects assorted by sticker ID.
*
* @remarks Includes user fields if the bot has the `MANAGE_EMOJIS_AND_STICKERS` permission.
*
* @see {@link https://discord.com/developers/docs/resources/sticker#list-guild-stickers}
*/
export async function getGuildStickers(bot: Bot, guildId: bigint): Promise<Collection<bigint, Sticker>> {
const results = await bot.rest.runMethod<DiscordSticker[]>(
bot.rest,
"GET",
bot.constants.routes.GUILD_STICKERS(guildId),
);
return new Collection(
results.map((result) => {
const pack = bot.transformers.sticker(bot, result);
return [pack.id, pack];
}),
);
}
+18
View File
@@ -0,0 +1,18 @@
import { Bot } from "../../bot.ts";
import { Sticker } from "../../mod.ts";
import { DiscordSticker } from "../../types/discord.ts";
/**
* Returns a sticker object for the given sticker ID.
*
* @param bot The bot instance to use to make the request.
* @param stickerId The ID of the sticker to get
* @returns A {@link Sticker}
*
* @see {@link https://discord.com/developers/docs/resources/sticker#get-sticker}
*/
export async function getSticker(bot: Bot, stickerId: bigint): Promise<Sticker> {
const result = await bot.rest.runMethod<DiscordSticker>(bot.rest, "GET", bot.constants.routes.STICKER(stickerId));
return bot.transformers.sticker(bot, result);
}
+6
View File
@@ -0,0 +1,6 @@
export * from "./createGuildSticker.ts";
export * from "./deleteGuildSticker.ts";
export * from "./editGuildSticker.ts";
export * from "./getGuildSticker.ts";
export * from "./getGuildStickers.ts";
export * from "./getSticker.ts";
+2
View File
@@ -6,6 +6,7 @@ import { integrations } from "./src/integrations/mod.ts";
import { members } from "./src/members/mod.ts";
import { messages } from "./src/messages/mod.ts";
import { roles } from "./src/roles/mod.ts";
import { stickers } from "./src/stickers/mod.ts";
import { webhooks } from "./src/webhooks/mod.ts";
// PLUGINS MUST TAKE A BOT ARGUMENT WHICH WILL BE MODIFIED
@@ -24,6 +25,7 @@ export function enablePermissionsPlugin<B extends BotWithCache = BotWithCache>(b
members(bot);
messages(bot);
roles(bot);
stickers(bot);
webhooks(bot);
// PLUGINS MUST RETURN THE BOT
@@ -0,0 +1,10 @@
import { BotWithCache } from "../../deps.ts";
import { requireBotGuildPermissions } from "../permissions.ts";
export async function createGuildSticker(bot: BotWithCache) {
const createGuildSticker = bot.helpers.createGuildSticker;
bot.helpers.createGuildSticker = (guildId, options) => {
requireBotGuildPermissions(bot, guildId, ["MANAGE_EMOJIS_AND_STICKERS"]);
return createGuildSticker(guildId, options);
};
}
@@ -0,0 +1,10 @@
import { BotWithCache } from "../../deps.ts";
import { requireBotGuildPermissions } from "../permissions.ts";
export async function deleteGuildSticker(bot: BotWithCache) {
const deleteGuildSticker = bot.helpers.deleteGuildSticker;
bot.helpers.deleteGuildSticker = (guildId, stickerId, reason) => {
requireBotGuildPermissions(bot, guildId, ["MANAGE_EMOJIS_AND_STICKERS"]);
return deleteGuildSticker(guildId, stickerId, reason);
};
}
@@ -0,0 +1,10 @@
import { BotWithCache } from "../../deps.ts";
import { requireBotGuildPermissions } from "../permissions.ts";
export async function editGuildSticker(bot: BotWithCache) {
const editGuildSticker = bot.helpers.editGuildSticker;
bot.helpers.editGuildSticker = (guildId, stickerId, options) => {
requireBotGuildPermissions(bot, guildId, ["MANAGE_EMOJIS_AND_STICKERS"]);
return editGuildSticker(guildId, stickerId, options);
};
}
+10
View File
@@ -0,0 +1,10 @@
import { BotWithCache } from "../../deps.ts";
import { createGuildSticker } from "./createGuildSticker.ts";
import { deleteGuildSticker } from "./deleteGuildSticker.ts";
import { editGuildSticker } from "./editGuildSticker.ts";
export function stickers(bot: BotWithCache) {
createGuildSticker(bot);
deleteGuildSticker(bot);
editGuildSticker(bot);
}
+2
View File
@@ -6,6 +6,7 @@ import { invites } from "./src/invites/mod.ts";
import { members } from "./src/members/mod.ts";
import { messages } from "./src/messages/mod.ts";
import { misc } from "./src/misc/mod.ts";
import { stickers } from "./src/stickers/mod.ts";
import { webhooks } from "./src/webhooks/mod.ts";
// PLUGINS MUST TAKE A BOT ARGUMENT WHICH WILL BE MODIFIED
@@ -21,6 +22,7 @@ export function enableValidationsPlugin<B extends Bot>(bot: B): B {
members(bot);
messages(bot);
misc(bot);
stickers(bot);
webhooks(bot);
// PLUGINS MUST RETURN THE BOT
@@ -0,0 +1,20 @@
import { Bot } from "../../../../bot.ts";
export function createGuildSticker(bot: Bot) {
const createGuildSticker = bot.helpers.createGuildSticker;
bot.helpers.createGuildSticker = (guildId, options) => {
if (!bot.utils.validateLength(options.name, { min: 2, max: 30 })) {
throw new Error("Sticker name length must be between 2 and 30 characters");
}
// description of the sticker (empty or 2-100 characters)
if (options.description !== "" && !bot.utils.validateLength(options.description, { min: 2, max: 100 })) {
throw new Error(
"Sticker description must be empty or sticker description length must be between 2 and 100 characters",
);
}
if (!bot.utils.validateLength(options.tags, { max: 200 })) {
throw new Error("Sticker tags length must be less than 200 characters");
}
return createGuildSticker(guildId, options);
};
}
@@ -0,0 +1,23 @@
import { Bot } from "../../../../bot.ts";
export function editGuildSticker(bot: Bot) {
const editGuildSticker = bot.helpers.editGuildSticker;
bot.helpers.editGuildSticker = (guildId, stickerId, options) => {
if (options.name && !bot.utils.validateLength(options.name, { min: 2, max: 30 })) {
throw new Error("Sticker name length must be between 2 and 30 characters");
}
// description of the sticker (empty or 2-100 characters)
if (
options.description && options.description !== "" &&
!bot.utils.validateLength(options.description, { min: 2, max: 100 })
) {
throw new Error(
"Sticker description must be empty or sticker description length must be between 2 and 100 characters",
);
}
if (options.tags && !bot.utils.validateLength(options.tags, { max: 200 })) {
throw new Error("Sticker tags length must be less than 200 characters");
}
return editGuildSticker(guildId, stickerId, options);
};
}
+8
View File
@@ -0,0 +1,8 @@
import { Bot } from "../../../../bot.ts";
import { createGuildSticker } from "./createGuildSticker.ts";
import { editGuildSticker } from "./editGuildSticker.ts";
export function stickers(bot: Bot) {
createGuildSticker(bot);
editGuildSticker(bot);
}
+27
View File
@@ -0,0 +1,27 @@
import { StickerFormatTypes } from "../../mod.ts";
import { assertEquals } from "../deps.ts";
import { loadBot } from "../mod.ts";
import { CACHED_COMMUNITY_GUILD_ID } from "../utils.ts";
Deno.test("[stickers] Create guild sticker", async () => {
const bot = loadBot();
const sticker = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "sticker name",
description: "sticker description",
tags: "sticker tags",
file: { blob: new Blob(), name: "test.png" },
});
assertEquals(sticker.name, "sticker name");
assertEquals(sticker.description, "sticker description");
assertEquals(sticker.tags, "sticker tags");
const channel = await bot.helpers.createChannel(CACHED_COMMUNITY_GUILD_ID);
const message = await bot.helpers.sendMessage(channel.id, { stickerIds: [sticker.id] });
assertEquals(message.stickerItems?.[0].formatType, StickerFormatTypes.Png);
assertEquals(message.stickerItems?.[0].id, sticker.id);
assertEquals(message.stickerItems?.[0].name, sticker.name);
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, sticker.id);
});
+15
View File
@@ -0,0 +1,15 @@
import { assertRejects } from "../deps.ts";
import { loadBot } from "../mod.ts";
import { CACHED_COMMUNITY_GUILD_ID } from "../utils.ts";
Deno.test("[stickers] Delete guild sticker", async () => {
const bot = loadBot();
const sticker = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "sticker name",
description: "sticker description",
tags: "sticker tags",
file: { blob: new Blob(), name: "sticker file name" },
});
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, sticker.id);
await assertRejects(() => bot.helpers.getGuildSticker(CACHED_COMMUNITY_GUILD_ID, sticker.id));
});
+23
View File
@@ -0,0 +1,23 @@
import { assertEquals } from "../deps.ts";
import { loadBot } from "../mod.ts";
import { CACHED_COMMUNITY_GUILD_ID } from "../utils.ts";
Deno.test("[stickers] Edit guild sticker", async () => {
const bot = loadBot();
const createSticker = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "test",
description: "test",
tags: "test",
file: { blob: new Blob(), name: "test.png" },
});
const editSticker = await bot.helpers.editGuildSticker(CACHED_COMMUNITY_GUILD_ID, createSticker.id, {
name: "sticker name",
description: "sticker description",
tags: "sticker tags",
});
assertEquals(editSticker.name, "sticker name");
assertEquals(editSticker.description, "sticker description");
assertEquals(editSticker.tags, "sticker tags");
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, editSticker.id);
});
+19
View File
@@ -0,0 +1,19 @@
import { assertEquals } from "../deps.ts";
import { loadBot } from "../mod.ts";
import { CACHED_COMMUNITY_GUILD_ID } from "../utils.ts";
Deno.test("[stickers] Get guild sticker", async () => {
const bot = loadBot();
const createSticker = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "sticker name",
description: "sticker description",
tags: "sticker tags",
file: { blob: new Blob(), name: "sticker file name" },
});
const getSticker = await bot.helpers.getGuildSticker(CACHED_COMMUNITY_GUILD_ID, createSticker.id);
assertEquals(getSticker.name, "sticker name");
assertEquals(getSticker.description, "sticker description");
assertEquals(getSticker.tags, "sticker tags");
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, getSticker.id);
});
+23
View File
@@ -0,0 +1,23 @@
import { assertEquals } from "../deps.ts";
import { loadBot } from "../mod.ts";
import { CACHED_COMMUNITY_GUILD_ID } from "../utils.ts";
Deno.test("[stickers] Get guild stickers", async () => {
const bot = loadBot();
const sticker1 = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "sticker 1",
description: "sticker 1",
tags: "sticker tags 1",
file: { blob: new Blob(), name: "test1.png" },
});
const sticker2 = await bot.helpers.createGuildSticker(CACHED_COMMUNITY_GUILD_ID, {
name: "sticker 2",
description: "sticker 2",
tags: "sticker tags 2",
file: { blob: new Blob(), name: "test2.png" },
});
const stickers = await bot.helpers.getGuildStickers(CACHED_COMMUNITY_GUILD_ID);
assertEquals(stickers.size > 1, true);
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, sticker1.id);
await bot.helpers.deleteGuildSticker(CACHED_COMMUNITY_GUILD_ID, sticker2.id);
});
+8
View File
@@ -0,0 +1,8 @@
import { assertEquals } from "../deps.ts";
import { loadBot } from "../mod.ts";
Deno.test("[stickers] Get sticker", async () => {
const bot = loadBot();
const sticker = await bot.helpers.getSticker(749054660769218631n);
assertEquals(sticker.name, "Wave");
});
+11 -2
View File
@@ -508,8 +508,17 @@ export const routes = {
return `/stage-instances/${channelId}`;
},
// Misc Endpoints
// Stickers Endpoints
NITRO_STICKER_PACKS: () => {
return `/sticker-packs`;
return "/sticker-packs";
},
STICKER: (stickerId: bigint) => {
return `/stickers/${stickerId}`;
},
GUILD_STICKERS: (guildId: bigint) => {
return `/guilds/${guildId}/stickers`;
},
GUILD_STICKER: (guildId: bigint, stickerId: bigint) => {
return `/guilds/${guildId}/stickers/${stickerId}`;
},
};