mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-04 01:40:08 +00:00
Merge pull request #159 from Skillz4Killz/rename-permission_overwrites
Rename permission overwrites
This commit is contained in:
13
.github/workflows/greetings.yml
vendored
13
.github/workflows/greetings.yml
vendored
@@ -1,13 +0,0 @@
|
||||
name: Greetings
|
||||
|
||||
on: [pull_request, issues]
|
||||
|
||||
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 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"
|
||||
17
.github/workflows/test.yml
vendored
Normal file
17
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: Test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: denolib/setup-deno@master
|
||||
- name: Cache dependencies
|
||||
run: deno cache mod.ts
|
||||
- name: Run test script
|
||||
run: deno test -A
|
||||
env:
|
||||
DISCORD_TOKEN: ${{ secrets.DISCORD_TOKEN }}
|
||||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -4,5 +4,8 @@
|
||||
"deno.import_intellisense_origins": {
|
||||
"https://deno.land": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": true
|
||||
},
|
||||
"editor.defaultFormatter": "denoland.vscode-deno"
|
||||
}
|
||||
|
||||
38
README.md
38
README.md
@@ -2,15 +2,19 @@
|
||||
|
||||
> Discord API library wrapper in Deno
|
||||
|
||||
[](https://discord.gg/J4NqJ72)
|
||||
[](https://discord.gg/J4NqJ72)
|
||||

|
||||
[](https://nest.land/package/Discordeno)
|
||||
[](https://nest.land/package/Discordeno)
|
||||
|
||||
[Website](https://discordeno.netlify.app)
|
||||
[WIP] 
|
||||
|
||||
## Beginner Developers
|
||||
## Why Discordeno?
|
||||
|
||||
Don't worry a lot of developers start out coding their first projects as a Discord bot(I did 😉) and it is not so easy. With Discordeno, I tried to build it in a way that solved all the headaches I had when first starting out coding bots. If you are a beginner developer, please use a boilerplate: The official one is at: [GitHub](https://github.com/Skillz4Killz/Discordeno-bot-template) but there will be more listed on the website. It is a beautiful website indeed! Check it out!
|
||||
### Beginner Developers
|
||||
|
||||
Don't worry a lot of developers start out coding their first projects as a Discord bot (I did 😉) and it is not so easy to do so. Discordeno is built considering all the issues wit pre-existing libraries, such as discord.js, and issues that I had when I first started out coding bots.
|
||||
|
||||
If you are a beginner developer, please use this official boilerplate: [GitHub](https://github.com/Skillz4Killz/Discordeno-bot-template) but there will be more listed on the website. It is a beautiful website indeed! Check it out!
|
||||
|
||||
**Modular commands, arguments, events, inhibitors, monitors, tasks.**
|
||||
|
||||
@@ -41,15 +45,10 @@ Don't worry a lot of developers start out coding their first projects as a Disco
|
||||
- Uses i18next, one of the best localization tools available.
|
||||
- Supports nested folders to keep cleaner translation files
|
||||
|
||||
**Hot Reloadable**
|
||||
- **Hot Reloadable**: Easily update your code without having to restart the bot everytime.
|
||||
- **Step By Step Guide**: There is a step by step walkthrough to learn how to create Discord bots with Discordeno on our website!
|
||||
|
||||
- Easily update your code without having to restart the bot everytime.
|
||||
|
||||
**Step By Step Guide**
|
||||
|
||||
- There is a step by step walkthrough to learn how to create Discord bots with Discordeno on our website!
|
||||
|
||||
## Advanced Developers
|
||||
### Advanced Developers
|
||||
|
||||
The instructions below are meant for advanced developers!
|
||||
|
||||
@@ -64,7 +63,7 @@ StartBot({
|
||||
intents: [Intents.GUILD_MESSAGES, Intents.GUILDS],
|
||||
eventHandlers: {
|
||||
ready: () => {
|
||||
console.log(`Logged!`);
|
||||
console.log('Successfully connected to gateway');
|
||||
},
|
||||
messageCreate: (message) => {
|
||||
if (message.content === "!ping") {
|
||||
@@ -77,8 +76,13 @@ StartBot({
|
||||
|
||||
Alternatively, you can use boilerplate template repositories that were created by wonderful developers. Review the list on the website, and add any of yours if you make your own.
|
||||
|
||||

|
||||
## Documentation
|
||||
|
||||
#### Dark Mode
|
||||
- [API Documentation](https://doc.deno.land/https/deno.land/x/discordeno/mod.ts)
|
||||
- [Guide](https://discordeno.netlify.com)
|
||||
- [Support server](https://discord.gg/J4NqJ72)
|
||||
- [Contributing Guide](https://github.com/Skillz4Killz/Discordeno/blob/master/.github/CONTRIBUTING.md)
|
||||
|
||||

|
||||
## License
|
||||
|
||||
MIT © Skillz4Killz
|
||||
|
||||
5
deps.ts
5
deps.ts
@@ -7,4 +7,9 @@ export {
|
||||
isWebSocketPongEvent,
|
||||
} from "https://deno.land/std@0.67.0/ws/mod.ts";
|
||||
export type { WebSocket } from "https://deno.land/std@0.67.0/ws/mod.ts";
|
||||
export {
|
||||
assert,
|
||||
assertArrayIncludes,
|
||||
assertEquals,
|
||||
} from "https://deno.land/std@0.75.0/testing/asserts.ts";
|
||||
export { decompress_with as inflate } from "https://unpkg.com/@evan/wasm@0.0.11/target/zlib/deno.js";
|
||||
|
||||
@@ -54,43 +54,44 @@ const nekosEndpoints = [
|
||||
{ name: "gecg", path: "/img/gecg", nsfw: false },
|
||||
{ name: "avatar", path: "/img/avatar", nsfw: false },
|
||||
{ name: "waifu", path: "/img/waifu", nsfw: false },
|
||||
{ name: "randomHentaiGif", path: "/img/Random_hentai_gif", nsfw: true },
|
||||
{ name: "pussy", path: "/img/pussy", nsfw: true },
|
||||
{ name: "nekoGif", path: "/img/nsfw_neko_gif", nsfw: true },
|
||||
{ name: "neko", path: "/img/lewd", nsfw: true },
|
||||
{ name: "lesbian", path: "/img/les", nsfw: true },
|
||||
{ name: "kuni", path: "/img/kuni", nsfw: true },
|
||||
{ name: "cumsluts", path: "/img/cum", nsfw: true },
|
||||
{ name: "classic", path: "/img/classic", nsfw: true },
|
||||
{ name: "boobs", path: "/img/boobs", nsfw: true },
|
||||
{ name: "bJ", path: "/img/bj", nsfw: true },
|
||||
{ name: "anal", path: "/img/anal", nsfw: true },
|
||||
{ name: "avatar", path: "/img/nsfw_avatar", nsfw: true },
|
||||
{ name: "yuri", path: "/img/yuri", nsfw: true },
|
||||
{ name: "trap", path: "/img/trap", nsfw: true },
|
||||
{ name: "tits", path: "/img/tits", nsfw: true },
|
||||
{ name: "girlSoloGif", path: "/img/solog", nsfw: true },
|
||||
{ name: "girlSolo", path: "/img/solo", nsfw: true },
|
||||
{ name: "pussyWankGif", path: "/img/pwankg", nsfw: true },
|
||||
{ name: "pussyArt", path: "/img/pussy_jpg", nsfw: true },
|
||||
{ name: "kemonomimi", path: "/img/lewdkemo", nsfw: true },
|
||||
{ name: "kitsune", path: "/img/lewdk", nsfw: true },
|
||||
{ name: "keta", path: "/img/keta", nsfw: true },
|
||||
{ name: "holo", path: "/img/hololewd", nsfw: true },
|
||||
{ name: "holoEro", path: "/img/holoero", nsfw: true },
|
||||
{ name: "hentai", path: "/img/hentai", nsfw: true },
|
||||
{ name: "futanari", path: "/img/futanari", nsfw: true },
|
||||
{ name: "femdom", path: "/img/femdom", nsfw: true },
|
||||
{ name: "feetGif", path: "/img/feetg", nsfw: true },
|
||||
{ name: "eroFeet", path: "/img/erofeet", nsfw: true },
|
||||
{ name: "feet", path: "/img/feet", nsfw: true },
|
||||
{ name: "ero", path: "/img/ero", nsfw: true },
|
||||
{ name: "eroKitsune", path: "/img/erok", nsfw: true },
|
||||
{ name: "eroKemonomimi", path: "/img/erokemo", nsfw: true },
|
||||
{ name: "eroNeko", path: "/img/eron", nsfw: true },
|
||||
{ name: "eroYuri", path: "/img/eroyuri", nsfw: true },
|
||||
{ name: "cumArts", path: "/img/cum_jpg", nsfw: true },
|
||||
{ name: "blowJob", path: "/img/blowjob", nsfw: true },
|
||||
// The follow name and paths have been hidden for this guide as they are NSFW.
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
{ name: "nsfw_endpoint", path: "/img/nsfw_example", nsfw: true },
|
||||
];
|
||||
|
||||
nekosEndpoints.forEach((endpoint) => {
|
||||
|
||||
@@ -110,9 +110,9 @@ import Client, {
|
||||
import { configs } from "./configs.ts";
|
||||
import { Intents } from "https://x.nest.land/Discordeno@9.0.1/src/types/options.ts";
|
||||
import { eventHandlers } from "./src/events/eventHandlers.ts";
|
||||
import type { Message } from "https://x.nest.land/Discordeno@9.0.1/src/structures/message.ts";
|
||||
import type { Command } from "./src/types/commands.ts";
|
||||
import type { Guild } from "https://x.nest.land/Discordeno@9.0.1/src/structures/guild.ts";
|
||||
import { Message } from "https://x.nest.land/Discordeno@9.0.1/src/structures/message.ts";
|
||||
import { Command } from "./src/types/commands.ts";
|
||||
import { Guild } from "https://x.nest.land/Discordeno@9.0.1/src/structures/guild.ts";
|
||||
|
||||
export const botCache = {
|
||||
commands: new Map<string, Command>(),
|
||||
@@ -346,7 +346,7 @@ module.exports = class kickCommand extends Command {
|
||||
Discordeno Version
|
||||
```ts
|
||||
import { sendMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/channel.ts";
|
||||
import type { Member } from "https://x.nest.land/Discordeno@9.0.1/src/structures/member.ts";
|
||||
import { Member } from "https://x.nest.land/Discordeno@9.0.1/src/structures/member.ts";
|
||||
import { kick } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/member.ts";
|
||||
import { deleteMessage } from "https://x.nest.land/Discordeno@9.0.1/src/handlers/message.ts";
|
||||
import { botCache } from "../../mod.ts";
|
||||
|
||||
@@ -75,7 +75,8 @@ Alternatively, you can use boilerplate template repositories that were created b
|
||||
| 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. |
|
||||
| Discordeno Helper | Suyashtnt | [Github](https://github.com/Suyashtnt/discordeno-helper-template/) | A reimplementation of DenoBot using the [discordeno-helper](https://github.com/Suyashtnt/discordeno-helper) framework
|
||||
|
||||
Open Sourced Bots:
|
||||
**Open Sourced Bots:**
|
||||
|
||||
| Bot Name | Developer | Links |
|
||||
| ----------------- | ---------- | ---------------------------------------------------------- |
|
||||
| discordeno-mattis | Mattis6666 | [Github](https://github.com/Mattis6666/discordeno-mattis/) |
|
||||
|
||||
@@ -19,6 +19,12 @@ Discordeno is a Third Party Deno Library for interacting with the Discord API.
|
||||
- Latest and Greatest JavaScript
|
||||
- Actively Maintained!
|
||||
|
||||
### User Reviews
|
||||
|
||||
If you wish to leave a review for other users, please send a PR adding your review to this section!
|
||||
|
||||
Using the Discord API with types but such a simple language like TypeScript is so easy now. Discordeno is A W E S O M E! -[LukasDoesDev](https://github.com/LukasDoesDev)
|
||||
|
||||
## Read me first...
|
||||
Discordeno is cool right? You could make the next big bot! Who knows, but before we get right into developing our Bot. We want to get started with learning the basics...
|
||||
|
||||
|
||||
8
egg.yml
8
egg.yml
@@ -2,18 +2,16 @@ name: Discordeno
|
||||
description: >-
|
||||
Discord Deno TypeScript API library wrapper(Officially vetted library by
|
||||
Discord Team) https://discordeno.netlify.app
|
||||
version: 9.0.1
|
||||
version: 9.0.15
|
||||
stable: true
|
||||
entry: /mod.ts
|
||||
entry: mod.ts
|
||||
repository: 'https://github.com/Skillz4Killz/Discordeno'
|
||||
files:
|
||||
- ./src/**/*
|
||||
- LICENSE
|
||||
- mod.ts
|
||||
- README.md
|
||||
- tsconfig.json
|
||||
- ./deps.ts
|
||||
- ./mod.ts
|
||||
- ./mod.ts
|
||||
- mod.ts
|
||||
checkAll: false
|
||||
unlisted: false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type { GuildBanPayload } from "../types/guild.ts";
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import { GuildBanPayload } from "../types/guild.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildBanAdd(data: DiscordPayload) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Channel } from "../structures/channel.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type { Message } from "../structures/message.ts";
|
||||
import type { PresenceUpdatePayload } from "../types/discord.ts";
|
||||
import { Channel } from "../structures/channel.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Message } from "../structures/message.ts";
|
||||
import { PresenceUpdatePayload } from "../types/discord.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import type { Collection } from "../utils/collection.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
|
||||
export type TableName =
|
||||
| "guilds"
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { ChannelCreatePayload } from "../types/channel.ts";
|
||||
import { ChannelTypes } from "../types/channel.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts";
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalChannelCreate(data: DiscordPayload) {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type {
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import {
|
||||
CreateGuildPayload,
|
||||
GuildDeletePayload,
|
||||
GuildEmojisUpdatePayload,
|
||||
UpdateGuildPayload,
|
||||
} from "../types/guild.ts";
|
||||
import type { GuildUpdateChange } from "../types/options.ts";
|
||||
import { GuildUpdateChange } from "../types/options.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type {
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import {
|
||||
GuildBanPayload,
|
||||
GuildMemberAddPayload,
|
||||
GuildMemberChunkPayload,
|
||||
@@ -41,11 +41,6 @@ export async function handleInternalGuildMemberRemove(data: DiscordPayload) {
|
||||
member || payload.user,
|
||||
);
|
||||
|
||||
eventHandlers.guildMemberRemove?.(
|
||||
guild,
|
||||
member || payload.user,
|
||||
);
|
||||
|
||||
guild.members.delete(payload.user.id);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type {
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import {
|
||||
MessageCreateOptions,
|
||||
MessageDeleteBulkPayload,
|
||||
MessageDeletePayload,
|
||||
@@ -15,9 +15,6 @@ export async function handleInternalMessageCreate(data: DiscordPayload) {
|
||||
const channel = await cacheHandlers.get("channels", payload.channel_id);
|
||||
if (channel) channel.lastMessageID = payload.id;
|
||||
|
||||
const message = await structures.createMessage(payload);
|
||||
// Cache the message
|
||||
cacheHandlers.set("messages", payload.id, message);
|
||||
const guild = payload.guild_id
|
||||
? await cacheHandlers.get("guilds", payload.guild_id)
|
||||
: undefined;
|
||||
@@ -46,6 +43,10 @@ export async function handleInternalMessageCreate(data: DiscordPayload) {
|
||||
}
|
||||
});
|
||||
|
||||
const message = await structures.createMessage(payload);
|
||||
// Cache the message
|
||||
cacheHandlers.set("messages", payload.id, message);
|
||||
|
||||
eventHandlers.messageCreate?.(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { delay } from "../../deps.ts";
|
||||
import { eventHandlers, setBotID } from "../module/client.ts";
|
||||
import { allowNextShard } from "../module/shardingManager.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type {
|
||||
import {
|
||||
DiscordPayload,
|
||||
PresenceUpdatePayload,
|
||||
ReadyPayload,
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
VoiceStateUpdatePayload,
|
||||
WebhookUpdatePayload,
|
||||
} from "../types/discord.ts";
|
||||
import type { UserPayload } from "../types/guild.ts";
|
||||
import { UserPayload } from "../types/guild.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { botID, eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type {
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import {
|
||||
BaseMessageReactionPayload,
|
||||
MessageReactionPayload,
|
||||
MessageReactionRemoveEmojiPayload,
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import { eventHandlers } from "../module/client.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { DiscordPayload } from "../types/discord.ts";
|
||||
import type {
|
||||
GuildRoleDeletePayload,
|
||||
GuildRolePayload,
|
||||
} from "../types/guild.ts";
|
||||
import { DiscordPayload } from "../types/discord.ts";
|
||||
import { GuildRoleDeletePayload, GuildRolePayload } from "../types/guild.ts";
|
||||
import { cacheHandlers } from "./cache.ts";
|
||||
|
||||
export async function handleInternalGuildRoleCreate(data: DiscordPayload) {
|
||||
@@ -30,6 +27,11 @@ export async function handleInternalGuildRoleDelete(data: DiscordPayload) {
|
||||
const cachedRole = guild.roles.get(payload.role_id)!;
|
||||
guild.roles.delete(payload.role_id);
|
||||
eventHandlers.roleDelete?.(guild, cachedRole);
|
||||
|
||||
// For bots without GUILD_MEMBERS member.roles is never updated breaking permissions checking.
|
||||
guild.members.forEach((member) => {
|
||||
member.roles = member.roles.filter((id) => id !== payload.role_id);
|
||||
});
|
||||
}
|
||||
|
||||
export async function handleInternalGuildRoleUpdate(data: DiscordPayload) {
|
||||
@@ -43,5 +45,6 @@ export async function handleInternalGuildRoleUpdate(data: DiscordPayload) {
|
||||
if (!cachedRole) return;
|
||||
|
||||
const role = await structures.createRole(payload.role);
|
||||
guild.roles.set(payload.role.id, role);
|
||||
eventHandlers.roleUpdate?.(guild, role, cachedRole);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { endpoints } from "../constants/discord.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type {
|
||||
import {
|
||||
ChannelEditOptions,
|
||||
ChannelTypes,
|
||||
CreateInviteOptions,
|
||||
FollowedChannelPayload,
|
||||
GetMessages,
|
||||
@@ -12,16 +14,19 @@ import type {
|
||||
MessageContent,
|
||||
} from "../types/channel.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type { RawOverwrite } from "../types/guild.ts";
|
||||
import type { MessageCreateOptions } from "../types/message.ts";
|
||||
import { PermissionOverwrite } from "../types/guild.ts";
|
||||
import { MessageCreateOptions } from "../types/message.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import { botHasChannelPermissions } from "../utils/permissions.ts";
|
||||
import {
|
||||
botHasChannelPermissions,
|
||||
calculateBits,
|
||||
} from "../utils/permissions.ts";
|
||||
|
||||
/** Checks if a channel overwrite for a user id or a role id has permission in this channel */
|
||||
export function channelOverwriteHasPermission(
|
||||
guildID: string,
|
||||
id: string,
|
||||
overwrites: RawOverwrite[],
|
||||
overwrites: PermissionOverwrite[],
|
||||
permissions: Permissions[],
|
||||
) {
|
||||
const overwrite = overwrites.find((perm) => perm.id === id) ||
|
||||
@@ -29,8 +34,10 @@ export function channelOverwriteHasPermission(
|
||||
|
||||
return permissions.every((perm) => {
|
||||
if (overwrite) {
|
||||
if (BigInt(overwrite.deny) & BigInt(perm)) return false;
|
||||
if (BigInt(overwrite.allow) & BigInt(perm)) return true;
|
||||
const allowBits = calculateBits(overwrite.allow);
|
||||
const denyBits = calculateBits(overwrite.deny);
|
||||
if (BigInt(denyBits) & BigInt(perm)) return false;
|
||||
if (BigInt(allowBits) & BigInt(perm)) return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@@ -160,6 +167,15 @@ export async function sendMessage(
|
||||
}
|
||||
}
|
||||
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
if (!channel) throw new Error(Errors.CHANNEL_NOT_FOUND);
|
||||
if (
|
||||
![ChannelTypes.DM, ChannelTypes.GUILD_NEWS, ChannelTypes.GUILD_TEXT]
|
||||
.includes(channel.type)
|
||||
) {
|
||||
throw new Error(Errors.CHANNEL_NOT_TEXT_BASED);
|
||||
}
|
||||
|
||||
const result = await RequestManager.post(
|
||||
endpoints.CHANNEL_MESSAGES(channelID),
|
||||
{
|
||||
|
||||
@@ -3,13 +3,13 @@ import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { identifyPayload } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { requestAllMembers } from "../module/shardingManager.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type { Member } from "../structures/member.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Member } from "../structures/member.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type {
|
||||
import {
|
||||
BannedUser,
|
||||
BanOptions,
|
||||
ChannelCreateOptions,
|
||||
@@ -26,13 +26,13 @@ import type {
|
||||
PrunePayload,
|
||||
UserPayload,
|
||||
} from "../types/guild.ts";
|
||||
import type { MemberCreatePayload } from "../types/member.ts";
|
||||
import { MemberCreatePayload } from "../types/member.ts";
|
||||
import { Intents } from "../types/options.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import type { RoleData } from "../types/role.ts";
|
||||
import { RoleData } from "../types/role.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import { botHasPermission } from "../utils/permissions.ts";
|
||||
import { botHasPermission, calculateBits } from "../utils/permissions.ts";
|
||||
import { urlToBase64 } from "../utils/utils.ts";
|
||||
|
||||
/** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */
|
||||
@@ -105,7 +105,7 @@ export async function createGuildChannel(
|
||||
(await RequestManager.post(endpoints.GUILD_CHANNELS(guild.id), {
|
||||
...options,
|
||||
name,
|
||||
permission_overwrites: options?.permission_overwrites?.map((perm) => ({
|
||||
permission_overwrites: options?.permissionOverwrites?.map((perm) => ({
|
||||
...perm,
|
||||
|
||||
allow: perm.allow.reduce(
|
||||
@@ -317,7 +317,12 @@ export function editRole(
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), options);
|
||||
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
|
||||
...options,
|
||||
permissions: options.permissions
|
||||
? calculateBits(options.permissions)
|
||||
: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
|
||||
|
||||
@@ -2,15 +2,12 @@ import { endpoints } from "../constants/discord.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import type { Member } from "../structures/member.ts";
|
||||
import { Member } from "../structures/member.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import type {
|
||||
DMChannelCreatePayload,
|
||||
MessageContent,
|
||||
} from "../types/channel.ts";
|
||||
import { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import { DMChannelCreatePayload, MessageContent } from "../types/channel.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type { EditMemberOptions } from "../types/member.ts";
|
||||
import { EditMemberOptions } from "../types/member.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import { formatImageURL } from "../utils/cdn.ts";
|
||||
import {
|
||||
|
||||
@@ -3,12 +3,12 @@ import { endpoints } from "../constants/discord.ts";
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import type { Message } from "../structures/message.ts";
|
||||
import { Message } from "../structures/message.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import type { MessageContent } from "../types/channel.ts";
|
||||
import { MessageContent } from "../types/channel.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type { UserPayload } from "../types/guild.ts";
|
||||
import type { MessageCreateOptions } from "../types/message.ts";
|
||||
import { UserPayload } from "../types/guild.ts";
|
||||
import { MessageCreateOptions } from "../types/message.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import { botHasChannelPermissions } from "../utils/permissions.ts";
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ import { endpoints } from "../constants/discord.ts";
|
||||
import { RequestManager } from "../module/requestManager.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type { MessageCreateOptions } from "../types/message.ts";
|
||||
import { MessageCreateOptions } from "../types/message.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import type {
|
||||
import {
|
||||
ExecuteWebhookOptions,
|
||||
WebhookCreateOptions,
|
||||
WebhookPayload,
|
||||
|
||||
@@ -7,21 +7,22 @@ import {
|
||||
isWebSocketPongEvent,
|
||||
WebSocket,
|
||||
} from "../../deps.ts";
|
||||
import type {
|
||||
import {
|
||||
DiscordBotGatewayData,
|
||||
DiscordHeartbeatPayload,
|
||||
GatewayOpcode,
|
||||
ReadyPayload,
|
||||
} from "../types/discord.ts";
|
||||
import { GatewayOpcode } from "../types/discord.ts";
|
||||
import type { FetchMembersOptions } from "../types/guild.ts";
|
||||
import type { BotStatusRequest } from "../utils/utils.ts";
|
||||
import type { IdentifyPayload } from "./client.ts";
|
||||
import { botGatewayData, eventHandlers } from "./client.ts";
|
||||
import { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { BotStatusRequest } from "../utils/utils.ts";
|
||||
import { botGatewayData, eventHandlers, IdentifyPayload } from "./client.ts";
|
||||
import { handleDiscordPayload } from "./shardingManager.ts";
|
||||
|
||||
const basicShards = new Map<number, BasicShard>();
|
||||
const heartbeating = new Set<number>();
|
||||
const heartbeating = new Map<number, boolean>();
|
||||
const utf8decoder = new TextDecoder();
|
||||
const RequestMembersQueue: RequestMemberQueuedRequest[] = [];
|
||||
let processQueue = false;
|
||||
|
||||
export interface BasicShard {
|
||||
id: number;
|
||||
@@ -32,9 +33,6 @@ export interface BasicShard {
|
||||
needToResume: boolean;
|
||||
}
|
||||
|
||||
const RequestMembersQueue: RequestMemberQueuedRequest[] = [];
|
||||
let processQueue = false;
|
||||
|
||||
interface RequestMemberQueuedRequest {
|
||||
guildID: string;
|
||||
shardID: number;
|
||||
@@ -52,7 +50,7 @@ export async function createBasicShard(
|
||||
|
||||
const basicShard: BasicShard = {
|
||||
id: shardID,
|
||||
socket: await connectWebSocket(`${data.url}?v=6&encoding=json`),
|
||||
socket: await connectWebSocket(`${data.url}?v=8&encoding=json`),
|
||||
resumeInterval: 0,
|
||||
sessionID: oldShard?.sessionID || "",
|
||||
previousSequenceNumber: oldShard?.previousSequenceNumber || 0,
|
||||
@@ -123,9 +121,13 @@ export async function createBasicShard(
|
||||
heartbeat(
|
||||
basicShard,
|
||||
(data.d as DiscordHeartbeatPayload).heartbeat_interval,
|
||||
identifyPayload,
|
||||
);
|
||||
}
|
||||
break;
|
||||
case GatewayOpcode.HeartbeatACK:
|
||||
heartbeating.set(shardID, true);
|
||||
break;
|
||||
case GatewayOpcode.Reconnect:
|
||||
eventHandlers.debug?.(
|
||||
{ type: "reconnect", data: { shardID: basicShard.id } },
|
||||
@@ -200,17 +202,39 @@ function resume(shard: BasicShard, payload: IdentifyPayload) {
|
||||
}));
|
||||
}
|
||||
|
||||
// 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 heartbeat(
|
||||
shard: BasicShard,
|
||||
interval: number,
|
||||
payload: IdentifyPayload,
|
||||
) {
|
||||
// We lost socket connection between heartbeats, resume connection
|
||||
if (shard.socket.isClosed) {
|
||||
shard.needToResume = true;
|
||||
resumeConnection(botGatewayData, payload, shard.id);
|
||||
heartbeating.delete(shard.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!heartbeating.has(shard.id)) heartbeating.add(shard.id);
|
||||
if (heartbeating.has(shard.id)) {
|
||||
const receivedACK = heartbeating.get(shard.id);
|
||||
// If a ACK response was not received since last heartbeat, issue invalid session close
|
||||
if (!receivedACK) {
|
||||
eventHandlers.debug?.(
|
||||
{
|
||||
type: "heartbeatStopped",
|
||||
data: {
|
||||
interval,
|
||||
previousSequenceNumber: shard.previousSequenceNumber,
|
||||
shardID: shard.id,
|
||||
},
|
||||
},
|
||||
);
|
||||
return shard.socket.send(JSON.stringify({ op: 4009 }));
|
||||
}
|
||||
}
|
||||
|
||||
// Set it to false as we are issuing a new heartbeat
|
||||
heartbeating.set(shard.id, false);
|
||||
|
||||
shard.socket.send(
|
||||
JSON.stringify(
|
||||
@@ -228,7 +252,7 @@ async function heartbeat(
|
||||
},
|
||||
);
|
||||
await delay(interval);
|
||||
heartbeat(shard, interval);
|
||||
heartbeat(shard, interval, payload);
|
||||
}
|
||||
|
||||
async function resumeConnection(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { endpoints } from "../constants/discord.ts";
|
||||
import type { DiscordBotGatewayData } from "../types/discord.ts";
|
||||
import type { ClientOptions, EventHandlers } from "../types/options.ts";
|
||||
import { DiscordBotGatewayData } from "../types/discord.ts";
|
||||
import { ClientOptions, EventHandlers } from "../types/options.ts";
|
||||
import { RequestManager } from "./requestManager.ts";
|
||||
import { spawnShards } from "./shardingManager.ts";
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { delay } from "../../deps.ts";
|
||||
import { baseEndpoints } from "../constants/discord.ts";
|
||||
import { HttpResponseCode } from "../types/discord.ts";
|
||||
import { Errors } from "../types/errors.ts";
|
||||
import type { RequestMethods } from "../types/fetch.ts";
|
||||
import { RequestMethods } from "../types/fetch.ts";
|
||||
import { authorization, eventHandlers } from "./client.ts";
|
||||
|
||||
const pathQueues: { [key: string]: QueuedRequest[] } = {};
|
||||
@@ -286,6 +286,23 @@ async function runMethod(
|
||||
});
|
||||
}
|
||||
|
||||
async function logErrors(response: Response, errorStack?: unknown) {
|
||||
try {
|
||||
const error = await response.json();
|
||||
console.error(error);
|
||||
|
||||
eventHandlers.debug?.({ type: "error", data: { errorStack, error } });
|
||||
} catch {
|
||||
eventHandlers.debug?.(
|
||||
{
|
||||
type: "error",
|
||||
data: { errorStack },
|
||||
},
|
||||
);
|
||||
console.error(response);
|
||||
}
|
||||
}
|
||||
|
||||
function handleStatusCode(response: Response, errorStack?: unknown) {
|
||||
const status = response.status;
|
||||
|
||||
@@ -296,13 +313,7 @@ function handleStatusCode(response: Response, errorStack?: unknown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
eventHandlers.debug?.(
|
||||
{
|
||||
type: "error",
|
||||
data: { errorStack },
|
||||
},
|
||||
);
|
||||
console.error(response);
|
||||
logErrors(response, errorStack);
|
||||
|
||||
switch (status) {
|
||||
case HttpResponseCode.BadRequest:
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
import type { WebSocket } from "../../deps.ts";
|
||||
import { connectWebSocket, delay, isWebSocketCloseEvent } from "../../deps.ts";
|
||||
import type {
|
||||
import {
|
||||
connectWebSocket,
|
||||
delay,
|
||||
isWebSocketCloseEvent,
|
||||
WebSocket,
|
||||
} from "../../deps.ts";
|
||||
import {
|
||||
DiscordBotGatewayData,
|
||||
DiscordHeartbeatPayload,
|
||||
GatewayOpcode,
|
||||
ReadyPayload,
|
||||
} from "../types/discord.ts";
|
||||
import { GatewayOpcode } from "../types/discord.ts";
|
||||
import type { FetchMembersOptions } from "../types/guild.ts";
|
||||
import type { DebugArg } from "../types/options.ts";
|
||||
import { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { DebugArg } from "../types/options.ts";
|
||||
|
||||
let shardSocket: WebSocket;
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
import { delay } from "../../deps.ts";
|
||||
import { controllers } from "../controllers/mod.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type {
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import {
|
||||
DiscordBotGatewayData,
|
||||
DiscordPayload,
|
||||
GatewayOpcode,
|
||||
} from "../types/discord.ts";
|
||||
import { GatewayOpcode } from "../types/discord.ts";
|
||||
import type { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { FetchMembersOptions } from "../types/guild.ts";
|
||||
import { cache } from "../utils/cache.ts";
|
||||
import type { BotStatusRequest } from "../utils/utils.ts";
|
||||
import { BotStatusRequest } from "../utils/utils.ts";
|
||||
import {
|
||||
botGatewayStatusRequest,
|
||||
createBasicShard,
|
||||
requestGuildMembers,
|
||||
} from "./basicShard.ts";
|
||||
import type { IdentifyPayload } from "./client.ts";
|
||||
import { botGatewayData, eventHandlers, identifyPayload } from "./client.ts";
|
||||
import {
|
||||
botGatewayData,
|
||||
eventHandlers,
|
||||
IdentifyPayload,
|
||||
identifyPayload,
|
||||
} from "./client.ts";
|
||||
|
||||
let shardCounter = 0;
|
||||
let basicSharding = false;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import type { ChannelCreatePayload } from "../types/channel.ts";
|
||||
import type { Unpromise } from "../types/misc.ts";
|
||||
import { ChannelCreatePayload } from "../types/channel.ts";
|
||||
import { PermissionOverwrite } from "../types/guild.ts";
|
||||
import { Unpromise } from "../types/misc.ts";
|
||||
import { calculatePermissions } from "../utils/permissions.ts";
|
||||
|
||||
export async function createChannel(
|
||||
@@ -14,13 +15,14 @@ export async function createChannel(
|
||||
rate_limit_per_user: rateLimitPerUser,
|
||||
parent_id: parentID,
|
||||
last_pin_timestamp: lastPinTimestamp,
|
||||
permission_overwrites,
|
||||
...rest
|
||||
} = data;
|
||||
|
||||
const channel = {
|
||||
...rest,
|
||||
/** The guild id of the channel if it is a guild channel. */
|
||||
guildID: guildID || rawGuildID,
|
||||
guildID: guildID || rawGuildID || "",
|
||||
/** The id of the last message sent in this channel */
|
||||
lastMessageID,
|
||||
/** The amount of users allowed in this voice channel. */
|
||||
@@ -32,13 +34,14 @@ export async function createChannel(
|
||||
/** The last time when a message was pinned in this channel */
|
||||
lastPinTimestamp,
|
||||
/** The permission overwrites for this channel */
|
||||
permissions: data.permission_overwrites
|
||||
? data.permission_overwrites.map((perm) => ({
|
||||
...perm,
|
||||
allow: calculatePermissions(BigInt(perm.allow)),
|
||||
deny: calculatePermissions(BigInt(perm.deny)),
|
||||
}))
|
||||
: [],
|
||||
permissionOverwrites:
|
||||
(data.permission_overwrites
|
||||
? data.permission_overwrites.map((perm) => ({
|
||||
...perm,
|
||||
allow: calculatePermissions(BigInt(perm.allow)),
|
||||
deny: calculatePermissions(BigInt(perm.deny)),
|
||||
}))
|
||||
: []) as PermissionOverwrite[],
|
||||
/** Whether this channel is nsfw or not */
|
||||
nsfw: data.nsfw || false,
|
||||
/** The mention of the channel */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { CreateGuildPayload } from "../types/guild.ts";
|
||||
import type { Unpromise } from "../types/misc.ts";
|
||||
import { CreateGuildPayload } from "../types/guild.ts";
|
||||
import { Unpromise } from "../types/misc.ts";
|
||||
import { Collection } from "../utils/collection.ts";
|
||||
import type { Member } from "./member.ts";
|
||||
import { Member } from "./member.ts";
|
||||
import { structures } from "./mod.ts";
|
||||
|
||||
export async function createGuild(data: CreateGuildPayload, shardID: number) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MemberCreatePayload } from "../types/member.ts";
|
||||
import type { Unpromise } from "../types/misc.ts";
|
||||
import { MemberCreatePayload } from "../types/member.ts";
|
||||
import { Unpromise } from "../types/misc.ts";
|
||||
|
||||
export async function createMember(data: MemberCreatePayload, guildID: string) {
|
||||
const {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { MessageCreateOptions } from "../types/message.ts";
|
||||
import type { Unpromise } from "../types/misc.ts";
|
||||
import { MessageCreateOptions } from "../types/message.ts";
|
||||
import { Unpromise } from "../types/misc.ts";
|
||||
|
||||
export async function createMessage(data: MessageCreateOptions) {
|
||||
const {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Unpromise } from "../types/misc.ts";
|
||||
import type { RoleData } from "../types/role.ts";
|
||||
import { Unpromise } from "../types/misc.ts";
|
||||
import { RoleData } from "../types/role.ts";
|
||||
|
||||
export async function createRole(data: RoleData) {
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import type { Timestamps } from "./discord.ts";
|
||||
|
||||
export interface ActivityPayload {
|
||||
name: string;
|
||||
type: number;
|
||||
url?: string;
|
||||
created_at: number;
|
||||
timestamps: Timestamps;
|
||||
timestamps?: ActivityTimestamps;
|
||||
application_id?: string;
|
||||
details?: string;
|
||||
state?: string;
|
||||
emoji?: ActivityEmoji;
|
||||
party?: ActivityParty;
|
||||
assets?: ActivityAssets;
|
||||
secrets?: ActivitySecrets;
|
||||
instance?: boolean;
|
||||
flags?: number;
|
||||
}
|
||||
|
||||
export enum ActivityType {
|
||||
@@ -18,4 +24,44 @@ export enum ActivityType {
|
||||
Listening,
|
||||
/** Example: ":smiley: I am cool" */
|
||||
Custom = 4,
|
||||
/** Example: "Competing in Arena World Champions" */
|
||||
Competing,
|
||||
}
|
||||
|
||||
export interface ActivityTimestamps {
|
||||
start?: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
export interface ActivityEmoji {
|
||||
name: string;
|
||||
id?: string;
|
||||
animated?: boolean;
|
||||
}
|
||||
|
||||
export interface ActivityParty {
|
||||
id?: string;
|
||||
size?: [number, number];
|
||||
}
|
||||
|
||||
export interface ActivityAssets {
|
||||
large_image?: string;
|
||||
large_text?: string;
|
||||
small_image?: string;
|
||||
small_text?: string;
|
||||
}
|
||||
|
||||
export interface ActivitySecrets {
|
||||
join?: string;
|
||||
spectate?: string;
|
||||
match?: string;
|
||||
}
|
||||
|
||||
export enum ActivityFlags {
|
||||
INSTANCE = 1 << 0,
|
||||
JOIN = 1 << 1,
|
||||
SPECTATE = 1 << 2,
|
||||
JOIN_REQUEST = 1 << 3,
|
||||
SYNC = 1 << 4,
|
||||
PLAY = 1 << 5,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Overwrite, RawOverwrite } from "./guild.ts";
|
||||
import type { Embed } from "./message.ts";
|
||||
import { Overwrite, RawOverwrite } from "./guild.ts";
|
||||
import { Embed } from "./message.ts";
|
||||
|
||||
export interface ChannelEditOptions {
|
||||
/** 2-100 character channel name. All */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { PartialUser, UserPayload } from "./guild.ts";
|
||||
import type { MemberCreatePayload } from "./member.ts";
|
||||
import type { Activity } from "./message.ts";
|
||||
import type { ClientStatusPayload } from "./presence.ts";
|
||||
import { PartialUser, UserPayload } from "./guild.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
import { Activity } from "./message.ts";
|
||||
import { ClientStatusPayload } from "./presence.ts";
|
||||
|
||||
export interface DiscordPayload {
|
||||
/** OP code for the payload */
|
||||
@@ -196,11 +196,6 @@ export interface Properties {
|
||||
$device: string;
|
||||
}
|
||||
|
||||
export interface Timestamps {
|
||||
start?: number;
|
||||
end?: number;
|
||||
}
|
||||
|
||||
export interface Emoji {
|
||||
name: string;
|
||||
id?: string;
|
||||
|
||||
@@ -31,4 +31,6 @@ export enum Errors {
|
||||
CHANNEL_NOT_IN_GUILD = "CHANNEL_NOT_IN_GUILD",
|
||||
INVALID_WEBHOOK_NAME = "INVALID_WEBHOOK_NAME",
|
||||
INVALID_WEBHOOK_OPTIONS = "INVALID_WEBHOOK_OPTIONS",
|
||||
CHANNEL_NOT_FOUND = "CHANNEL_NOT_FOUND",
|
||||
CHANNEL_NOT_TEXT_BASED = "CHANNEL_NOT_TEXT_BASED",
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { ChannelCreatePayload, ChannelTypes } from "./channel.ts";
|
||||
import type { Emoji, StatusType } from "./discord.ts";
|
||||
import type { MemberCreatePayload } from "./member.ts";
|
||||
import type { Activity } from "./message.ts";
|
||||
import type { Permission } from "./permission.ts";
|
||||
import type { ClientStatusPayload } from "./presence.ts";
|
||||
import type { RoleData } from "./role.ts";
|
||||
import { ChannelCreatePayload, ChannelTypes } from "./channel.ts";
|
||||
import { Emoji, StatusType } from "./discord.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
import { Activity } from "./message.ts";
|
||||
import { Permission } from "./permission.ts";
|
||||
import { ClientStatusPayload } from "./presence.ts";
|
||||
import { RoleData } from "./role.ts";
|
||||
|
||||
export interface GuildRolePayload {
|
||||
/** The id of the guild */
|
||||
@@ -473,6 +473,12 @@ export interface RawOverwrite {
|
||||
deny: number;
|
||||
}
|
||||
|
||||
export interface PermissionOverwrite
|
||||
extends Omit<RawOverwrite, "allow" | "deny"> {
|
||||
allow: Permission[];
|
||||
deny: Permission[];
|
||||
}
|
||||
|
||||
export interface ChannelCreateOptions {
|
||||
/** The type of the channel */
|
||||
type?: ChannelTypes;
|
||||
@@ -487,7 +493,7 @@ export interface ChannelCreateOptions {
|
||||
/** The sorting position of the channel */
|
||||
position?: number;
|
||||
/** The channel's permission overwrites */
|
||||
permission_overwrites?: Overwrite[];
|
||||
permissionOverwrites?: Overwrite[];
|
||||
/** The id of the parent category for the channel */
|
||||
parent_id?: string;
|
||||
/** Whether the channel is nsfw */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UserPayload } from "./guild.ts";
|
||||
import { UserPayload } from "./guild.ts";
|
||||
|
||||
export interface EditMemberOptions {
|
||||
/** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Channel } from "../structures/channel.ts";
|
||||
import type { ChannelType } from "./channel.ts";
|
||||
import type { UserPayload } from "./guild.ts";
|
||||
import type { MemberCreatePayload } from "./member.ts";
|
||||
import { Channel } from "../structures/channel.ts";
|
||||
import { ChannelType } from "./channel.ts";
|
||||
import { UserPayload } from "./guild.ts";
|
||||
import { MemberCreatePayload } from "./member.ts";
|
||||
|
||||
export interface MentionedUser extends UserPayload {
|
||||
member: MemberCreatePayload;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Channel } from "../structures/channel.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type { Member } from "../structures/member.ts";
|
||||
import type { Message } from "../structures/message.ts";
|
||||
import type { Role } from "../structures/role.ts";
|
||||
import type {
|
||||
import { Channel } from "../structures/channel.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Member } from "../structures/member.ts";
|
||||
import { Message } from "../structures/message.ts";
|
||||
import { Role } from "../structures/role.ts";
|
||||
import {
|
||||
DiscordPayload,
|
||||
Emoji,
|
||||
PresenceUpdatePayload,
|
||||
@@ -11,8 +11,8 @@ import type {
|
||||
TypingStartPayload,
|
||||
VoiceStateUpdatePayload,
|
||||
} from "./discord.ts";
|
||||
import type { UserPayload } from "./guild.ts";
|
||||
import type {
|
||||
import { UserPayload } from "./guild.ts";
|
||||
import {
|
||||
Attachment,
|
||||
BaseMessageReactionPayload,
|
||||
Embed,
|
||||
@@ -64,6 +64,7 @@ export interface DebugArg {
|
||||
| "requestManagerFetched"
|
||||
| "requestMembersProcessing"
|
||||
| "heartbeat"
|
||||
| "heartbeatStopped"
|
||||
| "createShard"
|
||||
| "invalidSession"
|
||||
| "reconnect"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { StatusType } from "./discord.ts";
|
||||
import { StatusType } from "./discord.ts";
|
||||
|
||||
export interface ClientStatusPayload {
|
||||
/** The user's status set for an active desktop (Windows, Linux, Mac) application session */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { UserPayload } from "./guild.ts";
|
||||
import type { Embed } from "./message.ts";
|
||||
import { UserPayload } from "./guild.ts";
|
||||
import { Embed } from "./message.ts";
|
||||
|
||||
export interface WebhookPayload {
|
||||
/** The id of the webhook */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Channel } from "../structures/channel.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type { Message } from "../structures/message.ts";
|
||||
import type { PresenceUpdatePayload } from "../types/discord.ts";
|
||||
import { Channel } from "../structures/channel.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Message } from "../structures/message.ts";
|
||||
import { PresenceUpdatePayload } from "../types/discord.ts";
|
||||
import { Collection } from "./collection.ts";
|
||||
|
||||
export interface CacheData {
|
||||
@@ -21,5 +21,5 @@ export const cache: CacheData = {
|
||||
messages: new Collection(),
|
||||
unavailableGuilds: new Collection(),
|
||||
presences: new Collection(),
|
||||
fetchAllMembersProcessingRequests: new Collection<string, Function>(),
|
||||
fetchAllMembersProcessingRequests: new Collection(),
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
import { ImageFormats, ImageSize } from "../types/cdn.ts";
|
||||
|
||||
export const formatImageURL = (
|
||||
url: string,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { cacheHandlers } from "../controllers/cache.ts";
|
||||
import { botID } from "../module/client.ts";
|
||||
import type { Guild } from "../structures/guild.ts";
|
||||
import type { Role } from "../structures/role.ts";
|
||||
import type { Permission } from "../types/permission.ts";
|
||||
import { Permissions } from "../types/permission.ts";
|
||||
import { Guild } from "../structures/guild.ts";
|
||||
import { Role } from "../structures/role.ts";
|
||||
import { PermissionOverwrite } from "../types/guild.ts";
|
||||
import { Permission, Permissions } from "../types/permission.ts";
|
||||
|
||||
/** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */
|
||||
export async function memberIDHasPermission(
|
||||
@@ -34,6 +34,8 @@ export function memberHasPermission(
|
||||
const permissionBits = memberRoleIDs.map((id) =>
|
||||
guild.roles.get(id)?.permissions
|
||||
)
|
||||
// Removes any edge case undefined
|
||||
.filter((id) => id)
|
||||
.reduce((bits, permissions) => {
|
||||
bits |= BigInt(permissions);
|
||||
return bits;
|
||||
@@ -58,6 +60,8 @@ export async function botHasPermission(
|
||||
|
||||
const permissionBits = member.roles
|
||||
.map((id) => guild.roles.get(id)!)
|
||||
// Remove any edge case undefined
|
||||
.filter((r) => r)
|
||||
.reduce((bits, data) => {
|
||||
bits |= BigInt(data.permissions);
|
||||
|
||||
@@ -84,101 +88,104 @@ export async function hasChannelPermissions(
|
||||
permissions: Permissions[],
|
||||
) {
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
if (!channel?.guildID) return true;
|
||||
if (!channel) return false;
|
||||
if (!channel.guildID) return true;
|
||||
|
||||
const guild = await cacheHandlers.get("guilds", channel.guildID);
|
||||
if (!guild) return false;
|
||||
|
||||
if (guild.ownerID === memberID) return true;
|
||||
if (botHasPermission(guild.id, [Permissions.ADMINISTRATOR])) return true;
|
||||
if (
|
||||
await memberIDHasPermission(memberID, guild.id, ["ADMINISTRATOR"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const member = guild.members.get(memberID);
|
||||
if (!member) return false;
|
||||
|
||||
const memberOverwrite = channel.permission_overwrites?.find((o) =>
|
||||
o.id === memberID
|
||||
);
|
||||
let memberOverwrite: PermissionOverwrite | undefined;
|
||||
let everyoneOverwrite: PermissionOverwrite | undefined;
|
||||
let rolesOverwrites: PermissionOverwrite[] = [];
|
||||
|
||||
const rolesOverwrites = channel.permission_overwrites?.filter((o) =>
|
||||
member.roles.includes(o.id)
|
||||
);
|
||||
|
||||
const everyoneOverwrite = channel.permission_overwrites?.find((o) =>
|
||||
o.id === guild.id
|
||||
);
|
||||
for (const overwrite of channel.permissionOverwrites || []) {
|
||||
// If the overwrite on this channel is specific to this member
|
||||
if (overwrite.id === memberID) memberOverwrite = overwrite;
|
||||
// If it is the everyone role overwrite
|
||||
if (overwrite.id === guild.id) everyoneOverwrite = overwrite;
|
||||
// If it is one of the roles the member has
|
||||
if (member.roles.includes(overwrite.id)) rolesOverwrites.push(overwrite);
|
||||
}
|
||||
|
||||
const allowedPermissions = new Set<Permissions>();
|
||||
|
||||
// Member perms override everything so we must check them first
|
||||
if (memberOverwrite) {
|
||||
// One of the necessary permissions is denied
|
||||
if (
|
||||
permissions.some((perm) => BigInt(memberOverwrite.deny) & BigInt(perm))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
permissions.forEach((perm) => {
|
||||
const allowBits = calculateBits(memberOverwrite.allow);
|
||||
const denyBits = calculateBits(memberOverwrite.deny);
|
||||
for (const perm of permissions) {
|
||||
// One of the necessary permissions is denied. Since this is main permission we can cancel if its denied.
|
||||
if (BigInt(denyBits) & BigInt(perm)) return false;
|
||||
// Already allowed perm
|
||||
if (allowedPermissions.has(perm)) return;
|
||||
if (allowedPermissions.has(perm)) continue;
|
||||
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(memberOverwrite.allow) & BigInt(perm)) {
|
||||
if (BigInt(allowBits) & BigInt(perm)) {
|
||||
allowedPermissions.add(perm);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check the necessary permissions for roles
|
||||
if (rolesOverwrites?.length) {
|
||||
if (
|
||||
rolesOverwrites.some((overwrite) =>
|
||||
permissions.some((perm) =>
|
||||
(BigInt(overwrite.deny) & BigInt(perm)) &&
|
||||
// If another role allows these perms then they are not denied
|
||||
!rolesOverwrites.some((o) => BigInt(o.allow) & BigInt(perm)) &&
|
||||
// Make sure the memberOverwrite does not allow this perm
|
||||
!(memberOverwrite && BigInt(memberOverwrite.allow) & BigInt(perm))
|
||||
)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
for (const perm of permissions) {
|
||||
// If this is already allowed, skip
|
||||
if (allowedPermissions.has(perm)) continue;
|
||||
|
||||
permissions.forEach((perm) => {
|
||||
for (const overwrite of rolesOverwrites) {
|
||||
const allowBits = calculateBits(overwrite.allow);
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(allowBits) & BigInt(perm)) {
|
||||
allowedPermissions.add(perm);
|
||||
break;
|
||||
}
|
||||
|
||||
const denyBits = calculateBits(overwrite.deny);
|
||||
// If this role denies it we need to save and check if another role allows it, allows > deny
|
||||
if (BigInt(denyBits) & BigInt(perm)) {
|
||||
// This role denies his perm, but before denying we need to check all other roles if any allow as allow > deny
|
||||
const isAllowed = rolesOverwrites.some((o) =>
|
||||
BigInt(calculateBits(o.allow)) & BigInt(perm)
|
||||
);
|
||||
if (isAllowed) continue;
|
||||
// This permission is in fact denied. Since Roles overrule everything below here we can cancel ou here
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (everyoneOverwrite) {
|
||||
const allowBits = calculateBits(everyoneOverwrite.allow);
|
||||
const denyBits = calculateBits(everyoneOverwrite.deny);
|
||||
for (const perm of permissions) {
|
||||
// Already allowed perm
|
||||
if (allowedPermissions.has(perm)) return;
|
||||
rolesOverwrites.forEach((overwrite) => {
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(overwrite.allow) & BigInt(perm)) {
|
||||
allowedPermissions.add(perm);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Check the necessary permissions for everyone
|
||||
if (
|
||||
everyoneOverwrite
|
||||
) {
|
||||
if (
|
||||
permissions.some((perm) =>
|
||||
BigInt(everyoneOverwrite.deny) & BigInt(perm) &&
|
||||
!allowedPermissions.has(perm)
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
// If all permissions are granted
|
||||
if (
|
||||
permissions.every((perm) =>
|
||||
BigInt(everyoneOverwrite.allow) & BigInt(perm)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
if (allowedPermissions.has(perm)) continue;
|
||||
// One of the necessary permissions is denied. Since everyone overwrite overrides role perms we can cancel here
|
||||
if (BigInt(denyBits) & BigInt(perm)) return false;
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(allowBits) & BigInt(perm)) {
|
||||
allowedPermissions.add(perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Is there any remaining permission to check role perms or can we determine that permissions are allowed
|
||||
if (permissions.every((perm) => allowedPermissions.has(perm))) return true;
|
||||
|
||||
// Some permission was not explicitly allowed so we default to checking role perms directly
|
||||
return botHasPermission(guild.id, permissions);
|
||||
}
|
||||
|
||||
/** This function converts a bitwise string to permission strings */
|
||||
export function calculatePermissions(permissionBits: bigint) {
|
||||
return Object.keys(Permissions).filter((perm) => {
|
||||
if (typeof perm !== "number") return false;
|
||||
@@ -186,6 +193,14 @@ export function calculatePermissions(permissionBits: bigint) {
|
||||
}) as Permission[];
|
||||
}
|
||||
|
||||
/** This function converts an array of permissions into the bitwise string. */
|
||||
export function calculateBits(permissions: Permission[]) {
|
||||
return permissions.reduce(
|
||||
(bits, perm) => bits |= BigInt(Permissions[perm]),
|
||||
BigInt(0),
|
||||
).toString();
|
||||
}
|
||||
|
||||
export async function highestRole(guildID: string, memberID: string) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { encode } from "../../deps.ts";
|
||||
import { sendGatewayCommand } from "../module/shardingManager.ts";
|
||||
import { ActivityType } from "../types/activity.ts";
|
||||
import type { StatusType } from "../types/discord.ts";
|
||||
import { StatusType } from "../types/discord.ts";
|
||||
|
||||
export const sleep = (timeout: number) => {
|
||||
return new Promise((resolve) => setTimeout(resolve, timeout));
|
||||
|
||||
253
tests/mod.test.ts
Normal file
253
tests/mod.test.ts
Normal file
@@ -0,0 +1,253 @@
|
||||
import { assert, assertEquals, delay } from "../deps.ts";
|
||||
import {
|
||||
botID,
|
||||
cache,
|
||||
Channel,
|
||||
createClient,
|
||||
createGuildChannel,
|
||||
createGuildRole,
|
||||
createServer,
|
||||
deleteChannel,
|
||||
deleteRole,
|
||||
deleteServer,
|
||||
editRole,
|
||||
getMessage,
|
||||
Guild,
|
||||
Intents,
|
||||
OverwriteType,
|
||||
Role,
|
||||
sendMessage,
|
||||
} from "../mod.ts";
|
||||
import {
|
||||
channelOverwriteHasPermission,
|
||||
editChannel,
|
||||
} from "../src/handlers/channel.ts";
|
||||
import { getChannel } from "../src/handlers/guild.ts";
|
||||
import { Permissions } from "../src/types/permission.ts";
|
||||
|
||||
const token = Deno.env.get("DISCORD_TOKEN");
|
||||
if (!token) throw "Token is not provided";
|
||||
|
||||
createClient({
|
||||
token,
|
||||
intents: [Intents.GUILD_MESSAGES, Intents.GUILDS],
|
||||
});
|
||||
|
||||
// Default options for all test cases
|
||||
const testOptions = {
|
||||
sanitizeOps: false,
|
||||
sanitizeResources: false,
|
||||
};
|
||||
|
||||
Deno.test({
|
||||
name: "connect to the gateway",
|
||||
fn: async () => {
|
||||
// Delay the execution by 15 seconds (15000 ms)
|
||||
await delay(15000);
|
||||
|
||||
// Check whether botID is nil or not
|
||||
assert(botID);
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
const data = {
|
||||
guildID: "",
|
||||
roleID: "",
|
||||
channelID: "",
|
||||
};
|
||||
|
||||
Deno.test({
|
||||
name: "create a guild",
|
||||
async fn() {
|
||||
// Create a guild "Discordeno Test"
|
||||
const createdGuild = (await createServer({
|
||||
name: "Discordeno Test",
|
||||
})) as Guild;
|
||||
|
||||
// Check whether createdGuild is nil or not
|
||||
assert(createdGuild);
|
||||
|
||||
data.guildID = createdGuild.id;
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
// Role
|
||||
|
||||
Deno.test({
|
||||
name: "create a role in a guild",
|
||||
async fn() {
|
||||
// Create a role "Role 1" in the guild "Discordeno Test"
|
||||
const createdRole = await createGuildRole(data.guildID, {
|
||||
name: "Role 1",
|
||||
});
|
||||
|
||||
// Check whether the created role is nil or not
|
||||
assert(createdRole);
|
||||
|
||||
data.roleID = createdRole.id;
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "edit a role in a guild",
|
||||
async fn() {
|
||||
// Edit a role "Role 1" in the guild "Discordeno Test"
|
||||
const editedRole = (await editRole(data.guildID, data.roleID, {
|
||||
name: "Edited Role",
|
||||
color: 4320244,
|
||||
hoist: false,
|
||||
mentionable: false,
|
||||
})) as Role;
|
||||
|
||||
// Assertions
|
||||
assert(editedRole);
|
||||
assertEquals(editedRole.name, "Edited Role");
|
||||
assertEquals(editedRole.color, 4320244);
|
||||
assertEquals(editedRole.hoist, false);
|
||||
assertEquals(editedRole.mentionable, false);
|
||||
|
||||
data.roleID = editedRole.id;
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
// Channel
|
||||
|
||||
Deno.test({
|
||||
name: "create a channel in a guild",
|
||||
async fn() {
|
||||
const guild = cache.guilds.get(data.guildID);
|
||||
if (!guild) throw "Guild not found";
|
||||
const createdChannel = await createGuildChannel(guild, "test");
|
||||
|
||||
// Check whether the created channel is nil or not
|
||||
assert(createdChannel);
|
||||
|
||||
data.channelID = createdChannel.id;
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "get a channel in a guild",
|
||||
async fn() {
|
||||
const channel = await getChannel(data.channelID);
|
||||
|
||||
assertEquals(channel.id, data.channelID);
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "edit a channel in a guild",
|
||||
async fn() {
|
||||
const channel = await editChannel(data.channelID, {
|
||||
name: "edited channel",
|
||||
overwrites: [
|
||||
{
|
||||
id: data.roleID,
|
||||
type: OverwriteType.ROLE,
|
||||
allow: ["VIEW_CHANNEL", "SEND_MESSAGES"],
|
||||
deny: ["USE_EXTERNAL_EMOJIS"],
|
||||
},
|
||||
],
|
||||
}) as Channel;
|
||||
const editedChannel = await getChannel(data.channelID);
|
||||
|
||||
assert(channel);
|
||||
assertEquals(editedChannel.name, "edited channel");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "channel overwrite has permission",
|
||||
async fn() {
|
||||
const channel = cache.channels.get(data.channelID);
|
||||
if (!channel) throw "Channel not found";
|
||||
|
||||
if (!channel.permissionOverwrites) throw "Channel overwrites not found.";
|
||||
|
||||
const hasPerm = channelOverwriteHasPermission(
|
||||
data.guildID,
|
||||
data.roleID,
|
||||
channel.permissionOverwrites,
|
||||
[Permissions.VIEW_CHANNEL, Permissions.SEND_MESSAGES],
|
||||
);
|
||||
const missingPerm = channelOverwriteHasPermission(
|
||||
data.guildID,
|
||||
data.roleID,
|
||||
channel.permissionOverwrites,
|
||||
[Permissions.USE_EXTERNAL_EMOJIS],
|
||||
);
|
||||
|
||||
assertEquals(hasPerm, true);
|
||||
assertEquals(missingPerm, false);
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
// Message
|
||||
|
||||
let messageID: string;
|
||||
|
||||
Deno.test({
|
||||
name: "create a message in a guild",
|
||||
async fn() {
|
||||
const createdMessage = await sendMessage(data.channelID, "test");
|
||||
|
||||
// Check whether the created message is nil or not
|
||||
assert(createdMessage);
|
||||
|
||||
messageID = createdMessage.id;
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "get a message in a guild",
|
||||
async fn() {
|
||||
const message = await getMessage(data.channelID, messageID);
|
||||
|
||||
assertEquals(messageID, message.id);
|
||||
},
|
||||
});
|
||||
|
||||
// Clean up
|
||||
|
||||
Deno.test({
|
||||
name: "delete a role from the guild",
|
||||
async fn() {
|
||||
await deleteRole(data.guildID, data.roleID);
|
||||
data.roleID = "";
|
||||
assertEquals(data.roleID, "");
|
||||
},
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "delete a channel in the guild",
|
||||
async fn() {
|
||||
await deleteChannel(data.guildID, data.channelID);
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
Deno.test({
|
||||
name: "delete a guild",
|
||||
async fn() {
|
||||
await deleteServer(data.guildID);
|
||||
data.guildID = "";
|
||||
assertEquals(data.guildID, "");
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
|
||||
// This is meant to be the final test that forcefully crashes the bot
|
||||
Deno.test({
|
||||
name: "exit the process forcefully after all the tests are done",
|
||||
async fn() {
|
||||
Deno.exit(1);
|
||||
},
|
||||
...testOptions,
|
||||
});
|
||||
Reference in New Issue
Block a user