mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-03 09:20:08 +00:00
push latest to get easier to make new prs
This commit is contained in:
41
src/cache.ts
41
src/cache.ts
@@ -40,7 +40,9 @@ export function createCache(
|
||||
executedSlashCommands: new Set(),
|
||||
} as AsyncCache;
|
||||
|
||||
cache.execute = createExecute(cache);
|
||||
cache.execute = async function () {
|
||||
throw new Error("Async Cache requires a custom execute function to be implemented.");
|
||||
};
|
||||
|
||||
return cache;
|
||||
}
|
||||
@@ -161,25 +163,30 @@ export type CacheExecutor = (
|
||||
| "DELETE_CHANNELS_FROM_GUILD"
|
||||
| "DELETE_GUILD_FROM_MEMBER",
|
||||
options: Record<string, any>
|
||||
) => Promise<undefined>;
|
||||
) => Promise<any>;
|
||||
|
||||
export function createExecute(cache: Cache | AsyncCache): CacheExecutor {
|
||||
// @ts-ignore no time to look into these errors now
|
||||
return async (type, options) => {
|
||||
if (type === "DELETE_MESSAGES_FROM_CHANNEL") {
|
||||
await cache.messages.forEach(async (message) => {
|
||||
// @ts-ignore me smarter than u
|
||||
if (BigInt(message.channelId) === options.channelId) {
|
||||
await cache.messages.delete(BigInt(message.id));
|
||||
}
|
||||
});
|
||||
export function createExecute(cache: Cache): CacheExecutor {
|
||||
return function (type, options) {
|
||||
switch (type) {
|
||||
case "DELETE_MESSAGES_FROM_CHANNEL":
|
||||
cache.messages.forEach((message) => {
|
||||
if (message.channelId === options.channelId) {
|
||||
cache.messages.delete(message.id);
|
||||
}
|
||||
});
|
||||
return;
|
||||
case "BULK_DELETE_MESSAGES":
|
||||
return options.messageIds
|
||||
.map((id: bigint) => {
|
||||
const cached = cache.messages.get(id);
|
||||
if (!cached) return;
|
||||
|
||||
return undefined;
|
||||
cache.messages.delete(id);
|
||||
|
||||
return cached;
|
||||
})
|
||||
.filter((m: DiscordenoMessage) => m);
|
||||
}
|
||||
|
||||
// switch type {
|
||||
// case ""
|
||||
// }
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,16 @@ export async function handleMessageDeleteBulk(bot: Bot, data: DiscordGatewayPayl
|
||||
const payload = data.d as SnakeCasedPropertiesDeep<MessageDeleteBulk>;
|
||||
|
||||
const ids = payload.ids.map((id) => bot.transformers.snowflake(id));
|
||||
const messages = await bot.cache.execute("BULK_DELETE_MESSAGES", { messageIds: ids });
|
||||
const messages = (await bot.cache.execute("BULK_DELETE_MESSAGES", { messageIds: ids })) || [];
|
||||
|
||||
ids.forEach((id) => {
|
||||
// @ts-ignore let itoh fix cache typings hes the king of typigns and cache
|
||||
const msg = messages.find((m) => m.id === id);
|
||||
const message = msg ? bot.transformers.message(bot, msg) : undefined;
|
||||
const message = msg
|
||||
? bot.utils.hasProperty(msg, "authorId")
|
||||
? msg
|
||||
: bot.transformers.message(bot, msg)
|
||||
: undefined;
|
||||
|
||||
bot.events.messageDelete(
|
||||
bot,
|
||||
|
||||
@@ -3,21 +3,18 @@ import type { Emoji } from "../../types/emojis/emoji.ts";
|
||||
import type { Bot } from "../../bot.ts";
|
||||
|
||||
/** Create an emoji in the server. Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a JSON status code. If a URL is provided to the image parameter, Discordeno will automatically convert it to a base64 string internally. */
|
||||
export async function createEmoji(bot: Bot, guildId: bigint, name: string, image: string, options: CreateGuildEmoji) {
|
||||
export async function createEmoji(bot: Bot, guildId: bigint, options: CreateGuildEmoji) {
|
||||
await bot.utils.requireBotGuildPermissions(bot, guildId, ["MANAGE_EMOJIS"]);
|
||||
|
||||
if (image && !image.startsWith("data:image/")) {
|
||||
image = await bot.utils.urlToBase64(image);
|
||||
if (options.image && !options.image.startsWith("data:image/")) {
|
||||
options.image = await bot.utils.urlToBase64(options.image);
|
||||
}
|
||||
|
||||
const emoji = await bot.rest.runMethod<Emoji>(bot.rest,
|
||||
const emoji = await bot.rest.runMethod<Emoji>(
|
||||
bot.rest,
|
||||
"post",
|
||||
bot.constants.endpoints.GUILD_EMOJIS(guildId),
|
||||
{
|
||||
...options,
|
||||
name,
|
||||
image,
|
||||
}
|
||||
options
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -13,7 +13,7 @@ export async function deleteMessages(bot: Bot, channelId: bigint, ids: bigint[],
|
||||
}
|
||||
|
||||
return await bot.rest.runMethod<undefined>(bot.rest, "post", bot.constants.endpoints.CHANNEL_BULK_DELETE(channelId), {
|
||||
messages: ids.splice(0, 100),
|
||||
messages: ids.splice(0, 100).map(id => id.toString()),
|
||||
reason,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export function createRequestBody(rest: RestManager, queuedRequest: { request: R
|
||||
headers["Content-Type"] = "application/json";
|
||||
}
|
||||
|
||||
if (!queuedRequest.payload.body) headers["Content-Length"] = "0";
|
||||
return {
|
||||
headers,
|
||||
body: (queuedRequest.payload.body?.file || JSON.stringify(queuedRequest.payload.body)) as FormData | string,
|
||||
|
||||
@@ -53,10 +53,10 @@ export async function processGlobalQueue(rest: RestManager) {
|
||||
|
||||
try {
|
||||
// CUSTOM HANDLER FOR USER TO LOG OR WHATEVER WHENEVER A FETCH IS MADE
|
||||
rest.debug(`[REST - fetching] ${JSON.stringify(request.payload)}`);
|
||||
rest.debug(`[REST - fetching] URL: ${request.urlToUse} | ${JSON.stringify(request.payload)}`);
|
||||
|
||||
const response = await fetch(request.urlToUse, rest.createRequestBody(rest, request));
|
||||
rest.debug(`[REST - fetched] ${JSON.stringify(request.payload)}`);
|
||||
rest.debug(`[REST - fetched] URL: ${request.urlToUse} | ${JSON.stringify(request.payload)}`);
|
||||
|
||||
const bucketIdFromHeaders = rest.processRequestHeaders(rest, request.basicURL, response.headers);
|
||||
// SET THE BUCKET Id IF IT WAS PRESENT
|
||||
@@ -115,32 +115,13 @@ export async function processGlobalQueue(rest: RestManager) {
|
||||
|
||||
// SOMETIMES DISCORD RETURNS AN EMPTY 204 RESPONSE THAT CAN'T BE MADE TO JSON
|
||||
if (response.status === 204) {
|
||||
rest.debug(`[REST - FetchSuccess] ${JSON.stringify(request.payload)}`);
|
||||
rest.debug(`[REST - FetchSuccess] URL: ${request.urlToUse} | ${JSON.stringify(request.payload)}`);
|
||||
// REMOVE FROM QUEUE
|
||||
rest.globalQueue.shift();
|
||||
request.request.respond({ status: 204 });
|
||||
} else {
|
||||
// CONVERT THE RESPONSE TO JSON
|
||||
const json = await response.json();
|
||||
// IF THE RESPONSE WAS RATE LIMITED, HANDLE ACCORDINGLY
|
||||
// if (json.retry_after || json.message === "You are being rate limited.") {
|
||||
// // IF IT HAS MAXED RETRIES SOMETHING SERIOUSLY WRONG. CANCEL OUT.
|
||||
// if (request.payload.retryCount >= rest.maxRetryCount) {
|
||||
// rest.eventHandlers.retriesMaxed(request.payload);
|
||||
// request.request.respond({
|
||||
// status: 200,
|
||||
// body: JSON.stringify({
|
||||
// error: "The request was rate limited and it maxed out the retries limit.",
|
||||
// }),
|
||||
// });
|
||||
// // REMOVE ITEM FROM QUEUE TO PREVENT RETRY
|
||||
// rest.globalQueue.shift();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// // SINCE IT WAS RATELIMITE, RETRY AGAIN
|
||||
// continue;
|
||||
// }
|
||||
|
||||
rest.debug(`[REST - fetchSuccess] ${JSON.stringify(request.payload)}`);
|
||||
// REMOVE FROM QUEUE
|
||||
|
||||
@@ -5,7 +5,7 @@ export function processRateLimitedPaths(rest: RestManager) {
|
||||
const now = Date.now();
|
||||
|
||||
for (const [key, value] of rest.ratelimitedPaths.entries()) {
|
||||
rest.debug(`[REST - processRateLimitedPaths] Running forEach loop.`);
|
||||
rest.debug(`[REST - processRateLimitedPaths] Running for of loop.`);
|
||||
// IF THE TIME HAS NOT REACHED CANCEL
|
||||
if (value.resetTimestamp > now) continue;
|
||||
|
||||
@@ -18,13 +18,12 @@ export function processRateLimitedPaths(rest: RestManager) {
|
||||
// ALL PATHS ARE CLEARED CAN CANCEL OUT!
|
||||
if (!rest.ratelimitedPaths.size) {
|
||||
rest.processingRateLimitedPaths = false;
|
||||
return;
|
||||
} else {
|
||||
rest.processingRateLimitedPaths = true;
|
||||
// RECHECK IN 1 SECOND
|
||||
setTimeout(() => {
|
||||
rest.debug(`[REST - processRateLimitedPaths] Running setTimeout.`);
|
||||
processRateLimitedPaths(rest);
|
||||
rest.processRateLimitedPaths(rest);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Bot } from "../bot.ts";
|
||||
import { Interaction } from "../types/mod.ts";
|
||||
import { ApplicationCommandInteractionData, ButtonData, DiscordInteractionTypes, Interaction, SelectMenuData } from "../types/mod.ts";
|
||||
import { SnakeCasedPropertiesDeep } from "../types/util.ts";
|
||||
import { DiscordenoMember, DiscordenoUser } from "./member.ts";
|
||||
import { DiscordenoMessage } from "./message.ts";
|
||||
@@ -23,12 +23,12 @@ export function transformInteraction(bot: Bot, payload: SnakeCasedPropertiesDeep
|
||||
channelId: payload.channel_id ? bot.transformers.snowflake(payload.channel_id) : undefined,
|
||||
member: payload.member && guildId ? bot.transformers.member(bot, payload.member, guildId, user.id) : undefined,
|
||||
// TODO: CamelCase INTERACTION DATA
|
||||
// @ts-ignore
|
||||
data: payload.data,
|
||||
};
|
||||
}
|
||||
|
||||
export interface DiscordenoInteraction
|
||||
extends Omit<Interaction, "id" | "applicationId" | "guildId" | "channelId" | "member" | "user" | "message"> {
|
||||
export interface DiscordenoInteraction {
|
||||
/** Id of the interaction */
|
||||
id: bigint;
|
||||
/** Id of the application this interaction is for */
|
||||
@@ -43,4 +43,12 @@ export interface DiscordenoInteraction
|
||||
user: DiscordenoUser;
|
||||
/** For the message the button was attached to */
|
||||
message?: DiscordenoMessage;
|
||||
/** The type of interaction */
|
||||
type: DiscordInteractionTypes;
|
||||
/** A continuation token for responding to the interaction */
|
||||
token: string;
|
||||
/** Read-only property, always `1` */
|
||||
version: 1;
|
||||
|
||||
data?: ApplicationCommandInteractionData | ButtonData | SelectMenuData;
|
||||
}
|
||||
|
||||
@@ -5,5 +5,7 @@ export interface CreateGuildEmoji {
|
||||
/** The 128x128 emoji image */
|
||||
image: string;
|
||||
/** Roles allowed to use this emoji */
|
||||
roles: bigint[];
|
||||
}
|
||||
roles?: bigint[];
|
||||
/** The reason you are creating this emoji */
|
||||
reason?: string;
|
||||
}
|
||||
76
tests/helpers/messages/addReaction.ts
Normal file
76
tests/helpers/messages/addReaction.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { Bot } from "../../../src/bot.ts";
|
||||
import { assertEquals, assertExists } from "../../deps.ts";
|
||||
import { delayUntil } from "../../utils.ts";
|
||||
|
||||
export async function addReactionTest(
|
||||
bot: Bot,
|
||||
guildId: bigint,
|
||||
channelId: bigint,
|
||||
options: { custom: boolean; single: boolean, ordered: boolean; },
|
||||
t: Deno.TestContext
|
||||
) {
|
||||
const message = await bot.helpers.sendMessage(channelId, "Hello World!");
|
||||
|
||||
// Assertions
|
||||
assertExists(message);
|
||||
|
||||
// Delay the execution by 5 seconds to allow MESSAGE_CREATE event to be processed
|
||||
await delayUntil(10000, () => bot.cache.messages.has(message.id));
|
||||
|
||||
if (!bot.cache.messages.has(message.id)) {
|
||||
throw new Error("The message seemed to be sent but it was not cached.");
|
||||
}
|
||||
|
||||
let emojiId = "❤";
|
||||
let emojiIds = ["❤", "😃"];
|
||||
|
||||
if (options.custom) {
|
||||
if (options.single) {
|
||||
emojiId = `<:blamewolf:${
|
||||
(
|
||||
await bot.helpers.createEmoji(guildId, {
|
||||
name: "blamewolf",
|
||||
image: "https://cdn.discordapp.com/emojis/814955268123000832.png",
|
||||
// roles: [],
|
||||
})
|
||||
).id
|
||||
}>`;
|
||||
} else {
|
||||
emojiIds = [
|
||||
`<:blamewolf:${
|
||||
(
|
||||
await bot.helpers.createEmoji(guildId, {
|
||||
name: "blamewolf",
|
||||
image: "https://cdn.discordapp.com/emojis/814955268123000832.png",
|
||||
// roles: [],
|
||||
})
|
||||
).id
|
||||
}>`,
|
||||
`<:blamewolf2:${
|
||||
(
|
||||
await bot.helpers.createEmoji(guildId, {
|
||||
name: "blamewolf2",
|
||||
image: "https://cdn.discordapp.com/emojis/814955268123000832.png",
|
||||
// roles: [],
|
||||
})
|
||||
).id
|
||||
}>`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
let reactions = 0;
|
||||
|
||||
bot.events.reactionAdd = function (bot, payload) {
|
||||
if (payload.messageId !== message.id) return;
|
||||
|
||||
reactions++;
|
||||
};
|
||||
|
||||
if (options.single) await bot.helpers.addReaction(message.channelId, message.id, emojiId);
|
||||
else await bot.helpers.addReactions(message.channelId, message.id, emojiIds, options.ordered);
|
||||
|
||||
await delayUntil(10000, () => reactions === (options.single ? 1 : emojiIds.length));
|
||||
|
||||
assertEquals(reactions, options.single ? 1 : emojiIds.length);
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import { Bot } from "../../../src/bot.ts";
|
||||
import { assertExists } from "../../deps.ts";
|
||||
import { delayUntil } from "../../utils.ts";
|
||||
import { CreateMessage } from "../../../src/types/messages/create_message.ts";
|
||||
import { DiscordMessageComponentTypes } from "https://raw.githubusercontent.com/discordeno/discordeno/cool-stuff/src/types/messages/components/message_component_types.ts";
|
||||
import { DiscordButtonStyles } from "https://raw.githubusercontent.com/discordeno/discordeno/cool-stuff/src/types/messages/components/button_styles.ts";
|
||||
import { DiscordMessageComponentTypes } from "../../../src/types/messages/components/message_component_types.ts";
|
||||
import { DiscordButtonStyles } from "../../../src/types/messages/components/button_styles.ts";
|
||||
|
||||
async function ifItFailsBlameWolf(bot: Bot, channelId: bigint, content: string | CreateMessage) {
|
||||
const message = await bot.helpers.sendMessage(channelId, content);
|
||||
|
||||
101
tests/mod.ts
101
tests/mod.ts
@@ -14,6 +14,7 @@ import {
|
||||
// CONDUCT LOCAL TESTS FIRST BEFORE RUNNING API TEST
|
||||
import "./local.ts";
|
||||
import { getMessageTest } from "./helpers/messages/getMessage.ts";
|
||||
import { addReactionTest } from "./helpers/messages/addReaction.ts";
|
||||
|
||||
Deno.test("[Bot] - Starting Tests", async (t) => {
|
||||
// CHANGE TO TRUE WHEN DEBUGGING SANITIZATION ERRORS
|
||||
@@ -37,7 +38,7 @@ Deno.test("[Bot] - Starting Tests", async (t) => {
|
||||
},
|
||||
// debug: console.log,
|
||||
}),
|
||||
intents: ["Guilds", "GuildMessages"],
|
||||
intents: ["Guilds", "GuildMessages", "GuildMessageReactions"],
|
||||
cache: {
|
||||
isAsync: false,
|
||||
},
|
||||
@@ -100,13 +101,13 @@ Deno.test("[Bot] - Starting Tests", async (t) => {
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
t.step({
|
||||
name: "[message] send message with components",
|
||||
fn: async (t) => {
|
||||
await sendMessageWithComponents(bot, channel.id, t);
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
// t.step({
|
||||
// name: "[message] send message with components",
|
||||
// fn: async (t) => {
|
||||
// await sendMessageWithComponents(bot, channel.id, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
t.step({
|
||||
name: "[message] delete message without a reason",
|
||||
fn: async (t) => {
|
||||
@@ -128,27 +129,69 @@ Deno.test("[Bot] - Starting Tests", async (t) => {
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
t.step({
|
||||
name: "[message] delete messages with a reason",
|
||||
fn: async (t) => {
|
||||
await deleteMessagesWithReasonTest(bot, channel.id, t);
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
t.step({
|
||||
name: "[message] fetch a message",
|
||||
fn: async (t) => {
|
||||
await getMessageTest(bot, channel.id, t);
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
t.step({
|
||||
name: "[message] fetch messages",
|
||||
fn: async (t) => {
|
||||
await getMessagesTest(bot, channel.id, t);
|
||||
},
|
||||
...sanitizeMode,
|
||||
}),
|
||||
// t.step({
|
||||
// name: "[message] delete messages with a reason",
|
||||
// fn: async (t) => {
|
||||
// await deleteMessagesWithReasonTest(bot, channel.id, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] fetch a message",
|
||||
// fn: async (t) => {
|
||||
// await getMessageTest(bot, channel.id, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] fetch messages",
|
||||
// fn: async (t) => {
|
||||
// await getMessagesTest(bot, channel.id, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add a reaction",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: false, single: true, ordered: false }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add a custom reaction",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: true, single: true, ordered: false }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add multiple reactions",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: false, single: false, ordered: false }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add multiple custom reactions",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: true, single: false, ordered: false }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add multiple reactions in order",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: false, single: false, ordered: true }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
// t.step({
|
||||
// name: "[message] add multiple custom reactions in order",
|
||||
// fn: async (t) => {
|
||||
// await addReactionTest(bot, guild.id, channel.id, { custom: true, single: false, ordered: true }, t);
|
||||
// },
|
||||
// ...sanitizeMode,
|
||||
// }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user