formatting

This commit is contained in:
Skillz
2020-05-10 15:37:41 -04:00
parent b4950e9d32
commit c6e4624f49
26 changed files with 1079 additions and 681 deletions

View File

@@ -1,10 +1,5 @@
{
"deno.enable": true,
"editor.formatOnSave": true,
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
"deno.unstable": true
}

View File

@@ -1,10 +1,10 @@
export const baseEndpoints = {
/** Although, the version can be defaulted, keep the v6 as it can be changed to test newer versions when necessary. */
BASE_URL: 'https://discordapp.com/api/v6',
CDN_URL: 'https://cdn.discordapp.com'
}
BASE_URL: "https://discordapp.com/api/v6",
CDN_URL: "https://cdn.discordapp.com",
};
const GUILDS_BASE = (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`
const GUILDS_BASE = (id: string) => `${baseEndpoints.BASE_URL}/guilds/${id}`;
export const endpoints = {
GATEWAY_BOT: `${baseEndpoints.BASE_URL}/gateway/bot`,
@@ -12,44 +12,66 @@ export const endpoints = {
// Channel Endpoints
CHANNEL_MESSAGE: (id: string, message_id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}`,
CHANNEL_MESSAGES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages`,
CHANNEL_MESSAGES: (id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages`,
CHANNEL_PINS: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/pins`,
CHANNEL_BULK_DELETE: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/bulk-delete`,
CHANNEL_INVITES: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/invites`,
CHANNEL_WEBHOOKS: (id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/webhooks`,
CHANNEL_MESSAGE_REACTION_ME: (id: string, message_id: string, emoji: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}/@me`,
CHANNEL_MESSAGE_REACTIONS: (id: string, message_id: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions`,
CHANNEL_MESSAGE_REACTION: (id: string, message_id: string, emoji: string) => `${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}`,
CHANNEL_BULK_DELETE: (id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages/bulk-delete`,
CHANNEL_INVITES: (id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/invites`,
CHANNEL_WEBHOOKS: (id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/webhooks`,
CHANNEL_MESSAGE_REACTION_ME: (
id: string,
message_id: string,
emoji: string,
) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}/@me`,
CHANNEL_MESSAGE_REACTIONS: (id: string, message_id: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions`,
CHANNEL_MESSAGE_REACTION: (id: string, message_id: string, emoji: string) =>
`${baseEndpoints.BASE_URL}/channels/${id}/messages/${message_id}/reactions/${emoji}`,
// Guild Endpoints
GUILD: (id: string) => `${GUILDS_BASE(id)}`,
GUILD_AUDIT_LOGS: (id: string) => `${GUILDS_BASE(id)}/audit-logs`,
GUILD_BAN: (id: string, user_id: string) => `${GUILDS_BASE(id)}/bans/${user_id}`,
GUILD_BAN: (id: string, user_id: string) =>
`${GUILDS_BASE(id)}/bans/${user_id}`,
GUILD_BANS: (id: string) => `${GUILDS_BASE(id)}/bans`,
GUILD_BANNER: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/banners/${id}/${icon}`,
GUILD_BANNER: (id: string, icon: string) =>
`${baseEndpoints.CDN_URL}/banners/${id}/${icon}`,
GUILD_CHANNELS: (id: string) => `${GUILDS_BASE(id)}/channels`,
GUILD_EMBED: (id: string) => `${GUILDS_BASE(id)}/embed`,
GUILD_EMOJI: (id: string, emoji_id: string) => `${GUILDS_BASE(id)}/emojis/${emoji_id}`,
GUILD_EMOJI: (id: string, emoji_id: string) =>
`${GUILDS_BASE(id)}/emojis/${emoji_id}`,
GUILD_EMOJIS: (id: string) => `${GUILDS_BASE(id)}/emojis`,
GUILD_ICON: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/icons/${id}/${icon}`,
GUILD_INTEGRATION: (id: string, integration_id: string) => `${GUILDS_BASE(id)}/integrations/${integration_id}`,
GUILD_INTEGRATION_SYNC: (id: string, integration_id: string) => `${GUILDS_BASE(id)}/integrations/${integration_id}/sync`,
GUILD_ICON: (id: string, icon: string) =>
`${baseEndpoints.CDN_URL}/icons/${id}/${icon}`,
GUILD_INTEGRATION: (id: string, integration_id: string) =>
`${GUILDS_BASE(id)}/integrations/${integration_id}`,
GUILD_INTEGRATION_SYNC: (id: string, integration_id: string) =>
`${GUILDS_BASE(id)}/integrations/${integration_id}/sync`,
GUILD_INTEGRATIONS: (id: string) => `${GUILDS_BASE(id)}/integrations`,
GUILD_INVITES: (id: string) => `${GUILDS_BASE(id)}/invites`,
GUILD_LEAVE: (id: string) => `${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`,
GUILD_MEMBER: (id: string, member_id: string) => `${GUILDS_BASE(id)}/members/${member_id}`,
GUILD_LEAVE: (id: string) =>
`${baseEndpoints.BASE_URL}/users/@me/guilds/${id}`,
GUILD_MEMBER: (id: string, member_id: string) =>
`${GUILDS_BASE(id)}/members/${member_id}`,
GUILD_MEMBER_ROLE: (id: string, member_id: string, role_id: string) =>
`${GUILDS_BASE(id)}/members/${member_id}/roles/${role_id}`,
GUILD_PRUNE: (id: string) => `${GUILDS_BASE(id)}/prune`,
GUILD_REGIONS: (id: string) => `${GUILDS_BASE(id)}/regions`,
GUILD_ROLE: (id: string, role_id: string) => `${GUILDS_BASE(id)}/roles/${role_id}`,
GUILD_ROLE: (id: string, role_id: string) =>
`${GUILDS_BASE(id)}/roles/${role_id}`,
GUILD_ROLES: (id: string) => `${GUILDS_BASE(id)}/roles`,
GUILD_SPLASH: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`,
GUILD_SPLASH: (id: string, icon: string) =>
`${baseEndpoints.CDN_URL}/splashes/${id}/${icon}`,
GUILD_VANITY_URL: (id: string) => `${GUILDS_BASE(id)}/vanity-url`,
GUILD_WEBHOOKS: (id: string) => `${GUILDS_BASE(id)}/webhooks`,
// User endpoints
USER_AVATAR: (id: string, icon: string) => `${baseEndpoints.CDN_URL}/avatars/${id}/${icon}`,
USER_DEFAULT_AVATAR: (icon: number) => `${baseEndpoints.CDN_URL}/embed/avatars${icon}.png`
}
USER_AVATAR: (id: string, icon: string) =>
`${baseEndpoints.CDN_URL}/avatars/${id}/${icon}`,
USER_DEFAULT_AVATAR: (icon: number) =>
`${baseEndpoints.CDN_URL}/embed/avatars${icon}.png`,
};

View File

@@ -1,47 +1,49 @@
import { cache } from "../utils/cache.ts"
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts"
import { createChannel } from "../structures/channel.ts"
import { eventHandlers } from "../module/client.ts"
import { cache } from "../utils/cache.ts";
import { ChannelCreatePayload, ChannelTypes } from "../types/channel.ts";
import { createChannel } from "../structures/channel.ts";
import { eventHandlers } from "../module/client.ts";
export const handleInternalChannelCreate = (data: ChannelCreatePayload) => {
const channel = createChannel(data)
cache.channels.set(channel.id, channel)
eventHandlers.channelCreate?.(channel)
}
const channel = createChannel(data);
cache.channels.set(channel.id, channel);
eventHandlers.channelCreate?.(channel);
};
export const handleInternalChannelUpdate = (data: ChannelCreatePayload) => {
const cachedChannel = cache.channels.get(data.id)
const channel = createChannel(data)
cache.channels.set(channel.id, channel)
if (!cachedChannel) return
const cachedChannel = cache.channels.get(data.id);
const channel = createChannel(data);
cache.channels.set(channel.id, channel);
if (!cachedChannel) return;
eventHandlers.channel_update?.(channel, cachedChannel)
}
eventHandlers.channel_update?.(channel, cachedChannel);
};
export const handleInternalChannelDelete = (data: ChannelCreatePayload) => {
const cachedChannel = cache.channels.get(data.id)
if (!cachedChannel) return
const cachedChannel = cache.channels.get(data.id);
if (!cachedChannel) return;
if (cachedChannel.type === ChannelTypes.GUILD_VOICE && data.guild_id) {
const guild = cache.guilds.get(data.guild_id)
const guild = cache.guilds.get(data.guild_id);
guild?.voice_states.forEach((vs) => {
if (vs.channel_id !== data.id) return
if (vs.channel_id !== data.id) return;
const member = guild.members.get(vs.user_id)
if (!member) return
const member = guild.members.get(vs.user_id);
if (!member) return;
eventHandlers.voiceChannelLeave?.(member, vs.channel_id)
})
eventHandlers.voiceChannelLeave?.(member, vs.channel_id);
});
if (guild) {
cache.guilds.set(data.guild_id, {
...guild,
voice_states: [...guild.voice_states.filter((vs) => vs.channel_id !== data.id)],
})
voice_states: [
...guild.voice_states.filter((vs) => vs.channel_id !== data.id),
],
});
}
}
cache.channels.delete(data.id)
eventHandlers.channelDelete?.(cachedChannel)
}
cache.channels.delete(data.id);
eventHandlers.channelDelete?.(cachedChannel);
};

View File

@@ -1,14 +1,14 @@
import { cache } from "../utils/cache.ts"
import { Guild } from "../structures/guild.ts"
import { cache } from "../utils/cache.ts";
import { Guild } from "../structures/guild.ts";
export const handleInternalGuildCreate = (guild: Guild) => {
cache.guilds.set(guild.id, guild)
}
cache.guilds.set(guild.id, guild);
};
export const handleInternalGuildUpdate = (guild: Guild) => {
cache.guilds.set(guild.id, guild)
}
cache.guilds.set(guild.id, guild);
};
export const handleInternalGuildDelete = (guild: Guild) => {
cache.guilds.delete(guild.id)
}
cache.guilds.delete(guild.id);
};

10
mod.ts
View File

