mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-04 18:00:08 +00:00
Merge branch 'master' of https://github.com/discordeno/discordeno
This commit is contained in:
98
.github/CONTRIBUTING.md
vendored
98
.github/CONTRIBUTING.md
vendored
@@ -1,69 +1,47 @@
|
||||
# Fundamental Design Goals
|
||||
# Contributing
|
||||
|
||||
This document serves to outline the overall design goals of the project. Please
|
||||
see below a list of these fundamentals.
|
||||
- Read the [style guide](#style-guide).
|
||||
- Ask for help on the [official Discord server](https:)
|
||||
- If you are going to work on an issue, mention so in the issue comments before
|
||||
you start working on the issue.
|
||||
- If you are going to work on a new feature, create an issue and discuss with
|
||||
other contributors before you start working on the feature.
|
||||
- Abide by and heed to
|
||||
[Discord Developer Terms of Service](https://discord.com/developers/docs/legal)
|
||||
|
||||
## Do not allow anything Discord does not permit
|
||||
## Submitting a Pull Request
|
||||
|
||||
Prevent any and all attempts of making user bots. If someone connects and the
|
||||
client.user is not a bot user then throw an error immediately.
|
||||
- Give the PR a descriptive title.
|
||||
|
||||
Do not support non-bot features like Group DMs or dm calls etc...
|
||||
Examples of good PR title:
|
||||
|
||||
## Prettier Philosophy Regarding Options
|
||||
- fix(controllers): cache member from INTERACTION_CREATE payload
|
||||
- docs: improve wording
|
||||
- feat(handlers): add editGuild() function Examples of bad PR title:
|
||||
- fix #7123
|
||||
- update docs
|
||||
- fix bugs
|
||||
|
||||
Avoid options/customizable whenever possible. Always enforce default values.
|
||||
Except in cases like intents where the user should be able to pick which intents
|
||||
to listen for.
|
||||
|
||||
## Security
|
||||
|
||||
Permission checks should be done by the library! We can throw a custom error
|
||||
that shows which permissions are missing in order to run this request and save
|
||||
an API call. This will also prevent bots from getting banned due to Missing
|
||||
Access errors.
|
||||
|
||||
Typescript 3.8 provides **TRUE** private props and methods that no one can
|
||||
access. We will use this to our advantage to truly make a proper API. This isn't
|
||||
a silly `_` to mark it as a private but the user should NEVER be able to access
|
||||
it no matter what.
|
||||
|
||||
## Functional API
|
||||
|
||||
Events emitted by the client, for example the message creation event, should not
|
||||
emit a `Message` class instance, but instead a _POJO_ (Plain Ol' JavaScript
|
||||
Object). This will overall make a cleaner and more performant API, while
|
||||
removing the headaches of extending built-in classes, and inheritance.
|
||||
|
||||
Use functions when possible instead of an event emitter to prevent emitter
|
||||
related memory leak issues or a number of other headaches that arise.
|
||||
|
||||
TLDR: Avoid `classes` whenever possible. Avoid `loops` whenever possible(opt for
|
||||
iterations like .forEach, map reduce, some find etc...)
|
||||
|
||||
## Documentation
|
||||
|
||||
Use `/** Description here */` comments above all properties and methods to
|
||||
describe it so that VSC and other good IDE's with intellisense can pick it up
|
||||
and provide the documentation right inside the IDE preventing a developer from
|
||||
needing Discord API docs or even Deno documentation.
|
||||
|
||||
We should have a step by step guide nonetheless but this is a POST v1 launch. We
|
||||
should have a template repo to creating a boilerplate bot.
|
||||
|
||||
## Backwards Compatibility BS
|
||||
|
||||
Backwards compatibility is the death of code. It causes clutter and uglyness to
|
||||
pile up and makes developers lazier. There will be no such thing as backwards
|
||||
compatibility reasons in Discordeno. We will always support the latest and
|
||||
greatest of JS. The end! Users can fork the lib at any commit to keep older
|
||||
versions until they are ready to update.
|
||||
|
||||
That said, we don't expect many things to be changing drastically after v1. As
|
||||
you can imagine Typescript allows the latest and greatest of JS so we will be
|
||||
ahead of the curve for years to come.
|
||||
- Ensure there is a related issue and it is referenced in the pull request text.
|
||||
- Ensure there are tests that cover the changes.
|
||||
- Ensure all of the checks (lint and test) are passing.
|
||||
|
||||
## Style Guide
|
||||
|
||||
Prettier is our style guide. No discussions around styling ever. The options are
|
||||
set and that is all. When you code let prettier handle the styling. PERIOD!
|
||||
- Use underscores as a separator in filenames.
|
||||
- Comply with
|
||||
[these guidelines for inclusive code](https://chromium.googlesource.com/chromium/src/+/master/styleguide/inclusive_code.md).
|
||||
- An exported function must not have more than 4 individual parameters, the rest
|
||||
arguments should be encorporated inside an object as a single parameter.
|
||||
- Export all interfaces, types, and enums that are used for or inside an
|
||||
exported entity.
|
||||
- Every exported entity must be accompanied by a Typedoc (JSDoc without explicit
|
||||
types) comment block. Ideally, we prefer single line comment block.
|
||||
- Top-level functions should not use arrow syntax.
|
||||
- Minimize dependencies; do not make circular imports.
|
||||
- Utilize functional API wherever possible and avoid usage of ES6 classes.
|
||||
- Follow
|
||||
[Convention Over Configuration](https://en.wikipedia.org/wiki/Convention_over_configuration)
|
||||
wherever possible.
|
||||
- Please follow the
|
||||
[guidelines for inclusive code](https://chromium.googlesource.com/chromium/src/+/master/styleguide/inclusive_code.md).
|
||||
|
||||
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v2
|
||||
- name: Build documentation
|
||||
run: |
|
||||
cd docs
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -9,4 +9,4 @@ jobs:
|
||||
- name: Run fmt check script
|
||||
run: deno fmt --check
|
||||
- name: Run lint script
|
||||
run: deno lint src/** --unstable
|
||||
run: deno lint src/** test/** --unstable
|
||||
|
||||
5
.github/workflows/test.yml
vendored
5
.github/workflows/test.yml
vendored
@@ -3,9 +3,14 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
deno: ["v1.x", "nightly"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: denolib/setup-deno@v2
|
||||
with:
|
||||
deno-version: ${{ matrix.deno }}
|
||||
- name: Cache dependencies
|
||||
run: deno cache mod.ts
|
||||
- name: Run test script
|
||||
|
||||
37
README.md
37
README.md
@@ -8,21 +8,20 @@
|
||||
|
||||
## Features
|
||||
|
||||
- **Secure & stable**: Discordeno is comparatively more stable than the other
|
||||
libraries. One of the greatest issues with almost every library is stability;
|
||||
types are outdated, less (or minimal) parity with the API, core maintainers
|
||||
have quit or no longer actively maintain the library, and whatnot. Discordeno,
|
||||
on the other hand, is actively maintained to ensure great performance and
|
||||
convenience. Discordeno internally checks all missing permissions before
|
||||
forwarding a request to the API so that the client does not get
|
||||
globally-banned by Discord.
|
||||
- **Efficient & lightweight**: Discordeno is simplistic, easy-to-use, versatile,
|
||||
and efficient. Uses
|
||||
- **Secure & stable**: Discordeno is secure and stable. One of the greatest
|
||||
issues with almost every library is stability; types are outdated, less (or
|
||||
minimal) parity with the API, core maintainers have quit or no longer actively
|
||||
maintain the library, and whatnot. Discordeno, on the other hand, is actively
|
||||
maintained to ensure great performance and convenience. Moreover, it
|
||||
internally checks all missing permissions before forwarding a request to the
|
||||
Discord API so that the client does not get globally-banned by Discord.
|
||||
- **Simple, Efficient, & Lightweight**: Discordeno is simplistic, easy-to-use,
|
||||
versatile while being efficient and lightweight. Follows
|
||||
[Convention Over Configuration](https://en.wikipedia.org/wiki/Convention_over_configuration)
|
||||
design paradigm ― prefers defaults that Discord recommends or the best
|
||||
configuration for the majority of the users.
|
||||
design paradigm ― prefers defaults options or values that are recommended by
|
||||
Discord or the best configuration for the majority of the users.
|
||||
- [**Functional API**](https://en.wikipedia.org/wiki/Functional_programming):
|
||||
This will produce an overall concise and more performant code while removing
|
||||
Functional API ensures an overall concise yet performant code while removing
|
||||
the difficulties of extending built-in classes and inheritance.
|
||||
|
||||
## Getting Started
|
||||
@@ -54,12 +53,14 @@ startBot({
|
||||
|
||||
Note to developers: don't worry a lot of developers start out programming a
|
||||
Discord bot as their first project (I did 😉) and it is not so easy to do so.
|
||||
Discordeno is built considering all the issues that I and a lot of developers
|
||||
that I personally know had when I first started out coding Discord bots with
|
||||
existing libraries. If you are a beginner, you can check out these awesome
|
||||
official and unofficial boilerplates:
|
||||
Discordeno is designed and built considering all the issues that I and a lot of
|
||||
developers had when I first started out coding Discord bots with existing
|
||||
libraries. If you are a beginner, you can check out these awesome official and
|
||||
unofficial boilerplates:
|
||||
|
||||
- [Discordeno Bot Template (official)](https://github.com/Skillz4Killz/Discordeno-bot-template)
|
||||
- [Discordeno Bot Template (official)](https://github.com/discordeno/discordeno-bot-template)
|
||||
- [Serverless Slash Commands Template
|
||||
(official)](https://github.com/slash-commands-bot)
|
||||
- [Add Your Own!](https://github.com/discordeno/discordeno/pulls)
|
||||
|
||||
## Useful Links
|
||||
|
||||
@@ -6,7 +6,7 @@ module.exports = {
|
||||
"/",
|
||||
"faq",
|
||||
"gettingstarted",
|
||||
"djs",
|
||||
"migrating",
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -8,22 +8,22 @@
|
||||
|
||||
## Features
|
||||
|
||||
- **Secure & stable**: Discordeno is comparatively more stable than the other
|
||||
libraries. One of the greatest issues with almost every library is stability;
|
||||
types are outdated, less (or minimal) parity with the API, core maintainers
|
||||
have quit or no longer actively maintain the library, and whatnot. Discordeno,
|
||||
on the other hand, is actively maintained to ensure great performance and
|
||||
convenience. Discordeno internally checks all missing permissions before
|
||||
forwarding a request to the API so that the client does not get
|
||||
globally-banned by Discord.
|
||||
- **Efficient & lightweight**: Discordeno is simplistic, easy-to-use, versatile,
|
||||
and efficient. Uses
|
||||
- **Secure & stable**: Discordeno is secure and stable. One of the greatest
|
||||
issues with almost every library is stability; types are outdated, less (or
|
||||
minimal) parity with the API, core maintainers have quit or no longer actively
|
||||
maintain the library, and whatnot. Discordeno, on the other hand, is actively
|
||||
maintained to ensure great performance and convenience. Moreover, it
|
||||
internally checks all missing permissions before forwarding a request to the
|
||||
Discord API so that the client does not get globally-banned by Discord.
|
||||
- **Simple, Efficient, & Lightweight**: Discordeno is simplistic, easy-to-use,
|
||||
versatile while being efficient and lightweight. Follows
|
||||
[Convention Over Configuration](https://en.wikipedia.org/wiki/Convention_over_configuration)
|
||||
design paradigm ― prefers defaults that Discord recommends or the best
|
||||
configuration for the majority of the users.
|
||||
design paradigm ― prefers defaults options or values that are recommended by
|
||||
Discord or the best configuration for the majority of the users.
|
||||
- [**Functional API**](https://en.wikipedia.org/wiki/Functional_programming):
|
||||
This will produce an overall concise and more performant code while removing
|
||||
Functional API ensures an overall concise yet performant code while removing
|
||||
the difficulties of extending built-in classes and inheritance.
|
||||
[Learn more about class-free JavaScript](https://dannyfritz.wordpress.com/2014/10/11/class-free-object-oriented-programming/)
|
||||
|
||||
## Useful Links
|
||||
|
||||
|
||||
@@ -68,11 +68,8 @@ startBot({
|
||||
Below you will find youtube playlists that display channels using Discordeno for
|
||||
their tutorials.
|
||||
|
||||
Web-Mystery Tutorials:
|
||||
|
||||
- [Making a Discord bot with Deno and
|
||||
Discordeno](https://web-mystery.com/articles/making-discord-bot-deno-and-discordeno)
|
||||
- [Running a Discord bot written in Deno in
|
||||
- [Running a Discord bot written using Deno in
|
||||
Docker](https://web-mystery.com/articles/running-discord-bot-written-deno-docker)
|
||||
- YouTube Tutorials:
|
||||
- [Discordeno Bot Tutorials YouTube series](https://youtu.be/rIph9-BGsuQ)
|
||||
- [Discordeno Bot Tutorials (YouTube)](https://youtu.be/rIph9-BGsuQ)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Migrating
|
||||
|
||||
# Migrating from Discord.js
|
||||
## Migrating from Discord.js
|
||||
|
||||
This migration guide is not intended to discredit Discord.js authors/maintainers
|
||||
or Discord.js itself. In fact, Discord.js is the most popular Node.js library,
|
||||
admired and praised by a lot of JavaScript developers.
|
||||
|
||||
## Finding A Open Source Bot
|
||||
## Finding an Open-Source Discord Bot
|
||||
|
||||
For the purposes of this guide, I wanted to find a moderation bot that is
|
||||
totally open source to show an example of how to convert the bot to Discordeno.
|
||||
|
||||
@@ -70,7 +70,7 @@ export async function handleInternalGuildMemberUpdate(data: DiscordPayload) {
|
||||
payload.guild_id,
|
||||
);
|
||||
|
||||
await cacheHandlers.set("messages", member.id, member);
|
||||
await cacheHandlers.set("members", member.id, member);
|
||||
|
||||
if (guildMember?.nick !== payload.nick) {
|
||||
eventHandlers.nicknameUpdate?.(
|
||||
|
||||
@@ -75,6 +75,7 @@ export async function getMessage(
|
||||
const result = await RequestManager.get(
|
||||
endpoints.CHANNEL_MESSAGE(channelID, id),
|
||||
) as MessageCreateOptions;
|
||||
|
||||
return structures.createMessage(result);
|
||||
}
|
||||
|
||||
@@ -113,6 +114,7 @@ export async function getMessages(
|
||||
endpoints.CHANNEL_MESSAGES(channelID),
|
||||
options,
|
||||
)) as MessageCreateOptions[];
|
||||
|
||||
return Promise.all(result.map((res) => structures.createMessage(res)));
|
||||
}
|
||||
|
||||
@@ -121,6 +123,7 @@ export async function getPins(channelID: string) {
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.CHANNEL_PINS(channelID),
|
||||
)) as MessageCreateOptions[];
|
||||
|
||||
return Promise.all(result.map((res) => structures.createMessage(res)));
|
||||
}
|
||||
|
||||
@@ -129,8 +132,10 @@ export async function getPins(channelID: string) {
|
||||
* However, if a bot is responding to a command and expects the computation to take a few seconds,
|
||||
* this endpoint may be called to let the user know that the bot is processing their message.
|
||||
*/
|
||||
export function startTyping(channelID: string) {
|
||||
return RequestManager.post(endpoints.CHANNEL_TYPING(channelID));
|
||||
export async function startTyping(channelID: string) {
|
||||
const result = await RequestManager.post(endpoints.CHANNEL_TYPING(channelID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
|
||||
@@ -236,9 +241,9 @@ export async function sendMessage(
|
||||
message_id: content.replyMessageID,
|
||||
},
|
||||
},
|
||||
);
|
||||
) as MessageCreateOptions;
|
||||
|
||||
return structures.createMessage(result as MessageCreateOptions);
|
||||
return structures.createMessage(result);
|
||||
}
|
||||
|
||||
/** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */
|
||||
@@ -266,10 +271,15 @@ export async function deleteMessages(
|
||||
);
|
||||
}
|
||||
|
||||
return RequestManager.post(endpoints.CHANNEL_BULK_DELETE(channelID), {
|
||||
messages: ids.splice(0, 100),
|
||||
reason,
|
||||
});
|
||||
const result = await RequestManager.post(
|
||||
endpoints.CHANNEL_BULK_DELETE(channelID),
|
||||
{
|
||||
messages: ids.splice(0, 100),
|
||||
reason,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Gets the invites for this channel. Requires MANAGE_CHANNEL */
|
||||
@@ -283,7 +293,10 @@ export async function getChannelInvites(channelID: string) {
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
return RequestManager.get(endpoints.CHANNEL_INVITES(channelID));
|
||||
|
||||
const result = await RequestManager.get(endpoints.CHANNEL_INVITES(channelID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */
|
||||
@@ -300,14 +313,22 @@ export async function createInvite(
|
||||
) {
|
||||
throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE);
|
||||
}
|
||||
return RequestManager.post(endpoints.CHANNEL_INVITES(channelID), options);
|
||||
|
||||
const result = await RequestManager.post(
|
||||
endpoints.CHANNEL_INVITES(channelID),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns an invite for the given code. */
|
||||
export function getInvite(inviteCode: string) {
|
||||
return RequestManager.get(endpoints.INVITE(inviteCode)) as Promise<
|
||||
InvitePayload
|
||||
>;
|
||||
export async function getInvite(inviteCode: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.INVITE(inviteCode),
|
||||
);
|
||||
|
||||
return result as InvitePayload;
|
||||
}
|
||||
|
||||
/** Deletes an invite for the given code. Requires `MANAGE_CHANNELS` or `MANAGE_GUILD` permission */
|
||||
@@ -331,9 +352,11 @@ export async function deleteInvite(
|
||||
}
|
||||
}
|
||||
|
||||
return RequestManager.delete(endpoints.INVITE(inviteCode)) as Promise<
|
||||
InvitePayload
|
||||
>;
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.INVITE(inviteCode),
|
||||
);
|
||||
|
||||
return result as InvitePayload;
|
||||
}
|
||||
|
||||
/** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */
|
||||
@@ -348,9 +371,11 @@ export async function getChannelWebhooks(channelID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
return RequestManager.get(
|
||||
const result = await RequestManager.get(
|
||||
endpoints.CHANNEL_WEBHOOKS(channelID),
|
||||
) as Promise<WebhookPayload[]>;
|
||||
);
|
||||
|
||||
return result as WebhookPayload[];
|
||||
}
|
||||
|
||||
interface EditChannelRequest {
|
||||
@@ -460,13 +485,15 @@ export async function editChannel(
|
||||
),
|
||||
};
|
||||
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.CHANNEL_BASE(channelID),
|
||||
{
|
||||
...payload,
|
||||
reason,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Follow a News Channel to send messages to a target channel. Requires the `MANAGE_WEBHOOKS` permission in the target channel. Returns the webhook id. */
|
||||
|
||||
@@ -3,8 +3,10 @@ import { DiscordBotGatewayData } from "../../types/mod.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Get the bots Gateway metadata that can help during the operation of large or sharded bots. */
|
||||
export function getGatewayBot() {
|
||||
return RequestManager.get(
|
||||
export async function getGatewayBot() {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GATEWAY_BOT,
|
||||
) as Promise<DiscordBotGatewayData>;
|
||||
);
|
||||
|
||||
return result as DiscordBotGatewayData;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import { botHasPermission, calculateBits } from "../../util/permissions.ts";
|
||||
import { formatImageURL, urlToBase64 } from "../../util/utils.ts";
|
||||
import { requestAllMembers } from "../../ws/shard_manager.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { Guild, Member, structures, Template } from "../structures/mod.ts";
|
||||
import { Guild, Member, structures } from "../structures/mod.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) {
|
||||
@@ -48,13 +48,16 @@ export async function createServer(options: CreateServerOptions) {
|
||||
endpoints.GUILDS,
|
||||
options,
|
||||
) as CreateGuildPayload;
|
||||
|
||||
return structures.createGuild(guild, 0);
|
||||
}
|
||||
|
||||
/** Delete a guild permanently. User must be owner. Returns 204 No Content on success. Fires a Guild Delete Gateway event.
|
||||
*/
|
||||
export function deleteServer(guildID: string) {
|
||||
return RequestManager.delete(endpoints.GUILDS_BASE(guildID));
|
||||
export async function deleteServer(guildID: string) {
|
||||
const result = await RequestManager.delete(endpoints.GUILDS_BASE(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Gets an array of all the channels ids that are the children of this category. */
|
||||
@@ -161,7 +164,12 @@ export async function deleteChannel(
|
||||
throw new Error(Errors.UPDATES_CHANNEL_CANNOT_BE_DELETED);
|
||||
}
|
||||
|
||||
return RequestManager.delete(endpoints.CHANNEL_BASE(channelID), { reason });
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_BASE(channelID),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of guild channel objects.
|
||||
@@ -172,6 +180,7 @@ export async function getChannels(guildID: string, addToCache = true) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_CHANNELS(guildID),
|
||||
) as ChannelCreatePayload[];
|
||||
|
||||
return Promise.all(result.map(async (res) => {
|
||||
const channel = await structures.createChannel(res, guildID);
|
||||
if (addToCache) {
|
||||
@@ -189,23 +198,28 @@ export async function getChannel(channelID: string, addToCache = true) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.CHANNEL_BASE(channelID),
|
||||
) as ChannelCreatePayload;
|
||||
|
||||
const channel = await structures.createChannel(result, result.guild_id);
|
||||
if (addToCache) await cacheHandlers.set("channels", channel.id, channel);
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */
|
||||
export function swapChannels(
|
||||
export async function swapChannels(
|
||||
guildID: string,
|
||||
channelPositions: PositionSwap[],
|
||||
) {
|
||||
if (channelPositions.length < 2) {
|
||||
throw "You must provide at least two channels to be swapped.";
|
||||
}
|
||||
return RequestManager.patch(
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_CHANNELS(guildID),
|
||||
channelPositions,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Edit the channel permission overwrites for a user or role in this channel. Requires `MANAGE_ROLES` permission. */
|
||||
@@ -223,7 +237,7 @@ export async function editChannelOverwrite(
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.put(
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_OVERWRITE(channelID, overwriteID),
|
||||
{
|
||||
allow: calculateBits(options.allow),
|
||||
@@ -231,6 +245,8 @@ export async function editChannelOverwrite(
|
||||
type: options.type,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete the channel permission overwrites for a user or role in this channel. Requires `MANAGE_ROLES` permission. */
|
||||
@@ -247,9 +263,11 @@ export async function deleteChannelOverwrite(
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_OVERWRITE(channelID, overwriteID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a guild member object for the specified user.
|
||||
@@ -268,7 +286,7 @@ export async function getMember(
|
||||
endpoints.GUILD_MEMBER(guildID, id),
|
||||
) as MemberCreatePayload;
|
||||
|
||||
return await structures.createMember(data, guildID);
|
||||
return structures.createMember(data, guildID);
|
||||
}
|
||||
|
||||
/** Returns guild member objects for the specified user by their nickname/username.
|
||||
@@ -304,11 +322,13 @@ export async function createEmoji(
|
||||
image = await urlToBase64(image);
|
||||
}
|
||||
|
||||
return RequestManager.post(endpoints.GUILD_EMOJIS(guildID), {
|
||||
const result = await RequestManager.post(endpoints.GUILD_EMOJIS(guildID), {
|
||||
...options,
|
||||
name,
|
||||
image,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */
|
||||
@@ -322,10 +342,15 @@ export async function editEmoji(
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
|
||||
return RequestManager.patch(endpoints.GUILD_EMOJI(guildID, id), {
|
||||
name: options.name,
|
||||
roles: options.roles,
|
||||
});
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_EMOJI(guildID, id),
|
||||
{
|
||||
name: options.name,
|
||||
roles: options.roles,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */
|
||||
@@ -339,10 +364,12 @@ export async function deleteEmoji(
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_EMOJI(guildID, id),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Creates a url to the emoji from the Discord CDN. */
|
||||
@@ -422,6 +449,7 @@ export async function createGuildRole(
|
||||
const role = await structures.createRole(roleData);
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
guild?.roles.set(role.id, role);
|
||||
|
||||
return role;
|
||||
}
|
||||
|
||||
@@ -436,12 +464,14 @@ export async function editRole(
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
|
||||
const result = await RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
|
||||
...options,
|
||||
permissions: options.permissions
|
||||
? calculateBits(options.permissions)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
|
||||
@@ -451,7 +481,9 @@ export async function deleteRole(guildID: string, id: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.delete(endpoints.GUILD_ROLE(guildID, id));
|
||||
const result = await RequestManager.delete(endpoints.GUILD_ROLE(guildID, id));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of role objects for the guild.
|
||||
@@ -464,7 +496,9 @@ export async function getRoles(guildID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_ROLES(guildID));
|
||||
const result = await RequestManager.get(endpoints.GUILD_ROLES(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */
|
||||
@@ -474,7 +508,12 @@ export async function swapRoles(guildID: string, rolePositons: PositionSwap) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.patch(endpoints.GUILD_ROLES(guildID), rolePositons);
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_ROLES(guildID),
|
||||
rolePositons,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */
|
||||
@@ -505,10 +544,12 @@ export async function pruneMembers(guildID: string, options: PruneOptions) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
const result = await RequestManager.post(
|
||||
endpoints.GUILD_PRUNE(guildID),
|
||||
{ ...options, include_roles: options.roles.join(",") },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -547,7 +588,7 @@ export async function getAuditLogs(
|
||||
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_AUDIT_LOGS(guildID), {
|
||||
const result = await RequestManager.get(endpoints.GUILD_AUDIT_LOGS(guildID), {
|
||||
...options,
|
||||
action_type: options.action_type
|
||||
? AuditLogs[options.action_type]
|
||||
@@ -556,6 +597,8 @@ export async function getAuditLogs(
|
||||
? options.limit
|
||||
: 50,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns the guild embed object. Requires the MANAGE_GUILD permission. */
|
||||
@@ -565,7 +608,9 @@ export async function getEmbed(guildID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_WIDGET(guildID));
|
||||
const result = await RequestManager.get(endpoints.GUILD_WIDGET(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */
|
||||
@@ -579,15 +624,19 @@ export async function editEmbed(
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_WIDGET(guildID),
|
||||
{ enabled, channel_id: channelID },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */
|
||||
export function getVanityURL(guildID: string) {
|
||||
return RequestManager.get(endpoints.GUILD_VANITY_URL(guildID));
|
||||
export async function getVanityURL(guildID: string) {
|
||||
const result = await RequestManager.get(endpoints.GUILD_VANITY_URL(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */
|
||||
@@ -597,7 +646,11 @@ export async function getIntegrations(guildID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_INTEGRATIONS(guildID));
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_INTEGRATIONS(guildID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */
|
||||
@@ -611,10 +664,12 @@ export async function editIntegration(
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_INTEGRATION(guildID, id),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */
|
||||
@@ -624,7 +679,11 @@ export async function deleteIntegration(guildID: string, id: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.delete(endpoints.GUILD_INTEGRATION(guildID, id));
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_INTEGRATION(guildID, id),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Sync an integration. Requires the MANAGE_GUILD permission. */
|
||||
@@ -634,7 +693,11 @@ export async function syncIntegration(guildID: string, id: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(guildID, id));
|
||||
const result = await RequestManager.post(
|
||||
endpoints.GUILD_INTEGRATION_SYNC(guildID, id),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
|
||||
@@ -660,9 +723,11 @@ export async function getBan(guildID: string, memberID: string) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
|
||||
return await RequestManager.get(
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_BAN(guildID, memberID),
|
||||
) as Promise<BannedUser>;
|
||||
);
|
||||
|
||||
return result as BannedUser;
|
||||
}
|
||||
|
||||
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */
|
||||
@@ -672,10 +737,12 @@ export async function ban(guildID: string, id: string, options: BanOptions) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
|
||||
return RequestManager.put(
|
||||
const result = await RequestManager.put(
|
||||
endpoints.GUILD_BAN(guildID, id),
|
||||
{ ...options, delete_message_days: options.days },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Remove the ban for a user. Requires BAN_MEMBERS permission */
|
||||
@@ -684,12 +751,17 @@ export async function unban(guildID: string, id: string) {
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
return RequestManager.delete(endpoints.GUILD_BAN(guildID, id));
|
||||
|
||||
const result = await RequestManager.delete(endpoints.GUILD_BAN(guildID, id));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns the guild preview object for the given id. If the bot is not in the guild, then the guild must be Discoverable. */
|
||||
export function getGuildPreview(guildID: string) {
|
||||
return RequestManager.get(endpoints.GUILD_PREVIEW(guildID));
|
||||
export async function getGuildPreview(guildID: string) {
|
||||
const result = await RequestManager.get(endpoints.GUILD_PREVIEW(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
|
||||
@@ -711,7 +783,12 @@ export async function editGuild(guildID: string, options: GuildEditOptions) {
|
||||
options.splash = await urlToBase64(options.splash);
|
||||
}
|
||||
|
||||
return RequestManager.patch(endpoints.GUILDS_BASE(guildID), options);
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILDS_BASE(guildID),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
|
||||
@@ -721,22 +798,30 @@ export async function getInvites(guildID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_INVITES(guildID));
|
||||
const result = await RequestManager.get(endpoints.GUILD_INVITES(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Leave a guild */
|
||||
export function leaveGuild(guildID: string) {
|
||||
return RequestManager.delete(endpoints.GUILD_LEAVE(guildID));
|
||||
export async function leaveGuild(guildID: string) {
|
||||
const result = await RequestManager.delete(endpoints.GUILD_LEAVE(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns an array of voice regions that can be used when creating servers. */
|
||||
export function getAvailableVoiceRegions() {
|
||||
return RequestManager.get(endpoints.VOICE_REGIONS);
|
||||
export async function getAvailableVoiceRegions() {
|
||||
const result = await RequestManager.get(endpoints.VOICE_REGIONS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when the guild is VIP-enabled. */
|
||||
export function getVoiceRegions(guildID: string) {
|
||||
return RequestManager.get(endpoints.GUILD_REGIONS(guildID));
|
||||
export async function getVoiceRegions(guildID: string) {
|
||||
const result = await RequestManager.get(endpoints.GUILD_REGIONS(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */
|
||||
@@ -749,12 +834,18 @@ export async function getWebhooks(guildID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
return RequestManager.get(endpoints.GUILD_WEBHOOKS(guildID));
|
||||
const result = await RequestManager.get(endpoints.GUILD_WEBHOOKS(guildID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** This function will return the raw user payload in the rare cases you need to fetch a user directly from the API. */
|
||||
export function getUser(userID: string) {
|
||||
return RequestManager.get(endpoints.USER(userID)) as Promise<UserPayload>;
|
||||
export async function getUser(userID: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.USER(userID),
|
||||
);
|
||||
|
||||
return result as UserPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -764,11 +855,13 @@ export function getUser(userID: string) {
|
||||
* This function fetches a guild's data. This is not the same data as a GUILD_CREATE.
|
||||
* So it does not cache the guild, you must do it manually.
|
||||
* */
|
||||
export function getGuild(guildID: string, counts = true) {
|
||||
return RequestManager.get(
|
||||
export async function getGuild(guildID: string, counts = true) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILDS_BASE(guildID),
|
||||
{ with_counts: counts },
|
||||
) as Promise<UpdateGuildPayload>;
|
||||
);
|
||||
|
||||
return result as UpdateGuildPayload;
|
||||
}
|
||||
|
||||
/** Returns the guild template if it exists */
|
||||
@@ -777,6 +870,7 @@ export async function getTemplate(templateCode: string) {
|
||||
endpoints.GUILD_TEMPLATE(templateCode),
|
||||
) as GuildTemplate;
|
||||
const template = await structures.createTemplate(result);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
@@ -809,10 +903,12 @@ export async function createGuildFromTemplate(
|
||||
data.icon = await urlToBase64(data.icon);
|
||||
}
|
||||
|
||||
return await RequestManager.post(
|
||||
const result = await await RequestManager.post(
|
||||
endpoints.GUILD_TEMPLATE(templateCode),
|
||||
data,
|
||||
) as Promise<CreateGuildPayload>;
|
||||
);
|
||||
|
||||
return result as CreateGuildPayload;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -826,6 +922,7 @@ export async function getGuildTemplates(guildID: string) {
|
||||
const templates = await RequestManager.get(
|
||||
endpoints.GUILD_TEMPLATES(guildID),
|
||||
) as GuildTemplate[];
|
||||
|
||||
return templates.map((template) => structures.createTemplate(template));
|
||||
}
|
||||
|
||||
@@ -843,6 +940,7 @@ export async function deleteGuildTemplate(
|
||||
const deletedTemplate = await RequestManager.delete(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
) as GuildTemplate;
|
||||
|
||||
return structures.createTemplate(deletedTemplate);
|
||||
}
|
||||
|
||||
@@ -874,6 +972,7 @@ export async function createGuildTemplate(
|
||||
endpoints.GUILD_TEMPLATES(guildID),
|
||||
data,
|
||||
) as GuildTemplate;
|
||||
|
||||
return structures.createTemplate(template);
|
||||
}
|
||||
|
||||
@@ -888,6 +987,7 @@ export async function syncGuildTemplate(guildID: string, templateCode: string) {
|
||||
const template = await RequestManager.put(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
) as GuildTemplate;
|
||||
|
||||
return structures.createTemplate(template);
|
||||
}
|
||||
|
||||
@@ -918,5 +1018,6 @@ export async function editGuildTemplate(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
data,
|
||||
) as GuildTemplate;
|
||||
|
||||
return structures.createTemplate(template);
|
||||
}
|
||||
|
||||
@@ -75,10 +75,12 @@ export async function addRole(
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.put(
|
||||
const result = await RequestManager.put(
|
||||
endpoints.GUILD_MEMBER_ROLE(guildID, memberID, roleID),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Remove a role from the member */
|
||||
@@ -109,10 +111,12 @@ export async function removeRole(
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_MEMBER_ROLE(guildID, memberID, roleID),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Send a message to a users DM. Note: this takes 2 API calls. 1 is to fetch the users dm channel. 2 is to send a message to that channel. */
|
||||
@@ -157,10 +161,12 @@ export async function kick(guildID: string, memberID: string, reason?: string) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_MEMBER(guildID, memberID),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Edit the member */
|
||||
@@ -220,10 +226,12 @@ export async function editMember(
|
||||
|
||||
// TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel
|
||||
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_MEMBER(guildID, memberID),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,13 +276,15 @@ export async function editBotProfile(username?: string, botAvatarURL?: string) {
|
||||
}
|
||||
|
||||
const avatar = botAvatarURL ? await urlToBase64(botAvatarURL) : undefined;
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.USER_BOT,
|
||||
{
|
||||
username: username?.trim(),
|
||||
avatar,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Edit the nickname of the bot in this guild */
|
||||
|
||||
@@ -24,10 +24,12 @@ export async function deleteMessageByID(
|
||||
|
||||
if (delayMilliseconds) await delay(delayMilliseconds);
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE(channelID, messageID),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete a message */
|
||||
@@ -51,10 +53,12 @@ export async function deleteMessage(
|
||||
|
||||
if (delayMilliseconds) await delay(delayMilliseconds);
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE(message.channelID, message.id),
|
||||
{ reason },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */
|
||||
@@ -69,7 +73,11 @@ export async function pin(channelID: string, messageID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
|
||||
return RequestManager.put(endpoints.CHANNEL_PIN(channelID, messageID));
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_PIN(channelID, messageID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */
|
||||
@@ -84,9 +92,11 @@ export async function unpin(channelID: string, messageID: string) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_PIN(channelID, messageID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Create a reaction for the message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */
|
||||
@@ -119,15 +129,18 @@ export async function addReaction(
|
||||
reaction = reaction.substring(3, reaction.length - 1);
|
||||
}
|
||||
|
||||
return RequestManager.put(
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(
|
||||
channelID,
|
||||
messageID,
|
||||
reaction,
|
||||
),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: add a return?
|
||||
/** Adds multiple reactions to a message. If `ordered` is true(default is false), it will add the reactions one at a time in the order provided. Note: Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */
|
||||
export async function addReactions(
|
||||
channelID: string,
|
||||
@@ -147,7 +160,7 @@ export async function addReactions(
|
||||
}
|
||||
|
||||
/** Removes a reaction from the bot on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
|
||||
export function removeReaction(
|
||||
export async function removeReaction(
|
||||
channelID: string,
|
||||
messageID: string,
|
||||
reaction: string,
|
||||
@@ -158,13 +171,15 @@ export function removeReaction(
|
||||
reaction = reaction.substring(3, reaction.length - 1);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(
|
||||
channelID,
|
||||
messageID,
|
||||
reaction,
|
||||
),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Removes a reaction from the specified user on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
|
||||
@@ -188,7 +203,7 @@ export async function removeUserReaction(
|
||||
reaction = reaction.substring(3, reaction.length - 1);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_USER(
|
||||
channelID,
|
||||
messageID,
|
||||
@@ -196,6 +211,8 @@ export async function removeUserReaction(
|
||||
userID,
|
||||
),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Removes all reactions for all emojis on this message. */
|
||||
@@ -209,9 +226,12 @@ export async function removeAllReactions(channelID: string, messageID: string) {
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
return RequestManager.delete(
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTIONS(channelID, messageID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
|
||||
@@ -236,9 +256,11 @@ export async function removeReactionEmoji(
|
||||
reaction = reaction.substring(3, reaction.length - 1);
|
||||
}
|
||||
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION(channelID, messageID, reaction),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get a list of users that reacted with this emoji. */
|
||||
@@ -295,6 +317,7 @@ export async function editMessage(
|
||||
endpoints.CHANNEL_MESSAGE(message.channelID, message.id),
|
||||
content,
|
||||
);
|
||||
|
||||
return structures.createMessage(result as MessageCreateOptions);
|
||||
}
|
||||
|
||||
|
||||
@@ -120,6 +120,7 @@ import {
|
||||
getSlashCommands,
|
||||
getWebhook,
|
||||
upsertSlashCommand,
|
||||
upsertSlashCommands,
|
||||
} from "./webhook.ts";
|
||||
|
||||
export let handlers = {
|
||||
@@ -241,6 +242,7 @@ export let handlers = {
|
||||
getSlashCommand,
|
||||
getSlashCommands,
|
||||
upsertSlashCommand,
|
||||
upsertSlashCommands,
|
||||
editSlashCommand,
|
||||
deleteSlashCommand,
|
||||
executeSlashCommand,
|
||||
|
||||
@@ -3,8 +3,10 @@ import { OAuthApplication } from "../../types/oauth.ts";
|
||||
import { endpoints } from "../../util/constants.ts";
|
||||
|
||||
/** Returns the bot's OAuth2 application object without `flags`. */
|
||||
export function getApplicationInformation() {
|
||||
return RequestManager.get(endpoints.OAUTH2_APPLICATION) as Promise<
|
||||
OAuthApplication
|
||||
>;
|
||||
export async function getApplicationInformation() {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.OAUTH2_APPLICATION,
|
||||
);
|
||||
|
||||
return result as OAuthApplication;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
MessageCreateOptions,
|
||||
SlashCommand,
|
||||
UpsertSlashCommandOptions,
|
||||
UpsertSlashCommandsOptions,
|
||||
WebhookCreateOptions,
|
||||
WebhookEditOptions,
|
||||
WebhookPayload,
|
||||
} from "../../types/mod.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
@@ -48,13 +50,98 @@ export async function createWebhook(
|
||||
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
const result = await RequestManager.post(
|
||||
endpoints.CHANNEL_WEBHOOKS(channelID),
|
||||
{
|
||||
...options,
|
||||
avatar: options.avatar ? await urlToBase64(options.avatar) : undefined,
|
||||
},
|
||||
) as Promise<WebhookPayload>;
|
||||
);
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
|
||||
/** Edit a webhook. Requires the `MANAGE_WEBHOOKS` permission. Returns the updated webhook object on success. */
|
||||
export async function editWebhook(
|
||||
channelID: string,
|
||||
webhookID: string,
|
||||
options: WebhookEditOptions,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
const result = await RequestManager.patch(endpoints.WEBHOOK_ID(webhookID), {
|
||||
...options,
|
||||
channel_id: options.channelID,
|
||||
});
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
|
||||
/** Edit a webhook. Returns the updated webhook object on success. */
|
||||
export async function editWebhookWithToken(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
options: Omit<WebhookEditOptions, "channelID">,
|
||||
) {
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.WEBHOOK(webhookID, webhookToken),
|
||||
options,
|
||||
);
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
|
||||
/** Delete a webhook permanently. Requires the `MANAGE_WEBHOOKS` permission. Returns a undefined on success */
|
||||
export async function deleteWebhook(channelID: string, webhookID: string) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
|
||||
const result = await RequestManager.delete(endpoints.WEBHOOK_ID(webhookID));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Delete a webhook permanently. Returns a undefined on success */
|
||||
export async function deleteWebhookWithToken(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
) {
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.WEBHOOK(webhookID, webhookToken),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Returns the new webhook object for the given id. */
|
||||
export async function getWebhook(webhookID: string) {
|
||||
const result = await RequestManager.get(endpoints.WEBHOOK_ID(webhookID));
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
|
||||
/** Returns the new webhook object for the given id, this call does not require authentication and returns no user in the webhook object. */
|
||||
export async function getWebhookWithToken(webhookID: string, token: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.WEBHOOK(webhookID, token),
|
||||
);
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
|
||||
/** Execute a webhook with webhook ID and webhook token */
|
||||
@@ -116,12 +203,7 @@ export async function executeWebhook(
|
||||
return structures.createMessage(result as MessageCreateOptions);
|
||||
}
|
||||
|
||||
/** Returns the new webhook object for the given id. */
|
||||
export function getWebhook(webhookID: string) {
|
||||
return RequestManager.get(endpoints.WEBHOOK_ID(webhookID));
|
||||
}
|
||||
|
||||
export function editWebhookMessage(
|
||||
export async function editWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
@@ -167,20 +249,24 @@ export function editWebhookMessage(
|
||||
}
|
||||
}
|
||||
|
||||
return RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.WEBHOOK_MESSAGE(webhookID, webhookToken, messageID),
|
||||
{ ...options, allowed_mentions: options.allowed_mentions },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function deleteWebhookMessage(
|
||||
export async function deleteWebhookMessage(
|
||||
webhookID: string,
|
||||
webhookToken: string,
|
||||
messageID: string,
|
||||
) {
|
||||
return RequestManager.delete(
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.WEBHOOK_MESSAGE(webhookID, webhookToken, messageID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,7 +280,7 @@ export function deleteWebhookMessage(
|
||||
* 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) {
|
||||
export async 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);
|
||||
@@ -206,7 +292,7 @@ export function createSlashCommand(options: CreateSlashCommandOptions) {
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
return RequestManager.post(
|
||||
const result = await RequestManager.post(
|
||||
options.guildID
|
||||
? endpoints.COMMANDS_GUILD(applicationID, options.guildID)
|
||||
: endpoints.COMMANDS(applicationID),
|
||||
@@ -214,6 +300,8 @@ export function createSlashCommand(options: CreateSlashCommandOptions) {
|
||||
...options,
|
||||
},
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Fetchs the global command for the given ID. If a guildID is provided, the guild command will be fetched. */
|
||||
@@ -228,19 +316,21 @@ export async function getSlashCommand(commandID: string, guildID?: string) {
|
||||
}
|
||||
|
||||
/** Fetch all of the global commands for your application. */
|
||||
export function getSlashCommands(guildID?: string) {
|
||||
export async function getSlashCommands(guildID?: string) {
|
||||
// TODO: Should this be a returned as a collection?
|
||||
return RequestManager.get(
|
||||
const result = await RequestManager.get(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD(applicationID, guildID)
|
||||
: endpoints.COMMANDS(applicationID),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit an existing slash command. If this command did not exist, it will create it.
|
||||
*/
|
||||
export function upsertSlashCommand(
|
||||
export async function upsertSlashCommand(
|
||||
commandID: string,
|
||||
options: UpsertSlashCommandOptions,
|
||||
guildID?: string,
|
||||
@@ -256,7 +346,7 @@ export function upsertSlashCommand(
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
const result = RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(
|
||||
applicationID,
|
||||
@@ -270,12 +360,46 @@ export function upsertSlashCommand(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk edit existing slash commands. If a command does not exist, it will create it.
|
||||
*
|
||||
* **NOTE:** Any slash commands that are not specified in this function will be **deleted**. If you don't provide the commandID and rename your command, the command gets a new ID.
|
||||
*/
|
||||
export async function upsertSlashCommands(
|
||||
options: UpsertSlashCommandsOptions[],
|
||||
guildID?: string,
|
||||
) {
|
||||
const data = options.map((option) => {
|
||||
// Use ... for content length due to unicode characters and js .length handling
|
||||
if ([...option.name].length < 2 || [...option.name].length > 32) {
|
||||
throw new Error(Errors.INVALID_SLASH_NAME);
|
||||
}
|
||||
|
||||
if (
|
||||
[...option.description].length < 1 || [...option.description].length > 100
|
||||
) {
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
return option;
|
||||
});
|
||||
|
||||
const result = await RequestManager.put(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD(applicationID, guildID)
|
||||
: endpoints.COMMANDS(applicationID),
|
||||
data,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: remove this function for v11
|
||||
/**
|
||||
* Edit an existing slash command.
|
||||
* @deprecated This function will be removed in v11. Use `upsertSlashCommand()` instead
|
||||
*/
|
||||
export function editSlashCommand(
|
||||
export async function editSlashCommand(
|
||||
commandID: string,
|
||||
options: EditSlashCommandOptions,
|
||||
guildID?: string,
|
||||
@@ -291,7 +415,7 @@ export function editSlashCommand(
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
const result = RequestManager.patch(
|
||||
const result = await RequestManager.patch(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(
|
||||
applicationID,
|
||||
@@ -321,7 +445,7 @@ export function deleteSlashCommand(id: string, guildID?: string) {
|
||||
*
|
||||
* NOTE: By default we will suppress mentions. To enable mentions, just pass any mentions object.
|
||||
*/
|
||||
export function executeSlashCommand(
|
||||
export async function executeSlashCommand(
|
||||
id: string,
|
||||
token: string,
|
||||
options: ExecuteSlashCommandOptions,
|
||||
@@ -345,33 +469,83 @@ export function executeSlashCommand(
|
||||
options.data.allowed_mentions = { parse: [] };
|
||||
}
|
||||
|
||||
return RequestManager.post(endpoints.INTERACTION_ID_TOKEN(id, token), {
|
||||
...options,
|
||||
});
|
||||
const result = await RequestManager.post(
|
||||
endpoints.INTERACTION_ID_TOKEN(id, token),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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(
|
||||
export async function deleteSlashResponse(
|
||||
token: string,
|
||||
messageID?: string,
|
||||
) {
|
||||
if (!messageID) {
|
||||
return RequestManager.delete(
|
||||
endpoints.INTERACTION_ORIGINAL_ID_TOKEN(applicationID, token),
|
||||
);
|
||||
}
|
||||
return RequestManager.delete(
|
||||
endpoints.INTERACTION_ID_TOKEN_MESSAGEID(applicationID, token, messageID),
|
||||
const result = await RequestManager.delete(
|
||||
messageID
|
||||
? endpoints.INTERACTION_ID_TOKEN_MESSAGEID(
|
||||
applicationID,
|
||||
token,
|
||||
messageID,
|
||||
)
|
||||
: endpoints.INTERACTION_ORIGINAL_ID_TOKEN(applicationID, token),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 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(
|
||||
export async function editSlashResponse(
|
||||
token: string,
|
||||
options: EditSlashResponseOptions,
|
||||
) {
|
||||
return RequestManager.patch(
|
||||
endpoints.INTERACTION_ORIGINAL_ID_TOKEN(applicationID, token),
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
options.messageID
|
||||
? endpoints.WEBHOOK_MESSAGE(applicationID, token, options.messageID)
|
||||
: endpoints.INTERACTION_ORIGINAL_ID_TOKEN(applicationID, token),
|
||||
options,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ export async function createGuild(data: CreateGuildPayload, shardID: number) {
|
||||
|
||||
initialMemberLoadQueue.set(guild.id, members);
|
||||
|
||||
return guild;
|
||||
return guild as Guild;
|
||||
}
|
||||
|
||||
export interface Guild {
|
||||
|
||||
@@ -126,7 +126,7 @@ export async function createMember(data: MemberCreatePayload, guildID: string) {
|
||||
|
||||
await cacheHandlers.set("members", member.id, member);
|
||||
|
||||
return member;
|
||||
return member as Member;
|
||||
}
|
||||
|
||||
export interface Member {
|
||||
|
||||
@@ -40,7 +40,7 @@ export function createTemplate(
|
||||
sourceGuildID: createNewProp(sourceGuildID),
|
||||
serializedSourceGuild: createNewProp(serializedSourceGuild),
|
||||
isDirty: createNewProp(isDirty),
|
||||
});
|
||||
}) as Template;
|
||||
}
|
||||
|
||||
export interface Template {
|
||||
|
||||
@@ -423,5 +423,16 @@ function processHeaders(url: string, headers: Headers) {
|
||||
}
|
||||
}
|
||||
|
||||
if (ratelimited) {
|
||||
eventHandlers.rateLimit?.({
|
||||
remaining,
|
||||
bucketID,
|
||||
global,
|
||||
resetTimestamp,
|
||||
retryAfter,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
return ratelimited ? bucketID : undefined;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,23 @@ export interface DebugArg {
|
||||
data: unknown;
|
||||
}
|
||||
|
||||
interface RateLimitData {
|
||||
/** The number of remaining requests that can be made */
|
||||
remaining: string | null;
|
||||
/** Epoch time (seconds since 00:00:00 UTC on January 1, 1970) at which the rate limit resets */
|
||||
resetTimestamp: string | null;
|
||||
/** Total time (in seconds) of when the current rate limit bucket will reset. Can have decimals to match previous millisecond ratelimit precision */
|
||||
retryAfter: string | null;
|
||||
/** Returned only on a HTTP 429 response if the rate limit headers returned are of the global rate limit (not per-route) */
|
||||
global: string | null;
|
||||
/** A unique string denoting the rate limit being encountered (non-inclusive of major parameters in the route path) */
|
||||
bucketID: string | null;
|
||||
/** The URL the HTTP request is made to */
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface EventHandlers {
|
||||
rateLimit?: (data: RateLimitData) => unknown;
|
||||
applicationCommandCreate?: (data: Application) => unknown;
|
||||
/** Sent when properties about the user change. */
|
||||
botUpdate?: (user: UserPayload) => unknown;
|
||||
@@ -126,7 +142,9 @@ export interface EventHandlers {
|
||||
) => unknown;
|
||||
heartbeat?: () => unknown;
|
||||
/** Sent when a user in a guild uses a Slash Command. */
|
||||
interactionCreate?: (data: InteractionCommandPayload) => unknown;
|
||||
interactionCreate?: (
|
||||
data: Omit<InteractionCommandPayload, "member"> & { member: Member },
|
||||
) => unknown;
|
||||
/** Sent when a message is created. */
|
||||
messageCreate?: (message: Message) => unknown;
|
||||
/** Sent when a message is deleted. */
|
||||
|
||||
@@ -36,6 +36,15 @@ export interface WebhookCreateOptions {
|
||||
avatar?: string;
|
||||
}
|
||||
|
||||
export interface WebhookEditOptions {
|
||||
/** Name of the webhook (1-80 characters) */
|
||||
name?: string;
|
||||
/** Image url for avatar image for the default webhook avatar */
|
||||
avatar?: string | null;
|
||||
/** The new channel id this webhook should be moved to */
|
||||
channelID?: string;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -228,3 +237,14 @@ export interface UpsertSlashCommandOptions {
|
||||
/** The parameters for the command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
export interface UpsertSlashCommandsOptions {
|
||||
/** The id of the command */
|
||||
id: string;
|
||||
/** 3-32 character command name */
|
||||
name: string;
|
||||
/** 1-100 character description */
|
||||
description: string;
|
||||
/** The parameters for the command */
|
||||
options?: SlashCommandOption[];
|
||||
}
|
||||
|
||||
@@ -55,19 +55,14 @@ export async function botHasPermission(
|
||||
const member = await cacheHandlers.get("members", botID);
|
||||
if (!member) return false;
|
||||
|
||||
const guild = member.guild(guildID);
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return false;
|
||||
|
||||
// The owner of a guild has all permissions, therefore, if the bot is the owner of the guild, permissions do not need to be inspected.
|
||||
// Check if the bot is the owner of the guild, if it is, returns true
|
||||
if (guild.ownerID === botID) return true;
|
||||
|
||||
const guildMember = member.guilds.get(
|
||||
guildID,
|
||||
);
|
||||
if (!guildMember) return false;
|
||||
|
||||
// The everyone role is not in member.roles
|
||||
const permissionBits = [...guildMember.roles, guild.id]
|
||||
const permissionBits = [...member.guilds.get(guildID)?.roles || [], guild.id]
|
||||
.map((id) => guild.roles.get(id)!)
|
||||
// Remove any edge case undefined
|
||||
.filter((r) => r)
|
||||
|
||||
@@ -60,3 +60,57 @@ export const formatImageURL = (
|
||||
return `${url}.${format ||
|
||||
(url.includes("/a_") ? "gif" : "jpg")}?size=${size}`;
|
||||
};
|
||||
|
||||
function camelToSnakeCase(text: string) {
|
||||
return text.replace(/ID|[A-Z]/g, ($1) => {
|
||||
if ($1 === "ID") return "_id";
|
||||
return `_${$1.toLowerCase()}`;
|
||||
});
|
||||
}
|
||||
|
||||
function snakeToCamelCase(text: string) {
|
||||
return text.replace(/_id|([-_][a-z])/ig, ($1) => {
|
||||
if ($1 === "_id") return "ID";
|
||||
return $1.toUpperCase().replace("_", "");
|
||||
});
|
||||
}
|
||||
|
||||
function isObject(obj: unknown) {
|
||||
return obj === Object(obj) && !Array.isArray(obj) &&
|
||||
typeof obj !== "function";
|
||||
}
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export function camelKeysToSnakeCase(obj: Record<string, any>) {
|
||||
if (isObject(obj)) {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const convertedObject: Record<string, any> = {};
|
||||
Object.keys(obj)
|
||||
.forEach((key) => {
|
||||
convertedObject[camelToSnakeCase(key)] = camelKeysToSnakeCase(
|
||||
obj[key],
|
||||
);
|
||||
});
|
||||
return convertedObject;
|
||||
} else if (Array.isArray(obj)) {
|
||||
obj = obj.map((element) => camelKeysToSnakeCase(element));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export function snakeKeysToCamelCase(obj: Record<string, any>) {
|
||||
if (isObject(obj)) {
|
||||
// deno-lint-ignore no-explicit-any
|
||||
const convertedObject: Record<string, any> = {};
|
||||
Object.keys(obj)
|
||||
.forEach((key) => {
|
||||
convertedObject[snakeToCamelCase(key)] = snakeKeysToCamelCase(
|
||||
obj[key],
|
||||
);
|
||||
});
|
||||
return convertedObject;
|
||||
} else if (Array.isArray(obj)) {
|
||||
obj = obj.map((element) => snakeKeysToCamelCase(element));
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -248,8 +248,8 @@ Deno.test({
|
||||
|
||||
Deno.test({
|
||||
name: "[message] pin a message in a channel",
|
||||
fn() {
|
||||
pin(tempData.channelID, tempData.messageID);
|
||||
async fn() {
|
||||
await pin(tempData.channelID, tempData.messageID);
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
@@ -269,18 +269,18 @@ Deno.test({
|
||||
|
||||
Deno.test({
|
||||
name: "[message] unpin a message",
|
||||
fn() {
|
||||
unpin(tempData.channelID, tempData.messageID);
|
||||
async fn() {
|
||||
await unpin(tempData.channelID, tempData.messageID);
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[message] add a reaction to a message",
|
||||
fn() {
|
||||
async fn() {
|
||||
// TODO: add tests for a guild emoji ― <:name:id>
|
||||
|
||||
addReaction(tempData.channelID, tempData.messageID, "👍");
|
||||
await addReaction(tempData.channelID, tempData.messageID, "👍");
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
@@ -289,8 +289,8 @@ Deno.test({
|
||||
|
||||
Deno.test({
|
||||
name: "[message] remove a reaction to a message",
|
||||
fn() {
|
||||
removeReaction(tempData.channelID, tempData.messageID, "👍");
|
||||
async fn() {
|
||||
await removeReaction(tempData.channelID, tempData.messageID, "👍");
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
@@ -299,31 +299,31 @@ Deno.test({
|
||||
|
||||
Deno.test({
|
||||
name: "[message] delete a message by channel ID",
|
||||
fn() {
|
||||
deleteMessageByID(tempData.channelID, tempData.messageID);
|
||||
async fn() {
|
||||
await deleteMessageByID(tempData.channelID, tempData.messageID);
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[channel] delete a channel in a guild",
|
||||
fn() {
|
||||
deleteChannel(tempData.guildID, tempData.channelID);
|
||||
async fn() {
|
||||
await deleteChannel(tempData.guildID, tempData.channelID);
|
||||
},
|
||||
...defaultTestOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[role] delete a role in a guild",
|
||||
fn() {
|
||||
deleteRole(tempData.guildID, tempData.roleID);
|
||||
async fn() {
|
||||
await deleteRole(tempData.guildID, tempData.roleID);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[guild] delete a guild",
|
||||
fn() {
|
||||
deleteServer(tempData.guildID);
|
||||
async fn() {
|
||||
await deleteServer(tempData.guildID);
|
||||
|
||||
// TODO(ayntee): remove this weird shit lol
|
||||
// TODO(ayntee): check if the GUILD_DELETE event is fired
|
||||
|
||||
76
test/util/utils.test.ts
Normal file
76
test/util/utils.test.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import {
|
||||
camelKeysToSnakeCase,
|
||||
snakeKeysToCamelCase,
|
||||
} from "../../src/util/utils.ts";
|
||||
import { assertEquals } from "../deps.ts";
|
||||
|
||||
const testSnakeObject = {
|
||||
// deno-lint-ignore camelcase
|
||||
hello_world: "hello_world",
|
||||
// deno-lint-ignore camelcase
|
||||
the_universe: {
|
||||
blue_planet: {
|
||||
water: "is_blue",
|
||||
dirt: "isDirty",
|
||||
},
|
||||
moon: {
|
||||
earth_moon: {
|
||||
is_round: true,
|
||||
},
|
||||
other_moon: {
|
||||
is_round: 0,
|
||||
},
|
||||
},
|
||||
arrays: ["one_two", { moo_cow: { boo: true } }],
|
||||
test_the_id: "123123123123",
|
||||
},
|
||||
};
|
||||
|
||||
const testCamelObject = {
|
||||
helloWorld: "hello_world",
|
||||
theUniverse: {
|
||||
bluePlanet: {
|
||||
water: "is_blue",
|
||||
dirt: "isDirty",
|
||||
},
|
||||
moon: {
|
||||
earthMoon: {
|
||||
isRound: true,
|
||||
},
|
||||
otherMoon: {
|
||||
isRound: 0,
|
||||
},
|
||||
},
|
||||
arrays: ["one_two", { mooCow: { boo: true } }],
|
||||
testTheID: "123123123123",
|
||||
},
|
||||
};
|
||||
|
||||
const someOther = {
|
||||
helloWorld: 1,
|
||||
};
|
||||
|
||||
const someElseOther = {
|
||||
// deno-lint-ignore camelcase
|
||||
hello_world: 1,
|
||||
};
|
||||
|
||||
Deno.test({
|
||||
name: "[utils] snakeKeysToCamelCase: assert convertion",
|
||||
fn() {
|
||||
const result = snakeKeysToCamelCase(testSnakeObject);
|
||||
assertEquals(result, testCamelObject);
|
||||
const resultTwo = snakeKeysToCamelCase(someOther);
|
||||
assertEquals(resultTwo, someOther);
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "[utils] camelKeysToSnakeCase: assert convertion",
|
||||
fn() {
|
||||
const result = camelKeysToSnakeCase(testCamelObject);
|
||||
assertEquals(result, testSnakeObject);
|
||||
const resultTwo = camelKeysToSnakeCase(someElseOther);
|
||||
assertEquals(resultTwo, someElseOther);
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user