types: fix new types issues (#829)

* feat: add tests for deleting channel overwrites

* fix: typings
This commit is contained in:
Skillz4Killz
2021-04-12 02:26:55 -04:00
committed by GitHub
parent b7a2b0ed55
commit 51e27d8f17
31 changed files with 329 additions and 273 deletions

View File

@@ -1,7 +1,8 @@
import { getGatewayBot } from "./helpers/misc/get_gateway_bot.ts";
import { rest } from "./rest/rest.ts";
import { EventHandlers } from "./types/discordeno/eventHandlers.ts";
import { DiscordGatewayIntents } from "./types/gateway/gateway_intents.ts";
import { DiscordGetGatewayBot } from "./types/gateway/get_gateway_bot.ts";
import { GetGatewayBot } from "./types/gateway/get_gateway_bot.ts";
import { baseEndpoints, GATEWAY_VERSION } from "./util/constants.ts";
import { ws } from "./ws/ws.ts";
@@ -12,7 +13,7 @@ export let applicationId = "";
export let eventHandlers: EventHandlers = {};
export let botGatewayData: DiscordGetGatewayBot;
export let botGatewayData: GetGatewayBot;
export let proxyWSURL = `wss://gateway.discord.gg`;
export const identifyPayload = {

View File

@@ -15,5 +15,5 @@ export async function handleMessageReactionRemoveAll(
await cacheHandlers.set("messages", payload.message_id, message);
}
eventHandlers.reactionRemoveAll?.(data.d);
eventHandlers.reactionRemoveAll?.(payload);
}

View File

@@ -1,10 +1,11 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { DiscordUser } from "../../types/users/user.ts";
import { DiscordUser, User } from "../../types/users/user.ts";
import { camelKeysToSnakeCase } from "../../util/utils.ts";
export async function handleUserUpdate(data: DiscordGatewayPayload) {
const userData = data.d as DiscordUser;
const userData = camelKeysToSnakeCase(data.d as DiscordUser) as User;
const member = await cacheHandlers.get("members", userData.id);
if (!member) return;

View File

@@ -1,11 +1,14 @@
import { eventHandlers } from "../../bot.ts";
import { cacheHandlers } from "../../cache.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { DiscordVoiceServerUpdate, VoiceServerUpdate } from "../../types/voice/voice_server_update.ts";
import { snakeKeysToCamelCase } from "../../util/utils.ts";
export async function handleVoiceServerUpdate(data: DiscordPayload) {
const payload = data.d as DiscordVoiceServerUpdate;
export async function handleVoiceServerUpdate(data: DiscordGatewayPayload) {
const payload = snakeKeysToCamelCase(data.d as DiscordVoiceServerUpdate) as VoiceServerUpdate;
const guild = await cacheHandlers.get("guilds", payload.guild_id);
const guild = await cacheHandlers.get("guilds", payload.guildId);
if (!guild) return;
eventHandlers.voiceServerUpdate?.(payload.token, guild, payload.endpoint);
eventHandlers.voiceServerUpdate?.(payload, guild);
}

View File

@@ -1,9 +1,9 @@
import { eventHandlers } from "../../bot.ts";
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
import { DiscordWebhooksUpdate } from "../../types/webhooks/webhooks_update.ts";
import { DiscordWebhookUpdate } from "../../types/webhooks/webhooks_update.ts";
export function handleWebhooksUpdate(data: DiscordGatewayPayload) {
const options = data.d as DiscordWebhooksUpdate;
const options = data.d as DiscordWebhookUpdate;
eventHandlers.webhooksUpdate?.(
options.channel_id,
options.guild_id,

View File

@@ -1,6 +1,7 @@
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordGuildMember } from "../../types/guilds/guild_member.ts";
import { Errors } from "../../types/misc/errors.ts";
import { endpoints } from "../../util/constants.ts";
import {
@@ -69,7 +70,7 @@ export async function editMember(
"patch",
endpoints.GUILD_MEMBER(guildId, memberId),
options,
) as MemberCreatePayload;
) as DiscordGuildMember;
const member = await structures.createDiscordenoMember(result, guildId);
return member;

View File

@@ -1,6 +1,7 @@
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordGuildMember } from "../../types/guilds/guild_member.ts";
import { endpoints } from "../../util/constants.ts";
/** Returns a guild member object for the specified user.
@@ -18,7 +19,7 @@ export async function getMember(
const data = (await rest.runMethod(
"get",
endpoints.GUILD_MEMBER(guildId, id),
)) as MemberCreatePayload;
)) as DiscordGuildMember;
const discordenoMember = await structures.createDiscordenoMember(
data,

View File

@@ -1,5 +1,5 @@
import { cacheHandlers } from "../../cache.ts";
import { Member } from "../../structures/mod.ts";
import { DiscordenoMember } from "../../structures/member.ts";
import { Collection } from "../../util/collection.ts";
/** Returns guild member objects for the specified user by their nickname/username.
@@ -19,5 +19,5 @@ export async function getMembersByQuery(
query: name,
limit,
});
}) as Promise<Collection<string, Member>>;
}) as Promise<Collection<string, DiscordenoMember>>;
}

View File

@@ -9,7 +9,7 @@ import { requireBotChannelPermissions } from "../../util/permissions.ts";
/** Edit the message. */
export async function editMessage(
message: Message,
content: string | MessageContent,
content: string | MessageContent
) {
if (message.author.id !== botId) {
throw "You can only edit a message that was sent by the bot.";
@@ -30,8 +30,8 @@ export async function editMessage(
const result = await rest.runMethod(
"patch",
endpoints.CHANNEL_MESSAGE(message.channelId, message.id),
content,
content
);
return structures.createDiscordenoMessage(result as MessageCreateOptions);
return structures.createDiscordenoMessage(result as DiscordMessage);
}

View File

@@ -1,6 +1,7 @@
import { cacheHandlers } from "../../cache.ts";
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotChannelPermissions } from "../../util/permissions.ts";
@@ -15,8 +16,8 @@ export async function getMessage(channelId: string, id: string) {
const result = (await rest.runMethod(
"get",
endpoints.CHANNEL_MESSAGE(channelId, id),
)) as MessageCreateOptions;
endpoints.CHANNEL_MESSAGE(channelId, id)
)) as DiscordMessage;
return structures.createDiscordenoMessage(result);
}

View File

@@ -1,5 +1,6 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotChannelPermissions } from "../../util/permissions.ts";
@@ -10,7 +11,7 @@ export async function getMessages(
| GetMessagesAfter
| GetMessagesBefore
| GetMessagesAround
| GetMessages,
| GetMessages
) {
await requireBotChannelPermissions(channelId, [
"VIEW_CHANNEL",
@@ -22,10 +23,10 @@ export async function getMessages(
const result = (await rest.runMethod(
"get",
endpoints.CHANNEL_MESSAGES(channelId),
options,
)) as MessageCreateOptions[];
options
)) as DiscordMessage[];
return Promise.all(
result.map((res) => structures.createDiscordenoMessage(res)),
result.map((res) => structures.createDiscordenoMessage(res))
);
}

View File

@@ -1,13 +1,14 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { endpoints } from "../../util/constants.ts";
/** Crosspost a message in a News Channel to following channels. */
export async function publishMessage(channelId: string, messageId: string) {
const data = (await rest.runMethod(
"post",
endpoints.CHANNEL_MESSAGE_CROSSPOST(channelId, messageId),
)) as MessageCreateOptions;
endpoints.CHANNEL_MESSAGE_CROSSPOST(channelId, messageId)
)) as DiscordMessage;
return structures.createDiscordenoMessage(data);
}

