From d1d0a1ce76458cb974c4df6d91325e04f9d26f39 Mon Sep 17 00:00:00 2001 From: Yuzu Date: Sat, 2 Jul 2022 12:15:47 -0500 Subject: [PATCH] refactor: structures & folders --- handlers/Actions.ts | 12 +- mod.ts | 28 +-- structures/AnonymousGuild.ts | 55 ------ structures/BaseChannel.ts | 51 ----- structures/BaseGuild.ts | 41 ---- structures/ChannelFactory.ts | 39 ---- structures/DMChannel.ts | 40 ---- structures/Guild.ts | 367 ----------------------------------- structures/GuildChannel.ts | 70 ------- structures/GuildEmoji.ts | 4 +- structures/Interaction.ts | 145 -------------- structures/Invite.ts | 4 +- structures/InviteGuild.ts | 19 -- structures/Member.ts | 4 +- structures/NewsChannel.ts | 31 --- structures/Role.ts | 2 +- structures/TextChannel.ts | 215 -------------------- structures/ThreadChannel.ts | 35 ---- structures/VoiceChannel.ts | 60 ------ 19 files changed, 29 insertions(+), 1193 deletions(-) delete mode 100644 structures/AnonymousGuild.ts delete mode 100644 structures/BaseChannel.ts delete mode 100644 structures/BaseGuild.ts delete mode 100644 structures/ChannelFactory.ts delete mode 100644 structures/DMChannel.ts delete mode 100644 structures/Guild.ts delete mode 100644 structures/GuildChannel.ts delete mode 100644 structures/Interaction.ts delete mode 100644 structures/InviteGuild.ts delete mode 100644 structures/NewsChannel.ts delete mode 100644 structures/TextChannel.ts delete mode 100644 structures/ThreadChannel.ts delete mode 100644 structures/VoiceChannel.ts diff --git a/handlers/Actions.ts b/handlers/Actions.ts index 2d44512..5975a4b 100644 --- a/handlers/Actions.ts +++ b/handlers/Actions.ts @@ -15,14 +15,14 @@ import type { } from "../vendor/external.ts"; import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; -import type { Channel } from "../structures/BaseChannel.ts"; -import ChannelFactory from "../structures/ChannelFactory.ts"; -import GuildChannel from "../structures/GuildChannel.ts"; -import ThreadChannel from "../structures/ThreadChannel.ts"; +import type { Channel } from "../structures/channels/ChannelFactory.ts"; +import ChannelFactory from "../structures/channels/ChannelFactory.ts"; +import GuildChannel from "../structures/channels/GuildChannel.ts"; +import ThreadChannel from "../structures/channels/ThreadChannel.ts"; import Member from "../structures/Member.ts"; import Message from "../structures/Message.ts"; import User from "../structures/User.ts"; -import Interaction from "../structures/Interaction.ts"; +import Interaction from "../structures/interactions/Interaction.ts"; export type RawHandler = (...args: [Session, number, T]) => void; export type Handler = (...args: T) => unknown; @@ -103,8 +103,8 @@ export const THREAD_LIST_SYNC: RawHandler = (session, _sh guildId: payload.guild_id, channelIds: payload.channel_ids ?? [], threads: payload.threads.map((channel) => new ThreadChannel(session, channel, payload.guild_id)), - // @ts-ignore: TODO: thread member structure members: payload.members.map((member) => + // @ts-ignore: TODO: thread member structure new Member(session, member as DiscordMemberWithUser, payload.guild_id) ), }); diff --git a/mod.ts b/mod.ts index 7262d9a..f7d725a 100644 --- a/mod.ts +++ b/mod.ts @@ -1,29 +1,26 @@ -export * from "./structures/AnonymousGuild.ts"; export * from "./structures/Attachment.ts"; export * from "./structures/Base.ts"; -export * from "./structures/BaseGuild.ts"; -export * from "./structures/BaseChannel.ts"; -export * from "./structures/DMChannel.ts"; export * from "./structures/Embed.ts"; export * from "./structures/Emoji.ts"; -export * from "./structures/Guild.ts"; -export * from "./structures/GuildChannel.ts"; export * from "./structures/GuildEmoji.ts"; -export * from "./structures/Interaction.ts"; export * from "./structures/Invite.ts"; -export * from "./structures/InviteGuild.ts"; export * from "./structures/Member.ts"; export * from "./structures/Message.ts"; -export * from "./structures/NewsChannel.ts"; export * from "./structures/Permissions.ts"; export * from "./structures/Role.ts"; -export * from "./structures/TextChannel.ts"; -export * from "./structures/ThreadChannel.ts"; export * from "./structures/User.ts"; -export * from "./structures/VoiceChannel.ts"; export * from "./structures/WelcomeChannel.ts"; export * from "./structures/WelcomeScreen.ts"; +export * from "./structures/channels/BaseChannel.ts"; +export * from "./structures/channels/ChannelFactory.ts"; +export * from "./structures/channels/DMChannel.ts"; +export * from "./structures/channels/GuildChannel.ts"; +export * from "./structures/channels/NewsChannel.ts"; +export * from "./structures/channels/TextChannel.ts"; +export * from "./structures/channels/ThreadChannel.ts"; +export * from "./structures/channels/VoiceChannel.ts"; + export * from "./structures/components/ActionRowComponent.ts"; export * from "./structures/components/ButtonComponent.ts"; export * from "./structures/components/Component.ts"; @@ -31,6 +28,13 @@ export * from "./structures/components/LinkButtonComponent.ts"; export * from "./structures/components/SelectMenuComponent.ts"; export * from "./structures/components/TextInputComponent.ts"; +export * from "./structures/guilds/AnonymousGuild.ts"; +export * from "./structures/guilds/BaseGuild.ts"; +export * from "./structures/guilds/Guild.ts"; +export * from "./structures/guilds/InviteGuild.ts"; + +export * from "./structures/interactions/Interaction.ts"; + export * from "./session/Session.ts"; export * from "./util/shared/flags.ts"; diff --git a/structures/AnonymousGuild.ts b/structures/AnonymousGuild.ts deleted file mode 100644 index fa86532..0000000 --- a/structures/AnonymousGuild.ts +++ /dev/null @@ -1,55 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Session } from "../session/Session.ts"; -import type { DiscordGuild, GuildNsfwLevel, VerificationLevels } from "../vendor/external.ts"; -import type { ImageFormat, ImageSize } from "../util/shared/images.ts"; -import { iconBigintToHash, iconHashToBigInt } from "../util/hash.ts"; -import { formatImageUrl } from "../util/shared/images.ts"; -import BaseGuild from "./BaseGuild.ts"; -import * as Routes from "../util/Routes.ts"; - -export class AnonymousGuild extends BaseGuild implements Model { - constructor(session: Session, data: Partial); // TODO: Improve this type (name and id are required) - constructor(session: Session, data: DiscordGuild) { - super(session, data); - - this.splashHash = data.splash ? iconHashToBigInt(data.splash) : undefined; - this.bannerHash = data.banner ? iconHashToBigInt(data.banner) : undefined; - - this.verificationLevel = data.verification_level; - this.vanityUrlCode = data.vanity_url_code ? data.vanity_url_code : undefined; - this.nsfwLevel = data.nsfw_level; - this.description = data.description ? data.description : undefined; - this.premiumSubscriptionCount = data.premium_subscription_count; - } - - splashHash?: bigint; - bannerHash?: bigint; - - verificationLevel: VerificationLevels; - vanityUrlCode?: string; - nsfwLevel: GuildNsfwLevel; - description?: string; - premiumSubscriptionCount?: number; - - splashUrl(options: { size?: ImageSize; format?: ImageFormat } = { size: 128 }) { - if (this.splashHash) { - return formatImageUrl( - Routes.GUILD_SPLASH(this.id, iconBigintToHash(this.splashHash)), - options.size, - options.format, - ); - } - } - - bannerUrl(options: { size?: ImageSize; format?: ImageFormat } = { size: 128 }) { - if (this.bannerHash) { - return formatImageUrl( - Routes.GUILD_BANNER(this.id, iconBigintToHash(this.bannerHash)), - options.size, - options.format, - ); - } - } -} - -export default AnonymousGuild; diff --git a/structures/BaseChannel.ts b/structures/BaseChannel.ts deleted file mode 100644 index 602f62c..0000000 --- a/structures/BaseChannel.ts +++ /dev/null @@ -1,51 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { DiscordChannel } from "../vendor/external.ts"; -import type TextChannel from "./TextChannel.ts"; -import type VoiceChannel from "./VoiceChannel.ts"; -import type DMChannel from "./DMChannel.ts"; -import type NewsChannel from "./NewsChannel.ts"; -import type ThreadChannel from "./ThreadChannel.ts"; -import { ChannelTypes } from "../vendor/external.ts"; -import { textBasedChannels } from "./TextChannel.ts"; - -export abstract class BaseChannel implements Model { - constructor(session: Session, data: DiscordChannel) { - this.id = data.id; - this.session = session; - this.name = data.name; - this.type = data.type; - } - readonly id: Snowflake; - readonly session: Session; - - name?: string; - type: ChannelTypes; - - isText(): this is TextChannel { - return textBasedChannels.includes(this.type); - } - - isVoice(): this is VoiceChannel { - return this.type === ChannelTypes.GuildVoice; - } - - isDM(): this is DMChannel { - return this.type === ChannelTypes.DM; - } - - isNews(): this is NewsChannel { - return this.type === ChannelTypes.GuildNews; - } - - isThread(): this is ThreadChannel { - return this.type === ChannelTypes.GuildPublicThread || this.type === ChannelTypes.GuildPrivateThread; - } - - toString(): string { - return `<#${this.id}>`; - } -} - -export default BaseChannel; diff --git a/structures/BaseGuild.ts b/structures/BaseGuild.ts deleted file mode 100644 index d233e9c..0000000 --- a/structures/BaseGuild.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Session } from "../session/Session.ts"; -import type { DiscordGuild, GuildFeatures } from "../vendor/external.ts"; -import { Snowflake } from "../util/Snowflake.ts"; -import { iconHashToBigInt } from "../util/hash.ts"; - -/** - * Class for {@link Guild} and {@link AnonymousGuild} - */ -export abstract class BaseGuild implements Model { - constructor(session: Session, data: DiscordGuild) { - this.session = session; - this.id = data.id; - - this.name = data.name; - this.iconHash = data.icon ? iconHashToBigInt(data.icon) : undefined; - - this.features = data.features; - } - - readonly session: Session; - readonly id: Snowflake; - - name: string; - iconHash?: bigint; - features: GuildFeatures[]; - - get createdTimestamp() { - return Snowflake.snowflakeToTimestamp(this.id); - } - - get createdAt() { - return new Date(this.createdTimestamp); - } - - toString() { - return this.name; - } -} - -export default BaseGuild; diff --git a/structures/ChannelFactory.ts b/structures/ChannelFactory.ts deleted file mode 100644 index be073ff..0000000 --- a/structures/ChannelFactory.ts +++ /dev/null @@ -1,39 +0,0 @@ -import type { Session } from "../session/Session.ts"; -import type { DiscordChannel } from "../vendor/external.ts"; -import { ChannelTypes } from "../vendor/external.ts"; -import { textBasedChannels } from "./TextChannel.ts"; -import TextChannel from "./TextChannel.ts"; -import VoiceChannel from "./VoiceChannel.ts"; -import DMChannel from "./DMChannel.ts"; -import NewsChannel from "./NewsChannel.ts"; -import ThreadChannel from "./ThreadChannel.ts"; - -export type Channel = - | TextChannel - | VoiceChannel - | DMChannel - | NewsChannel - | ThreadChannel; - -export class ChannelFactory { - static from(session: Session, channel: DiscordChannel): Channel { - switch (channel.type) { - case ChannelTypes.GuildPublicThread: - case ChannelTypes.GuildPrivateThread: - return new ThreadChannel(session, channel, channel.guild_id!); - case ChannelTypes.GuildNews: - return new NewsChannel(session, channel, channel.guild_id!); - case ChannelTypes.DM: - return new DMChannel(session, channel); - case ChannelTypes.GuildVoice: - return new VoiceChannel(session, channel, channel.guild_id!); - default: - if (textBasedChannels.includes(channel.type)) { - return new TextChannel(session, channel); - } - throw new Error("Channel was not implemented"); - } - } -} - -export default ChannelFactory; diff --git a/structures/DMChannel.ts b/structures/DMChannel.ts deleted file mode 100644 index d8006c0..0000000 --- a/structures/DMChannel.ts +++ /dev/null @@ -1,40 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Session } from "../session/Session.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { ChannelTypes, DiscordChannel } from "../vendor/external.ts"; -import TextChannel from "./TextChannel.ts"; -import BaseChannel from "./BaseChannel.ts"; -import User from "./User.ts"; -import * as Routes from "../util/Routes.ts"; - -export class DMChannel extends BaseChannel implements Model { - constructor(session: Session, data: DiscordChannel) { - super(session, data); - - this.user = new User(this.session, data.recipents!.find((r) => r.id !== this.session.botId)!); - this.type = data.type as ChannelTypes.DM | ChannelTypes.GroupDm; - if (data.last_message_id) { - this.lastMessageId = data.last_message_id; - } - } - - override type: ChannelTypes.DM | ChannelTypes.GroupDm; - user: User; - lastMessageId?: Snowflake; - - async close() { - const channel = await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.CHANNEL(this.id), - ); - - return new DMChannel(this.session, channel); - } -} - -TextChannel.applyTo(DMChannel); - -export interface DMChannel extends TextChannel, BaseChannel {} - -export default DMChannel; diff --git a/structures/Guild.ts b/structures/Guild.ts deleted file mode 100644 index 8f8f57f..0000000 --- a/structures/Guild.ts +++ /dev/null @@ -1,367 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { - DiscordEmoji, - DiscordGuild, - DiscordInviteMetadata, - DiscordMemberWithUser, - DiscordRole, -} from "../vendor/external.ts"; -import type { GetInvite } from "../util/Routes.ts"; -import { - DefaultMessageNotificationLevels, - ExplicitContentFilterLevels, - VerificationLevels, -} from "../vendor/external.ts"; -import { iconBigintToHash, iconHashToBigInt } from "../util/hash.ts"; -import { urlToBase64 } from "../util/urlToBase64.ts"; -import Member from "./Member.ts"; -import BaseGuild from "./BaseGuild.ts"; -import Role from "./Role.ts"; -import GuildEmoji from "./GuildEmoji.ts"; -import Invite from "./Invite.ts"; -import * as Routes from "../util/Routes.ts"; - -export interface CreateRole { - name?: string; - color?: number; - iconHash?: string | bigint; - unicodeEmoji?: string; - hoist?: boolean; - mentionable?: boolean; -} - -export interface ModifyGuildRole { - name?: string; - color?: number; - hoist?: boolean; - mentionable?: boolean; - unicodeEmoji?: string; -} - -export interface CreateGuildEmoji { - name: string; - image: string; - roles?: Snowflake[]; - reason?: string; -} - -export interface ModifyGuildEmoji { - name?: string; - roles?: Snowflake[]; -} - -/** - * @link https://discord.com/developers/docs/resources/guild#create-guild-ban - */ -export interface CreateGuildBan { - deleteMessageDays?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7; - reason?: string; -} - -/** - * @link https://discord.com/developers/docs/resources/guild#modify-guild-member - */ -export interface ModifyGuildMember { - nick?: string; - roles?: Snowflake[]; - mute?: boolean; - deaf?: boolean; - channelId?: Snowflake; - communicationDisabledUntil?: number; -} - -/** - * @link https://discord.com/developers/docs/resources/guild#begin-guild-prune - */ -export interface BeginGuildPrune { - days?: number; - computePruneCount?: boolean; - includeRoles?: Snowflake[]; -} - -export interface ModifyRolePositions { - id: Snowflake; - position?: number | null; -} - -/** - * Represents a guild - * @link https://discord.com/developers/docs/resources/guild#guild-object - */ -export class Guild extends BaseGuild implements Model { - constructor(session: Session, data: DiscordGuild) { - super(session, data); - - this.splashHash = data.splash ? iconHashToBigInt(data.splash) : undefined; - this.discoverySplashHash = data.discovery_splash ? iconHashToBigInt(data.discovery_splash) : undefined; - this.ownerId = data.owner_id; - this.widgetEnabled = !!data.widget_enabled; - this.widgetChannelId = data.widget_channel_id ? data.widget_channel_id : undefined; - this.vefificationLevel = data.verification_level; - this.defaultMessageNotificationLevel = data.default_message_notifications; - this.explicitContentFilterLevel = data.explicit_content_filter; - this.members = data.members?.map((member) => new Member(session, { ...member, user: member.user! }, data.id)) ?? - []; - this.roles = data.roles.map((role) => new Role(session, role, data.id)); - this.emojis = data.emojis.map((guildEmoji) => new GuildEmoji(session, guildEmoji, data.id)); - } - - splashHash?: bigint; - discoverySplashHash?: bigint; - ownerId: Snowflake; - widgetEnabled: boolean; - widgetChannelId?: Snowflake; - vefificationLevel: VerificationLevels; - defaultMessageNotificationLevel: DefaultMessageNotificationLevels; - explicitContentFilterLevel: ExplicitContentFilterLevels; - members: Member[]; - roles: Role[]; - emojis: GuildEmoji[]; - - /** - * 'null' would reset the nickname - */ - async editBotNickname(options: { nick: string | null; reason?: string }) { - const result = await this.session.rest.runMethod<{ nick?: string } | undefined>( - this.session.rest, - "PATCH", - Routes.USER_NICK(this.id), - options, - ); - - return result?.nick; - } - - async createEmoji(options: CreateGuildEmoji): Promise { - if (options.image && !options.image.startsWith("data:image/")) { - options.image = await urlToBase64(options.image); - } - - const emoji = await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.GUILD_EMOJIS(this.id), - options, - ); - - return new GuildEmoji(this.session, emoji, this.id); - } - - async deleteEmoji(id: Snowflake, { reason }: { reason?: string } = {}): Promise { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.GUILD_EMOJI(this.id, id), - { reason }, - ); - } - - async editEmoji(id: Snowflake, options: ModifyGuildEmoji): Promise { - const emoji = await this.session.rest.runMethod( - this.session.rest, - "PATCH", - Routes.GUILD_EMOJI(this.id, id), - options, - ); - - return new GuildEmoji(this.session, emoji, this.id); - } - - async createRole(options: CreateRole): Promise { - let icon: string | undefined; - - if (options.iconHash) { - if (typeof options.iconHash === "string") { - icon = options.iconHash; - } else { - icon = iconBigintToHash(options.iconHash); - } - } - - const role = await this.session.rest.runMethod( - this.session.rest, - "PUT", - Routes.GUILD_ROLES(this.id), - { - name: options.name, - color: options.color, - icon, - unicode_emoji: options.unicodeEmoji, - hoist: options.hoist, - mentionable: options.mentionable, - }, - ); - - return new Role(this.session, role, this.id); - } - - async deleteRole(roleId: Snowflake): Promise { - await this.session.rest.runMethod(this.session.rest, "DELETE", Routes.GUILD_ROLE(this.id, roleId)); - } - - async editRole(roleId: Snowflake, options: ModifyGuildRole): Promise { - const role = await this.session.rest.runMethod( - this.session.rest, - "PATCH", - Routes.GUILD_ROLE(this.id, roleId), - { - name: options.name, - color: options.color, - hoist: options.hoist, - mentionable: options.mentionable, - }, - ); - - return new Role(this.session, role, this.id); - } - - async addRole(memberId: Snowflake, roleId: Snowflake, { reason }: { reason?: string } = {}) { - await this.session.rest.runMethod( - this.session.rest, - "PUT", - Routes.GUILD_MEMBER_ROLE(this.id, memberId, roleId), - { reason }, - ); - } - - async removeRole(memberId: Snowflake, roleId: Snowflake, { reason }: { reason?: string } = {}) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.GUILD_MEMBER_ROLE(this.id, memberId, roleId), - { reason }, - ); - } - - /** - * Returns the roles moved - */ - async moveRoles(options: ModifyRolePositions[]) { - const roles = await this.session.rest.runMethod( - this.session.rest, - "PATCH", - Routes.GUILD_ROLES(this.id), - options, - ); - - return roles.map((role) => new Role(this.session, role, this.id)); - } - - async deleteInvite(inviteCode: string): Promise { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.INVITE(inviteCode), - {}, - ); - } - - async fetchInvite(inviteCode: string, options: GetInvite): Promise { - const inviteMetadata = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.INVITE(inviteCode, options), - ); - - return new Invite(this.session, inviteMetadata); - } - - async fetchInvites(): Promise { - const invites = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.GUILD_INVITES(this.id), - ); - - return invites.map((invite) => new Invite(this.session, invite)); - } - - /** - * Bans the member - */ - async banMember(memberId: Snowflake, options: CreateGuildBan) { - await this.session.rest.runMethod( - this.session.rest, - "PUT", - Routes.GUILD_BAN(this.id, memberId), - options - ? { - delete_message_days: options.deleteMessageDays, - reason: options.reason, - } - : {}, - ); - } - - /** - * Kicks the member - */ - async kickMember(memberId: Snowflake, { reason }: { reason?: string }) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.GUILD_MEMBER(this.id, memberId), - { reason }, - ); - } - - /* - * Unbans the member - * */ - async unbanMember(memberId: Snowflake) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.GUILD_BAN(this.id, memberId), - ); - } - - async editMember(memberId: Snowflake, options: ModifyGuildMember) { - const member = await this.session.rest.runMethod( - this.session.rest, - "PATCH", - Routes.GUILD_MEMBER(this.id, memberId), - { - nick: options.nick, - roles: options.roles, - mute: options.mute, - deaf: options.deaf, - channel_id: options.channelId, - communication_disabled_until: options.communicationDisabledUntil - ? new Date(options.communicationDisabledUntil).toISOString() - : undefined, - }, - ); - - return new Member(this.session, member, this.id); - } - - async pruneMembers(options: BeginGuildPrune): Promise { - const result = await this.session.rest.runMethod<{ pruned: number }>( - this.session.rest, - "POST", - Routes.GUILD_PRUNE(this.id), - { - days: options.days, - compute_prune_count: options.computePruneCount, - include_roles: options.includeRoles, - }, - ); - - return result.pruned; - } - - async getPruneCount(): Promise { - const result = await this.session.rest.runMethod<{ pruned: number }>( - this.session.rest, - "GET", - Routes.GUILD_PRUNE(this.id), - ); - - return result.pruned; - } -} - -export default Guild; diff --git a/structures/GuildChannel.ts b/structures/GuildChannel.ts deleted file mode 100644 index 19aa931..0000000 --- a/structures/GuildChannel.ts +++ /dev/null @@ -1,70 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { ChannelTypes, DiscordChannel, DiscordInviteMetadata } from "../vendor/external.ts"; -import BaseChannel from "./BaseChannel.ts"; -import Invite from "./Invite.ts"; -import * as Routes from "../util/Routes.ts"; - -/** - * Represent the options object to create a Thread Channel - * @link https://discord.com/developers/docs/resources/channel#start-thread-without-message - */ -export interface ThreadCreateOptions { - name: string; - autoArchiveDuration: 60 | 1440 | 4320 | 10080; - type: 10 | 11 | 12; - invitable?: boolean; - reason?: string; -} - -export class GuildChannel extends BaseChannel implements Model { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data); - this.type = data.type as number; - this.guildId = guildId; - this.position = data.position; - data.topic ? this.topic = data.topic : null; - data.parent_id ? this.parentId = data.parent_id : undefined; - } - - override type: Exclude; - guildId: Snowflake; - topic?: string; - position?: number; - parentId?: Snowflake; - - async fetchInvites(): Promise { - const invites = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.CHANNEL_INVITES(this.id), - ); - - return invites.map((invite) => new Invite(this.session, invite)); - } - - /* - async createThread(options: ThreadCreateOptions): Promise { - const thread = await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.CHANNEL_CREATE_THREAD(this.id), - options, - ); - return new ThreadChannel(this.session, thread, this.guildId); - }*/ - - async delete(reason?: string) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.CHANNEL(this.id), - { - reason, - }, - ); - } -} - -export default GuildChannel; diff --git a/structures/GuildEmoji.ts b/structures/GuildEmoji.ts index 0de968d..6b3d2b3 100644 --- a/structures/GuildEmoji.ts +++ b/structures/GuildEmoji.ts @@ -2,8 +2,8 @@ import type { Model } from "./Base.ts"; import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; import type { DiscordEmoji } from "../vendor/external.ts"; -import type { ModifyGuildEmoji } from "./Guild.ts"; -import Guild from "./Guild.ts"; +import type { ModifyGuildEmoji } from "./guilds/Guild.ts"; +import Guild from "./guilds/Guild.ts"; import Emoji from "./Emoji.ts"; import User from "./User.ts"; import * as Routes from "../util/Routes.ts"; diff --git a/structures/Interaction.ts b/structures/Interaction.ts deleted file mode 100644 index 762244f..0000000 --- a/structures/Interaction.ts +++ /dev/null @@ -1,145 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { - DiscordInteraction, - DiscordMessage, - FileContent, - InteractionResponseTypes, - InteractionTypes, -} from "../vendor/external.ts"; -import type { MessageFlags } from "../util/shared/flags.ts"; -import type { AllowedMentions } from "./Message.ts"; -import User from "./User.ts"; -import Message from "./Message.ts"; -import Member from "./Member.ts"; -import * as Routes from "../util/Routes.ts"; - -export interface InteractionResponse { - type: InteractionResponseTypes; - data?: InteractionApplicationCommandCallbackData; -} - -export interface InteractionApplicationCommandCallbackData { - content?: string; - tts?: boolean; - allowedMentions?: AllowedMentions; - files?: FileContent[]; - customId?: string; - title?: string; - // components?: Component[]; - flags?: MessageFlags; - choices?: ApplicationCommandOptionChoice[]; -} - -/** https://discord.com/developers/docs/interactions/slash-commands#applicationcommandoptionchoice */ -export interface ApplicationCommandOptionChoice { - name: string; - value: string | number; -} - -// TODO: abstract Interaction, CommandInteraction, ComponentInteraction, PingInteraction, etc - -export class Interaction implements Model { - constructor(session: Session, data: DiscordInteraction) { - this.session = session; - this.id = data.id; - this.token = data.token; - this.type = data.type; - this.guildId = data.guild_id; - this.channelId = data.channel_id; - this.applicationId = data.application_id; - this.locale = data.locale; - this.data = data.data; - - if (!data.guild_id) { - this.user = new User(session, data.user!); - } else { - this.member = new Member(session, data.member!, data.guild_id); - } - } - - readonly session: Session; - readonly id: Snowflake; - readonly token: string; - - type: InteractionTypes; - guildId?: Snowflake; - channelId?: Snowflake; - applicationId?: Snowflake; - locale?: string; - // deno-lint-ignore no-explicit-any - data: any; - user?: User; - member?: Member; - - async respond({ type, data }: InteractionResponse) { - const toSend = { - tts: data?.tts, - title: data?.title, - flags: data?.flags, - content: data?.content, - choices: data?.choices, - custom_id: data?.customId, - allowed_mentions: data?.allowedMentions - ? { - users: data.allowedMentions.users, - roles: data.allowedMentions.roles, - parse: data.allowedMentions.parse, - replied_user: data.allowedMentions.repliedUser, - } - : { parse: [] }, - }; - - if (this.session.unrepliedInteractions.delete(BigInt(this.id))) { - await this.session.rest.sendRequest( - this.session.rest, - { - url: Routes.INTERACTION_ID_TOKEN(this.id, this.token), - method: "POST", - payload: this.session.rest.createRequestBody(this.session.rest, { - method: "POST", - body: { - type: type, - data: toSend, - file: data?.files, - }, - headers: { - // remove authorization header - Authorization: "", - }, - }), - }, - ); - - return; - } - - const result = await this.session.rest.sendRequest( - this.session.rest, - { - url: Routes.WEBHOOK(this.session.applicationId ?? this.session.botId, this.token), - method: "POST", - payload: this.session.rest.createRequestBody(this.session.rest, { - method: "POST", - body: { - ...toSend, - file: data?.files, - }, - headers: { - // remove authorization header - Authorization: "", - }, - }), - }, - ); - - return new Message(this.session, result); - } - - inGuild(): this is Interaction & { user: undefined; guildId: Snowflake; member: Member } { - return !!this.guildId; - } -} - -export default Interaction; diff --git a/structures/Invite.ts b/structures/Invite.ts index 2e12880..5d238e7 100644 --- a/structures/Invite.ts +++ b/structures/Invite.ts @@ -1,9 +1,9 @@ import type { Session } from "../session/Session.ts"; import type { DiscordInvite } from "../vendor/external.ts"; import { TargetTypes } from "../vendor/external.ts"; -import InviteGuild from "./InviteGuild.ts"; +import InviteGuild from "./guilds/InviteGuild.ts"; import User from "./User.ts"; -import Guild from "./Guild.ts"; +import Guild from "./guilds/Guild.ts"; /** * @link https://discord.com/developers/docs/resources/invite#invite-object diff --git a/structures/InviteGuild.ts b/structures/InviteGuild.ts deleted file mode 100644 index 0465165..0000000 --- a/structures/InviteGuild.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Session } from "../session/Session.ts"; -import type { DiscordGuild } from "../vendor/external.ts"; -import AnonymousGuild from "./AnonymousGuild.ts"; -import WelcomeScreen from "./WelcomeScreen.ts"; - -export class InviteGuild extends AnonymousGuild implements Model { - constructor(session: Session, data: Partial) { - super(session, data); - - if (data.welcome_screen) { - this.welcomeScreen = new WelcomeScreen(session, data.welcome_screen); - } - } - - welcomeScreen?: WelcomeScreen; -} - -export default InviteGuild; diff --git a/structures/Member.ts b/structures/Member.ts index 8babac0..601d5af 100644 --- a/structures/Member.ts +++ b/structures/Member.ts @@ -3,10 +3,10 @@ import type { Snowflake } from "../util/Snowflake.ts"; import type { Session } from "../session/Session.ts"; import type { DiscordMemberWithUser } from "../vendor/external.ts"; import type { ImageFormat, ImageSize } from "../util/shared/images.ts"; -import type { CreateGuildBan, ModifyGuildMember } from "./Guild.ts"; +import type { CreateGuildBan, ModifyGuildMember } from "./guilds/Guild.ts"; import { iconBigintToHash, iconHashToBigInt } from "../util/hash.ts"; import User from "./User.ts"; -import Guild from "./Guild.ts"; +import Guild from "./guilds/Guild.ts"; import * as Routes from "../util/Routes.ts"; /** diff --git a/structures/NewsChannel.ts b/structures/NewsChannel.ts deleted file mode 100644 index 2dbebbb..0000000 --- a/structures/NewsChannel.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { ChannelTypes, DiscordChannel } from "../vendor/external.ts"; -import GuildChannel from "./GuildChannel.ts"; -import Message from "./Message.ts"; -import TextChannel from "./TextChannel.ts"; - -export class NewsChannel extends GuildChannel { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - this.type = data.type as ChannelTypes.GuildNews; - this.defaultAutoArchiveDuration = data.default_auto_archive_duration; - } - - override type: ChannelTypes.GuildNews; - defaultAutoArchiveDuration?: number; - - crosspostMessage(messageId: Snowflake): Promise { - return Message.prototype.crosspost.call({ id: messageId, channelId: this.id, session: this.session }); - } - - get publishMessage() { - return this.crosspostMessage; - } -} - -TextChannel.applyTo(NewsChannel); - -export interface NewsChannel extends TextChannel, GuildChannel {} - -export default NewsChannel; diff --git a/structures/Role.ts b/structures/Role.ts index 41a4c97..43ec61d 100644 --- a/structures/Role.ts +++ b/structures/Role.ts @@ -4,7 +4,7 @@ import type { Session } from "../session/Session.ts"; import { Snowflake } from "../util/Snowflake.ts"; import { iconHashToBigInt } from "../util/hash.ts"; import Permissions from "./Permissions.ts"; -import Guild, { ModifyGuildRole } from "./Guild.ts"; +import Guild, { ModifyGuildRole } from "./guilds/Guild.ts"; export class Role implements Model { constructor(session: Session, data: DiscordRole, guildId: Snowflake) { diff --git a/structures/TextChannel.ts b/structures/TextChannel.ts deleted file mode 100644 index d007807..0000000 --- a/structures/TextChannel.ts +++ /dev/null @@ -1,215 +0,0 @@ -// deno-lint-ignore-file ban-types -import type { Session } from "../session/Session.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { GetMessagesOptions, GetReactions } from "../util/Routes.ts"; -import type { DiscordChannel, DiscordInvite, DiscordMessage, DiscordWebhook, TargetTypes } from "../vendor/external.ts"; -import type { CreateMessage, EditMessage, ReactionResolvable } from "./Message.ts"; -import { ChannelTypes } from "../vendor/external.ts"; -import { urlToBase64 } from "../util/urlToBase64.ts"; -import Message from "./Message.ts"; -import Invite from "./Invite.ts"; -import Webhook from "./Webhook.ts"; -import * as Routes from "../util/Routes.ts"; - -/** - * Represents the options object to create an invitation - * @link https://discord.com/developers/docs/resources/channel#create-channel-invite-json-params - */ -export interface DiscordInviteOptions { - maxAge?: number; - maxUses?: number; - unique?: boolean; - temporary: boolean; - reason?: string; - targetType?: TargetTypes; - targetUserId?: Snowflake; - targetApplicationId?: Snowflake; -} - -export interface CreateWebhook { - name: string; - avatar?: string; - reason?: string; -} - -export const textBasedChannels = [ - ChannelTypes.DM, - ChannelTypes.GroupDm, - ChannelTypes.GuildPrivateThread, - ChannelTypes.GuildPublicThread, - ChannelTypes.GuildNews, - ChannelTypes.GuildText, -]; - -export type TextBasedChannels = - | ChannelTypes.DM - | ChannelTypes.GroupDm - | ChannelTypes.GuildPrivateThread - | ChannelTypes.GuildPublicThread - | ChannelTypes.GuildNews - | ChannelTypes.GuildText; - -export class TextChannel { - constructor(session: Session, data: DiscordChannel) { - this.session = session; - this.id = data.id; - this.name = data.name; - this.type = data.type as number; - this.rateLimitPerUser = data.rate_limit_per_user ?? 0; - this.nsfw = !!data.nsfw ?? false; - - if (data.last_message_id) { - this.lastMessageId = data.last_message_id; - } - - if (data.last_pin_timestamp) { - this.lastPinTimestamp = data.last_pin_timestamp; - } - } - - readonly session: Session; - readonly id: Snowflake; - name?: string; - type: TextBasedChannels; - lastMessageId?: Snowflake; - lastPinTimestamp?: string; - rateLimitPerUser: number; - nsfw: boolean; - - /** - * Mixin - */ - static applyTo(klass: Function) { - klass.prototype.fetchPins = TextChannel.prototype.fetchPins; - klass.prototype.createInvite = TextChannel.prototype.createInvite; - klass.prototype.fetchMessages = TextChannel.prototype.fetchMessages; - klass.prototype.sendTyping = TextChannel.prototype.sendTyping; - klass.prototype.pinMessage = TextChannel.prototype.pinMessage; - klass.prototype.unpinMessage = TextChannel.prototype.unpinMessage; - klass.prototype.addReaction = TextChannel.prototype.addReaction; - klass.prototype.removeReaction = TextChannel.prototype.removeReaction; - klass.prototype.removeReactionEmoji = TextChannel.prototype.removeReactionEmoji; - klass.prototype.nukeReactions = TextChannel.prototype.nukeReactions; - klass.prototype.fetchReactions = TextChannel.prototype.fetchReactions; - klass.prototype.sendMessage = TextChannel.prototype.sendMessage; - klass.prototype.editMessage = TextChannel.prototype.editMessage; - klass.prototype.createWebhook = TextChannel.prototype.createWebhook; - } - - async fetchPins(): Promise { - const messages = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.CHANNEL_PINS(this.id), - ); - return messages[0] ? messages.map((x: DiscordMessage) => new Message(this.session, x)) : []; - } - - async createInvite(options?: DiscordInviteOptions) { - const invite = await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.CHANNEL_INVITES(this.id), - options - ? { - max_age: options.maxAge, - max_uses: options.maxUses, - temporary: options.temporary, - unique: options.unique, - target_type: options.targetType, - target_user_id: options.targetUserId, - target_application_id: options.targetApplicationId, - } - : {}, - ); - - return new Invite(this.session, invite); - } - - async fetchMessages(options?: GetMessagesOptions): Promise { - if (options?.limit! > 100) throw Error("Values must be between 0-100"); - const messages = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.CHANNEL_MESSAGES(this.id, options), - ); - return messages[0] ? messages.map((x) => new Message(this.session, x)) : []; - } - - async sendTyping() { - await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.CHANNEL_TYPING(this.id), - ); - } - - async pinMessage(messageId: Snowflake) { - await Message.prototype.pin.call({ id: messageId, channelId: this.id, session: this.session }); - } - - async unpinMessage(messageId: Snowflake) { - await Message.prototype.unpin.call({ id: messageId, channelId: this.id, session: this.session }); - } - - async addReaction(messageId: Snowflake, reaction: ReactionResolvable) { - await Message.prototype.addReaction.call( - { channelId: this.id, id: messageId, session: this.session }, - reaction, - ); - } - - async removeReaction(messageId: Snowflake, reaction: ReactionResolvable, options?: { userId: Snowflake }) { - await Message.prototype.removeReaction.call( - { channelId: this.id, id: messageId, session: this.session }, - reaction, - options, - ); - } - - async removeReactionEmoji(messageId: Snowflake, reaction: ReactionResolvable) { - await Message.prototype.removeReactionEmoji.call( - { channelId: this.id, id: messageId, session: this.session }, - reaction, - ); - } - - async nukeReactions(messageId: Snowflake) { - await Message.prototype.nukeReactions.call({ channelId: this.id, id: messageId }); - } - - async fetchReactions(messageId: Snowflake, reaction: ReactionResolvable, options?: GetReactions) { - const users = await Message.prototype.fetchReactions.call( - { channelId: this.id, id: messageId, session: this.session }, - reaction, - options, - ); - - return users; - } - - sendMessage(options: CreateMessage) { - return Message.prototype.reply.call({ channelId: this.id, session: this.session }, options); - } - - editMessage(messageId: Snowflake, options: EditMessage) { - return Message.prototype.edit.call({ channelId: this.id, id: messageId, session: this.session }, options); - } - - async createWebhook(options: CreateWebhook) { - const webhook = await this.session.rest.runMethod( - this.session.rest, - "POST", - Routes.CHANNEL_WEBHOOKS(this.id), - { - name: options.name, - avatar: options.avatar ? urlToBase64(options.avatar) : undefined, - reason: options.reason, - }, - ); - - return new Webhook(this.session, webhook); - } -} - -export default TextChannel; diff --git a/structures/ThreadChannel.ts b/structures/ThreadChannel.ts deleted file mode 100644 index a6073eb..0000000 --- a/structures/ThreadChannel.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { ChannelTypes, DiscordChannel } from "../vendor/external.ts"; -import GuildChannel from "./GuildChannel.ts"; -import TextChannel from "./TextChannel.ts"; - -export class ThreadChannel extends GuildChannel implements Model { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - this.type = data.type as number; - this.archived = !!data.thread_metadata?.archived; - this.archiveTimestamp = data.thread_metadata?.archive_timestamp; - this.autoArchiveDuration = data.thread_metadata?.auto_archive_duration; - this.locked = !!data.thread_metadata?.locked; - this.messageCount = data.message_count; - this.memberCount = data.member_count; - this.ownerId = data.owner_id; - } - - override type: ChannelTypes.GuildNewsThread | ChannelTypes.GuildPrivateThread | ChannelTypes.GuildPublicThread; - archived?: boolean; - archiveTimestamp?: string; - autoArchiveDuration?: number; - locked?: boolean; - messageCount?: number; - memberCount?: number; - ownerId?: Snowflake; -} - -TextChannel.applyTo(ThreadChannel); - -export interface ThreadChannel extends Omit, Omit {} - -export default ThreadChannel; diff --git a/structures/VoiceChannel.ts b/structures/VoiceChannel.ts deleted file mode 100644 index fa26b7e..0000000 --- a/structures/VoiceChannel.ts +++ /dev/null @@ -1,60 +0,0 @@ -import type { Snowflake } from "../util/Snowflake.ts"; -import type { Session } from "../session/Session.ts"; -import type { DiscordChannel, VideoQualityModes } from "../vendor/external.ts"; -import { GatewayOpcodes } from "../vendor/external.ts"; -import { calculateShardId } from "../vendor/gateway/calculateShardId.ts"; -import GuildChannel from "./GuildChannel.ts"; - -/** - * @link https://discord.com/developers/docs/topics/gateway#update-voice-state - */ -export interface UpdateVoiceState { - guildId: string; - channelId?: string; - selfMute: boolean; - selfDeaf: boolean; -} - -export class VoiceChannel extends GuildChannel { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - this.bitRate = data.bitrate; - this.userLimit = data.user_limit ?? 0; - this.videoQuality = data.video_quality_mode; - this.nsfw = !!data.nsfw; - - if (data.rtc_region) { - this.rtcRegion = data.rtc_region; - } - } - bitRate?: number; - userLimit: number; - rtcRegion?: Snowflake; - - videoQuality?: VideoQualityModes; - nsfw: boolean; - - /** - * This function was gathered from Discordeno it may not work - */ - async connect(options?: UpdateVoiceState) { - const shardId = calculateShardId(this.session.gateway, BigInt(super.guildId)); - const shard = this.session.gateway.manager.shards.get(shardId); - - if (!shard) { - throw new Error(`Shard (id: ${shardId} not found`); - } - - await shard.send({ - op: GatewayOpcodes.VoiceStateUpdate, - d: { - guild_id: super.guildId, - channel_id: super.id, - self_mute: Boolean(options?.selfMute), - self_deaf: options?.selfDeaf ?? true, - }, - }); - } -} - -export default VoiceChannel;