This commit is contained in:
Skillz
2020-11-17 23:46:13 -05:00
27 changed files with 970 additions and 280 deletions
+130 -37
View File
@@ -1,4 +1,3 @@
import { endpoints } from "../constants/discord.ts";
import { cacheHandlers } from "../controllers/cache.ts";
import { RequestManager } from "../module/requestManager.ts";
import { structures } from "../structures/mod.ts";
@@ -17,10 +16,8 @@ import { Errors } from "../types/errors.ts";
import { PermissionOverwrite } from "../types/guild.ts";
import { MessageCreateOptions } from "../types/message.ts";
import { Permissions } from "../types/permission.ts";
import {
botHasChannelPermissions,
calculateBits,
} from "../utils/permissions.ts";
import { endpoints } from "../utils/constants.ts";
import { botHasChannelPermissions } from "../utils/permissions.ts";
/** Checks if a channel overwrite for a user id or a role id has permission in this channel */
export function channelOverwriteHasPermission(
@@ -48,16 +45,22 @@ export async function getMessage(
channelID: string,
id: string,
) {
const hasViewChannelPerm = await botHasChannelPermissions(
channelID,
[Permissions.VIEW_CHANNEL],
);
if (
!botHasChannelPermissions(channelID, [Permissions.VIEW_CHANNEL])
!hasViewChannelPerm
) {
throw new Error(Errors.MISSING_VIEW_CHANNEL);
}
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
);
if (
!botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
)
!hasReadMessageHistoryPerm
) {
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
}
@@ -77,16 +80,22 @@ export async function getMessages(
| GetMessagesAround
| GetMessages,
) {
const hasViewChannelPerm = await botHasChannelPermissions(
channelID,
[Permissions.VIEW_CHANNEL],
);
if (
!botHasChannelPermissions(channelID, [Permissions.VIEW_CHANNEL])
!hasViewChannelPerm
) {
throw new Error(Errors.MISSING_VIEW_CHANNEL);
}
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
);
if (
!botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
)
!hasReadMessageHistoryPerm
) {
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
}
@@ -114,24 +123,34 @@ export async function sendMessage(
content: string | MessageContent,
) {
if (typeof content === "string") content = { content };
const hasSendMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.SEND_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.SEND_MESSAGES])
!hasSendMessagesPerm
) {
throw new Error(Errors.MISSING_SEND_MESSAGES);
}
const hasSendTtsMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.SEND_TTS_MESSAGES],
);
if (
content.tts &&
!botHasChannelPermissions(
channelID,
[Permissions.SEND_TTS_MESSAGES],
)
!hasSendTtsMessagesPerm
) {
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
}
const hasEmbedLinksPerm = await botHasChannelPermissions(
channelID,
[Permissions.EMBED_LINKS],
);
if (
content.embed &&
!botHasChannelPermissions(channelID, [Permissions.EMBED_LINKS])
!hasEmbedLinksPerm
) {
throw new Error(Errors.MISSING_EMBED_LINKS);
}
@@ -165,6 +184,17 @@ export async function sendMessage(
content.mentions.roles = content.mentions.roles.slice(0, 100);
}
}
if (content.mentions.repliedUser) {
if (
!(await botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
))
) {
throw new Error(Errors.MISSING_SEND_MESSAGES);
}
}
}
const channel = await cacheHandlers.get("channels", channelID);
@@ -180,7 +210,15 @@ export async function sendMessage(
endpoints.CHANNEL_MESSAGES(channelID),
{
...content,
allowed_mentions: content.mentions,
allowed_mentions: content.mentions
? {
...content.mentions,
replied_user: content.mentions.repliedUser !== false,
}
: undefined,
message_reference: {
message_id: content.replyMessageID,
},
},
);
@@ -188,13 +226,17 @@ export async function sendMessage(
}
/** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */
export function deleteMessages(
export async function deleteMessages(
channelID: string,
ids: string[],
reason?: string,
) {
const hasManageMessages = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])
!hasManageMessages
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -215,9 +257,13 @@ export function deleteMessages(
}
/** Gets the invites for this channel. Requires MANAGE_CHANNEL */
export function getChannelInvites(channelID: string) {
export async function getChannelInvites(channelID: string) {
const hasManagaChannels = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_CHANNELS],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_CHANNELS])
!hasManagaChannels
) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
@@ -225,12 +271,16 @@ export function getChannelInvites(channelID: string) {
}
/** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */
export function createInvite(channelID: string, options: CreateInviteOptions) {
export async function createInvite(
channelID: string,
options: CreateInviteOptions,
) {
const hasCreateInstantInvitePerm = await botHasChannelPermissions(
channelID,
[Permissions.CREATE_INSTANT_INVITE],
);
if (
!botHasChannelPermissions(
channelID,
[Permissions.CREATE_INSTANT_INVITE],
)
!hasCreateInstantInvitePerm
) {
throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE);
}
@@ -238,9 +288,13 @@ export function createInvite(channelID: string, options: CreateInviteOptions) {
}
/** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */
export function getChannelWebhooks(channelID: string) {
export async function getChannelWebhooks(channelID: string) {
const hasManageWebhooksPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_WEBHOOKS],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_WEBHOOKS])
!hasManageWebhooksPerm
) {
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
}
@@ -293,12 +347,17 @@ function processEditChannelQueue() {
}
}
export function editChannel(
export async function editChannel(
channelID: string,
options: ChannelEditOptions,
reason?: string,
) {
const hasManageChannelsPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_CHANNELS],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_CHANNELS])
!hasManageChannelsPerm
) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
@@ -352,7 +411,10 @@ export function editChannel(
return RequestManager.patch(
endpoints.GUILD_CHANNEL(channelID),
payload,
{
...payload,
reason,
},
);
}
@@ -361,8 +423,12 @@ export async function followChannel(
sourceChannelID: string,
targetChannelID: string,
) {
const hasManageWebhooksPerm = await botHasChannelPermissions(
targetChannelID,
[Permissions.MANAGE_WEBHOOKS],
);
if (
!botHasChannelPermissions(targetChannelID, [Permissions.MANAGE_WEBHOOKS])
!hasManageWebhooksPerm
) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
@@ -376,3 +442,30 @@ export async function followChannel(
return data.webhook_id;
}
/**
* Checks whether a channel is synchronized with its parent/category channel or not.
* @param channelID The ID of the channel to test for synchronization
* @return Returns `true` if the channel is synchronized, otherwise `false`. Returns `false` if the channel is not cached.
*/
export async function isChannelSynced(channelID: string) {
const channel = await cacheHandlers.get("channels", channelID);
if (!channel?.parentID) return false;
const parentChannel = await cacheHandlers.get("channels", channel.parentID);
if (!parentChannel) return false;
return channel.permission_overwrites?.every((overwrite) => {
const permission = parentChannel.permission_overwrites?.find((ow) =>
ow.id === overwrite.id
);
if (!permission) return false;
if (
overwrite.allow !== permission.allow || overwrite.deny !== permission.deny
) {
return false;
}
return true;
});
}
+275 -95
View File
@@ -1,4 +1,3 @@
import { endpoints } from "../constants/discord.ts";
import { cacheHandlers } from "../controllers/cache.ts";
import { identifyPayload } from "../module/client.ts";
import { RequestManager } from "../module/requestManager.ts";
@@ -6,6 +5,7 @@ import { requestAllMembers } from "../module/shardingManager.ts";
import { Guild } from "../structures/guild.ts";
import { Member } from "../structures/member.ts";
import { structures } from "../structures/mod.ts";
import { Template } from "../structures/template.ts";
import { ImageFormats, ImageSize } from "../types/cdn.ts";
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts";
import { Errors } from "../types/errors.ts";
@@ -14,16 +14,22 @@ import {
BanOptions,
ChannelCreateOptions,
CreateEmojisOptions,
CreateGuildFromTemplate,
CreateGuildPayload,
CreateGuildTemplate,
CreateRoleOptions,
CreateServerOptions,
EditEmojisOptions,
EditGuildTemplate,
EditIntegrationOptions,
FetchMembersOptions,
GetAuditLogsOptions,
GuildEditOptions,
GuildTemplate,
PositionSwap,
PruneOptions,
PrunePayload,
UpdateGuildPayload,
UserPayload,
} from "../types/guild.ts";
import { MemberCreatePayload } from "../types/member.ts";
@@ -32,6 +38,7 @@ import { Permissions } from "../types/permission.ts";
import { RoleData } from "../types/role.ts";
import { formatImageURL } from "../utils/cdn.ts";
import { Collection } from "../utils/collection.ts";
import { endpoints } from "../utils/constants.ts";
import { botHasPermission, calculateBits } from "../utils/permissions.ts";
import { urlToBase64 } from "../utils/utils.ts";
@@ -97,7 +104,11 @@ export async function createGuildChannel(
name: string,
options?: ChannelCreateOptions,
) {
if (!botHasPermission(guild.id, [Permissions.MANAGE_CHANNELS])) {
const hasPerm = await botHasPermission(
guild.id,
[Permissions.MANAGE_CHANNELS],
);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
@@ -126,12 +137,16 @@ export async function createGuildChannel(
}
/** Delete a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */
export function deleteChannel(
export async function deleteChannel(
guildID: string,
channelID: string,
reason?: string,
) {
if (!botHasPermission(guildID, [Permissions.MANAGE_CHANNELS])) {
const hasPerm = await botHasPermission(
guildID,
[Permissions.MANAGE_CHANNELS],
);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
@@ -186,16 +201,20 @@ export function swapChannels(
*
* ⚠️ **ADVANCED USE ONLY: Your members will be cached in your guild most likely. Only use this when you are absolutely sure the member is not cached.**
*/
export async function getMember(guildID: string, id: string) {
export async function getMember(
guildID: string,
id: string,
options?: { force?: boolean },
) {
const guild = await cacheHandlers.get("guilds", guildID);
if (!guild) return;
if (!guild && !options?.force) return;
const data = await RequestManager.get(
endpoints.GUILD_MEMBER(guildID, id),
) as MemberCreatePayload;
const member = await structures.createMember(data, guild.id);
guild.members.set(id, member);
const member = await structures.createMember(data, guildID);
guild?.members.set(id, member);
return member;
}
@@ -223,9 +242,8 @@ export async function createEmoji(
image: string,
options: CreateEmojisOptions,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_EMOJIS])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
@@ -241,16 +259,16 @@ export async function createEmoji(
}
/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */
export function editEmoji(
export async function editEmoji(
guildID: string,
id: string,
options: EditEmojisOptions,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_EMOJIS])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
return RequestManager.patch(endpoints.GUILD_EMOJI(guildID, id), {
name: options.name,
roles: options.roles,
@@ -258,12 +276,16 @@ export function editEmoji(
}
/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */
export function deleteEmoji(guildID: string, id: string, reason?: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_EMOJIS])
) {
export async function deleteEmoji(
guildID: string,
id: string,
reason?: string,
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_EMOJIS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
return RequestManager.delete(
endpoints.GUILD_EMOJI(guildID, id),
{ reason },
@@ -281,11 +303,11 @@ export async function createGuildRole(
options: CreateRoleOptions,
reason?: string,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
const result = await RequestManager.post(
endpoints.GUILD_ROLES(guildID),
{
@@ -307,16 +329,16 @@ export async function createGuildRole(
}
/** Edit a guild role. Requires the MANAGE_ROLES permission. */
export function editRole(
export async function editRole(
guildID: string,
id: string,
options: CreateRoleOptions,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.patch(endpoints.GUILD_ROLE(guildID, id), {
...options,
permissions: options.permissions
@@ -326,12 +348,12 @@ export function editRole(
}
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
export function deleteRole(guildID: string, id: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
) {
export async function deleteRole(guildID: string, id: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.delete(endpoints.GUILD_ROLE(guildID, id));
}
@@ -339,22 +361,22 @@ export function deleteRole(guildID: string, id: string) {
*
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your roles will be cached in your guild.**
*/
export function getRoles(guildID: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
) {
export async function getRoles(guildID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.get(endpoints.GUILD_ROLES(guildID));
}
/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */
export function swapRoles(guildID: string, rolePositons: PositionSwap) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
) {
export async function swapRoles(guildID: string, rolePositons: PositionSwap) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.patch(endpoints.GUILD_ROLES(guildID), rolePositons);
}
@@ -363,9 +385,9 @@ export async function getPruneCount(guildID: string, options: PruneOptions) {
if (options.days < 1) {
throw new Error(Errors.PRUNE_MIN_DAYS);
}
if (
!botHasPermission(guildID, [Permissions.KICK_MEMBERS])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.KICK_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
@@ -378,13 +400,13 @@ export async function getPruneCount(guildID: string, options: PruneOptions) {
}
/** Begin pruning all members in the given time period */
export function pruneMembers(guildID: string, options: PruneOptions) {
export async function pruneMembers(guildID: string, options: PruneOptions) {
if (options.days < 1) {
throw new Error(Errors.PRUNE_MIN_DAYS);
}
if (
!botHasPermission(guildID, [Permissions.KICK_MEMBERS])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.KICK_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
@@ -405,8 +427,12 @@ export function fetchMembers(guild: Guild, options?: FetchMembersOptions) {
}
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
export function getAuditLogs(guildID: string, options: GetAuditLogsOptions) {
if (!botHasPermission(guildID, [Permissions.VIEW_AUDIT_LOG])) {
export async function getAuditLogs(
guildID: string,
options: GetAuditLogsOptions,
) {
const hasPerm = await botHasPermission(guildID, [Permissions.VIEW_AUDIT_LOG]);
if (!hasPerm) {
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG);
}
@@ -419,26 +445,26 @@ export function getAuditLogs(guildID: string, options: GetAuditLogsOptions) {
}
/** Returns the guild embed object. Requires the MANAGE_GUILD permission. */
export function getEmbed(guildID: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
export async function getEmbed(guildID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_EMBED(guildID));
}
/** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */
export function editEmbed(
export async function editEmbed(
guildID: string,
enabled: boolean,
channelID?: string | null,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.patch(
endpoints.GUILD_EMBED(guildID),
{ enabled, channel_id: channelID },
@@ -451,26 +477,26 @@ export function getVanityURL(guildID: string) {
}
/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */
export function getIntegrations(guildID: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
export async function getIntegrations(guildID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_INTEGRATIONS(guildID));
}
/** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */
export function editIntegration(
export async function editIntegration(
guildID: string,
id: string,
options: EditIntegrationOptions,
) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.patch(
endpoints.GUILD_INTEGRATION(guildID, id),
options,
@@ -478,30 +504,29 @@ export function editIntegration(
}
/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */
export function deleteIntegration(guildID: string, id: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
export async function deleteIntegration(guildID: string, id: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.delete(endpoints.GUILD_INTEGRATION(guildID, id));
}
/** Sync an integration. Requires the MANAGE_GUILD permission. */
export function syncIntegration(guildID: string, id: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
export async function syncIntegration(guildID: string, id: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(guildID, id));
}
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
export async function getBans(guildID: string) {
if (
!botHasPermission(guildID, [Permissions.BAN_MEMBERS])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
@@ -515,10 +540,9 @@ export async function getBans(guildID: string) {
}
/** Returns a ban object for the given user or a 404 not found if the ban cannot be found. Requires the BAN_MEMBERS permission. */
export function getBan(guildID: string, memberID: string) {
if (
!botHasPermission(guildID, [Permissions.BAN_MEMBERS])
) {
export async function getBan(guildID: string, memberID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
@@ -528,10 +552,9 @@ export function getBan(guildID: string, memberID: string) {
}
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires the BAN_MEMBERS permission. */
export function ban(guildID: string, id: string, options: BanOptions) {
if (
!botHasPermission(guildID, [Permissions.BAN_MEMBERS])
) {
export async function ban(guildID: string, id: string, options: BanOptions) {
const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
@@ -542,10 +565,9 @@ export function ban(guildID: string, id: string, options: BanOptions) {
}
/** Remove the ban for a user. REquires BAN_MEMBERS permission */
export function unban(guildID: string, id: string) {
if (
!botHasPermission(guildID, [Permissions.BAN_MEMBERS])
) {
export async function unban(guildID: string, id: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.BAN_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
return RequestManager.delete(endpoints.GUILD_BAN(guildID, id));
@@ -553,9 +575,8 @@ export function unban(guildID: string, id: string) {
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
export async function editGuild(guildID: string, options: GuildEditOptions) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
@@ -575,12 +596,12 @@ export async function editGuild(guildID: string, options: GuildEditOptions) {
}
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
export function getInvites(guildID: string) {
if (
!botHasPermission(guildID, [Permissions.MANAGE_GUILD])
) {
export async function getInvites(guildID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_INVITES(guildID));
}
@@ -595,8 +616,12 @@ export function getVoiceRegions(guildID: string) {
}
/** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */
export function getWebhooks(guildID: string) {
if (!botHasPermission(guildID, [Permissions.MANAGE_WEBHOOKS])) {
export async function getWebhooks(guildID: string) {
const hasPerm = await botHasPermission(
guildID,
[Permissions.MANAGE_WEBHOOKS],
);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
}
@@ -607,3 +632,158 @@ export function getWebhooks(guildID: string) {
export function getUser(userID: string) {
return RequestManager.get(endpoints.USER(userID)) as Promise<UserPayload>;
}
/**
* ⚠️ **If you need this, you are probably doing something wrong. Always use cache.guilds.get()
*
* Advanced Devs:
* This function fetches a guild's data. This is not the same data as a GUILD_CREATE.
* So it does not cache the guild, you must do it manually.
* */
export function getGuild(guildID: string, counts = true) {
return RequestManager.get(
endpoints.GUILD(guildID),
{ with_counts: counts },
) as Promise<UpdateGuildPayload>;
}
/** Returns the guild template if it exists */
export function getGuildTemplate(
guildID: string,
templateCode: string,
) {
return RequestManager.get(
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
) as Promise<Template>;
}
/**
* Create a new guild based on a template
* NOTE: This endpoint can be used only by bots in less than 10 guilds.
*/
export async function createGuildFromTemplate(
templateCode: string,
data: CreateGuildFromTemplate,
) {
if (await cacheHandlers.size("guilds") >= 10) {
throw new Error(
"This function can only be used by bots in less than 10 guilds.",
);
}
if (data.icon) {
data.icon = await urlToBase64(data.icon);
}
const guild = await RequestManager.post(
endpoints.GUILD_TEMPLATE(templateCode),
data,
) as Promise<CreateGuildPayload>;
return guild;
}
/**
* Returns an array of templates.
* Requires the `MANAGE_GUILD` permission.
*/
export async function getGuildTemplates(guildID: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
const templates = await RequestManager.get(
endpoints.GUILD_TEMPLATES(guildID),
) as GuildTemplate[];
return templates.map((template) => structures.createTemplate(template));
}
/**
* Deletes a template from a guild.
* Requires the `MANAGE_GUILD` permission.
*/
export async function deleteGuildTemplate(
guildID: string,
templateCode: string,
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
const deletedTemplate = await RequestManager.delete(
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
) as GuildTemplate;
return structures.createTemplate(deletedTemplate);
}
/**
* Creates a template for the guild.
* Requires the `MANAGE_GUILD` permission.
* @param name name of the template (1-100 characters)
* @param description description for the template (0-120 characters
*/
export async function createGuildTemplate(
guildID: string,
data: CreateGuildTemplate,
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) throw new Error(Errors.MISSING_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
) {
throw new Error("The description can only be in between 0-120 characters.");
}
const template = await RequestManager.post(
endpoints.GUILD_TEMPLATES(guildID),
data,
) as GuildTemplate;
return structures.createTemplate(template);
}
/**
* Syncs the template to the guild's current state.
* Requires the `MANAGE_GUILD` permission.
*/
export async function syncGuildTemplate(guildID: string, templateCode: string) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) throw new Error(Errors.MISSING_MANAGE_GUILD);
const template = await RequestManager.put(
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
) as GuildTemplate;
return structures.createTemplate(template);
}
/**
* Edit a template's metadata.
* Requires the `MANAGE_GUILD` permission.
*/
export async function editGuildTemplate(
guildID: string,
templateCode: string,
data: EditGuildTemplate,
) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_GUILD]);
if (!hasPerm) throw new Error(Errors.MISSING_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
) {
throw new Error("The description can only be in between 0-120 characters.");
}
const template = await RequestManager.patch(
`${endpoints.GUILD_TEMPLATES(guildID)}/${templateCode}`,
data,
) as GuildTemplate;
return structures.createTemplate(template);
}
+83 -20
View File
@@ -1,4 +1,3 @@
import { endpoints } from "../constants/discord.ts";
import { cacheHandlers } from "../controllers/cache.ts";
import { botID } from "../module/client.ts";
import { RequestManager } from "../module/requestManager.ts";
@@ -10,11 +9,13 @@ import { Errors } from "../types/errors.ts";
import { EditMemberOptions } from "../types/member.ts";
import { Permissions } from "../types/permission.ts";
import { formatImageURL } from "../utils/cdn.ts";
import { endpoints } from "../utils/constants.ts";
import {
botHasPermission,
higherRolePosition,
highestRole,
} from "../utils/permissions.ts";
import { urlToBase64 } from "../utils/utils.ts";
import { sendMessage } from "./channel.ts";
/** The users custom avatar or the default avatar if you don't have a member object. */
@@ -53,14 +54,19 @@ export async function addRole(
reason?: string,
) {
const botsHighestRole = await highestRole(guildID, botID);
if (
botsHighestRole &&
!higherRolePosition(guildID, botsHighestRole.id, roleID)
) {
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
if (botsHighestRole) {
const hasHigherRolePosition = await higherRolePosition(
guildID,
botsHighestRole.id,
roleID,
);
if (!hasHigherRolePosition) {
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
}
}
if (!botHasPermission(guildID, [Permissions.MANAGE_ROLES])) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
@@ -78,16 +84,23 @@ export async function removeRole(
reason?: string,
) {
const botsHighestRole = await highestRole(guildID, botID);
if (
botsHighestRole &&
!higherRolePosition(guildID, botsHighestRole.id, roleID)
) {
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
if (botsHighestRole) {
const hasHigherRolePosition = await higherRolePosition(
guildID,
botsHighestRole.id,
roleID,
);
if (!hasHigherRolePosition) {
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
}
}
if (!botHasPermission(guildID, [Permissions.MANAGE_ROLES])) {
const hasPerm = await botHasPermission(guildID, [Permissions.MANAGE_ROLES]);
if (!hasPerm) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.delete(
endpoints.GUILD_MEMBER_ROLE(guildID, memberID, roleID),
{ reason },
@@ -129,9 +142,11 @@ export async function kick(guildID: string, memberID: string, reason?: string) {
throw new Error(Errors.BOTS_HIGHEST_ROLE_TOO_LOW);
}
if (!botHasPermission(guildID, [Permissions.KICK_MEMBERS])) {
const hasPerm = await botHasPermission(guildID, [Permissions.KICK_MEMBERS]);
if (!hasPerm) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
return RequestManager.delete(
endpoints.GUILD_MEMBER(guildID, memberID),
{ reason },
@@ -139,7 +154,7 @@ export async function kick(guildID: string, memberID: string, reason?: string) {
}
/** Edit the member */
export function editMember(
export async function editMember(
guildID: string,
memberID: string,
options: EditMemberOptions,
@@ -148,30 +163,47 @@ export function editMember(
if (options.nick.length > 32) {
throw new Error(Errors.NICKNAMES_MAX_LENGTH);
}
if (!botHasPermission(guildID, [Permissions.MANAGE_NICKNAMES])) {
const hasManageNickPerm = await botHasPermission(
guildID,
[Permissions.MANAGE_NICKNAMES],
);
if (!hasManageNickPerm) {
throw new Error(Errors.MISSING_MANAGE_NICKNAMES);
}
}
const hasManageRolesPerm = await botHasPermission(
guildID,
[Permissions.MANAGE_ROLES],
);
if (
options.roles &&
!botHasPermission(guildID, [Permissions.MANAGE_ROLES])
!hasManageRolesPerm
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
if (options.mute) {
const hasMuteMembersPerm = await botHasPermission(
guildID,
[Permissions.MUTE_MEMBERS],
);
// TODO: This should check if the member is in a voice channel
if (
!botHasPermission(guildID, [Permissions.MUTE_MEMBERS])
!hasMuteMembersPerm
) {
throw new Error(Errors.MISSING_MUTE_MEMBERS);
}
}
const hasDeafenMembersPerm = await botHasPermission(
guildID,
[Permissions.DEAFEN_MEMBERS],
);
if (
options.deaf &&
!botHasPermission(guildID, [Permissions.DEAFEN_MEMBERS])
!hasDeafenMembersPerm
) {
throw new Error(Errors.MISSING_DEAFEN_MEMBERS);
}
@@ -184,7 +216,7 @@ export function editMember(
);
}
/**
/**
* Move a member from a voice channel to another.
* @param guildID the id of the guild which the channel exists in
* @param memberID the id of the member to move.
@@ -197,3 +229,34 @@ export function moveMember(
) {
return editMember(guildID, memberID, { channel_id: channelID });
}
/** Modifies the bot's username or avatar.
* NOTE: username: if changed may cause the bot's discriminator to be randomized.
*/
export function editBotProfile(username?: string, avatarURL?: string) {
// Nothing was edited
if (!username && !avatarURL) return;
// Check username requirements if username was provided
if (username) {
if (username.length > 32) {
throw new Error(Errors.USERNAME_MAX_LENGTH);
}
if (username.length < 2) {
throw new Error(Errors.USERNAME_MIN_LENGTH);
}
if (["@", "#", ":", "```"].some((char) => username.includes(char))) {
throw new Error(Errors.USERNAME_INVALID_CHARACTER);
}
if (["discordtag", "everyone", "here"].includes(username)) {
throw new Error(Errors.USERNAME_INVALID_USERNAME);
}
}
RequestManager.patch(
endpoints.USER_BOT,
{
username: username?.trim(),
avatar: avatarURL ? urlToBase64(avatarURL) : undefined,
},
);
}
+57 -23
View File
@@ -1,5 +1,4 @@
import { delay } from "../../deps.ts";
import { endpoints } from "../constants/discord.ts";
import { cacheHandlers } from "../controllers/cache.ts";
import { botID } from "../module/client.ts";
import { RequestManager } from "../module/requestManager.ts";
@@ -10,6 +9,7 @@ import { Errors } from "../types/errors.ts";
import { UserPayload } from "../types/guild.ts";
import { MessageCreateOptions } from "../types/message.ts";
import { Permissions } from "../types/permission.ts";
import { endpoints } from "../utils/constants.ts";
import { botHasChannelPermissions } from "../utils/permissions.ts";
/** Delete a message with the channel id and message id only. */
@@ -38,11 +38,12 @@ 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,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(
message.channelID,
[Permissions.MANAGE_MESSAGES],
)
!hasManageMessages
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -57,9 +58,13 @@ export async function deleteMessage(
}
/** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */
export function pin(channelID: string, messageID: string) {
export async function pin(channelID: string, messageID: string) {
const hasManageMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])
!hasManageMessagesPerm
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -67,9 +72,13 @@ export function pin(channelID: string, messageID: string) {
}
/** Unpin a message in a channel. Requires MANAGE_MESSAGES. */
export function unpin(channelID: string, messageID: string) {
export async function unpin(channelID: string, messageID: string) {
const hasManageMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])
!hasManageMessagesPerm
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -79,17 +88,25 @@ export function unpin(channelID: string, messageID: string) {
}
/** Create a reaction for the message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. Requires READ_MESSAGE_HISTORY and ADD_REACTIONS */
export function addReaction(
export async function addReaction(
channelID: string,
messageID: string,
reaction: string,
) {
if (!botHasChannelPermissions(channelID, [Permissions.ADD_REACTIONS])) {
const hasAddReactionsPerm = await botHasChannelPermissions(
channelID,
[Permissions.ADD_REACTIONS],
);
if (!hasAddReactionsPerm) {
throw new Error(Errors.MISSING_ADD_REACTIONS);
}
const hasReadMessageHistoryPerm = await botHasChannelPermissions(
channelID,
[Permissions.READ_MESSAGE_HISTORY],
);
if (
!botHasChannelPermissions(channelID, [Permissions.READ_MESSAGE_HISTORY])
!hasReadMessageHistoryPerm
) {
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
}
@@ -143,13 +160,17 @@ export function removeReaction(
}
/** Removes a reaction from the specified user on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
export function removeUserReaction(
export async function removeUserReaction(
channelID: string,
messageID: string,
reaction: string,
userID: string,
) {
if (!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])) {
const hasManageMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (!hasManageMessagesPerm) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -164,9 +185,13 @@ export function removeUserReaction(
}
/** Removes all reactions for all emojis on this message. */
export function removeAllReactions(channelID: string, messageID: string) {
export async function removeAllReactions(channelID: string, messageID: string) {
const hasManageMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])
!hasManageMessagesPerm
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -176,13 +201,17 @@ export function removeAllReactions(channelID: string, messageID: string) {
}
/** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
export function removeReactionEmoji(
export async function removeReactionEmoji(
channelID: string,
messageID: string,
reaction: string,
) {
const hasManageMessagesPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_MESSAGES],
);
if (
!botHasChannelPermissions(channelID, [Permissions.MANAGE_MESSAGES])
!hasManageMessagesPerm
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
@@ -216,18 +245,23 @@ export async function editMessage(
if (typeof content === "string") content = { content };
const hasSendMessagesPerm = await botHasChannelPermissions(
message.channelID,
[Permissions.SEND_MESSAGES],
);
if (
!botHasChannelPermissions(message.channelID, [Permissions.SEND_MESSAGES])
!hasSendMessagesPerm
) {
throw new Error(Errors.MISSING_SEND_MESSAGES);
}
const hasSendTtsMessagesPerm = await botHasChannelPermissions(
message.channelID,
[Permissions.SEND_TTS_MESSAGES],
);
if (
content.tts &&
!botHasChannelPermissions(
message.channelID,
[Permissions.SEND_TTS_MESSAGES],
)
!hasSendTtsMessagesPerm
) {
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
}
+203
View File
@@ -0,0 +1,203 @@
import {
channelOverwriteHasPermission,
createInvite,
deleteMessages,
editChannel,
followChannel,
getChannelInvites,
getChannelWebhooks,
getMessage,
getMessages,
getPins,
isChannelSynced,
sendMessage,
} from "./channel.ts";
import {
ban,
categoryChildrenIDs,
createEmoji,
createGuildChannel,
createGuildFromTemplate,
createGuildRole,
createGuildTemplate,
createServer,
deleteChannel,
deleteEmoji,
deleteGuildTemplate,
deleteIntegration,
deleteRole,
deleteServer,
editEmbed,
editEmoji,
editGuild,
editGuildTemplate,
editIntegration,
editRole,
emojiURL,
fetchMembers,
getAuditLogs,
getBan,
getBans,
getChannel,
getChannels,
getEmbed,
getGuild,
getGuildTemplate,
getGuildTemplates,
getIntegrations,
getInvites,
getMember,
getMembersByQuery,
getPruneCount,
getRoles,
getUser,
getVanityURL,
getVoiceRegions,
getWebhooks,
guildBannerURL,
guildIconURL,
guildSplashURL,
leaveGuild,
pruneMembers,
swapChannels,
swapRoles,
syncGuildTemplate,
syncIntegration,
unban,
} from "./guild.ts";
import {
addRole,
avatarURL,
editBotProfile,
editMember,
kick,
moveMember,
rawAvatarURL,
removeRole,
sendDirectMessage,
} from "./member.ts";
import {
addReaction,
addReactions,
deleteMessage,
deleteMessageByID,
editMessage,
getReactions,
pin,
publishMessage,
removeAllReactions,
removeReaction,
removeReactionEmoji,
removeUserReaction,
unpin,
} from "./message.ts";
import { createWebhook, executeWebhook, getWebhook } from "./webhook.ts";
export let handlers = {
// Channel handler
channelOverwriteHasPermission,
createInvite,
deleteMessages,
editChannel,
followChannel,
getChannelInvites,
getChannelWebhooks,
getMessage,
getMessages,
getPins,
isChannelSynced,
sendMessage,
// Guild handler
ban,
categoryChildrenIDs,
createEmoji,
createGuildChannel,
createGuildFromTemplate,
createGuildRole,
createGuildTemplate,
createServer,
deleteChannel,
deleteEmoji,
deleteGuildTemplate,
deleteIntegration,
deleteRole,
deleteServer,
editEmbed,
editEmoji,
editGuild,
editGuildTemplate,
editIntegration,
editRole,
emojiURL,
fetchMembers,
getAuditLogs,
getBan,
getBans,
getChannel,
getChannels,
getEmbed,
getGuild,
getGuildTemplate,
getGuildTemplates,
getIntegrations,
getInvites,
getMember,
getMembersByQuery,
getPruneCount,
getRoles,
getUser,
getVanityURL,
getVoiceRegions,
getWebhooks,
guildBannerURL,
guildIconURL,
guildSplashURL,
leaveGuild,
pruneMembers,
swapChannels,
swapRoles,
syncGuildTemplate,
syncIntegration,
unban,
// Member handler
addRole,
avatarURL,
editBotProfile,
editMember,
kick,
moveMember,
rawAvatarURL,
removeRole,
sendDirectMessage,
// Message handler
addReaction,
addReactions,
deleteMessage,
deleteMessageByID,
editMessage,
getReactions,
pin,
publishMessage,
removeAllReactions,
removeReaction,
removeReactionEmoji,
removeUserReaction,
unpin,
// Webhook handler
createWebhook,
executeWebhook,
getWebhook,
};
export type Handlers = typeof handlers;
export function updateHandlers(newHandlers: Partial<Handlers>) {
handlers = {
...handlers,
...newHandlers,
};
}
+6 -5
View File
@@ -1,4 +1,3 @@
import { endpoints } from "../constants/discord.ts";
import { RequestManager } from "../module/requestManager.ts";
import { structures } from "../structures/mod.ts";
import { Errors } from "../types/errors.ts";
@@ -9,6 +8,7 @@ import {
WebhookCreateOptions,
WebhookPayload,
} from "../types/webhook.ts";
import { endpoints } from "../utils/constants.ts";
import { botHasChannelPermissions } from "../utils/permissions.ts";
import { urlToBase64 } from "../utils/utils.ts";
@@ -20,11 +20,12 @@ export async function createWebhook(
channelID: string,
options: WebhookCreateOptions,
) {
const hasManageWebhooksPerm = await botHasChannelPermissions(
channelID,
[Permissions.MANAGE_WEBHOOKS],
);
if (
!botHasChannelPermissions(
channelID,
[Permissions.MANAGE_WEBHOOKS],
)
!hasManageWebhooksPerm
) {
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
}