diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml deleted file mode 100644 index f34093188..000000000 --- a/.github/workflows/deno.yml +++ /dev/null @@ -1,35 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: Testing/Linting - -# Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the master branch -on: - push: - branches: [master] - pull_request: - branches: [master] - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 - - - name: Setup Deno environment - uses: denolib/setup-deno@master - - - name: Deno Fetch - run: deno cache mod.ts - - - name: Deno Format Check - run: deno fmt *.ts --check - - - name: Deno Format Check src/ - run: deno fmt src/* --check diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..f16d35b38 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,16 @@ +name: Lint +on: + push: + branches: [master] + pull_request: + branches: [master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: denolib/setup-deno@master + - name: Cache the dependencies + run: deno cache mod.ts + - name: Run format script with --check + run: deno fmt --ignore=./docs --check diff --git a/.github/workflows/nestland.yml b/.github/workflows/nestland.yml index 6625cc71c..fbb97d5d9 100644 --- a/.github/workflows/nestland.yml +++ b/.github/workflows/nestland.yml @@ -1,8 +1,8 @@ name: Ship Nest.Land on: - release: - types: [published] + create: + ref_type: "tag" jobs: release: @@ -16,5 +16,6 @@ jobs: - name: Publish module run: | - deno run -A --unstable https://x.nest.land/eggs@0.2.1/mod.ts link ${{ secrets.NESTAPIKEY }} - deno run -A --unstable https://x.nest.land/eggs@0.2.1/mod.ts publish --version ${{ github.event.inputs.tags }} + deno install -A --unstable https://x.nest.land/eggs@0.3.2/eggs.ts + eggs link ${{ secrets.NESTAPIKEY }} + eggs publish --yes --no-check --version $(git describe --tags $(git rev-list --tags --max-count=1)) diff --git a/README.md b/README.md index 98f27eb16..1f59e2281 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ > Discord API library wrapper in Deno [![Discord](https://img.shields.io/discord/223909216866402304?color=7289da&logo=discord&logoColor=dark)](https://discord.gg/J4NqJ72) -![Testing/Linting](https://github.com/Skillz4Killz/Discordeno/workflows/Testing/Linting/badge.svg) +![Lint](https://github.com/Skillz4Killz/Discordeno/workflows/Lint/badge.svg) ![Test](https://github.com/Skillz4Killz/Discordeno/workflows/Test/badge.svg) [![nest badge](https://nest.land/badge.svg)](https://nest.land/package/Discordeno) @@ -27,7 +27,7 @@ The instructions below are meant for advanced developers! Starting with Discordeno is very simple, you can start from scratch without any boilerplates/frameworks: Add this snippet of code into a new TypeScript file: ```typescript -import StartBot, { sendMessage, Intents } from "https://x.nest.land/Discordeno@9.0.1/mod.ts"; +import StartBot, { sendMessage, Intents } from "https://x.nest.land/Discordeno@9.0.15/mod.ts"; import config from "./config.ts"; StartBot({ @@ -57,4 +57,4 @@ Alternatively, you can use boilerplate template repositories that were created b ## License -MIT © Skillz4Killz +[MIT © Skillz4Killz](https://github.com/Skillz4Killz/Discordeno/blob/master/LICENSE) diff --git a/docs/content/gettingstarted.md b/docs/content/gettingstarted.md index 6ee051f24..4892df5f2 100644 --- a/docs/content/gettingstarted.md +++ b/docs/content/gettingstarted.md @@ -90,7 +90,7 @@ Web-Mystery Tutorials: - Running a Discord bot written in Deno in Docker YouTube Tutorials: -- Coming soon to [NTM Development](https://www.youtube.com/channel/UCkOFck-WCQtolha4NJuK7zA/) +- [Discordeno Bot Tutorials YouTube series](https://youtu.be/rIph9-BGsuQ) --- diff --git a/egg.yml b/egg.yml index 79b0633d1..c25ed8734 100644 --- a/egg.yml +++ b/egg.yml @@ -2,7 +2,6 @@ name: Discordeno description: >- Discord Deno TypeScript API library wrapper(Officially vetted library by Discord Team) https://discordeno.netlify.app -version: 9.0.15 stable: true entry: mod.ts repository: 'https://github.com/Skillz4Killz/Discordeno' diff --git a/src/controllers/cache.ts b/src/controllers/cache.ts index 55829c14a..a0588c6ac 100644 --- a/src/controllers/cache.ts +++ b/src/controllers/cache.ts @@ -93,6 +93,11 @@ export let cacheHandlers = { return cache[table].has(key); }, + /** Get the number of key-value pairs */ + size: async (table: TableName) => { + return cache[table].size; + }, + // Done differently to have overloads /** Add a key value pair to the cache */ set, diff --git a/src/controllers/misc.ts b/src/controllers/misc.ts index 54f60991d..66eb2d94b 100644 --- a/src/controllers/misc.ts +++ b/src/controllers/misc.ts @@ -26,8 +26,8 @@ export async function handleInternalReady( // Triggered on each shard eventHandlers.shardReady?.(shardID); if (payload.shard && shardID === payload.shard[1] - 1) { - // Wait 10 seconds to allow all guild create events to be processed - await delay(10000); + // Wait for 5 seconds to allow all guild create events to be processed + await delay(5000); cache.isReady = true; eventHandlers.ready?.(); } diff --git a/src/handlers/channel.ts b/src/handlers/channel.ts index 37f2e7a9f..fedba0bd1 100644 --- a/src/handlers/channel.ts +++ b/src/handlers/channel.ts @@ -1,4 +1,3 @@ -import { endpoints } from "../constants/discord.ts"; import { cacheHandlers } from "../controllers/cache.ts"; import { RequestManager } from "../module/requestManager.ts"; import { structures } from "../structures/mod.ts"; @@ -17,10 +16,8 @@ import { Errors } from "../types/errors.ts"; import { PermissionOverwrite } from "../types/guild.ts"; import { MessageCreateOptions } from "../types/message.ts"; import { Permissions } from "../types/permission.ts"; -import { - botHasChannelPermissions, - calculateBits, -} from "../utils/permissions.ts"; +import { endpoints } from "../utils/constants.ts"; +import { botHasChannelPermissions } 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( @@ -48,16 +45,22 @@ export async function getMessage( channelID: string, id: string, ) { + const hasViewChannelPerm = await botHasChannelPermissions( + channelID, + [Permissions.VIEW_CHANNEL], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.VIEW_CHANNEL]) + !hasViewChannelPerm ) { throw new Error(Errors.MISSING_VIEW_CHANNEL); } + + const hasReadMessageHistoryPerm = await botHasChannelPermissions( + channelID, + [Permissions.READ_MESSAGE_HISTORY], + ); if ( - !botHasChannelPermissions( - channelID, - [Permissions.READ_MESSAGE_HISTORY], - ) + !hasReadMessageHistoryPerm ) { throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY); } @@ -77,16 +80,22 @@ export async function getMessages( | GetMessagesAround | GetMessages, ) { + const hasViewChannelPerm = await botHasChannelPermissions( + channelID, + [Permissions.VIEW_CHANNEL], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.VIEW_CHANNEL]) + !hasViewChannelPerm ) { throw new Error(Errors.MISSING_VIEW_CHANNEL); } + + const hasReadMessageHistoryPerm = await botHasChannelPermissions( + channelID, + [Permissions.READ_MESSAGE_HISTORY], + ); if ( - !botHasChannelPermissions( - channelID, - [Permissions.READ_MESSAGE_HISTORY], - ) + !hasReadMessageHistoryPerm ) { throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY); } @@ -114,24 +123,34 @@ export async function sendMessage( content: string | MessageContent, ) { if (typeof content === "string") content = { content }; + const hasSendMessagesPerm = await botHasChannelPermissions( + channelID, + [Permissions.SEND_MESSAGES], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.SEND_MESSAGES]) + !hasSendMessagesPerm ) { throw new Error(Errors.MISSING_SEND_MESSAGES); } + + const hasSendTtsMessagesPerm = await botHasChannelPermissions( + channelID, + [Permissions.SEND_TTS_MESSAGES], + ); if ( content.tts && - !botHasChannelPermissions( - channelID, - [Permissions.SEND_TTS_MESSAGES], - ) + !hasSendTtsMessagesPerm ) { throw new Error(Errors.MISSING_SEND_TTS_MESSAGE); } + const hasEmbedLinksPerm = await botHasChannelPermissions( + channelID, + [Permissions.EMBED_LINKS], + ); if ( content.embed && - !botHasChannelPermissions(channelID, [Permissions.EMBED_LINKS]) + !hasEmbedLinksPerm ) { throw new Error(Errors.MISSING_EMBED_LINKS); } @@ -165,6 +184,17 @@ export async function sendMessage( content.mentions.roles = content.mentions.roles.slice(0, 100); } } + + if (content.mentions.repliedUser) { + if ( + !(await botHasChannelPermissions( + channelID, + [Permissions.READ_MESSAGE_HISTORY], + )) + ) { + throw new Error(Errors.MISSING_SEND_MESSAGES); + } + } } const channel = await cacheHandlers.get("channels", channelID); @@ -180,7 +210,15 @@ export async function sendMessage( endpoints.CHANNEL_MESSAGES(channelID), { ...content, - allowed_mentions: content.mentions, + allowed_mentions: content.mentions + ? { + ...content.mentions, + replied_user: content.mentions.repliedUser !== false, + } + : undefined, + message_reference: { + message_id: content.replyMessageID, + }, }, ); @@ -188,13 +226,17 @@ export async function sendMessage( } /** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */ -export function deleteMessages( +export async function deleteMessages( channelID: string, ids: string[], reason?: string, ) { + const hasManageMessages = await botHasChannelPermissions( + channelID, + [Permissions.MANAGE_MESSAGES], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES]) + !hasManageMessages ) { throw new Error(Errors.MISSING_MANAGE_MESSAGES); } @@ -215,9 +257,13 @@ export function deleteMessages( } /** Gets the invites for this channel. Requires MANAGE_CHANNEL */ -export function getChannelInvites(channelID: string) { +export async function getChannelInvites(channelID: string) { + const hasManagaChannels = await botHasChannelPermissions( + channelID, + [Permissions.MANAGE_CHANNELS], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.MANAGE_CHANNELS]) + !hasManagaChannels ) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -225,12 +271,16 @@ export function getChannelInvites(channelID: string) { } /** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */ -export function createInvite(channelID: string, options: CreateInviteOptions) { +export async function createInvite( + channelID: string, + options: CreateInviteOptions, +) { + const hasCreateInstantInvitePerm = await botHasChannelPermissions( + channelID, + [Permissions.CREATE_INSTANT_INVITE], + ); if ( - !botHasChannelPermissions( - channelID, - [Permissions.CREATE_INSTANT_INVITE], - ) + !hasCreateInstantInvitePerm ) { throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE); } @@ -238,9 +288,13 @@ export function createInvite(channelID: string, options: CreateInviteOptions) { } /** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */ -export function getChannelWebhooks(channelID: string) { +export async function getChannelWebhooks(channelID: string) { + const hasManageWebhooksPerm = await botHasChannelPermissions( + channelID, + [Permissions.MANAGE_WEBHOOKS], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.MANAGE_WEBHOOKS]) + !hasManageWebhooksPerm ) { throw new Error(Errors.MISSING_MANAGE_WEBHOOKS); } @@ -293,12 +347,17 @@ function processEditChannelQueue() { } } -export function editChannel( +export async function editChannel( channelID: string, options: ChannelEditOptions, + reason?: string, ) { + const hasManageChannelsPerm = await botHasChannelPermissions( + channelID, + [Permissions.MANAGE_CHANNELS], + ); if ( - !botHasChannelPermissions(channelID, [Permissions.MANAGE_CHANNELS]) + !hasManageChannelsPerm ) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -352,7 +411,10 @@ export function editChannel( return RequestManager.patch( endpoints.GUILD_CHANNEL(channelID), - payload, + { + ...payload, + reason, + }, ); } @@ -361,8 +423,12 @@ export async function followChannel( sourceChannelID: string, targetChannelID: string, ) { + const hasManageWebhooksPerm = await botHasChannelPermissions( + targetChannelID, + [Permissions.MANAGE_WEBHOOKS], + ); if ( - !botHasChannelPermissions(targetChannelID, [Permissions.MANAGE_WEBHOOKS]) + !hasManageWebhooksPerm ) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -376,3 +442,30 @@ export async function followChannel( return data.webhook_id; } + +/** + * Checks whether a channel is synchronized with its parent/category channel or not. + * @param channelID The ID of the channel to test for synchronization + * @return Returns `true` if the channel is synchronized, otherwise `false`. Returns `false` if the channel is not cached. + */ +export async function isChannelSynced(channelID: string) { + const channel = await cacheHandlers.get("channels", channelID); + if (!channel?.parentID) return false; + + const parentChannel = await cacheHandlers.get("channels", channel.parentID); + if (!parentChannel) return false; + + return channel.permission_overwrites?.every((overwrite) => { + const permission = parentChannel.permission_overwrites?.find((ow) => + ow.id === overwrite.id + ); + if (!permission) return false; + if ( + overwrite.allow !== permission.allow || overwrite.deny !== permission.deny + ) { + return false; + } + + return true; + }); +} diff --git a/src/handlers/guild.ts b/src/handlers/guild.ts index f80780993..ebcdb7e6e 100644 --- a/src/handlers/guild.ts +++ b/src/handlers/guild.ts @@ -1,4 +1,3 @@ -import { endpoints } from "../constants/discord.ts"; import { cacheHandlers } from "../controllers/cache.ts"; import { identifyPayload } from "../module/client.ts"; import { RequestManager } from "../module/requestManager.ts"; @@ -6,6 +5,7 @@ import { requestAllMembers } from "../module/shardingManager.ts"; import { Guild } from "../structures/guild.ts"; import { Member } from "../structures/member.ts"; import { structures } from "../structures/mod.ts"; +import { Template } from "../structures/template.ts"; import { ImageFormats, ImageSize } from "../types/cdn.ts"; import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts"; import { Errors } from "../types/errors.ts"; @@ -14,16 +14,22 @@ import { BanOptions, ChannelCreateOptions, CreateEmojisOptions, + CreateGuildFromTemplate, + CreateGuildPayload, + CreateGuildTemplate, CreateRoleOptions, CreateServerOptions, EditEmojisOptions, + EditGuildTemplate, EditIntegrationOptions, FetchMembersOptions, GetAuditLogsOptions, GuildEditOptions, + GuildTemplate, PositionSwap, PruneOptions, PrunePayload, + UpdateGuildPayload, UserPayload, } from "../types/guild.ts"; import { MemberCreatePayload } from "../types/member.ts"; @@ -32,6 +38,7 @@ import { Permissions } from "../types/permission.ts"; import { RoleData } from "../types/role.ts"; import { formatImageURL } from "../utils/cdn.ts"; import { Collection } from "../utils/collection.ts"; +import { endpoints } from "../utils/constants.ts"; import { botHasPermission, calculateBits } from "../utils/permissions.ts"; import { urlToBase64 } from "../utils/utils.ts"; @@ -97,7 +104,11 @@ export async function createGuildChannel( name: string, options?: ChannelCreateOptions, ) { - if (!botHasPermission(guild.id, [Permissions.MANAGE_CHANNELS])) { + const hasPerm = await botHasPermission( + guild.id, + [Permissions.MANAGE_CHANNELS], + ); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -126,12 +137,16 @@ export async function createGuildChannel( } /** Delete a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */ -export function deleteChannel( +export async function deleteChannel( guildID: string, channelID: string, reason?: string, ) { - if (!botHasPermission(guildID, [Permissions.MANAGE_CHANNELS])) { + const hasPerm = await botHasPermission( + guildID, + [Permissions.MANAGE_CHANNELS], + ); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_CHANNELS); } @@ -186,16 +201,20 @@ export function swapChannels( * * ⚠️ **ADVANCED USE ONLY: Your members will be cached in your guild most likely. Only use this when you are absolutely sure the member is not cached.** */ -export async function getMember(guildID: string, id: string) { +export async function getMember( + guildID: string, + id: string, + options?: { force?: boolean }, +) { const guild = await cacheHandlers.get("guilds", guildID); - if (!guild) return; + if (!guild && !options?.force) return; const data = await RequestManager.get( endpoints.GUILD_MEMBER(guildID, id), ) as MemberCreatePayload; - const member = await structures.createMember(data, guild.id); - guild.members.set(id, member); + const member = await structures.createMember(data, guildID); + guild?.members.set(id, member); return member; } @@ -223,9 +242,8 @@ export async function createEmoji( image: string, options: CreateEmojisOptions, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_EMOJIS); } @@ -241,16 +259,16 @@ export async function createEmoji( } /** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */ -export function editEmoji( +export async function editEmoji( guildID: string, id: string, options: EditEmojisOptions, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_EMOJIS); } + return RequestManager.patch(endpoints.GUILD_EMOJI(guildID, id), { name: options.name, roles: options.roles, @@ -258,12 +276,16 @@ export function editEmoji( } /** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */ -export function deleteEmoji(guildID: string, id: string, reason?: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]) - ) { +export async function deleteEmoji( + guildID: string, + id: string, + reason?: string, +) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_EMOJIS); } + return RequestManager.delete( endpoints.GUILD_EMOJI(guildID, id), { reason }, @@ -281,11 +303,11 @@ export async function createGuildRole( options: CreateRoleOptions, reason?: string, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_ROLES]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_ROLES); } + const result = await RequestManager.post( endpoints.GUILD_ROLES(guildID), { @@ -307,16 +329,16 @@ export async function createGuildRole( } /** Edit a guild role. Requires the MANAGE_ROLES permission. */ -export function editRole( +export async function editRole( guildID: string, id: string, options: CreateRoleOptions, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_ROLES]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_ROLES); } + return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), { ...options, permissions: options.permissions @@ -326,12 +348,12 @@ export function editRole( } /** Delete a guild role. Requires the MANAGE_ROLES permission. */ -export function deleteRole(guildID: string, id: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_ROLES]) - ) { +export async function deleteRole(guildID: string, id: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_ROLES); } + return RequestManager.delete(endpoints.GUILD_ROLE(guildID, id)); } @@ -339,22 +361,22 @@ export function deleteRole(guildID: string, id: string) { * * ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your roles will be cached in your guild.** */ -export function getRoles(guildID: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_ROLES]) - ) { +export async function getRoles(guildID: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_ROLES); } + return RequestManager.get(endpoints.GUILD_ROLES(guildID)); } /** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */ -export function swapRoles(guildID: string, rolePositons: PositionSwap) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_ROLES]) - ) { +export async function swapRoles(guildID: string, rolePositons: PositionSwap) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_ROLES); } + return RequestManager.patch(endpoints.GUILD_ROLES(guildID), rolePositons); } @@ -363,9 +385,9 @@ export async function getPruneCount(guildID: string, options: PruneOptions) { if (options.days < 1) { throw new Error(Errors.PRUNE_MIN_DAYS); } - if ( - !botHasPermission(guildID, [Permissions.KICK_MEMBERS]) - ) { + + const hasPerm = await botHasPermission(guildID, [Permissions.KICK_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_KICK_MEMBERS); } @@ -378,13 +400,13 @@ export async function getPruneCount(guildID: string, options: PruneOptions) { } /** Begin pruning all members in the given time period */ -export function pruneMembers(guildID: string, options: PruneOptions) { +export async function pruneMembers(guildID: string, options: PruneOptions) { if (options.days < 1) { throw new Error(Errors.PRUNE_MIN_DAYS); } - if ( - !botHasPermission(guildID, [Permissions.KICK_MEMBERS]) - ) { + + const hasPerm = await botHasPermission(guildID, [Permissions.KICK_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_KICK_MEMBERS); } @@ -405,8 +427,12 @@ export function fetchMembers(guild: Guild, options?: FetchMembersOptions) { } /** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */ -export function getAuditLogs(guildID: string, options: GetAuditLogsOptions) { - if (!botHasPermission(guildID, [Permissions.VIEW_AUDIT_LOG])) { +export async function getAuditLogs( + guildID: string, + options: GetAuditLogsOptions, +) { + const hasPerm = await botHasPermission(guildID, [Permissions.VIEW_AUDIT_LOG]); + if (!hasPerm) { throw new Error(Errors.MISSING_VIEW_AUDIT_LOG); } @@ -419,26 +445,26 @@ export function getAuditLogs(guildID: string, options: GetAuditLogsOptions) { } /** Returns the guild embed object. Requires the MANAGE_GUILD permission. */ -export function getEmbed(guildID: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { +export async function getEmbed(guildID: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.get(endpoints.GUILD_EMBED(guildID)); } /** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */ -export function editEmbed( +export async function editEmbed( guildID: string, enabled: boolean, channelID?: string | null, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.patch( endpoints.GUILD_EMBED(guildID), { enabled, channel_id: channelID }, @@ -451,26 +477,26 @@ export function getVanityURL(guildID: string) { } /** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */ -export function getIntegrations(guildID: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { +export async function getIntegrations(guildID: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.get(endpoints.GUILD_INTEGRATIONS(guildID)); } /** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */ -export function editIntegration( +export async function editIntegration( guildID: string, id: string, options: EditIntegrationOptions, ) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.patch( endpoints.GUILD_INTEGRATION(guildID, id), options, @@ -478,30 +504,29 @@ export function editIntegration( } /** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */ -export function deleteIntegration(guildID: string, id: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { +export async function deleteIntegration(guildID: string, id: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.delete(endpoints.GUILD_INTEGRATION(guildID, id)); } /** Sync an integration. Requires the MANAGE_GUILD permission. */ -export function syncIntegration(guildID: string, id: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { +export async function syncIntegration(guildID: string, id: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(guildID, id)); } /** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */ export async function getBans(guildID: string) { - if ( - !botHasPermission(guildID, [Permissions.BAN_MEMBERS]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_BAN_MEMBERS); } @@ -515,10 +540,9 @@ export async function getBans(guildID: string) { } /** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */ -export function getBan(guildID: string, memberID: string) { - if ( - !botHasPermission(guildID, [Permissions.BAN_MEMBERS]) - ) { +export async function getBan(guildID: string, memberID: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_BAN_MEMBERS); } @@ -528,10 +552,9 @@ export function getBan(guildID: string, memberID: string) { } /** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */ -export function ban(guildID: string, id: string, options: BanOptions) { - if ( - !botHasPermission(guildID, [Permissions.BAN_MEMBERS]) - ) { +export async function ban(guildID: string, id: string, options: BanOptions) { + const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_BAN_MEMBERS); } @@ -542,10 +565,9 @@ export function ban(guildID: string, id: string, options: BanOptions) { } /** Remove the ban for a user. REquires BAN_MEMBERS permission */ -export function unban(guildID: string, id: string) { - if ( - !botHasPermission(guildID, [Permissions.BAN_MEMBERS]) - ) { +export async function unban(guildID: string, id: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]); + if (!hasPerm) { throw new Error(Errors.MISSING_BAN_MEMBERS); } return RequestManager.delete(endpoints.GUILD_BAN(guildID, id)); @@ -553,9 +575,8 @@ export function unban(guildID: string, id: string) { /** Modify a guilds settings. Requires the MANAGE_GUILD permission. */ export async function editGuild(guildID: string, options: GuildEditOptions) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } @@ -575,12 +596,12 @@ export async function editGuild(guildID: string, options: GuildEditOptions) { } /** Get all the invites for this guild. Requires MANAGE_GUILD permission */ -export function getInvites(guildID: string) { - if ( - !botHasPermission(guildID, [Permissions.MANAGE_GUILD]) - ) { +export async function getInvites(guildID: string) { + const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_GUILD); } + return RequestManager.get(endpoints.GUILD_INVITES(guildID)); } @@ -595,8 +616,12 @@ export function getVoiceRegions(guildID: string) { } /** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */ -export function getWebhooks(guildID: string) { - if (!botHasPermission(guildID, [Permissions.MANAGE_WEBHOOKS])) { +export async function getWebhooks(guildID: string) { + const hasPerm = await botHasPermission( + guildID, + [Permissions.MANAGE_WEBHOOKS], + ); + if (!hasPerm) { throw new Error(Errors.MISSING_MANAGE_WEBHOOKS); } @@ -607,3 +632,158 @@ export function getWebhooks(guildID: string) { export function getUser(userID: string) { return RequestManager.get(endpoints.USER(userID)) as Promise; } + +/** + * ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get() + * + * Advanced Devs: + * 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( + endpoints.GUILD(guildID), + { with_counts: counts }, + ) as Promise; +} + +/** Returns the guild template if it exists */ +export function getGuildTemplate( + guildID: string, + templateCode: string, +) { + return RequestManager.get( + `${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`, + ) as Promise