@@ -1,7 +1,7 @@
import Client from "./module/client.ts"
import { configs } from "./configs.ts"
import { Intents } from "./types/options.ts"
import { logYellow, logGreen } from "./utils/logger.ts"
import Client from "./module/client.ts";
import { configs } from "./configs.ts";
import { Intents } from "./types/options.ts";
import { logYellow, logGreen } from "./utils/logger.ts";
Client({
token: configs.token,
@@ -11,4 +11,4 @@ Client({
ready: () => logYellow("Bot ready emitted"),
raw: (data) => logGreen("[RAW] => " + JSON.stringify(data)),
},
})
});

View File

@@ -7,15 +7,18 @@ import {
MessageContent,
CreateInviteOptions,
ChannelEditOptions,
} from "../types/channel.ts"
import { botID, updateChannelCache } from "../module/client.ts"
import { endpoints } from "../constants/discord.ts"
import { createMessage } from "./message.ts"
import { MessageCreateOptions } from "../types/message.ts"
import { calculatePermissions, botHasPermission } from "../utils/permissions.ts"
import { Permissions } from "../types/permission.ts"
import { Errors } from "../types/errors.ts"
import { RequestManager } from "../module/requestManager.ts"
} from "../types/channel.ts";
import { botID, updateChannelCache } from "../module/client.ts";
import { endpoints } from "../constants/discord.ts";
import { createMessage } from "./message.ts";
import { MessageCreateOptions } from "../types/message.ts";
import {
calculatePermissions,
botHasPermission,
} from "../utils/permissions.ts";
import { Permissions } from "../types/permission.ts";
import { Errors } from "../types/errors.ts";
import { RequestManager } from "../module/requestManager.ts";
export function createChannel(data: ChannelCreatePayload) {
const channel = {
@@ -25,10 +28,10 @@ export function createChannel(data: ChannelCreatePayload) {
/** The permission overwrites for this channel */
permissions: data.permission_overwrites
? data.permission_overwrites.map((perm) => ({
...perm,
allow: calculatePermissions(perm.allow),
deny: calculatePermissions(perm.deny),
}))
...perm,
allow: calculatePermissions(perm.allow),
deny: calculatePermissions(perm.deny),
}))
: [],
/** Whether this channel is nsfw or not */
nsfw: data.nsfw || false,
@@ -38,96 +41,168 @@ export function createChannel(data: ChannelCreatePayload) {
/** Fetch a single message from the server. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */
getMessage: async (id: string) => {
if (data.guild_id) {
if (!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL]))
throw new Error(Errors.MISSING_VIEW_CHANNEL)
if (!botHasPermission(data.guild_id, botID, [Permissions.READ_MESSAGE_HISTORY]))
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY)
if (
!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL])
) {
throw new Error(Errors.MISSING_VIEW_CHANNEL);
}
if (
!botHasPermission(
data.guild_id,
botID,
[Permissions.READ_MESSAGE_HISTORY],
)
) {
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
}
}
const result = await RequestManager.get(endpoints.CHANNEL_MESSAGE(data.id, id))
return createMessage(result)
const result = await RequestManager.get(
endpoints.CHANNEL_MESSAGE(data.id, id),
);
return createMessage(result);
},
/** Fetches between 2-100 messages. Requires VIEW_CHANNEL and READ_MESSAGE_HISTORY */
getMessages: async (options?: GetMessagesAfter | GetMessagesBefore | GetMessagesAround | GetMessages) => {
getMessages: async (
options?:
| GetMessagesAfter
| GetMessagesBefore
| GetMessagesAround
| GetMessages,
) => {
if (data.guild_id) {
if (!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL]))
throw new Error(Errors.MISSING_VIEW_CHANNEL)
if (!botHasPermission(data.guild_id, botID, [Permissions.READ_MESSAGE_HISTORY]))
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY)
if (
!botHasPermission(data.guild_id, botID, [Permissions.VIEW_CHANNEL])
) {
throw new Error(Errors.MISSING_VIEW_CHANNEL);
}
if (
!botHasPermission(
data.guild_id,
botID,
[Permissions.READ_MESSAGE_HISTORY],
)
) {
throw new Error(Errors.MISSING_READ_MESSAGE_HISTORY);
}
}
if (options?.limit && options.limit > 100) return
if (options?.limit && options.limit > 100) return;
const result = (await RequestManager.get(endpoints.CHANNEL_MESSAGES(data.id), options)) as MessageCreateOptions[]
return result.map((res) => createMessage(res))
const result =
(await RequestManager.get(
endpoints.CHANNEL_MESSAGES(data.id),
options,
)) as MessageCreateOptions[];
return result.map((res) => createMessage(res));
},
/** Get pinned messages in this channel. */
getPins: async () => {
const result = (await RequestManager.get(endpoints.CHANNEL_PINS(data.id))) as MessageCreateOptions[]
return result.map((res) => createMessage(res))
const result =
(await RequestManager.get(
endpoints.CHANNEL_PINS(data.id),
)) as MessageCreateOptions[];
return result.map((res) => createMessage(res));
},
/** Send a message to the channel. Requires SEND_MESSAGES permission. */
sendMessage: async (content: string | MessageContent) => {
if (typeof content === "string") content = { content }
if (typeof content === "string") content = { content };
if (data.guild_id) {
if (!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES]))
throw new Error(Errors.MISSING_SEND_MESSAGES)
if (content.tts && !botHasPermission(data.guild_id, botID, [Permissions.SEND_TTS_MESSAGES]))
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE)
if (
!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES])
) {
throw new Error(Errors.MISSING_SEND_MESSAGES);
}
if (
content.tts &&
!botHasPermission(
data.guild_id,
botID,
[Permissions.SEND_TTS_MESSAGES],
)
) {
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
}
}
if (content.content && content.content.length > 2000) throw new Error(Errors.MESSAGE_MAX_LENGTH)
if (content.content && content.content.length > 2000) {
throw new Error(Errors.MESSAGE_MAX_LENGTH);
}
const result = await RequestManager.post(endpoints.CHANNEL_MESSAGES(data.id), content)
return createMessage(result)
const result = await RequestManager.post(
endpoints.CHANNEL_MESSAGES(data.id),
content,
);
return createMessage(result);
},
/** Delete messages from the channel. 2-100. Requires the MANAGE_MESSAGES permission */
deleteMessages: (ids: string[], reason?: string) => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
if (ids.length < 2) throw new Error(Errors.DELETE_MESSAGES_MIN)
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
if (ids.length < 2) throw new Error(Errors.DELETE_MESSAGES_MIN);
if (ids.length > 100)
if (ids.length > 100) {
console.warn(
`This endpoint only accepts a maximum of 100 messages. Deleting the first 100 message ids provided.`
)
`This endpoint only accepts a maximum of 100 messages. Deleting the first 100 message ids provided.`,
);
}
return RequestManager.post(endpoints.CHANNEL_BULK_DELETE(data.id), {
messages: ids.splice(0, 100),
reason,
})
});
},
/** Gets the invites for this channel. Requires MANAGE_CHANNEL */
getInvites: () => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_CHANNELS]))
throw new Error(Errors.MISSING_MANAGE_CHANNELS)
return RequestManager.get(endpoints.CHANNEL_INVITES(data.id))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_CHANNELS])
) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
return RequestManager.get(endpoints.CHANNEL_INVITES(data.id));
},
/** Creates a new invite for this channel. Requires CREATE_INSTANT_INVITE */
createInvite: (options: CreateInviteOptions) => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.CREATE_INSTANT_INVITE]))
throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE)
return RequestManager.post(endpoints.CHANNEL_INVITES(data.id), options)
if (
data.guild_id &&
!botHasPermission(
data.guild_id,
botID,
[Permissions.CREATE_INSTANT_INVITE],
)
) {
throw new Error(Errors.MISSING_CREATE_INSTANT_INVITE);
}
return RequestManager.post(endpoints.CHANNEL_INVITES(data.id), options);
},
/** Gets the webhooks for this channel. Requires MANAGE_WEBHOOKS */
getWebhooks: () => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_WEBHOOKS]))
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS)
return RequestManager.get(endpoints.CHANNEL_WEBHOOKS(data.id))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_WEBHOOKS])
) {
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
}
return RequestManager.get(endpoints.CHANNEL_WEBHOOKS(data.id));
},
edit: (options: ChannelEditOptions) => {
return RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), options)
return RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), options);
},
// TODO: after learning opus and stuff
/** Join a voice channel. */
// join: () => {},
/** Leave a voice channel */
// leave: () => {}
}
};
updateChannelCache(data.id, channel)
return channel
updateChannelCache(data.id, channel);
return channel;
}
export type Channel = ReturnType<typeof createChannel>
export type Channel = ReturnType<typeof createChannel>;

View File