View File

@@ -3,6 +3,7 @@ import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordChannelTypes } from "../../types/channels/channel_types.ts";
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
import { CreateMessage } from "../../types/messages/create_message.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { Errors } from "../../types/misc/errors.ts";
import { PermissionStrings } from "../../types/permissions/permission_strings.ts";
@@ -13,7 +14,7 @@ import { camelKeysToSnakeCase } from "../../util/utils.ts";
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
export async function sendMessage(
channelId: string,
content: string | DiscordenoCreateMessage,
content: string | CreateMessage
) {
if (typeof content === "string") content = { content };
@@ -55,18 +56,18 @@ export async function sendMessage(
if (content.allowedMentions.users?.length) {
if (
content.allowedMentions.parse?.includes(
DiscordAllowedMentionsTypes.UserMentions,
DiscordAllowedMentionsTypes.UserMentions
)
) {
content.allowedMentions.parse = content.allowedMentions.parse.filter(
(p) => p !== "users",
(p) => p !== "users"
);
}
if (content.allowedMentions.users.length > 100) {
content.allowedMentions.users = content.allowedMentions.users.slice(
0,
100,
100
);
}
}
@@ -74,18 +75,18 @@ export async function sendMessage(
if (content.allowedMentions.roles?.length) {
if (
content.allowedMentions.parse?.includes(
DiscordAllowedMentionsTypes.RoleMentions,
DiscordAllowedMentionsTypes.RoleMentions
)
) {
content.allowedMentions.parse = content.allowedMentions.parse.filter(
(p) => p !== "roles",
(p) => p !== "roles"
);
}
if (content.allowedMentions.roles.length > 100) {
content.allowedMentions.roles = content.allowedMentions.roles.slice(
0,
100,
100
);
}
}
@@ -98,13 +99,14 @@ export async function sendMessage(
...content,
...(content.messageReference?.messageId
? {
messageReference: {
...content.messageReference,
failIfNotExists: content.messageReference.failIfNotExists === true,
},
}
messageReference: {
...content.messageReference,
failIfNotExists:
content.messageReference.failIfNotExists === true,
},
}
: {}),
}),
})
)) as DiscordMessage;
return structures.createDiscordenoMessage(result);

View File

@@ -1,9 +1,11 @@
import { rest } from "../../rest/rest.ts";
import { DiscordGetGatewayBot, GetGatewayBot } from "../../types/gateway/get_gateway_bot.ts";
import { endpoints } from "../../util/constants.ts";
import { camelKeysToSnakeCase } from "../../util/utils.ts";
/** Get the bots Gateway metadata that can help during the operation of large or sharded bots. */
export async function getGatewayBot() {
const result = await rest.runMethod("get", endpoints.GATEWAY_BOT);
return result as DiscordBotGatewayData;
return camelKeysToSnakeCase(result as DiscordGetGatewayBot) as GetGatewayBot;
}

View File

@@ -1,5 +1,6 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotGuildPermissions } from "../../util/permissions.ts";
@@ -27,7 +28,7 @@ export async function createGuildTemplate(
"post",
endpoints.GUILD_TEMPLATES(guildId),
data,
)) as GuildTemplate;
)) as DiscordTemplate;
return structures.createTemplateStruct(template);
}

View File

@@ -1,5 +1,6 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotGuildPermissions } from "../../util/permissions.ts";
@@ -16,7 +17,7 @@ export async function deleteGuildTemplate(
const deletedTemplate = (await rest.runMethod(
"delete",
`${endpoints.GUILD_TEMPLATES(guildId)}/${templateCode}`,
)) as GuildTemplate;
)) as DiscordTemplate;
return structures.createTemplateStruct(deletedTemplate);
}

View File

@@ -1,5 +1,6 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotGuildPermissions } from "../../util/permissions.ts";
@@ -26,7 +27,7 @@ export async function editGuildTemplate(
"patch",
`${endpoints.GUILD_TEMPLATES(guildId)}/${templateCode}`,
data,
)) as GuildTemplate;
)) as DiscordTemplate;
return structures.createTemplateStruct(template);
}

View File

@@ -2,6 +2,7 @@ import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotGuildPermissions } from "../../util/permissions.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
/**
* Returns an array of templates.
@@ -12,8 +13,8 @@ export async function getGuildTemplates(guildId: string) {
const templates = (await rest.runMethod(
"get",
endpoints.GUILD_TEMPLATES(guildId),
)) as GuildTemplate[];
endpoints.GUILD_TEMPLATES(guildId)
)) as DiscordTemplate[];
return templates.map((template) => structures.createTemplateStruct(template));
}

View File

@@ -1,13 +1,14 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { endpoints } from "../../util/constants.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
/** Returns the guild template if it exists */
export async function getTemplate(templateCode: string) {
const result = (await rest.runMethod(
"get",
endpoints.GUILD_TEMPLATE(templateCode),
) as GuildTemplate);
) as DiscordTemplate);
const template = await structures.createTemplateStruct(result);
return template;

View File

