mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-03 09:20:08 +00:00
Merge branch 'master' of https://github.com/Skillz4Killz/Discordeno into snake-case
This commit is contained in:
9
.editorconfig
Normal file
9
.editorconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -3,7 +3,7 @@ name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: Skillz4Killz, resynth1943
|
||||
assignees: Skillz4Killz
|
||||
|
||||
---
|
||||
|
||||
|
||||
29
.github/workflows/deno.yml
vendored
29
.github/workflows/deno.yml
vendored
@@ -2,13 +2,13 @@
|
||||
|
||||
name: Testing/Linting
|
||||
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# Controls when the action will run. Triggers the workflow on push or pull request
|
||||
# events but only for the master branch
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [master]
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
@@ -19,14 +19,17 @@ jobs:
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup project
|
||||
run: cp configs.ts.example configs.ts
|
||||
|
||||
- name: Setup Deno environment
|
||||
uses: denolib/setup-deno@master
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Deno Fetch
|
||||
run: deno cache mod.ts
|
||||
- name: Setup project
|
||||
run: cp configs.ts.example configs.ts
|
||||
|
||||
- name: Setup Deno environment
|
||||
uses: denolib/setup-deno@master
|
||||
|
||||
- name: Deno Fetch
|
||||
run: deno cache mod.ts
|
||||
|
||||
- name: Deno Format Check
|
||||
run: deno fmt --check
|
||||
|
||||
10
.github/workflows/greetings.yml
vendored
10
.github/workflows/greetings.yml
vendored
@@ -6,8 +6,8 @@ jobs:
|
||||
greeting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Thank you for helping contribute to Discordeno. I really do appreciate any and all contributions! Hopefully, together we will be able to make the very best gaming bot in the world. Since, this is your very first issue, feel free to look around the repository and then hop on into the Discord server, where you can chat with me directly. https://discord.gg/J4NqJ72'
|
||||
pr-message: 'Thank you for helping contribute to Discordeno. I really do appreciate any and all contributions! Hopefully, together we will be able to make the very best gaming bot in the world. Since, this is your very first pull request, feel free to look around the repository and then hop on into the Discord server, where you can chat with me directly. https://discord.gg/J4NqJ72'
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: "Thank you for helping contribute to Discordeno. I really do appreciate any and all contributions! Hopefully, together we will be able to make the very best bot Discord API module in the world. Since, this is your very first issue, feel free to look around the repository and then hop on into the Discord server, where you can chat with me directly. https://discord.gg/J4NqJ72"
|
||||
pr-message: "Thank you for helping contribute to Discordeno. I really do appreciate any and all contributions! Hopefully, together we will be able to make the very best Discord API module in the world. Since, this is your very first pull request, feel free to look around the repository and then hop on into the Discord server, where you can chat with me directly. https://discord.gg/J4NqJ72"
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"semi": false,
|
||||
"printWidth": 120
|
||||
}
|
||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"deno.enable": true,
|
||||
"editor.formatOnSave": true,
|
||||
"deno.autoFmtOnSave": true,
|
||||
"deno.unstable": true
|
||||
}
|
||||
|
||||
18
README.md
18
README.md
@@ -10,7 +10,16 @@ Discord API library wrapper in Deno
|
||||
|
||||
If you are just starting out, you can use the Discordeno Template repo to get the base of your bot pre-built for you. As other developers create other command frameworks for this library, those frameworks will be listed here:
|
||||
|
||||
**[Official Boilerplate](https://github.com/Skillz4Killz/Discordeno-bot-template):** This is a very minimalistic design for a boilerplate for your bot to get you started.
|
||||
| Bot Name | Developer | Links | Description |
|
||||
|--------------------|--------------------|---------------------------------------------------------|-----|
|
||||
| Official Boilerplate | Skillz4Killz#4500 | [Github](https://github.com/Skillz4Killz/Discordeno-bot-template), [Support Server](https://discord.gg/J4NqJ72) | This is a very minimalistic design for a boilerplate for your bot to get you started. |
|
||||
| DenoBot | NTM Nathan#0001 | [Github](https://github.com/ntm-development/DenoBot), [Support Server](https://discord.com/invite/G2rb53z) | Another boilerplate example of the first one, with more commands and improvements. |
|
||||
|
||||
## Open Source Bots Using Discordeno
|
||||
|
||||
| Bot Name | Developer | Links |
|
||||
|--------------------|--------------------|---------------------------------------------------------|
|
||||
| discordeno-mattis | Mattis6666 | [Github](https://github.com/Mattis6666/discordeno-mattis/) |
|
||||
|
||||
## Motivations/Features
|
||||
|
||||
@@ -294,3 +303,10 @@ This section will list out all the available methods and functionality in the li
|
||||
```ts
|
||||
- .avatarURL(size, format)
|
||||
```
|
||||
|
||||
## Utils
|
||||
|
||||
```ts
|
||||
.editBotsStatus(status, name, type)
|
||||
updateEventHandlers(eventHandlers)
|
||||
```
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export const configs = {
|
||||
token: 'BOT TOKEN HERE'
|
||||
}
|
||||
token: "BOT TOKEN HERE",
|
||||
};
|
||||
|
||||
44
debug.ts
Normal file
44
debug.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This File will never run when you use it.
|
||||
* It is only meant for easy debugging/adding features to the library.
|
||||
* It allows me to easily run up a bot using the library itself without having to commit code
|
||||
* and then reloading cache from another bot folder to then test each micro change.
|
||||
* Especially since a lot of Deno is still unstable and we have to be able to adjust on the fly this is helpful.
|
||||
* Don't worry this will never run and you should never touch this file.
|
||||
* Review the official boilerplates to see how to start a bot!
|
||||
*/
|
||||
|
||||
import Client from "./module/client.ts";
|
||||
import { configs } from "./configs.ts";
|
||||
import { Intents } from "./types/options.ts";
|
||||
import { logYellow } from "./utils/logger.ts";
|
||||
import { cache } from "./utils/cache.ts";
|
||||
import { editBotsStatus } from "./utils/utils.ts";
|
||||
import { StatusType } from "./types/discord.ts";
|
||||
import { ActivityType } from "./types/activity.ts";
|
||||
|
||||
Client({
|
||||
token: configs.token,
|
||||
botID: "675412054529540107",
|
||||
intents: [Intents.GUILDS, Intents.GUILD_MESSAGES, Intents.GUILD_MEMBERS],
|
||||
eventHandlers: {
|
||||
ready: () => {
|
||||
logYellow("Bot ready emitted");
|
||||
editBotsStatus(
|
||||
StatusType.DoNotDisturb,
|
||||
"Testing Name DND",
|
||||
ActivityType.Listening,
|
||||
);
|
||||
},
|
||||
// raw: (data) => logGreen("[RAW] => " + JSON.stringify(data)),
|
||||
messageCreate: async (message) => {
|
||||
if (message.author.id === "130136895395987456") {
|
||||
if (message.content.startsWith("!test")) {
|
||||
if (!message.guild_id) return;
|
||||
const guild = cache.guilds.get(message.guild_id);
|
||||
if (!guild) return logYellow("no guild");
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -6,6 +6,10 @@ import { eventHandlers } from "../module/client.ts";
|
||||
export const handleInternalChannelCreate = (data: ChannelCreatePayload) => {
|
||||
const channel = createChannel(data);
|
||||
cache.channels.set(channel.id, channel);
|
||||
if (channel.guildID) {
|
||||
const guild = cache.guilds.get(channel.guildID);
|
||||
guild?.channels.set(channel.id, channel);
|
||||
}
|
||||
eventHandlers.channelCreate?.(channel);
|
||||
};
|
||||
|
||||
@@ -15,6 +19,11 @@ export const handleInternalChannelUpdate = (data: ChannelCreatePayload) => {
|
||||
cache.channels.set(channel.id, channel);
|
||||
if (!cachedChannel) return;
|
||||
|
||||
if (channel.guildID) {
|
||||
const guild = cache.guilds.get(channel.guildID);
|
||||
guild?.channels.set(channel.id, channel);
|
||||
}
|
||||
|
||||
eventHandlers.channelUpdate?.(channel, cachedChannel);
|
||||
};
|
||||
|
||||
@@ -38,6 +47,8 @@ export const handleInternalChannelDelete = (data: ChannelCreatePayload) => {
|
||||
eventHandlers.voiceChannelLeave?.(member, vs.channelID);
|
||||
});
|
||||
}
|
||||
|
||||
guild?.channels.delete(data.id);
|
||||
}
|
||||
|
||||
cache.channels.delete(data.id);
|
||||
|
||||
51
mod.ts
51
mod.ts
@@ -1,25 +1,30 @@
|
||||
import Client from "./module/client.ts";
|
||||
import { configs } from "./configs.ts";
|
||||
import { Intents } from "./types/options.ts";
|
||||
import { logYellow } from "./utils/logger.ts";
|
||||
import { cache } from "./utils/cache.ts";
|
||||
export * from "./module/client.ts";
|
||||
export * from "./module/requestManager.ts";
|
||||
export * from "./module/shardingManager.ts";
|
||||
|
||||
Client({
|
||||
token: configs.token,
|
||||
botID: "675412054529540107",
|
||||
intents: [Intents.GUILDS, Intents.GUILD_MESSAGES, Intents.GUILD_MEMBERS],
|
||||
eventHandlers: {
|
||||
ready: () => logYellow("Bot ready emitted"),
|
||||
// raw: (data) => logGreen("[RAW] => " + JSON.stringify(data)),
|
||||
messageCreate: async (message) => {
|
||||
if (message.author.id === "130136895395987456") {
|
||||
if (message.content.startsWith("!test")) {
|
||||
if (!message.guildID) return;
|
||||
const guild = cache.guilds.get(message.guildID);
|
||||
if (!guild) return logYellow("no guild");
|
||||
export * from "./structures/channel.ts";
|
||||
export * from "./structures/guild.ts";
|
||||
export * from "./structures/member.ts";
|
||||
export * from "./structures/message.ts";
|
||||
export * from "./structures/role.ts";
|
||||
export * from "./structures/user.ts";
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
export * from "./types/activity.ts";
|
||||
export * from "./types/cdn.ts";
|
||||
export * from "./types/channel.ts";
|
||||
export * from "./types/discord.ts";
|
||||
export * from "./types/errors.ts";
|
||||
export * from "./types/fetch.ts";
|
||||
export * from "./types/guild.ts";
|
||||
export * from "./types/member.ts";
|
||||
export * from "./types/message.ts";
|
||||
export * from "./types/options.ts";
|
||||
export * from "./types/permission.ts";
|
||||
export * from "./types/presence.ts";
|
||||
export * from "./types/role.ts";
|
||||
|
||||
export * from "./utils/cache.ts";
|
||||
export * from "./utils/cdn.ts";
|
||||
export * from "./utils/logger.ts";
|
||||
export * from "./utils/permissions.ts";
|
||||
export * from "./utils/utils.ts";
|
||||
|
||||
@@ -32,7 +32,9 @@ export const createClient = async (data: ClientOptions) => {
|
||||
authorization = `Bot ${data.token}`;
|
||||
|
||||
// Initial API connection to get info about bots connection
|
||||
botGatewayData = await RequestManager.get(endpoints.GATEWAY_BOT);
|
||||
botGatewayData = await RequestManager.get(
|
||||
endpoints.GATEWAY_BOT,
|
||||
) as DiscordBotGatewayData;
|
||||
|
||||
identifyPayload.token = data.token;
|
||||
identifyPayload.intents = data.intents.reduce(
|
||||
@@ -48,3 +50,7 @@ export default createClient;
|
||||
export const updateChannelCache = (key: string, value: Channel) => {
|
||||
cache.channels.set(key, value);
|
||||
};
|
||||
|
||||
export function updateEventHandlers(newEventHandlers: EventHandlers) {
|
||||
eventHandlers = newEventHandlers;
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import { WebSocket } from "https://deno.land/std@0.50.0/ws/mod.ts";
|
||||
import { GatewayOpcode } from "../types/discord.ts";
|
||||
import { delay } from "https://deno.land/std@0.50.0/async/mod.ts";
|
||||
|
||||
// Discord requests null if no number has yet been sent by discord
|
||||
export let previousSequenceNumber: number | null = null;
|
||||
|
||||
// TODO: If a client does not receive a heartbeat ack between its attempts at sending heartbeats, it should immediately terminate the connection with a non-1000 close code, reconnect, and attempt to resume.
|
||||
export const sendConstantHeartbeats = async (
|
||||
socket: WebSocket,
|
||||
interval: number,
|
||||
) => {
|
||||
await delay(interval);
|
||||
socket.send(
|
||||
JSON.stringify({ op: GatewayOpcode.Heartbeat, d: previousSequenceNumber }),
|
||||
);
|
||||
sendConstantHeartbeats(socket, interval);
|
||||
};
|
||||
|
||||
export const updatePreviousSequenceNumber = (sequence: number) => {
|
||||
previousSequenceNumber = sequence;
|
||||
};
|
||||
@@ -2,21 +2,29 @@ import { RequestMethod } from "../types/fetch.ts";
|
||||
import { authorization } from "./client.ts";
|
||||
import { delay } from "https://deno.land/std@0.50.0/async/delay.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import { HttpResponseCode } from "../types/discord.ts";
|
||||
|
||||
const queue: Array<() => Promise<unknown>> = [];
|
||||
const queue: QueuedRequest[] = [];
|
||||
const ratelimitedPaths = new Map<string, RateLimitedPath>();
|
||||
let globallyRateLimited = false;
|
||||
let queueInProcess = false;
|
||||
|
||||
export interface QueuedRequest {
|
||||
callback: () => Promise<unknown>;
|
||||
bucketID?: string | null;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface RateLimitedPath {
|
||||
url: string;
|
||||
resetTimestamp: number;
|
||||
bucketID: string | null;
|
||||
}
|
||||
|
||||
async function processRateLimitedPaths() {
|
||||
const now = Date.now();
|
||||
ratelimitedPaths.forEach((value, key) => {
|
||||
if (value.resetTimestamp > now) return;
|
||||
if (value.resetTimestamp < now) return;
|
||||
ratelimitedPaths.delete(key);
|
||||
if (key === "global") globallyRateLimited = false;
|
||||
});
|
||||
@@ -27,8 +35,25 @@ async function processRateLimitedPaths() {
|
||||
|
||||
async function processQueue() {
|
||||
if (queue.length && !globallyRateLimited) {
|
||||
const callback = queue.shift();
|
||||
if (callback) await callback();
|
||||
const request = queue.shift();
|
||||
|
||||
if (request?.bucketID) {
|
||||
const rateLimitResetIn = checkRatelimits(request.bucketID);
|
||||
const rateLimitedURLResetIn = checkRatelimits(request.url);
|
||||
if (rateLimitResetIn) {
|
||||
// This request is still rate limited readd to queue
|
||||
queue.push(request);
|
||||
} else if (rateLimitedURLResetIn) {
|
||||
// This URL is rate limited readd to queue
|
||||
queue.push(request);
|
||||
} else {
|
||||
// This request is not rate limited so it should be run
|
||||
await request.callback();
|
||||
}
|
||||
} else {
|
||||
// This request has no bucket id so it should be processed
|
||||
await request?.callback();
|
||||
}
|
||||
}
|
||||
|
||||
if (queue.length) processQueue();
|
||||
@@ -38,13 +63,8 @@ async function processQueue() {
|
||||
processRateLimitedPaths();
|
||||
|
||||
export const RequestManager = {
|
||||
// Something off about using runMethod with get breaks when using fetch
|
||||
get: async (url: string, body?: unknown) => {
|
||||
await checkRatelimits(url);
|
||||
const result = await fetch(url, createRequestBody(body));
|
||||
processHeaders(url, result.headers);
|
||||
|
||||
return result.json();
|
||||
return runMethod(RequestMethod.Get, url, body);
|
||||
},
|
||||
post: (url: string, body?: unknown) => {
|
||||
return runMethod(RequestMethod.Post, url, body);
|
||||
@@ -60,7 +80,7 @@ export const RequestManager = {
|
||||
},
|
||||
};
|
||||
|
||||
function createRequestBody (body: any, method?: RequestMethod) {
|
||||
function createRequestBody(body: any, method: RequestMethod) {
|
||||
return {
|
||||
headers: {
|
||||
Authorization: authorization,
|
||||
@@ -70,21 +90,23 @@ function createRequestBody (body: any, method?: RequestMethod) {
|
||||
"X-Audit-Log-Reason": body ? encodeURIComponent(body.reason) : "",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
method: method?.toUpperCase(),
|
||||
method: method.toUpperCase(),
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
async function checkRatelimits(url: string) {
|
||||
function checkRatelimits(url: string) {
|
||||
const ratelimited = ratelimitedPaths.get(url);
|
||||
const global = ratelimitedPaths.get("global");
|
||||
|
||||
const now = Date.now();
|
||||
if (ratelimited && now < ratelimited.resetTimestamp) {
|
||||
await delay(now - ratelimited.resetTimestamp);
|
||||
return ratelimited.resetTimestamp - now;
|
||||
}
|
||||
if (global && now < global.resetTimestamp) {
|
||||
await delay(now - global.resetTimestamp);
|
||||
return global.resetTimestamp - now;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async function runMethod(
|
||||
@@ -92,24 +114,40 @@ async function runMethod(
|
||||
url: string,
|
||||
body?: unknown,
|
||||
retryCount = 0,
|
||||
bucketID?: string | null,
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const callback = async () => {
|
||||
try {
|
||||
await checkRatelimits(url);
|
||||
const rateLimitResetIn = checkRatelimits(url);
|
||||
if (rateLimitResetIn) {
|
||||
return setTimeout(
|
||||
() => runMethod(method, url, body, retryCount++, bucketID),
|
||||
rateLimitResetIn,
|
||||
);
|
||||
}
|
||||
|
||||
const response = await fetch(url, createRequestBody(body, method));
|
||||
processHeaders(url, response.headers);
|
||||
const bucketIDFromHeaders = processHeaders(url, response.headers);
|
||||
handleStatusCode(response.status);
|
||||
|
||||
// Sometimes Discord returns an empty 204 response that can't be made to JSON.
|
||||
if (response.status === 204) resolve();
|
||||
|
||||
const json = await response.json();
|
||||
if (
|
||||
json.retry_after || json.message === "You are being rate limited."
|
||||
json.retry_after ||
|
||||
json.message === "You are being rate limited."
|
||||
) {
|
||||
if (retryCount > 10) throw new Error(Errors.RATE_LIMIT_RETRY_MAXED);
|
||||
await delay(json.retry_after);
|
||||
return runMethod(method, url, body, retryCount++)
|
||||
return runMethod(
|
||||
method,
|
||||
url,
|
||||
body,
|
||||
retryCount++,
|
||||
bucketIDFromHeaders,
|
||||
);
|
||||
}
|
||||
|
||||
return resolve(json);
|
||||
@@ -118,16 +156,40 @@ async function runMethod(
|
||||
}
|
||||
};
|
||||
|
||||
queue.push(callback);
|
||||
queue.push({
|
||||
callback,
|
||||
bucketID,
|
||||
url,
|
||||
});
|
||||
if (!queueInProcess) {
|
||||
queueInProcess = true;
|
||||
processQueue();
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function handleStatusCode(status: number) {
|
||||
if (status >= 200 && status < 400) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case HttpResponseCode.BadRequest:
|
||||
case HttpResponseCode.Unauthorized:
|
||||
case HttpResponseCode.Forbidden:
|
||||
case HttpResponseCode.NotFound:
|
||||
case HttpResponseCode.MethodNotAllowed:
|
||||
case HttpResponseCode.TooManyRequests:
|
||||
throw new Error(Errors.REQUEST_CLIENT_ERROR);
|
||||
case HttpResponseCode.GatewayUnavailable:
|
||||
throw new Error(Errors.REQUEST_SERVER_ERROR);
|
||||
}
|
||||
|
||||
// left are all unknown
|
||||
throw new Error(Errors.REQUEST_UNKNOWN_ERROR);
|
||||
}
|
||||
|
||||
function processHeaders(url: string, headers: Headers) {
|
||||
// If a rate limit response is encountered this will become true and returned
|
||||
let ratelimited = false;
|
||||
|
||||
// Get all useful headers
|
||||
@@ -135,7 +197,7 @@ function processHeaders(url: string, headers: Headers) {
|
||||
const resetTimestamp = headers.get("x-ratelimit-reset");
|
||||
const retryAfter = headers.get("retry-after");
|
||||
const global = headers.get("x-ratelimit-global");
|
||||
// const bucketID = headers.get("x-ratelimit-bucket");
|
||||
const bucketID = headers.get("x-ratelimit-bucket");
|
||||
|
||||
// If there is no remaining rate limit for this endpoint, we save it in cache
|
||||
if (remaining && remaining === "0") {
|
||||
@@ -143,8 +205,17 @@ function processHeaders(url: string, headers: Headers) {
|
||||
|
||||
ratelimitedPaths.set(url, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp),
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID,
|
||||
});
|
||||
|
||||
if (bucketID) {
|
||||
ratelimitedPaths.set(bucketID, {
|
||||
url,
|
||||
resetTimestamp: Number(resetTimestamp) * 1000,
|
||||
bucketID,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no remaining global limit, we save it in cache
|
||||
@@ -155,9 +226,17 @@ function processHeaders(url: string, headers: Headers) {
|
||||
ratelimitedPaths.set("global", {
|
||||
url: "global",
|
||||
resetTimestamp: Date.now() + Number(retryAfter),
|
||||
bucketID,
|
||||
});
|
||||
|
||||
if (bucketID) {
|
||||
ratelimitedPaths.set(bucketID, {
|
||||
url: "global",
|
||||
resetTimestamp: Date.now() + Number(retryAfter),
|
||||
bucketID,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a boolean to check if we need to request again once the rate limit resets
|
||||
return ratelimited;
|
||||
};
|
||||
return ratelimited ? bucketID : undefined;
|
||||
}
|
||||
|
||||
219
module/shard.ts
219
module/shard.ts
@@ -9,99 +9,123 @@ import {
|
||||
DiscordHeartbeatPayload,
|
||||
ReadyPayload,
|
||||
} from "../types/discord.ts";
|
||||
import { logRed, logBlue } from "../utils/logger.ts";
|
||||
import { sendConstantHeartbeats, previousSequenceNumber } from "./gateway.ts";
|
||||
import { logRed } from "../utils/logger.ts";
|
||||
import { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { delay } from "https://deno.land/std@0.50.0/async/delay.ts";
|
||||
|
||||
let shardSocket: WebSocket;
|
||||
|
||||
/** The session id is needed for RESUME functionality when discord disconnects randomly. */
|
||||
let sessionID = "";
|
||||
|
||||
async function resumeConnection(payload: object, botGatewayData: DiscordBotGatewayData) {
|
||||
return setInterval(async () => {
|
||||
console.log("in resume interval");
|
||||
shardSocket = await connectWebSocket(botGatewayData.url);
|
||||
console.log("after connect");
|
||||
await shardSocket.send(
|
||||
JSON.stringify({
|
||||
op: GatewayOpcode.Resume,
|
||||
d: {
|
||||
...payload,
|
||||
session_id: sessionID,
|
||||
seq: previousSequenceNumber,
|
||||
},
|
||||
}),
|
||||
);
|
||||
console.log("after sending resume");
|
||||
}, 1000 * 15);
|
||||
// Discord requests null if no number has yet been sent by discord
|
||||
let previousSequenceNumber: number | null = null;
|
||||
let needToResume = false;
|
||||
|
||||
// TODO: If a client does not receive a heartbeat ack between its attempts at sending heartbeats, it should immediately terminate the connection with a non-1000 close code, reconnect, and attempt to resume.
|
||||
async function sendConstantHeartbeats(
|
||||
interval: number,
|
||||
) {
|
||||
await delay(interval);
|
||||
shardSocket.send(
|
||||
JSON.stringify({ op: GatewayOpcode.Heartbeat, d: previousSequenceNumber }),
|
||||
);
|
||||
sendConstantHeartbeats(interval);
|
||||
}
|
||||
|
||||
export const createShard = async (
|
||||
async function resumeConnection(
|
||||
botGatewayData: DiscordBotGatewayData,
|
||||
identifyPayload: object,
|
||||
) {
|
||||
// Run it once
|
||||
createShard(botGatewayData, identifyPayload, true);
|
||||
// Then retry every 15 seconds
|
||||
await delay(1000 * 15);
|
||||
if (needToResume) resumeConnection(botGatewayData, identifyPayload);
|
||||
}
|
||||
|
||||
const createShard = async (
|
||||
botGatewayData: DiscordBotGatewayData,
|
||||
identifyPayload: object,
|
||||
resuming = false,
|
||||
) => {
|
||||
shardSocket = await connectWebSocket(botGatewayData.url);
|
||||
let resumeInterval = 0;
|
||||
|
||||
// Intial identify with the gateway
|
||||
await shardSocket.send(
|
||||
JSON.stringify({ op: GatewayOpcode.Identify, d: identifyPayload }),
|
||||
);
|
||||
if (!resuming) {
|
||||
// Intial identify with the gateway
|
||||
await shardSocket.send(
|
||||
JSON.stringify({ op: GatewayOpcode.Identify, d: identifyPayload }),
|
||||
);
|
||||
} else {
|
||||
await shardSocket.send(JSON.stringify({
|
||||
op: GatewayOpcode.Resume,
|
||||
d: {
|
||||
...identifyPayload,
|
||||
session_id: sessionID,
|
||||
seq: previousSequenceNumber,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
try {
|
||||
for await (const message of shardSocket) {
|
||||
if (typeof message === "string") {
|
||||
const data = JSON.parse(message);
|
||||
for await (const message of shardSocket) {
|
||||
if (typeof message === "string") {
|
||||
const data = JSON.parse(message);
|
||||
|
||||
switch (data.op) {
|
||||
case GatewayOpcode.Hello:
|
||||
sendConstantHeartbeats(
|
||||
shardSocket,
|
||||
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
|
||||
);
|
||||
switch (data.op) {
|
||||
case GatewayOpcode.Hello:
|
||||
sendConstantHeartbeats(
|
||||
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
|
||||
);
|
||||
break;
|
||||
case GatewayOpcode.Reconnect:
|
||||
case GatewayOpcode.InvalidSession:
|
||||
needToResume = true;
|
||||
resumeConnection(botGatewayData, identifyPayload);
|
||||
break;
|
||||
default:
|
||||
if (data.t === "RESUMED") {
|
||||
needToResume = false;
|
||||
break;
|
||||
case GatewayOpcode.Reconnect:
|
||||
case GatewayOpcode.InvalidSession:
|
||||
// Reconnect to the gateway https://discordapp.com/developers/docs/topics/gateway#reconnect
|
||||
resumeInterval = await resumeConnection(
|
||||
identifyPayload,
|
||||
botGatewayData,
|
||||
);
|
||||
break;
|
||||
case GatewayOpcode.Resume:
|
||||
logBlue("Got RESUME EVENT");
|
||||
clearInterval(resumeInterval);
|
||||
break;
|
||||
default:
|
||||
// Important for RESUME
|
||||
if (data.t === "READY") {
|
||||
sessionID = (data.d as ReadyPayload).session_id;
|
||||
}
|
||||
// @ts-ignore
|
||||
postMessage(
|
||||
{
|
||||
type: "HANDLE_DISCORD_PAYLOAD",
|
||||
payload: message,
|
||||
resumeInterval,
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else if (isWebSocketCloseEvent(message)) {
|
||||
logRed(`Close :( ${JSON.stringify(message)}`);
|
||||
resumeInterval = await resumeConnection(
|
||||
identifyPayload,
|
||||
botGatewayData,
|
||||
}
|
||||
// Important for RESUME
|
||||
if (data.t === "READY") {
|
||||
sessionID = (data.d as ReadyPayload).session_id;
|
||||
}
|
||||
|
||||
// Update the sequence number if it is present
|
||||
if (data.s) previousSequenceNumber = data.s;
|
||||
|
||||
// @ts-ignore
|
||||
postMessage(
|
||||
{
|
||||
type: "HANDLE_DISCORD_PAYLOAD",
|
||||
payload: message,
|
||||
resumeInterval,
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else if (isWebSocketCloseEvent(message)) {
|
||||
logRed(`Close :( ${JSON.stringify(message)}`);
|
||||
// These error codes should just crash the projects
|
||||
if ([4004, 4005, 4012, 4013, 4014].includes(message.code)) {
|
||||
throw new Error(
|
||||
"Shard.ts: Error occurred that is not resumeable or able to be reconnected.",
|
||||
);
|
||||
}
|
||||
// These error codes can not be resumed but need to reconnect from start
|
||||
if ([4003, 4007, 4008, 4009].includes(message.code)) {
|
||||
createShard(botGatewayData, identifyPayload);
|
||||
} else {
|
||||
needToResume = true;
|
||||
resumeConnection(botGatewayData, identifyPayload);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logRed(error);
|
||||
}
|
||||
};
|
||||
|
||||
export function requestGuildMembers(
|
||||
function requestGuildMembers(
|
||||
guildID: string,
|
||||
nonce: string,
|
||||
options?: FetchMembersOptions,
|
||||
@@ -119,29 +143,40 @@ export function requestGuildMembers(
|
||||
}));
|
||||
}
|
||||
|
||||
// TODO: Remove ts-ignore once https://github.com/denoland/deno/issues/5262 fixed
|
||||
// TODO: Errors need to be fixed by VSC plugin
|
||||
// @ts-ignore
|
||||
if (typeof self.postMessage === "function") {
|
||||
// @ts-ignore
|
||||
postMessage({ type: "REQUEST_CLIENT_OPTIONS" });
|
||||
}
|
||||
postMessage({ type: "REQUEST_CLIENT_OPTIONS" });
|
||||
// @ts-ignore
|
||||
if (typeof self.onmessage === "function") {
|
||||
// @ts-ignore
|
||||
onmessage = (message) => {
|
||||
if (message.data.type === "CREATE_SHARD") {
|
||||
createShard(
|
||||
message.data.botGatewayData,
|
||||
message.data.identifyPayload,
|
||||
);
|
||||
}
|
||||
onmessage = (message: MessageEvent) => {
|
||||
if (message.data.type === "CREATE_SHARD") {
|
||||
createShard(
|
||||
message.data.botGatewayData,
|
||||
message.data.identifyPayload,
|
||||
);
|
||||
}
|
||||
|
||||
if (message.data.type === "FETCH_MEMBERS") {
|
||||
requestGuildMembers(
|
||||
message.data.guildID,
|
||||
message.data.nonce,
|
||||
message.data.options,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (message.data.type === "FETCH_MEMBERS") {
|
||||
requestGuildMembers(
|
||||
message.data.guildID,
|
||||
message.data.nonce,
|
||||
message.data.options,
|
||||
);
|
||||
}
|
||||
|
||||
if (message.data.type === "EDIT_BOTS_STATUS") {
|
||||
shardSocket.send(JSON.stringify({
|
||||
op: GatewayOpcode.StatusUpdate,
|
||||
d: {
|
||||
since: null,
|
||||
game: message.data.game.name
|
||||
? {
|
||||
name: message.data.game.name,
|
||||
type: message.data.game.type,
|
||||
}
|
||||
: null,
|
||||
status: message.data.status,
|
||||
afk: false,
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,9 +14,6 @@ import {
|
||||
botID,
|
||||
} from "./client.ts";
|
||||
import { delay } from "https://deno.land/std@0.50.0/async/delay.ts";
|
||||
import {
|
||||
updatePreviousSequenceNumber,
|
||||
} from "./gateway.ts";
|
||||
import {
|
||||
handleInternalChannelCreate,
|
||||
handleInternalChannelUpdate,
|
||||
@@ -105,8 +102,6 @@ export const spawnShards = async (
|
||||
};
|
||||
|
||||
function handleDiscordPayload(data: DiscordPayload) {
|
||||
// Update the sequence number if it is present
|
||||
if (data.s) updatePreviousSequenceNumber(data.s);
|
||||
eventHandlers.raw?.(data);
|
||||
|
||||
switch (data.op) {
|
||||
@@ -195,7 +190,7 @@ function handleDiscordPayload(data: DiscordPayload) {
|
||||
guild.memberCount = memberCount;
|
||||
const member = createMember(
|
||||
options,
|
||||
options.guild_id,
|
||||
guild.id,
|
||||
[...guild.roles.values()].map((role) => role.raw),
|
||||
guild.ownerID,
|
||||
);
|
||||
@@ -561,3 +556,12 @@ export function requestAllMembers(
|
||||
options,
|
||||
});
|
||||
}
|
||||
|
||||
export function sendGatewayCommand(type: "EDIT_BOTS_STATUS", payload: object) {
|
||||
shards.forEach((shard) => {
|
||||
shard.postMessage({
|
||||
type,
|
||||
...payload,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
CreateInviteOptions,
|
||||
ChannelEditOptions,
|
||||
} from "../types/channel.ts";
|
||||
import { botID, updateChannelCache } from "../module/client.ts";
|
||||
import { updateChannelCache } from "../module/client.ts";
|
||||
import { endpoints } from "../constants/discord.ts";
|
||||
import { createMessage } from "./message.ts";
|
||||
import { MessageCreateOptions } from "../types/message.ts";
|
||||
@@ -55,14 +55,13 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
getMessage: async (id: string) => {
|
||||
if (data.guild_id) {
|
||||
if (
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL])
|
||||
!botHasPermission(data.guild_id, [Permissions.VIEW_CHANNEL])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_VIEW_CHANNEL);
|
||||
}
|
||||
if (
|
||||
!botHasPermission(
|
||||
data.guild_id,
|
||||
botID,
|
||||
[Permissions.READ_MESSAGE_HISTORY],
|
||||
)
|
||||
) {
|
||||
@@ -71,7 +70,7 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
}
|
||||
const result = await RequestManager.get(
|
||||
endpoints.CHANNEL_MESSAGE(data.id, id),
|
||||
);
|
||||
) as MessageCreateOptions;
|
||||
return createMessage(result);
|
||||
},
|
||||
/** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */
|
||||
@@ -84,14 +83,13 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
) => {
|
||||
if (data.guild_id) {
|
||||
if (
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL])
|
||||
!botHasPermission(data.guild_id, [Permissions.VIEW_CHANNEL])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_VIEW_CHANNEL);
|
||||
}
|
||||
if (
|
||||
!botHasPermission(
|
||||
data.guild_id,
|
||||
botID,
|
||||
[Permissions.READ_MESSAGE_HISTORY],
|
||||
)
|
||||
) {
|
||||
@@ -120,7 +118,7 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
|
||||
if (data.guild_id) {
|
||||
if (
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.SEND_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_MESSAGES);
|
||||
}
|
||||
@@ -128,7 +126,6 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
content.tts &&
|
||||
!botHasPermission(
|
||||
data.guild_id,
|
||||
botID,
|
||||
[Permissions.SEND_TTS_MESSAGES],
|
||||
)
|
||||
) {
|
||||
@@ -152,7 +149,7 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
deleteMessages: (ids: string[], reason?: string) => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
@@ -173,7 +170,7 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
getInvites: () => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_CHANNELS])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_CHANNELS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
@@ -185,7 +182,6 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
data.guild_id &&
|
||||
!botHasPermission(
|
||||
data.guild_id,
|
||||
botID,
|
||||
[Permissions.CREATE_INSTANT_INVITE],
|
||||
)
|
||||
) {
|
||||
@@ -197,7 +193,7 @@ export function createChannel(data: ChannelCreatePayload) {
|
||||
getWebhooks: () => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_WEBHOOKS])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_WEBHOOKS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { botID, identifyPayload } from "../module/client.ts";
|
||||
import { identifyPayload } from "../module/client.ts";
|
||||
import { endpoints } from "../constants/discord.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import {
|
||||
@@ -121,13 +121,12 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
: undefined,
|
||||
/** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */
|
||||
createChannel: async (name: string, options: CreateChannelOptions) => {
|
||||
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_CHANNELS])) {
|
||||
if (!botHasPermission(data.id, [Permissions.MANAGE_CHANNELS])) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
const result =
|
||||
(await RequestManager.post(endpoints.GUILD_CHANNELS(data.id), {
|
||||
name,
|
||||
type: options.type ? ChannelTypes[options.type] : undefined,
|
||||
permission_overwrites: options?.permission_overwrites
|
||||
? options.permission_overwrites.map((perm) => ({
|
||||
...perm,
|
||||
@@ -136,6 +135,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
}))
|
||||
: undefined,
|
||||
...options,
|
||||
type: options.type ? ChannelTypes[options.type] : undefined,
|
||||
})) as ChannelCreatePayload;
|
||||
|
||||
const channel = createChannel(result);
|
||||
@@ -173,7 +173,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
options: CreateEmojisOptions,
|
||||
) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_EMOJIS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
@@ -186,7 +186,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */
|
||||
editEmoji: (id: string, options: EditEmojisOptions) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_EMOJIS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
@@ -198,7 +198,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */
|
||||
deleteEmoji: (id: string, reason?: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_EMOJIS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
@@ -210,7 +210,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Create a new role for the guild. Requires the MANAGE_ROLES permission. */
|
||||
createRole: async (options: CreateRoleOptions, reason?: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -231,7 +231,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Edit a guild role. Requires the MANAGE_ROLES permission. */
|
||||
editRole: (id: string, options: CreateRoleOptions) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -240,7 +240,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
|
||||
deleteRole: (id: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -252,7 +252,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
*/
|
||||
getRoles: () => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */
|
||||
swapRoles: (rolePositons: PositionSwap) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -271,7 +271,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
getPruneCount: async (days: number) => {
|
||||
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])
|
||||
!botHasPermission(data.id, [Permissions.KICK_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
pruneMembers: (days: number) => {
|
||||
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])
|
||||
!botHasPermission(data.id, [Permissions.KICK_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
@@ -302,7 +302,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
},
|
||||
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
|
||||
getAuditLogs: (options: GetAuditLogsOptions) => {
|
||||
if (!botHasPermission(data.id, botID, [Permissions.VIEW_AUDIT_LOG])) {
|
||||
if (!botHasPermission(data.id, [Permissions.VIEW_AUDIT_LOG])) {
|
||||
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG);
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Returns the guild embed object. Requires the MANAGE_GUILD permission. */
|
||||
getEmbed: () => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -325,7 +325,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */
|
||||
editEmbed: (enabled: boolean, channelID?: string | null) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -341,7 +341,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */
|
||||
getIntegrations: () => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -350,7 +350,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */
|
||||
editIntegration: (id: string, options: EditIntegrationOptions) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -362,7 +362,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */
|
||||
deleteIntegration: (id: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -371,7 +371,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Sync an integration. Requires teh MANAGE_GUILD permission. */
|
||||
syncIntegration: (id: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -380,7 +380,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
|
||||
getBans: () => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
|
||||
!botHasPermission(data.id, [Permissions.BAN_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
@@ -389,7 +389,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires teh BAN_MEMBERS permission. */
|
||||
ban: (id: string, options: BanOptions) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
|
||||
!botHasPermission(data.id, [Permissions.BAN_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
@@ -398,7 +398,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Remove the ban for a user. REquires BAN_MEMBERS permission */
|
||||
unban: (id: string) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
|
||||
!botHasPermission(data.id, [Permissions.BAN_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
@@ -440,7 +440,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
|
||||
edit: (options: GuildEditOptions) => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -449,7 +449,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
|
||||
getInvites: () => {
|
||||
if (
|
||||
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
|
||||
!botHasPermission(data.id, [Permissions.MANAGE_GUILD])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
@@ -465,7 +465,7 @@ export const createGuild = (data: CreateGuildPayload) => {
|
||||
},
|
||||
/** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */
|
||||
getWebhooks: () => {
|
||||
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_WEBHOOKS])) {
|
||||
if (!botHasPermission(data.id, [Permissions.MANAGE_WEBHOOKS])) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
import { botID } from "../module/client.ts";
|
||||
import { endpoints } from "../constants/discord.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import { MemberCreatePayload, EditMemberOptions } from "../types/member.ts";
|
||||
import { ImageSize, ImageFormats } from "../types/cdn.ts";
|
||||
import { Permission, Permissions } from "../types/permission.ts";
|
||||
import { RoleData } from "../types/role.ts";
|
||||
import { memberHasPermission, botHasPermission } from "../utils/permissions.ts";
|
||||
import {
|
||||
memberHasPermission,
|
||||
botHasPermission,
|
||||
highestRole,
|
||||
higherRolePosition,
|
||||
} from "../utils/permissions.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
|
||||
export const createMember = (
|
||||
data: MemberCreatePayload,
|
||||
@@ -38,12 +43,18 @@ export const createMember = (
|
||||
: endpoints.USER_DEFAULT_AVATAR(Number(data.user.discriminator) % 5),
|
||||
/** Add a role to the member */
|
||||
addRole: (roleID: string, reason?: string) => {
|
||||
// TODO: check if the bots highest role is above this one
|
||||
const botsHighestRole = highestRole(guildID, botID);
|
||||
if (
|
||||
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
|
||||
botsHighestRole &&
|
||||
!higherRolePosition(guildID, botsHighestRole.id, roleID)
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
if (!botHasPermission(guildID, [Permissions.MANAGE_ROLES])) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.put(
|
||||
endpoints.GUILD_MEMBER_ROLE(guildID, data.user.id, roleID),
|
||||
{ reason },
|
||||
@@ -51,10 +62,15 @@ export const createMember = (
|
||||
},
|
||||
/** Remove a role from the member */
|
||||
removeRole: (roleID: string, reason?: string) => {
|
||||
// TODO: check if the bots highest role is above this role
|
||||
const botsHighestRole = highestRole(guildID, botID);
|
||||
if (
|
||||
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
|
||||
botsHighestRole &&
|
||||
!higherRolePosition(guildID, botsHighestRole.id, roleID)
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
if (!botHasPermission(guildID, [Permissions.MANAGE_ROLES])) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
return RequestManager.delete(
|
||||
@@ -64,10 +80,16 @@ export const createMember = (
|
||||
},
|
||||
/** Kick a member from the server */
|
||||
kick: (reason?: string) => {
|
||||
// TODO: Check if the bot is above the user so it is capable of kicking
|
||||
const botsHighestRole = highestRole(guildID, botID);
|
||||
const membersHighestRole = highestRole(guildID, data.user.id);
|
||||
if (
|
||||
!botHasPermission(guildID, botID, [Permissions.KICK_MEMBERS])
|
||||
botsHighestRole && membersHighestRole &&
|
||||
botsHighestRole.position <= membersHighestRole.position
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
if (!botHasPermission(guildID, [Permissions.KICK_MEMBERS])) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
return RequestManager.delete(
|
||||
@@ -81,14 +103,14 @@ export const createMember = (
|
||||
if (options.nick.length > 32) {
|
||||
throw new Error(Errors.NICKNAMES_MAX_LENGTH);
|
||||
}
|
||||
if (!botHasPermission(guildID, botID, [Permissions.MANAGE_NICKNAMES])) {
|
||||
if (!botHasPermission(guildID, [Permissions.MANAGE_NICKNAMES])) {
|
||||
throw new Error(Errors.MISSING_MANAGE_NICKNAMES);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
options.roles &&
|
||||
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
|
||||
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
@@ -96,7 +118,7 @@ export const createMember = (
|
||||
if (options.mute) {
|
||||
// TODO: This should check if the member is in a voice channel
|
||||
if (
|
||||
!botHasPermission(guildID, botID, [Permissions.MUTE_MEMBERS])
|
||||
!botHasPermission(guildID, [Permissions.MUTE_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MUTE_MEMBERS);
|
||||
}
|
||||
@@ -104,7 +126,7 @@ export const createMember = (
|
||||
|
||||
if (
|
||||
options.deaf &&
|
||||
!botHasPermission(guildID, botID, [Permissions.DEAFEN_MEMBERS])
|
||||
!botHasPermission(guildID, [Permissions.DEAFEN_MEMBERS])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_DEAFEN_MEMBERS);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
mentionsEveryone: data.mentions_everyone,
|
||||
mentionRoles: data.mention_roles,
|
||||
mentionChannels: data.mention_channels,
|
||||
mentions: data.mentions.map(user => createUser(user)),
|
||||
mentions: data.mentions.map((user) => createUser(user)),
|
||||
webhookID: data.webhook_id,
|
||||
messageReference: data.message_reference,
|
||||
|
||||
@@ -34,7 +34,7 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
delete: (reason?: string) => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
@@ -50,16 +50,17 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
pin: () => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
RequestManager.put(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id));
|
||||
},
|
||||
/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */
|
||||
unpin: () => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
@@ -91,7 +92,7 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
removeAllReactions: () => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
@@ -103,7 +104,7 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
removeReactionEmoji: (reaction: string) => {
|
||||
if (
|
||||
data.guild_id &&
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.MANAGE_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
@@ -130,7 +131,7 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
|
||||
if (data.guild_id) {
|
||||
if (
|
||||
!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES])
|
||||
!botHasPermission(data.guild_id, [Permissions.SEND_MESSAGES])
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_MESSAGES);
|
||||
}
|
||||
@@ -139,7 +140,6 @@ export function createMessage(data: MessageCreateOptions) {
|
||||
content.tts &&
|
||||
!botHasPermission(
|
||||
data.guild_id,
|
||||
botID,
|
||||
[Permissions.SEND_TTS_MESSAGES],
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -10,8 +10,12 @@ export interface ActivityPayload {
|
||||
}
|
||||
|
||||
export enum ActivityType {
|
||||
/** Example: "Playing Rocket League" */
|
||||
Game,
|
||||
/** Example: "Streaming Rocket League" */
|
||||
Streaming,
|
||||
/** Example: "Listening to spotify" */
|
||||
Listening,
|
||||
/** Example: ":smiley: I am cool" */
|
||||
Custom = 4,
|
||||
}
|
||||
|
||||
@@ -92,14 +92,14 @@ export enum VoiceCloseEventCode {
|
||||
|
||||
export enum HttpResponseCode {
|
||||
Ok = 200,
|
||||
Created,
|
||||
Created = 201,
|
||||
NoContent = 204,
|
||||
NotModified = 304,
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
Forbidden = 403,
|
||||
NotFound,
|
||||
MethodNotAllowed,
|
||||
NotFound = 404,
|
||||
MethodNotAllowed = 405,
|
||||
TooManyRequests = 429,
|
||||
GatewayUnavailable = 502,
|
||||
// ServerError left untyped because it's 5xx.
|
||||
|
||||
@@ -22,5 +22,9 @@ export enum Errors {
|
||||
NICKNAMES_MAX_LENGTH = "NICKNAMES_MAX_LENGTH",
|
||||
PRUNE_MIN_DAYS = "PRUNE_MIN_DAYS",
|
||||
RATE_LIMIT_RETRY_MAXED = "RATE_LIMIT_RETRY_MAXED",
|
||||
MISSING_INTENT_GUILD_MEMBERS = "MISSING_INTENT_GUILD_MEMBERS"
|
||||
MISSING_INTENT_GUILD_MEMBERS = "MISSING_INTENT_GUILD_MEMBERS",
|
||||
REQUEST_CLIENT_ERROR = "REQUEST_CLIENT_ERROR",
|
||||
REQUEST_SERVER_ERROR = "REQUEST_SERVER_ERROR",
|
||||
REQUEST_UNKNOWN_ERROR = "REQUEST_UNKNOWN_ERROR",
|
||||
BOTS_HIGHEST_ROLE_TOO_LOW = "BOTS_HIGHEST_ROLE_TOO_LOW",
|
||||
}
|
||||
|
||||
470
types/guild.ts
470
types/guild.ts
@@ -1,307 +1,307 @@
|
||||
import { Emoji, StatusType } from "./discord.ts"
|
||||
import { User } from "../structures/user.ts"
|
||||
import { Permission } from "./permission.ts"
|
||||
import { RoleData } from "./role.ts"
|
||||
import { MemberCreatePayload } from "./member.ts"
|
||||
import { Activity } from "./message.ts"
|
||||
import { Client_Status_Payload } from "./presence.ts"
|
||||
import { ChannelCreatePayload } from "./channel.ts"
|
||||
import { Emoji, StatusType } from "./discord.ts";
|
||||
import { User } from "../structures/user.ts";
|
||||
import { Permission } from "./permission.ts";
|
||||
import { RoleData } from "./role.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
import { Activity } from "./message.ts";
|
||||
import { Client_Status_Payload } from "./presence.ts";
|
||||
import { ChannelCreatePayload } from "./channel.ts";
|
||||
|
||||
export interface GuildRolePayload {
|
||||
/** The id of the guild */
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
/** The role object of the role created, deleted, or updated */
|
||||
role: RoleData
|
||||
role: RoleData;
|
||||
}
|
||||
|
||||
export interface GuildMemberChunkPayload {
|
||||
/** The id of the guild */
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
/** The set of guild members */
|
||||
members: MemberCreatePayload[]
|
||||
members: MemberCreatePayload[];
|
||||
/** The chunk index in the expected chunks for this response */
|
||||
chunk_index: number
|
||||
chunk_index: number;
|
||||
/** The total number of expected chunks for this response */
|
||||
chunk_count: number
|
||||
chunk_count: number;
|
||||
/** if passing an invalid id, it will be found here */
|
||||
not_found?: string[]
|
||||
not_found?: string[];
|
||||
/** if passing true, presences of the members will be here */
|
||||
presences?: Presence[]
|
||||
presences?: Presence[];
|
||||
/** The nonce to help identify */
|
||||
nonce?: string
|
||||
nonce?: string;
|
||||
}
|
||||
|
||||
export interface GuildMemberUpdatePayload {
|
||||
/** The id of the guild */
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
/** The user's role ids */
|
||||
roles: string[]
|
||||
roles: string[];
|
||||
/** The user */
|
||||
user: UserPayload
|
||||
user: UserPayload;
|
||||
/** The nickname of the user in the guild */
|
||||
nick: string
|
||||
nick: string;
|
||||
/** When the user used their nitro boost on the guild. */
|
||||
premium_since: string | null
|
||||
premium_since: string | null;
|
||||
}
|
||||
|
||||
export interface GuildMemberAddPayload extends MemberCreatePayload {
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
}
|
||||
|
||||
export interface GuildEmojisUpdatePayload {
|
||||
guild_id: string
|
||||
emojis: Emoji[]
|
||||
guild_id: string;
|
||||
emojis: Emoji[];
|
||||
}
|
||||
|
||||
export interface GuildBanPayload {
|
||||
/** The id of the guild */
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
/** The banned user. Not a member as you can ban users outside of your guild. */
|
||||
user: UserPayload
|
||||
user: UserPayload;
|
||||
}
|
||||
|
||||
export interface GuildDeletePayload {
|
||||
/** The id of the guild */
|
||||
id: string
|
||||
id: string;
|
||||
/** Whether this guild went unavailable. */
|
||||
unavailable?: boolean
|
||||
unavailable?: boolean;
|
||||
}
|
||||
|
||||
export interface CreateGuildPayload {
|
||||
/** The guild id */
|
||||
id: string
|
||||
id: string;
|
||||
/** The guild name 2-100 characters */
|
||||
name: string
|
||||
name: string;
|
||||
/** The guild icon image hash */
|
||||
icon: string | null
|
||||
icon: string | null;
|
||||
/** The guild splash image hash */
|
||||
splash: string | null
|
||||
splash: string | null;
|
||||
/** The id of the owner */
|
||||
owner_id: string
|
||||
owner_id: string;
|
||||
/** The voice region id for the guild */
|
||||
region: string
|
||||
region: string;
|
||||
/** The afk channel id */
|
||||
afk_channel_id: string | null
|
||||
afk_channel_id: string | null;
|
||||
/** AFK Timeout in seconds. */
|
||||
afk_timeout: number
|
||||
afk_timeout: number;
|
||||
/** Whether this guild is embeddable (widget) */
|
||||
embed_enabled?: boolean
|
||||
embed_enabled?: boolean;
|
||||
/** If not null, the channel id that the widge will generate an invite to. */
|
||||
embed_channel_id?: string | null
|
||||
embed_channel_id?: string | null;
|
||||
/** The verification level required for the guild */
|
||||
verification_level: number
|
||||
verification_level: number;
|
||||
/** The roles in the guild */
|
||||
roles: RoleData[]
|
||||
roles: RoleData[];
|
||||
/** The custom guild emojis */
|
||||
emojis: Emoji[]
|
||||
emojis: Emoji[];
|
||||
/** Enabled guild features */
|
||||
features: Guild_Features[]
|
||||
features: Guild_Features[];
|
||||
/** Required MFA level for the guild */
|
||||
mfa_level: number
|
||||
mfa_level: number;
|
||||
/** The id of the channel to which system mesages are sent */
|
||||
system_channel_id: string | null
|
||||
system_channel_id: string | null;
|
||||
/** When this guild was joined at */
|
||||
joined_at: string
|
||||
joined_at: string;
|
||||
/** Whether this is considered a large guild */
|
||||
large: boolean
|
||||
large: boolean;
|
||||
/** Whether this guild is unavailable */
|
||||
unavailable: boolean
|
||||
unavailable: boolean;
|
||||
/** Total number of members in this guild */
|
||||
member_count?: number
|
||||
voice_states: Voice_State[]
|
||||
member_count?: number;
|
||||
voice_states: Voice_State[];
|
||||
/** Users in the guild */
|
||||
members: MemberCreatePayload[]
|
||||
members: MemberCreatePayload[];
|
||||
/** Channels in the guild */
|
||||
channels: ChannelCreatePayload[]
|
||||
presences: Presence[]
|
||||
channels: ChannelCreatePayload[];
|
||||
presences: Presence[];
|
||||
/** The maximum amount of presences for the guild(the default value, currently 5000 is in effect when null is returned.) */
|
||||
max_presences?: number | null
|
||||
max_presences?: number | null;
|
||||
/** The maximum amount of members for the guild */
|
||||
max_members?: number
|
||||
max_members?: number;
|
||||
/** The vanity url code for the guild */
|
||||
vanity_url_code: string | null
|
||||
vanity_url_code: string | null;
|
||||
/** The description for the guild */
|
||||
description: string | null
|
||||
description: string | null;
|
||||
/** The banner hash */
|
||||
banner: string | null
|
||||
banner: string | null;
|
||||
/** The premium tier */
|
||||
premium_tier: number
|
||||
premium_tier: number;
|
||||
/** The total number of users currently boosting this server. */
|
||||
premium_subscription_count: number
|
||||
premium_subscription_count: number;
|
||||
/** The preferred local of this guild only set if guild has the DISCOVERABLE feature, defaults to en-US */
|
||||
preferred_locale: string
|
||||
preferred_locale: string;
|
||||
}
|
||||
|
||||
export type Guild_Features =
|
||||
| `INVITE_SPLASH`
|
||||
| `VIP_REGIONS`
|
||||
| `VANITY_URL`
|
||||
| `VERIFIED`
|
||||
| `PARTNERED`
|
||||
| `PUBLIC`
|
||||
| `COMMERCE`
|
||||
| `NEWS`
|
||||
| `DISCOVERABLE`
|
||||
| `FEATURABLE`
|
||||
| `ANIMATED_ICON`
|
||||
| `BANNER`
|
||||
| "INVITE_SPLASH"
|
||||
| "VIP_REGIONS"
|
||||
| "VANITY_URL"
|
||||
| "VERIFIED"
|
||||
| "PARTNERED"
|
||||
| "PUBLIC"
|
||||
| "COMMERCE"
|
||||
| "NEWS"
|
||||
| "DISCOVERABLE"
|
||||
| "FEATURABLE"
|
||||
| "ANIMATED_ICON"
|
||||
| "BANNER";
|
||||
|
||||
export interface Voice_Region {
|
||||
/** unique ID for the region */
|
||||
id: string
|
||||
id: string;
|
||||
/** name of the region */
|
||||
name: string
|
||||
name: string;
|
||||
/** true if this is a vip-only server */
|
||||
vip: boolean
|
||||
vip: boolean;
|
||||
/** true for a single server that is closest to the current user's client */
|
||||
optimal: boolean
|
||||
optimal: boolean;
|
||||
/** whether this is a deprecated voice region (avoid switching to these) */
|
||||
deprecated: boolean
|
||||
deprecated: boolean;
|
||||
/** whether this is a custom voice region (used for events/etc) */
|
||||
custom: boolean
|
||||
custom: boolean;
|
||||
}
|
||||
|
||||
export interface BanOptions {
|
||||
/** number of days to delete messages for (0-7) */
|
||||
delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
|
||||
delete_message_days?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
/** The reason for the ban. */
|
||||
reason?: string
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface BannedUser {
|
||||
/** The reason for the ban */
|
||||
reason?: string
|
||||
reason?: string;
|
||||
/** The banned user object */
|
||||
user: User
|
||||
user: User;
|
||||
}
|
||||
|
||||
export interface PositionSwap {
|
||||
/** The unique id */
|
||||
id: string
|
||||
id: string;
|
||||
/** The sorting position number. */
|
||||
position: number
|
||||
position: number;
|
||||
}
|
||||
|
||||
export interface GuildEditOptions {
|
||||
/** The guild name */
|
||||
name?: string
|
||||
name?: string;
|
||||
/** The guild voice region id */
|
||||
region?: string
|
||||
region?: string;
|
||||
/** The verification level. 0 is UNRESTRICTED. 1 is Verified email. 2 is 5 minutes user. 3 is 10 minutes member in guild. 4 is verified phone number */
|
||||
verification_level?: 0 | 1 | 2 | 3
|
||||
verification_level?: 0 | 1 | 2 | 3;
|
||||
/** The default message notification level. 0 is ALL_MESSAGES and 1 is ONLY_MENTINS */
|
||||
default_message_notifications?: 0 | 1
|
||||
default_message_notifications?: 0 | 1;
|
||||
/** Explicit content filter level. 0 is DISABLED 1 is members without roles. 2 is all members */
|
||||
explicit_content_filter?: 0 | 1 | 2
|
||||
explicit_content_filter?: 0 | 1 | 2;
|
||||
/** The id for the afk channel. */
|
||||
afk_channel_id?: string
|
||||
afk_channel_id?: string;
|
||||
/** The afk timeout in seconds. */
|
||||
afk_timeout?: number
|
||||
afk_timeout?: number;
|
||||
/** base64 1024x1024 png/jpeg/gif image for the guild icon (can be animated gif when the server has ANIMATED_ICON feature) */
|
||||
icon?: string
|
||||
icon?: string;
|
||||
/** user id to transfer guild ownership to (must be owner) */
|
||||
owner_id?: string
|
||||
owner_id?: string;
|
||||
/** base64 16:9 png/jpeg image for the guild splash (when the server has INVITE_SPLASH feature) */
|
||||
splash?: string
|
||||
splash?: string;
|
||||
/** base64 16:9 png/jpeg image for the guild banner (when the server has BANNER feature) */
|
||||
banner?: string
|
||||
banner?: string;
|
||||
/** the id of the channel to which system messages are sent */
|
||||
system_channel_id?: string
|
||||
system_channel_id?: string;
|
||||
}
|
||||
|
||||
export interface EditIntegrationOptions {
|
||||
/** The behavior when an integration subscription lapses. */
|
||||
expire_behavior: number
|
||||
expire_behavior: number;
|
||||
/** The period in seconds where the integration will ignore lapsed subscriptions */
|
||||
expire_grace_period: number
|
||||
expire_grace_period: number;
|
||||
/** Whether emoticons should be synced for this integrations (twitch only currently) */
|
||||
enable_emoticons: boolean
|
||||
enable_emoticons: boolean;
|
||||
}
|
||||
|
||||
export interface Guild_Integration {
|
||||
/** The integrations unique id */
|
||||
id: string
|
||||
id: string;
|
||||
/** the integrations name */
|
||||
name: string
|
||||
name: string;
|
||||
/** The integration type like twitch, youtube etc */
|
||||
type: string
|
||||
type: string;
|
||||
/** Is this integration enabled */
|
||||
enabled: boolean
|
||||
enabled: boolean;
|
||||
/** is this integration syncing */
|
||||
syncing: boolean
|
||||
syncing: boolean;
|
||||
/** id that this integration uses for "subscribers" */
|
||||
role_id: string
|
||||
role_id: string;
|
||||
/** The behavior of expiring subscribers */
|
||||
expire_behavior: number
|
||||
expire_behavior: number;
|
||||
/** The grace period before expiring subscribers */
|
||||
expire_grace_period: number
|
||||
expire_grace_period: number;
|
||||
/** The user for this integration */
|
||||
user: UserPayload
|
||||
user: UserPayload;
|
||||
/** The integration account information */
|
||||
account: Account
|
||||
account: Account;
|
||||
/** When this integration was last synced */
|
||||
synced_at: string
|
||||
synced_at: string;
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
/** id of the account */
|
||||
id: string
|
||||
id: string;
|
||||
/** name of the account */
|
||||
name: string
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface UserPayload {
|
||||
/** The user's id */
|
||||
id: string
|
||||
id: string;
|
||||
/** the user's username, not unique across the platform */
|
||||
username: string
|
||||
username: string;
|
||||
/** The user's 4 digit discord tag */
|
||||
discriminator: string
|
||||
discriminator: string;
|
||||
/** The user's avatar hash */
|
||||
avatar: string | null
|
||||
avatar: string | null;
|
||||
/** Whether the user is a bot */
|
||||
bot?: boolean
|
||||
bot?: boolean;
|
||||
/** Whether the user is an official discord system user (part of the urgent message system.) */
|
||||
system?: boolean
|
||||
system?: boolean;
|
||||
/** Whether the user has two factor enabled on their account */
|
||||
mfa_enabled?: boolean
|
||||
mfa_enabled?: boolean;
|
||||
/** the user's chosen language option */
|
||||
locale?: string
|
||||
locale?: string;
|
||||
/** Whether the email on this account has been verified */
|
||||
verified?: boolean
|
||||
verified?: boolean;
|
||||
/** The user's email */
|
||||
email?: string
|
||||
email?: string;
|
||||
/** The flags on a user's account. */
|
||||
flags?: number
|
||||
flags?: number;
|
||||
/** The type of Nitro subscription on a user's account. */
|
||||
premium_type?: number
|
||||
premium_type?: number;
|
||||
}
|
||||
|
||||
export interface Partial_User {
|
||||
/** The user's id */
|
||||
id: string
|
||||
id: string;
|
||||
/** the user's username, not unique across the platform */
|
||||
username?: string
|
||||
username?: string;
|
||||
/** The user's 4 digit discord tag */
|
||||
discriminator?: string
|
||||
discriminator?: string;
|
||||
/** The user's avatar hash */
|
||||
avatar?: string | null
|
||||
avatar?: string | null;
|
||||
/** Whether the user is a bot */
|
||||
bot?: boolean
|
||||
bot?: boolean;
|
||||
/** Whether the user is an official discord system user (part of the urgent message system.) */
|
||||
system?: boolean
|
||||
system?: boolean;
|
||||
/** Whether the user has two factor enabled on their account */
|
||||
mfa_enabled?: boolean
|
||||
mfa_enabled?: boolean;
|
||||
/** the user's chosen language option */
|
||||
locale?: string
|
||||
locale?: string;
|
||||
/** Whether the email on this account has been verified */
|
||||
verified?: boolean
|
||||
verified?: boolean;
|
||||
/** The user's email */
|
||||
email?: string
|
||||
email?: string;
|
||||
/** The flags on a user's account. */
|
||||
flags?: number
|
||||
flags?: number;
|
||||
/** The type of Nitro subscription on a user's account. */
|
||||
premium_type?: number
|
||||
premium_type?: number;
|
||||
}
|
||||
|
||||
export enum User_Flags {
|
||||
@@ -324,62 +324,62 @@ export enum Nitro_Types {
|
||||
}
|
||||
|
||||
export interface Vanity_Invite {
|
||||
code: string | null
|
||||
uses: number
|
||||
code: string | null;
|
||||
uses: number;
|
||||
}
|
||||
|
||||
export interface Guild_Embed {
|
||||
/** Whether the embed is enbaled. */
|
||||
enabled: boolean
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export interface GetAuditLogsOptions {
|
||||
/** Filter the logs for actions made by this user. */
|
||||
user_id?: string
|
||||
user_id?: string;
|
||||
/** The type of audit log. */
|
||||
action_type?: AuditLogType
|
||||
action_type?: AuditLogType;
|
||||
/** Filter the logs before a certain log entry. */
|
||||
before?: string
|
||||
before?: string;
|
||||
/** How many entries are returned. Between 1-100. Default 50. */
|
||||
limit?: number
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
export type AuditLogType =
|
||||
| `GUILD_UPDATE`
|
||||
| `CHANNEL_CREATE`
|
||||
| `CHANNEL_UPDATE`
|
||||
| `CHANNEL_DELETE`
|
||||
| `CHANNEL_OVERWRITE_CREATE`
|
||||
| `CHANNEL_OVERWRITE_UPDATE`
|
||||
| `CHANNEL_OVERWRITE_DELETE`
|
||||
| `MEMBER_KICK`
|
||||
| `MEMBER_PRUNE`
|
||||
| `MEMBER_BAN_ADD`
|
||||
| `MEMBER_BAN_REMOVE`
|
||||
| `MEMBER_UPDATE`
|
||||
| `MEMBER_ROLE_UPDATE`
|
||||
| `MEMBER_MOVE`
|
||||
| `MEMBER_DISCONNECT`
|
||||
| `BOT_ADD`
|
||||
| `ROLE_CREATE`
|
||||
| `ROLE_UPDATE`
|
||||
| `ROLE_DELETE`
|
||||
| `INVITE_CREATE`
|
||||
| `INVITE_UPDATE`
|
||||
| `INVITE_DELETE`
|
||||
| `WEBHOOK_CREATE`
|
||||
| `WEBHOOK_UPDATE`
|
||||
| `WEBHOOK_DELETE`
|
||||
| `EMOJI_CREATE`
|
||||
| `EMOJI_UPDATE`
|
||||
| `EMOJI_DELETE`
|
||||
| `MESSAGE_DELETE`
|
||||
| `MESSAGE_BULK_DELETE`
|
||||
| `MESSAGE_PIN`
|
||||
| `MESSAGE_UNPIN`
|
||||
| `INTEGRATION_CREATE`
|
||||
| `INTEGRATION_UPDATE`
|
||||
| `INTEGRATION_DELETE`
|
||||
| "GUILD_UPDATE"
|
||||
| "CHANNEL_CREATE"
|
||||
| "CHANNEL_UPDATE"
|
||||
| "CHANNEL_DELETE"
|
||||
| "CHANNEL_OVERWRITE_CREATE"
|
||||
| "CHANNEL_OVERWRITE_UPDATE"
|
||||
| "CHANNEL_OVERWRITE_DELETE"
|
||||
| "MEMBER_KICK"
|
||||
| "MEMBER_PRUNE"
|
||||
| "MEMBER_BAN_ADD"
|
||||
| "MEMBER_BAN_REMOVE"
|
||||
| "MEMBER_UPDATE"
|
||||
| "MEMBER_ROLE_UPDATE"
|
||||
| "MEMBER_MOVE"
|
||||
| "MEMBER_DISCONNECT"
|
||||
| "BOT_ADD"
|
||||
| "ROLE_CREATE"
|
||||
| "ROLE_UPDATE"
|
||||
| "ROLE_DELETE"
|
||||
| "INVITE_CREATE"
|
||||
| "INVITE_UPDATE"
|
||||
| "INVITE_DELETE"
|
||||
| "WEBHOOK_CREATE"
|
||||
| "WEBHOOK_UPDATE"
|
||||
| "WEBHOOK_DELETE"
|
||||
| "EMOJI_CREATE"
|
||||
| "EMOJI_UPDATE"
|
||||
| "EMOJI_DELETE"
|
||||
| "MESSAGE_DELETE"
|
||||
| "MESSAGE_BULK_DELETE"
|
||||
| "MESSAGE_PIN"
|
||||
| "MESSAGE_UNPIN"
|
||||
| "INTEGRATION_CREATE"
|
||||
| "INTEGRATION_UPDATE"
|
||||
| "INTEGRATION_DELETE";
|
||||
|
||||
export enum AuditLogs {
|
||||
GUILD_UPDATE = 1,
|
||||
@@ -419,128 +419,134 @@ export enum AuditLogs {
|
||||
INTEGRATION_DELETE,
|
||||
}
|
||||
|
||||
export type ChannelType = "text" | "dm" | "news" | "voice" | "category" | "store"
|
||||
export type ChannelType =
|
||||
| "text"
|
||||
| "dm"
|
||||
| "news"
|
||||
| "voice"
|
||||
| "category"
|
||||
| "store";
|
||||
|
||||
export interface Overwrite {
|
||||
/** The role or user id */
|
||||
id: string
|
||||
id: string;
|
||||
/** Whether this is a role or a member */
|
||||
type: "role" | "member"
|
||||
type: "role" | "member";
|
||||
/** The permissions that this id is allowed to do. (This will mark it as a green check.) */
|
||||
allow: Permission[]
|
||||
allow: Permission[];
|
||||
/** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */
|
||||
deny: Permission[]
|
||||
deny: Permission[];
|
||||
}
|
||||
|
||||
export interface Raw_Overwrite {
|
||||
/** The role or user id */
|
||||
id: string
|
||||
id: string;
|
||||
/** Whether this is a role or a member */
|
||||
type: "role" | "member"
|
||||
type: "role" | "member";
|
||||
/** The permissions that this id is allowed to do. (This will mark it as a green check.) */
|
||||
allow: number
|
||||
allow: number;
|
||||
/** The permissions that this id is NOT allowed to do. (This will mark it as a red x.) */
|
||||
deny: number
|
||||
deny: number;
|
||||
}
|
||||
|
||||
export interface ChannelCreate_Options {
|
||||
/** The type of the channel */
|
||||
type?: ChannelType
|
||||
type?: ChannelType;
|
||||
/** The channel topic. (0-1024 characters) */
|
||||
topic?: string
|
||||
topic?: string;
|
||||
/** The bitrate(in bits) of the voice channel. */
|
||||
bitrate?: number
|
||||
bitrate?: number;
|
||||
/** The user limit of the voice channel. */
|
||||
user_limit?: number
|
||||
user_limit?: number;
|
||||
/** The amount of seconds a user has to wait before sending another message. (0-21600 seconds). Bots, as well as users with the permission `manage_messages or manage_channel` are unaffected. */
|
||||
rate_limit_per_user?: number
|
||||
rate_limit_per_user?: number;
|
||||
/** The sorting position of the channel */
|
||||
position?: number
|
||||
position?: number;
|
||||
/** The channel's permission overwrites */
|
||||
permission_overwrites?: Overwrite[]
|
||||
permission_overwrites?: Overwrite[];
|
||||
/** The id of the parent category for the channel */
|
||||
parent_id?: string
|
||||
parent_id?: string;
|
||||
/** Whether the channel is nsfw */
|
||||
nsfw?: boolean
|
||||
nsfw?: boolean;
|
||||
/** The reason to add in the Audit Logs. */
|
||||
reason?: string
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface CreateEmojisOptions {
|
||||
/** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */
|
||||
roles: string[]
|
||||
roles: string[];
|
||||
/** The reason to have in the Audit Logs. */
|
||||
reason: string
|
||||
reason: string;
|
||||
}
|
||||
|
||||
export interface EditEmojisOptions {
|
||||
/** The name of the emoji */
|
||||
name: string
|
||||
name: string;
|
||||
/** The roles for which this emoji will be whitelisted. Only the users with one of these roles can use this emoji. */
|
||||
roles: string[]
|
||||
roles: string[];
|
||||
}
|
||||
|
||||
export interface CreateRoleOptions {
|
||||
name?: string
|
||||
permissions?: Permission[]
|
||||
color?: number
|
||||
hoist?: boolean
|
||||
mentionable?: boolean
|
||||
name?: string;
|
||||
permissions?: Permission[];
|
||||
color?: number;
|
||||
hoist?: boolean;
|
||||
mentionable?: boolean;
|
||||
}
|
||||
|
||||
export interface PrunePayload {
|
||||
pruned: number
|
||||
pruned: number;
|
||||
}
|
||||
|
||||
export interface Voice_State {
|
||||
/** the guild id this voice state is for */
|
||||
guild_id?: string
|
||||
guild_id?: string;
|
||||
/** the channel id this user is connected to */
|
||||
channel_id: string | null
|
||||
channel_id: string | null;
|
||||
/** the user id this voice state is for */
|
||||
user_id: string
|
||||
user_id: string;
|
||||
/** the guild member this voice state is for */
|
||||
member?: MemberCreatePayload
|
||||
member?: MemberCreatePayload;
|
||||
/** the session id for this voice state */
|
||||
session_id: string
|
||||
session_id: string;
|
||||
/** whether this user is deafened by the server */
|
||||
deaf: boolean
|
||||
deaf: boolean;
|
||||
/** whether this user is muted by the server */
|
||||
mute: boolean
|
||||
mute: boolean;
|
||||
/** whether this user is locally deafened */
|
||||
self_deaf: boolean
|
||||
self_deaf: boolean;
|
||||
/** whether this user is locally muted */
|
||||
self_mute: boolean
|
||||
self_mute: boolean;
|
||||
/** whether this user is streaming using "Go Live" */
|
||||
self_stream?: boolean
|
||||
self_stream?: boolean;
|
||||
/** whether this user is muted by the current user */
|
||||
suppress: boolean
|
||||
suppress: boolean;
|
||||
}
|
||||
|
||||
export interface Presence {
|
||||
/** The user presence is being updated for */
|
||||
user: UserPayload
|
||||
user: UserPayload;
|
||||
/** The roles this user is in */
|
||||
roles: string[]
|
||||
roles: string[];
|
||||
/** null, or the user's current activity */
|
||||
game: Activity | null
|
||||
game: Activity | null;
|
||||
/** The id of the guild */
|
||||
guild_id: string
|
||||
guild_id: string;
|
||||
/** Either idle */
|
||||
status: StatusType
|
||||
activities: Activity[]
|
||||
client_status: Client_Status_Payload
|
||||
premium_since?: string | null
|
||||
nick?: string | null
|
||||
status: StatusType;
|
||||
activities: Activity[];
|
||||
client_status: Client_Status_Payload;
|
||||
premium_since?: string | null;
|
||||
nick?: string | null;
|
||||
}
|
||||
|
||||
export interface FetchMembersOptions {
|
||||
/** Used to specify if you want the presences of the matched members. Default = false. */
|
||||
presences?: boolean
|
||||
presences?: boolean;
|
||||
/** Only returns members whose username or nickname starts with this string. DO NOT INCLUDE discriminators. If a string is provided, the max amount of members that can be fetched is 100. Default = return all members. */
|
||||
query?: string
|
||||
query?: string;
|
||||
/** Used to specify which users to fetch specifically. */
|
||||
userIDs?: string[]
|
||||
userIDs?: string[];
|
||||
/** Maximum number of members to return that match the query. Default = 0 which will return all members. */
|
||||
limit?: number
|
||||
limit?: number;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ export interface Reaction {
|
||||
/** Whether the current user reacted using this emoji */
|
||||
me: boolean;
|
||||
/** The emoji information. Can be partial. */
|
||||
emoji: Emoji;
|
||||
emoji: EmojiReaction;
|
||||
}
|
||||
|
||||
export enum Message_Types {
|
||||
@@ -200,7 +200,7 @@ export enum Message_Flags {
|
||||
URGENT = 1 << 4,
|
||||
}
|
||||
|
||||
export interface Emoji {
|
||||
export interface EmojiReaction {
|
||||
/** The emoji id. */
|
||||
id?: string;
|
||||
/** The emoji name. Null in reaction emoji object if emoji is no longer on the server */
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
export type Permission =
|
||||
| `CREATE_INSTANT_INVITE`
|
||||
| `KICK_MEMBERS`
|
||||
| `BAN_MEMBERS`
|
||||
| `ADMINISTRATOR`
|
||||
| `MANAGE_CHANNELS`
|
||||
| `MANAGE_GUILD`
|
||||
| `ADD_REACTIONS`
|
||||
| `VIEW_AUDIT_LOG`
|
||||
| `VIEW_CHANNEL`
|
||||
| `SEND_MESSAGES`
|
||||
| `SEND_TTS_MESSAGES`
|
||||
| `MANAGE_MESSAGES`
|
||||
| `EMBED_LINKS`
|
||||
| `ATTACH_FILES`
|
||||
| `READ_MESSAGE_HISTORY`
|
||||
| `MENTION_EVERYONE`
|
||||
| `USE_EXTERNAL_EMOJIS`
|
||||
| `CONNECT`
|
||||
| `SPEAK`
|
||||
| `MUTE_MEMBERS`
|
||||
| `DEAFEN_MEMBERS`
|
||||
| `MOVE_MEMBERS`
|
||||
| `USE_VAD`
|
||||
| `PRIORITY_SPEAKER`
|
||||
| `STREAM`
|
||||
| `CHANGE_NICKNAME`
|
||||
| `MANAGE_NICKNAMES`
|
||||
| `MANAGE_ROLES`
|
||||
| `MANAGE_WEBHOOKS`
|
||||
| `MANAGE_EMOJIS`
|
||||
| "CREATE_INSTANT_INVITE"
|
||||
| "KICK_MEMBERS"
|
||||
| "BAN_MEMBERS"
|
||||
| "ADMINISTRATOR"
|
||||
| "MANAGE_CHANNELS"
|
||||
| "MANAGE_GUILD"
|
||||
| "ADD_REACTIONS"
|
||||
| "VIEW_AUDIT_LOG"
|
||||
| "VIEW_CHANNEL"
|
||||
| "SEND_MESSAGES"
|
||||
| "SEND_TTS_MESSAGES"
|
||||
| "MANAGE_MESSAGES"
|
||||
| "EMBED_LINKS"
|
||||
| "ATTACH_FILES"
|
||||
| "READ_MESSAGE_HISTORY"
|
||||
| "MENTION_EVERYONE"
|
||||
| "USE_EXTERNAL_EMOJIS"
|
||||
| "CONNECT"
|
||||
| "SPEAK"
|
||||
| "MUTE_MEMBERS"
|
||||
| "DEAFEN_MEMBERS"
|
||||
| "MOVE_MEMBERS"
|
||||
| "USE_VAD"
|
||||
| "PRIORITY_SPEAKER"
|
||||
| "STREAM"
|
||||
| "CHANGE_NICKNAME"
|
||||
| "MANAGE_NICKNAMES"
|
||||
| "MANAGE_ROLES"
|
||||
| "MANAGE_WEBHOOKS"
|
||||
| "MANAGE_EMOJIS";
|
||||
|
||||
export enum Permissions {
|
||||
CREATE_INSTANT_INVITE = 0x00000001,
|
||||
@@ -60,5 +60,5 @@ export enum Permissions {
|
||||
MANAGE_NICKNAMES = 0x08000000,
|
||||
MANAGE_ROLES = 0x10000000,
|
||||
MANAGE_WEBHOOKS = 0x20000000,
|
||||
MANAGE_EMOJIS = 0x40000000
|
||||
MANAGE_EMOJIS = 0x40000000,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,5 @@ export const formatImageURL = (
|
||||
size: ImageSize = 128,
|
||||
format?: ImageFormats,
|
||||
) => {
|
||||
return `${url}.${
|
||||
format || url.includes("/a_") ? "gif" : "jpg"
|
||||
}/?size=${size}`;
|
||||
return `${url}.${format || url.includes("/a_") ? "gif" : "jpg"}?size=${size}`;
|
||||
};
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { Permission, Permissions } from "../types/permission.ts";
|
||||
import { RoleData } from "../types/role.ts";
|
||||
import { cache } from "./cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { Role } from "../structures/role.ts";
|
||||
|
||||
export const memberHasPermission = (
|
||||
export function memberHasPermission(
|
||||
memberID: string,
|
||||
ownerID: string,
|
||||
roleData: RoleData[],
|
||||
memberRoleIDs: string[],
|
||||
permissions: Permission[],
|
||||
) => {
|
||||
) {
|
||||
if (memberID === ownerID) return true;
|
||||
|
||||
const permissionBits = roleData
|
||||
@@ -24,13 +26,9 @@ export const memberHasPermission = (
|
||||
return permissions.every((permission) =>
|
||||
permissionBits & Permissions[permission]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export const botHasPermission = (
|
||||
guildID: string,
|
||||
botID: string,
|
||||
permissions: Permissions[],
|
||||
) => {
|
||||
export function botHasPermission(guildID: string, permissions: Permissions[]) {
|
||||
const guild = cache.guilds.get(guildID);
|
||||
if (!guild) return false;
|
||||
|
||||
@@ -49,10 +47,48 @@ export const botHasPermission = (
|
||||
if (permissionBits & Permissions.ADMINISTRATOR) return true;
|
||||
|
||||
return permissions.every((permission) => permissionBits & permission);
|
||||
};
|
||||
}
|
||||
|
||||
export const calculatePermissions = (permissionBits: number) => {
|
||||
export function calculatePermissions(permissionBits: number) {
|
||||
return Object.keys(Permissions).filter((perm) => {
|
||||
return permissionBits & Permissions[perm as Permission];
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function highestRole(guildID: string, memberID: string) {
|
||||
const guild = cache.guilds.get(guildID);
|
||||
if (!guild) return;
|
||||
|
||||
const member = guild?.members.get(memberID);
|
||||
if (!member) return;
|
||||
|
||||
let memberHighestRole: Role | undefined;
|
||||
|
||||
for (const roleID of member.roles) {
|
||||
const role = guild.roles.get(roleID);
|
||||
if (!role) continue;
|
||||
|
||||
if (
|
||||
!memberHighestRole || memberHighestRole.position < role.position
|
||||
) {
|
||||
memberHighestRole = role;
|
||||
}
|
||||
}
|
||||
|
||||
return memberHighestRole || (guild.roles.get(guild.id) as Role);
|
||||
}
|
||||
|
||||
export function higherRolePosition(
|
||||
guildID: string,
|
||||
roleID: string,
|
||||
otherRoleID: string,
|
||||
) {
|
||||
const guild = cache.guilds.get(guildID);
|
||||
if (!guild) return;
|
||||
|
||||
const role = guild.roles.get(roleID);
|
||||
const otherRole = guild.roles.get(otherRoleID);
|
||||
if (!role || !otherRole) return;
|
||||
|
||||
return role.position > otherRole.position;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
import { StatusType } from "../types/discord.ts";
|
||||
import { ActivityType } from "../types/activity.ts";
|
||||
import { sendGatewayCommand } from "../module/shardingManager.ts";
|
||||
|
||||
export const sleep = (timeout: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
||||
};
|
||||
|
||||
export function editBotsStatus(
|
||||
status: StatusType,
|
||||
name?: string,
|
||||
type = ActivityType.Game,
|
||||
) {
|
||||
sendGatewayCommand("EDIT_BOTS_STATUS", { status, game: { name, type } });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user