@@ -1,6 +1,6 @@
import { botID } from "../module/client.ts"
import { endpoints } from "../constants/discord.ts"
import { formatImageURL } from "../utils/cdn.ts"
import { botID } from "../module/client.ts";
import { endpoints } from "../constants/discord.ts";
import { formatImageURL } from "../utils/cdn.ts";
import {
CreateGuildPayload,
PrunePayload,
@@ -12,17 +12,21 @@ import {
CreateEmojisOptions,
EditEmojisOptions,
CreateRoleOptions,
} from "../types/guild.ts"
import { createRole } from "./role.ts"
import { createMember } from "./member.ts"
import { createChannel } from "./channel.ts"
import { CreateChannelOptions, ChannelTypes, ChannelCreatePayload } from "../types/channel.ts"
import { ImageSize, ImageFormats } from "../types/cdn.ts"
import { Permissions, Permission } from "../types/permission.ts"
import { botHasPermission } from "../utils/permissions.ts"
import { Errors } from "../types/errors.ts"
import { RequestManager } from "../module/requestManager.ts"
import { RoleData } from "../types/role.ts"
} from "../types/guild.ts";
import { createRole } from "./role.ts";
import { createMember } from "./member.ts";
import { createChannel } from "./channel.ts";
import {
CreateChannelOptions,
ChannelTypes,
ChannelCreatePayload,
} from "../types/channel.ts";
import { ImageSize, ImageFormats } from "../types/cdn.ts";
import { Permissions, Permission } from "../types/permission.ts";
import { botHasPermission } from "../utils/permissions.ts";
import { Errors } from "../types/errors.ts";
import { RequestManager } from "../module/requestManager.ts";
import { RoleData } from "../types/role.ts";
export const createGuild = (data: CreateGuildPayload) => {
const guild = {
@@ -34,253 +38,391 @@ export const createGuild = (data: CreateGuildPayload) => {
/** When this guild was joined at. */
joinedAt: Date.parse(data.joined_at),
/** The users in this guild. */
members: new Map(data.members.map((m) => [m.user.id, createMember(m, data.id, data.roles, data.owner_id)])),
members: new Map(
data.members.map((
m,
) => [m.user.id, createMember(m, data.id, data.roles, data.owner_id)]),
),
/** The channels in the guild */
channels: new Map(data.channels.map((c) => [c.id, createChannel(c)])),
/** The presences of all the users in the guild. */
presences: new Map(data.presences.map((p) => [p.user.id, p])),
/** Gets an array of all the channels ids that are the children of this category. */
categoryChildrenIDs: (id: string) => data.channels.filter((c) => c.parent_id === id).map((c) => c.id),
categoryChildrenIDs: (id: string) =>
data.channels.filter((c) => c.parent_id === id).map((c) => c.id),
/** The full URL of the icon from Discords CDN. Undefined when no icon is set. */
iconURL: (size: ImageSize = 128, format?: ImageFormats) =>
data.icon ? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format) : undefined,
data.icon
? formatImageURL(endpoints.GUILD_ICON(data.id, data.icon), size, format)
: undefined,
/** The full URL of the splash from Discords CDN. Undefined if no splash is set. */
splashURL: (size: ImageSize = 128, format?: ImageFormats) =>
data.splash ? formatImageURL(endpoints.GUILD_SPLASH(data.id, data.splash), size, format) : undefined,
data.splash
? formatImageURL(
endpoints.GUILD_SPLASH(data.id, data.splash),
size,
format,
)
: undefined,
/** The full URL of the banner from Discords CDN. Undefined if no banner is set. */
bannerURL: (size: ImageSize = 128, format?: ImageFormats) =>
data.banner ? formatImageURL(endpoints.GUILD_BANNER(data.id, data.banner), size, format) : undefined,
data.banner
? formatImageURL(
endpoints.GUILD_BANNER(data.id, data.banner),
size,
format,
)
: undefined,
/** Create a channel in your server. Bot needs MANAGE_CHANNEL permissions in the server. */
createChannel: async (name: string, options: CreateChannelOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_CHANNELS]))
throw new Error(Errors.MISSING_MANAGE_CHANNELS)
const result = (await RequestManager.post(endpoints.GUILD_CHANNELS(data.id), {
name,
type: options.type ? ChannelTypes[options.type] : undefined,
permission_overwrites: options?.permission_overwrites
? options.permission_overwrites.map((perm) => ({
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_CHANNELS])) {
throw new Error(Errors.MISSING_MANAGE_CHANNELS);
}
const result =
(await RequestManager.post(endpoints.GUILD_CHANNELS(data.id), {
name,
type: options.type ? ChannelTypes[options.type] : undefined,
permission_overwrites: options?.permission_overwrites
? options.permission_overwrites.map((perm) => ({
...perm,
allow: perm.allow.map((p) => Permissions[p]),
deny: perm.deny.map((p) => Permissions[p]),
}))
: undefined,
...options,
})) as ChannelCreatePayload
: undefined,
...options,
})) as ChannelCreatePayload;
const channel = createChannel(result)
guild.channels.set(result.id, channel)
return channel
const channel = createChannel(result);
guild.channels.set(result.id, channel);
return channel;
},
/** 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.**
*/
getChannels: () => {
return RequestManager.get(endpoints.GUILD_CHANNELS(data.id))
return RequestManager.get(endpoints.GUILD_CHANNELS(data.id));
},
/** Modify the positions of channels on the guild. Requires MANAGE_CHANNELS permisison. */
swapChannels: (channelPositions: PositionSwap[]) => {
if (channelPositions.length < 2) {
throw "You must provide atleast two channels to be swapped."
throw "You must provide atleast two channels to be swapped.";
}
return RequestManager.patch(endpoints.GUILD_CHANNELS(data.id), channelPositions)
return RequestManager.patch(
endpoints.GUILD_CHANNELS(data.id),
channelPositions,
);
},
/** Returns a guild member object for the specified user.
*
* ⚠️ **If you need this, you are probably doing something wrong. This is not intended for use. Your members will be cached in your guild.**
*/
getMember: (id: string) => {
return RequestManager.get(endpoints.GUILD_MEMBER(data.id, id))
return RequestManager.get(endpoints.GUILD_MEMBER(data.id, id));
},
/** Create an emoji in the server. Emojis and animated emojis have a maximum file size of 256kb. Attempting to upload an emoji larger than this limit will fail and return 400 Bad Request and an error message, but not a JSON status code. */
createEmoji: (name: string, image: string, options: CreateEmojisOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])) throw new Error(Errors.MISSING_MANAGE_EMOJIS)
createEmoji: (
name: string,
image: string,
options: CreateEmojisOptions,
) => {
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
return RequestManager.post(endpoints.GUILD_EMOJIS(data.id), {
...options,
name,
image,
})
});
},
/** Modify the given emoji. Requires the MANAGE_EMOJIS permission. */
editEmoji: (id: string, options: EditEmojisOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])) throw new Error(Errors.MISSING_MANAGE_EMOJIS)
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
return RequestManager.patch(endpoints.GUILD_EMOJI(data.id, id), {
name: options.name,
roles: options.roles,
})
});
},
/** Delete the given emoji. Requires the MANAGE_EMOJIS permission. Returns 204 No Content on success. */
deleteEmoji: (id: string, reason?: string) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])) throw new Error(Errors.MISSING_MANAGE_EMOJIS)
return RequestManager.delete(endpoints.GUILD_EMOJI(data.id, id), { reason })
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_EMOJIS])
) {
throw new Error(Errors.MISSING_MANAGE_EMOJIS);
}
return RequestManager.delete(
endpoints.GUILD_EMOJI(data.id, id),
{ reason },
);
},
/** Create a new role for the guild. Requires the MANAGE_ROLES permission. */
createRole: async (options: CreateRoleOptions, reason?: string) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
const role_data = await RequestManager.post(endpoints.GUILD_ROLES(data.id), {
...options,
permissions: options.permissions?.map((perm) => Permissions[perm]),
reason,
})
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
const role_data = await RequestManager.post(
endpoints.GUILD_ROLES(data.id),
{
...options,
permissions: options.permissions?.map((perm) => Permissions[perm]),
reason,
},
);
const role = createRole(role_data as RoleData)
guild.roles.set(role_data.id, role)
return role
const role = createRole(role_data as RoleData);
guild.roles.set(role_data.id, role);
return role;
},
/** Edit a guild role. Requires the MANAGE_ROLES permission. */
editRole: (id: string, options: CreateRoleOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options)
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.patch(endpoints.GUILD_ROLE(data.id, id), options);
},
/** Delete a guild role. Requires the MANAGE_ROLES permission. */
deleteRole: (id: string) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.delete(endpoints.GUILD_ROLE(data.id, id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.delete(endpoints.GUILD_ROLE(data.id, id));
},
/** 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.**
*/
getRoles: () => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.get(endpoints.GUILD_ROLES(data.id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.get(endpoints.GUILD_ROLES(data.id));
},
/** Modify the positions of a set of role objects for the guild. Requires the MANAGE_ROLES permission. */
swapRoles: (rolePositons: PositionSwap) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.patch(endpoints.GUILD_ROLES(data.id), rolePositons)
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.patch(endpoints.GUILD_ROLES(data.id), rolePositons);
},
/** Check how many members would be removed from the server in a prune operation. Requires the KICK_MEMBERS permission */
getPruneCount: async (days: number) => {
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS)
if (!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])) throw new Error(Errors.MISSING_KICK_MEMBERS)
const result = (await RequestManager.get(endpoints.GUILD_PRUNE(data.id), { days })) as PrunePayload
return result.pruned
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
if (
!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])
) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
const result =
(await RequestManager.get(
endpoints.GUILD_PRUNE(data.id),
{ days },
)) as PrunePayload;
return result.pruned;
},
/** Begin pruning all members in the given time period */
pruneMembers: (days: number) => {
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS)
if (!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])) throw new Error(Errors.MISSING_KICK_MEMBERS)
return RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days })
if (days < 1) throw new Error(Errors.PRUNE_MIN_DAYS);
if (
!botHasPermission(data.id, botID, [Permissions.KICK_MEMBERS])
) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
return RequestManager.post(endpoints.GUILD_PRUNE(data.id), { days });
},
// TODO: REQUEST THIS OVER WEBSOCKET WITH GET_GUILD_MEMBERS ENDPOINT
// fetch_all_members: () => {
// },
/** Returns the audit logs for the guild. Requires VIEW AUDIT LOGS permission */
getAuditLogs: (options: GetAuditLogsOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.VIEW_AUDIT_LOG]))
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG)
if (!botHasPermission(data.id, botID, [Permissions.VIEW_AUDIT_LOG])) {
throw new Error(Errors.MISSING_VIEW_AUDIT_LOG);
}
return RequestManager.get(endpoints.GUILD_AUDIT_LOGS(data.id), {
...options,
limit: options.limit && options.limit >= 1 && options.limit <= 100 ? options.limit : 50,
})
limit: options.limit && options.limit >= 1 && options.limit <= 100
? options.limit
: 50,
});
},
/** Returns the guild embed object. Requires the MANAGE_GUILD permission. */
getEmbed: () => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.get(endpoints.GUILD_EMBED(data.id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_EMBED(data.id));
},
/** Modify a guild embed object for the guild. Requires the MANAGE_GUILD permission. */
editEmbed: (enabled: boolean, channel_id?: string | null) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.patch(endpoints.GUILD_EMBED(data.id), { enabled, channel_id })
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.patch(
endpoints.GUILD_EMBED(data.id),
{ enabled, channel_id },
);
},
/** Returns the code and uses of the vanity url for this server if it is enabled. Requires the MANAGE_GUILD permission. */
getVanityURL: () => {
return RequestManager.get(endpoints.GUILD_VANITY_URL(data.id))
return RequestManager.get(endpoints.GUILD_VANITY_URL(data.id));
},
/** Returns a list of integrations for the guild. Requires the MANAGE_GUILD permission. */
getIntegrations: () => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_INTEGRATIONS(data.id));
},
/** Modify the behavior and settings of an integration object for the guild. Requires the MANAGE_GUILD permission. */
editIntegration: (id: string, options: EditIntegrationOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.patch(endpoints.GUILD_INTEGRATION(data.id, id), options)
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.patch(
endpoints.GUILD_INTEGRATION(data.id, id),
options,
);
},
/** Delete the attached integration object for the guild with this id. Requires MANAGE_GUILD permission. */
deleteIntegration: (id: string) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.delete(endpoints.GUILD_INTEGRATION(data.id, id));
},
/** Sync an integration. Requires teh MANAGE_GUILD permission. */
syncIntegration: (id: string) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(data.id, id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.post(endpoints.GUILD_INTEGRATION_SYNC(data.id, id));
},
/** Returns a list of ban objects for the users banned from this guild. Requires the BAN_MEMBERS permission. */
getBans: () => {
if (!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])) throw new Error(Errors.MISSING_BAN_MEMBERS)
return RequestManager.get(endpoints.GUILD_BANS(data.id))
if (
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
return RequestManager.get(endpoints.GUILD_BANS(data.id));
},
/** Ban a user from the guild and optionally delete previous messages sent by the user. Requires teh BAN_MEMBERS permission. */
ban: (id: string, options: BanOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])) throw new Error(Errors.MISSING_BAN_MEMBERS)
return RequestManager.put(endpoints.GUILD_BAN(data.id, id), options)
if (
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
return RequestManager.put(endpoints.GUILD_BAN(data.id, id), options);
},
/** Remove the ban for a user. REquires BAN_MEMBERS permission */
unban: (id: string) => {
if (!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])) throw new Error(Errors.MISSING_BAN_MEMBERS)
return RequestManager.delete(endpoints.GUILD_BAN(data.id, id))
if (
!botHasPermission(data.id, botID, [Permissions.BAN_MEMBERS])
) {
throw new Error(Errors.MISSING_BAN_MEMBERS);
}
return RequestManager.delete(endpoints.GUILD_BAN(data.id, id));
},
/** Check whether a member has certain permissions in this channel. */
channelHasPermissions: (channelID: string, memberID: string, permissions: Permission[]) => {
if (memberID === data.owner_id) return true
channelHasPermissions: (
channelID: string,
memberID: string,
permissions: Permission[],
) => {
if (memberID === data.owner_id) return true;
const member = guild.members.get(memberID)
const member = guild.members.get(memberID);
if (!member) {
throw "Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild."
throw "Invalid member id provided. This member was not found in the cache. Please fetch them with getMember on guild.";
}
const channel = guild.channels.get(channelID)
const channel = guild.channels.get(channelID);
if (!channel) {
throw "Invalid channel id provided. This channel was not found in the cache."
throw "Invalid channel id provided. This channel was not found in the cache.";
}
let permissionBits = member.roles.reduce((bits, roleID) => {
const role = guild.roles.get(roleID)
if (!role) return bits
const role = guild.roles.get(roleID);
if (!role) return bits;
bits |= role.permissions
bits |= role.permissions;
return bits
}, 0)
return bits;
}, 0);
if (permissionBits & Permissions.ADMINISTRATOR) return true
if (permissionBits & Permissions.ADMINISTRATOR) return true;
return permissions.every((permission) => permissionBits & Permissions[permission])
return permissions.every((permission) =>
permissionBits & Permissions[permission]
);
},
/** Modify a guilds settings. Requires the MANAGE_GUILD permission. */
edit: (options: GuildEditOptions) => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.patch(endpoints.GUILD(data.id), options)
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.patch(endpoints.GUILD(data.id), options);
},
/** Get all the invites for this guild. Requires MANAGE_GUILD permission */
getInvites: () => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])) throw new Error(Errors.MISSING_MANAGE_GUILD)
return RequestManager.get(endpoints.GUILD_INVITES(data.id))
if (
!botHasPermission(data.id, botID, [Permissions.MANAGE_GUILD])
) {
throw new Error(Errors.MISSING_MANAGE_GUILD);
}
return RequestManager.get(endpoints.GUILD_INVITES(data.id));
},
/** Leave a guild */
leave: () => {
return RequestManager.delete(endpoints.GUILD_LEAVE(data.id))
return RequestManager.delete(endpoints.GUILD_LEAVE(data.id));
},
/** Returns a list of voice region objects for the guild. Unlike the similar /voice route, this returns VIP servers when the guild is VIP-enabled. */
getVoiceRegions: () => {
return RequestManager.get(endpoints.GUILD_REGIONS(data.id))
return RequestManager.get(endpoints.GUILD_REGIONS(data.id));
},
/** Returns a list of guild webhooks objects. Requires the MANAGE_WEBHOOKs permission. */
getWebhooks: () => {
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_WEBHOOKS]))
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS)
if (!botHasPermission(data.id, botID, [Permissions.MANAGE_WEBHOOKS])) {
throw new Error(Errors.MISSING_MANAGE_WEBHOOKS);
}
return RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id))
return RequestManager.get(endpoints.GUILD_WEBHOOKS(data.id));
},
}
};
return guild
}
return guild;
};
export type Guild = ReturnType<typeof createGuild>
export type Guild = ReturnType<typeof createGuild>;

