mirror of
https://github.com/discordeno/discordeno.git
synced 2026-05-31 07:50:07 +00:00
Merge branch 'main' of https://github.com/discordeno/discordeno into main
This commit is contained in:
@@ -5,7 +5,6 @@ import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import { DiscordGatewayIntents } from "../../types/gateway/gateway_intents.ts";
|
||||
import type { RequestGuildMembers } from "../../types/members/request_guild_members.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { sendShardMessage } from "../../ws/send_shard_message.ts";
|
||||
import { ws } from "../../ws/ws.ts";
|
||||
|
||||
/**
|
||||
@@ -37,7 +36,7 @@ export function fetchMembers(
|
||||
const nonce = `${guildId}-${Date.now()}`;
|
||||
cache.fetchAllMembersProcessingRequests.set(nonce, resolve);
|
||||
|
||||
sendShardMessage(shardId, {
|
||||
ws.sendShardMessage(shardId, {
|
||||
op: DiscordGatewayOpcodes.RequestGuildMembers,
|
||||
d: {
|
||||
guild_id: guildId,
|
||||
|
||||
@@ -2,12 +2,13 @@ import { botId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { DiscordenoMessage } from "../../structures/message.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import { EditMessage } from "../../types/messages/edit_message.ts";
|
||||
import type { Message } from "../../types/messages/message.ts";
|
||||
import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import type { PermissionStrings } from "../../types/permissions/permission_strings.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||
import { validateComponents } from "../../util/utils.ts";
|
||||
|
||||
/** Edit the message. */
|
||||
export async function editMessage(
|
||||
@@ -20,6 +21,10 @@ export async function editMessage(
|
||||
|
||||
if (typeof content === "string") content = { content };
|
||||
|
||||
if (content.components?.length) {
|
||||
validateComponents(content.components);
|
||||
}
|
||||
|
||||
const requiredPerms: PermissionStrings[] = ["SEND_MESSAGES"];
|
||||
|
||||
await requireBotChannelPermissions(message.channelId, requiredPerms);
|
||||
|
||||
@@ -4,16 +4,13 @@ import { structures } from "../../structures/mod.ts";
|
||||
import { DiscordChannelTypes } from "../../types/channels/channel_types.ts";
|
||||
import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
|
||||
import { ButtonStyles } from "../../types/messages/components/button_styles.ts";
|
||||
import type { CreateMessage } from "../../types/messages/create_message.ts";
|
||||
import type { Message } from "../../types/messages/message.ts";
|
||||
import type { PermissionStrings } from "../../types/permissions/permission_strings.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||
import { snakelize } from "../../util/utils.ts";
|
||||
import { snakelize, validateComponents } from "../../util/utils.ts";
|
||||
import { validateLength } from "../../util/validate_length.ts";
|
||||
import { isActionRow } from "../type_guards/is_action_row.ts";
|
||||
import { isButton } from "../type_guards/is_button.ts";
|
||||
|
||||
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||
export async function sendMessage(
|
||||
@@ -100,49 +97,7 @@ export async function sendMessage(
|
||||
}
|
||||
|
||||
if (content.components?.length) {
|
||||
let actionRowCounter = 0;
|
||||
|
||||
for (const component of content.components) {
|
||||
// 5 Link buttons can not have a customId
|
||||
if (isButton(component)) {
|
||||
if (
|
||||
component.type === ButtonStyles.Link &&
|
||||
component.customId
|
||||
) {
|
||||
throw new Error(Errors.LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID);
|
||||
}
|
||||
// Other buttons must have a customId
|
||||
if (
|
||||
!component.customId && component.type !== ButtonStyles.Link
|
||||
) {
|
||||
throw new Error(Errors.BUTTON_REQUIRES_CUSTOM_ID);
|
||||
}
|
||||
|
||||
if (!validateLength(component.label, { max: 80 })) {
|
||||
throw new Error(Errors.COMPONENT_LABEL_TOO_BIG);
|
||||
}
|
||||
|
||||
if (
|
||||
component.customId &&
|
||||
!validateLength(component.customId, { max: 100 })
|
||||
) {
|
||||
throw new Error(Errors.COMPONENT_CUSTOM_ID_TOO_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActionRow(component)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
actionRowCounter++;
|
||||
// Max of 5 ActionRows per message
|
||||
if (actionRowCounter > 5) throw new Error(Errors.TOO_MANY_ACTION_ROWS);
|
||||
|
||||
// Max of 5 Buttons (or any component type) within an ActionRow
|
||||
if (component.components?.length > 5) {
|
||||
throw new Error(Errors.TOO_MANY_COMPONENTS);
|
||||
}
|
||||
}
|
||||
validateComponents(content.components);
|
||||
}
|
||||
|
||||
const result = await rest.runMethod<Message>(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayOpcodes } from "../../types/codes/gateway_opcodes.ts";
|
||||
import type { StatusUpdate } from "../../types/gateway/status_update.ts";
|
||||
import { sendShardMessage } from "../../ws/send_shard_message.ts";
|
||||
import { ws } from "../../ws/ws.ts";
|
||||
|
||||
export function editBotStatus(data: Omit<StatusUpdate, "afk" | "since">) {
|
||||
@@ -11,7 +10,7 @@ export function editBotStatus(data: Omit<StatusUpdate, "afk" | "since">) {
|
||||
`Running forEach loop in editBotStatus function.`,
|
||||
);
|
||||
|
||||
sendShardMessage(shard, {
|
||||
ws.sendShardMessage(shard, {
|
||||
op: DiscordGatewayOpcodes.StatusUpdate,
|
||||
d: {
|
||||
since: null,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
|
||||
import type { Message } from "../../types/messages/message.ts";
|
||||
import { Errors } from "../../types/discordeno/errors.ts";
|
||||
import type { EditWebhookMessage } from "../../types/webhooks/edit_webhook_message.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { validateComponents } from "../../util/utils.ts";
|
||||
|
||||
export async function editWebhookMessage(
|
||||
webhookId: bigint,
|
||||
@@ -59,6 +60,10 @@ export async function editWebhookMessage(
|
||||
}
|
||||
}
|
||||
|
||||
if (options.components?.length) {
|
||||
validateComponents(options.components);
|
||||
}
|
||||
|
||||
const result = await rest.runMethod<Message>(
|
||||
"patch",
|
||||
options.messageId
|
||||
|
||||
@@ -2,6 +2,7 @@ import { FileContent } from "../discordeno/file_content.ts";
|
||||
import { Embed } from "../embeds/embed.ts";
|
||||
import { AllowedMentions } from "./allowed_mentions.ts";
|
||||
import { Attachment } from "./attachment.ts";
|
||||
import { MessageComponents } from "./components/message_components.ts";
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#edit-message-json-params */
|
||||
export interface EditMessage {
|
||||
@@ -17,4 +18,6 @@ export interface EditMessage {
|
||||
allowedMentions?: AllowedMentions | null;
|
||||
/** Attached files to keep */
|
||||
attachments?: Attachment | null;
|
||||
/** The components you would like to have sent in this message */
|
||||
components?: MessageComponents;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { FileContent } from "../discordeno/file_content.ts";
|
||||
import { Embed } from "../embeds/embed.ts";
|
||||
import { AllowedMentions } from "../messages/allowed_mentions.ts";
|
||||
import { Attachment } from "../messages/attachment.ts";
|
||||
import { FileContent } from "../discordeno/file_content.ts";
|
||||
import { MessageComponents } from "../messages/components/message_components.ts";
|
||||
|
||||
/** https://discord.com/developers/docs/resources/webhook#edit-webhook-message-jsonform-params */
|
||||
export interface EditWebhookMessage {
|
||||
@@ -15,4 +16,6 @@ export interface EditWebhookMessage {
|
||||
allowedMentions?: AllowedMentions | null;
|
||||
/** Attached files to keep */
|
||||
attachments?: Attachment | null;
|
||||
/** The components you would like to have sent in this message */
|
||||
components?: MessageComponents;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { encode } from "../../deps.ts";
|
||||
import { eventHandlers } from "../bot.ts";
|
||||
import { isActionRow } from "../helpers/type_guards/is_action_row.ts";
|
||||
import { isButton } from "../helpers/type_guards/is_button.ts";
|
||||
import { Errors } from "../types/discordeno/errors.ts";
|
||||
import type { ApplicationCommandOption } from "../types/interactions/commands/application_command_option.ts";
|
||||
import type { ApplicationCommandOptionChoice } from "../types/interactions/commands/application_command_option_choice.ts";
|
||||
import { DiscordApplicationCommandOptionTypes } from "../types/interactions/commands/application_command_option_types.ts";
|
||||
import type { CreateGlobalApplicationCommand } from "../types/interactions/commands/create_global_application_command.ts";
|
||||
import type { EditGlobalApplicationCommand } from "../types/interactions/commands/edit_global_application_command.ts";
|
||||
import { ButtonStyles } from "../types/messages/components/button_styles.ts";
|
||||
import type { MessageComponents } from "../types/messages/components/message_components.ts";
|
||||
import type { DiscordImageFormat } from "../types/misc/image_format.ts";
|
||||
import type { DiscordImageSize } from "../types/misc/image_size.ts";
|
||||
import { SLASH_COMMANDS_NAME_REGEX } from "./constants.ts";
|
||||
@@ -216,3 +220,51 @@ export function hasOwnProperty<T extends {}, Y extends PropertyKey = string>(
|
||||
// deno-lint-ignore no-prototype-builtins
|
||||
return obj.hasOwnProperty(prop);
|
||||
}
|
||||
|
||||
export function validateComponents(components: MessageComponents) {
|
||||
if (components?.length) {
|
||||
let actionRowCounter = 0;
|
||||
|
||||
for (const component of components) {
|
||||
// 5 Link buttons can not have a customId
|
||||
if (isButton(component)) {
|
||||
if (
|
||||
component.type === ButtonStyles.Link &&
|
||||
component.customId
|
||||
) {
|
||||
throw new Error(Errors.LINK_BUTTON_CANNOT_HAVE_CUSTOM_ID);
|
||||
}
|
||||
// Other buttons must have a customId
|
||||
if (
|
||||
!component.customId && component.type !== ButtonStyles.Link
|
||||
) {
|
||||
throw new Error(Errors.BUTTON_REQUIRES_CUSTOM_ID);
|
||||
}
|
||||
|
||||
if (!validateLength(component.label, { max: 80 })) {
|
||||
throw new Error(Errors.COMPONENT_LABEL_TOO_BIG);
|
||||
}
|
||||
|
||||
if (
|
||||
component.customId &&
|
||||
!validateLength(component.customId, { max: 100 })
|
||||
) {
|
||||
throw new Error(Errors.COMPONENT_CUSTOM_ID_TOO_BIG);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActionRow(component)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
actionRowCounter++;
|
||||
// Max of 5 ActionRows per message
|
||||
if (actionRowCounter > 5) throw new Error(Errors.TOO_MANY_ACTION_ROWS);
|
||||
|
||||
// Max of 5 Buttons (or any component type) within an ActionRow
|
||||
if (component.components?.length > 5) {
|
||||
throw new Error(Errors.TOO_MANY_COMPONENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { camelize, delay } from "../util/utils.ts";
|
||||
import { decompressWith } from "./deps.ts";
|
||||
import { identify } from "./identify.ts";
|
||||
import { resume } from "./resume.ts";
|
||||
import { sendShardMessage } from "./send_shard_message.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
/** Handler for handling every message event from websocket. */
|
||||
@@ -39,7 +38,7 @@ export async function handleOnMessage(message: any, shardId: number) {
|
||||
|
||||
shard.heartbeat.lastSentAt = Date.now();
|
||||
// Discord randomly sends this requiring an immediate heartbeat back
|
||||
sendShardMessage(shard, {
|
||||
ws.sendShardMessage(shard, {
|
||||
op: DiscordGatewayOpcodes.Heartbeat,
|
||||
d: shard?.previousSequenceNumber,
|
||||
}, true);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { delay } from "../util/utils.ts";
|
||||
import { closeWS } from "./close_ws.ts";
|
||||
import { identify } from "./identify.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
@@ -45,7 +44,7 @@ export async function heartbeat(shardId: number, interval: number) {
|
||||
}
|
||||
|
||||
if (!currentShard.heartbeat.acknowledged) {
|
||||
closeWS(currentShard.ws, 3066, "Did not receive an ACK in time.");
|
||||
ws.closeWS(currentShard.ws, 3066, "Did not receive an ACK in time.");
|
||||
return identify(shardId, ws.maxShards);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { closeWS } from "./close_ws.ts";
|
||||
import { sendShardMessage } from "./send_shard_message.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
export async function identify(shardId: number, maxShards: number) {
|
||||
@@ -9,7 +7,7 @@ export async function identify(shardId: number, maxShards: number) {
|
||||
// Need to clear the old heartbeat interval
|
||||
const oldShard = ws.shards.get(shardId);
|
||||
if (oldShard) {
|
||||
closeWS(oldShard.ws, 3065, "Reidentifying closure of old shard");
|
||||
ws.closeWS(oldShard.ws, 3065, "Reidentifying closure of old shard");
|
||||
clearInterval(oldShard.heartbeat.intervalId);
|
||||
}
|
||||
|
||||
@@ -41,7 +39,7 @@ export async function identify(shardId: number, maxShards: number) {
|
||||
});
|
||||
|
||||
socket.onopen = () => {
|
||||
sendShardMessage(shardId, {
|
||||
ws.sendShardMessage(shardId, {
|
||||
op: DiscordGatewayOpcodes.Identify,
|
||||
d: { ...ws.identifyPayload, shard: [shardId, maxShards] },
|
||||
}, true);
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { closeWS } from "./close_ws.ts";
|
||||
import { sendShardMessage } from "./send_shard_message.ts";
|
||||
import { ws } from "./ws.ts";
|
||||
|
||||
export async function resume(shardId: number) {
|
||||
@@ -12,7 +10,7 @@ export async function resume(shardId: number) {
|
||||
|
||||
if (oldShard) {
|
||||
// HOW TO CLOSE OLD SHARD SOCKET!!!
|
||||
closeWS(oldShard.ws, 3064, "Resuming the shard, closing old shard.");
|
||||
ws.closeWS(oldShard.ws, 3064, "Resuming the shard, closing old shard.");
|
||||
// STOP OLD HEARTBEAT
|
||||
clearInterval(oldShard.heartbeat.intervalId);
|
||||
}
|
||||
@@ -48,7 +46,7 @@ export async function resume(shardId: number) {
|
||||
|
||||
// Resume on open
|
||||
socket.onopen = () => {
|
||||
sendShardMessage(shardId, {
|
||||
ws.sendShardMessage(shardId, {
|
||||
op: DiscordGatewayOpcodes.Resume,
|
||||
d: {
|
||||
token: ws.identifyPayload.token,
|
||||
|
||||
12
src/ws/ws.ts
12
src/ws/ws.ts
@@ -1,6 +1,7 @@
|
||||
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { cleanupLoadingShards } from "./cleanup_loading_shards.ts";
|
||||
import { closeWS } from "./close_ws.ts";
|
||||
import { createShard } from "./create_shard.ts";
|
||||
import { log } from "./events.ts";
|
||||
import { handleDiscordPayload } from "./handle_discord_payload.ts";
|
||||
@@ -9,6 +10,7 @@ import { heartbeat } from "./heartbeat.ts";
|
||||
import { identify } from "./identify.ts";
|
||||
import { processQueue } from "./process_queue.ts";
|
||||
import { resharder } from "./resharder.ts";
|
||||
import { sendShardMessage } from "./send_shard_message.ts";
|
||||
import { spawnShards } from "./spawn_shards.ts";
|
||||
import { startGateway } from "./start_gateway.ts";
|
||||
import { tellClusterToIdentify } from "./tell_cluster_to_identify.ts";
|
||||
@@ -92,7 +94,7 @@ export const ws = {
|
||||
spawnShards,
|
||||
/** Create the websocket and adds the proper handlers to the websocket. */
|
||||
createShard,
|
||||
/** Begins identification of the shard to discord */
|
||||
/** Begins identification of the shard to discord. */
|
||||
identify,
|
||||
/** Begins heartbeating of the shard to keep it alive */
|
||||
heartbeat,
|
||||
@@ -106,10 +108,14 @@ export const ws = {
|
||||
resharder,
|
||||
/** Cleanups loading shards that were unable to load. */
|
||||
cleanupLoadingShards,
|
||||
/** Handles the message events from websocket */
|
||||
/** Handles the message events from websocket. */
|
||||
handleOnMessage,
|
||||
/** Handles processing queue of requests send to this shard */
|
||||
/** Handles processing queue of requests send to this shard. */
|
||||
processQueue,
|
||||
/** Closes shard WebSocket connection properly. */
|
||||
closeWS,
|
||||
/** Properly adds a message to the shards queue */
|
||||
sendShardMessage,
|
||||
};
|
||||
|
||||
export interface DiscordenoShard {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { delay } from "../../src/util/utils.ts";
|
||||
import { closeWS } from "../../src/ws/close_ws.ts";
|
||||
import { ws } from "../../src/ws/ws.ts";
|
||||
import { defaultTestOptions } from "./start_bot.ts";
|
||||
|
||||
@@ -9,7 +8,7 @@ Deno.test({
|
||||
async fn() {
|
||||
ws.shards.forEach((shard) => {
|
||||
clearInterval(shard.heartbeat.intervalId);
|
||||
closeWS(shard.ws, 3061, "Discordeno Testing Finished! Do Not RESUME!");
|
||||
ws.closeWS(shard.ws, 3061, "Discordeno Testing Finished! Do Not RESUME!");
|
||||
});
|
||||
|
||||
await delay(3000);
|
||||
|
||||
Reference in New Issue
Block a user