mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-02 00:40:07 +00:00
feat(util/permissions): improve permission-checking (#381)
* Update permissions.ts
* add(permissions): explaining comments
Since Discord permissions are quiet complex it is better to have detailed comments explaining everything.
* docs: add better permissions jsdoc comments
* types: add missing errors
* change imports
* we want a string here
* strange commit here
* we need an s in tts
* permissions: update channel permission handling
* permissions: update guild permission handling
* permissions: update member permission handling
* permissions: update message permission handling
* permissions: update webhook permission handling
* fix this buggg
* fix: typo
* better func names
* better description
* permissions(editMember): add permission check if channel_id is provided
* added todo for deaf
* fixxx
* FIIIXXX
* Update permissions.ts
* throwOn to require
* change up review things
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Apply suggestions from code review
Co-authored-by: Ayyan <ayyantee@gmail.com>
* BigInt() to n
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Ayyan <ayyantee@gmail.com>
* Update src/util/permissions.ts
Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com>
* missed this
* Update permissions.ts
* here enum is needed
* use set so errors arenn't strange
* dumb idea
* hasChannelPermissions functions are nice to have
* role to guild
* bugg
* fix(handlers): createGuildChannel check overwrite perms
* remove redundant if check
* fixes
* Update guild.ts
* bettrrrr
* Revert "bettrrrr"
This reverts commit ecbd30e160.
* I hate it
* fix fix
* fixxesss
* this function is better
* oh forgot these
* better I guess
* more functions
* silly me forgot to remove console.logs
* buuuuugs
* small changes
* Update permission.ts
* Update permissions.ts
* Update GUILD_CREATE.ts
* Update channel.ts
* remove this
* suggestions
Co-authored-by: Ayyan <ayyantee@gmail.com>
Co-authored-by: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { eventHandlers } from "../../bot.ts";
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { CreateGuildPayload, DiscordPayload } from "../../types/mod.ts";
|
||||
import { cache } from "../../util/cache.ts";
|
||||
import { basicShards } from "../../ws/shard.ts";
|
||||
import { structures } from "../../structures/mod.ts";
|
||||
import { cacheHandlers } from "../../cache.ts";
|
||||
|
||||
export async function handleGuildCreate(
|
||||
data: DiscordPayload,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { RequestManager } from "../rest/request_manager.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import {
|
||||
ChannelEditOptions,
|
||||
ChannelTypes,
|
||||
@@ -20,11 +22,10 @@ import {
|
||||
import { endpoints } from "../util/constants.ts";
|
||||
import {
|
||||
botHasChannelPermissions,
|
||||
botHasPermission,
|
||||
calculateBits,
|
||||
requireBotChannelPermissions,
|
||||
requireBotGuildPermissions,
|
||||
} from "../util/permissions.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
|
||||
/** Checks if a channel overwrite for a user id or a role id has permission in this channel */
|
||||
export function channelOverwriteHasPermission(
|
||||
@@ -48,33 +49,15 @@ export function channelOverwriteHasPermission(
|
||||
}
|
||||
|
||||
/** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */
|
||||
export async function getMessage(
|
||||
channelID: string,
|
||||
id: string,
|
||||
) {
|
||||
const hasViewChannelPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["VIEW_CHANNEL"],
|
||||
);
|
||||
if (
|
||||
!hasViewChannelPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_VIEW_CHANNEL);
|
||||
}
|
||||
export async function getMessage(channelID: string, id: string) {
|
||||
await requireBotChannelPermissions(channelID, [
|
||||
"VIEW_CHANNEL",
|
||||
"READ_MESSAGE_HISTORY",
|
||||
]);
|
||||
|
||||
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["READ_MESSAGE_HISTORY"],
|
||||
);
|
||||
if (
|
||||
!hasReadMessageHistoryPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
|
||||
}
|
||||
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.CHANNEL_MESSAGE(channelID, id),
|
||||
) as MessageCreateOptions;
|
||||
)) as MessageCreateOptions;
|
||||
|
||||
return structures.createMessageStruct(result);
|
||||
}
|
||||
@@ -88,25 +71,10 @@ export async function getMessages(
|
||||
| GetMessagesAround
|
||||
| GetMessages,
|
||||
) {
|
||||
const hasViewChannelPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["VIEW_CHANNEL"],
|
||||
);
|
||||
if (
|
||||
!hasViewChannelPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_VIEW_CHANNEL);
|
||||
}
|
||||
|
||||
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["READ_MESSAGE_HISTORY"],
|
||||
);
|
||||
if (
|
||||
!hasReadMessageHistoryPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, [
|
||||
"VIEW_CHANNEL",
|
||||
"READ_MESSAGE_HISTORY",
|
||||
]);
|
||||
|
||||
if (options?.limit && options.limit > 100) return;
|
||||
|
||||
@@ -174,57 +142,29 @@ export async function sendMessage(
|
||||
if (typeof content === "string") content = { content };
|
||||
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
// If the channel is cached, we can do extra checks/safety
|
||||
if (channel) {
|
||||
if (
|
||||
![ChannelTypes.DM, ChannelTypes.GUILD_NEWS, ChannelTypes.GUILD_TEXT]
|
||||
.includes(channel.type)
|
||||
![
|
||||
ChannelTypes.DM,
|
||||
ChannelTypes.GUILD_NEWS,
|
||||
ChannelTypes.GUILD_TEXT,
|
||||
].includes(channel.type)
|
||||
) {
|
||||
throw new Error(Errors.CHANNEL_NOT_TEXT_BASED);
|
||||
}
|
||||
|
||||
const hasSendMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["SEND_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasSendMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_MESSAGES);
|
||||
const requiredPerms: Set<Permission> = new Set([
|
||||
"SEND_MESSAGES",
|
||||
"VIEW_CHANNEL",
|
||||
]);
|
||||
|
||||
if (content.tts) requiredPerms.add("SEND_TTS_MESSAGES");
|
||||
if (content.embed) requiredPerms.add("EMBED_LINKS");
|
||||
if (content.replyMessageID || content.mentions?.repliedUser) {
|
||||
requiredPerms.add("READ_MESSAGE_HISTORY");
|
||||
}
|
||||
|
||||
const hasSendTtsMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["SEND_TTS_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
content.tts &&
|
||||
!hasSendTtsMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
|
||||
}
|
||||
|
||||
const hasEmbedLinksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["EMBED_LINKS"],
|
||||
);
|
||||
if (
|
||||
content.embed &&
|
||||
!hasEmbedLinksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_EMBED_LINKS);
|
||||
}
|
||||
|
||||
if (content.mentions?.repliedUser) {
|
||||
if (
|
||||
!(await botHasChannelPermissions(
|
||||
channelID,
|
||||
["READ_MESSAGE_HISTORY"],
|
||||
))
|
||||
) {
|
||||
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
|
||||
}
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, [...requiredPerms]);
|
||||
}
|
||||
|
||||
// Use ... for content length due to unicode characters and js .length handling
|
||||
@@ -235,8 +175,8 @@ export async function sendMessage(
|
||||
if (content.mentions) {
|
||||
if (content.mentions.users?.length) {
|
||||
if (content.mentions.parse?.includes("users")) {
|
||||
content.mentions.parse = content.mentions.parse.filter((p) =>
|
||||
p !== "users"
|
||||
content.mentions.parse = content.mentions.parse.filter(
|
||||
(p) => p !== "users",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -247,8 +187,8 @@ export async function sendMessage(
|
||||
|
||||
if (content.mentions.roles?.length) {
|
||||
if (content.mentions.parse?.includes("roles")) {
|
||||
content.mentions.parse = content.mentions.parse.filter((p) =>
|
||||
p !== "roles"
|
||||
content.mentions.parse = content.mentions.parse.filter(
|
||||
(p) => p !== "roles",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,7 +198,7 @@ export async function sendMessage(
|
||||
}
|
||||
}
|
||||
|
||||
const result = await RequestManager.post(
|
||||
const result = (await RequestManager.post(
|
||||
endpoints.CHANNEL_MESSAGES(channelID),
|
||||
{
|
||||
...content,
|
||||
@@ -277,9 +217,9 @@ export async function sendMessage(
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
) as MessageCreateOptions;
|
||||
)) as MessageCreateOptions;
|
||||
|
||||
return structures.createMessageStruct(result as MessageCreateOptions);
|
||||
return structures.createMessageStruct(result);
|
||||
}
|
||||
|
||||
/** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */
|
||||
@@ -288,15 +228,8 @@ export async function deleteMessages(
|
||||
ids: string[],
|
||||
reason?: string,
|
||||
) {
|
||||
const hasManageMessages = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessages
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
if (ids.length < 2) {
|
||||
throw new Error(Errors.DELETE_MESSAGES_MIN);
|
||||
}
|
||||
@@ -320,15 +253,7 @@ export async function deleteMessages(
|
||||
|
||||
/** Gets the invites for this channel. Requires MANAGE_CHANNEL */
|
||||
export async function getChannelInvites(channelID: string) {
|
||||
const hasManagaChannels = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_CHANNELS"],
|
||||
);
|
||||
if (
|
||||
!hasManagaChannels
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_CHANNELS"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.CHANNEL_INVITES(channelID));
|
||||
|
||||
@@ -340,15 +265,7 @@ export async function createInvite(
|
||||
channelID: string,
|
||||
options: CreateInviteOptions,
|
||||
) {
|
||||
const hasCreateInstantInvitePerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["CREATE_INSTANT_INVITE"],
|
||||
);
|
||||
if (
|
||||
!hasCreateInstantInvitePerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["CREATE_INSTANT_INVITE"]);
|
||||
|
||||
if (options.max_age && (options.max_age > 604800 || options.max_age < 0)) {
|
||||
console.log(
|
||||
@@ -374,52 +291,33 @@ export async function createInvite(
|
||||
|
||||
/** Returns an invite for the given code. */
|
||||
export async function getInvite(inviteCode: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.INVITE(inviteCode),
|
||||
);
|
||||
const result = await RequestManager.get(endpoints.INVITE(inviteCode));
|
||||
|
||||
return result as InvitePayload;
|
||||
}
|
||||
|
||||
/** Deletes an invite for the given code. Requires `MANAGE_CHANNELS` or `MANAGE_GUILD` permission */
|
||||
export async function deleteInvite(
|
||||
channelID: string,
|
||||
inviteCode: string,
|
||||
) {
|
||||
const hasPerm = await botHasChannelPermissions(channelID, [
|
||||
export async function deleteInvite(channelID: string, inviteCode: string) {
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
|
||||
if (!channel) throw new Error(Errors.CHANNEL_NOT_FOUND);
|
||||
|
||||
const hasPerm = await botHasChannelPermissions(channel, [
|
||||
"MANAGE_CHANNELS",
|
||||
]);
|
||||
|
||||
if (!hasPerm) {
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
|
||||
const hasManageGuildPerm = await botHasPermission(channel!.guildID, [
|
||||
"MANAGE_GUILD",
|
||||
]);
|
||||
|
||||
if (!hasManageGuildPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
await requireBotGuildPermissions(channel!.guildID, ["MANAGE_GUILD"]);
|
||||
}
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.INVITE(inviteCode),
|
||||
);
|
||||
const result = await RequestManager.delete(endpoints.INVITE(inviteCode));
|
||||
|
||||
return result as InvitePayload;
|
||||
}
|
||||
|
||||
/** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */
|
||||
export async function getChannelWebhooks(channelID: string) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
const result = await RequestManager.get(
|
||||
endpoints.CHANNEL_WEBHOOKS(channelID),
|
||||
@@ -461,10 +359,7 @@ function processEditChannelQueue() {
|
||||
const secondDetails = request.items.shift();
|
||||
if (!secondDetails) return;
|
||||
|
||||
return editChannel(
|
||||
secondDetails.channelID,
|
||||
secondDetails.options,
|
||||
);
|
||||
return editChannel(secondDetails.channelID, secondDetails.options);
|
||||
});
|
||||
|
||||
if (editChannelNameTopicQueue.size) {
|
||||
@@ -480,15 +375,7 @@ export async function editChannel(
|
||||
options: ChannelEditOptions,
|
||||
reason?: string,
|
||||
) {
|
||||
const hasManageChannelsPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_CHANNELS"],
|
||||
);
|
||||
if (
|
||||
!hasManageChannelsPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_CHANNELS"]);
|
||||
|
||||
if (options.name || options.topic) {
|
||||
const request = editChannelNameTopicQueue.get(channelID);
|
||||
@@ -524,24 +411,19 @@ export async function editChannel(
|
||||
// deno-lint-ignore camelcase
|
||||
user_limit: options.userLimit,
|
||||
// deno-lint-ignore camelcase
|
||||
permission_overwrites: options.overwrites?.map(
|
||||
(overwrite) => {
|
||||
return {
|
||||
...overwrite,
|
||||
allow: calculateBits(overwrite.allow),
|
||||
deny: calculateBits(overwrite.deny),
|
||||
};
|
||||
},
|
||||
),
|
||||
permission_overwrites: options.overwrites?.map((overwrite) => {
|
||||
return {
|
||||
...overwrite,
|
||||
allow: calculateBits(overwrite.allow),
|
||||
deny: calculateBits(overwrite.deny),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.CHANNEL_BASE(channelID),
|
||||
{
|
||||
...payload,
|
||||
reason,
|
||||
},
|
||||
);
|
||||
const result = await RequestManager.patch(endpoints.CHANNEL_BASE(channelID), {
|
||||
...payload,
|
||||
reason,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -551,22 +433,14 @@ export async function followChannel(
|
||||
sourceChannelID: string,
|
||||
targetChannelID: string,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
targetChannelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
await requireBotChannelPermissions(targetChannelID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
const data = await RequestManager.post(
|
||||
const data = (await RequestManager.post(
|
||||
endpoints.CHANNEL_FOLLOW(sourceChannelID),
|
||||
{
|
||||
webhook_channel_id: targetChannelID,
|
||||
},
|
||||
) as FollowedChannelPayload;
|
||||
)) as FollowedChannelPayload;
|
||||
|
||||
return data.webhook_id;
|
||||
}
|
||||
@@ -584,11 +458,12 @@ export async function isChannelSynced(channelID: string) {
|
||||
if (!parentChannel) return false;
|
||||
|
||||
return channel.permissionOverwrites?.every((overwrite) => {
|
||||
const permission = parentChannel.permissionOverwrites?.find((ow) =>
|
||||
ow.id === overwrite.id
|
||||
const permission = parentChannel.permissionOverwrites?.find(
|
||||
(ow) => ow.id === overwrite.id,
|
||||
);
|
||||
if (!permission) return false;
|
||||
return !(overwrite.allow !== permission.allow ||
|
||||
overwrite.deny !== permission.deny);
|
||||
return !(
|
||||
overwrite.allow !== permission.allow || overwrite.deny !== permission.deny
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { identifyPayload } from "../bot.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { RequestManager } from "../rest/request_manager.ts";
|
||||
import { Guild, Member, structures } from "../structures/mod.ts";
|
||||
import {
|
||||
AuditLogs,
|
||||
BannedUser,
|
||||
@@ -28,6 +30,7 @@ import {
|
||||
Intents,
|
||||
MemberCreatePayload,
|
||||
Overwrite,
|
||||
Permission,
|
||||
PositionSwap,
|
||||
PruneOptions,
|
||||
PrunePayload,
|
||||
@@ -37,22 +40,23 @@ import {
|
||||
} from "../types/mod.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { endpoints } from "../util/constants.ts";
|
||||
import { botHasPermission, calculateBits } from "../util/permissions.ts";
|
||||
import {
|
||||
calculateBits,
|
||||
requireBotGuildPermissions,
|
||||
} from "../util/permissions.ts";
|
||||
import {
|
||||
camelKeysToSnakeCase,
|
||||
formatImageURL,
|
||||
urlToBase64,
|
||||
} from "../util/utils.ts";
|
||||
import { requestAllMembers } from "../ws/shard_manager.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { Guild, Member, structures } from "../structures/mod.ts";
|
||||
|
||||
/** Create a new guild. Returns a guild object on success. Fires a Guild Create Gateway event. This endpoint can be used only by bots in less than 10 guilds. */
|
||||
export async function createGuild(options: CreateServerOptions) {
|
||||
const guild = await RequestManager.post(
|
||||
const guild = (await RequestManager.post(
|
||||
endpoints.GUILDS,
|
||||
options,
|
||||
) as CreateGuildPayload;
|
||||
)) as CreateGuildPayload;
|
||||
|
||||
return structures.createGuildStruct(guild, 0);
|
||||
}
|
||||
@@ -120,25 +124,29 @@ export async function createGuildChannel(
|
||||
name: string,
|
||||
options?: ChannelCreateOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_CHANNELS"],
|
||||
);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
const requiredPerms: Set<Permission> = new Set(["MANAGE_CHANNELS"]);
|
||||
|
||||
const result = (await RequestManager.post(endpoints.GUILD_CHANNELS(guildID), {
|
||||
...options,
|
||||
name,
|
||||
permission_overwrites: options?.permissionOverwrites?.map((perm) => ({
|
||||
...perm,
|
||||
options?.permissionOverwrites?.forEach((overwrite) => {
|
||||
overwrite.allow.forEach(requiredPerms.add, requiredPerms);
|
||||
overwrite.deny.forEach(requiredPerms.add, requiredPerms);
|
||||
});
|
||||
|
||||
allow: calculateBits(perm.allow),
|
||||
deny: calculateBits(perm.deny),
|
||||
})),
|
||||
type: options?.type || ChannelTypes.GUILD_TEXT,
|
||||
})) as ChannelCreatePayload;
|
||||
await requireBotGuildPermissions(guildID, [...requiredPerms]);
|
||||
|
||||
const result = (await RequestManager.post(
|
||||
endpoints.GUILD_CHANNELS(guildID),
|
||||
{
|
||||
...options,
|
||||
name,
|
||||
permission_overwrites: options?.permissionOverwrites?.map((perm) => ({
|
||||
...perm,
|
||||
|
||||
allow: calculateBits(perm.allow),
|
||||
deny: calculateBits(perm.deny),
|
||||
})),
|
||||
type: options?.type || ChannelTypes.GUILD_TEXT,
|
||||
},
|
||||
)) as ChannelCreatePayload;
|
||||
|
||||
const channelStruct = await structures.createChannelStruct(result);
|
||||
await cacheHandlers.set("channels", channelStruct.id, channelStruct);
|
||||
@@ -152,13 +160,7 @@ export async function deleteChannel(
|
||||
channelID: string,
|
||||
reason?: string,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_CHANNELS"],
|
||||
);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_CHANNELS"]);
|
||||
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) throw new Error(Errors.GUILD_NOT_FOUND);
|
||||
@@ -180,13 +182,13 @@ export async function deleteChannel(
|
||||
}
|
||||
|
||||
/** Returns a list of guild channel objects.
|
||||
*
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.**
|
||||
*/
|
||||
*
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.**
|
||||
*/
|
||||
export async function getChannels(guildID: string, addToCache = true) {
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.GUILD_CHANNELS(guildID),
|
||||
) as ChannelCreatePayload[];
|
||||
) as ChannelCreatePayload[]);
|
||||
|
||||
return Promise.all(result.map(async (res) => {
|
||||
const channelStruct = await structures.createChannelStruct(res, guildID);
|
||||
@@ -199,13 +201,13 @@ export async function getChannels(guildID: string, addToCache = true) {
|
||||
}
|
||||
|
||||
/** Fetches a single channel object from the api.
|
||||
*
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.**
|
||||
*/
|
||||
*
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your channels will be cached in your guild.**
|
||||
*/
|
||||
export async function getChannel(channelID: string, addToCache = true) {
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.CHANNEL_BASE(channelID),
|
||||
) as ChannelCreatePayload;
|
||||
)) as ChannelCreatePayload;
|
||||
|
||||
const channelStruct = await structures.createChannelStruct(
|
||||
result,
|
||||
@@ -242,13 +244,7 @@ export async function editChannelOverwrite(
|
||||
overwriteID: string,
|
||||
options: Omit<Overwrite, "id">,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_ROLES"],
|
||||
);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_OVERWRITE(channelID, overwriteID),
|
||||
@@ -268,13 +264,7 @@ export async function deleteChannelOverwrite(
|
||||
channelID: string,
|
||||
overwriteID: string,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_ROLES"],
|
||||
);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_OVERWRITE(channelID, overwriteID),
|
||||
@@ -284,9 +274,9 @@ export async function deleteChannelOverwrite(
|
||||
}
|
||||
|
||||
/** Returns a guild member object for the specified user.
|
||||
*
|
||||
* ⚠️ **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.**
|
||||
*/
|
||||
*
|
||||
* ⚠️ **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,
|
||||
@@ -295,9 +285,9 @@ export async function getMember(
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild && !options?.force) return;
|
||||
|
||||
const data = await RequestManager.get(
|
||||
const data = (await RequestManager.get(
|
||||
endpoints.GUILD_MEMBER(guildID, id),
|
||||
) as MemberCreatePayload;
|
||||
)) as MemberCreatePayload;
|
||||
|
||||
const memberStruct = await structures.createMemberStruct(data, guildID);
|
||||
await cacheHandlers.set("members", memberStruct.id, memberStruct);
|
||||
@@ -306,9 +296,9 @@ export async function getMember(
|
||||
}
|
||||
|
||||
/** Returns guild member objects for the specified user by their nickname/username.
|
||||
*
|
||||
* ⚠️ **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.**
|
||||
*/
|
||||
*
|
||||
* ⚠️ **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 getMembersByQuery(
|
||||
guildID: string,
|
||||
name: string,
|
||||
@@ -329,10 +319,7 @@ export async function createEmoji(
|
||||
image: string,
|
||||
options: CreateEmojisOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_EMOJIS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_EMOJIS"]);
|
||||
|
||||
if (image && !image.startsWith("data:image/")) {
|
||||
image = await urlToBase64(image);
|
||||
@@ -353,10 +340,7 @@ export async function editEmoji(
|
||||
id: string,
|
||||
options: EditEmojisOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_EMOJIS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_EMOJIS"]);
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_EMOJI(guildID, id),
|
||||
@@ -375,10 +359,7 @@ export async function deleteEmoji(
|
||||
id: string,
|
||||
reason?: string,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_EMOJIS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_EMOJIS"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_EMOJI(guildID, id),
|
||||
@@ -399,9 +380,9 @@ export function emojiURL(id: string, animated = false) {
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get()?.emojis
|
||||
*/
|
||||
export async function getEmojis(guildID: string, addToCache = true) {
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.GUILD_EMOJIS(guildID),
|
||||
) as Emoji[];
|
||||
)) as Emoji[];
|
||||
|
||||
if (addToCache) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
@@ -425,9 +406,9 @@ export async function getEmoji(
|
||||
emojiID: string,
|
||||
addToCache = true,
|
||||
) {
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.GUILD_EMOJI(guildID, emojiID),
|
||||
) as Emoji;
|
||||
)) as Emoji;
|
||||
|
||||
if (addToCache) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
@@ -449,19 +430,13 @@ export async function createRole(
|
||||
options: CreateRoleOptions,
|
||||
reason?: string,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.post(
|
||||
endpoints.GUILD_ROLES(guildID),
|
||||
{
|
||||
...options,
|
||||
permissions: calculateBits(options?.permissions || []),
|
||||
reason,
|
||||
},
|
||||
);
|
||||
const result = await RequestManager.post(endpoints.GUILD_ROLES(guildID), {
|
||||
...options,
|
||||
permissions: calculateBits(options?.permissions || []),
|
||||
reason,
|
||||
});
|
||||
|
||||
const roleData = result as RoleData;
|
||||
const role = await structures.createRoleStruct(roleData);
|
||||
@@ -477,10 +452,7 @@ export async function editRole(
|
||||
id: string,
|
||||
options: CreateRoleOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
|
||||
...options,
|
||||
@@ -494,10 +466,7 @@ export async function editRole(
|
||||
|
||||
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
|
||||
export async function deleteRole(guildID: string, id: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.delete(endpoints.GUILD_ROLE(guildID, id));
|
||||
|
||||
@@ -505,14 +474,11 @@ export async function deleteRole(guildID: string, id: string) {
|
||||
}
|
||||
|
||||
/** Returns a list of role objects for the guild.
|
||||
*
|
||||
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your roles will be cached in your guild.**
|
||||
*/
|
||||
*
|
||||
* ⚠️ **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 async function getRoles(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.GUILD_ROLES(guildID));
|
||||
|
||||
@@ -521,10 +487,7 @@ export async function getRoles(guildID: string) {
|
||||
|
||||
/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */
|
||||
export async function swapRoles(guildID: string, rolePositons: PositionSwap) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_ROLES(guildID),
|
||||
@@ -541,10 +504,7 @@ export async function getPruneCount(guildID: string, options?: PruneOptions) {
|
||||
throw new Error(Errors.PRUNE_MAX_DAYS);
|
||||
}
|
||||
|
||||
const hasPerm = await botHasPermission(guildID, ["KICK_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["KICK_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_PRUNE(guildID),
|
||||
@@ -566,10 +526,7 @@ export async function pruneMembers(
|
||||
if (options.days && options.days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
|
||||
if (options.days && options.days > 30) throw new Error(Errors.PRUNE_MAX_DAYS);
|
||||
|
||||
const hasPerm = await botHasPermission(guildID, ["KICK_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["KICK_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.post(
|
||||
endpoints.GUILD_PRUNE(guildID),
|
||||
@@ -586,7 +543,7 @@ export async function pruneMembers(
|
||||
* Highly recommended to use this function to fetch members instead of getMember from REST.
|
||||
* REST: 50/s global(across all shards) rate limit with ALL requests this included
|
||||
* GW(this function): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is now 960/m.
|
||||
*/
|
||||
*/
|
||||
export function fetchMembers(guild: Guild, options?: FetchMembersOptions) {
|
||||
// You can request 1 member without the intent
|
||||
if (
|
||||
@@ -612,11 +569,8 @@ export function fetchMembers(guild: Guild, options?: FetchMembersOptions) {
|
||||
* Highly recommended to **NOT** use this function to get members instead use fetchMembers().
|
||||
* REST(this function): 50/s global(across all shards) rate limit with ALL requests this included
|
||||
* GW(fetchMembers): 120/m(PER shard) rate limit. Meaning if you have 8 shards your limit is 960/m.
|
||||
*/
|
||||
export async function getMembers(
|
||||
guildID: string,
|
||||
options?: GetMemberOptions,
|
||||
) {
|
||||
*/
|
||||
export async function getMembers(guildID: string, options?: GetMemberOptions) {
|
||||
if (!(identifyPayload.intents && Intents.GUILD_MEMBERS)) {
|
||||
throw new Error(Errors.MISSING_INTENT_GUILD_MEMBERS);
|
||||
}
|
||||
@@ -629,21 +583,24 @@ export async function getMembers(
|
||||
let membersLeft = options?.limit ?? guild.memberCount;
|
||||
let loops = 1;
|
||||
while (
|
||||
(options?.limit ?? guild.memberCount) > members.size && membersLeft > 0
|
||||
(options?.limit ?? guild.memberCount) > members.size &&
|
||||
membersLeft > 0
|
||||
) {
|
||||
if (options?.limit && options.limit > 1000) {
|
||||
console.log(
|
||||
`Paginating get members from REST. #${loops} / ${
|
||||
Math.ceil((options?.limit ?? 1) / 1000)
|
||||
Math.ceil(
|
||||
(options?.limit ?? 1) / 1000,
|
||||
)
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
`${endpoints.GUILD_MEMBERS(guildID)}?limit=${
|
||||
membersLeft > 1000 ? 1000 : membersLeft
|
||||
}${options?.after ? `&after=${options.after}` : ""}`,
|
||||
) as MemberCreatePayload[];
|
||||
)) as MemberCreatePayload[];
|
||||
|
||||
const memberStructures = await Promise.all(
|
||||
result.map(async (member) => {
|
||||
@@ -680,10 +637,7 @@ export async function getAuditLogs(
|
||||
guildID: string,
|
||||
options: GetAuditLogsOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["VIEW_AUDIT_LOG"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["VIEW_AUDIT_LOG"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.GUILD_AUDIT_LOGS(guildID), {
|
||||
...options,
|
||||
@@ -700,10 +654,7 @@ export async function getAuditLogs(
|
||||
|
||||
/** Returns the guild widget object. Requires the MANAGE_GUILD permission. */
|
||||
export async function getWidgetSettings(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.GUILD_WIDGET(guildID));
|
||||
|
||||
@@ -716,15 +667,12 @@ export async function editWidget(
|
||||
enabled: boolean,
|
||||
channelID?: string | null,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_WIDGET(guildID),
|
||||
{ enabled, channel_id: channelID },
|
||||
);
|
||||
const result = await RequestManager.patch(endpoints.GUILD_WIDGET(guildID), {
|
||||
enabled,
|
||||
channel_id: channelID,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -767,10 +715,7 @@ export async function getVanityURL(guildID: string) {
|
||||
|
||||
/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */
|
||||
export async function getIntegrations(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_INTEGRATIONS(guildID),
|
||||
@@ -785,10 +730,7 @@ export async function editIntegration(
|
||||
id: string,
|
||||
options: EditIntegrationOptions,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_INTEGRATION(guildID, id),
|
||||
@@ -800,10 +742,7 @@ export async function editIntegration(
|
||||
|
||||
/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */
|
||||
export async function deleteIntegration(guildID: string, id: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_INTEGRATION(guildID, id),
|
||||
@@ -814,10 +753,7 @@ export async function deleteIntegration(guildID: string, id: string) {
|
||||
|
||||
/** Sync an integration. Requires the MANAGE_GUILD permission. */
|
||||
export async function syncIntegration(guildID: string, id: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.post(
|
||||
endpoints.GUILD_INTEGRATION_SYNC(guildID, id),
|
||||
@@ -828,14 +764,11 @@ export async function syncIntegration(guildID: string, id: string) {
|
||||
|
||||
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
|
||||
export async function getBans(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["BAN_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["BAN_MEMBERS"]);
|
||||
|
||||
const results = await RequestManager.get(
|
||||
const results = (await RequestManager.get(
|
||||
endpoints.GUILD_BANS(guildID),
|
||||
) as BannedUser[];
|
||||
)) as BannedUser[];
|
||||
|
||||
return new Collection<string, BannedUser>(
|
||||
results.map((res) => [res.user.id, res]),
|
||||
@@ -844,10 +777,7 @@ 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 async function getBan(guildID: string, memberID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["BAN_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["BAN_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILD_BAN(guildID, memberID),
|
||||
@@ -858,25 +788,19 @@ export async 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 async function ban(guildID: string, id: string, options: BanOptions) {
|
||||
const hasPerm = await botHasPermission(guildID, ["BAN_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["BAN_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.put(
|
||||
endpoints.GUILD_BAN(guildID, id),
|
||||
{ ...options, delete_message_days: options.days },
|
||||
);
|
||||
const result = await RequestManager.put(endpoints.GUILD_BAN(guildID, id), {
|
||||
...options,
|
||||
delete_message_days: options.days,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Remove the ban for a user. Requires BAN_MEMBERS permission */
|
||||
export async function unban(guildID: string, id: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["BAN_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_BAN_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["BAN_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.delete(endpoints.GUILD_BAN(guildID, id));
|
||||
|
||||
@@ -892,10 +816,7 @@ export async function getGuildPreview(guildID: string) {
|
||||
|
||||
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
|
||||
export async function editGuild(guildID: string, options: GuildEditOptions) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
if (options.icon && !options.icon.startsWith("data:image/")) {
|
||||
options.icon = await urlToBase64(options.icon);
|
||||
@@ -919,10 +840,7 @@ export async function editGuild(guildID: string, options: GuildEditOptions) {
|
||||
|
||||
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
|
||||
export async function getInvites(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.GUILD_INVITES(guildID));
|
||||
|
||||
@@ -952,13 +870,7 @@ export async function getVoiceRegions(guildID: string) {
|
||||
|
||||
/** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */
|
||||
export async function getWebhooks(guildID: string) {
|
||||
const hasPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
const result = await RequestManager.get(endpoints.GUILD_WEBHOOKS(guildID));
|
||||
|
||||
@@ -967,9 +879,7 @@ export async function getWebhooks(guildID: string) {
|
||||
|
||||
/** This function will return the raw user payload in the rare cases you need to fetch a user directly from the API. */
|
||||
export async function getUser(userID: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.USER(userID),
|
||||
);
|
||||
const result = await RequestManager.get(endpoints.USER(userID));
|
||||
|
||||
return result as UserPayload;
|
||||
}
|
||||
@@ -982,19 +892,18 @@ export async function getUser(userID: string) {
|
||||
* So it does not cache the guild, you must do it manually.
|
||||
* */
|
||||
export async function getGuild(guildID: string, counts = true) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.GUILDS_BASE(guildID),
|
||||
{ with_counts: counts },
|
||||
);
|
||||
const result = await RequestManager.get(endpoints.GUILDS_BASE(guildID), {
|
||||
with_counts: counts,
|
||||
});
|
||||
|
||||
return result as UpdateGuildPayload;
|
||||
}
|
||||
|
||||
/** Returns the guild template if it exists */
|
||||
export async function getTemplate(templateCode: string) {
|
||||
const result = await RequestManager.get(
|
||||
const result = (await RequestManager.get(
|
||||
endpoints.GUILD_TEMPLATE(templateCode),
|
||||
) as GuildTemplate;
|
||||
) as GuildTemplate);
|
||||
const template = await structures.createTemplateStruct(result);
|
||||
|
||||
return template;
|
||||
@@ -1004,10 +913,7 @@ export async function getTemplate(templateCode: string) {
|
||||
* Returns the guild template if it exists
|
||||
* @deprecated will get removed in v11 use `getTemplate` instead
|
||||
*/
|
||||
export function getGuildTemplate(
|
||||
guildID: string,
|
||||
templateCode: string,
|
||||
) {
|
||||
export function getGuildTemplate(guildID: string, templateCode: string) {
|
||||
return getTemplate(templateCode);
|
||||
}
|
||||
|
||||
@@ -1019,7 +925,7 @@ export async function createGuildFromTemplate(
|
||||
templateCode: string,
|
||||
data: CreateGuildFromTemplate,
|
||||
) {
|
||||
if (await cacheHandlers.size("guilds") >= 10) {
|
||||
if ((await cacheHandlers.size("guilds")) >= 10) {
|
||||
throw new Error(
|
||||
"This function can only be used by bots in less than 10 guilds.",
|
||||
);
|
||||
@@ -1042,12 +948,11 @@ export async function createGuildFromTemplate(
|
||||
* Requires the `MANAGE_GUILD` permission.
|
||||
*/
|
||||
export async function getGuildTemplates(guildID: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const templates = await RequestManager.get(
|
||||
const templates = (await RequestManager.get(
|
||||
endpoints.GUILD_TEMPLATES(guildID),
|
||||
) as GuildTemplate[];
|
||||
)) as GuildTemplate[];
|
||||
|
||||
return templates.map((template) => structures.createTemplateStruct(template));
|
||||
}
|
||||
@@ -1060,12 +965,11 @@ export async function deleteGuildTemplate(
|
||||
guildID: string,
|
||||
templateCode: string,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const deletedTemplate = await RequestManager.delete(
|
||||
const deletedTemplate = (await RequestManager.delete(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
) as GuildTemplate;
|
||||
)) as GuildTemplate;
|
||||
|
||||
return structures.createTemplateStruct(deletedTemplate);
|
||||
}
|
||||
@@ -1080,24 +984,20 @@ export async function createGuildTemplate(
|
||||
guildID: string,
|
||||
data: CreateGuildTemplate,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
if (data.name.length < 1 || data.name.length > 100) {
|
||||
throw new Error("The name can only be in between 1-100 characters.");
|
||||
}
|
||||
|
||||
if (
|
||||
data.description?.length &&
|
||||
data.description.length > 120
|
||||
) {
|
||||
if (data.description?.length && data.description.length > 120) {
|
||||
throw new Error("The description can only be in between 0-120 characters.");
|
||||
}
|
||||
|
||||
const template = await RequestManager.post(
|
||||
const template = (await RequestManager.post(
|
||||
endpoints.GUILD_TEMPLATES(guildID),
|
||||
data,
|
||||
) as GuildTemplate;
|
||||
)) as GuildTemplate;
|
||||
|
||||
return structures.createTemplateStruct(template);
|
||||
}
|
||||
@@ -1107,12 +1007,11 @@ export async function createGuildTemplate(
|
||||
* Requires the `MANAGE_GUILD` permission.
|
||||
*/
|
||||
export async function syncGuildTemplate(guildID: string, templateCode: string) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
const template = await RequestManager.put(
|
||||
const template = (await RequestManager.put(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
) as GuildTemplate;
|
||||
)) as GuildTemplate;
|
||||
|
||||
return structures.createTemplateStruct(template);
|
||||
}
|
||||
@@ -1126,24 +1025,20 @@ export async function editGuildTemplate(
|
||||
templateCode: string,
|
||||
data: EditGuildTemplate,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_GUILD"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_GUILD"]);
|
||||
|
||||
if (data.name?.length && (data.name.length < 1 || data.name.length > 100)) {
|
||||
throw new Error("The name can only be in between 1-100 characters.");
|
||||
}
|
||||
|
||||
if (
|
||||
data.description?.length &&
|
||||
data.description.length > 120
|
||||
) {
|
||||
if (data.description?.length && data.description.length > 120) {
|
||||
throw new Error("The description can only be in between 0-120 characters.");
|
||||
}
|
||||
|
||||
const template = await RequestManager.patch(
|
||||
const template = (await RequestManager.patch(
|
||||
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
|
||||
data,
|
||||
) as GuildTemplate;
|
||||
)) as GuildTemplate;
|
||||
|
||||
return structures.createTemplateStruct(template);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { botID } from "../bot.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { RequestManager } from "../rest/request_manager.ts";
|
||||
import { Member, structures } from "../structures/mod.ts";
|
||||
import {
|
||||
ChannelCreatePayload,
|
||||
DMChannelCreatePayload,
|
||||
@@ -9,16 +11,16 @@ import {
|
||||
ImageSize,
|
||||
MemberCreatePayload,
|
||||
MessageContent,
|
||||
Permission,
|
||||
} from "../types/mod.ts";
|
||||
import { endpoints } from "../util/constants.ts";
|
||||
import {
|
||||
botHasPermission,
|
||||
higherRolePosition,
|
||||
highestRole,
|
||||
isHigherPosition,
|
||||
requireBotChannelPermissions,
|
||||
requireBotGuildPermissions,
|
||||
} from "../util/permissions.ts";
|
||||
import { formatImageURL, urlToBase64 } from "../util/utils.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { Member, structures } from "../structures/mod.ts";
|
||||
import { sendMessage } from "./channel.ts";
|
||||
|
||||
/** The users custom avatar or the default avatar if you don't have a member object. */
|
||||
@@ -56,25 +58,16 @@ export async function addRole(
|
||||
roleID: string,
|
||||
reason?: string,
|
||||
) {
|
||||
const botsHighestRole = await highestRole(guildID, botID);
|
||||
if (botsHighestRole) {
|
||||
const hasHigherRolePosition = await higherRolePosition(
|
||||
guildID,
|
||||
botsHighestRole.id,
|
||||
roleID,
|
||||
);
|
||||
if (
|
||||
!hasHigherRolePosition &&
|
||||
(await cacheHandlers.get("guilds", guildID))?.ownerID !== botID
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
const isHigherRolePosition = await isHigherPosition(
|
||||
guildID,
|
||||
botID,
|
||||
roleID,
|
||||
);
|
||||
if (!isHigherRolePosition) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.put(
|
||||
endpoints.GUILD_MEMBER_ROLE(guildID, memberID, roleID),
|
||||
@@ -91,26 +84,18 @@ export async function removeRole(
|
||||
roleID: string,
|
||||
reason?: string,
|
||||
) {
|
||||
const botsHighestRole = await highestRole(guildID, botID);
|
||||
|
||||
if (botsHighestRole) {
|
||||
const hasHigherRolePosition = await higherRolePosition(
|
||||
guildID,
|
||||
botsHighestRole.id,
|
||||
roleID,
|
||||
);
|
||||
if (
|
||||
!hasHigherRolePosition &&
|
||||
(await cacheHandlers.get("guilds", guildID))?.ownerID !== botID
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
const isHigherRolePosition = await isHigherPosition(
|
||||
guildID,
|
||||
botID,
|
||||
roleID,
|
||||
);
|
||||
if (
|
||||
!isHigherRolePosition
|
||||
) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
const hasPerm = await botHasPermission(guildID, ["MANAGE_ROLES"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["MANAGE_ROLES"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_MEMBER_ROLE(guildID, memberID, roleID),
|
||||
@@ -155,10 +140,7 @@ export async function kick(guildID: string, memberID: string, reason?: string) {
|
||||
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
|
||||
}
|
||||
|
||||
const hasPerm = await botHasPermission(guildID, ["KICK_MEMBERS"]);
|
||||
if (!hasPerm) {
|
||||
throw new Error(Errors.MISSING_KICK_MEMBERS);
|
||||
}
|
||||
await requireBotGuildPermissions(guildID, ["KICK_MEMBERS"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.GUILD_MEMBER(guildID, memberID),
|
||||
@@ -174,56 +156,56 @@ export async function editMember(
|
||||
memberID: string,
|
||||
options: EditMemberOptions,
|
||||
) {
|
||||
const requiredPerms: Set<Permission> = new Set();
|
||||
|
||||
if (options.nick) {
|
||||
if (options.nick.length > 32) {
|
||||
throw new Error(Errors.NICKNAMES_MAX_LENGTH);
|
||||
}
|
||||
requiredPerms.add("MANAGE_NICKNAMES");
|
||||
}
|
||||
|
||||
const hasManageNickPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_NICKNAMES"],
|
||||
);
|
||||
if (!hasManageNickPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_NICKNAMES);
|
||||
if (options.roles) requiredPerms.add("MANAGE_ROLES");
|
||||
|
||||
if (
|
||||
typeof options.mute !== "undefined" ||
|
||||
typeof options.deaf !== "undefined" ||
|
||||
(typeof options.channel_id !== "undefined" || "null")
|
||||
) {
|
||||
const memberVoiceState = (await cacheHandlers.get("guilds", guildID))
|
||||
?.voiceStates.get(memberID);
|
||||
|
||||
if (!memberVoiceState?.channelID) {
|
||||
throw new Error(Errors.MEMBER_NOT_IN_VOICE_CHANNEL);
|
||||
}
|
||||
|
||||
if (typeof options.mute !== "undefined") {
|
||||
requiredPerms.add("MUTE_MEMBERS");
|
||||
}
|
||||
|
||||
if (typeof options.deaf !== "undefined") {
|
||||
requiredPerms.add("DEAFEN_MEMBERS");
|
||||
}
|
||||
|
||||
if (options.channel_id) {
|
||||
const requiredVoicePerms: Set<Permission> = new Set([
|
||||
"CONNECT",
|
||||
"MOVE_MEMBERS",
|
||||
]);
|
||||
if (memberVoiceState) {
|
||||
await requireBotChannelPermissions(
|
||||
memberVoiceState?.channelID,
|
||||
[...requiredVoicePerms],
|
||||
);
|
||||
}
|
||||
await requireBotChannelPermissions(
|
||||
options.channel_id,
|
||||
[...requiredVoicePerms],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const hasManageRolesPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MANAGE_ROLES"],
|
||||
);
|
||||
if (
|
||||
options.roles &&
|
||||
!hasManageRolesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_ROLES);
|
||||
}
|
||||
|
||||
if (options.mute) {
|
||||
const hasMuteMembersPerm = await botHasPermission(
|
||||
guildID,
|
||||
["MUTE_MEMBERS"],
|
||||
);
|
||||
// TODO: This should check if the member is in a voice channel
|
||||
if (
|
||||
!hasMuteMembersPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MUTE_MEMBERS);
|
||||
}
|
||||
}
|
||||
|
||||
const hasDeafenMembersPerm = await botHasPermission(
|
||||
guildID,
|
||||
["DEAFEN_MEMBERS"],
|
||||
);
|
||||
if (
|
||||
options.deaf &&
|
||||
!hasDeafenMembersPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_DEAFEN_MEMBERS);
|
||||
}
|
||||
|
||||
// TODO: if channel id is provided check if the bot has CONNECT and MOVE in channel and current channel
|
||||
await requireBotGuildPermissions(guildID, [...requiredPerms]);
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
endpoints.GUILD_MEMBER(guildID, memberID),
|
||||
@@ -292,8 +274,7 @@ export async function editBotNickname(
|
||||
guildID: string,
|
||||
nickname: string | null,
|
||||
) {
|
||||
const hasPerm = await botHasPermission(guildID, ["CHANGE_NICKNAME"]);
|
||||
if (!hasPerm) throw new Error(Errors.MISSING_CHANGE_NICKNAME);
|
||||
await requireBotGuildPermissions(guildID, ["CHANGE_NICKNAME"]);
|
||||
|
||||
const response = await RequestManager.patch(
|
||||
endpoints.USER_NICK(guildID),
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { botID } from "../bot.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { RequestManager } from "../rest/request_manager.ts";
|
||||
import { Message, structures } from "../structures/mod.ts";
|
||||
import {
|
||||
DiscordGetReactionsParams,
|
||||
Errors,
|
||||
MessageContent,
|
||||
MessageCreateOptions,
|
||||
Permission,
|
||||
UserPayload,
|
||||
} from "../types/mod.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { endpoints } from "../util/constants.ts";
|
||||
import { botHasChannelPermissions } from "../util/permissions.ts";
|
||||
import { requireBotChannelPermissions } from "../util/permissions.ts";
|
||||
import { delay } from "../util/utils.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { Message, structures } from "../structures/mod.ts";
|
||||
|
||||
/** Delete a message with the channel id and message id only. */
|
||||
export async function deleteMessageByID(
|
||||
@@ -42,15 +43,7 @@ export async function deleteMessage(
|
||||
) {
|
||||
if (message.author.id !== botID) {
|
||||
// This needs to check the channels permission not the guild permission
|
||||
const hasManageMessages = await botHasChannelPermissions(
|
||||
message.channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessages
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(message.channelID, ["MANAGE_MESSAGES"]);
|
||||
}
|
||||
|
||||
if (delayMilliseconds) await delay(delayMilliseconds);
|
||||
@@ -65,15 +58,7 @@ export async function deleteMessage(
|
||||
|
||||
/** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */
|
||||
export async function pin(channelID: string, messageID: string) {
|
||||
const hasManageMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_PIN(channelID, messageID),
|
||||
@@ -84,15 +69,7 @@ export async function pin(channelID: string, messageID: string) {
|
||||
|
||||
/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */
|
||||
export async function unpin(channelID: string, messageID: string) {
|
||||
const hasManageMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_PIN(channelID, messageID),
|
||||
@@ -107,23 +84,10 @@ export async function addReaction(
|
||||
messageID: string,
|
||||
reaction: string,
|
||||
) {
|
||||
const hasAddReactionsPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["ADD_REACTIONS"],
|
||||
);
|
||||
if (!hasAddReactionsPerm) {
|
||||
throw new Error(Errors.MISSING_ADD_REACTIONS);
|
||||
}
|
||||
|
||||
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["READ_MESSAGE_HISTORY"],
|
||||
);
|
||||
if (
|
||||
!hasReadMessageHistoryPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, [
|
||||
"ADD_REACTIONS",
|
||||
"READ_MESSAGE_HISTORY",
|
||||
]);
|
||||
|
||||
if (reaction.startsWith("<:")) {
|
||||
reaction = reaction.substring(2, reaction.length - 1);
|
||||
@@ -132,11 +96,7 @@ export async function addReaction(
|
||||
}
|
||||
|
||||
const result = await RequestManager.put(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(
|
||||
channelID,
|
||||
messageID,
|
||||
reaction,
|
||||
),
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(channelID, messageID, reaction),
|
||||
);
|
||||
|
||||
return result;
|
||||
@@ -174,11 +134,7 @@ export async function removeReaction(
|
||||
}
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(
|
||||
channelID,
|
||||
messageID,
|
||||
reaction,
|
||||
),
|
||||
endpoints.CHANNEL_MESSAGE_REACTION_ME(channelID, messageID, reaction),
|
||||
);
|
||||
|
||||
return result;
|
||||
@@ -191,13 +147,7 @@ export async function removeUserReaction(
|
||||
reaction: string,
|
||||
userID: string,
|
||||
) {
|
||||
const hasManageMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (!hasManageMessagesPerm) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
if (reaction.startsWith("<:")) {
|
||||
reaction = reaction.substring(2, reaction.length - 1);
|
||||
@@ -219,15 +169,7 @@ export async function removeUserReaction(
|
||||
|
||||
/** Removes all reactions for all emojis on this message. */
|
||||
export async function removeAllReactions(channelID: string, messageID: string) {
|
||||
const hasManageMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
const result = await RequestManager.delete(
|
||||
endpoints.CHANNEL_MESSAGE_REACTIONS(channelID, messageID),
|
||||
@@ -242,15 +184,7 @@ export async function removeReactionEmoji(
|
||||
messageID: string,
|
||||
reaction: string,
|
||||
) {
|
||||
const hasManageMessagesPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasManageMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_MESSAGES"]);
|
||||
|
||||
if (reaction.startsWith("<:")) {
|
||||
reaction = reaction.substring(2, reaction.length - 1);
|
||||
@@ -285,34 +219,17 @@ export async function editMessage(
|
||||
message: Message,
|
||||
content: string | MessageContent,
|
||||
) {
|
||||
if (
|
||||
message.author.id !== botID
|
||||
) {
|
||||
if (message.author.id !== botID) {
|
||||
throw "You can only edit a message that was sent by the bot.";
|
||||
}
|
||||
|
||||
if (typeof content === "string") content = { content };
|
||||
|
||||
const hasSendMessagesPerm = await botHasChannelPermissions(
|
||||
message.channelID,
|
||||
["SEND_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
!hasSendMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_MESSAGES);
|
||||
}
|
||||
const requiredPerms: Permission[] = ["SEND_MESSAGES"];
|
||||
|
||||
const hasSendTtsMessagesPerm = await botHasChannelPermissions(
|
||||
message.channelID,
|
||||
["SEND_TTS_MESSAGES"],
|
||||
);
|
||||
if (
|
||||
content.tts &&
|
||||
!hasSendTtsMessagesPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
|
||||
}
|
||||
if (content.tts) requiredPerms.push("SEND_TTS_MESSAGES");
|
||||
|
||||
await requireBotChannelPermissions(message.channelID, requiredPerms);
|
||||
|
||||
if (content.content && content.content.length > 2000) {
|
||||
throw new Error(Errors.MESSAGE_MAX_LENGTH);
|
||||
@@ -328,9 +245,9 @@ export async function editMessage(
|
||||
|
||||
/** Crosspost a message in a News Channel to following channels. */
|
||||
export async function publishMessage(channelID: string, messageID: string) {
|
||||
const data = await RequestManager.post(
|
||||
const data = (await RequestManager.post(
|
||||
endpoints.CHANNEL_MESSAGE_CROSSPOST(channelID, messageID),
|
||||
) as MessageCreateOptions;
|
||||
)) as MessageCreateOptions;
|
||||
|
||||
return structures.createMessageStruct(data);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { applicationID } from "../bot.ts";
|
||||
import { RequestManager } from "../rest/request_manager.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
import {
|
||||
CreateSlashCommandOptions,
|
||||
EditSlashCommandOptions,
|
||||
@@ -22,11 +23,10 @@ import {
|
||||
import { cache } from "../util/cache.ts";
|
||||
import { Collection } from "../util/collection.ts";
|
||||
import { endpoints, SLASH_COMMANDS_NAME_REGEX } from "../util/constants.ts";
|
||||
import { botHasChannelPermissions } from "../util/permissions.ts";
|
||||
import { requireBotChannelPermissions } from "../util/permissions.ts";
|
||||
import { urlToBase64 } from "../util/utils.ts";
|
||||
import { structures } from "../structures/mod.ts";
|
||||
|
||||
/**
|
||||
/**
|
||||
* Create a new webhook. Requires the MANAGE_WEBHOOKS permission. Returns a webhook object on success. Webhook names follow our naming restrictions that can be found in our Usernames and Nicknames documentation, with the following additional stipulations:
|
||||
*
|
||||
* Webhook names cannot be: 'clyde'
|
||||
@@ -35,21 +35,14 @@ export async function createWebhook(
|
||||
channelID: string,
|
||||
options: WebhookCreateOptions,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
if (
|
||||
// Specific usernames that discord does not allow
|
||||
options.name === "clyde" ||
|
||||
// Character limit checks. [...] checks are because of js unicode length handling
|
||||
[...options.name].length < 2 || [...options.name].length > 32
|
||||
[...options.name].length < 2 ||
|
||||
[...options.name].length > 32
|
||||
) {
|
||||
throw new Error(Errors.INVALID_WEBHOOK_NAME);
|
||||
}
|
||||
@@ -71,15 +64,7 @@ export async function editWebhook(
|
||||
webhookID: string,
|
||||
options: WebhookEditOptions,
|
||||
) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
const result = await RequestManager.patch(endpoints.WEBHOOK_ID(webhookID), {
|
||||
...options,
|
||||
@@ -105,15 +90,7 @@ export async function editWebhookWithToken(
|
||||
|
||||
/** Delete a webhook permanently. Requires the `MANAGE_WEBHOOKS` permission. Returns a undefined on success */
|
||||
export async function deleteWebhook(channelID: string, webhookID: string) {
|
||||
const hasManageWebhooksPerm = await botHasChannelPermissions(
|
||||
channelID,
|
||||
["MANAGE_WEBHOOKS"],
|
||||
);
|
||||
if (
|
||||
!hasManageWebhooksPerm
|
||||
) {
|
||||
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
|
||||
}
|
||||
await requireBotChannelPermissions(channelID, ["MANAGE_WEBHOOKS"]);
|
||||
|
||||
const result = await RequestManager.delete(endpoints.WEBHOOK_ID(webhookID));
|
||||
|
||||
@@ -141,9 +118,7 @@ export async function getWebhook(webhookID: string) {
|
||||
|
||||
/** Returns the new webhook object for the given id, this call does not require authentication and returns no user in the webhook object. */
|
||||
export async function getWebhookWithToken(webhookID: string, token: string) {
|
||||
const result = await RequestManager.get(
|
||||
endpoints.WEBHOOK(webhookID, token),
|
||||
);
|
||||
const result = await RequestManager.get(endpoints.WEBHOOK(webhookID, token));
|
||||
|
||||
return result as WebhookPayload;
|
||||
}
|
||||
@@ -169,8 +144,8 @@ export async function executeWebhook(
|
||||
if (options.mentions) {
|
||||
if (options.mentions.users?.length) {
|
||||
if (options.mentions.parse.includes("users")) {
|
||||
options.mentions.parse = options.mentions.parse.filter((p) =>
|
||||
p !== "users"
|
||||
options.mentions.parse = options.mentions.parse.filter(
|
||||
(p) => p !== "users",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -181,8 +156,8 @@ export async function executeWebhook(
|
||||
|
||||
if (options.mentions.roles?.length) {
|
||||
if (options.mentions.parse.includes("roles")) {
|
||||
options.mentions.parse = options.mentions.parse.filter((p) =>
|
||||
p !== "roles"
|
||||
options.mentions.parse = options.mentions.parse.filter(
|
||||
(p) => p !== "roles",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,9 +199,9 @@ export async function editWebhookMessage(
|
||||
if (options.allowed_mentions) {
|
||||
if (options.allowed_mentions.users?.length) {
|
||||
if (options.allowed_mentions.parse.includes("users")) {
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter((
|
||||
p,
|
||||
) => p !== "users");
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
|
||||
(p) => p !== "users",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.users.length > 100) {
|
||||
@@ -239,9 +214,9 @@ export async function editWebhookMessage(
|
||||
|
||||
if (options.allowed_mentions.roles?.length) {
|
||||
if (options.allowed_mentions.parse.includes("roles")) {
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter((
|
||||
p,
|
||||
) => p !== "roles");
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
|
||||
(p) => p !== "roles",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.roles.length > 100) {
|
||||
@@ -410,11 +385,7 @@ export async function upsertSlashCommand(
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(
|
||||
applicationID,
|
||||
guildID,
|
||||
commandID,
|
||||
)
|
||||
? endpoints.COMMANDS_GUILD_ID(applicationID, guildID, commandID)
|
||||
: endpoints.COMMANDS_ID(applicationID, commandID),
|
||||
options,
|
||||
);
|
||||
@@ -424,7 +395,7 @@ export async function upsertSlashCommand(
|
||||
|
||||
/**
|
||||
* Bulk edit existing slash commands. If a command does not exist, it will create it.
|
||||
*
|
||||
*
|
||||
* **NOTE:** Any slash commands that are not specified in this function will be **deleted**. If you don't provide the commandID and rename your command, the command gets a new ID.
|
||||
*/
|
||||
export async function upsertSlashCommands(
|
||||
@@ -444,8 +415,8 @@ export async function upsertSlashCommands(
|
||||
}
|
||||
|
||||
// TODO: remove this function for v11
|
||||
/**
|
||||
* Edit an existing slash command.
|
||||
/**
|
||||
* Edit an existing slash command.
|
||||
* @deprecated This function will be removed in v11. Use `upsertSlashCommand()` instead
|
||||
*/
|
||||
export async function editSlashCommand(
|
||||
@@ -458,18 +429,15 @@ export async function editSlashCommand(
|
||||
}
|
||||
|
||||
if (
|
||||
[...options.description].length < 1 || [...options.description].length > 100
|
||||
[...options.description].length < 1 ||
|
||||
[...options.description].length > 100
|
||||
) {
|
||||
throw new Error(Errors.INVALID_SLASH_DESCRIPTION);
|
||||
}
|
||||
|
||||
const result = await RequestManager.patch(
|
||||
guildID
|
||||
? endpoints.COMMANDS_GUILD_ID(
|
||||
applicationID,
|
||||
guildID,
|
||||
commandID,
|
||||
)
|
||||
? endpoints.COMMANDS_GUILD_ID(applicationID, guildID, commandID)
|
||||
: endpoints.COMMANDS_ID(applicationID, commandID),
|
||||
options,
|
||||
);
|
||||
@@ -518,7 +486,7 @@ export async function executeSlashCommand(
|
||||
}
|
||||
|
||||
// If no mentions are provided, force disable mentions
|
||||
if (!(options.data.allowed_mentions)) {
|
||||
if (!options.data.allowed_mentions) {
|
||||
options.data.allowed_mentions = { parse: [] };
|
||||
}
|
||||
|
||||
@@ -531,10 +499,7 @@ export async function executeSlashCommand(
|
||||
}
|
||||
|
||||
/** To delete your response to a slash command. If a message id is not provided, it will default to deleting the original response. */
|
||||
export async function deleteSlashResponse(
|
||||
token: string,
|
||||
messageID?: string,
|
||||
) {
|
||||
export async function deleteSlashResponse(token: string, messageID?: string) {
|
||||
const result = await RequestManager.delete(
|
||||
messageID
|
||||
? endpoints.INTERACTION_ID_TOKEN_MESSAGEID(
|
||||
@@ -564,9 +529,9 @@ export async function editSlashResponse(
|
||||
if (options.allowed_mentions) {
|
||||
if (options.allowed_mentions.users?.length) {
|
||||
if (options.allowed_mentions.parse.includes("users")) {
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter((
|
||||
p,
|
||||
) => p !== "users");
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
|
||||
(p) => p !== "users",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.users.length > 100) {
|
||||
@@ -579,9 +544,9 @@ export async function editSlashResponse(
|
||||
|
||||
if (options.allowed_mentions.roles?.length) {
|
||||
if (options.allowed_mentions.parse.includes("roles")) {
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter((
|
||||
p,
|
||||
) => p !== "roles");
|
||||
options.allowed_mentions.parse = options.allowed_mentions.parse.filter(
|
||||
(p) => p !== "roles",
|
||||
);
|
||||
}
|
||||
|
||||
if (options.allowed_mentions.roles.length > 100) {
|
||||
|
||||
@@ -13,6 +13,7 @@ export enum Errors {
|
||||
GUILD_WIDGET_NOT_ENABLED = "GUILD_WIDGET_NOT_ENABLED",
|
||||
GUILD_NOT_FOUND = "GUILD_NOT_FOUND",
|
||||
MEMBER_NOT_FOUND = "MEMBER_NOT_FOUND",
|
||||
MEMBER_NOT_IN_VOICE_CHANNEL = "MEMBER_NOT_IN_VOICE_CHANNEL",
|
||||
PRUNE_MAX_DAYS = "PRUNE_MAX_DAYS",
|
||||
ROLE_NOT_FOUND = "ROLE_NOT_FOUND",
|
||||
// Message Delete Errors
|
||||
|
||||
@@ -9,6 +9,8 @@ export enum Permissions {
|
||||
MANAGE_GUILD = 0x00000020,
|
||||
ADD_REACTIONS = 0x00000040,
|
||||
VIEW_AUDIT_LOG = 0x00000080,
|
||||
PRIORITY_SPEAKER = 0x00000100,
|
||||
STREAM = 0x00000200,
|
||||
VIEW_CHANNEL = 0x00000400,
|
||||
SEND_MESSAGES = 0x00000800,
|
||||
SEND_TTS_MESSAGES = 0x00001000,
|
||||
@@ -18,14 +20,13 @@ export enum Permissions {
|
||||
READ_MESSAGE_HISTORY = 0x00010000,
|
||||
MENTION_EVERYONE = 0x00020000,
|
||||
USE_EXTERNAL_EMOJIS = 0x00040000,
|
||||
VIEW_GUILD_INSIGHTS = 0x00080000,
|
||||
CONNECT = 0x00100000,
|
||||
SPEAK = 0x00200000,
|
||||
MUTE_MEMBERS = 0x00400000,
|
||||
DEAFEN_MEMBERS = 0x00800000,
|
||||
MOVE_MEMBERS = 0x01000000,
|
||||
USE_VAD = 0x02000000,
|
||||
PRIORITY_SPEAKER = 0x00000100,
|
||||
STREAM = 0x00000200,
|
||||
CHANGE_NICKNAME = 0x04000000,
|
||||
MANAGE_NICKNAMES = 0x08000000,
|
||||
MANAGE_ROLES = 0x10000000,
|
||||
|
||||
@@ -1,249 +1,328 @@
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { Guild, Role } from "../structures/mod.ts";
|
||||
import { botID } from "../bot.ts";
|
||||
import { Permission, Permissions, RawOverwrite } from "../types/mod.ts";
|
||||
import { cacheHandlers } from "../cache.ts";
|
||||
import { Channel, Guild, Member, Role } from "../structures/mod.ts";
|
||||
import { Errors, Permission, Permissions } from "../types/mod.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(
|
||||
memberID: string,
|
||||
guildID: string,
|
||||
permissions: Permission[],
|
||||
async function getCached(
|
||||
table: "guilds",
|
||||
key: string | Guild,
|
||||
): Promise<Guild>;
|
||||
async function getCached(
|
||||
table: "channels",
|
||||
key: string | Channel,
|
||||
): Promise<Channel>;
|
||||
async function getCached(
|
||||
table: "members",
|
||||
key: string | Member,
|
||||
): Promise<Member>;
|
||||
async function getCached(
|
||||
table: "guilds" | "channels" | "members",
|
||||
key: string | Guild | Channel | Member,
|
||||
) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return false;
|
||||
const cached = typeof key === "string"
|
||||
? // @ts-ignore TS is wrong here
|
||||
(await cacheHandlers.get(table, key))
|
||||
: key;
|
||||
if (!cached || typeof cached === "string") {
|
||||
throw new Error(
|
||||
Errors[`${table.slice(0, -1).toUpperCase()}_NOT_FOUND` as Errors],
|
||||
);
|
||||
}
|
||||
|
||||
if (memberID === guild.ownerID) return true;
|
||||
|
||||
const member = (await cacheHandlers.get("members", memberID))?.guilds.get(
|
||||
guildID,
|
||||
);
|
||||
if (!member) return false;
|
||||
|
||||
return memberHasPermission(memberID, guild, member.roles, permissions);
|
||||
return cached;
|
||||
}
|
||||
|
||||
/** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */
|
||||
export function memberHasPermission(
|
||||
memberID: string,
|
||||
guild: Guild,
|
||||
memberRoleIDs: string[],
|
||||
permissions: Permission[],
|
||||
/** Calculates the permissions this member has in the given guild */
|
||||
export async function calculateBasePermissions(
|
||||
guild: string | Guild,
|
||||
member: string | Member,
|
||||
) {
|
||||
if (memberID === guild.ownerID) return true;
|
||||
guild = await getCached("guilds", guild);
|
||||
member = await getCached("members", member);
|
||||
|
||||
const permissionBits = [guild.id, ...memberRoleIDs].map((id) =>
|
||||
guild.roles.get(id)?.permissions
|
||||
)
|
||||
let permissions = 0n;
|
||||
// Calculate the role permissions bits, @everyone role is not in memberRoleIDs so we need to pass guildID manualy
|
||||
permissions |= [...(member.guilds.get(guild.id)?.roles || []), guild.id]
|
||||
.map((id) => (guild as Guild).roles.get(id)?.permissions)
|
||||
// Removes any edge case undefined
|
||||
.filter((id) => id)
|
||||
.filter((perm) => perm)
|
||||
.reduce((bits, perms) => {
|
||||
bits |= BigInt(perms);
|
||||
return bits;
|
||||
}, BigInt(0));
|
||||
}, 0n);
|
||||
|
||||
if (permissionBits & BigInt(Permissions.ADMINISTRATOR)) return true;
|
||||
// If the memberID is equal to the guild ownerID he automatically has every permission so we add ADMINISTRATOR permission
|
||||
if (guild.ownerID === member.id) permissions |= 8n;
|
||||
// Return the members permission bits as a string
|
||||
return permissions.toString();
|
||||
}
|
||||
|
||||
return permissions.every((permission) =>
|
||||
permissionBits & BigInt(Permissions[permission])
|
||||
/** Calculates the permissions this member has for the given Channel */
|
||||
export async function calculateChannelOverwrites(
|
||||
channel: string | Channel,
|
||||
member: string | Member,
|
||||
) {
|
||||
channel = await getCached("channels", channel);
|
||||
|
||||
// This is a DM channel so return ADMINISTRATOR permission
|
||||
if (!channel.guildID) return "8";
|
||||
|
||||
member = await getCached("members", member);
|
||||
|
||||
// Get all the role permissions this member already has
|
||||
let permissions = BigInt(
|
||||
await calculateBasePermissions(channel.guildID, member),
|
||||
);
|
||||
|
||||
// First calculate @everyone overwrites since these have the lowest priority
|
||||
const overwriteEveryone = channel?.permissionOverwrites.find(
|
||||
(overwrite) => overwrite.id === (channel as Channel).guildID,
|
||||
);
|
||||
if (overwriteEveryone) {
|
||||
// First remove denied permissions since denied < allowed
|
||||
permissions &= ~BigInt(overwriteEveryone.deny);
|
||||
permissions |= BigInt(overwriteEveryone.allow);
|
||||
}
|
||||
|
||||
const overwrites = channel?.permissionOverwrites;
|
||||
|
||||
// In order to calculate the role permissions correctly we need to temporarily save the allowed and denied permissions
|
||||
let allow = 0n;
|
||||
let deny = 0n;
|
||||
const memberRoles = member.guilds.get(channel.guildID)?.roles || [];
|
||||
// Second calculate members role overwrites since these have middle priority
|
||||
for (const overwrite of overwrites) {
|
||||
if (!memberRoles.includes(overwrite.id)) continue;
|
||||
|
||||
deny &= ~BigInt(overwrite.deny);
|
||||
allow |= BigInt(overwrite.allow);
|
||||
}
|
||||
// After role overwrite calculate save allowed permissions first we remove denied permissions since "denied < allowed"
|
||||
permissions &= ~deny;
|
||||
permissions |= allow;
|
||||
|
||||
// Third calculate member specific overwrites since these have the highest priority
|
||||
const overwriteMember = overwrites.find(
|
||||
(overwrite) => overwrite.id === (member as Member).id,
|
||||
);
|
||||
if (overwriteMember) {
|
||||
permissions &= ~BigInt(overwriteMember.deny);
|
||||
permissions |= BigInt(overwriteMember.allow);
|
||||
}
|
||||
|
||||
return permissions.toString();
|
||||
}
|
||||
|
||||
/** Checks if the given permission bits are matching the given permissions. `ADMINISTRATOR` always returns `true` */
|
||||
export function validatePermissions(
|
||||
permissionBits: string,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
if (BigInt(permissionBits) & 8n) return true;
|
||||
|
||||
return permissions.every(
|
||||
(permission) =>
|
||||
// Check if permission is in permissionBits
|
||||
BigInt(permissionBits) & BigInt(Permissions[permission]),
|
||||
);
|
||||
}
|
||||
|
||||
export async function botHasPermission(
|
||||
guildID: string,
|
||||
/** Checks if the given member has these permissions in the given guild */
|
||||
export async function hasGuildPermissions(
|
||||
guild: string | Guild,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return false;
|
||||
|
||||
// Check if the bot is the owner of the guild, if it is, returns true
|
||||
if (guild.ownerID === botID) return true;
|
||||
|
||||
const member = await cacheHandlers.get("members", botID);
|
||||
if (!member) return false;
|
||||
|
||||
// The everyone role is not in member.roles
|
||||
const permissionBits = [...member.guilds.get(guildID)?.roles || [], guild.id]
|
||||
.map((id) => guild.roles.get(id)!)
|
||||
// Remove any edge case undefined
|
||||
.filter((r) => r)
|
||||
.reduce((bits, data) => {
|
||||
bits |= BigInt(data.permissions);
|
||||
|
||||
return bits;
|
||||
}, BigInt(0));
|
||||
|
||||
if (permissionBits & BigInt(Permissions.ADMINISTRATOR)) return true;
|
||||
|
||||
return permissions.every((permission) =>
|
||||
permissionBits & BigInt(Permissions[permission])
|
||||
);
|
||||
// First we need the role permission bits this member has
|
||||
const basePermissions = await calculateBasePermissions(guild, member);
|
||||
// Second use the validatePermissions function to check if the member has every permission
|
||||
return validatePermissions(basePermissions, permissions);
|
||||
}
|
||||
|
||||
/** Checks if the bot has the permissions in a channel */
|
||||
export function botHasChannelPermissions(
|
||||
channelID: string,
|
||||
/** Checks if the bot has these permissions in the given guild */
|
||||
export function botHasGuildPermissions(
|
||||
guild: string | Guild,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
return hasChannelPermissions(channelID, botID, permissions);
|
||||
// Since Bot is a normal member we can use the hasRolePermissions() function
|
||||
return hasGuildPermissions(guild, botID, permissions);
|
||||
}
|
||||
|
||||
/** Checks if a user has permissions in a channel. */
|
||||
/** Checks if the given member has these permissions for the given channel */
|
||||
export async function hasChannelPermissions(
|
||||
channelID: string,
|
||||
memberID: string,
|
||||
channel: string | Channel,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
const channel = await cacheHandlers.get("channels", channelID);
|
||||
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 (
|
||||
await memberIDHasPermission(memberID, guild.id, ["ADMINISTRATOR"])
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
const member = (await cacheHandlers.get("members", memberID))?.guilds.get(
|
||||
guild.id,
|
||||
// First we need the overwrite bits this member has
|
||||
const channelOverwrites = await calculateChannelOverwrites(
|
||||
channel,
|
||||
member,
|
||||
);
|
||||
if (!member) return false;
|
||||
// Second use the validatePermissions function to check if the member has every permission
|
||||
return validatePermissions(channelOverwrites, permissions);
|
||||
}
|
||||
|
||||
let memberOverwrite: RawOverwrite | undefined;
|
||||
let everyoneOverwrite: RawOverwrite | undefined;
|
||||
const rolesOverwrites: RawOverwrite[] = [];
|
||||
/** Checks if the bot has these permissions f0r the given channel */
|
||||
export function botHasChannelPermissions(
|
||||
channel: string | Channel,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
// Since Bot is a normal member we can use the hasRolePermissions() function
|
||||
return hasChannelPermissions(channel, botID, permissions);
|
||||
}
|
||||
|
||||
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);
|
||||
/** Returns the permissions that are not in the given permissionBits */
|
||||
export function missingPermissions(
|
||||
permissionBits: string,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
if (BigInt(permissionBits) & 8n) return [];
|
||||
|
||||
return permissions.filter(
|
||||
(permission) => !(BigInt(permissionBits) & BigInt(Permissions[permission])),
|
||||
);
|
||||
}
|
||||
|
||||
/** Get the missing Guild permissions this member has */
|
||||
export async function getMissingGuildPermissions(
|
||||
guild: string | Guild,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
// First we need the role permissino bits this member has
|
||||
const permissionBits = await calculateBasePermissions(guild, member);
|
||||
// Second returnn the members missing permissions
|
||||
return missingPermissions(permissionBits, permissions);
|
||||
}
|
||||
|
||||
/** Get the missing Channel permissions this member has */
|
||||
export async function getMissingChannelPermissions(
|
||||
channel: string | Channel,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
// First we need the role permissino bits this member has
|
||||
const permissionBits = await calculateChannelOverwrites(channel, member);
|
||||
// Second returnn the members missing permissions
|
||||
return missingPermissions(permissionBits, permissions);
|
||||
}
|
||||
|
||||
/** Throws an error if this member has not all of the given permissions */
|
||||
export async function requireGuildPermissions(
|
||||
guild: string | Guild,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
const missing = await getMissingGuildPermissions(guild, member, permissions);
|
||||
if (missing.length) {
|
||||
// If the member is missing a permission throw an Error
|
||||
throw new Error(`Missing Permissions: ${missing.join(" & ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
const allowedPermissions = new Set<Permission>();
|
||||
/** Throws an error if the bot does not have all permissions */
|
||||
export function requireBotGuildPermissions(
|
||||
guild: string | Guild,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
// Since Bot is a normal member we can use the throwOnMissingGuildPermission() function
|
||||
return requireGuildPermissions(guild, botID, permissions);
|
||||
}
|
||||
|
||||
// Member perms override everything so we must check them first
|
||||
if (memberOverwrite) {
|
||||
const allowBits = memberOverwrite.allow;
|
||||
const denyBits = 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(Permissions[perm])) return false;
|
||||
// Already allowed perm
|
||||
if (allowedPermissions.has(perm)) continue;
|
||||
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(allowBits) & BigInt(Permissions[perm])) {
|
||||
allowedPermissions.add(perm);
|
||||
}
|
||||
}
|
||||
/** Throws an error if this member has not all of the given permissions */
|
||||
export async function requireChannelPermissions(
|
||||
channel: string | Channel,
|
||||
member: string | Member,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
const missing = await getMissingChannelPermissions(
|
||||
channel,
|
||||
member,
|
||||
permissions,
|
||||
);
|
||||
if (missing.length) {
|
||||
// If the member is missing a permission throw an Error
|
||||
throw new Error(`Missing Permissions: ${missing.join(" & ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Check the necessary permissions for roles
|
||||
for (const perm of permissions) {
|
||||
// If this is already allowed, skip
|
||||
if (allowedPermissions.has(perm)) continue;
|
||||
|
||||
for (const overwrite of rolesOverwrites) {
|
||||
const allowBits = overwrite.allow;
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(allowBits) & BigInt(Permissions[perm])) {
|
||||
allowedPermissions.add(perm);
|
||||
break;
|
||||
}
|
||||
|
||||
const denyBits = overwrite.deny;
|
||||
// If this role denies it we need to save and check if another role allows it, allows > deny
|
||||
if (BigInt(denyBits) & BigInt(Permissions[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(o.allow) & BigInt(Permissions[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 = everyoneOverwrite.allow;
|
||||
const denyBits = everyoneOverwrite.deny;
|
||||
for (const perm of permissions) {
|
||||
// Already allowed perm
|
||||
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(Permissions[perm])) return false;
|
||||
// This perm is allowed so we save it
|
||||
if (BigInt(allowBits) & BigInt(Permissions[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 memberIDHasPermission(memberID, guild.id, permissions);
|
||||
/** Throws an error if the bot has not all of the given channel permissions */
|
||||
export function requireBotChannelPermissions(
|
||||
channel: string | Channel,
|
||||
permissions: Permission[],
|
||||
) {
|
||||
// Since Bot is a normal member we can use the throwOnMissingChannelPermission() function
|
||||
return requireChannelPermissions(channel, botID, permissions);
|
||||
}
|
||||
|
||||
/** This function converts a bitwise string to permission strings */
|
||||
export function calculatePermissions(permissionBits: bigint) {
|
||||
return Object.keys(Permissions).filter((perm) => {
|
||||
if (Number(perm)) return false;
|
||||
return permissionBits & BigInt(Permissions[perm as Permission]);
|
||||
return Object.keys(Permissions).filter((permission) => {
|
||||
// Since Object.keys() not only returns the permission names but also the bit values we need to return false if it is a Number
|
||||
if (Number(permission)) return false;
|
||||
// Check if permissionBits has this permission
|
||||
return permissionBits & BigInt(Permissions[permission as Permission]);
|
||||
}) 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();
|
||||
return permissions
|
||||
.reduce((bits, perm) => {
|
||||
bits |= BigInt(Permissions[perm]);
|
||||
return bits;
|
||||
}, 0n)
|
||||
.toString();
|
||||
}
|
||||
|
||||
export async function highestRole(guildID: string, memberID: string) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return;
|
||||
/** Gets the highest role from the member in this guild */
|
||||
export async function highestRole(
|
||||
guild: string | Guild,
|
||||
member: string | Member,
|
||||
) {
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
const member = (await cacheHandlers.get("members", memberID))?.guilds.get(
|
||||
guildID,
|
||||
);
|
||||
if (!member) return;
|
||||
// Get the roles from the member
|
||||
const memberRoles = (
|
||||
await getCached("members", member)
|
||||
).guilds.get(guild.id)?.roles;
|
||||
// This member has no roles so the highest one is the @everyone role
|
||||
if (!memberRoles) return guild.roles.get(guild.id) as Role;
|
||||
|
||||
let memberHighestRole: Role | undefined;
|
||||
|
||||
for (const roleID of member.roles) {
|
||||
for (const roleID of memberRoles) {
|
||||
const role = guild.roles.get(roleID);
|
||||
// Rare edge case handling if undefined
|
||||
if (!role) continue;
|
||||
|
||||
// If memberHighestRole is still undefined we want to assign the role,
|
||||
// else we want to check if the current role position is higher than the current memberHighestRole
|
||||
if (
|
||||
!memberHighestRole || memberHighestRole.position < role.position
|
||||
!memberHighestRole ||
|
||||
memberHighestRole.position < role.position ||
|
||||
memberHighestRole.position === role.position
|
||||
) {
|
||||
memberHighestRole = role;
|
||||
}
|
||||
}
|
||||
|
||||
return memberHighestRole || (guild.roles.get(guild.id) as Role);
|
||||
// The member has at least one role so memberHighestRole must exist
|
||||
return memberHighestRole!;
|
||||
}
|
||||
|
||||
/** Checks if the first role is higher than the second role */
|
||||
export async function higherRolePosition(
|
||||
guildID: string,
|
||||
guild: string | Guild,
|
||||
roleID: string,
|
||||
otherRoleID: string,
|
||||
) {
|
||||
const guild = await cacheHandlers.get("guilds", guildID);
|
||||
if (!guild) return;
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
const role = guild.roles.get(roleID);
|
||||
const otherRole = guild.roles.get(otherRoleID);
|
||||
if (!role || !otherRole) return;
|
||||
if (!role || !otherRole) throw new Error(Errors.ROLE_NOT_FOUND);
|
||||
|
||||
// Rare edge case handling
|
||||
if (role.position === otherRole.position) {
|
||||
@@ -252,3 +331,17 @@ export async function higherRolePosition(
|
||||
|
||||
return role.position > otherRole.position;
|
||||
}
|
||||
|
||||
/** Checks if the member has a higher position than the given role */
|
||||
export async function isHigherPosition(
|
||||
guild: string | Guild,
|
||||
memberID: string,
|
||||
compareRoleID: string,
|
||||
) {
|
||||
guild = await getCached("guilds", guild);
|
||||
|
||||
if (guild.ownerID === memberID) return true;
|
||||
|
||||
const memberHighestRole = await highestRole(guild, memberID);
|
||||
return higherRolePosition(guild.id, memberHighestRole.id, compareRoleID);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user