View File

@@ -1,15 +1,20 @@
import { botID } from "../module/client.ts"
import { endpoints } from "../constants/discord.ts"
import { formatImageURL } from "../utils/cdn.ts"
import { MemberCreatePayload, EditMemberOptions } from "../types/member.ts"
import { ImageSize, ImageFormats } from "../types/cdn.ts"
import { Permission, Permissions } from "../types/permission.ts"
import { RoleData } from "../types/role.ts"
import { memberHasPermission, botHasPermission } from "../utils/permissions.ts"
import { Errors } from "../types/errors.ts"
import { RequestManager } from "../module/requestManager.ts"
import { botID } from "../module/client.ts";
import { endpoints } from "../constants/discord.ts";
import { formatImageURL } from "../utils/cdn.ts";
import { MemberCreatePayload, EditMemberOptions } from "../types/member.ts";
import { ImageSize, ImageFormats } from "../types/cdn.ts";
import { Permission, Permissions } from "../types/permission.ts";
import { RoleData } from "../types/role.ts";
import { memberHasPermission, botHasPermission } from "../utils/permissions.ts";
import { Errors } from "../types/errors.ts";
import { RequestManager } from "../module/requestManager.ts";
export const createMember = (data: MemberCreatePayload, guildID: string, roleData: RoleData[], ownerID: string) => ({
export const createMember = (
data: MemberCreatePayload,
guildID: string,
roleData: RoleData[],
ownerID: string,
) => ({
...data,
/** The complete raw data from the member create payload */
raw: data,
@@ -25,51 +30,100 @@ export const createMember = (data: MemberCreatePayload, guildID: string, roleDat
/** The users custom avatar or the default avatar */
avatarURL: (size: ImageSize = 128, format?: ImageFormats) =>
data.user.avatar
? formatImageURL(endpoints.USER_AVATAR(data.user.id, data.user.avatar), size, format)
? formatImageURL(
endpoints.USER_AVATAR(data.user.id, data.user.avatar),
size,
format,
)
: endpoints.USER_DEFAULT_AVATAR(Number(data.user.discriminator) % 5),
/** Add a role to the member */
addRole: (roleID: string, reason?: string) => {
// TODO: check if the bots highest role is above this one
if (!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.put(endpoints.GUILD_MEMBER_ROLE(guildID, data.user.id, roleID), { reason })
if (
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.put(
endpoints.GUILD_MEMBER_ROLE(guildID, data.user.id, roleID),
{ reason },
);
},
/** Remove a role from the member */
remove_role: (roleID: string, reason?: string) => {
// TODO: check if the bots highest role is above this role
if (!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])) throw new Error(Errors.MISSING_MANAGE_ROLES)
return RequestManager.delete(endpoints.GUILD_MEMBER_ROLE(guildID, data.user.id, roleID), { reason })
if (
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
return RequestManager.delete(
endpoints.GUILD_MEMBER_ROLE(guildID, data.user.id, roleID),
{ reason },
);
},
/** Kick a member from the server */
kick: (reason?: string) => {
// TODO: Check if the bot is above the user so it is capable of kicking
if (!botHasPermission(guildID, botID, [Permissions.KICK_MEMBERS])) throw new Error(Errors.MISSING_KICK_MEMBERS)
return RequestManager.delete(endpoints.GUILD_MEMBER(guildID, data.user.id), { reason })
if (
!botHasPermission(guildID, botID, [Permissions.KICK_MEMBERS])
) {
throw new Error(Errors.MISSING_KICK_MEMBERS);
}
return RequestManager.delete(
endpoints.GUILD_MEMBER(guildID, data.user.id),
{ reason },
);
},
/** Edit the member */
edit: (options: EditMemberOptions) => {
if (options.nick) {
if (options.nick.length > 32) throw new Error(Errors.NICKNAMES_MAX_LENGTH)
if (!botHasPermission(guildID, botID, [Permissions.MANAGE_NICKNAMES]))
throw new Error(Errors.MISSING_MANAGE_NICKNAMES)
if (options.nick.length > 32) {
throw new Error(Errors.NICKNAMES_MAX_LENGTH);
}
if (!botHasPermission(guildID, botID, [Permissions.MANAGE_NICKNAMES])) {
throw new Error(Errors.MISSING_MANAGE_NICKNAMES);
}
}
if (options.roles && !botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES]))
throw new Error(Errors.MISSING_MANAGE_ROLES)
if (
options.roles &&
!botHasPermission(guildID, botID, [Permissions.MANAGE_ROLES])
) {
throw new Error(Errors.MISSING_MANAGE_ROLES);
}
if (options.mute) {
// TODO: This should check if the member is in a voice channel
if (!botHasPermission(guildID, botID, [Permissions.MUTE_MEMBERS])) throw new Error(Errors.MISSING_MUTE_MEMBERS)
if (
!botHasPermission(guildID, botID, [Permissions.MUTE_MEMBERS])
) {
throw new Error(Errors.MISSING_MUTE_MEMBERS);
}
}
if (options.deaf && !botHasPermission(guildID, botID, [Permissions.DEAFEN_MEMBERS]))
throw new Error(Errors.MISSING_DEAFEN_MEMBERS)
if (
options.deaf &&
!botHasPermission(guildID, botID, [Permissions.DEAFEN_MEMBERS])
) {
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
return RequestManager.patch(endpoints.GUILD_MEMBER(guildID, data.user.id), options)
return RequestManager.patch(
endpoints.GUILD_MEMBER(guildID, data.user.id),
options,
);
},
/** Checks if the member has this permission. If the member is an owner or has admin perms it will always be true. */
hasPermissions: (permissions: Permission[]) => {
return memberHasPermission(data.user.id, ownerID, roleData, data.roles, permissions)
return memberHasPermission(
data.user.id,
ownerID,
roleData,
data.roles,
permissions,
);
},
})
});

View File

@@ -1,14 +1,14 @@
import { MessageCreateOptions } from "../types/message.ts"
import { endpoints } from "../constants/discord.ts"
import { MessageContent } from "../types/channel.ts"
import { createUser } from "./user.ts"
import { UserPayload } from "../types/guild.ts"
import { botHasPermission } from "../utils/permissions.ts"
import { Errors } from "../types/errors.ts"
import { Permissions } from "../types/permission.ts"
import { RequestManager } from "../module/requestManager.ts"
import { botID } from "../module/client.ts"
import { cache } from "../utils/cache.ts"
import { MessageCreateOptions } from "../types/message.ts";
import { endpoints } from "../constants/discord.ts";
import { MessageContent } from "../types/channel.ts";
import { createUser } from "./user.ts";
import { UserPayload } from "../types/guild.ts";
import { botHasPermission } from "../utils/permissions.ts";
import { Errors } from "../types/errors.ts";
import { Permissions } from "../types/permission.ts";
import { RequestManager } from "../module/requestManager.ts";
import { botID } from "../module/client.ts";
import { cache } from "../utils/cache.ts";
export function createMessage(data: MessageCreateOptions) {
return {
@@ -16,76 +16,139 @@ export function createMessage(data: MessageCreateOptions) {
raw: data,
author: createUser({ ...data.author, avatar: data.author.avatar || "" }),
timestamp: Date.parse(data.timestamp),
editedTimestamp: data.edited_timestamp ? Date.parse(data.edited_timestamp) : undefined,
editedTimestamp: data.edited_timestamp
? Date.parse(data.edited_timestamp)
: undefined,
channel: cache.channels.get(data.channel_id)!,
/** Delete a message */
delete: (reason?: string) => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
if (data.author.id !== botID) {
}
return RequestManager.delete(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), { reason })
return RequestManager.delete(
endpoints.CHANNEL_MESSAGE(data.channel_id, data.id),
{ reason },
);
},
/** Pin a message in a channel. Requires MANAGE_MESSAGES. Max pins allowed in a channel = 50. */
pin: () => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
RequestManager.put(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
RequestManager.put(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id));
},
unpin: () => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
RequestManager.delete(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
RequestManager.delete(
endpoints.CHANNEL_MESSAGE(data.channel_id, data.id),
);
},
/** 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 */
addReaction: (reaction: string) => {
RequestManager.put(endpoints.CHANNEL_MESSAGE_REACTION_ME(data.channel_id, data.id, reaction))
RequestManager.put(
endpoints.CHANNEL_MESSAGE_REACTION_ME(
data.channel_id,
data.id,
reaction,
),
);
},
/** Removes a reaction from the bot on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
removeReaction: (reaction: string) => {
RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTION_ME(data.channel_id, data.id, reaction))
RequestManager.delete(
endpoints.CHANNEL_MESSAGE_REACTION_ME(
data.channel_id,
data.id,
reaction,
),
);
},
/** Removes all reactions for all emojis on this message. */
removeAllReactions: () => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTIONS(data.channel_id, data.id))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
RequestManager.delete(
endpoints.CHANNEL_MESSAGE_REACTIONS(data.channel_id, data.id),
);
},
/** Removes all reactions for a single emoji on this message. Reaction takes the form of **name:id** for custom guild emoji, or Unicode characters. */
removeReactionEmoji: (reaction: string) => {
if (data.guild_id && !botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES]))
throw new Error(Errors.MISSING_MANAGE_MESSAGES)
RequestManager.delete(endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction))
if (
data.guild_id &&
!botHasPermission(data.guild_id, botID, [Permissions.MANAGE_MESSAGES])
) {
throw new Error(Errors.MISSING_MANAGE_MESSAGES);
}
RequestManager.delete(
endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction),
);
},
/** Get a list of users that reacted with this emoji. */
getReactions: async (reaction: string) => {
const result = (await RequestManager.get(
endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction)
)) as UserPayload[]
return result.map((res) => createUser(res))
endpoints.CHANNEL_MESSAGE_REACTION(data.channel_id, data.id, reaction),
)) as UserPayload[];
return result.map((res) => createUser(res));
},
/** Edit the message. */
edit: async (content: string | MessageContent) => {
if (data.author.id !== botID) throw "You can only edit a message that was sent by the bot."
if (typeof content === "string") content = { content }
if (data.guild_id) {
if (!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES]))
throw new Error(Errors.MISSING_SEND_MESSAGES)
if (content.tts && !botHasPermission(data.guild_id, botID, [Permissions.SEND_TTS_MESSAGES]))
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE)
if (
data.author.id !== botID
) {
throw "You can only edit a message that was sent by the bot.";
}
if (content.content && content.content.length > 2000) throw new Error(Errors.MESSAGE_MAX_LENGTH)
if (typeof content === "string") content = { content };
const result = await RequestManager.patch(endpoints.CHANNEL_MESSAGE(data.channel_id, data.id), content)
return createMessage(result)
if (data.guild_id) {
if (
!botHasPermission(data.guild_id, botID, [Permissions.SEND_MESSAGES])
) {
throw new Error(Errors.MISSING_SEND_MESSAGES);
}
if (
content.tts &&
!botHasPermission(
data.guild_id,
botID,
[Permissions.SEND_TTS_MESSAGES],
)
) {
throw new Error(Errors.MISSING_SEND_TTS_MESSAGE);
}
}
if (content.content && content.content.length > 2000) {
throw new Error(Errors.MESSAGE_MAX_LENGTH);
}
const result = await RequestManager.patch(
endpoints.CHANNEL_MESSAGE(data.channel_id, data.id),
content,
);
return createMessage(result);
},
}
};
}
export type Message = ReturnType<typeof createMessage>
export type Message = ReturnType<typeof createMessage>;