@@ -2,6 +2,7 @@ import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { endpoints } from "../../util/constants.ts";
import { requireBotGuildPermissions } from "../../util/permissions.ts";
import { DiscordTemplate } from "../../types/templates/template.ts";
/**
* Syncs the template to the guild's current state.
@@ -13,7 +14,7 @@ export async function syncGuildTemplate(guildId: string, templateCode: string) {
const template = (await rest.runMethod(
"put",
`${endpoints.GUILD_TEMPLATES(guildId)}/${templateCode}`,
)) as GuildTemplate;
)) as DiscordTemplate;
return structures.createTemplateStruct(template);
}

View File

@@ -1,13 +1,16 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { Errors } from "../../types/misc/errors.ts";
import { EditWebhookMessage } from "../../types/webhooks/edit_webhook_message.ts";
import { endpoints } from "../../util/constants.ts";
export async function editWebhookMessage(
webhookId: string,
webhookToken: string,
messageId: string,
options: EditWebhookMessageOptions,
options: EditWebhookMessage
) {
if (options.content && options.content.length > 2000) {
throw Error(Errors.MESSAGE_MAX_LENGTH);
@@ -17,43 +20,43 @@ export async function editWebhookMessage(
options.embeds.splice(10);
}
if (options.allowed_mentions) {
if (options.allowed_mentions.users?.length) {
if (options.allowed_mentions.parse.includes("users")) {
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
(p) => p !== "users",
if (options.allowedMentions) {
if (options.allowedMentions.users?.length) {
if (options.allowedMentions.parse.includes("users")) {
options.allowedMentions.parse = options.allowedMentions.parse.filter(
(p) => p !== "users"
);
}
if (options.allowed_mentions.users.length > 100) {
options.allowed_mentions.users = options.allowed_mentions.users.slice(
if (options.allowedMentions.users.length > 100) {
options.allowedMentions.users = options.allowedMentions.users.slice(
0,
100,
100
);
}
}
if (options.allowed_mentions.roles?.length) {
if (options.allowed_mentions.parse.includes("roles")) {
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
(p) => p !== "roles",
if (options.allowedMentions.roles?.length) {
if (options.allowedMentions.parse.includes(DiscordAllowedMentionsTypes.RoleMentions)) {
options.allowedMentions.parse = options.allowedMentions.parse.filter(
(p) => p !== "roles"
);
}
if (options.allowed_mentions.roles.length > 100) {
options.allowed_mentions.roles = options.allowed_mentions.roles.slice(
if (options.allowedMentions.roles.length > 100) {
options.allowedMentions.roles = options.allowedMentions.roles.slice(
0,
100,
100
);
}
}
}
const result = await rest.runMethod(
const result = (await rest.runMethod(
"patch",
endpoints.WEBHOOK_MESSAGE(webhookId, webhookToken, messageId),
{ ...options, allowed_mentions: options.allowed_mentions },
) as MessageCreateOptions;
{ ...options, allowedMentions: options.allowedMentions }
)) as DiscordMessage;
const message = await structures.createDiscordenoMessage(result);
return message;

View File

@@ -1,13 +1,16 @@
import { rest } from "../../rest/rest.ts";
import { structures } from "../../structures/mod.ts";
import { DiscordAllowedMentionsTypes } from "../../types/messages/allowed_mentions_types.ts";
import { DiscordMessage } from "../../types/messages/message.ts";
import { Errors } from "../../types/misc/errors.ts";
import { ExecuteWebhook } from "../../types/webhooks/execute_webhook.ts";
import { endpoints } from "../../util/constants.ts";
/** Execute a webhook with webhook Id and webhook token */
export async function executeWebhook(
webhookId: string,
webhookToken: string,
options: ExecuteWebhookOptions,
options: ExecuteWebhook
) {
if (!options.content && !options.file && !options.embeds) {
throw new Error(Errors.INVALID_WEBHOOK_OPTIONS);
@@ -21,28 +24,42 @@ export async function executeWebhook(
options.embeds.splice(10);
}
if (options.mentions) {
if (options.mentions.users?.length) {
if (options.mentions.parse.includes("users")) {
options.mentions.parse = options.mentions.parse.filter(
(p) => p !== "users",
if (options.allowedMentions) {
if (options.allowedMentions.users?.length) {
if (
options.allowedMentions.parse.includes(
DiscordAllowedMentionsTypes.UserMentions
)
) {
options.allowedMentions.parse = options.allowedMentions.parse.filter(
(p) => p !== "users"
);
}
if (options.mentions.users.length > 100) {
options.mentions.users = options.mentions.users.slice(0, 100);
if (options.allowedMentions.users.length > 100) {
options.allowedMentions.users = options.allowedMentions.users.slice(
0,
100
);
}
}
if (options.mentions.roles?.length) {
if (options.mentions.parse.includes("roles")) {
options.mentions.parse = options.mentions.parse.filter(
(p) => p !== "roles",
if (options.allowedMentions.roles?.length) {
if (
options.allowedMentions.parse.includes(
DiscordAllowedMentionsTypes.RoleMentions
)
) {
options.allowedMentions.parse = options.allowedMentions.parse.filter(
(p) => p !== "roles"
);
}
if (options.mentions.roles.length > 100) {
options.mentions.roles = options.mentions.roles.slice(0, 100);
if (options.allowedMentions.roles.length > 100) {
options.allowedMentions.roles = options.allowedMentions.roles.slice(
0,
100
);
}
}
}
@@ -54,11 +71,15 @@ export async function executeWebhook(
}`,
{
...options,
allowed_mentions: options.mentions,
avatar_url: options.avatar_url,
},
allowed_mentions: options.allowedMentions,
avatar_url: options.avatarUrl,
}
);
if (!options.wait) return;
return structures.createDiscordenoMessage(result as MessageCreateOptions);
return structures.createDiscordenoMessage(result as DiscordMessage);
}
function DiscordAllowedMentionTypes(DiscordAllowedMentionTypes: any) {
throw new Error("Function not implemented.");
}

View File

@@ -24,15 +24,15 @@ export const rest = {
ratelimitedPaths: new Map(),
eventHandlers: {
// BY DEFAULT WE WILL LOG ALL ERRORS TO CONSOLE. USER CAN CHOOSE TO OVERRIDE
error: console.error,
error: function (...args: unknown[]) {},
// PLACEHOLDERS TO ALLOW USERS TO CUSTOMIZE
debug: function (_type, error, ...args) {},
fetching() {},
fetched() {},
fetchSuccess() {},
fetchFailed() {},
globallyRateLimited() {},
retriesMaxed() {},
debug: function (type: string, error: string | Record<string, unknown>) {},
fetching(payload: Record<string, unknown>) {},
fetched(payload: Record<string, unknown>) {},
fetchSuccess(payload: Record<string, unknown>) {},
fetchFailed(payload: Record<string, unknown>, error: any) {},
globallyRateLimited(url: string, resetsAt: number) {},
retriesMaxed(payload: Record<string, unknown>) {},
},
/** Handler function for every request. Converts to json, verified authorization & requirements and begins processing the request */
handlePayload,

View File

@@ -11,6 +11,7 @@ import { removeReaction } from "../helpers/messages/remove_reaction.ts";
import { removeReactionEmoji } from "../helpers/messages/remove_reaction_emoji.ts";
import { sendMessage } from "../helpers/messages/send_message.ts";
import { GuildMember } from "../types/guilds/guild_member.ts";
import { CreateMessage } from "../types/messages/create_message.ts";
import { EditMessage } from "../types/messages/edit_message.ts";
import { DiscordMessage, Message } from "../types/messages/message.ts";
import { CHANNEL_MENTION_REGEX } from "../util/constants.ts";
@@ -38,8 +39,9 @@ const baseMessage: Partial<DiscordenoMessage> = {
return this.member?.guilds.get(this.guildId);
},
get link() {
return `https://discord.com/channels/${this.guildId ||
"@me"}/${this.channelId}/${this.id}`;
return `https://discord.com/channels/${this.guildId || "@me"}/${
this.channelId
}/${this.id}`;
},
get mentionedRoles() {
return this.mentionedRoleIds?.map((id) => this.guild?.roles.get(id)) || [];
@@ -68,20 +70,25 @@ const baseMessage: Partial<DiscordenoMessage> = {
return addReactions(this.channelId!, this.id!, reactions, ordered);
},
reply(content) {
const contentWithMention = typeof content === "string"
? {
content,
mentions: { repliedUser: true },
messageReference: { messageId: this.id },
failReplyIfNotExists: false,
}
: {
...content,
mentions: { ...(content.allowedMentions || {}), repliedUser: true },
messageReference: { messageId: this.id },
failReplyIfNotExists:
content.messageReference?.failIfNotExists === true,
};
const contentWithMention =
typeof content === "string"
? {
content,
mentions: { repliedUser: true },
messageReference: {
messageId: this.id,
failReplyIfNotExists: false,
},
}
: {
...content,
mentions: { ...(content.allowedMentions || {}), repliedUser: true },
messageReference: {
messageId: this.id,
failReplyIfNotExists:
content.messageReference?.failIfNotExists === true,
},
};
if (this.guildId) return sendMessage(this.channelId!, contentWithMention);
return sendDirectMessage(this.author!.id, contentWithMention);
@@ -124,7 +131,7 @@ export async function createDiscordenoMessage(data: DiscordMessage) {
mentionChannels = [],
mentions,
mentionRoles,
edited_timestamp: editedTimestamp,
editedTimestamp,
...rest
} = snakeKeysToCamelCase(data) as Message;
@@ -132,15 +139,15 @@ export async function createDiscordenoMessage(data: DiscordMessage) {
for (const key of Object.keys(rest)) {
eventHandlers.debug?.(
"loop",
`Running for of loop in createDiscordenoMessage function.`,
`Running for of loop in createDiscordenoMessage function.`
);
// @ts-ignore index signature
props[key] = createNewProp(rest[key]);
}
// Discord doesnt give guild id for getMessage() so this will fill it in
const guildIdFinal = guildId ||
(await cacheHandlers.get("channels", channelId))?.guildId || "";
const guildIdFinal =
guildId || (await cacheHandlers.get("channels", channelId))?.guildId || "";
const message: DiscordenoMessage = Object.create(baseMessage, {
...props,
@@ -149,20 +156,18 @@ export async function createDiscordenoMessage(data: DiscordMessage) {
guildId: createNewProp(guildIdFinal),
mentionedUserIds: createNewProp(mentions.map((m) => m.id)),
mentionedRoleIds: createNewProp(mentionRoles),
mentionedChannelIds: createNewProp(
[
// Keep any ids that discord sends
...mentionChannels.map((m) => m.id),
// Add any other ids that can be validated in a channel mention format
...(rest.content.match(CHANNEL_MENTION_REGEX) || []).map((text) =>
// converts the <#123> into 123
text.substring(2, text.length - 1)
),
],
),
mentionedChannelIds: createNewProp([
// Keep any ids that discord sends
...mentionChannels.map((m) => m.id),
// Add any other ids that can be validated in a channel mention format
...(rest.content.match(CHANNEL_MENTION_REGEX) || []).map((text) =>
// converts the <#123> into 123
text.substring(2, text.length - 1)
),
]),
timestamp: createNewProp(Date.parse(data.timestamp)),
editedTimestamp: createNewProp(
editedTimestamp ? Date.parse(editedTimestamp) : undefined,
editedTimestamp ? Date.parse(editedTimestamp) : undefined
),
});
@@ -204,7 +209,7 @@ export interface DiscordenoMessage extends Message {
/** Delete the message */
delete(
reason?: string,
delayMilliseconds?: number,
delayMilliseconds?: number
): ReturnType<typeof deleteMessage>;
/** Edit the message */
edit(content: string | EditMessage): ReturnType<typeof editMessage>;
@@ -215,27 +220,23 @@ export interface DiscordenoMessage extends Message {
/** Add multiple reactions to the message without or without order. */
addReactions(
reactions: string[],
ordered?: boolean,
ordered?: boolean
): ReturnType<typeof addReactions>;
/** Send a inline reply to this message */
reply(
content: string | DiscordenoCreateMessage,
): ReturnType<typeof sendMessage>;
reply(content: string | CreateMessage): ReturnType<typeof sendMessage>;
/** Send a message to this channel where this message is */
send(
content: string | DiscordenoCreateMessage,
): ReturnType<typeof sendMessage>;
send(content: string | CreateMessage): ReturnType<typeof sendMessage>;
/** Send a message to this channel and then delete it after a bit. By default it will delete after 10 seconds with no reason provided. */
alert(
content: string | DiscordenoCreateMessage,
content: string | CreateMessage,
timeout?: number,
reason?: string,
reason?: string
): Promise<void>;
/** Send a inline reply to this message but then delete it after a bit. By default it will delete after 10 seconds with no reason provided. */
alertReply(
content: string | DiscordenoCreateMessage,
content: string | CreateMessage,
timeout?: number,
reason?: string,
reason?: string
): Promise<unknown>;
/** Remove all reactions */
removeAllReactions(): ReturnType<typeof removeAllReactions>;

View File

@@ -1,8 +1,10 @@
import { eventHandlers } from "../bot.ts";
import { cache } from "../cache.ts";
import { DiscordTemplate, Template } from "../types/templates/template.ts";
import { createNewProp } from "../util/utils.ts";
import { DiscordenoGuild } from "./guild.ts";
const baseTemplate: Partial<Template> = {
const baseTemplate: Partial<DiscordTemplate> = {
get sourceGuild() {
// deno-lint-ignore getter-return
if (!this.sourceGuildId) return;
@@ -11,7 +13,7 @@ const baseTemplate: Partial<Template> = {
};
export function createTemplateStruct(
data: GuildTemplate,
data: DiscordTemplate,
) {
const {
usage_count: usageCount,
@@ -43,5 +45,9 @@ export function createTemplateStruct(
sourceGuildId: createNewProp(sourceGuildId),
serializedSourceGuild: createNewProp(serializedSourceGuild),
isDirty: createNewProp(isDirty),
}) as Template;
}) as DiscordenoTemplate;
}
export interface DiscordenoTemplate extends Template {
sourceGuild?: DiscordenoGuild;
}

View File

@@ -1,19 +1,21 @@
import { DiscordenoChannel } from "../../structures/channel.ts";
import { DiscordenoGuild } from "../../structures/guild.ts";
import { DiscordenoMember } from "../../structures/member.ts";
import { DiscordenoMessage } from "../../structures/message.ts";
import { DiscordenoRole } from "../../structures/role.ts";
import { Collection } from "../../util/collection.ts";
import { IntegrationCreateUpdate } from "../integration/integration_create_update.ts";
import { ApplicationCommandCreateUpdateDelete } from "../interactions/application_command_create_update_delete.ts";
import {
Channel,
DiscordGatewayPayload,
DiscordMessageReactionRemoveAll,
Emoji,
GatewayPayload,
IntegrationDelete,
Interaction,
InviteCreate,
InviteDelete,
MessageDelete,
MessageReactionAdd,
MessageReactionRemove,
PresenceUpdate,
@@ -21,21 +23,22 @@ import {
User,
VoiceState,
} from "../mod.ts";
import { VoiceServerUpdate } from "../voice/voice_server_update.ts";
import { DebugArg } from "./debug_arg.ts";
import { GuildUpdateChange } from "./guild_update_change.ts";
export interface EventHandlers {
/** Sent when a new Slash Command is created, relevant to the current user. */
applicationCommandCreate?: (
data: ApplicationCommandCreateUpdateDelete,
data: ApplicationCommandCreateUpdateDelete
) => unknown;
/** Sent when a Slash Command relevant to the current user is updated. */
applicationCommandUpdate?: (
data: ApplicationCommandCreateUpdateDelete,
data: ApplicationCommandCreateUpdateDelete
) => unknown;
/** Sent when a Slash Command relevant to the current user is deleted. */
applicationCommandDelete?: (
data: ApplicationCommandCreateUpdateDelete,
data: ApplicationCommandCreateUpdateDelete
) => unknown;
/** Sent when properties about the user change. */
botUpdate?: (user: User) => unknown;
@@ -45,94 +48,96 @@ export interface EventHandlers {
channelUpdate?: (channel: Channel, oldChannel: Channel) => unknown;
/** Sent when a channel relevant to the current user is deleted. */
channelDelete?: (channel: Channel) => unknown;
debug?: (args: DebugArg) => unknown;
debug?: (args: string | DebugArg, data?: string) => unknown;
/** Sent before every event. Discordeno awaits the execution of this event before main event gets sent. */
dispatchRequirements?: (
data: DiscordGatewayPayload,
shardId: number,
shardId: number
) => unknown;
/** Sent when a user is banned from a guild. */
guildBanAdd?: (
guild: DiscordenoGuild,
user: User,
member?: DiscordenoMember,
member?: DiscordenoMember
) => unknown;
/** Sent when a user is unbanned from a guild. */
guildBanRemove?: (
guild: DiscordenoGuild,
user: User,
member?: DiscordenoMember,
member?: DiscordenoMember
) => unknown;
/**
* This event can be sent in three different scenarios:
* 1. When a user is initially connecting, to lazily load and backfill information for all unavailable guilds sent in the `READY` event. Guilds that are unavailable due to an outage will send a `GUILD_DELETE` event.
* 2. When a Guild becomes available again to the client.
* 3. When the current user joins a new Guild.
*
*
* This event does not get sent on startup
*/
guildCreate?: (guild: DiscordenoGuild) => unknown;
/** This event does get sent on start when shards are loading the guilds */
guildLoaded?: (guild: DiscordenoGuild) => unknown;
/** When a guild goes unavailable this event will be ran. */
guildAvailable?: (guild: DiscordenoGuild) => unknown;
/** Sent when a guild is updated. */
guildUpdate?: (
guild: DiscordenoGuild,
changes: GuildUpdateChange[],
changes: GuildUpdateChange[]
) => unknown;
/** Sent when a guild becomes or was already unavailable due to an outage, or when the user leaves or is removed from a guild. If the `unavailable` field is not set, the user was removed from the guild. */
guildDelete?: (guild: DiscordenoGuild) => unknown;
/** Sent when a guild's emojis have been updated. */
guildEmojisUpdate?: (
guild: DiscordenoGuild,
emojis: Emoji[],
oldEmojis: Emoji[],
emojis: Collection<string, Emoji>,
oldEmojis: Collection<string, Emoji>
) => unknown;
/** Sent when a new user joins a guild. */
guildMemberAdd?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
member: DiscordenoMember
) => unknown;
/** Sent when a user is removed from a guild (leave/kick/ban). */
guildMemberRemove?: (
guild: DiscordenoGuild,
user: User,
member?: DiscordenoMember,
member?: DiscordenoMember
) => unknown;
/** Sent when a guild member is updated. This will also fire when the user object of a guild member changes. */
guildMemberUpdate?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
oldMember?: DiscordenoMember,
oldMember?: DiscordenoMember
) => unknown;
// TODO: remove this?
//heartbeat?: () => unknown;
/** Sent when a user in a guild uses a Slash Command. */
interactionCreate?: (
data: Omit<Interaction, "member"> & { member: DiscordenoMember },
data: Omit<Interaction, "member"> & { member: DiscordenoMember }
) => unknown;
/** Sent when a message is created. */
messageCreate?: (message: DiscordenoMessage) => unknown;
/** Sent when a message is deleted. */
messageDelete?: (
partial: MessageDelete,
message?: DiscordenoMessage,
partial: { id: string; channel: DiscordenoChannel },
message?: DiscordenoMessage
) => unknown;
/** Sent when a message is updated. */
messageUpdate?: (
message: DiscordenoMessage,
oldMessage: DiscordenoMessage,
oldMessage: DiscordenoMessage
) => unknown;
/** Sent when a user updates its nickname */
nicknameUpdate?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
nickname: string,
oldNickname?: string,
oldNickname?: string
) => unknown;
/** A user's presence is their current state on a guild. This event is sent when a user's presence or info, such as name or avatar, is updated. */
presenceUpdate?: (
presence: PresenceUpdate,
oldPresence?: PerformanceEntry,
oldPresence?: PerformanceEntry
) => unknown;
/** Sent before every event execution. Discordeno will not await its execution. */
raw?: (data: GatewayPayload) => unknown;
@@ -144,25 +149,21 @@ export interface EventHandlers {
reactionAdd?: (
data: MessageReactionAdd,
member?: DiscordenoMember,
message?: DiscordenoMessage,
message?: DiscordenoMessage
) => unknown;
/** Sent when a user removes a reaction from a message. */
reactionRemove?: (
data: MessageReactionRemove,
message?: DiscordenoMessage,
message?: DiscordenoMessage
) => unknown;
/** Sent when a user explicitly removes all reactions from a message. */
reactionRemoveAll?: (
messageId: string,
channelId: string,
guildId?: string,
) => unknown;
reactionRemoveAll?: (payload: DiscordMessageReactionRemoveAll) => unknown;
/** Sent when a bot removes all instances of a given emoji from the reactions of a message. */
reactionRemoveEmoji?: (
emoji: Partial<Emoji>,
messageId: string,
channelId: string,
guildId?: string,
guildId?: string
) => unknown;
/** Sent when a guild role is created. */
roleCreate?: (guild: DiscordenoGuild, role: DiscordenoRole) => unknown;
@@ -172,17 +173,17 @@ export interface EventHandlers {
roleUpdate?: (
guild: DiscordenoGuild,
role: DiscordenoRole,
old: DiscordenoRole,
old: DiscordenoRole
) => unknown;
roleGained?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
roleId: string,
roleId: string
) => unknown;
roleLost?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
roleId: string,
roleId: string
) => unknown;
shardReady?: (shardId: number) => unknown;
/** Sent when a user starts typing in a channel. */
@@ -195,19 +196,24 @@ export interface EventHandlers {
voiceChannelSwitch?: (
member: DiscordenoMember,
channelId: string,
oldChannelId: string,
oldChannelId: string
) => unknown;
/** Sent when a voice server is updated with information for making the bot connect to a voice channel. */
voiceServerUpdate?: (
payload: VoiceServerUpdate,
guild: DiscordenoGuild
) => unknown;
/** Sent when someone joins/leaves/moves voice channels. */
voiceStateUpdate?: (
member: DiscordenoMember,
voiceState: VoiceState,
voiceState: VoiceState
) => unknown;
/** Sent when a guild channel's webhook is created, updated, or deleted. */
webhooksUpdate?: (channelId: string, guildId: string) => unknown;
/** Sent when a member has passed the guild's Membership Screening requirements */
membershipScreeningPassed?: (
guild: DiscordenoGuild,
member: DiscordenoMember,
member: DiscordenoMember
) => unknown;
/** Sent when an integration is created on a server such as twitch, youtube etc.. */
integrationCreate?: (data: IntegrationCreateUpdate) => unknown;

View File

@@ -34,7 +34,7 @@ export interface Message {
/** When this message was sent */
timestamp: string;
/** When this message was edited (or null if never) */
edited_timestamp: string | null;
editedTimestamp: string | null;
/** Whether this was a TTS message */
tts: boolean;
/** Whether this message mentions everyone */

View File

@@ -1,33 +1,37 @@
import { botId } from "../bot.ts";
import { cacheHandlers } from "../cache.ts";
import { Channel, Guild, Member, Role } from "../structures/mod.ts";
import { DiscordenoChannel } from "../structures/channel.ts";
import { DiscordenoGuild } from "../structures/guild.ts";
import { DiscordenoMember } from "../structures/member.ts";
import { DiscordenoRole } from "../structures/role.ts";
import { Errors } from "../types/misc/errors.ts";
import { DiscordBitwisePermissionFlags } from "../types/permissions/bitwise_permission_flags.ts";
import { PermissionStrings } from "../types/permissions/permission_strings.ts";
async function getCached(
table: "guilds",
key: string | Guild,
): Promise<Guild | undefined>;
key: string | DiscordenoGuild
): Promise<DiscordenoGuild | undefined>;
async function getCached(
table: "channels",
key: string | Channel,
): Promise<Channel | undefined>;
key: string | DiscordenoChannel
): Promise<DiscordenoChannel | undefined>;
async function getCached(
table: "members",
key: string | Member,
): Promise<Member | undefined>;
key: string | DiscordenoMember
): Promise<DiscordenoMember | undefined>;
async function getCached(
table: "guilds" | "channels" | "members",
key: string | Guild | Channel | Member,
key: string | DiscordenoGuild | DiscordenoChannel | DiscordenoMember
) {
const cached = typeof key === "string"
? // @ts-ignore TS is wrong here
await cacheHandlers.get(table, key)
: key;
const cached =
typeof key === "string"
? // @ts-ignore TS is wrong here
await cacheHandlers.get(table, key)
: key;
if (!cached || typeof cached === "string") {
throw new Error(
Errors[`${table.slice(0, -1).toUpperCase()}_NOT_FOUND` as Errors],
Errors[`${table.slice(0, -1).toUpperCase()}_NOT_FOUND` as Errors]
);
}
@@ -36,18 +40,18 @@ async function getCached(
/** Calculates the permissions this member has in the given guild */
export async function calculateBasePermissions(
guild: string | Guild,
member: string | Member,
guildOrId: string | DiscordenoGuild,
memberOrId: string | DiscordenoMember
) {
guild = await getCached("guilds", guild);
member = await getCached("members", member);
const guild = await getCached("guilds", guildOrId);
const member = await getCached("members", memberOrId);
if (!guild || !member) return "8";
let permissions = 0n;
// Calculate the role permissions bits, @everyone role is not in memberRoleIds so we need to pass guildId manualy
permissions |= [...(member.guilds.get(guild.id)?.roles || []), guild.id]
.map((id) => (guild as Guild).roles.get(id)?.permissions)
.map((id) => (guild as DiscordenoGuild).roles.get(id)?.permissions)
// Removes any edge case undefined
.filter((perm) => perm)
.reduce((bits, perms) => {
@@ -63,26 +67,26 @@ export async function calculateBasePermissions(
/** Calculates the permissions this member has for the given Channel */
export async function calculateChannelOverwrites(
channel: string | Channel,
member: string | Member,
channelOrId: string | DiscordenoChannel,
memberOrId: string | DiscordenoMember
) {
channel = await getCached("channels", channel);
const channel = await getCached("channels", channelOrId);
// This is a DM channel so return ADMINISTRATOR permission
if (!channel?.guildId) return "8";
member = await getCached("members", member);
const member = await getCached("members", memberOrId);
if (!member) return "8";
// Get all the role permissions this member already has
let permissions = BigInt(
await calculateBasePermissions(channel.guildId, member),
await calculateBasePermissions(channel.guildId, member)
);
// First calculate @everyone overwrites since these have the lowest priority
const overwriteEveryone = channel?.permissionOverwrites.find(
(overwrite) => overwrite.id === (channel as Channel).guildId,
const overwriteEveryone = channel?.permissionOverwrites?.find(
(overwrite) => overwrite.id === (channel as DiscordenoChannel).guildId
);
if (overwriteEveryone) {
// First remove denied permissions since denied < allowed
@@ -97,7 +101,7 @@ export async function calculateChannelOverwrites(
let deny = 0n;
const memberRoles = member.guilds.get(channel.guildId)?.roles || [];
// Second calculate members role overwrites since these have middle priority
for (const overwrite of overwrites) {
for (const overwrite of overwrites || []) {
if (!memberRoles.includes(overwrite.id)) continue;
deny |= BigInt(overwrite.deny);
@@ -108,8 +112,8 @@ export async function calculateChannelOverwrites(
permissions |= allow;
// Third calculate member specific overwrites since these have the highest priority
const overwriteMember = overwrites.find(
(overwrite) => overwrite.id === (member as Member).id,
const overwriteMember = overwrites?.find(
(overwrite) => overwrite.id === (member as DiscordenoMember).id
);
if (overwriteMember) {
permissions &= ~BigInt(overwriteMember.deny);
@@ -122,23 +126,22 @@ export async function calculateChannelOverwrites(
/** Checks if the given permission bits are matching the given permissions. `ADMINISTRATOR` always returns `true` */
export function validatePermissions(
permissionBits: string,
permissions: PermissionStrings[],
permissions: PermissionStrings[]
) {
if (BigInt(permissionBits) & 8n) return true;
return permissions.every(
(permission) =>
// Check if permission is in permissionBits
BigInt(permissionBits) &
BigInt(DiscordBitwisePermissionFlags[permission]),
BigInt(permissionBits) & BigInt(DiscordBitwisePermissionFlags[permission])
);
}
/** Checks if the given member has these permissions in the given guild */
export async function hasGuildPermissions(
guild: string | Guild,
member: string | Member,
permissions: PermissionStrings[],
guild: string | DiscordenoGuild,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
// First we need the role permission bits this member has
const basePermissions = await calculateBasePermissions(guild, member);
@@ -148,8 +151,8 @@ export async function hasGuildPermissions(
/** Checks if the bot has these permissions in the given guild */
export function botHasGuildPermissions(
guild: string | Guild,
permissions: PermissionStrings[],
guild: string | DiscordenoGuild,
permissions: PermissionStrings[]
) {
// Since Bot is a normal member we can use the hasRolePermissions() function
return hasGuildPermissions(guild, botId, permissions);
@@ -157,9 +160,9 @@ export function botHasGuildPermissions(
/** Checks if the given member has these permissions for the given channel */
export async function hasChannelPermissions(
channel: string | Channel,
member: string | Member,
permissions: PermissionStrings[],
channel: string | DiscordenoChannel,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
// First we need the overwrite bits this member has
const channelOverwrites = await calculateChannelOverwrites(channel, member);
@@ -169,8 +172,8 @@ export async function hasChannelPermissions(
/** Checks if the bot has these permissions f0r the given channel */
export function botHasChannelPermissions(
channel: string | Channel,
permissions: PermissionStrings[],
channel: string | DiscordenoChannel,
permissions: PermissionStrings[]
) {
// Since Bot is a normal member we can use the hasRolePermissions() function
return hasChannelPermissions(channel, botId, permissions);
@@ -179,7 +182,7 @@ export function botHasChannelPermissions(
/** Returns the permissions that are not in the given permissionBits */
export function missingPermissions(
permissionBits: string,
permissions: PermissionStrings[],
permissions: PermissionStrings[]
) {
if (BigInt(permissionBits) & 8n) return [];
@@ -188,15 +191,15 @@ export function missingPermissions(
!(
BigInt(permissionBits) &
BigInt(DiscordBitwisePermissionFlags[permission])
),
)
);
}
/** Get the missing Guild permissions this member has */
export async function getMissingGuildPermissions(
guild: string | Guild,
member: string | Member,
permissions: PermissionStrings[],
guild: string | DiscordenoGuild,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
// First we need the role permissino bits this member has
const permissionBits = await calculateBasePermissions(guild, member);
@@ -206,9 +209,9 @@ export async function getMissingGuildPermissions(
/** Get the missing Channel permissions this member has */
export async function getMissingChannelPermissions(
channel: string | Channel,
member: string | Member,
permissions: PermissionStrings[],
channel: string | DiscordenoChannel,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
// First we need the role permissino bits this member has
const permissionBits = await calculateChannelOverwrites(channel, member);
@@ -218,9 +221,9 @@ export async function getMissingChannelPermissions(
/** Throws an error if this member has not all of the given permissions */
export async function requireGuildPermissions(
guild: string | Guild,
member: string | Member,
permissions: PermissionStrings[],
guild: string | DiscordenoGuild,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
const missing = await getMissingGuildPermissions(guild, member, permissions);
if (missing.length) {
@@ -231,8 +234,8 @@ export async function requireGuildPermissions(
/** Throws an error if the bot does not have all permissions */
export function requireBotGuildPermissions(
guild: string | Guild,
permissions: PermissionStrings[],
guild: string | DiscordenoGuild,
permissions: PermissionStrings[]
) {
// Since Bot is a normal member we can use the throwOnMissingGuildPermission() function
return requireGuildPermissions(guild, botId, permissions);
@@ -240,14 +243,14 @@ export function requireBotGuildPermissions(
/** Throws an error if this member has not all of the given permissions */
export async function requireChannelPermissions(
channel: string | Channel,
member: string | Member,
permissions: PermissionStrings[],
channel: string | DiscordenoChannel,
member: string | DiscordenoMember,
permissions: PermissionStrings[]
) {
const missing = await getMissingChannelPermissions(
channel,
member,
permissions,
permissions
);
if (missing.length) {
// If the member is missing a permission throw an Error
@@ -257,8 +260,8 @@ export async function requireChannelPermissions(
/** Throws an error if the bot has not all of the given channel permissions */
export function requireBotChannelPermissions(
channel: string | Channel,
permissions: PermissionStrings[],
channel: string | DiscordenoChannel,
permissions: PermissionStrings[]
) {
// Since Bot is a normal member we can use the throwOnMissingChannelPermission() function
return requireChannelPermissions(channel, botId, permissions);
@@ -270,8 +273,10 @@ export function calculatePermissions(permissionBits: bigint) {
// Since Object.keys() not only returns the permission names but also the bit values we need to return false if it is a Number
if (Number(permission)) return false;
// Check if permissionBits has this permission
return permissionBits &
BigInt(DiscordBitwisePermissionFlags[permission as PermissionStrings]);
return (
permissionBits &
BigInt(DiscordBitwisePermissionFlags[permission as PermissionStrings])
);
}) as PermissionStrings[];
}
@@ -287,20 +292,20 @@ export function calculateBits(permissions: PermissionStrings[]) {
/** Gets the highest role from the member in this guild */
export async function highestRole(
guild: string | Guild,
member: string | Member,
guildOrId: string | DiscordenoGuild,
memberOrId: string | DiscordenoMember
) {
guild = await getCached("guilds", guild);
const guild = await getCached("guilds", guildOrId);
if (!guild) throw new Error(Errors.GUILD_NOT_FOUND);
// Get the roles from the member
const memberRoles = (await getCached("members", member))?.guilds.get(guild.id)
const memberRoles = (await getCached("members", memberOrId))?.guilds.get(guild.id)
?.roles;
// This member has no roles so the highest one is the @everyone role
if (!memberRoles) return guild.roles.get(guild.id) as Role;
if (!memberRoles) return guild.roles.get(guild.id)!;
let memberHighestRole: Role | undefined;
let memberHighestRole: DiscordenoRole | undefined;
for (const roleId of memberRoles) {
const role = guild.roles.get(roleId);
@@ -324,11 +329,11 @@ export async function highestRole(
/** Checks if the first role is higher than the second role */
export async function higherRolePosition(
guild: string | Guild,
guildOrId: string | DiscordenoGuild,
roleId: string,
otherRoleId: string,
otherRoleId: string
) {
guild = await getCached("guilds", guild);
const guild = await getCached("guilds", guildOrId);
if (!guild) return true;
@@ -346,11 +351,11 @@ export async function higherRolePosition(
/** Checks if the member has a higher position than the given role */
export async function isHigherPosition(
guild: string | Guild,
guildOrId: string | DiscordenoGuild,
memberId: string,
compareRoleId: string,
compareRoleId: string
) {
guild = await getCached("guilds", guild);
const guild = await getCached("guilds", guildOrId);
if (!guild || guild.ownerId === memberId) return true;

View File

@@ -1,6 +1,8 @@
import { encode } from "../../deps.ts";
import { eventHandlers } from "../bot.ts";
import { DiscordGatewayOpcodes } from "../types/codes/gateway_opcodes.ts";
import { StatusUpdate } from "../types/gateway/status_update.ts";
import { DiscordApplicationCommandOptionTypes } from "../types/interactions/application_command_option_types.ts";
import { Errors } from "../types/misc/errors.ts";
import { DiscordImageFormat } from "../types/misc/image_format.ts";
import { DiscordImageSize } from "../types/misc/image_size.ts";
@@ -9,7 +11,7 @@ import { SLASH_COMMANDS_NAME_REGEX } from "./constants.ts";
// TODO: move this function to helpers
export function editBotStatus(
data: Pick<GatewayStatusUpdatePayload, "activities" | "status">,
data: Omit<StatusUpdate, "afk" | "since">,
) {
ws.shards.forEach((shard) => {
eventHandlers.debug?.(
@@ -131,7 +133,7 @@ export function snakeKeysToCamelCase<T>(
/** @private */
function validateSlashOptionChoices(
choices: SlashCommandOptionChoice[],
optionType: SlashCommandOptionType,
optionType: DiscordApplicationCommandOptionTypes,
) {
for (const choice of choices) {
eventHandlers.debug?.(
@@ -143,11 +145,11 @@ function validateSlashOptionChoices(
}
if (
(optionType === SlashCommandOptionType.STRING &&
(optionType === DiscordApplicationCommandOptionTypes.STRING &&
(typeof choice.value !== "string" ||
choice.value.length < 1 ||
choice.value.length > 100)) ||
(optionType === SlashCommandOptionType.INTEGER &&
(optionType === DiscordApplicationCommandOptionTypes.INTEGER &&
typeof choice.value !== "number")
) {
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
@@ -165,8 +167,8 @@ function validateSlashOptions(options: SlashCommandOption[]) {
if (
option.choices?.length &&
(option.choices.length > 25 ||
(option.type !== SlashCommandOptionType.STRING &&
option.type !== SlashCommandOptionType.INTEGER))
(option.type !== DiscordApplicationCommandOptionTypes.STRING &&
option.type !== DiscordApplicationCommandOptionTypes.INTEGER))
) {
throw new Error(Errors.INVALID_SLASH_OPTIONS_CHOICES);
}
@@ -186,7 +188,6 @@ function validateSlashOptions(options: SlashCommandOption[]) {
}
}
/** @private */
export function validateSlashCommands(
commands: UpsertSlashCommandOptions[],
create = false,

View File

@@ -1,30 +1,22 @@
import { getGatewayBot } from "../helpers/misc/get_gateway_bot.ts";
import { camelKeysToSnakeCase } from "../util/utils.ts";
import { ws } from "./ws.ts";
/** The handler to automatically reshard when necessary. */
export async function resharder() {
const data = await getGatewayBot();
const percentage = ((data.shards - ws.maxShards) / ws.maxShards) * 100;
ws.botGatewayData = camelKeysToSnakeCase(await getGatewayBot());
const percentage =
((ws.botGatewayData.shards - ws.maxShards) / ws.maxShards) * 100;
// Less than necessary% being used so do nothing
if (percentage < ws.reshardPercentage) return;
// Don't have enough identify rate limits to reshard
if (data.session_start_limit.remaining < data.shards) return;
if (ws.botGatewayData.sessionStartLimit.remaining < ws.botGatewayData.shards)
return;
// Begin resharding
ws.maxShards = data.shards;
// TODO: ALL THE FOLLOWING CAN BE REPLACED BY THIS 1 LINE
// ws.botGatewayData = snakeToCamel(await getGatewayBot())
ws.botGatewayData.sessionStartLimit.total = data.session_start_limit.total;
ws.botGatewayData.sessionStartLimit.resetAfter =
data.session_start_limit.reset_after;
ws.botGatewayData.sessionStartLimit.remaining =
data.session_start_limit.remaining;
ws.botGatewayData.sessionStartLimit.maxConcurrency =
data.session_start_limit.max_concurrency;
ws.botGatewayData.shards = data.shards;
ws.botGatewayData.url = data.url;
ws.maxShards = ws.botGatewayData.shards;
ws.spawnShards(ws.firstShardId);
}

View File

@@ -1,4 +1,5 @@
import { DiscordGatewayIntents } from "../types/gateway/gateway_intents.ts";
import { DiscordGetGatewayBot } from "../types/gateway/get_gateway_bot.ts";
import { StartGatewayOptions } from "./start_gateway_options.ts";
import { ws } from "./ws.ts";
@@ -33,7 +34,7 @@ export async function startGateway(options: StartGatewayOptions) {
const data = (await fetch(`https://discord.com/api/gateway/bot`, {
headers: { Authorization: ws.identifyPayload.token },
}).then((res) => res.json())) as DiscordBotGatewayData;
}).then((res) => res.json())) as DiscordGetGatewayBot;
ws.maxShards = options.maxShards || data.shards;
ws.lastShardId = options.lastShardId || data.shards - 1;