mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-16 19:28:17 +00:00
Merge branch 'next' of https://github.com/discordeno/discordeno into getters
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,3 +1,6 @@
|
||||
# Allows quick testing of changes and keeps stuff like tokens private
|
||||
debug.ts
|
||||
|
||||
.DS_Store
|
||||
.lock
|
||||
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
export { delay } from "https://deno.land/std@0.81.0/async/delay.ts";
|
||||
export { encode } from "https://deno.land/std@0.81.0/encoding/base64.ts";
|
||||
export {
|
||||
assert,
|
||||
assertArrayIncludes,
|
||||
assertEquals,
|
||||
} from "https://deno.land/std@0.81.0/testing/asserts.ts";
|
||||
export { decompress_with as inflate } from "https://unpkg.com/@evan/wasm@0.0.22/target/zlib/deno.js";
|
||||
|
||||
@@ -1,27 +1,27 @@
|
||||
export * from "./src/controllers/bans.ts";
|
||||
export * from "./src/controllers/cache.ts";
|
||||
export * from "./src/controllers/channels.ts";
|
||||
export * from "./src/controllers/guilds.ts";
|
||||
export * from "./src/controllers/members.ts";
|
||||
export * from "./src/controllers/messages.ts";
|
||||
export * from "./src/controllers/misc.ts";
|
||||
export * from "./src/controllers/mod.ts";
|
||||
export * from "./src/controllers/reactions.ts";
|
||||
export * from "./src/controllers/roles.ts";
|
||||
export * from "./src/handlers/channel.ts";
|
||||
export * from "./src/handlers/guild.ts";
|
||||
export * from "./src/handlers/member.ts";
|
||||
export * from "./src/handlers/message.ts";
|
||||
export * from "./src/handlers/webhook.ts";
|
||||
export * from "./src/module/client.ts";
|
||||
export * from "./src/module/requestManager.ts";
|
||||
export * from "./src/module/shardingManager.ts";
|
||||
export * from "./src/structures/channel.ts";
|
||||
export * from "./src/structures/guild.ts";
|
||||
export * from "./src/structures/member.ts";
|
||||
export * from "./src/structures/message.ts";
|
||||
export * from "./src/structures/mod.ts";
|
||||
export * from "./src/structures/role.ts";
|
||||
export * from "./src/api/controllers/bans.ts";
|
||||
export * from "./src/api/controllers/cache.ts";
|
||||
export * from "./src/api/controllers/channels.ts";
|
||||
export * from "./src/api/controllers/guilds.ts";
|
||||
export * from "./src/api/controllers/members.ts";
|
||||
export * from "./src/api/controllers/messages.ts";
|
||||
export * from "./src/api/controllers/misc.ts";
|
||||
export * from "./src/api/controllers/mod.ts";
|
||||
export * from "./src/api/controllers/reactions.ts";
|
||||
export * from "./src/api/controllers/roles.ts";
|
||||
export * from "./src/api/handlers/channel.ts";
|
||||
export * from "./src/api/handlers/guild.ts";
|
||||
export * from "./src/api/handlers/member.ts";
|
||||
export * from "./src/api/handlers/message.ts";
|
||||
export * from "./src/api/handlers/webhook.ts";
|
||||
export * from "./src/bot.ts";
|
||||
export * from "./src/rest/mod.ts";
|
||||
export * from "./src/ws/mod.ts";
|
||||
export * from "./src/api/structures/channel.ts";
|
||||
export * from "./src/api/structures/guild.ts";
|
||||
export * from "./src/api/structures/member.ts";
|
||||
export * from "./src/api/structures/message.ts";
|
||||
export * from "./src/api/structures/mod.ts";
|
||||
export * from "./src/api/structures/role.ts";
|
||||
export * from "./src/types/activity.ts";
|
||||
export * from "./src/types/cdn.ts";
|
||||
export * from "./src/types/channel.ts";
|
||||
@@ -35,8 +35,8 @@ export * from "./src/types/options.ts";
|
||||
export * from "./src/types/permission.ts";
|
||||
export * from "./src/types/presence.ts";
|
||||
export * from "./src/types/role.ts";
|
||||
export * from "./src/utils/cache.ts";
|
||||
export * from "./src/utils/cdn.ts";
|
||||
export * from "./src/utils/collection.ts";
|
||||
export * from "./src/utils/permissions.ts";
|
||||
export * from "./src/utils/utils.ts";
|
||||
export * from "./src/util/cache.ts";
|
||||
export * from "./src/util/cdn.ts";
|
||||
export * from "./src/util/collection.ts";
|
||||
export * from "./src/util/permissions.ts";
|
||||
export * from "./src/util/utils.ts";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { DiscordPayload, GuildBanPayload } from "../types/types.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { DiscordPayload, GuildBanPayload } from "../../types/types.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildBanAdd(data: DiscordPayload) {
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Channel, Guild, Member, Message } from "../structures/structures.ts";
|
||||
import { PresenceUpdatePayload } from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { PresenceUpdatePayload } from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
|
||||
export type TableName =
|
||||
| "guilds"
|
||||
@@ -1,10 +1,10 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
ChannelCreatePayload,
|
||||
ChannelTypes,
|
||||
DiscordPayload,
|
||||
} from "../types/types.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalChannelCreate(data: DiscordPayload) {
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
CreateGuildPayload,
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
GuildEmojisUpdatePayload,
|
||||
GuildUpdateChange,
|
||||
UpdateGuildPayload,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildCreate(
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
GuildMemberAddPayload,
|
||||
GuildMemberChunkPayload,
|
||||
GuildMemberUpdatePayload,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildMemberAdd(data: DiscordPayload) {
|
||||
@@ -1,11 +1,11 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
MessageCreateOptions,
|
||||
MessageDeleteBulkPayload,
|
||||
MessageDeletePayload,
|
||||
} from "../types/types.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalMessageCreate(data: DiscordPayload) {
|
||||
@@ -1,8 +1,9 @@
|
||||
import { delay } from "../../deps.ts";
|
||||
import { initialMemberLoadQueue } from "../../mod.ts";
|
||||
import { eventHandlers, setBotID } from "../module/client.ts";
|
||||
import { allowNextShard } from "../module/shardingManager.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
initialMemberLoadQueue,
|
||||
structures,
|
||||
} from "../structures/structures.ts";
|
||||
import { eventHandlers, setBotID } from "../../bot.ts";
|
||||
import { allowNextShard } from "../../ws/shard_manager.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
PresenceUpdatePayload,
|
||||
@@ -11,8 +12,9 @@ import {
|
||||
UserPayload,
|
||||
VoiceStateUpdatePayload,
|
||||
WebhookUpdatePayload,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { delay } from "../../util/utils.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalReady(
|
||||
@@ -44,6 +44,10 @@ import {
|
||||
handleInternalGuildRoleDelete,
|
||||
handleInternalGuildRoleUpdate,
|
||||
} from "./roles.ts";
|
||||
import {
|
||||
handleInternalInteractionsCommandCreate,
|
||||
handleInternalInteractionsCreate,
|
||||
} from "./interactions.ts";
|
||||
|
||||
export let controllers = {
|
||||
READY: handleInternalReady,
|
||||
@@ -63,6 +67,8 @@ export let controllers = {
|
||||
GUILD_ROLE_CREATE: handleInternalGuildRoleCreate,
|
||||
GUILD_ROLE_DELETE: handleInternalGuildRoleDelete,
|
||||
GUILD_ROLE_UPDATE: handleInternalGuildRoleUpdate,
|
||||
INTERACTION_CREATE: handleInternalInteractionsCreate,
|
||||
APPLICATION_COMMAND_CREATE: handleInternalInteractionsCommandCreate,
|
||||
MESSAGE_CREATE: handleInternalMessageCreate,
|
||||
MESSAGE_DELETE: handleInternalMessageDelete,
|
||||
MESSAGE_DELETE_BULK: handleInternalMessageDeleteBulk,
|
||||
@@ -1,11 +1,11 @@
|
||||
import { botID, eventHandlers } from "../module/client.ts";
|
||||
import { botID, eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
BaseMessageReactionPayload,
|
||||
DiscordPayload,
|
||||
MessageReactionPayload,
|
||||
MessageReactionRemoveEmojiPayload,
|
||||
} from "../types/types.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalMessageReactionAdd(data: DiscordPayload) {
|
||||
@@ -1,10 +1,10 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
GuildRoleDeletePayload,
|
||||
GuildRolePayload,
|
||||
} from "../types/types.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildRoleCreate(data: DiscordPayload) {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { RequestManager } from "../../rest/mod.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
ChannelEditOptions,
|
||||
@@ -17,12 +17,12 @@ import {
|
||||
Permissions,
|
||||
RawOverwrite,
|
||||
WebhookPayload,
|
||||
} from "../types/types.ts";
|
||||
import { endpoints } from "../utils/constants.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import {
|
||||
botHasChannelPermissions,
|
||||
calculateBits,
|
||||
} from "../utils/permissions.ts";
|
||||
} from "../../util/permissions.ts";
|
||||
|
||||
/** Checks if a channel overwrite for a user id or a role id has permission in this channel */
|
||||
export function channelOverwriteHasPermission(
|
||||
@@ -1,7 +1,7 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { identifyPayload } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { requestAllMembers } from "../module/shardingManager.ts";
|
||||
import { identifyPayload } from "../../bot.ts";
|
||||
import { RequestManager } from "../../rest/mod.ts";
|
||||
import { requestAllMembers } from "../../ws/shard_manager.ts";
|
||||
import {
|
||||
Guild,
|
||||
Member,
|
||||
@@ -39,12 +39,12 @@ import {
|
||||
RoleData,
|
||||
UpdateGuildPayload,
|
||||
UserPayload,
|
||||
} from "../types/types.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { endpoints } from "../utils/constants.ts";
|
||||
import { botHasPermission, calculateBits } from "../utils/permissions.ts";
|
||||
import { urlToBase64 } from "../utils/utils.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { formatImageURL } from "../../util/cdn.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { botHasPermission, calculateBits } from "../../util/permissions.ts";
|
||||
import { urlToBase64 } from "../../util/utils.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 createServer(options: CreateServerOptions) {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { botID } from "../../bot.ts";
|
||||
import { RequestManager } from "../../rest/mod.ts";
|
||||
import { Member, structures } from "../structures/structures.ts";
|
||||
import {
|
||||
DMChannelCreatePayload,
|
||||
@@ -9,15 +9,15 @@ import {
|
||||
ImageFormats,
|
||||
ImageSize,
|
||||
MessageContent,
|
||||
} from "../types/types.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import { endpoints } from "../utils/constants.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { formatImageURL } from "../../util/cdn.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import {
|
||||
botHasPermission,
|
||||
higherRolePosition,
|
||||
highestRole,
|
||||
} from "../utils/permissions.ts";
|
||||
import { urlToBase64 } from "../utils/utils.ts";
|
||||
} from "../../util/permissions.ts";
|
||||
import { urlToBase64 } from "../../util/utils.ts";
|
||||
import { sendMessage } from "./channel.ts";
|
||||
|
||||
/** The users custom avatar or the default avatar if you don't have a member object. */
|
||||
@@ -1,16 +1,16 @@
|
||||
import { delay } from "../../deps.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { botID } from "../../bot.ts";
|
||||
import { RequestManager } from "../../rest/mod.ts";
|
||||
import { Message, structures } from "../structures/structures.ts";
|
||||
import {
|
||||
Errors,
|
||||
MessageContent,
|
||||
MessageCreateOptions,
|
||||
UserPayload,
|
||||
} from "../types/types.ts";
|
||||
import { endpoints } from "../utils/constants.ts";
|
||||
import { botHasChannelPermissions } from "../utils/permissions.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { botHasChannelPermissions } from "../../util/permissions.ts";
|
||||
import { delay } from "../../util/utils.ts";
|
||||
|
||||
/** Delete a message with the channel id and message id only. */
|
||||
export async function deleteMessageByID(
|
||||
@@ -0,0 +1,316 @@
|
||||
import { RequestManager } from "../../rest/mod.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
CreateSlashCommandOptions,
|
||||
EditSlashCommandOptions,
|
||||
EditSlashResponseOptions,
|
||||
EditWebhookMessageOptions,
|
||||
Errors,
|
||||
ExecuteSlashCommandOptions,
|
||||
ExecuteWebhookOptions,
|
||||
MessageCreateOptions,
|
||||
UpsertSlashCommandOptions,
|
||||
WebhookCreateOptions,
|
||||
WebhookPayload,
|
||||
} from "../../types/types.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
import { botHasChannelPermissions } from "../../util/permissions.ts";
|
||||
import { urlToBase64 } from "../../util/utils.ts";
|
||||
import { botID } from "../../bot.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
|
||||
/** Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
|
||||
*
|
||||
* Webhook names cannot be: 'clyde'
|
||||
*/
|
||||
export async function createWebhook(
|
||||
channelID: string,
|
||||
options: WebhookCreateOptions,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
if (
|
||||
// Specific usernames that discord does not allow
|
||||
options.name === "clyde" ||
|
||||
// Character limit checks. [...] checks are because of js unicode length handling
|
||||
[...options.name].length < 2 || [...options.name].length > 32
|
||||
) {
|
||||
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
endpoints.CHANNEL_WEBHOOKS(channelID),
|
||||
{
|
||||
...options,
|
||||
avatar: options.avatar ? await urlToBase64(options.avatar) : undefined,
|
||||
},
|
||||
) as Promise<WebhookPayload>;
|
||||
}
|
||||
|
||||
export async function executeWebhook(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
options: ExecuteWebhookOptions,
|
||||
) {
|
||||
if (!options.content && !options.file && !options.embeds) {
|
||||
throw new Error(Errors.INVALID_WEBHOOK_OPTIONS);
|
||||
}
|
||||
|
||||
if (options.content && options.content.length > 2000) {
|
||||
throw Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (options.embeds && options.embeds.length > 10) {
|
||||
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.mentions.users.length > 100) {
|
||||
options.mentions.users = options.mentions.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.mentions.roles.length > 100) {
|
||||
options.mentions.roles = options.mentions.roles.slice(0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await RequestManager.post(
|
||||
`${endpoints.WEBHOOK(webhookID, webhookToken)}${
|
||||
options.wait ? "?wait=true" : ""
|
||||
}`,
|
||||
{
|
||||
...options,
|
||||
allowed_mentions: options.mentions,
|
||||
avatar_url: options.avatar_url,
|
||||
},
|
||||
);
|
||||
if (!options.wait) return;
|
||||
|
||||
return structures.createMessage(result as MessageCreateOptions);
|
||||
}
|
||||
|
||||
export function getWebhook(webhookID: string) {
|
||||
return RequestManager.get(endpoints.WEBHOOK_ID(webhookID));
|
||||
}
|
||||
|
||||
export function editWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
options: EditWebhookMessageOptions,
|
||||
) {
|
||||
if (options.content && options.content.length > 2000) {
|
||||
throw Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (options.embeds && options.embeds.length > 10) {
|
||||
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.allowed_mentions.users.length > 100) {
|
||||
options.allowed_mentions.users = options.allowed_mentions.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((
|
||||
p,
|
||||
) => p !== "roles");
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.roles.length > 100) {
|
||||
options.allowed_mentions.roles = options.allowed_mentions.roles.slice(
|
||||
0,
|
||||
100,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RequestManager.patch(
|
||||
endpoints.WEBHOOK_EDIT(webhookID, webhookToken, messageID),
|
||||
{ ...options, allowed_mentions: options.allowed_mentions },
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
) {
|
||||
return RequestManager.delete(
|
||||
endpoints.WEBHOOK_DELETE(webhookID, webhookToken, messageID),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* - Your app **cannot** have two global commands with the same name
|
||||
* - Your app **cannot** have two guild commands within the same name **on the same guild**
|
||||
* - Your app **can** have a global and guild command with the same name
|
||||
* - Multiple apps **can** have commands with the same names
|
||||
*
|
||||
* 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 function createSlashCommand(options: CreateSlashCommandOptions) {
|
||||
// Use ... for content length due to unicode characters and js .length handling
|
||||
if ([...options.name].length < 2 || [...options.name].length > 32) {
|
||||
throw new Error(Errors.INVALID_SLASH_NAME);
|
||||
}
|
||||
|
||||
if (
|
||||
[...options.description].length < 1 || [...options.description].length > 100
|
||||
) {
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
options.guildID
|
||||
? endpoints.COMMANDS_GUILD(botID, options.guildID)
|
||||
: endpoints.COMMANDS(botID),
|
||||
{
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Fetch all of the global commands for your application. */
|
||||
export function getSlashCommands(guildID?: string) {
|
||||
// TODO: Should this be a returned as a collection?
|
||||
return RequestManager.get(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD(botID, guildID)
|
||||
: endpoints.COMMANDS(botID),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit an existing slash command. If this command did not exist, it will create it.
|
||||
*/
|
||||
export function upsertSlashCommand(options: UpsertSlashCommandOptions) {
|
||||
return RequestManager.post(
|
||||
options.guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(botID, options.id, options.guildID)
|
||||
: endpoints.COMMANDS_ID(botID, options.id),
|
||||
{
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Edit an existing slash command. */
|
||||
export function editSlashCommand(options: EditSlashCommandOptions) {
|
||||
return RequestManager.patch(
|
||||
options.guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(botID, options.id, options.guildID)
|
||||
: endpoints.COMMANDS_ID(botID, options.id),
|
||||
{
|
||||
...options,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/** Deletes a slash command. */
|
||||
export function deleteSlashCommand(id: string, guildID?: string) {
|
||||
if (!guildID) return RequestManager.delete(endpoints.COMMANDS_ID(botID, id));
|
||||
return RequestManager.delete(endpoints.COMMANDS_GUILD_ID(botID, id, guildID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a response to a users slash command. The command data will have the id and token necessary to respond.
|
||||
* Interaction `tokens` are valid for **15 minutes** and can be used to send followup messages.
|
||||
*
|
||||
* NOTE: By default we will suppress mentions. To enable mentions, just pass any mentions object.
|
||||
*/
|
||||
export function executeSlashCommand(
|
||||
id: string,
|
||||
token: string,
|
||||
options: ExecuteSlashCommandOptions,
|
||||
) {
|
||||
// If its already been executed, we need to send a followup response
|
||||
if (cache.executedSlashCommands.has(token)) {
|
||||
return RequestManager.post(endpoints.WEBHOOK(botID, token), {
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
// Expire in 15 minutes
|
||||
cache.executedSlashCommands.set(token, id);
|
||||
setTimeout(
|
||||
() => cache.executedSlashCommands.delete(token),
|
||||
Date.now() + 900000,
|
||||
);
|
||||
|
||||
// IF NO MENTIONS ARE PROVIDED, FORCE DISABLE MENTIONS
|
||||
if (!(options.data.allowed_mentions)) {
|
||||
options.data.allowed_mentions = { parse: [] };
|
||||
}
|
||||
|
||||
return RequestManager.post(endpoints.INTERACTION_ID_TOKEN(id, token), {
|
||||
...options,
|
||||
});
|
||||
}
|
||||
|
||||
/** To delete your response to a slash command. If a message id is not provided, it will default to deleting the original response. */
|
||||
export function deleteSlashResponse(
|
||||
token: string,
|
||||
messageID?: string,
|
||||
) {
|
||||
if (!messageID) {
|
||||
return RequestManager.delete(
|
||||
endpoints.INTERACTION_ORIGINAL_ID_TOKEN(botID, token),
|
||||
);
|
||||
}
|
||||
return RequestManager.delete(
|
||||
endpoints.INTERACTION_ID_TOKEN_MESSAGEID(botID, token, messageID),
|
||||
);
|
||||
}
|
||||
|
||||
/** To edit your response to a slash command. If a messageID is not provided it will default to editing the original response. */
|
||||
export function editSlashResponse(
|
||||
token: string,
|
||||
options: EditSlashResponseOptions,
|
||||
) {
|
||||
return RequestManager.patch(
|
||||
endpoints.INTERACTION_ORIGINAL_ID_TOKEN(botID, token),
|
||||
options,
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
import { Collection, createNewProp, Guild } from "../../mod.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import {
|
||||
ChannelCreatePayload,
|
||||
ChannelType,
|
||||
RawOverwrite,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { createNewProp } from "../../util/utils.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { Guild } from "./guild.ts";
|
||||
import { Message } from "./message.ts";
|
||||
|
||||
const baseChannel: any = {
|
||||
@@ -1,9 +1,11 @@
|
||||
import { CreateGuildPayload, MemberCreatePayload } from "../types/types.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { CreateGuildPayload, MemberCreatePayload } from "../../types/types.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { structures } from "./mod.ts";
|
||||
|
||||
export const initialMemberLoadQueue = new Map<string, MemberCreatePayload[]>();
|
||||
|
||||
const baseGuild: any = {};
|
||||
|
||||
export async function createGuild(data: CreateGuildPayload, shardID: number) {
|
||||
const {
|
||||
owner_id: ownerID,
|
||||
@@ -1,3 +1,13 @@
|
||||
import {
|
||||
BanOptions,
|
||||
EditMemberOptions,
|
||||
GuildMember,
|
||||
MemberCreatePayload,
|
||||
MessageContent,
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { createNewProp } from "../../util/utils.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { ban } from "../handlers/guild.ts";
|
||||
import {
|
||||
@@ -7,16 +17,6 @@ import {
|
||||
removeRole,
|
||||
sendDirectMessage,
|
||||
} from "../handlers/member.ts";
|
||||
import {
|
||||
BanOptions,
|
||||
EditMemberOptions,
|
||||
GuildMember,
|
||||
MemberCreatePayload,
|
||||
MessageContent,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { createNewProp } from "../utils/utils.ts";
|
||||
import { Guild } from "./guild.ts";
|
||||
|
||||
const baseMember: Partial<Member> = {
|
||||
@@ -1,15 +1,3 @@
|
||||
import { Channel } from "../../mod.ts";
|
||||
import { sendMessage } from "../handlers/channel.ts";
|
||||
import {
|
||||
addReaction,
|
||||
addReactions,
|
||||
deleteMessageByID,
|
||||
editMessage,
|
||||
pin,
|
||||
removeAllReactions,
|
||||
removeReaction,
|
||||
removeReactionEmoji,
|
||||
} from "../handlers/message.ts";
|
||||
import {
|
||||
Activity,
|
||||
Application,
|
||||
@@ -22,9 +10,21 @@ import {
|
||||
Reaction,
|
||||
Reference,
|
||||
UserPayload,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { createNewProp } from "../utils/utils.ts";
|
||||
} from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { createNewProp } from "../../util/utils.ts";
|
||||
import { sendMessage } from "../handlers/channel.ts";
|
||||
import {
|
||||
addReaction,
|
||||
addReactions,
|
||||
deleteMessageByID,
|
||||
editMessage,
|
||||
pin,
|
||||
removeAllReactions,
|
||||
removeReaction,
|
||||
removeReactionEmoji,
|
||||
} from "../handlers/message.ts";
|
||||
import { Channel } from "./channel.ts";
|
||||
import { Guild } from "./guild.ts";
|
||||
import { Member } from "./member.ts";
|
||||
import { Role } from "./role.ts";
|
||||
@@ -1,12 +1,12 @@
|
||||
import { CreateRoleOptions, RoleData } from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { Collection } from "../../util/collection.ts";
|
||||
import { createNewProp } from "../../util/utils.ts";
|
||||
import { deleteRole, editRole } from "../handlers/guild.ts";
|
||||
import { CreateRoleOptions, RoleData } from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { createNewProp } from "../utils/utils.ts";
|
||||
import { Guild } from "./guild.ts";
|
||||
import { Member } from "./member.ts";
|
||||
|
||||
const baseRole: Partial<Role> = {
|
||||
const baseRole: any = {
|
||||
get guild() {
|
||||
return cache.guilds.find((g) => g.roles.has(this.id));
|
||||
},
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GuildTemplate, UserPayload } from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { createNewProp } from "../utils/utils.ts";
|
||||
import { GuildTemplate, UserPayload } from "../../types/types.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { createNewProp } from "../../util/utils.ts";
|
||||
import { Guild } from "./guild.ts";
|
||||
|
||||
const baseTemplate: any = {
|
||||
@@ -2,10 +2,10 @@ import {
|
||||
ClientOptions,
|
||||
DiscordBotGatewayData,
|
||||
EventHandlers,
|
||||
} from "../types/types.ts";
|
||||
import { baseEndpoints, endpoints } from "../utils/constants.ts";
|
||||
import { RequestManager } from "./requestManager.ts";
|
||||
import { spawnShards } from "./shardingManager.ts";
|
||||
} from "./types/types.ts";
|
||||
import { baseEndpoints, endpoints } from "./util/constants.ts";
|
||||
import { RequestManager } from "./rest/mod.ts";
|
||||
import { spawnShards } from "./ws/shard_manager.ts";
|
||||
|
||||
export let authorization = "";
|
||||
export let botID = "";
|
||||
@@ -0,0 +1,26 @@
|
||||
import { DiscordPayload } from "../types/types.ts";
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import { InteractionCommandPayload } from "../types/types.ts";
|
||||
|
||||
export async function handleInternalInteractionsCreate(data: DiscordPayload) {
|
||||
if (data.t !== "INTERACTION_CREATE") return;
|
||||
|
||||
const payload = data.d as InteractionCommandPayload;
|
||||
|
||||
eventHandlers.interactionCreate?.(
|
||||
{
|
||||
...payload,
|
||||
member: await structures.createMember(payload.member, payload.guild_id),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function handleInternalInteractionsCommandCreate(
|
||||
data: DiscordPayload,
|
||||
) {
|
||||
if (data.t !== "APPLICATION_COMMAND_CREATE") return;
|
||||
|
||||
console.log(data);
|
||||
eventHandlers.interactionCreate?.(data);
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { structures } from "../structures/structures.ts";
|
||||
import {
|
||||
EditWebhookMessageOptions,
|
||||
Errors,
|
||||
ExecuteWebhookOptions,
|
||||
MessageCreateOptions,
|
||||
WebhookCreateOptions,
|
||||
WebhookPayload,
|
||||
} from "../types/types.ts";
|
||||
import { endpoints } from "../utils/constants.ts";
|
||||
import { botHasChannelPermissions } from "../utils/permissions.ts";
|
||||
import { urlToBase64 } from "../utils/utils.ts";
|
||||
|
||||
/** Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
|
||||
*
|
||||
* Webhook names cannot be: 'clyde'
|
||||
*/
|
||||
export async function createWebhook(
|
||||
channelID: string,
|
||||
options: WebhookCreateOptions,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
if (
|
||||
// Specific usernames that discord does not allow
|
||||
options.name === "clyde" ||
|
||||
// Character limit checks. [...] checks are because of js unicode length handling
|
||||
[...options.name].length < 2 || [...options.name].length > 32
|
||||
) {
|
||||
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
endpoints.CHANNEL_WEBHOOKS(channelID),
|
||||
{
|
||||
...options,
|
||||
avatar: options.avatar ? await urlToBase64(options.avatar) : undefined,
|
||||
},
|
||||
) as Promise<WebhookPayload>;
|
||||
}
|
||||
|
||||
export async function executeWebhook(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
options: ExecuteWebhookOptions,
|
||||
) {
|
||||
if (!options.content && !options.file && !options.embeds) {
|
||||
throw new Error(Errors.INVALID_WEBHOOK_OPTIONS);
|
||||
}
|
||||
|
||||
if (options.content && options.content.length > 2000) {
|
||||
throw Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (options.embeds && options.embeds.length > 10) {
|
||||
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.mentions.users.length > 100) {
|
||||
options.mentions.users = options.mentions.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.mentions.roles.length > 100) {
|
||||
options.mentions.roles = options.mentions.roles.slice(0, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await RequestManager.post(
|
||||
`${endpoints.WEBHOOK(webhookID, webhookToken)}${
|
||||
options.wait ? "?wait=true" : ""
|
||||
}`,
|
||||
{
|
||||
...options,
|
||||
allowed_mentions: options.mentions,
|
||||
avatar_url: options.avatar_url,
|
||||
},
|
||||
);
|
||||
if (!options.wait) return;
|
||||
|
||||
return structures.createMessage(result as MessageCreateOptions);
|
||||
}
|
||||
|
||||
export function getWebhook(webhookID: string) {
|
||||
return RequestManager.get(endpoints.WEBHOOK_ID(webhookID));
|
||||
}
|
||||
|
||||
export function editWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
options: EditWebhookMessageOptions,
|
||||
) {
|
||||
if (options.content && options.content.length > 2000) {
|
||||
throw Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
}
|
||||
|
||||
if (options.embeds && options.embeds.length > 10) {
|
||||
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.allowed_mentions.users.length > 100) {
|
||||
options.allowed_mentions.users = options.allowed_mentions.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((
|
||||
p,
|
||||
) => p !== "roles");
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.roles.length > 100) {
|
||||
options.allowed_mentions.roles = options.allowed_mentions.roles.slice(
|
||||
0,
|
||||
100,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RequestManager.patch(
|
||||
endpoints.WEBHOOK_EDIT(webhookID, webhookToken, messageID),
|
||||
{ ...options, allowed_mentions: options.allowed_mentions },
|
||||
);
|
||||
}
|
||||
|
||||
export function deleteWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
) {
|
||||
return RequestManager.delete(
|
||||
endpoints.WEBHOOK_DELETE(webhookID, webhookToken, messageID),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { serve } from "https://deno.land/std@0.81.0/http/server.ts";
|
||||
export { verify } from "https://esm.sh/@evan/wasm@0.0.18/target/ed25519/deno.js";
|
||||
@@ -0,0 +1,133 @@
|
||||
import { serve, verify } from "./deps.ts";
|
||||
import {
|
||||
Interaction,
|
||||
InteractionResponse,
|
||||
InteractionResponseType,
|
||||
InteractionType,
|
||||
} from "./types/mod.ts";
|
||||
|
||||
/** This variable is a holder for the public key and other configuration */
|
||||
const serverOptions = {
|
||||
publicKey: "",
|
||||
port: 80,
|
||||
};
|
||||
|
||||
/** Theses are the controllers that you can plug into and customize to your needs. */
|
||||
export const controllers = {
|
||||
handlePayload,
|
||||
handleApplicationCommand,
|
||||
};
|
||||
|
||||
export interface StartServerConfig {
|
||||
/** The public key from your discord bot dashboard at discord.dev */
|
||||
publicKey: string;
|
||||
/** The port number you are wanting to listen to, if you are following the guide, you probably want 80 */
|
||||
port: number;
|
||||
/** The function you would like to provide to handle your commands. */
|
||||
handleApplicationCommand?(
|
||||
payload: Interaction,
|
||||
): Promise<{ status?: number; body: InteractionResponse }>;
|
||||
}
|
||||
|
||||
/** Starts the slash command server */
|
||||
export async function startServer(
|
||||
{ port, publicKey, handleApplicationCommand }: StartServerConfig,
|
||||
) {
|
||||
serverOptions.publicKey = publicKey;
|
||||
serverOptions.port = port;
|
||||
if (handleApplicationCommand) {
|
||||
controllers.handleApplicationCommand = handleApplicationCommand;
|
||||
}
|
||||
|
||||
const server = serve({ port: serverOptions.port });
|
||||
|
||||
for await (const req of server) {
|
||||
const buffer = await Deno.readAll(req.body);
|
||||
const signature = req.headers.get("X-Signature-Ed25519");
|
||||
const timestamp = req.headers.get("X-Signature-Timestamp");
|
||||
|
||||
if (!signature || !timestamp) {
|
||||
req.respond({ status: 400, body: "Bad request" });
|
||||
continue;
|
||||
}
|
||||
|
||||
const isVerified = verifySecurity(buffer, signature!, timestamp!);
|
||||
if (!isVerified) {
|
||||
req.respond({ status: 401, body: "Invalid request signature" });
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = JSON.parse(new TextDecoder().decode(buffer));
|
||||
const response = await controllers.handlePayload(data);
|
||||
req.respond(
|
||||
{ status: response.status || 200, body: JSON.stringify(response.body) },
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function handlePayload(payload: Interaction) {
|
||||
switch (payload.type) {
|
||||
case InteractionType.PING:
|
||||
return { status: 200, body: { type: InteractionResponseType.PONG } };
|
||||
default: // APPLICATION_COMMAND
|
||||
return controllers.handleApplicationCommand(payload);
|
||||
}
|
||||
}
|
||||
|
||||
/** The function that handles your commands. This command can be overriden by you and you can receive the payload and handle accordingly and respond back. The status if not provided will default to 200. */
|
||||
async function handleApplicationCommand(
|
||||
payload: Interaction,
|
||||
): Promise<{ status?: number; body: InteractionResponse }> {
|
||||
// Handle the command
|
||||
if (payload.data?.name === "ping") {
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: { content: "Pong from Discordeno!" },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
|
||||
data: {
|
||||
content:
|
||||
"Whoopsies! Seems the handling for this command is missing. Please contact my developers!",
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Internal function to verify security. Discord will send bad and good data and this function is important to verify it. If it is not verified properly, Discord will kill your bot. */
|
||||
function verifySecurity(buffer: Uint8Array, signature: string, time: string) {
|
||||
const sig = new Uint8Array(64);
|
||||
const timestamp = new TextEncoder().encode(time);
|
||||
|
||||
let offset = 0;
|
||||
const message = new Uint8Array(buffer.length + timestamp.length);
|
||||
while (offset < 2 * 64) {
|
||||
sig[offset / 2] = parseInt(signature!.substring(offset, offset += 2), 16);
|
||||
}
|
||||
|
||||
const slash_key = new Uint8Array(32);
|
||||
|
||||
let keyoffset = 0;
|
||||
while (keyoffset < 2 * 32) {
|
||||
slash_key[keyoffset / 2] = parseInt(
|
||||
serverOptions.publicKey.substring(keyoffset, keyoffset += 2),
|
||||
16,
|
||||
);
|
||||
}
|
||||
|
||||
message.set(timestamp);
|
||||
message.set(buffer, timestamp.length);
|
||||
|
||||
return verify(slash_key, sig, message);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./interactions.ts";
|
||||
export * from "./types/mod.ts";
|
||||
@@ -0,0 +1,95 @@
|
||||
export interface Embed {
|
||||
/** The title of the embed */
|
||||
title?: string;
|
||||
/** The type of embed (always rich for webhook embeds) */
|
||||
type?: string;
|
||||
/** The description of embeds */
|
||||
description?: string;
|
||||
/** The url of embed */
|
||||
url?: string;
|
||||
/** The timestap of the embed content */
|
||||
timestamp?: string;
|
||||
/** The color code of the embed */
|
||||
color?: number;
|
||||
/** The footer information */
|
||||
footer?: EmbedFooter;
|
||||
/** The image information */
|
||||
image?: EmbedImage;
|
||||
/** The thumbnail information */
|
||||
thumbnail?: EmbedThumbnail;
|
||||
/** The video information */
|
||||
video?: EmbedVideo;
|
||||
/** Provider information */
|
||||
provider?: EmbedProvider;
|
||||
/** Author information */
|
||||
author?: EmbedAuthor;
|
||||
/** Fields information */
|
||||
fields?: EmbedField[];
|
||||
}
|
||||
|
||||
export interface EmbedFooter {
|
||||
/** The text of the footer */
|
||||
text: string;
|
||||
/** The url of the footer icon. Only supports http(s) and attachments */
|
||||
icon_url?: string;
|
||||
/** A proxied url of footer icon */
|
||||
proxy_icon_url?: string;
|
||||
}
|
||||
|
||||
export interface EmbedImage {
|
||||
/** The source url of image (only supports http(s) and attachments) */
|
||||
url?: string;
|
||||
/** A proxied url of the image */
|
||||
proxy_url?: string;
|
||||
/** The height of image */
|
||||
height?: number;
|
||||
/** The width of the image */
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export interface EmbedThumbnail {
|
||||
/** The source url of image (only supports http(s) and attachments) */
|
||||
url?: string;
|
||||
/** A proxied url of the thumbnail */
|
||||
proxy_url?: string;
|
||||
/** The height of the thumbnail */
|
||||
height?: number;
|
||||
/** The width of the thumbnail */
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export interface EmbedVideo {
|
||||
/** The source url of video */
|
||||
url?: string;
|
||||
/** The height of the video */
|
||||
height?: number;
|
||||
/** The width of the video */
|
||||
width?: number;
|
||||
}
|
||||
|
||||
export interface EmbedProvider {
|
||||
/** The name of the provider */
|
||||
name?: string;
|
||||
/** The url of the provider */
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface EmbedAuthor {
|
||||
/** The name of the author */
|
||||
name?: string;
|
||||
/** The url of the author */
|
||||
url?: string;
|
||||
/** The url of the author icon (supports http(s) and attachments) */
|
||||
icon_url?: string;
|
||||
/** A proxied url of author icon */
|
||||
proxy_icon_url?: string;
|
||||
}
|
||||
|
||||
export interface EmbedField {
|
||||
/** The name of the field */
|
||||
name: string;
|
||||
/** The value of the field */
|
||||
value: string;
|
||||
/** Whether or not this field should display inline */
|
||||
inline?: boolean;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Embed } from "./embed.ts";
|
||||
import { AllowedMentions } from "./misc.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
|
||||
export interface Interaction {
|
||||
/** The id of the interaction */
|
||||
id: string;
|
||||
/** The type of interaction */
|
||||
type: InteractionType;
|
||||
/** The command data payload */
|
||||
data?: SlashCommandInteractionData;
|
||||
/** The id of the guild it was sent from */
|
||||
guild_id: string;
|
||||
/** The id of the channel it was sent from */
|
||||
channel_id: string;
|
||||
/** The Payload of the member it was sent from */
|
||||
member: MemberCreatePayload;
|
||||
/** The token for this interaction */
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface SlashCommandInteractionData {
|
||||
/** The id of the command */
|
||||
id: string;
|
||||
/** The name of the command */
|
||||
name: string;
|
||||
/** the params and values from the user */
|
||||
options: SlashCommandInteractionDataOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandInteractionDataOption {
|
||||
/** The name of the parammeter */
|
||||
name: string;
|
||||
/** The value of the pair */
|
||||
value?: any;
|
||||
/** Present if this option is a group or subcommand */
|
||||
options?: SlashCommandInteractionDataOption[];
|
||||
}
|
||||
|
||||
export interface InteractionResponse {
|
||||
/** The type of response */
|
||||
type: InteractionResponseType;
|
||||
/** The optional response message */
|
||||
data?: SlashCommandCallbackData;
|
||||
}
|
||||
|
||||
export interface SlashCommandCallbackData {
|
||||
/** is the response TTS */
|
||||
tts?: boolean;
|
||||
/** message content */
|
||||
content: string;
|
||||
/** supports up to 10 embeds */
|
||||
embeds?: Embed[];
|
||||
/** allowed mentions for the message */
|
||||
allowed_mentions?: AllowedMentions;
|
||||
/** acceptable values are message flags */
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export enum InteractionType {
|
||||
PING = 1,
|
||||
APPLICATION_COMMAND = 2,
|
||||
}
|
||||
|
||||
export enum InteractionResponseType {
|
||||
/** ACK a `Ping` */
|
||||
PONG = 1,
|
||||
/** ACK a command without sending a message, eating the user's input */
|
||||
ACKNOWLEDGE = 2,
|
||||
/** respond with a message, eating the user's input */
|
||||
CHANNEL_MESSAGE = 3,
|
||||
/** respond with a message, showing the user's input */
|
||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||
/** ACK a command without sending a message, showing the user's input */
|
||||
ACK_WITH_SOURCE = 5,
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
export interface UserPayload {
|
||||
/** The user's id */
|
||||
id: string;
|
||||
/** the user's username, not unique across the platform */
|
||||
username: string;
|
||||
/** The user's 4 digit discord tag */
|
||||
discriminator: string;
|
||||
/** The user's avatar hash */
|
||||
avatar: string | null;
|
||||
/** Whether the user is a bot */
|
||||
bot?: boolean;
|
||||
/** Whether the user is an official discord system user (part of the urgent message system.) */
|
||||
system?: boolean;
|
||||
/** Whether the user has two factor enabled on their account */
|
||||
mfa_enabled?: boolean;
|
||||
/** the user's chosen language option */
|
||||
locale?: string;
|
||||
/** Whether the email on this account has been verified */
|
||||
verified?: boolean;
|
||||
/** The user's email */
|
||||
email?: string;
|
||||
/** The flags on a user's account. */
|
||||
flags?: number;
|
||||
/** The type of Nitro subscription on a user's account. */
|
||||
premium_type?: number;
|
||||
}
|
||||
|
||||
export interface MemberCreatePayload {
|
||||
/** The user this guild member represents */
|
||||
user: UserPayload;
|
||||
/** The user's guild nickname if one is set. */
|
||||
nick?: string;
|
||||
/** Array of role ids that the member has */
|
||||
roles: string[];
|
||||
/** When the user joined the guild. */
|
||||
joined_at: string;
|
||||
/** When the user used their nitro boost on the server. */
|
||||
premium_since?: string;
|
||||
/** Whether the user is deafened in voice channels */
|
||||
deaf: boolean;
|
||||
/** Whether the user is muted in voice channels */
|
||||
mute: boolean;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
export interface AllowedMentions {
|
||||
/** An array of allowed mention types to parse from the content. */
|
||||
parse: ("roles" | "users" | "everyone")[];
|
||||
/** Array of role_ids to mention (Max size of 100) */
|
||||
roles?: string[];
|
||||
/** Array of user_ids to mention (Max size of 100) */
|
||||
users?: string[];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from "./embed.ts";
|
||||
export * from "./interactions.ts";
|
||||
export * from "./misc.ts";
|
||||
export * from "./slash.ts";
|
||||
export * from "./member.ts";
|
||||
export * from "./webhook.ts";
|
||||
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
InteractionResponseType,
|
||||
SlashCommandCallbackData,
|
||||
} from "./interactions.ts";
|
||||
|
||||
export interface CreateSlashCommandOptions {
|
||||
/** The name of the slash command. */
|
||||
name: string;
|
||||
/** The description of the slash command. */
|
||||
description: String;
|
||||
/** If a guildID is provided, this will be a GUILD command. If none is provided it will be a GLOBAL command. */
|
||||
guildID?: string;
|
||||
/** The options for this command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommand {
|
||||
/** unique id of the command */
|
||||
id: string;
|
||||
/** unique id of the parent application */
|
||||
application_id: string;
|
||||
/** 3-32 character name */
|
||||
name: string;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** the parameters for the command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandOption {
|
||||
/** The type of option */
|
||||
type: SlashCommandOptionType;
|
||||
/** 1-32 character name */
|
||||
name: string;
|
||||
/** 1-100 character description*/
|
||||
description: string;
|
||||
/** the first `required` option for the user to complete--only one option can be `default` */
|
||||
default?: boolean;
|
||||
/** if the parameter is required or optional--default `false`*/
|
||||
required?: boolean;
|
||||
/**
|
||||
* If you specify `choices` for an option, they are the **only** valid values for a user to pick.
|
||||
* choices for `string` and `int` types for the user to pick from
|
||||
*/
|
||||
choices?: SlashCommandOptionChoice[];
|
||||
/** if the option is a subcommand or subcommand group type, this nested options will be the parameters */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandOptionChoice {
|
||||
/** The name of the choice */
|
||||
name: string;
|
||||
/** The value of the choice */
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export enum SlashCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
}
|
||||
|
||||
export interface EditSlashCommandOptions {
|
||||
id: string;
|
||||
guildID?: string;
|
||||
}
|
||||
|
||||
export interface ExecuteSlashCommandOptions {
|
||||
type: InteractionResponseType;
|
||||
data: SlashCommandCallbackData;
|
||||
}
|
||||
|
||||
export interface EditSlashResponseOptions extends SlashCommandCallbackData {
|
||||
/** If this is not provided, it will default to editing the original response. */
|
||||
messageID?: string;
|
||||
}
|
||||
|
||||
export interface UpsertSlashCommandOptions {
|
||||
id: string;
|
||||
guildID?: string;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Embed } from "./embed.ts";
|
||||
|
||||
export interface ExecuteWebhookOptions {
|
||||
/** waits for server confirmation of message send before response, and returns the created message body (defaults to false; when false a message that is not saved does not return an error) */
|
||||
wait?: boolean;
|
||||
/** the message contents (up to 2000 characters) */
|
||||
content?: string;
|
||||
/** override the default username of the webhook */
|
||||
username?: string;
|
||||
/** override the default avatar of the webhook*/
|
||||
avatar_url?: string;
|
||||
/** true if this is a TTS message */
|
||||
tts?: boolean;
|
||||
/** file contents the contents of the file being sent one of content, file, embeds */
|
||||
file?: { blob: unknown; name: string };
|
||||
/** array of up to 10 embed objects embedded rich content. */
|
||||
embeds?: Embed[];
|
||||
/** allowed mentions for the message */
|
||||
mentions?: {
|
||||
/** An array of allowed mention types to parse from the content. */
|
||||
parse: ("roles" | "users" | "everyone")[];
|
||||
/** Array of role_ids to mention (Max size of 100) */
|
||||
roles?: string[];
|
||||
/** Array of user_ids to mention (Max size of 100) */
|
||||
users?: string[];
|
||||
};
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { delay } from "../../deps.ts";
|
||||
import { Errors, HttpResponseCode, RequestMethods } from "../types/types.ts";
|
||||
import { baseEndpoints, discordAPIURLS } from "../utils/constants.ts";
|
||||
import { authorization, eventHandlers } from "./client.ts";
|
||||
import { baseEndpoints, discordAPIURLS } from "../util/constants.ts";
|
||||
import { delay } from "../util/utils.ts";
|
||||
import { authorization, eventHandlers } from "../bot.ts";
|
||||
|
||||
const pathQueues: { [key: string]: QueuedRequest[] } = {};
|
||||
const ratelimitedPaths = new Map<string, RateLimitedPath>();
|
||||
@@ -62,8 +62,7 @@ async function cleanupQueues() {
|
||||
}
|
||||
|
||||
async function processQueue() {
|
||||
// Putting this code inside a function like this allows us to use tail recursion like a while loop without hitting the max stack error.
|
||||
async function avoidMaxStackError() {
|
||||
while (queueInProcess) {
|
||||
if (
|
||||
(Object.keys(pathQueues).length) && !globallyRateLimited
|
||||
) {
|
||||
@@ -110,12 +109,9 @@ async function processQueue() {
|
||||
}
|
||||
|
||||
if (Object.keys(pathQueues).length) {
|
||||
avoidMaxStackError();
|
||||
cleanupQueues();
|
||||
} else queueInProcess = false;
|
||||
}
|
||||
|
||||
return avoidMaxStackError();
|
||||
}
|
||||
|
||||
processRateLimitedPaths();
|
||||
@@ -12,7 +12,7 @@ export interface DiscordPayload {
|
||||
s?: number;
|
||||
/** The event name for this payload. ONLY for OPCode 0 */
|
||||
t?:
|
||||
| "READY"
|
||||
| "APPLICATION_COMMAND_CREATE"
|
||||
| "CHANNEL_CREATE"
|
||||
| "CHANNEL_DELETE"
|
||||
| "CHANNEL_UPDATE"
|
||||
@@ -29,6 +29,7 @@ export interface DiscordPayload {
|
||||
| "GUILD_ROLE_CREATE"
|
||||
| "GUILD_ROLE_DELETE"
|
||||
| "GUILD_ROLE_UPDATE"
|
||||
| "INTERACTION_CREATE"
|
||||
| "MESSAGE_CREATE"
|
||||
| "MESSAGE_DELETE"
|
||||
| "MESSAGE_DELETE_BULK"
|
||||
@@ -38,6 +39,7 @@ export interface DiscordPayload {
|
||||
| "MESSAGE_REACTION_REMOVE_ALL"
|
||||
| "MESSAGE_REACTION_REMOVE_EMOJI"
|
||||
| "PRESENCE_UPDATE"
|
||||
| "READY"
|
||||
| "TYPING_START"
|
||||
| "USER_UPDATE"
|
||||
| "VOICE_STATE_UPDATE"
|
||||
|
||||
@@ -30,6 +30,8 @@ export enum Errors {
|
||||
BOTS_HIGHEST_ROLE_TOO_LOW = "BOTS_HIGHEST_ROLE_TOO_LOW",
|
||||
CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD",
|
||||
INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME",
|
||||
INVALID_SLASH_NAME = "INVALID_SLASH_NAME",
|
||||
INVALID_SLASH_DESCRIPTION = "INVALID_SLASH_DESCRIPTION",
|
||||
INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS",
|
||||
CHANNEL_NOT_FOUND = "CHANNEL_NOT_FOUND",
|
||||
CHANNEL_NOT_TEXT_BASED = "CHANNEL_NOT_TEXT_BASED",
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
import { Guild } from "../structures/structures.ts";
|
||||
import { Guild } from "../api/structures/structures.ts";
|
||||
import { ChannelCreatePayload, ChannelTypes } from "./channel.ts";
|
||||
import { Emoji, StatusType } from "./discord.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
|
||||
export interface InteractionCommandPayload {
|
||||
/** id of the interaction */
|
||||
id: string;
|
||||
/** the type of interaction */
|
||||
type: InteractionType;
|
||||
/** the command data payload */
|
||||
data?: InteractionData;
|
||||
/** the guild it was sent from */
|
||||
guild_id: string;
|
||||
/** the channel it was sent from */
|
||||
channel_id: string;
|
||||
/** guild member data for the invoking user */
|
||||
member: MemberCreatePayload;
|
||||
/** a contintuation token for responding to the interaction */
|
||||
token: string;
|
||||
}
|
||||
|
||||
export enum InteractionType {
|
||||
/** This type is for ACK on webhook only setup. Discord may send these which require. In a sense its a heartbeat. */
|
||||
PING = 1,
|
||||
/** Slash commands */
|
||||
APPLICATION_COMMAND,
|
||||
}
|
||||
|
||||
export interface InteractionData {
|
||||
/** the ID of the invoked command */
|
||||
id: string;
|
||||
/** the name of the invoked command */
|
||||
name: string;
|
||||
/** the params + values from the user */
|
||||
options: InteractionDataOption[];
|
||||
}
|
||||
|
||||
export interface InteractionDataOption {
|
||||
/** the name of the parameter */
|
||||
name: string;
|
||||
/** the value of the pair. present if there was no more options */
|
||||
value?: string | number;
|
||||
/** present if this option is a group or subcommand */
|
||||
options?: InteractionDataOption[];
|
||||
}
|
||||
@@ -28,6 +28,8 @@ export interface MemberCreatePayload {
|
||||
deaf: boolean;
|
||||
/** Whether the user is muted in voice channels */
|
||||
mute: boolean;
|
||||
/** Whether the user has passed the guild's Membership Screening requirements */
|
||||
pending?: boolean;
|
||||
}
|
||||
|
||||
export interface GuildMember {
|
||||
@@ -43,4 +45,6 @@ export interface GuildMember {
|
||||
deaf: boolean;
|
||||
/** Whether the user is muted in voice channels */
|
||||
mute: boolean;
|
||||
/** Whether the user has passed the guild's Membership Screening requirements */
|
||||
pending?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Channel } from "../structures/structures.ts";
|
||||
import { Channel } from "../api/structures/structures.ts";
|
||||
import { ChannelType } from "./channel.ts";
|
||||
import { UserPayload } from "./guild.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
Member,
|
||||
Message,
|
||||
Role,
|
||||
} from "../structures/structures.ts";
|
||||
} from "../api/structures/structures.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
Emoji,
|
||||
@@ -63,9 +63,9 @@ export interface DebugArg {
|
||||
| "reconnect"
|
||||
| "resuming"
|
||||
| "resumed"
|
||||
| "websocketClose"
|
||||
| "websocketErrored"
|
||||
| "websocketReconnecting"
|
||||
| "wsClose"
|
||||
| "wsError"
|
||||
| "wsReconnect"
|
||||
| "missingShard";
|
||||
data: unknown;
|
||||
}
|
||||
@@ -104,6 +104,8 @@ export interface EventHandlers {
|
||||
cachedMember?: Member,
|
||||
) => unknown;
|
||||
heartbeat?: () => unknown;
|
||||
// TODO: FIX THIS
|
||||
interactionCreate?: (data: unknown) => unknown;
|
||||
messageCreate?: (message: Message) => unknown;
|
||||
messageDelete?: (partial: PartialMessage, message?: Message) => unknown;
|
||||
messageUpdate?: (message: Message, cachedMessage: OldMessage) => unknown;
|
||||
|
||||
@@ -13,3 +13,4 @@ export * from "./permission.ts";
|
||||
export * from "./presence.ts";
|
||||
export * from "./role.ts";
|
||||
export * from "./webhook.ts";
|
||||
export * from "./interactions.ts";
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AllowedMentions } from "./channel.ts";
|
||||
import { UserPayload } from "./guild.ts";
|
||||
import { InteractionType } from "./interactions.ts";
|
||||
import { Embed } from "./message.ts";
|
||||
|
||||
export interface WebhookPayload {
|
||||
@@ -66,3 +67,153 @@ export interface EditWebhookMessageOptions {
|
||||
embeds?: Embed[];
|
||||
allowed_mentions?: AllowedMentions;
|
||||
}
|
||||
|
||||
export interface CreateSlashCommandOptions {
|
||||
/** The name of the slash command. */
|
||||
name: string;
|
||||
/** The description of the slash command. */
|
||||
description: String;
|
||||
/** If a guildID is provided, this will be a GUILD command. If none is provided it will be a GLOBAL command. */
|
||||
guildID?: string;
|
||||
/** The options for this command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommand {
|
||||
/** unique id of the command */
|
||||
id: string;
|
||||
/** unique id of the parent application */
|
||||
application_id: string;
|
||||
/** 3-32 character name */
|
||||
name: string;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** the parameters for the command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandOption {
|
||||
/** The type of option */
|
||||
type: SlashCommandOptionType;
|
||||
/** 1-32 character name */
|
||||
name: string;
|
||||
/** 1-100 character description*/
|
||||
description: string;
|
||||
/** the first `required` option for the user to complete--only one option can be `default` */
|
||||
default?: boolean;
|
||||
/** if the parameter is required or optional--default `false`*/
|
||||
required?: boolean;
|
||||
/**
|
||||
* If you specify `choices` for an option, they are the **only** valid values for a user to pick.
|
||||
* choices for `string` and `int` types for the user to pick from
|
||||
*/
|
||||
choices?: SlashCommandOptionChoice[];
|
||||
/** if the option is a subcommand or subcommand group type, this nested options will be the parameters */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandOptionChoice {
|
||||
/** The name of the choice */
|
||||
name: string;
|
||||
/** The value of the choice */
|
||||
value: string | number;
|
||||
}
|
||||
|
||||
export enum SlashCommandOptionType {
|
||||
SUB_COMMAND = 1,
|
||||
SUB_COMMAND_GROUP = 2,
|
||||
STRING = 3,
|
||||
INTEGER = 4,
|
||||
BOOLEAN = 5,
|
||||
USER = 6,
|
||||
CHANNEL = 7,
|
||||
ROLE = 8,
|
||||
}
|
||||
|
||||
export interface Interaction {
|
||||
/** The id of the interaction */
|
||||
id: string;
|
||||
/** The type of interaction */
|
||||
type: InteractionType;
|
||||
/** The command data payload */
|
||||
data?: SlashCommandInteractionData;
|
||||
/** The id of the guild it was sent from */
|
||||
guild_id: string;
|
||||
/** The id of the channel it was sent from */
|
||||
channel_id: string;
|
||||
/** The Payload of the member it was sent from */
|
||||
member: UserPayload;
|
||||
/** The token for this interaction */
|
||||
token: string;
|
||||
}
|
||||
|
||||
export interface SlashCommandInteractionData {
|
||||
/** The id of the command */
|
||||
id: string;
|
||||
/** The name of the command */
|
||||
name: string;
|
||||
/** the params and values from the user */
|
||||
options: SlashCommandInteractionDataOption[];
|
||||
}
|
||||
|
||||
export interface SlashCommandInteractionDataOption {
|
||||
/** The name of the parammeter */
|
||||
name: string;
|
||||
/** The value of the pair */
|
||||
value?: any;
|
||||
/** Present if this option is a group or subcommand */
|
||||
options?: SlashCommandInteractionDataOption[];
|
||||
}
|
||||
|
||||
export interface InteractionResponse {
|
||||
/** The type of response */
|
||||
type: InteractionResponseType;
|
||||
/** The optional response message */
|
||||
data?: SlashCommandCallbackData;
|
||||
}
|
||||
|
||||
export interface SlashCommandCallbackData {
|
||||
/** is the response TTS */
|
||||
tts?: boolean;
|
||||
/** message content */
|
||||
content: string;
|
||||
/** supports up to 10 embeds */
|
||||
embeds?: Embed[];
|
||||
/** allowed mentions for the message */
|
||||
allowed_mentions?: AllowedMentions;
|
||||
/** acceptable values are message flags */
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export enum InteractionResponseType {
|
||||
/** ACK a `Ping` */
|
||||
PONG = 1,
|
||||
/** ACK a command without sending a message, eating the user's input */
|
||||
ACKNOWLEDGE = 2,
|
||||
/** respond with a message, eating the user's input */
|
||||
CHANNEL_MESSAGE = 3,
|
||||
/** respond with a message, showing the user's input */
|
||||
CHANNEL_MESSAGE_WITH_SOURCE = 4,
|
||||
/** ACK a command without sending a message, showing the user's input */
|
||||
ACK_WITH_SOURCE = 5,
|
||||
}
|
||||
|
||||
export interface EditSlashCommandOptions {
|
||||
id: string;
|
||||
guildID?: string;
|
||||
}
|
||||
|
||||
export interface ExecuteSlashCommandOptions {
|
||||
type: InteractionResponseType;
|
||||
data: SlashCommandCallbackData;
|
||||
}
|
||||
|
||||
export interface EditSlashResponseOptions extends SlashCommandCallbackData {
|
||||
/** If this is not provided, it will default to editing the original response. */
|
||||
messageID?: string;
|
||||
}
|
||||
|
||||
export interface UpsertSlashCommandOptions {
|
||||
id: string;
|
||||
guildID?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { Channel, Guild, Member, Message } from "../structures/structures.ts";
|
||||
import {
|
||||
Channel,
|
||||
Guild,
|
||||
Member,
|
||||
Message,
|
||||
} from "../api/structures/structures.ts";
|
||||
import { PresenceUpdatePayload } from "../types/types.ts";
|
||||
import { Collection } from "./collection.ts";
|
||||
|
||||
@@ -11,6 +16,7 @@ export interface CacheData {
|
||||
unavailableGuilds: Collection<string, number>;
|
||||
presences: Collection<string, PresenceUpdatePayload>;
|
||||
fetchAllMembersProcessingRequests: Collection<string, Function>;
|
||||
executedSlashCommands: Collection<string, string>;
|
||||
}
|
||||
|
||||
export const cache: CacheData = {
|
||||
@@ -22,4 +28,5 @@ export const cache: CacheData = {
|
||||
unavailableGuilds: new Collection(),
|
||||
presences: new Collection(),
|
||||
fetchAllMembersProcessingRequests: new Collection(),
|
||||
executedSlashCommands: new Collection(),
|
||||
};
|
||||
@@ -101,6 +101,28 @@ export const endpoints = {
|
||||
WEBHOOK_DELETE: (id: string, token: string, messageID: string) =>
|
||||
`${baseEndpoints.BASE_URL}/webhooks/${id}/${token}/messages/${messageID}`,
|
||||
|
||||
// Application Endpoints
|
||||
COMMANDS: (botID: string) =>
|
||||
`${baseEndpoints.BASE_URL}/applications/${botID}/commands`,
|
||||
COMMANDS_GUILD: (botID: string, id: string) =>
|
||||
`${baseEndpoints.BASE_URL}/applications/${botID}/guilds/${id}/commands`,
|
||||
COMMANDS_ID: (botID: string, id: string) =>
|
||||
`${baseEndpoints.BASE_URL}/applications/${botID}/commands/${id}`,
|
||||
COMMANDS_GUILD_ID: (botID: string, id: string, guildID: string) =>
|
||||
`${baseEndpoints.BASE_URL}/applications/${botID}/guilds/${guildID}/commands/${id}`,
|
||||
|
||||
// Interaction Endpoints
|
||||
INTERACTION_ID_TOKEN: (id: string, token: string) =>
|
||||
`${baseEndpoints.BASE_URL}/interactions/${id}/${token}/callback`,
|
||||
INTERACTION_ORIGINAL_ID_TOKEN: (id: string, token: string) =>
|
||||
`${baseEndpoints.BASE_URL}/webhooks/${id}/${token}/messages/@original`,
|
||||
INTERACTION_ID_TOKEN_MESSAGEID: (
|
||||
id: string,
|
||||
token: string,
|
||||
messageID: string,
|
||||
) =>
|
||||
`${baseEndpoints.BASE_URL}/webhooks/${id}/${token}/messages/${messageID}`,
|
||||
|
||||
// User endpoints
|
||||
USER: (id: string) => `${baseEndpoints.BASE_URL}/users/${id}`,
|
||||
USER_BOT: `${baseEndpoints.BASE_URL}/users/@me`,
|
||||
@@ -1,6 +1,6 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { Guild, Role } from "../structures/structures.ts";
|
||||
import { cacheHandlers } from "../api/controllers/cache.ts";
|
||||
import { botID } from "../bot.ts";
|
||||
import { Guild, Role } from "../api/structures/structures.ts";
|
||||
import { Permission, Permissions, RawOverwrite } from "../types/types.ts";
|
||||
|
||||
/** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */
|
||||
@@ -1,6 +1,6 @@
|
||||
import { encode } from "../../deps.ts";
|
||||
import { sendGatewayCommand } from "../module/shardingManager.ts";
|
||||
import { ActivityType, StatusType } from "../types/types.ts";
|
||||
import { sendGatewayCommand } from "../ws/shard_manager.ts";
|
||||
|
||||
export const sleep = (timeout: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
||||
@@ -37,3 +37,11 @@ export async function urlToBase64(url: string) {
|
||||
export function createNewProp(value: any): Partial<PropertyDescriptor> {
|
||||
return { configurable: true, enumerable: true, writable: true, value };
|
||||
}
|
||||
|
||||
export function delay(ms: number): Promise<void> {
|
||||
return new Promise((res): number =>
|
||||
setTimeout((): void => {
|
||||
res();
|
||||
}, ms)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { decompress_with as decompressWith } from "https://unpkg.com/@evan/wasm@0.0.22/target/zlib/deno.js";
|
||||
@@ -1,5 +1,4 @@
|
||||
import { delay, inflate } from "../../deps.ts";
|
||||
import { eventHandlers } from "../../mod.ts";
|
||||
import { botGatewayData, eventHandlers } from "../bot.ts";
|
||||
import {
|
||||
DiscordBotGatewayData,
|
||||
DiscordHeartbeatPayload,
|
||||
@@ -7,9 +6,10 @@ import {
|
||||
GatewayOpcode,
|
||||
ReadyPayload,
|
||||
} from "../types/types.ts";
|
||||
import { BotStatusRequest } from "../utils/utils.ts";
|
||||
import { IdentifyPayload, proxyWSURL } from "./client.ts";
|
||||
import { handleDiscordPayload } from "./shardingManager.ts";
|
||||
import { BotStatusRequest, delay } from "../util/utils.ts";
|
||||
import { IdentifyPayload, proxyWSURL } from "../bot.ts";
|
||||
import { handleDiscordPayload } from "./shard_manager.ts";
|
||||
import { decompressWith } from "./deps.ts";
|
||||
|
||||
const basicShards = new Map<number, BasicShard>();
|
||||
const heartbeating = new Map<number, boolean>();
|
||||
@@ -64,7 +64,7 @@ export async function createShard(
|
||||
};
|
||||
|
||||
socket.onerror = ({ timeStamp }) => {
|
||||
eventHandlers.debug?.({ type: "websocketErrored", data: { timeStamp } });
|
||||
eventHandlers.debug?.({ type: "wsError", data: { timeStamp } });
|
||||
};
|
||||
|
||||
socket.onmessage = ({ data: message }) => {
|
||||
@@ -73,7 +73,7 @@ export async function createShard(
|
||||
}
|
||||
|
||||
if (message instanceof Uint8Array) {
|
||||
message = inflate(
|
||||
message = decompressWith(
|
||||
message,
|
||||
0,
|
||||
(slice: Uint8Array) => utf8decoder.decode(slice),
|
||||
@@ -143,24 +143,18 @@ export async function createShard(
|
||||
socket.onclose = ({ reason, code, wasClean }) => {
|
||||
eventHandlers.debug?.(
|
||||
{
|
||||
type: "websocketClose",
|
||||
type: "wsClose",
|
||||
data: { shardID: basicShard.id, code, reason, wasClean },
|
||||
},
|
||||
);
|
||||
|
||||
switch (code) {
|
||||
case 4000:
|
||||
// TODO: reconnect
|
||||
case 4001:
|
||||
throw new Error(
|
||||
"[Unknown opcode] Sent an invalid Gateway opcode or an invalid payload for an opcode.",
|
||||
);
|
||||
case 4002:
|
||||
throw new Error("[Decode error] Sent an invalid payload to API.");
|
||||
case 4003:
|
||||
throw new Error(
|
||||
"[Not authenticated] Sent a payload prior to identifying.",
|
||||
);
|
||||
case 4004:
|
||||
throw new Error(
|
||||
"[Authentication failed] The account token sent with your identify payload is incorrect.",
|
||||
@@ -169,15 +163,6 @@ export async function createShard(
|
||||
throw new Error(
|
||||
"[Already authenticated] Sent more than one identify payload.",
|
||||
);
|
||||
case 4007:
|
||||
case 4008:
|
||||
case 4008:
|
||||
eventHandlers.debug?.({
|
||||
type: "websocketReconnecting",
|
||||
data: { shardID: basicShard.id, code, reason, wasClean },
|
||||
});
|
||||
createShard(data, identifyPayload, false, shardID);
|
||||
break;
|
||||
case 4010:
|
||||
throw new Error(
|
||||
"[Invalid shard] Sent an invalid shard when identifying.",
|
||||
@@ -198,6 +183,20 @@ export async function createShard(
|
||||
throw new Error(
|
||||
"[Disallowed intent(s)] Sent a disallowed intent for a Gateway Intent. You may have tried to specify an intent that you have not enabled or are not whitelisted for.",
|
||||
);
|
||||
case 4003:
|
||||
case 4007:
|
||||
case 4008:
|
||||
case 4009:
|
||||
eventHandlers.debug?.({
|
||||
type: "wsReconnect",
|
||||
data: { shardID: basicShard.id, code, reason, wasClean },
|
||||
});
|
||||
createShard(data, identifyPayload, false, shardID);
|
||||
break;
|
||||
default:
|
||||
basicShard.needToResume = true;
|
||||
resumeConnection(botGatewayData, identifyPayload, shardID);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,20 +1,19 @@
|
||||
import { delay } from "../../deps.ts";
|
||||
import { controllers } from "../controllers/mod.ts";
|
||||
import { Guild } from "../structures/structures.ts";
|
||||
import { controllers } from "../api/controllers/mod.ts";
|
||||
import { Guild } from "../api/structures/structures.ts";
|
||||
import {
|
||||
DiscordBotGatewayData,
|
||||
DiscordPayload,
|
||||
FetchMembersOptions,
|
||||
GatewayOpcode,
|
||||
} from "../types/types.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { BotStatusRequest } from "../utils/utils.ts";
|
||||
import { cache } from "../util/cache.ts";
|
||||
import { BotStatusRequest, delay } from "../util/utils.ts";
|
||||
import {
|
||||
botGatewayStatusRequest,
|
||||
createShard,
|
||||
requestGuildMembers,
|
||||
} from "./shard.ts";
|
||||
import { eventHandlers, IdentifyPayload } from "./client.ts";
|
||||
} from "./mod.ts";
|
||||
import { eventHandlers, IdentifyPayload } from "../bot.ts";
|
||||
|
||||
let createNextShard = true;
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export {
|
||||
assert,
|
||||
assertArrayIncludes,
|
||||
assertEquals,
|
||||
} from "https://deno.land/std@0.81.0/testing/asserts.ts";
|
||||
Reference in New Issue
Block a user