View File

@@ -1,4 +1,4 @@
import { RoleData } from "../types/role.ts"
import { RoleData } from "../types/role.ts";
export const createRole = (data: RoleData) => ({
...data,
@@ -6,6 +6,6 @@ export const createRole = (data: RoleData) => ({
raw: data,
/** The @ mention of the role in a string. */
mention: `<@&${data.id}>`,
})
});
export type Role = ReturnType<typeof createRole>
export type Role = ReturnType<typeof createRole>;

View File

@@ -1,7 +1,7 @@
import { formatImageURL } from "../utils/cdn.ts"
import { endpoints } from "../constants/discord.ts"
import { ImageSize, ImageFormats } from "../types/cdn.ts"
import { UserPayload } from "../types/guild.ts"
import { formatImageURL } from "../utils/cdn.ts";
import { endpoints } from "../constants/discord.ts";
import { ImageSize, ImageFormats } from "../types/cdn.ts";
import { UserPayload } from "../types/guild.ts";
export const enum PremiumType {
NitroClassic = 1,
@@ -14,8 +14,12 @@ export const createUser = (data: UserPayload) => ({
tag: `${data.username}#${data.discriminator}`,
avatarURL: (size: ImageSize = 128, format?: ImageFormats) =>
data.avatar
? formatImageURL(endpoints.USER_AVATAR(data.id, data.avatar), size, format)
? formatImageURL(
endpoints.USER_AVATAR(data.id, data.avatar),
size,
format,
)
: endpoints.USER_DEFAULT_AVATAR(Number(data.discriminator) % 5),
})
});
export type User = ReturnType<typeof createUser>
export type User = ReturnType<typeof createUser>;

View File

