mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-04 01:40:08 +00:00
Merge remote-tracking branch 'upstream/main' into main
This commit is contained in:
102
src/cache.ts
102
src/cache.ts
@@ -1,29 +1,32 @@
|
||||
// deno-lint-ignore-file require-await no-explicit-any prefer-const
|
||||
|
||||
import { Channel, Guild, Member, Message } from "./structures/mod.ts";
|
||||
import { ChannelStruct } from "./structures/channel.ts";
|
||||
import { GuildStruct } from "./structures/guild.ts";
|
||||
import { MemberStruct } from "./structures/member.ts";
|
||||
import { MessageStruct } from "./structures/message.ts";
|
||||
import { Emoji } from "./types/emojis/emoji.ts";
|
||||
import { PresenceUpdate } from "./types/misc/presence_update.ts";
|
||||
import { Collection } from "./util/collection.ts";
|
||||
|
||||
export const cache = {
|
||||
isReady: false,
|
||||
/** All of the guild objects the bot has access to, mapped by their Ids */
|
||||
guilds: new Collection<string, Guild>(),
|
||||
guilds: new Collection<string, GuildStruct>(),
|
||||
/** All of the channel objects the bot has access to, mapped by their Ids */
|
||||
channels: new Collection<string, Channel>(),
|
||||
channels: new Collection<string, ChannelStruct>(),
|
||||
/** All of the message objects the bot has cached since the bot acquired `READY` state, mapped by their Ids */
|
||||
messages: new Collection<string, Message>(),
|
||||
messages: new Collection<string, MessageStruct>(),
|
||||
/** All of the member objects that have been cached since the bot acquired `READY` state, mapped by their Ids */
|
||||
members: new Collection<string, Member>(),
|
||||
members: new Collection<string, MemberStruct>(),
|
||||
/** All of the unavailable guilds, mapped by their Ids (id, timestamp) */
|
||||
unavailableGuilds: new Collection<string, number>(),
|
||||
/** All of the presence update objects received in PRESENCE_UPDATE gateway event, mapped by their user Id */
|
||||
presences: new Collection<string, Presence>(),
|
||||
presences: new Collection<string, PresenceUpdate>(),
|
||||
fetchAllMembersProcessingRequests: new Collection<
|
||||
string,
|
||||
(
|
||||
value:
|
||||
| Collection<string, Member>
|
||||
| PromiseLike<Collection<string, Member>>,
|
||||
| Collection<string, MemberStruct>
|
||||
| PromiseLike<Collection<string, MemberStruct>>,
|
||||
) => void
|
||||
>(),
|
||||
executedSlashCommands: new Collection<string, string>(),
|
||||
@@ -78,28 +81,28 @@ export type TableName =
|
||||
function set(
|
||||
table: "guilds",
|
||||
key: string,
|
||||
value: Guild,
|
||||
): Promise<Collection<string, Guild>>;
|
||||
value: GuildStruct,
|
||||
): Promise<Collection<string, GuildStruct>>;
|
||||
function set(
|
||||
table: "channels",
|
||||
key: string,
|
||||
value: Channel,
|
||||
): Promise<Collection<string, Channel>>;
|
||||
value: ChannelStruct,
|
||||
): Promise<Collection<string, ChannelStruct>>;
|
||||
function set(
|
||||
table: "messages",
|
||||
key: string,
|
||||
value: Message,
|
||||
): Promise<Collection<string, Message>>;
|
||||
value: MessageStruct,
|
||||
): Promise<Collection<string, MessageStruct>>;
|
||||
function set(
|
||||
table: "members",
|
||||
key: string,
|
||||
value: Member,
|
||||
): Promise<Collection<string, Member>>;
|
||||
value: MemberStruct,
|
||||
): Promise<Collection<string, MemberStruct>>;
|
||||
function set(
|
||||
table: "presences",
|
||||
key: string,
|
||||
value: PresenceUpdatePayload,
|
||||
): Promise<Collection<string, PresenceUpdatePayload>>;
|
||||
value: PresenceUpdate,
|
||||
): Promise<Collection<string, PresenceUpdate>>;
|
||||
function set(
|
||||
table: "unavailableGuilds",
|
||||
key: string,
|
||||
@@ -109,41 +112,60 @@ async function set(table: TableName, key: string, value: any) {
|
||||
return cache[table].set(key, value);
|
||||
}
|
||||
|
||||
function get(table: "guilds", key: string): Promise<Guild | undefined>;
|
||||
function get(table: "channels", key: string): Promise<Channel | undefined>;
|
||||
function get(table: "messages", key: string): Promise<Message | undefined>;
|
||||
function get(table: "members", key: string): Promise<Member | undefined>;
|
||||
function get(table: "guilds", key: string): Promise<GuildStruct | undefined>;
|
||||
function get(
|
||||
table: "channels",
|
||||
key: string,
|
||||
): Promise<ChannelStruct | undefined>;
|
||||
function get(table: "messages", key: string): Promise<MemberStruct | undefined>;
|
||||
function get(table: "members", key: string): Promise<MemberStruct | undefined>;
|
||||
function get(
|
||||
table: "presences",
|
||||
key: string,
|
||||
): Promise<PresenceUpdatePayload | undefined>;
|
||||
): Promise<PresenceUpdate | undefined>;
|
||||
function get(
|
||||
table: "unavailableGuilds",
|
||||
key: string,
|
||||
): Promise<Guild | undefined>;
|
||||
): Promise<number | undefined>;
|
||||
async function get(table: TableName, key: string) {
|
||||
return cache[table].get(key);
|
||||
}
|
||||
|
||||
function forEach(
|
||||
table: "guilds",
|
||||
callback: (value: Guild, key: string, map: Map<string, Guild>) => unknown,
|
||||
callback: (
|
||||
value: GuildStruct,
|
||||
key: string,
|
||||
map: Map<string, GuildStruct>,
|
||||
) => unknown,
|
||||
): void;
|
||||
function forEach(
|
||||
table: "unavailableGuilds",
|
||||
callback: (value: Guild, key: string, map: Map<string, Guild>) => unknown,
|
||||
callback: (value: number, key: string, map: Map<string, number>) => unknown,
|
||||
): void;
|
||||
function forEach(
|
||||
table: "channels",
|
||||
callback: (value: Channel, key: string, map: Map<string, Channel>) => unknown,
|
||||
callback: (
|
||||
value: ChannelStruct,
|
||||
key: string,
|
||||
map: Map<string, ChannelStruct>,
|
||||
) => unknown,
|
||||
): void;
|
||||
function forEach(
|
||||
table: "messages",
|
||||
callback: (value: Message, key: string, map: Map<string, Message>) => unknown,
|
||||
callback: (
|
||||
value: MemberStruct,
|
||||
key: string,
|
||||
map: Map<string, MemberStruct>,
|
||||
) => unknown,
|
||||
): void;
|
||||
function forEach(
|
||||
table: "members",
|
||||
callback: (value: Member, key: string, map: Map<string, Member>) => unknown,
|
||||
callback: (
|
||||
value: MemberStruct,
|
||||
key: string,
|
||||
map: Map<string, MemberStruct>,
|
||||
) => unknown,
|
||||
): void;
|
||||
function forEach(
|
||||
table: TableName,
|
||||
@@ -154,24 +176,24 @@ function forEach(
|
||||
|
||||
function filter(
|
||||
table: "guilds",
|
||||
callback: (value: Guild, key: string) => boolean,
|
||||
): Promise<Collection<string, Guild>>;
|
||||
callback: (value: GuildStruct, key: string) => boolean,
|
||||
): Promise<Collection<string, GuildStruct>>;
|
||||
function filter(
|
||||
table: "unavailableGuilds",
|
||||
callback: (value: Guild, key: string) => boolean,
|
||||
): Promise<Collection<string, Guild>>;
|
||||
callback: (value: number, key: string) => boolean,
|
||||
): Promise<Collection<string, number>>;
|
||||
function filter(
|
||||
table: "channels",
|
||||
callback: (value: Channel, key: string) => boolean,
|
||||
): Promise<Collection<string, Channel>>;
|
||||
callback: (value: ChannelStruct, key: string) => boolean,
|
||||
): Promise<Collection<string, ChannelStruct>>;
|
||||
function filter(
|
||||
table: "messages",
|
||||
callback: (value: Message, key: string) => boolean,
|
||||
): Promise<Collection<string, Message>>;
|
||||
callback: (value: MessageStruct, key: string) => boolean,
|
||||
): Promise<Collection<string, MessageStruct>>;
|
||||
function filter(
|
||||
table: "members",
|
||||
callback: (value: Member, key: string) => boolean,
|
||||
): Promise<Collection<string, Member>>;
|
||||
callback: (value: MemberStruct, key: string) => boolean,
|
||||
): Promise<Collection<string, MemberStruct>>;
|
||||
async function filter(
|
||||
table: TableName,
|
||||
callback: (value: any, key: string) => boolean,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordApplicationCommandCreateUpdateDelete } from "../../types/interactions/application_command_create_update_delete.ts";
|
||||
|
||||
export function handleApplicationCommandCreate(
|
||||
data: DiscordGatewayPayload,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordApplicationCommandCreateUpdateDelete } from "../../types/interactions/application_command_create_update_delete.ts";
|
||||
|
||||
export function handleApplicationCommandDelete(data: DiscordGatewayPayload) {
|
||||
const {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordApplicationCommandCreateUpdateDelete } from "../../types/interactions/application_command_create_update_delete.ts";
|
||||
|
||||
export function handleApplicationCommandUpdate(data: DiscordGatewayPayload) {
|
||||
const {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordGuildBanAddRemove } from "../../types/guilds/guild_ban_add_remove.ts";
|
||||
|
||||
export async function handleGuildBanAdd(data: DiscordGatewayPayload) {
|
||||
const payload = data.d as DiscordGuildBanAddRemove;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordGuildBanAddRemove } from "../../types/guilds/guild_ban_add_remove.ts";
|
||||
|
||||
export async function handleGuildBanRemove(data: DiscordGatewayPayload) {
|
||||
const payload = data.d as DiscordGuildBanAddRemove;
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { GuildUpdateChange } from "../../types/discordeno/guild_update_change.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordGuild } from "../../types/guilds/guild.ts";
|
||||
|
||||
export async function handleGuildUpdate(data: DiscordGatewayPayload) {
|
||||
const payload = data.d as DiscordGuild;
|
||||
const cachedGuild = await cacheHandlers.get("guilds", payload.id);
|
||||
if (!cachedGuild) return;
|
||||
const newGuild = await cacheHandlers.get("guilds", payload.id);
|
||||
if (!newGuild) return;
|
||||
|
||||
const keysToSkip = [
|
||||
"roles",
|
||||
"guild_hashes",
|
||||
"guild_id",
|
||||
"max_members",
|
||||
"guildHashes",
|
||||
"guildId",
|
||||
"maxMembers",
|
||||
"emojis",
|
||||
];
|
||||
|
||||
@@ -21,7 +22,7 @@ export async function handleGuildUpdate(data: DiscordGatewayPayload) {
|
||||
if (keysToSkip.includes(key)) return;
|
||||
|
||||
// @ts-ignore index signature
|
||||
const cachedValue = cachedGuild[key];
|
||||
const cachedValue = newGuild[key];
|
||||
if (cachedValue !== value) {
|
||||
// Guild create sends undefined and update sends false.
|
||||
if (!cachedValue && !value) return;
|
||||
@@ -34,12 +35,12 @@ export async function handleGuildUpdate(data: DiscordGatewayPayload) {
|
||||
}
|
||||
|
||||
// @ts-ignore index signature
|
||||
cachedGuild[key] = value;
|
||||
newGuild[key] = value;
|
||||
return { key, oldValue: cachedValue, value };
|
||||
}
|
||||
}).filter((change) => change) as GuildUpdateChange[];
|
||||
|
||||
await cacheHandlers.set("guilds", payload.id, cachedGuild);
|
||||
await cacheHandlers.set("guilds", payload.id, newGuild);
|
||||
|
||||
eventHandlers.guildUpdate?.(cachedGuild, changes);
|
||||
eventHandlers.guildUpdate?.(newGuild, changes);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import {
|
||||
DiscordIntegrationCreateUpdate,
|
||||
IntegrationCreateUpdate,
|
||||
} from "../../types/integration/integration_create_update.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
export function handleIntegrationCreate(
|
||||
data: DiscordGatewayPayload,
|
||||
) {
|
||||
const {
|
||||
guild_id: guildId,
|
||||
enable_emoticons: enableEmoticons,
|
||||
expire_behavior: expireBehavior,
|
||||
expire_grace_period: expireGracePeriod,
|
||||
subscriber_count: subscriberCount,
|
||||
role_id: roleId,
|
||||
synced_at: syncedAt,
|
||||
...rest
|
||||
} = data.d as IntegrationCreateUpdateEvent;
|
||||
const payload = data.d as DiscordIntegrationCreateUpdate;
|
||||
|
||||
eventHandlers.integrationCreate?.({
|
||||
...rest,
|
||||
guildId,
|
||||
enableEmoticons,
|
||||
expireBehavior,
|
||||
expireGracePeriod,
|
||||
syncedAt,
|
||||
subscriberCount,
|
||||
roleId,
|
||||
});
|
||||
eventHandlers.integrationCreate?.(
|
||||
snakeKeysToCamelCase(payload) as IntegrationCreateUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import {
|
||||
DiscordIntegrationDelete,
|
||||
IntegrationDelete,
|
||||
} from "../../types/integration/integration_delete.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
export function handleIntegrationDelete(data: DiscordGatewayPayload) {
|
||||
const {
|
||||
guild_id: guildId,
|
||||
application_id: applicationId,
|
||||
...rest
|
||||
} = data.d as IntegrationDeleteEvent;
|
||||
const payload = data.d as DiscordIntegrationDelete;
|
||||
|
||||
eventHandlers.integrationDelete?.({
|
||||
...rest,
|
||||
applicationId,
|
||||
guildId,
|
||||
});
|
||||
eventHandlers.integrationDelete?.(
|
||||
snakeKeysToCamelCase(payload) as IntegrationDelete,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import {
|
||||
DiscordIntegrationCreateUpdate,
|
||||
IntegrationCreateUpdate,
|
||||
} from "../../types/integration/integration_create_update.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
export function handleIntegrationUpdate(data: DiscordGatewayPayload) {
|
||||
const {
|
||||
enable_emoticons: enableEmoticons,
|
||||
expire_behavior: expireBehavior,
|
||||
expire_grace_period: expireGracePeriod,
|
||||
role_id: roleId,
|
||||
subscriber_count: subscriberCount,
|
||||
synced_at: syncedAt,
|
||||
guild_id: guildId,
|
||||
...rest
|
||||
} = data.d as IntegrationCreateUpdateEvent;
|
||||
const payload = data.d as DiscordIntegrationCreateUpdate;
|
||||
|
||||
eventHandlers.integrationUpdate?.({
|
||||
...rest,
|
||||
guildId,
|
||||
subscriberCount,
|
||||
enableEmoticons,
|
||||
expireGracePeriod,
|
||||
roleId,
|
||||
expireBehavior,
|
||||
syncedAt,
|
||||
});
|
||||
eventHandlers.integrationUpdate?.(
|
||||
snakeKeysToCamelCase(payload) as IntegrationCreateUpdate,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,13 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import { DiscordInviteCreate } from "../../types/invites/invite_create.ts";
|
||||
import {
|
||||
DiscordInviteCreate,
|
||||
InviteCreate,
|
||||
} from "../../types/invites/invite_create.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
export function handleInviteCreate(payload: DiscordGatewayPayload) {
|
||||
// TODO: replace with tocamelcase
|
||||
const {
|
||||
channel_id: channelId,
|
||||
created_at: createdAt,
|
||||
max_age: maxAge,
|
||||
guild_id: guildId,
|
||||
target_user: targetUser,
|
||||
target_user_type: targetUserType,
|
||||
max_uses: maxUses,
|
||||
...rest
|
||||
} = payload.d as DiscordInviteCreate;
|
||||
export function handleInviteCreate(data: DiscordGatewayPayload) {
|
||||
const payload = data.d as DiscordInviteCreate;
|
||||
|
||||
eventHandlers.inviteCreate?.({
|
||||
...rest,
|
||||
channelId,
|
||||
guildId,
|
||||
maxAge,
|
||||
targetUser,
|
||||
targetUserType,
|
||||
maxUses,
|
||||
createdAt,
|
||||
});
|
||||
eventHandlers.inviteCreate?.(snakeKeysToCamelCase(payload) as InviteCreate);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordInviteDelete } from "../../types/invites/invite_delete.ts";
|
||||
import { DiscordGatewayPayload } from "../../types/gateway/gateway_payload.ts";
|
||||
import {
|
||||
DiscordInviteDelete,
|
||||
InviteDelete,
|
||||
} from "../../types/invites/invite_delete.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
export function handleInviteDelete(payload: DiscordGatewayPayload) {
|
||||
const {
|
||||
channel_id: channelId,
|
||||
guild_id: guildId,
|
||||
...rest
|
||||
} = payload.d as DiscordInviteDelete;
|
||||
export function handleInviteDelete(data: DiscordGatewayPayload) {
|
||||
const payload = data.d as DiscordInviteDelete;
|
||||
|
||||
eventHandlers.inviteDelete?.({
|
||||
...rest,
|
||||
channelId,
|
||||
guildId,
|
||||
});
|
||||
eventHandlers.inviteDelete?.(snakeKeysToCamelCase(payload) as InviteDelete);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export async function deleteChannel(
|
||||
guildId: string,
|
||||
channelId: string,
|
||||
reason?: string,
|
||||
) {
|
||||
): Promise<undefined> {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_CHANNELS"]);
|
||||
|
||||
const guild = await cacheHandlers.get("guilds", guildId);
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function deleteChannelOverwrite(
|
||||
guildId: string,
|
||||
channelId: string,
|
||||
overwriteId: string,
|
||||
) {
|
||||
): Promise<undefined> {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { ModifyChannel } from "../../types/channels/modify_channel.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import {
|
||||
calculateBits,
|
||||
@@ -8,7 +9,7 @@ import {
|
||||
/** Update a channel's settings. Requires the `MANAGE_CHANNELS` permission for the guild. */
|
||||
export async function editChannel(
|
||||
channelId: string,
|
||||
options: ChannelEditOptions,
|
||||
options: ModifyChannel,
|
||||
reason?: string,
|
||||
) {
|
||||
await requireBotChannelPermissions(channelId, ["MANAGE_CHANNELS"]);
|
||||
@@ -47,7 +48,7 @@ export async function editChannel(
|
||||
// deno-lint-ignore camelcase
|
||||
user_limit: options.userLimit,
|
||||
// deno-lint-ignore camelcase
|
||||
permission_overwrites: options.overwrites?.map((overwrite) => {
|
||||
permission_overwrites: options.permissionOverwrites?.map((overwrite) => {
|
||||
return {
|
||||
...overwrite,
|
||||
allow: calculateBits(overwrite.allow),
|
||||
|
||||
@@ -12,7 +12,7 @@ export async function editChannelOverwrite(
|
||||
channelId: string,
|
||||
overwriteId: string,
|
||||
options: Omit<Overwrite, "id">,
|
||||
) {
|
||||
): Promise<undefined> {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
|
||||
@@ -10,7 +10,7 @@ import { botHasChannelPermissions } from "../../util/permissions.ts";
|
||||
* However, if a bot is responding to a command and expects the computation to take a few seconds,
|
||||
* this endpoint may be called to let the user know that the bot is processing their message.
|
||||
*/
|
||||
export async function startTyping(channelId: string) {
|
||||
export async function startTyping(channelId: string): Promise<undefined> {
|
||||
const channel = await cacheHandlers.get("channels", channelId);
|
||||
// If the channel is cached, we can do extra checks/safety
|
||||
if (channel) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { endpoints } from "../../util/constants.ts";
|
||||
export async function swapChannels(
|
||||
guildId: string,
|
||||
channelPositions: ModifyGuildChannelPositions[],
|
||||
) {
|
||||
): Promise<undefined> {
|
||||
if (channelPositions.length < 2) {
|
||||
throw "You must provide at least two channels to be swapped.";
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { CreateGlobalApplicationCommand } from "../../types/interactions/create_global_application_command.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { validateSlashCommands } from "../../util/utils.ts";
|
||||
import {
|
||||
camelKeysToSnakeCase,
|
||||
validateSlashCommands,
|
||||
} from "../../util/utils.ts";
|
||||
|
||||
/**
|
||||
* There are two kinds of Slash Commands: global commands and guild commands. Global commands are available for every guild that adds your app; guild commands are specific to the guild you specify when making them. Command names are unique per application within each scope (global and guild). That means:
|
||||
@@ -14,17 +18,18 @@ import { validateSlashCommands } from "../../util/utils.ts";
|
||||
* Global commands are cached for **1 hour**. That means that new global commands will fan out slowly across all guilds, and will be guaranteed to be updated in an hour.
|
||||
* Guild commands update **instantly**. We recommend you use guild commands for quick testing, and global commands when they're ready for public use.
|
||||
*/
|
||||
export async function createSlashCommand(options: CreateSlashCommandOptions) {
|
||||
export async function createSlashCommand(
|
||||
options: CreateGlobalApplicationCommand,
|
||||
guildId: string,
|
||||
) {
|
||||
validateSlashCommands([options], true);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
"post",
|
||||
options.guildId
|
||||
? endpoints.COMMANDS_GUILD(applicationId, options.guildId)
|
||||
guildId
|
||||
? endpoints.COMMANDS_GUILD(applicationId, guildId)
|
||||
: endpoints.COMMANDS(applicationId),
|
||||
{
|
||||
...options,
|
||||
},
|
||||
camelKeysToSnakeCase(options),
|
||||
);
|
||||
|
||||
return result;
|
||||
|
||||
@@ -3,12 +3,16 @@ import { rest } from "../../rest/rest.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Deletes a slash command. */
|
||||
export function deleteSlashCommand(id: string, guildId?: string) {
|
||||
if (!guildId) {
|
||||
return rest.runMethod("delete", endpoints.COMMANDS_ID(applicationId, id));
|
||||
}
|
||||
return rest.runMethod(
|
||||
export async function deleteSlashCommand(
|
||||
id: string,
|
||||
guildId?: string,
|
||||
): Promise<undefined> {
|
||||
const result = await rest.runMethod(
|
||||
"delete",
|
||||
endpoints.COMMANDS_GUILD_ID(applicationId, guildId, id),
|
||||
guildId
|
||||
? endpoints.COMMANDS_GUILD_ID(applicationId, guildId, id)
|
||||
: endpoints.COMMANDS_ID(applicationId, id),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { rest } from "../../rest/rest.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** To delete your response to a slash command. If a message id is not provided, it will default to deleting the original response. */
|
||||
export async function deleteSlashResponse(token: string, messageId?: string) {
|
||||
export async function deleteSlashResponse(
|
||||
token: string,
|
||||
messageId?: string,
|
||||
): Promise<undefined> {
|
||||
const result = await rest.runMethod(
|
||||
"delete",
|
||||
messageId
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { DiscordenoEditWebhookMessage } from "../../types/discordeno/edit_webhook_message.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 { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** To edit your response to a slash command. If a messageId is not provided it will default to editing the original response. */
|
||||
export async function editSlashResponse(
|
||||
token: string,
|
||||
options: EditSlashResponseOptions,
|
||||
options: DiscordenoEditWebhookMessage,
|
||||
) {
|
||||
if (options.content && options.content.length > 2000) {
|
||||
throw Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
@@ -17,31 +20,39 @@ export async function editSlashResponse(
|
||||
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(
|
||||
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.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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.roles?.length) {
|
||||
if (options.allowed_mentions.parse.includes("roles")) {
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
|
||||
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,
|
||||
);
|
||||
@@ -61,7 +72,7 @@ export async function editSlashResponse(
|
||||
if (!options.messageId) return result;
|
||||
|
||||
const message = await structures.createMessageStruct(
|
||||
result as MessageCreateOptions,
|
||||
result as DiscordMessage,
|
||||
);
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { ApplicationCommand } from "../../types/interactions/application_command.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Fetchs the global command for the given Id. If a guildId is provided, the guild command will be fetched. */
|
||||
@@ -11,5 +12,5 @@ export async function getSlashCommand(commandId: string, guildId?: string) {
|
||||
: endpoints.COMMANDS_ID(applicationId, commandId),
|
||||
);
|
||||
|
||||
return result as SlashCommand;
|
||||
return result as ApplicationCommand;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { ApplicationCommand } from "../../types/interactions/application_command.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
@@ -10,7 +11,7 @@ export async function getSlashCommands(guildId?: string) {
|
||||
guildId
|
||||
? endpoints.COMMANDS_GUILD(applicationId, guildId)
|
||||
: endpoints.COMMANDS(applicationId),
|
||||
)) as SlashCommand[];
|
||||
)) as ApplicationCommand[];
|
||||
|
||||
return new Collection(result.map((command) => [command.name, command]));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { cache } from "../../cache.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { DiscordenoInteractionResponse } from "../../types/discordeno/interaction_response.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/**
|
||||
@@ -12,7 +13,7 @@ import { endpoints } from "../../util/constants.ts";
|
||||
export async function sendInteractionResponse(
|
||||
id: string,
|
||||
token: string,
|
||||
options: SlashCommandResponseOptions,
|
||||
options: DiscordenoInteractionResponse,
|
||||
) {
|
||||
// If its already been executed, we need to send a followup response
|
||||
if (cache.executedSlashCommands.has(token)) {
|
||||
@@ -30,12 +31,12 @@ export async function sendInteractionResponse(
|
||||
|
||||
// If the user wants this as a private message mark it ephemeral
|
||||
if (options.private) {
|
||||
options.data.flags = 64;
|
||||
options.data = { ...options.data, flags: 64 };
|
||||
}
|
||||
|
||||
// If no mentions are provided, force disable mentions
|
||||
if (!options.data.allowed_mentions) {
|
||||
options.data.allowed_mentions = { parse: [] };
|
||||
if (!options.data?.allowedMentions) {
|
||||
options.data = { ...options.data, allowedMentions: { parse: [] } };
|
||||
}
|
||||
|
||||
const result = await rest.runMethod(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { EditGlobalApplicationCommand } from "../../types/interactions/edit_global_application_command.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { validateSlashCommands } from "../../util/utils.ts";
|
||||
|
||||
@@ -8,7 +9,7 @@ import { validateSlashCommands } from "../../util/utils.ts";
|
||||
*/
|
||||
export async function upsertSlashCommand(
|
||||
commandId: string,
|
||||
options: UpsertSlashCommandOptions,
|
||||
options: EditGlobalApplicationCommand,
|
||||
guildId?: string,
|
||||
) {
|
||||
validateSlashCommands([options]);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { applicationId } from "../../bot.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { EditGlobalApplicationCommand } from "../../types/interactions/edit_global_application_command.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { validateSlashCommands } from "../../util/utils.ts";
|
||||
|
||||
@@ -9,7 +10,7 @@ import { validateSlashCommands } from "../../util/utils.ts";
|
||||
* **NOTE:** Any slash commands that are not specified in this function will be **deleted**. If you don't provide the commandId and rename your command, the command gets a new Id.
|
||||
*/
|
||||
export async function upsertSlashCommands(
|
||||
options: UpsertSlashCommandsOptions[],
|
||||
options: EditGlobalApplicationCommand[],
|
||||
guildId?: string,
|
||||
) {
|
||||
validateSlashCommands(options);
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { CreateGuildEmoji } from "../../types/emojis/create_guild_emoji.ts";
|
||||
import { Emoji } from "../../types/emojis/emoji.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { urlToBase64 } from "../../util/utils.ts";
|
||||
import { snakeKeysToCamelCase, urlToBase64 } from "../../util/utils.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(
|
||||
guildId: string,
|
||||
name: string,
|
||||
image: string,
|
||||
options: CreateEmojisOptions,
|
||||
options: CreateGuildEmoji,
|
||||
) {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_EMOJIS"]);
|
||||
|
||||
@@ -22,5 +24,5 @@ export async function createEmoji(
|
||||
image,
|
||||
});
|
||||
|
||||
return result;
|
||||
return snakeKeysToCamelCase(result) as Emoji;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function deleteEmoji(
|
||||
guildId: string,
|
||||
id: string,
|
||||
reason?: string,
|
||||
) {
|
||||
): Promise<undefined> {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_EMOJIS"]);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { ModifyGuildEmoji } from "../../types/emojis/modify_guild_emoji.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
|
||||
@@ -6,7 +7,7 @@ import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
export async function editEmoji(
|
||||
guildId: string,
|
||||
id: string,
|
||||
options: EditEmojisOptions,
|
||||
options: ModifyGuildEmoji,
|
||||
) {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_EMOJIS"]);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { Emoji } from "../../types/emojis/emoji.ts";
|
||||
import { Errors } from "../../types/misc/errors.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
@@ -21,7 +22,7 @@ export async function getEmoji(
|
||||
if (addToCache) {
|
||||
const guild = await cacheHandlers.get("guilds", guildId);
|
||||
if (!guild) throw new Error(Errors.GUILD_NOT_FOUND);
|
||||
guild.emojis.set(result.id ?? result.name, result);
|
||||
guild.emojis.set(emojiId, result);
|
||||
cacheHandlers.set(
|
||||
"guilds",
|
||||
guildId,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { Emoji } from "../../types/emojis/emoji.ts";
|
||||
import { Errors } from "../../types/misc/errors.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
@@ -16,7 +17,7 @@ export async function getEmojis(guildId: string, addToCache = true) {
|
||||
const guild = await cacheHandlers.get("guilds", guildId);
|
||||
if (!guild) throw new Error(Errors.GUILD_NOT_FOUND);
|
||||
|
||||
result.forEach((emoji) => guild.emojis.set(emoji.id ?? emoji.name, emoji));
|
||||
result.forEach((emoji) => guild.emojis.set(emoji.id!, emoji));
|
||||
|
||||
cacheHandlers.set("guilds", guildId, guild);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { CreateGuild } from "../../types/guilds/create_guild.ts";
|
||||
import { DiscordGuild } from "../../types/guilds/guild.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */
|
||||
export async function createGuild(options: CreateServerOptions) {
|
||||
export async function createGuild(options: CreateGuild) {
|
||||
const guild = (await rest.runMethod(
|
||||
"post",
|
||||
endpoints.GUILDS,
|
||||
options,
|
||||
)) as CreateGuildPayload;
|
||||
)) as DiscordGuild;
|
||||
|
||||
return structures.createGuildStruct(guild, 0);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event.
|
||||
*/
|
||||
export async function deleteServer(guildId: string) {
|
||||
export async function deleteServer(guildId: string): Promise<undefined> {
|
||||
const result = await rest.runMethod("delete", endpoints.GUILDS_BASE(guildId));
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { DiscordGuild } from "../../types/guilds/guild.ts";
|
||||
import { ModifyGuild } from "../../types/guilds/modify_guild.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { urlToBase64 } from "../../util/utils.ts";
|
||||
|
||||
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
|
||||
export async function editGuild(guildId: string, options: GuildEditOptions) {
|
||||
export async function editGuild(guildId: string, options: ModifyGuild) {
|
||||
await requireBotGuildPermissions(guildId, ["MANAGE_GUILD"]);
|
||||
|
||||
if (options.icon && !options.icon.startsWith("data:image/")) {
|
||||
@@ -23,7 +26,7 @@ export async function editGuild(guildId: string, options: GuildEditOptions) {
|
||||
"patch",
|
||||
endpoints.GUILDS_BASE(guildId),
|
||||
options,
|
||||
);
|
||||
) as DiscordGuild;
|
||||
|
||||
return result;
|
||||
return structures.createGuildStruct(result, -1);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { GuildWidget } from "../../types/guilds/guild_widget.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Modify a guild widget object for the guild. Requires the MANAGE_GUILD permission. */
|
||||
export async function editWidget(
|
||||
@@ -19,5 +21,5 @@ export async function editWidget(
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
return snakeKeysToCamelCase(result) as GuildWidget;
|
||||
}
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { AuditLog } from "../../types/audit_log/audit_log.ts";
|
||||
import { GetGuildAuditLog } from "../../types/audit_log/get_guild_audit_log.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import {
|
||||
camelKeysToSnakeCase,
|
||||
snakeKeysToCamelCase,
|
||||
} from "../../util/utils.ts";
|
||||
|
||||
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
|
||||
export async function getAuditLogs(
|
||||
guildId: string,
|
||||
options: GetAuditLogsOptions,
|
||||
options: GetGuildAuditLog,
|
||||
) {
|
||||
await requireBotGuildPermissions(guildId, ["VIEW_AUDIT_LOG"]);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
"get",
|
||||
endpoints.GUILD_AUDIT_LOGS(guildId),
|
||||
{
|
||||
camelKeysToSnakeCase({
|
||||
...options,
|
||||
action_type: options.action_type
|
||||
? AuditLogs[options.action_type]
|
||||
: undefined,
|
||||
limit: options.limit && options.limit >= 1 && options.limit <= 100
|
||||
? options.limit
|
||||
: 50,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return result;
|
||||
return snakeKeysToCamelCase(result) as AuditLog;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { VoiceRegion } from "../../types/voice/voice_region.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Returns an array of voice regions that can be used when creating servers. */
|
||||
export async function getAvailableVoiceRegions() {
|
||||
const result = await rest.runMethod("get", endpoints.VOICE_REGIONS);
|
||||
|
||||
return result;
|
||||
return result as VoiceRegion;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { Ban } from "../../types/guilds/ban.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */
|
||||
export async function getBan(guildId: string, memberId: string) {
|
||||
@@ -11,5 +13,5 @@ export async function getBan(guildId: string, memberId: string) {
|
||||
endpoints.GUILD_BAN(guildId, memberId),
|
||||
);
|
||||
|
||||
return result as BannedUser;
|
||||
return snakeKeysToCamelCase(result) as Ban;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { Ban, DiscordBan } from "../../types/guilds/ban.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
|
||||
export async function getBans(guildId: string) {
|
||||
@@ -10,9 +12,9 @@ export async function getBans(guildId: string) {
|
||||
const results = (await rest.runMethod(
|
||||
"get",
|
||||
endpoints.GUILD_BANS(guildId),
|
||||
)) as BannedUser[];
|
||||
)) as DiscordBan[];
|
||||
|
||||
return new Collection<string, BannedUser>(
|
||||
results.map((res) => [res.user.id, res]),
|
||||
return new Collection<string, Ban>(
|
||||
results.map((res) => [res.user.id, snakeKeysToCamelCase(res) as Ban]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { Guild } from "../../types/guilds/guild.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/**
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get()
|
||||
@@ -13,5 +15,5 @@ export async function getGuild(guildId: string, counts = true) {
|
||||
with_counts: counts,
|
||||
});
|
||||
|
||||
return result as UpdateGuildPayload;
|
||||
return snakeKeysToCamelCase(result) as Guild;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { GuildPreview } from "../../types/guilds/guild_preview.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Returns the guild preview object for the given id. If the bot is not in the guild, then the guild must be Discoverable. */
|
||||
export async function getGuildPreview(guildId: string) {
|
||||
const result = await rest.runMethod("get", endpoints.GUILD_PREVIEW(guildId));
|
||||
|
||||
return result;
|
||||
return snakeKeysToCamelCase(result) as GuildPreview;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { GetGuildPruneCountQuery } from "../../types/guilds/get_guild_prune_count.ts";
|
||||
import { Errors } from "../../types/misc/errors.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
import { camelKeysToSnakeCase } from "../../util/utils.ts";
|
||||
|
||||
/** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */
|
||||
export async function getPruneCount(guildId: string, options?: PruneOptions) {
|
||||
export async function getPruneCount(
|
||||
guildId: string,
|
||||
options?: GetGuildPruneCountQuery,
|
||||
) {
|
||||
if (options?.days && options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
|
||||
if (options?.days && options.days > 30) {
|
||||
throw new Error(Errors.PRUNE_MAX_DAYS);
|
||||
@@ -17,7 +21,7 @@ export async function getPruneCount(guildId: string, options?: PruneOptions) {
|
||||
"get",
|
||||
endpoints.GUILD_PRUNE(guildId),
|
||||
camelKeysToSnakeCase(options ?? {}),
|
||||
) as PrunePayload;
|
||||
);
|
||||
|
||||
return result.pruned;
|
||||
return result.pruned as number;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { InviteMetadata } from "../../types/invites/invite_metadata.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */
|
||||
/** Returns the code and uses of the vanity url for this server if it is enabled else `code` will be null. Requires the `MANAGE_GUILD` permission. */
|
||||
export async function getVanityURL(guildId: string) {
|
||||
const result = await rest.runMethod(
|
||||
"get",
|
||||
endpoints.GUILD_VANITY_URL(guildId),
|
||||
);
|
||||
|
||||
return result;
|
||||
return snakeKeysToCamelCase(result) as
|
||||
| (Partial<InviteMetadata> & Pick<InviteMetadata, "uses" | "code">)
|
||||
| { code: null };
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import {
|
||||
DiscordVoiceRegion,
|
||||
VoiceRegion,
|
||||
} from "../../types/voice/voice_region.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { snakeKeysToCamelCase } from "../../util/utils.ts";
|
||||
|
||||
/** Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when the guild is VIP-enabled. */
|
||||
export async function getVoiceRegions(guildId: string) {
|
||||
const result = await rest.runMethod("get", endpoints.GUILD_REGIONS(guildId));
|
||||
const result = await rest.runMethod(
|
||||
"get",
|
||||
endpoints.GUILD_REGIONS(guildId),
|
||||
) as DiscordVoiceRegion[];
|
||||
|
||||
return result;
|
||||
const convertedRegions = snakeKeysToCamelCase<VoiceRegion[]>(result);
|
||||
|
||||
return new Collection<string, VoiceRegion>(
|
||||
convertedRegions.map((region) => [region.id, region]),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { GetGuildWidgetImageQuery } from "../../types/guilds/get_guild_widget_image.ts";
|
||||
import { Errors } from "../../types/misc/errors.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Returns the widget image URL for the guild. */
|
||||
export async function getWidgetImageURL(
|
||||
guildId: string,
|
||||
options?: {
|
||||
style?: "shield" | "banner1" | "banner2" | "banner3" | "banner4";
|
||||
force?: boolean;
|
||||
},
|
||||
options?: GetGuildWidgetImageQuery & { force?: boolean },
|
||||
) {
|
||||
if (!options?.force) {
|
||||
const guild = await cacheHandlers.get("guilds", guildId);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { GuildWidget } from "../../types/guilds/guild_widget.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotGuildPermissions } from "../../util/permissions.ts";
|
||||
|
||||
@@ -8,5 +9,5 @@ export async function getWidgetSettings(guildId: string) {
|
||||
|
||||
const result = await rest.runMethod("get", endpoints.GUILD_WIDGET(guildId));
|
||||
|
||||
return result;
|
||||
return result as GuildWidget;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { formatImageURL } from "../../util/utils.ts";
|
||||
/** The full URL of the banner from Discords CDN. Undefined if no banner is set. */
|
||||
export function guildBannerURL(
|
||||
id: string,
|
||||
banner: string,
|
||||
banner?: string,
|
||||
size: DiscordImageSize = 128,
|
||||
format?: DiscordImageFormat,
|
||||
) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { formatImageURL } from "../../util/utils.ts";
|
||||
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
|
||||
export function guildIconURL(
|
||||
id: string,
|
||||
icon: string,
|
||||
icon?: string,
|
||||
size: DiscordImageSize = 128,
|
||||
format?: DiscordImageFormat,
|
||||
) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { formatImageURL } from "../../util/utils.ts";
|
||||
/** The full URL of the splash from Discords CDN. Undefined if no splash is set. */
|
||||
export function guildSplashURL(
|
||||
id: string,
|
||||
splash: string,
|
||||
splash?: string,
|
||||
size: DiscordImageSize = 128,
|
||||
format?: DiscordImageFormat,
|
||||
) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { rest } from "../../rest/rest.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Leave a guild */
|
||||
export async function leaveGuild(guildId: string) {
|
||||
export async function leaveGuild(guildId: string): Promise<undefined> {
|
||||
const result = await rest.runMethod("delete", endpoints.GUILD_LEAVE(guildId));
|
||||
|
||||
return result;
|
||||
|
||||
@@ -2,15 +2,19 @@ import { cacheHandlers } from "../../cache.ts";
|
||||
import { rest } from "../../rest/rest.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { DiscordChannelTypes } from "../../types/channels/channel_types.ts";
|
||||
import { DiscordenoCreateMessage } from "../../types/discordeno/create_message.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 { PermissionStrings } from "../../types/permissions/permission_strings.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||
import { camelKeysToSnakeCase } from "../../util/utils.ts";
|
||||
|
||||
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||
export async function sendMessage(
|
||||
channelId: string,
|
||||
content: string | MessageContent,
|
||||
content: string | DiscordenoCreateMessage,
|
||||
) {
|
||||
if (typeof content === "string") content = { content };
|
||||
|
||||
@@ -33,7 +37,10 @@ export async function sendMessage(
|
||||
|
||||
if (content.tts) requiredPerms.add("SEND_TTS_MESSAGES");
|
||||
if (content.embed) requiredPerms.add("EMBED_LINKS");
|
||||
if (content.replyMessageId || content.mentions?.repliedUser) {
|
||||
if (
|
||||
content.messageReference?.messageId ||
|
||||
content.allowedMentions?.repliedUser
|
||||
) {
|
||||
requiredPerms.add("READ_MESSAGE_HISTORY");
|
||||
}
|
||||
|
||||
@@ -45,50 +52,63 @@ export async function sendMessage(
|
||||
throw new Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (content.mentions) {
|
||||
if (content.mentions.users?.length) {
|
||||
if (content.mentions.parse?.includes("users")) {
|
||||
content.mentions.parse = content.mentions.parse.filter(
|
||||
if (content.allowedMentions) {
|
||||
if (content.allowedMentions.users?.length) {
|
||||
if (
|
||||
content.allowedMentions.parse?.includes(
|
||||
DiscordAllowedMentionsTypes.UserMentions,
|
||||
)
|
||||
) {
|
||||
content.allowedMentions.parse = content.allowedMentions.parse.filter(
|
||||
(p) => p !== "users",
|
||||
);
|
||||
}
|
||||
|
||||
if (content.mentions.users.length > 100) {
|
||||
content.mentions.users = content.mentions.users.slice(0, 100);
|
||||
if (content.allowedMentions.users.length > 100) {
|
||||
content.allowedMentions.users = content.allowedMentions.users.slice(
|
||||
0,
|
||||
100,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (content.mentions.roles?.length) {
|
||||
if (content.mentions.parse?.includes("roles")) {
|
||||
content.mentions.parse = content.mentions.parse.filter(
|
||||
if (content.allowedMentions.roles?.length) {
|
||||
if (
|
||||
content.allowedMentions.parse?.includes(
|
||||
DiscordAllowedMentionsTypes.RoleMentions,
|
||||
)
|
||||
) {
|
||||
content.allowedMentions.parse = content.allowedMentions.parse.filter(
|
||||
(p) => p !== "roles",
|
||||
);
|
||||
}
|
||||
|
||||
if (content.mentions.roles.length > 100) {
|
||||
content.mentions.roles = content.mentions.roles.slice(0, 100);
|
||||
if (content.allowedMentions.roles.length > 100) {
|
||||
content.allowedMentions.roles = content.allowedMentions.roles.slice(
|
||||
0,
|
||||
100,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result =
|
||||
(await rest.runMethod("post", endpoints.CHANNEL_MESSAGES(channelId), {
|
||||
...content,
|
||||
allowed_mentions: content.mentions
|
||||
? {
|
||||
...content.mentions,
|
||||
replied_user: content.mentions.repliedUser,
|
||||
}
|
||||
: undefined,
|
||||
...(content.replyMessageId
|
||||
? {
|
||||
message_reference: {
|
||||
message_id: content.replyMessageId,
|
||||
fail_if_not_exists: content.failReplyIfNotExists === true,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
})) as MessageCreateOptions;
|
||||
(await rest.runMethod(
|
||||
"post",
|
||||
endpoints.CHANNEL_MESSAGES(channelId),
|
||||
camelKeysToSnakeCase({
|
||||
...content,
|
||||
...(content.messageReference?.messageId
|
||||
? {
|
||||
messageReference: {
|
||||
...content.messageReference,
|
||||
failIfNotExists:
|
||||
content.messageReference.failIfNotExists === true,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
)) as DiscordMessage;
|
||||
|
||||
return structures.createMessageStruct(result);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@ import { endpoints } from "../../util/constants.ts";
|
||||
import { requireBotChannelPermissions } from "../../util/permissions.ts";
|
||||
|
||||
/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */
|
||||
export async function unpin(channelId: string, messageId: string) {
|
||||
export async function unpin(
|
||||
channelId: string,
|
||||
messageId: string,
|
||||
): Promise<undefined> {
|
||||
await requireBotChannelPermissions(channelId, ["MANAGE_MESSAGES"]);
|
||||
|
||||
const result = await rest.runMethod(
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Errors } from "../types/misc/errors.ts";
|
||||
import { IMAGE_BASE_URL } from "../util/constants.ts";
|
||||
import { API_VERSION } from "../util/constants.ts";
|
||||
import { BASE_URL } from "../util/constants.ts";
|
||||
import { API_VERSION, BASE_URL, IMAGE_BASE_URL } from "../util/constants.ts";
|
||||
import { rest } from "./rest.ts";
|
||||
|
||||
export function runMethod(
|
||||
export function runMethod<T = any>(
|
||||
method: "get" | "post" | "put" | "delete" | "patch",
|
||||
url: string,
|
||||
body?: unknown,
|
||||
retryCount = 0,
|
||||
bucketId?: string | null,
|
||||
) {
|
||||
): Promise<T | undefined> {
|
||||
rest.eventHandlers.debug?.("requestCreate", {
|
||||
method,
|
||||
url,
|
||||
@@ -37,7 +35,7 @@ export function runMethod(
|
||||
.then((res) => {
|
||||
if (res.status === 204) return undefined;
|
||||
|
||||
return res.json();
|
||||
return res.json() as unknown as T;
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
|
||||
@@ -13,17 +13,30 @@ import { getInvites } from "../helpers/invites/get_invites.ts";
|
||||
import { banMember } from "../helpers/members/ban_member.ts";
|
||||
import { unbanMember } from "../helpers/members/unban_member.ts";
|
||||
import { GetGuildAuditLog } from "../types/audit_log/get_guild_audit_log.ts";
|
||||
import { Emoji } from "../types/emojis/emoji.ts";
|
||||
import { CreateGuildBan } from "../types/guilds/create_guild_ban.ts";
|
||||
import { DiscordGuild, Guild } from "../types/guilds/guild.ts";
|
||||
import { DiscordGuildFeatures } from "../types/guilds/guild_features.ts";
|
||||
import { GuildMember } from "../types/guilds/guild_member.ts";
|
||||
import {
|
||||
DiscordGuildMember,
|
||||
GuildMember,
|
||||
} from "../types/guilds/guild_member.ts";
|
||||
import { ModifyGuild } from "../types/guilds/modify_guild.ts";
|
||||
import { DiscordImageFormat } from "../types/misc/image_format.ts";
|
||||
import { DiscordImageSize } from "../types/misc/image_size.ts";
|
||||
import { PresenceUpdate } from "../types/misc/presence_update.ts";
|
||||
import { DiscordUser } from "../types/users/user.ts";
|
||||
import { VoiceState } from "../types/voice/voice_state.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { createNewProp, snakeKeysToCamelCase } from "../util/utils.ts";
|
||||
import { RoleStruct, structures } from "./mod.ts";
|
||||
import {
|
||||
camelKeysToSnakeCase,
|
||||
createNewProp,
|
||||
snakeKeysToCamelCase,
|
||||
} from "../util/utils.ts";
|
||||
import { ChannelStruct } from "./channel.ts";
|
||||
import { MemberStruct } from "./member.ts";
|
||||
import { structures } from "./mod.ts";
|
||||
import { RoleStruct } from "./role.ts";
|
||||
|
||||
export const initialMemberLoadQueue = new Map<string, GuildMember[]>();
|
||||
|
||||
@@ -118,7 +131,9 @@ export async function createGuildStruct(
|
||||
} = snakeKeysToCamelCase(data) as Guild;
|
||||
|
||||
const roles = await Promise.all(
|
||||
data.roles.map((role) => structures.createRoleStruct(role)),
|
||||
data.roles.map((role) =>
|
||||
structures.createRoleStruct({ role, guild_id: rest.id })
|
||||
),
|
||||
);
|
||||
|
||||
await Promise.all(channels.map(async (channel) => {
|
||||
@@ -164,7 +179,9 @@ export async function createGuildStruct(
|
||||
await Promise.allSettled(
|
||||
members.map(async (member) => {
|
||||
const memberStruct = await structures.createMemberStruct(
|
||||
member,
|
||||
camelKeysToSnakeCase(member) as Omit<DiscordGuildMember, "user"> & {
|
||||
user: DiscordUser;
|
||||
},
|
||||
guild.id,
|
||||
);
|
||||
|
||||
@@ -179,14 +196,25 @@ export async function createGuildStruct(
|
||||
export interface GuildStruct extends
|
||||
Omit<
|
||||
Guild,
|
||||
"roles" | "presences" | "voiceStates" | "members" | "channels"
|
||||
| "roles"
|
||||
| "presences"
|
||||
| "voiceStates"
|
||||
| "members"
|
||||
| "channels"
|
||||
| "memberCount"
|
||||
| "owner"
|
||||
| "emojis"
|
||||
> {
|
||||
/** Total number of members in this guild */
|
||||
memberCount?: number;
|
||||
/** The roles in the guild */
|
||||
roles: Collection<string, RoleStruct>;
|
||||
/** The presences of all the users in the guild. */
|
||||
presences: Collection<string, PresenceUpdate>;
|
||||
/** The Voice State data for each user in a voice channel in this server. */
|
||||
voiceStates: Collection<string, CleanVoiceState>;
|
||||
voiceStates: Collection<string, VoiceState>;
|
||||
/** Custom guild emojis */
|
||||
emojis: Collection<string, Emoji>;
|
||||
|
||||
// GETTERS
|
||||
/** Members in this guild. */
|
||||
@@ -204,9 +232,12 @@ export interface GuildStruct extends
|
||||
/** The bot member in this guild if cached */
|
||||
bot?: MemberStruct;
|
||||
/** The bot guild member in this guild if cached */
|
||||
botMember?: GuildMember;
|
||||
botMember?: Omit<GuildMember, "joinedAt" | "premiumSince"> & {
|
||||
joinedAt: number;
|
||||
premiumSince?: number;
|
||||
};
|
||||
/** The bots voice state if there is one in this guild */
|
||||
botVoice?: CleanVoiceState;
|
||||
botVoice?: VoiceState;
|
||||
/** The owner member of this guild */
|
||||
owner?: MemberStruct;
|
||||
/** Whether or not this guild is partnered */
|
||||
|
||||
@@ -6,13 +6,13 @@ import { kickMember } from "../helpers/members/kick_member.ts";
|
||||
import { sendDirectMessage } from "../helpers/members/send_direct_message.ts";
|
||||
import { addRole } from "../helpers/roles/add_role.ts";
|
||||
import { removeRole } from "../helpers/roles/remove_role.ts";
|
||||
import { CreateMessage } from "../types/channels/create_message.ts";
|
||||
import { CreateGuildBan } from "../types/guilds/create_guild_ban.ts";
|
||||
import {
|
||||
DiscordGuildMember,
|
||||
GuildMember,
|
||||
} from "../types/guilds/guild_member.ts";
|
||||
import { ModifyGuildMember } from "../types/guilds/modify_guild_member.ts";
|
||||
import { CreateMessage } from "../types/messages/create_message.ts";
|
||||
import { DiscordImageFormat } from "../types/misc/image_format.ts";
|
||||
import { DiscordImageSize } from "../types/misc/image_size.ts";
|
||||
import { DiscordUser, User } from "../types/users/user.ts";
|
||||
@@ -151,7 +151,14 @@ export interface MemberStruct extends GuildMember, User {
|
||||
/** Get the nickname or the username if no nickname */
|
||||
name(guildID: string): string;
|
||||
/** Get the guild member object for the specified guild */
|
||||
guildMember(guildID: string): GuildMember | undefined;
|
||||
guildMember(
|
||||
guildID: string,
|
||||
):
|
||||
| Omit<GuildMember, "joinedAt" | "premiumSince"> & {
|
||||
joinedAt: number;
|
||||
premiumSince?: number;
|
||||
}
|
||||
| undefined;
|
||||
/** Send a direct message to the user is possible */
|
||||
sendDM(
|
||||
content: string | CreateMessage,
|
||||
|
||||
@@ -9,10 +9,18 @@ import { removeAllReactions } from "../helpers/messages/remove_all_reactions.ts"
|
||||
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 { DiscordenoCreateMessage } from "../types/discordeno/create_message.ts";
|
||||
import { GuildMember } from "../types/guilds/guild_member.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";
|
||||
import { createNewProp } from "../util/utils.ts";
|
||||
import { createNewProp, snakeKeysToCamelCase } from "../util/utils.ts";
|
||||
import { ChannelStruct } from "./channel.ts";
|
||||
import { GuildStruct } from "./guild.ts";
|
||||
import { MemberStruct } from "./member.ts";
|
||||
import { RoleStruct } from "./role.ts";
|
||||
|
||||
const baseMessage: Partial<Message> = {
|
||||
const baseMessage: Partial<MessageStruct> = {
|
||||
get channel() {
|
||||
if (this.guildId) return cache.channels.get(this.channelId!);
|
||||
return cache.channels.get(this.author?.id!);
|
||||
@@ -34,13 +42,13 @@ const baseMessage: Partial<Message> = {
|
||||
"@me"}/${this.channelId}/${this.id}`;
|
||||
},
|
||||
get mentionedRoles() {
|
||||
return this.mentionRoleIds?.map((id) => this.guild?.roles.get(id)) || [];
|
||||
return this.mentionedRoleIds?.map((id) => this.guild?.roles.get(id)) || [];
|
||||
},
|
||||
get mentionedChannels() {
|
||||
return this.mentionChannelIds?.map((id) => cache.channels.get(id)) || [];
|
||||
return this.mentionedChannelIds?.map((id) => cache.channels.get(id)) || [];
|
||||
},
|
||||
get mentionedMembers() {
|
||||
return this.mentions?.map((id) => cache.members.get(id)) || [];
|
||||
return this.mentionedUserIds?.map((id) => cache.members.get(id)) || [];
|
||||
},
|
||||
|
||||
// METHODS
|
||||
@@ -69,9 +77,10 @@ const baseMessage: Partial<Message> = {
|
||||
}
|
||||
: {
|
||||
...content,
|
||||
mentions: { ...(content.mentions || {}), repliedUser: true },
|
||||
mentions: { ...(content.allowedMentions || {}), repliedUser: true },
|
||||
replyMessageId: this.id,
|
||||
failReplyIfNotExists: content.failReplyIfNotExists === true,
|
||||
failReplyIfNotExists:
|
||||
content.messageReference?.failIfNotExists === true,
|
||||
};
|
||||
|
||||
if (this.guildId) return sendMessage(this.channelId!, contentWithMention);
|
||||
@@ -108,58 +117,126 @@ const baseMessage: Partial<Message> = {
|
||||
},
|
||||
};
|
||||
|
||||
export async function createMessageStruct(data: MessageCreateOptions) {
|
||||
export async function createMessageStruct(data: DiscordMessage) {
|
||||
const {
|
||||
guild_id: guildId = "",
|
||||
channel_id: channelId,
|
||||
mentions_everyone: mentionsEveryone,
|
||||
mention_channels: mentionChannelIds = [],
|
||||
mention_roles: mentionRoleIds,
|
||||
webhook_id: webhookId,
|
||||
message_reference: messageReference,
|
||||
guildId = "",
|
||||
channelId,
|
||||
mentionChannels = [],
|
||||
mentions,
|
||||
mentionRoles,
|
||||
edited_timestamp: editedTimestamp,
|
||||
referenced_message: referencedMessageId,
|
||||
member,
|
||||
...rest
|
||||
} = data;
|
||||
} = snakeKeysToCamelCase(data) as Message;
|
||||
|
||||
const restProps: Record<string, ReturnType<typeof createNewProp>> = {};
|
||||
const props: Record<string, ReturnType<typeof createNewProp>> = {};
|
||||
for (const key of Object.keys(rest)) {
|
||||
// @ts-ignore index signature
|
||||
restProps[key] = createNewProp(rest[key]);
|
||||
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 message = Object.create(baseMessage, {
|
||||
...restProps,
|
||||
const message: MessageStruct = Object.create(baseMessage, {
|
||||
...props,
|
||||
/** The message id of the original message if this message was sent as a reply. If null, the original message was deleted. */
|
||||
referencedMessageId: createNewProp(referencedMessageId),
|
||||
channelId: createNewProp(channelId),
|
||||
guildId: createNewProp(guildId || guildIdFinal),
|
||||
mentions: createNewProp(data.mentions.map((m) => m.id)),
|
||||
mentionsEveryone: createNewProp(mentionsEveryone),
|
||||
mentionRoleIds: createNewProp(mentionRoleIds),
|
||||
mentionChannelIds: createNewProp(
|
||||
guildId: createNewProp(guildIdFinal),
|
||||
mentionedUserIds: createNewProp(mentions.map((m) => m.id)),
|
||||
mentionedRoleIds: createNewProp(mentionRoles),
|
||||
mentionedChannelIds: createNewProp(
|
||||
[
|
||||
// Keep any ids that discord sends
|
||||
...mentionChannelIds,
|
||||
...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)
|
||||
),
|
||||
].map((m) => m.id),
|
||||
],
|
||||
),
|
||||
webhookId: createNewProp(webhookId),
|
||||
messageReference: createNewProp(messageReference),
|
||||
timestamp: createNewProp(Date.parse(data.timestamp)),
|
||||
editedTimestamp: createNewProp(
|
||||
editedTimestamp ? Date.parse(editedTimestamp) : undefined,
|
||||
),
|
||||
});
|
||||
|
||||
return message as Message;
|
||||
return message;
|
||||
}
|
||||
|
||||
export interface MessageStruct extends Message {
|
||||
// For better user experience
|
||||
/** Ids of users specifically mentioned in the message */
|
||||
mentionedUserIds: string[];
|
||||
/** Ids of roles specifically mentioned in this message */
|
||||
mentionedRoleIds: string[];
|
||||
/** Channels specifically mentioned in this message */
|
||||
mentionedChannelIds?: string[];
|
||||
// GETTERS
|
||||
|
||||
/** The channel where this message was sent. Can be undefined if uncached. */
|
||||
channel?: ChannelStruct;
|
||||
/** The guild of this message. Can be undefined if not in cache or in DM */
|
||||
guild?: GuildStruct;
|
||||
/** The member for the user who sent the message. Can be undefined if not in cache or in dm. */
|
||||
member?: MemberStruct;
|
||||
/** The guild member details for this guild and member. Can be undefined if not in cache or in dm. */
|
||||
guildMember?: Omit<GuildMember, "joinedAt" | "premiumSince"> & {
|
||||
joinedAt: number;
|
||||
premiumSince?: number;
|
||||
};
|
||||
/** The url link to this message */
|
||||
link: string;
|
||||
/** The role objects for all the roles that were mentioned in this message */
|
||||
mentionedRoles: (RoleStruct | undefined)[];
|
||||
/** The channel objects for all the channels that were mentioned in this message. */
|
||||
mentionedChannels: (ChannelStruct | undefined)[];
|
||||
/** The member objects for all the members that were mentioned in this message. */
|
||||
mentionedMembers: (MemberStruct | undefined)[];
|
||||
|
||||
// METHODS
|
||||
|
||||
/** Delete the message */
|
||||
delete(
|
||||
reason?: string,
|
||||
delayMilliseconds?: number,
|
||||
): ReturnType<typeof deleteMessage>;
|
||||
/** Edit the message */
|
||||
edit(content: string | EditMessage): ReturnType<typeof editMessage>;
|
||||
/** Pins the message in the channel */
|
||||
pin(): ReturnType<typeof pinMessage>;
|
||||
/** Add a reaction to the message */
|
||||
addReaction(reaction: string): ReturnType<typeof addReaction>;
|
||||
/** Add multiple reactions to the message without or without order. */
|
||||
addReactions(
|
||||
reactions: string[],
|
||||
ordered?: boolean,
|
||||
): ReturnType<typeof addReactions>;
|
||||
/** Send a inline reply to this message */
|
||||
reply(
|
||||
content: string | DiscordenoCreateMessage,
|
||||
): ReturnType<typeof sendMessage>;
|
||||
/** Send a message to this channel where this message is */
|
||||
send(
|
||||
content: string | DiscordenoCreateMessage,
|
||||
): 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,
|
||||
timeout?: number,
|
||||
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,
|
||||
timeout?: number,
|
||||
reason?: string,
|
||||
): Promise<unknown>;
|
||||
/** Remove all reactions */
|
||||
removeAllReactions(): ReturnType<typeof removeAllReactions>;
|
||||
/** Remove all reactions */
|
||||
removeReactionEmoji(reaction: string): ReturnType<typeof removeReactionEmoji>;
|
||||
/** Remove all reactions */
|
||||
removeReaction(reaction: string): ReturnType<typeof removeReaction>;
|
||||
}
|
||||
|
||||
7
src/types/discordeno/create_slash_command.ts
Normal file
7
src/types/discordeno/create_slash_command.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { CreateGlobalApplicationCommand } from "../interactions/create_global_application_command.ts";
|
||||
|
||||
export interface DiscordenoCreateApplicationCommand
|
||||
extends CreateGlobalApplicationCommand {
|
||||
/** Id of the guild to create a guild only application command */
|
||||
guildId: string;
|
||||
}
|
||||
6
src/types/discordeno/edit_webhook_message.ts
Normal file
6
src/types/discordeno/edit_webhook_message.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { EditWebhookMessage } from "../webhooks/edit_webhook_message.ts";
|
||||
|
||||
export interface DiscordenoEditWebhookMessage extends EditWebhookMessage {
|
||||
/** Id of the message you want to edit */
|
||||
messageId: string;
|
||||
}
|
||||
0
src/types/discordeno/guild_member.ts
Normal file
0
src/types/discordeno/guild_member.ts
Normal file
7
src/types/discordeno/guild_update_change.ts
Normal file
7
src/types/discordeno/guild_update_change.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Guild } from "../guilds/guild.ts";
|
||||
|
||||
export interface GuildUpdateChange {
|
||||
key: keyof Guild;
|
||||
oldValue?: unknown;
|
||||
value?: unknown;
|
||||
}
|
||||
6
src/types/discordeno/interaction_response.ts
Normal file
6
src/types/discordeno/interaction_response.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { InteractionResponse } from "../interactions/interaction_response.ts";
|
||||
|
||||
export interface DiscordenoInteractionResponse extends InteractionResponse {
|
||||
/** Set to true if the response should be private */
|
||||
private?: boolean;
|
||||
}
|
||||
12
src/types/integration/integration_create_update.ts
Normal file
12
src/types/integration/integration_create_update.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
import { Integration } from "./integration.ts";
|
||||
|
||||
export interface IntegrationCreateUpdate extends Integration {
|
||||
/** Id of the guild */
|
||||
guildId: string;
|
||||
}
|
||||
|
||||
/** https://github.com/discord/discord-api-docs/blob/master/docs/topics/Gateway.md#integration-create-event-additional-fields */
|
||||
export type DiscordIntegrationCreateUpdate = SnakeCaseProps<
|
||||
IntegrationCreateUpdate
|
||||
>;
|
||||
13
src/types/integration/integration_delete.ts
Normal file
13
src/types/integration/integration_delete.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
|
||||
export interface IntegrationDelete {
|
||||
/** Integration id */
|
||||
id: string;
|
||||
/** Id of the guild */
|
||||
guildId: string;
|
||||
/** Id of the bot/OAuth2 application for this discord integration */
|
||||
applicationId?: string;
|
||||
}
|
||||
|
||||
/** https://github.com/discord/discord-api-docs/blob/master/docs/topics/Gateway.md#integration-delete-event-fields */
|
||||
export type DiscordIntegrationDelete = SnakeCaseProps<IntegrationDelete>;
|
||||
@@ -1,3 +1,4 @@
|
||||
import { GuildMember } from "../guilds/guild_member.ts";
|
||||
import { User } from "../users/user.ts";
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
import { InteractionApplicationCommandCallbackData } from "./application_command_callback_data.ts";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
import { Invite } from "./invite.ts";
|
||||
|
||||
export interface InviteMetadata {
|
||||
export interface InviteMetadata extends Invite {
|
||||
/** Number of times this invite has been used */
|
||||
uses: number;
|
||||
/** Max number of times this invite can be used */
|
||||
|
||||
@@ -5,11 +5,11 @@ export interface AllowedMentions {
|
||||
/** An array of allowed mention types to parse from the content. */
|
||||
parse: DiscordAllowedMentionsTypes[];
|
||||
/** Array of role_ids to mention (Max size of 100) */
|
||||
roles: string[];
|
||||
roles?: string[];
|
||||
/** Array of user_ids to mention (Max size of 100) */
|
||||
users: string[];
|
||||
users?: string[];
|
||||
/** For replies, whether to mention the author of the message being replied to (default false) */
|
||||
repliedUser: boolean;
|
||||
repliedUser?: boolean;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#allowed-mentions-object */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Embed } from "../embeds/embed.ts";
|
||||
import { AllowedMentions } from "../messages/allowed_mentions.ts";
|
||||
import { MessageReference } from "../messages/message_reference.ts";
|
||||
import { FileContent } from "../misc/file_content.ts";
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
|
||||
export interface CreateMessage {
|
||||
@@ -16,7 +17,9 @@ export interface CreateMessage {
|
||||
allowedMentions?: AllowedMentions;
|
||||
/** Include to make your message a reply */
|
||||
messageReference?: MessageReference;
|
||||
/** The contents of the file being sent */
|
||||
file?: FileContent | FileContent[];
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#create-message */
|
||||
export type DiscordCreateMessage = SnakeCaseProps<CreateMessage>;
|
||||
export type DiscordCreateMessage = SnakeCaseProps<Omit<CreateMessage, "file">>;
|
||||
|
||||
17
src/types/messages/edit_message.ts
Normal file
17
src/types/messages/edit_message.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Embed } from "../embeds/embed.ts";
|
||||
import { SnakeCaseProps } from "../util.ts";
|
||||
import { AllowedMentions } from "./allowed_mentions.ts";
|
||||
|
||||
export interface EditMessage {
|
||||
/** The new message contents (up to 2000 characters) */
|
||||
content?: string | null;
|
||||
/** Embedded `rich` content */
|
||||
embed?: Embed | null;
|
||||
/** Edit the flags of the message (only `SUPRESS_EMBEDS` can currently be set/unset) */
|
||||
flags?: 4 | null;
|
||||
/** Allowed mentions for the message */
|
||||
allowedMentions?: AllowedMentions | null;
|
||||
}
|
||||
|
||||
/** https://discord.com/developers/docs/resources/channel#edit-message-json-params */
|
||||
export type DiscordEditMessage = SnakeCaseProps<EditMessage>;
|
||||
@@ -5,15 +5,18 @@ 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>;
|
||||
async function getCached(
|
||||
table: "guilds",
|
||||
key: string | Guild,
|
||||
): Promise<Guild | undefined>;
|
||||
async function getCached(
|
||||
table: "channels",
|
||||
key: string | Channel,
|
||||
): Promise<Channel>;
|
||||
): Promise<Channel | undefined>;
|
||||
async function getCached(
|
||||
table: "members",
|
||||
key: string | Member,
|
||||
): Promise<Member>;
|
||||
): Promise<Member | undefined>;
|
||||
async function getCached(
|
||||
table: "guilds" | "channels" | "members",
|
||||
key: string | Guild | Channel | Member,
|
||||
@@ -28,7 +31,7 @@ async function getCached(
|
||||
);
|
||||
}
|
||||
|
||||
return cached;
|
||||
return typeof cached === "string" ? undefined : cached;
|
||||
}
|
||||
|
||||
/** Calculates the permissions this member has in the given guild */
|
||||
@@ -39,6 +42,8 @@ export async function calculateBasePermissions(
|
||||
guild = await getCached("guilds", guild);
|
||||
member = await getCached("members", member);
|
||||
|
||||
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]
|
||||
@@ -64,10 +69,12 @@ export async function calculateChannelOverwrites(
|
||||
channel = await getCached("channels", channel);
|
||||
|
||||
// This is a DM channel so return ADMINISTRATOR permission
|
||||
if (!channel.guildId) return "8";
|
||||
if (!channel?.guildId) return "8";
|
||||
|
||||
member = await getCached("members", member);
|
||||
|
||||
if (!member) return "8";
|
||||
|
||||
// Get all the role permissions this member already has
|
||||
let permissions = BigInt(
|
||||
await calculateBasePermissions(channel.guildId, member),
|
||||
@@ -285,8 +292,10 @@ export async function highestRole(
|
||||
) {
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
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", member))?.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;
|
||||
@@ -321,6 +330,8 @@ export async function higherRolePosition(
|
||||
) {
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
if (!guild) return true;
|
||||
|
||||
const role = guild.roles.get(roleId);
|
||||
const otherRole = guild.roles.get(otherRoleId);
|
||||
if (!role || !otherRole) throw new Error(Errors.ROLE_NOT_FOUND);
|
||||
@@ -341,7 +352,7 @@ export async function isHigherPosition(
|
||||
) {
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
if (guild.ownerId === memberId) return true;
|
||||
if (!guild || guild.ownerId === memberId) return true;
|
||||
|
||||
const memberHighestRole = await highestRole(guild, memberId);
|
||||
return higherRolePosition(guild.id, memberHighestRole.id, compareRoleId);
|
||||
|
||||
@@ -72,7 +72,9 @@ function isObject(obj: unknown) {
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export function camelKeysToSnakeCase(obj: Record<string, any>) {
|
||||
export function camelKeysToSnakeCase<T>(
|
||||
obj: Record<string, any> | Record<string, any>[],
|
||||
): T {
|
||||
if (isObject(obj)) {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const convertedObject: Record<string, any> = {};
|
||||
@@ -80,20 +82,22 @@ export function camelKeysToSnakeCase(obj: Record<string, any>) {
|
||||
Object.keys(obj)
|
||||
.forEach((key) => {
|
||||
convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase(
|
||||
obj[key],
|
||||
(obj as Record<string, any>)[key],
|
||||
);
|
||||
});
|
||||
|
||||
return convertedObject;
|
||||
return convertedObject as T;
|
||||
} else if (Array.isArray(obj)) {
|
||||
obj = obj.map((element) => camelKeysToSnakeCase(element));
|
||||
}
|
||||
|
||||
return obj;
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export function snakeKeysToCamelCase(obj: Record<string, any>) {
|
||||
export function snakeKeysToCamelCase<T>(
|
||||
obj: Record<string, any> | Record<string, any>[],
|
||||
): T {
|
||||
if (isObject(obj)) {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const convertedObject: Record<string, any> = {};
|
||||
@@ -101,16 +105,16 @@ export function snakeKeysToCamelCase(obj: Record<string, any>) {
|
||||
Object.keys(obj)
|
||||
.forEach((key) => {
|
||||
convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase(
|
||||
obj[key],
|
||||
(obj as Record<string, any>)[key],
|
||||
);
|
||||
});
|
||||
|
||||
return convertedObject;
|
||||
return convertedObject as T;
|
||||
} else if (Array.isArray(obj)) {
|
||||
obj = obj.map((element) => snakeKeysToCamelCase(element));
|
||||
}
|
||||
|
||||
return obj;
|
||||
return obj as T;
|
||||
}
|
||||
|
||||
/** @private */
|
||||
|
||||
Reference in New Issue
Block a user