@@ -1,12 +1,12 @@
import { Timestamps } from "../types/discord.ts"
import { Timestamps } from "../types/discord.ts";
export interface ActivityPayload {
name: string
type: number
url?: string
created_at: number
timestamps: Timestamps
details?: string
name: string;
type: number;
url?: string;
created_at: number;
timestamps: Timestamps;
details?: string;
}
export enum ActivityType {

View File

@@ -1,2 +1,2 @@
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048
export type ImageFormats = "jpg" | "jpeg" | "png" | "webp" | "gif"
export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048;
export type ImageFormats = "jpg" | "jpeg" | "png" | "webp" | "gif";

View File

@@ -1,69 +1,69 @@
import { Raw_Overwrite, Overwrite } from "./guild.ts"
import { Embed } from "./message.ts"
import { Raw_Overwrite, Overwrite } from "./guild.ts";
import { Embed } from "./message.ts";
export interface ChannelEditOptions {
/** 2-100 character channel name. All */
name?: string
name?: string;
/** the position of the channel in the left-hand listing All */
position?: number
position?: number;
/** 0-1024 character channel topic. Text */
topic?: string
topic?: string;
/** whether the channel is nsfw Text */
nsfw?: boolean
nsfw?: boolean;
/** amount of seconds a user has to wait before sending another message (0-21600); bots, as well as users with the permission manage_messages or manage_channel, are unaffected Text */
rate_limit_per_user?: number
rate_limit_per_user?: number;
/** the bitrate (in bits) of the voice channel; 8000 to 96000 (128000 for VIP servers) Voice */
bitrate?: number
bitrate?: number;
/** the user limit of the voice channel; 0 refers to no limit, 1 to 99 refers to a user limit Voice */
user_limit?: number
user_limit?: number;
/** channel or category-specific permissions All */
permission_overwrites?: Overwrite[]
permission_overwrites?: Overwrite[];
/** id of the new parent category for a channel Text, Voice */
parent_id?: string
parent_id?: string;
}
export interface Base_Channel_Create {
/** The id of the guild */
guild_id?: string
guild_id?: string;
/** Sorting position of the channel */
position?: number
position?: number;
/** The name of the channel (2-100 characters) */
name?: string
name?: string;
/** The channel topic (0-1024 characters) */
topic?: string
topic?: string;
/** Whether the channel is nsfw */
nsfw?: boolean
nsfw?: boolean;
/** The id of the last message sent in this channel (may not point to an existing or valid message) */
last_message_id?: string | null
last_message_id?: string | null;
/** The bitrate (in bits) of the voice channel */
bitrate?: number
bitrate?: number;
/** The user limit of the voice channel */
user_limit?: number
user_limit?: number;
/** Amount of seconds a user has to wait before sending another message (0-21600) Bots and users with the permission MANAGE_MESSAGES or MANAGE_CHANNEL are unaffected. */
rate_limit_per_user?: number
rate_limit_per_user?: number;
/** The parent category id */
parent_id?: string | null
parent_id?: string | null;
/** When the last pinned message was pinned */
last_pin_timestamp?: string
last_pin_timestamp?: string;
}
export interface ChannelCreatePayload extends Base_Channel_Create {
/** The id of this channel */
id: string
id: string;
/** The type of the channel */
type: Channel_Type
type: Channel_Type;
/** Explicit permission overwrites for members and roles */
permission_overwrites?: Raw_Overwrite[]
permission_overwrites?: Raw_Overwrite[];
}
export interface CreateChannelOptions extends Base_Channel_Create {
/** The type of the channel */
type: ChannelTypes
type: ChannelTypes;
/** Explicit permission overwrites for members and roles */
permission_overwrites?: Overwrite[]
permission_overwrites?: Overwrite[];
}
export type Channel_Type = 0 | 1 | 2 | 4 | 5 | 6
export type Channel_Type = 0 | 1 | 2 | 4 | 5 | 6;
export enum ChannelTypes {
/** A text channel within a server */
@@ -88,47 +88,47 @@ export enum ChannelTypes {
export interface MessageContent {
/** The message contents, up to 2000 characters */
content?: string
content?: string;
/** A nonce that can be used for optimistic message sending. */
nonce?: number | string
nonce?: number | string;
/** Whether this is a TextToSpeech message */
tts?: boolean
tts?: boolean;
// file?: File_Content
/** The contents of the file being sent */
file?: unknown
file?: unknown;
/** Embed object */
embed?: Embed
embed?: Embed;
/** JSON encoded body of any additional request fields. */
payload_json?: string
payload_json?: string;
}
export interface GetMessages {
/** Max number of messages to return(1-100). Defaults to 50. */
limit?: number
limit?: number;
}
export interface GetMessagesAfter extends GetMessages {
/** Get messages after this message id */
after: string
after: string;
}
export interface GetMessagesBefore extends GetMessages {
/** Get messages before this message id */
before: string
before: string;
}
export interface GetMessagesAround extends GetMessages {
/** Get messages around this message id. */
around: string
around: string;
}
export interface CreateInviteOptions {
/** Duration of invite in seconds before expiry, or 0 for never. Defaults to 86400 (24 hours) */
max_age: number
max_age: number;
/** Max number of uses or 0 for unlimited. Default 0 */
max_uses: number
max_uses: number;
/** Whether this invite only grants temporary membership. */
temporary: boolean
temporary: boolean;
/** If true, don't try to reuse a similar invite (useful for creating many unique one time use invites.) */
unique: boolean
unique: boolean;
}

View File

@@ -1,37 +1,37 @@
import { Activity } from "./message.ts"
import { Client_Status_Payload } from "./presence.ts"
import { Partial_User } from "./guild.ts"
import { MemberCreatePayload } from "./member.ts"
import { Activity } from "./message.ts";
import { Client_Status_Payload } from "./presence.ts";
import { Partial_User } from "./guild.ts";
import { MemberCreatePayload } from "./member.ts";
export interface DiscordPayload {
/** OP code for the payload */
op: number
op: number;
/** The real event data. Any JSON value basically. */
d: unknown
d: unknown;
/** The sequence number, used for resuming sessions and heartbeats. ONLY for OPCode 0 */
s?: number
s?: number;
/** The event name for this payload. ONLY for OPCode 0 */
t?: string
t?: string;
}
export interface DiscordBotGatewayData {
/** The WSS URL that can be used for connecting to the gateway. */
url: string
url: string;
/** The recommended number of shards to use when connecting. */
shards: number
shards: number;
/** Info on the current start limit. */
session_start_limit: {
/** The total number of session starts the current user is allowed. */
total: number
total: number;
/** The remaining number of session starts the current user is allowed. */
remaining: number
remaining: number;
/** Milliseconds left until limit is reset. */
reset_after: number
}
reset_after: number;
};
}
export interface DiscordHeartbeatPayload {
heartbeat_interval: number
heartbeat_interval: number;
}
export enum GatewayOpcode {
@@ -161,20 +161,20 @@ export enum JSONErrorCode {
}
export interface Properties {
$os: string
$browser: string
$device: string
$os: string;
$browser: string;
$device: string;
}
export interface Timestamps {
start?: number
end?: number
start?: number;
end?: number;
}
export interface Emoji {
name: string
id?: string
animated?: boolean
name: string;
id?: string;
animated?: boolean;
}
export enum StatusType {
@@ -185,80 +185,80 @@ export enum StatusType {
Offline = "offline",
}
export type Status_Type = "online" | "dnd" | "idle" | "invisible" | "offline"
export type Status_Type = "online" | "dnd" | "idle" | "invisible" | "offline";
export interface Status {
afk: boolean
status: StatusType
afk: boolean;
status: StatusType;
}
export interface WebhookUpdatePayload {
channel_id: string
guild_id: string
channel_id: string;
guild_id: string;
}
export interface PresenceUpdatePayload {
/** The user presence is being updated for. */
user: Partial_User
user: Partial_User;
/** The roles this user is in */
roles: string[]
roles: string[];
/** null, or the user's current activity */
game: Activity | null
game: Activity | null;
/** The id of the guild */
guild_id: string
guild_id: string;
/** Either idle, dnd, online, or offline */
status: Status_Type
status: Status_Type;
/** All user's current activity */
activities: Activity[]
activities: Activity[];
/** The user's platform dependent status */
client_status: Client_Status_Payload
client_status: Client_Status_Payload;
/** When the user has started boosting the guild */
premium_since?: string | null
premium_since?: string | null;
/** This users guild nickname (if one is set) */
nick?: string | null
nick?: string | null;
}
export interface TypingStartPayload {
/** The id of the channel */
channel_id: string
channel_id: string;
/** The id of the guild */
guild_id?: string
guild_id?: string;
/** The id of the user */
user_id: string
user_id: string;
/** The unix time in seconds of when the user started typing */
timestamp: number
timestamp: number;
/** The member who started typing if this happened in a guild */
member?: MemberCreatePayload
member?: MemberCreatePayload;
}
export interface VoiceStateUpdatePayload {
/** The guild id this voice state is for */
guild_id?: string
guild_id?: string;
/** The channel id this user is connected to */
channel_id: string | null
channel_id: string | null;
/** The user id this voice state is for */
user_id: string
user_id: string;
/** The guild member this voice state is for */
member?: MemberCreatePayload
member?: MemberCreatePayload;
/** The session id for this voice state */
session_id: string
session_id: string;
/** Whether this user is deafened by the server */
deaf: boolean
deaf: boolean;
/** Whether this user is muted by the server */
mute: boolean
mute: boolean;
/** Whether this user is locally deafened */
self_deaf: boolean
self_deaf: boolean;
/** Whether this user is locally muted */
self_mute: boolean
self_mute: boolean;
/** Whether this user is streaming using Go Live */
self_stream?: boolean
self_stream?: boolean;
/** Whether this user is muted by the bot */
suppress: boolean
suppress: boolean;
}
export interface ReadyPayload {
/** used for resuming connections */
session_id: string
session_id: string;
/** (shard_id, num_shards) the shard information associated with this session, if sent when identifying */
shard?: [number, number]
shard?: [number, number];
}

View File

@@ -20,5 +20,5 @@ export enum Errors {
DELETE_MESSAGES_MAX = "DELETE_MESSAGES_MAX",
MESSAGE_MAX_LENGTH = "MESSAGE_MAX_LENGTH",
NICKNAMES_MAX_LENGTH = "NICKNAMES_MAX_LENGTH",
PRUNE_MIN_DAYS = "PRUNE_MIN_DAYS"
PRUNE_MIN_DAYS = "PRUNE_MIN_DAYS",
}

View File

@@ -1,8 +1,8 @@
export const enum RequestMethod {
Get = 'get',
Post = 'post',
Put = 'put',
Patch = 'patch',
Head = 'head',
Delete = 'delete'
}
Get = "get",
Post = "post",
Put = "put",
Patch = "patch",
Head = "head",
Delete = "delete",
}

View File

@@ -1,34 +1,34 @@
import { createMember } from "../structures/member.ts"
import { UserPayload } from "./guild.ts"
import { createMember } from "../structures/member.ts";
import { UserPayload } from "./guild.ts";
export interface EditMemberOptions {
/** Value to set users nickname to. Requires MANAGE_NICKNAMES permission. */
nick?: string
nick?: string;
/** Array of role ids the member is assigned. Requires MANAGE_ROLES permission. */
roles?: string[]
roles?: string[];
/** Whether the user is muted in voice channels. Requires MUTE_MEMBERS permission. */
mute?: boolean
mute?: boolean;
/** Whether the user is deafened in voice channels. Requires DEAFEN_MEMBERS permission. */
deaf?: boolean
deaf?: boolean;
/** The id of the channel to move user to if they are connected to voice. To kick the user from their current channel, set to null. Requires MOVE_MEMBERS permission. When moving members to channels, must have permissions to both CONNECT to the channel and have the MOVE_MEMBER permission. */
channel_id?: string | null
channel_id?: string | null;
}
export interface MemberCreatePayload {
/** The user this guild member represents */
user: UserPayload
user: UserPayload;
/** The user's guild nickname if one is set. */
nick?: string
nick?: string;
/** Array of role ids that the member has */
roles: string[]
roles: string[];
/** When the user joined the guild. */
joined_at: string
joined_at: string;
/** When the user used their nitro boost on the server. */
premium_since?: string
premium_since?: string;
/** Whether the user is deafened in voice channels */
deaf: boolean
deaf: boolean;
/** Whether the user is muted in voice channels */
mute: boolean
mute: boolean;
}
export type Member = ReturnType<typeof createMember>
export type Member = ReturnType<typeof createMember>;

View File

@@ -1,143 +1,143 @@
import { ChannelType, UserPayload } from "./guild.ts"
import { User } from "../structures/user.ts"
import { Member, MemberCreatePayload } from "./member.ts"
import { Channel } from "../structures/channel.ts"
import { ChannelType, UserPayload } from "./guild.ts";
import { User } from "../structures/user.ts";
import { Member, MemberCreatePayload } from "./member.ts";
import { Channel } from "../structures/channel.ts";
export interface MentionedUser extends User {
member: Member
member: Member;
}
export interface Mentioned_Channel {
/** The id of the channel */
id: string
id: string;
/** The id of the guild containing the channel */
guild_id: string
guild_id: string;
/** The type of the channel. */
type: ChannelType
type: ChannelType;
/** The name of the channel. */
name: string
name: string;
}
export interface Attachment {
/** Attachment id */
id: string
id: string;
/** The name of the file attached */
filename: string
filename: string;
/** The size of file in bytes */
size: number
size: number;
/** Source url of file */
url: string
url: string;
/** A proxied url of file */
proxy_url: string
proxy_url: string;
/** The height of file if an image */
height: number | null
height: number | null;
/** The width of the file if an image */
width: number | null
width: number | null;
}
export interface Embed {
/** The title of the embed */
title?: string
title?: string;
/** The type of embed (always rich for webhook embeds) */
type?: string
type?: string;
/** The description of embeds */
description?: string
description?: string;
/** The url of embed */
url?: string
url?: string;
/** The timestap of the embed content */
timestamp?: string
timestamp?: string;
/** The color code of the embed */
color?: number
color?: number;
/** The footer information */
footer?: Embed_Footer
footer?: Embed_Footer;
/** The image information */
image?: Embed_Image
image?: Embed_Image;
/** The thumbnail information */
thumbnail?: Embed_Thumbnail
thumbnail?: Embed_Thumbnail;
/** The video information */
video?: Embed_Video
video?: Embed_Video;
/** Provider information */
provider?: Embed_Provider
provider?: Embed_Provider;
/** Author information */
author?: Embed_Author
author?: Embed_Author;
/** Fields information */
fields?: Embed_Field[]
fields?: Embed_Field[];
}
export interface Embed_Footer {
/** The text of the footer */
text: string
text: string;
/** The url of the footer icon. Only supports http(s) and attachments */
iconURL?: string
iconURL?: string;
/** A proxied url of footer icon */
proxy_icon_url?: string
proxy_icon_url?: string;
}
export interface Embed_Image {
/** The source url of image (only supports http(s) and attachments) */
url?: string
url?: string;
/** A proxied url of the image */
proxy_url?: string
proxy_url?: string;
/** The height of image */
height?: number
height?: number;
/** The width of the image */
width?: number
width?: number;
}
export interface Embed_Thumbnail {
/** The source url of image (only supports http(s) and attachments) */
url?: string
url?: string;
/** A proxied url of the thumbnail */
proxy_url?: string
proxy_url?: string;
/** The height of the thumbnail */
height?: number
height?: number;
/** The width of the thumbnail */
width?: number
width?: number;
}
export interface Embed_Video {
/** The source url of video */
url?: string
url?: string;
/** The height of the video */
height?: number
height?: number;
/** The width of the video */
width?: number
width?: number;
}
export interface Embed_Provider {
/** The name of the provider */
name?: string
name?: string;
/** The url of the provider */
url?: string
url?: string;
}
export interface Embed_Author {
/** The name of the author */
name?: string
name?: string;
/** The url of the author */
url?: string
url?: string;
/** The url of the author icon (supports http(s) and attachments) */
iconURL?: string
iconURL?: string;
/** A proxied url of author icon */
proxy_icon_url?: string
proxy_icon_url?: string;
}
export interface Embed_Field {
/** The name of the field */
name: string
name: string;
/** The value of the field */
value: string
value: string;
/** Whether or not this field should display inline */
inline?: boolean
inline?: boolean;
}
export interface Reaction {
/** The times this emoji has been used to react */
count: number
count: number;
/** Whether the current user reacted using this emoji */
me: boolean
me: boolean;
/** The emoji information. Can be partial. */
emoji: Emoji
emoji: Emoji;
}
export enum Message_Types {
@@ -165,31 +165,31 @@ export enum Activity_Types {
export interface Activity {
/** The type of message activity */
type: 1 | 2 | 3 | 5
type: 1 | 2 | 3 | 5;
/** The party id from a rich presence event */
party_id?: string
party_id?: string;
}
export interface Application {
/** The id of the application */
id: string
id: string;
/** The id of the embed's image asset */
cover_image?: string
cover_image?: string;
/** The application's description */
description: string
description: string;
/** The id of the application's icon */
icon: string | null
icon: string | null;
/** The name of the application */
name: string
name: string;
}
export interface Reference {
/** The id of the originating message */
message_id?: string
message_id?: string;
/** The id of the originating message's channel */
channel_id: string
channel_id: string;
/** The id of the originating message's guild */
guild_id?: string
guild_id?: string;
}
export enum Message_Flags {
@@ -202,129 +202,130 @@ export enum Message_Flags {
export interface Emoji {
/** The emoji id. */
id?: string
id?: string;
/** The emoji name. Null in reaction emoji object if emoji is no longer on the server */
name: string | null
name: string | null;
/** The roles this emoji is whitelisted to */
roles?: string[]
roles?: string[];
/** The user that created this emoji */
user?: User
user?: User;
/** Whether this emoji must be wrapped in colons */
require_colons?: boolean
require_colons?: boolean;
/** Whether this emoji is managed */
managed?: boolean
managed?: boolean;
/** Whether this emoji is animated */
animated?: boolean
animated?: boolean;
}
export interface Reaction_Payload {
/** The id of the reaction. Null usually if it is a default discord emoji. */
id: string | null
id: string | null;
/** The name of the reaction. Null if it was deleted from the guild and the custom data is no longer available */
name: string | null
name: string | null;
/** If the reaction is an animated emoji. */
animated?: boolean
animated?: boolean;
}
export interface MessageCreateOptions {
/** The id of the message */
id: string
id: string;
/** The id of the channel the message was sent in */
channel_id: string
channel_id: string;
/** The id of the guild the message was sent in */
guild_id?: string
guild_id?: string;
/** The author of this message (not guaranteed to be a valid user such as a webhook.) */
author: UserPayload
author: UserPayload;
/** The member properties for this message's author. Can be partial. */
member?: Member
member?: Member;
/** The contents of the message */
content: string
content: string;
/** When this message was sent */
timestamp: string
timestamp: string;
/** When this message was edited (if it was not edited, null) */
edited_timestamp: string | null
edited_timestamp: string | null;
/** Whether this was a TextToSpeech message. */
tts: boolean
tts: boolean;
/** Whether this message mentions everyone */
mentions_everyone: boolean
mentions_everyone: boolean;
/** Users specifically mentioned in the message. */
mentions: MentionedUser[]
mentions: MentionedUser[];
/** Roles specifically mentioned in this message */
mention_roles: string[]
mention_roles: string[];
/** Channels specifically mentioned in this message */
mention_channels?: Mentioned_Channel[]
mention_channels?: Mentioned_Channel[];
/** Any attached files */
attachments: Attachment[]
attachments: Attachment[];
/** Any embedded content */
embeds: Embed[]
embeds: Embed[];
/** Reactions to the message */
reactions?: Reaction[]
reactions?: Reaction[];
/** Used for validating a message was sent */
nonce?: number | string
nonce?: number | string;
/** Whether this message is pinned */
pinned: boolean
pinned: boolean;
/** If the message is generated by a webhook, this is the webhooks id */
webhook_id?: string
webhook_id?: string;
/** The type of message */
type: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
type: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
/** The activities sent with Rich Presence-related chat embeds. */
activity?: Activity
activity?: Activity;
/** Applications that sent with Rich Presence related chat embeds. */
applications?: Application
applications?: Application;
/** The reference data sent with crossposted messages */
message_reference?: Reference
message_reference?: Reference;
/** The message flags combined like permission bits describe extra features of the message */
flags?: 1 | 2 | 4 | 8 | 16
flags?: 1 | 2 | 4 | 8 | 16;
}
export interface Base_Message_Delete_Payload {
/** The id of the channel */
channel_id: string
channel_id: string;
/** The id of the guild */
guild_id?: string
guild_id?: string;
}
export interface MessageDeletePayload extends Base_Message_Delete_Payload {
/** The id of the message */
id: string
id: string;
}
export interface MessageDeleteBulkPayload extends Base_Message_Delete_Payload {
/** The ids of the messages */
ids: string[]
ids: string[];
}
export interface MessageUpdatePayload {
/** The message id */
id: string
id: string;
/** The channel id */
channel_id: string
channel_id: string;
}
export interface BaseMessageReactionPayload {
/** The id of the channel */
channel_id: string
channel_id: string;
/** The id of the message */
message_id: string
message_id: string;
/** The id of the guild */
guild_id?: string
guild_id?: string;
}
export interface MessageReactionPayload extends BaseMessageReactionPayload {
/** The id of the user */
user_id: string
user_id: string;
/** The member who reacted if this happened in a guild. Not available for MESSAGE_REACTION_REMOVE */
member?: MemberCreatePayload
member?: MemberCreatePayload;
/** The emoji used to react */
emoji: Reaction_Payload
emoji: Reaction_Payload;
}
export interface MessageReactionRemoveEmojiPayload extends BaseMessageReactionPayload {
export interface MessageReactionRemoveEmojiPayload
extends BaseMessageReactionPayload {
/** The emoji that was removed. */
emoji: Reaction_Payload
emoji: Reaction_Payload;
}
export interface Partial_Message {
id: string
channel: Channel
id: string;
channel: Channel;
}

View File

@@ -5,73 +5,101 @@ import {
PresenceUpdatePayload,
TypingStartPayload,
VoiceStateUpdatePayload,
} from "./discord.ts"
import { User } from "../structures/user.ts"
import { Member } from "./member.ts"
import { Role } from "../structures/role.ts"
import { Message } from "../structures/message.ts"
} from "./discord.ts";
import { User } from "../structures/user.ts";
import { Member } from "./member.ts";
import { Role } from "../structures/role.ts";
import { Message } from "../structures/message.ts";
import {
Partial_Message,
MessageReactionPayload,
Reaction_Payload,
BaseMessageReactionPayload,
MessageReactionRemoveEmojiPayload,
} from "./message.ts"
import { Channel } from "../structures/channel.ts"
import { Guild } from "../structures/guild.ts"
} from "./message.ts";
import { Channel } from "../structures/channel.ts";
import { Guild } from "../structures/guild.ts";
export interface Fulfilled_Client_Options {
token: string
properties: Properties
compress: boolean
intents: number
token: string;
properties: Properties;
compress: boolean;
intents: number;
}
export interface ClientOptions {
token: string
properties?: Properties
compress?: boolean
botID: string
intents: Intents[]
eventHandlers?: EventHandlers
token: string;
properties?: Properties;
compress?: boolean;
botID: string;
intents: Intents[];
eventHandlers?: EventHandlers;
}
export interface EventHandlers {
botUpdate?: (user: User, cachedUser?: User) => unknown
channelCreate?: (channel: Channel) => unknown
channel_update?: (channel: Channel, cachedChannel: Channel) => unknown
channelDelete?: (channel: Channel) => unknown
guildBanAdd?: (guild: Guild, user: User) => unknown
guildBanRemove?: (guild: Guild, user: User) => unknown
guildCreate?: (guild: Guild) => unknown
guildUpdate?: (guild: Guild, cachedGuild: Guild) => unknown
guildDelete?: (guild: Guild) => unknown
guildEmojisUpdate?: (guild: Guild, emojis: Emoji[], cachedEmojis: Emoji[]) => unknown
guildMemberAdd?: (guild: Guild, member: Member) => unknown
guildMemberRemove?: (guild: Guild, member: Member | User) => unknown
guild_member_update?: (guild: Guild, member: Member, cachedMember?: Member) => unknown
heartbeat?: () => unknown
messageCreate?: (message: Message) => unknown
message_delete?: (message: Message | Partial_Message) => unknown
nicknameUpdate?: (guild: Guild, member: Member, nickname: string, old_nickname?: string) => unknown
presenceUpdate?: (data: PresenceUpdatePayload) => unknown
raw?: (data: DiscordPayload) => unknown
ready?: () => unknown
reactionAdd?: (message: Message | MessageReactionPayload, emoji: Reaction_Payload, user_id: string) => unknown
reactionRemove?: (message: Message | MessageReactionPayload, emoji: Reaction_Payload, user_id: string) => unknown
reactionRemoveAll?: (data: BaseMessageReactionPayload) => unknown
reactionRemoveEmoji?: (data: MessageReactionRemoveEmojiPayload) => unknown
roleCreate?: (guild: Guild, role: Role) => unknown
roleDelete?: (guild: Guild, role: Role) => unknown
roleUpdate?: (guild: Guild, role: Role, cached_role: Role) => unknown
role_gained?: (guild: Guild, member: Member, role_id: string) => unknown
role_lost?: (guild: Guild, member: Member, role_id: string) => unknown
typingStart?: (data: TypingStartPayload) => unknown
voiceChannelJoin?: (member: Member, channel_id: string) => unknown
voiceChannelLeave?: (member: Member, channel_id: string) => unknown
voiceChannelSwitch?: (member: Member, channel_id: string, old_channel_id: string) => unknown
voiceStateUpdate?: (member: Member, voice_state: VoiceStateUpdatePayload) => unknown
webhooksUpdate?: (channel_id: string, guild_id: string) => unknown
botUpdate?: (user: User, cachedUser?: User) => unknown;
channelCreate?: (channel: Channel) => unknown;
channel_update?: (channel: Channel, cachedChannel: Channel) => unknown;
channelDelete?: (channel: Channel) => unknown;
guildBanAdd?: (guild: Guild, user: User) => unknown;
guildBanRemove?: (guild: Guild, user: User) => unknown;
guildCreate?: (guild: Guild) => unknown;
guildUpdate?: (guild: Guild, cachedGuild: Guild) => unknown;
guildDelete?: (guild: Guild) => unknown;
guildEmojisUpdate?: (
guild: Guild,
emojis: Emoji[],
cachedEmojis: Emoji[],
) => unknown;
guildMemberAdd?: (guild: Guild, member: Member) => unknown;
guildMemberRemove?: (guild: Guild, member: Member | User) => unknown;
guild_member_update?: (
guild: Guild,
member: Member,
cachedMember?: Member,
) => unknown;
heartbeat?: () => unknown;
messageCreate?: (message: Message) => unknown;
message_delete?: (message: Message | Partial_Message) => unknown;
nicknameUpdate?: (
guild: Guild,
member: Member,
nickname: string,
old_nickname?: string,
) => unknown;
presenceUpdate?: (data: PresenceUpdatePayload) => unknown;
raw?: (data: DiscordPayload) => unknown;
ready?: () => unknown;
reactionAdd?: (
message: Message | MessageReactionPayload,
emoji: Reaction_Payload,
user_id: string,
) => unknown;
reactionRemove?: (
message: Message | MessageReactionPayload,
emoji: Reaction_Payload,
user_id: string,
) => unknown;
reactionRemoveAll?: (data: BaseMessageReactionPayload) => unknown;
reactionRemoveEmoji?: (data: MessageReactionRemoveEmojiPayload) => unknown;
roleCreate?: (guild: Guild, role: Role) => unknown;
roleDelete?: (guild: Guild, role: Role) => unknown;
roleUpdate?: (guild: Guild, role: Role, cached_role: Role) => unknown;
role_gained?: (guild: Guild, member: Member, role_id: string) => unknown;
role_lost?: (guild: Guild, member: Member, role_id: string) => unknown;
typingStart?: (data: TypingStartPayload) => unknown;
voiceChannelJoin?: (member: Member, channel_id: string) => unknown;
voiceChannelLeave?: (member: Member, channel_id: string) => unknown;
voiceChannelSwitch?: (
member: Member,
channel_id: string,
old_channel_id: string,
) => unknown;
voiceStateUpdate?: (
member: Member,
voice_state: VoiceStateUpdatePayload,
) => unknown;
webhooksUpdate?: (channel_id: string, guild_id: string) => unknown;
}
export enum Intents {

View File

@@ -1,12 +1,12 @@
import { StatusType } from "../types/discord.ts"
import { StatusType } from "../types/discord.ts";
export interface Client_Status_Payload {
/** The user's status set for an active desktop (Windows, Linux, Mac) application session */
desktop?: StatusType
desktop?: StatusType;
/** The user's status set for an active mobile (iOS, Android) application session */
mobile?: StatusType
mobile?: StatusType;
/** The user's status set for an active web (browser, bot account) application session */
web?: StatusType
web?: StatusType;
}

View File

@@ -1,18 +1,18 @@
export interface RoleData {
/** role id */
id: string
id: string;
/** role name */
name: string
name: string;
/** integer representation of hexadecimal color code */
color: number
color: number;
/** if this role is pinned in the user listing */
hoist: boolean
hoist: boolean;
/** position of this role */
position: number
position: number;
/** permission bit set */
permissions: number
permissions: number;
/** whether this role is managed by an integration */
managed: boolean
managed: boolean;
/** whether this role is mentionable */
mentionable: boolean
mentionable: boolean;
}

View File

@@ -1,14 +1,14 @@
import { User } from "../structures/user.ts"
import { Message } from "../structures/message.ts"
import { Guild } from "../structures/guild.ts"
import { Channel } from "../structures/channel.ts"
import { User } from "../structures/user.ts";
import { Message } from "../structures/message.ts";
import { Guild } from "../structures/guild.ts";
import { Channel } from "../structures/channel.ts";
export interface CacheData {
guilds: Map<string, Guild>
users: Map<string, User>
channels: Map<string, Channel>
messages: Map<string, Message>
unavailableGuilds: Map<string, number>
guilds: Map<string, Guild>;
users: Map<string, User>;
channels: Map<string, Channel>;
messages: Map<string, Message>;
unavailableGuilds: Map<string, number>;
}
export const cache: CacheData = {
@@ -17,4 +17,4 @@ export const cache: CacheData = {
channels: new Map(),
messages: new Map(),
unavailableGuilds: new Map(),
}
};

View File

@@ -1,5 +1,11 @@
import { ImageSize, ImageFormats } from "../types/cdn.ts"
import { ImageSize, ImageFormats } from "../types/cdn.ts";
export const formatImageURL = (url: string, size: ImageSize = 128, format?: ImageFormats) => {
return `${url}.${format || url.includes("/a_") ? "gif" : "jpg"}/?size=${size}`
}
export const formatImageURL = (
url: string,
size: ImageSize = 128,
format?: ImageFormats,
) => {
return `${url}.${
format || url.includes("/a_") ? "gif" : "jpg"
}/?size=${size}`;
};

View File

@@ -1,52 +1,58 @@
import { Permission, Permissions } from "../types/permission.ts"
import { RoleData } from "../types/role.ts"
import { cache } from "./cache.ts"
import { Permission, Permissions } from "../types/permission.ts";
import { RoleData } from "../types/role.ts";
import { cache } from "./cache.ts";
export const memberHasPermission = (
member_id: string,
owner_id: string,
role_data: RoleData[],
member_role_ids: string[],
permissions: Permission[]
permissions: Permission[],
) => {
if (member_id === owner_id) return true
if (member_id === owner_id) return true;
const permissionBits = role_data
.filter((role) => member_role_ids.includes(role.id))
.reduce((bits, data) => {
bits |= data.permissions
bits |= data.permissions;
return bits
}, 0)
return bits;
}, 0);
if (permissionBits & Permissions.ADMINISTRATOR) return true
if (permissionBits & Permissions.ADMINISTRATOR) return true;
return permissions.every((permission) => permissionBits & Permissions[permission])
}
return permissions.every((permission) =>
permissionBits & Permissions[permission]
);
};
export const botHasPermission = (guild_id: string, botID: string, permissions: Permissions[]) => {
const guild = cache.guilds.get(guild_id)
if (!guild) return false
export const botHasPermission = (
guild_id: string,
botID: string,
permissions: Permissions[],
) => {
const guild = cache.guilds.get(guild_id);
if (!guild) return false;
const member = guild.members.get(botID)
if (!member) return false
const member = guild.members.get(botID);
if (!member) return false;
const permissionBits = [...guild.roles.values()]
.map((role) => role.raw)
.filter((role) => member.roles.includes(role.id))
.reduce((bits, data) => {
bits |= data.permissions
bits |= data.permissions;
return bits
}, 0)
return bits;
}, 0);
if (permissionBits & Permissions.ADMINISTRATOR) return true
if (permissionBits & Permissions.ADMINISTRATOR) return true;
return permissions.every((permission) => permissionBits & permission)
}
return permissions.every((permission) => permissionBits & permission);
};
export const calculatePermissions = (permission_bits: number) => {
return Object.keys(Permissions).filter((perm) => {
return permission_bits & Permissions[perm as Permission]
})
}
return permission_bits & Permissions[perm as Permission];
});
};

View File

@@ -1,3 +1,3 @@
export const sleep = (timeout: number) => {
return new Promise(resolve => setTimeout(resolve, timeout))
}
return new Promise((resolve) => setTimeout(resolve, timeout));
};