From e90c336247cc4023cf6c72c3b98d2a235c84d536 Mon Sep 17 00:00:00 2001 From: socram03 Date: Fri, 8 Jul 2022 15:31:33 -0400 Subject: [PATCH 1/5] USER_UPDATE --- packages/biscuit/Actions.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index 6f3a0fd..67df5fe 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -34,11 +34,7 @@ import type { Session } from "./Session.ts"; import type { Channel } from "./structures/channels.ts"; import type { Interaction } from "./structures/interactions/InteractionFactory.ts"; -import { - ChannelFactory, - GuildChannel, - ThreadChannel, -} from "./structures/channels.ts"; +import { ChannelFactory, GuildChannel, ThreadChannel } from "./structures/channels.ts"; import ThreadMember from "./structures/ThreadMember.ts"; import Member from "./structures/Member.ts"; @@ -185,6 +181,10 @@ export const CHANNEL_PINS_UPDATE: RawHandler = (sessio }); }; +export const USER_UPDATE: RawHandler = (session, _shardId, payload) => { + session.emit("userUpdate", new User(session, payload)); +}; + export const WEBHOOKS_UPDATE: RawHandler = (session, _shardId, webhook) => { session.emit("webhooksUpdate", { guildId: webhook.guild_id, channelId: webhook.channel_id }); }; @@ -283,4 +283,5 @@ export interface Events { "integrationDelete": Handler<[{ id: Snowflake, guildId?: Snowflake, applicationId?: Snowflake }]>; "raw": Handler<[unknown, number]>; "webhooksUpdate": Handler<[{ guildId: Snowflake, channelId: Snowflake }]>; + "userUpdate": Handler<[User]>; } From 6ec048e47d2b664340a3b03cf86dbb96827e262c Mon Sep 17 00:00:00 2001 From: socram03 Date: Fri, 8 Jul 2022 15:55:09 -0400 Subject: [PATCH 2/5] TYPING_START --- packages/biscuit/Actions.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index 67df5fe..c2e2a8e 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -26,7 +26,9 @@ import type { // DiscordThreadMembersUpdate, DiscordThreadListSync, DiscordUser, + DiscordMemberWithUser, DiscordWebhookUpdate, + DiscordTypingStart, } from "../discordeno/mod.ts"; import type { Snowflake } from "./Snowflake.ts"; @@ -128,6 +130,16 @@ export const GUILD_ROLE_DELETE: RawHandler = (session, _ session.emit("guildRoleDelete", { guildId: data.guild_id, roleId: data.role_id }); }; +export const TYPING_START: RawHandler = (session, _shardId, payload) => { + session.emit("typingStart", { + channelId: payload.channel_id, + guildId: payload.guild_id ? payload.guild_id : undefined, + userId: payload.user_id, + timestamp: payload.timestamp, + member: payload.guild_id ? new Member(session, payload.member as DiscordMemberWithUser, payload.guild_id) : undefined + }) +} + export const INTERACTION_CREATE: RawHandler = (session, _shardId, interaction) => { session.emit("interactionCreate", InteractionFactory.from(session, interaction)); }; @@ -269,6 +281,7 @@ export interface Events { "guildRoleCreate": Handler<[{ guildId: Snowflake, role: DiscordRole }]>; "guildRoleUpdate": Handler<[{ guildId: Snowflake, role: DiscordRole }]>; "guildRoleDelete": Handler<[{ guildId: Snowflake, roleId: Snowflake }]>; + "typingStart": Handler<[{channelId: Snowflake, guildId?: Snowflake, userId: Snowflake, timestamp: number, member?: Member}]> "channelCreate": Handler<[Channel]>; "channelUpdate": Handler<[Channel]>; "channelDelete": Handler<[GuildChannel]>; From 870882313af7633663cfd8ea54974eb20b6dcfd7 Mon Sep 17 00:00:00 2001 From: socram03 Date: Fri, 8 Jul 2022 16:03:15 -0400 Subject: [PATCH 3/5] fix: fmt --- packages/biscuit/Actions.ts | 14 +- packages/biscuit/Util.ts | 7 +- packages/biscuit/structures/Member.ts | 2 +- packages/biscuit/structures/channels.ts | 1384 +++++++++-------- .../structures/components/ComponentFactory.ts | 2 +- 5 files changed, 706 insertions(+), 703 deletions(-) diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index c2e2a8e..7c109c1 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -14,6 +14,7 @@ import type { DiscordIntegration, DiscordIntegrationDelete, DiscordInteraction, + DiscordMemberWithUser, DiscordMessage, DiscordMessageDelete, DiscordMessageReactionAdd, @@ -25,10 +26,9 @@ import type { // DiscordThreadMemberUpdate, // DiscordThreadMembersUpdate, DiscordThreadListSync, - DiscordUser, - DiscordMemberWithUser, - DiscordWebhookUpdate, DiscordTypingStart, + DiscordUser, + DiscordWebhookUpdate, } from "../discordeno/mod.ts"; import type { Snowflake } from "./Snowflake.ts"; @@ -136,9 +136,11 @@ export const TYPING_START: RawHandler = (session, _shardId, guildId: payload.guild_id ? payload.guild_id : undefined, userId: payload.user_id, timestamp: payload.timestamp, - member: payload.guild_id ? new Member(session, payload.member as DiscordMemberWithUser, payload.guild_id) : undefined - }) -} + member: payload.guild_id + ? new Member(session, payload.member as DiscordMemberWithUser, payload.guild_id) + : undefined, + }); +}; export const INTERACTION_CREATE: RawHandler = (session, _shardId, interaction) => { session.emit("interactionCreate", InteractionFactory.from(session, interaction)); diff --git a/packages/biscuit/Util.ts b/packages/biscuit/Util.ts index f98a292..37c865d 100644 --- a/packages/biscuit/Util.ts +++ b/packages/biscuit/Util.ts @@ -33,17 +33,16 @@ export type ComponentBuilder = /*** * Utility type - * */ + */ export type ComponentEmoji = { id: Snowflake; name: string; animated?: boolean; }; - /** * Utility type - * */ + */ export interface PermissionsOverwrites { id: Snowflake; type: 0 | 1; @@ -63,7 +62,7 @@ export type ImageSize = 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096; /** * Utility functions - * */ + */ export class Util { static formatImageURL(url: string, size: ImageSize = 128, format?: ImageFormat) { return `${url}.${format || (url.includes("/a_") ? "gif" : "jpg")}?size=${size}`; diff --git a/packages/biscuit/structures/Member.ts b/packages/biscuit/structures/Member.ts index 4612319..3b711e9 100644 --- a/packages/biscuit/structures/Member.ts +++ b/packages/biscuit/structures/Member.ts @@ -1,7 +1,7 @@ import type { Model } from "./Base.ts"; import type { Snowflake } from "../Snowflake.ts"; import type { Session } from "../Session.ts"; -import type { DiscordMemberWithUser } from "../../discordeno/mod.ts" +import type { DiscordMemberWithUser } from "../../discordeno/mod.ts"; import type { ImageFormat, ImageSize } from "../Util.ts"; import type { CreateGuildBan, ModifyGuildMember } from "./guilds/Guild.ts"; import Util from "../Util.ts"; diff --git a/packages/biscuit/structures/channels.ts b/packages/biscuit/structures/channels.ts index ee4bad8..8c29c20 100644 --- a/packages/biscuit/structures/channels.ts +++ b/packages/biscuit/structures/channels.ts @@ -1,691 +1,693 @@ -/** Types */ -import type { Model } from "./Base.ts"; -import type { Snowflake } from "../Snowflake.ts"; -import type { Session } from "../Session.ts"; -import type { PermissionsOverwrites } from "../Util.ts"; - -/** External from vendor */ -import { - DiscordChannel, - VideoQualityModes, - ChannelTypes, - GatewayOpcodes, - DiscordInvite, - DiscordMessage, - DiscordWebhook, - TargetTypes, - DiscordInviteMetadata, - DiscordThreadMember, - DiscordListArchivedThreads -} from "../../discordeno/mod.ts"; - -/** Functions and others */ -import { calculateShardId } from "../../discordeno/gateway/calculateShardId.ts"; -import { urlToBase64 } from "../util/urlToBase64.ts"; - -/** Classes and routes */ -import * as Routes from "../Routes.ts"; -import Message, { CreateMessage, EditMessage, ReactionResolvable } from "./Message.ts"; -import Invite from "./Invite.ts"; -import Webhook from "./Webhook.ts"; -import User from "./User.ts"; -import ThreadMember from "./ThreadMember.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; - } - - isStage(): this is StageChannel { - return this.type === ChannelTypes.GuildStageVoice; - } - - toString(): string { - return `<#${this.id}>`; - } -} - -/** TextChannel */ -/** - * 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.GuildVoice, - ChannelTypes.GuildText, -]; - -export type TextBasedChannels = - | ChannelTypes.DM - | ChannelTypes.GroupDm - | ChannelTypes.GuildPrivateThread - | ChannelTypes.GuildPublicThread - | ChannelTypes.GuildNews - | ChannelTypes.GuildVoice - | 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 - */ - // deno-lint-ignore ban-types - static applyTo(klass: Function, ignore: Array = []) { - const methods: Array = [ - "fetchPins", - "createInvite", - "fetchMessages", - "sendTyping", - "pinMessage", - "unpinMessage", - "addReaction", - "removeReaction", - "nukeReactions", - "fetchPins", - "sendMessage", - "editMessage", - "createWebhook", - ]; - - for (const method of methods) { - if (ignore.includes(method)) continue; - - klass.prototype[method] = TextChannel.prototype[method]; - } - } - - 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?: Routes.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?: Routes.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); - } -} - -/** GuildChannel */ -/** - * 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; - rateLimitPerUser?: number; - reason?: string; -} - -/** - * Representations of the objects to edit a guild channel - * @link https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel - */ -export interface EditGuildChannelOptions { - name?: string; - position?: number; - permissionOverwrites?: PermissionsOverwrites[]; -} - -export interface EditNewsChannelOptions extends EditGuildChannelOptions { - type?: ChannelTypes.GuildNews | ChannelTypes.GuildText; - topic?: string | null; - nfsw?: boolean | null; - parentId?: Snowflake | null; - defaultAutoArchiveDuration?: number | null; -} - -export interface EditGuildTextChannelOptions extends EditNewsChannelOptions { - rateLimitPerUser?: number | null; -} - -export interface EditStageChannelOptions extends EditGuildChannelOptions { - bitrate?: number | null; - rtcRegion?: Snowflake | null; -} - -export interface EditVoiceChannelOptions extends EditStageChannelOptions { - nsfw?: boolean | null; - userLimit?: number | null; - parentId?: Snowflake | null; - videoQualityMode?: VideoQualityModes | null; -} - -/** - * Represents the option object to create a thread channel from a message - * @link https://discord.com/developers/docs/resources/channel#start-thread-from-message - */ -export interface ThreadCreateOptions { - name: string; - autoArchiveDuration?: 60 | 1440 | 4320 | 10080; - rateLimitPerUser?: number; - messageId: Snowflake; -} - -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 edit(options: EditNewsChannelOptions): Promise; - async edit(options: EditStageChannelOptions): Promise; - async edit(options: EditVoiceChannelOptions): Promise; - async edit( - options: EditGuildTextChannelOptions | EditNewsChannelOptions | EditVoiceChannelOptions, - ): Promise { - const channel = await this.session.rest.runMethod( - this.session.rest, - "PATCH", - Routes.CHANNEL(this.id), - { - name: options.name, - type: "type" in options ? options.type : undefined, - position: options.position, - topic: "topic" in options ? options.topic : undefined, - nsfw: "nfsw" in options ? options.nfsw : undefined, - rate_limit_per_user: "rateLimitPerUser" in options ? options.rateLimitPerUser : undefined, - bitrate: "bitrate" in options ? options.bitrate : undefined, - user_limit: "userLimit" in options ? options.userLimit : undefined, - permissions_overwrites: options.permissionOverwrites, - parent_id: "parentId" in options ? options.parentId : undefined, - rtc_region: "rtcRegion" in options ? options.rtcRegion : undefined, - video_quality_mode: "videoQualityMode" in options ? options.videoQualityMode : undefined, - default_auto_archive_duration: "defaultAutoArchiveDuration" in options - ? options.defaultAutoArchiveDuration - : undefined, - }, - ); - return ChannelFactory.from(this.session, channel); - } - - async getArchivedThreads(options: Routes.ListArchivedThreads & { type: "public" | "private" | "privateJoinedThreads" }) { - let func: (channelId: Snowflake, options: Routes.ListArchivedThreads) => string; - - switch (options.type) { - case "public": - func = Routes.THREAD_ARCHIVED_PUBLIC; - break; - case "private": - func = Routes.THREAD_START_PRIVATE; - break; - case "privateJoinedThreads": - func = Routes.THREAD_ARCHIVED_PRIVATE_JOINED; - break; - } - - const { threads, members, has_more } = await this.session.rest.runMethod( - this.session.rest, - "GET", - func(this.id, options), - ); - - return { - threads: Object.fromEntries( - threads.map((thread) => [thread.id, new ThreadChannel(this.session, thread, this.id)]), - ) as Record, - members: Object.fromEntries( - members.map((threadMember) => [threadMember.id, new ThreadMember(this.session, threadMember)]), - ) as Record, - hasMore: has_more, - }; - } - - async createThread(options: ThreadCreateOptions): Promise { - const thread = await this.session.rest.runMethod( - this.session.rest, - "POST", - "messageId" in options - ? Routes.THREAD_START_PUBLIC(this.id, options.messageId) - : Routes.THREAD_START_PRIVATE(this.id), - { - name: options.name, - auto_archive_duration: options.autoArchiveDuration, - }, - ); - - return new ThreadChannel(this.session, thread, thread.guild_id ?? this.guildId); - } -} - -/** BaseVoiceChannel */ -/** - * @link https://discord.com/developers/docs/topics/gateway#update-voice-state - */ - export interface UpdateVoiceState { - guildId: string; - channelId?: string; - selfMute: boolean; - selfDeaf: boolean; -} - -export abstract class BaseVoiceChannel 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; - this.type = data.type as number; - - if (data.rtc_region) { - this.rtcRegion = data.rtc_region; - } - } - override type: ChannelTypes.GuildVoice | ChannelTypes.GuildStageVoice; - 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, - }, - }); - } -} - -/** DMChannel */ -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 Omit, Omit {} - -/** VoiceChannel */ -export class VoiceChannel extends BaseVoiceChannel { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - this.type = data.type as number; - } - override type: ChannelTypes.GuildVoice; -} - -export interface VoiceChannel extends TextChannel, BaseVoiceChannel {} - -TextChannel.applyTo(VoiceChannel); - -/** NewsChannel */ -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 {} - -/** StageChannel */ -export class StageChannel extends BaseVoiceChannel { - constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { - super(session, data, guildId); - this.type = data.type as number; - this.topic = data.topic ? data.topic : undefined; - } - override type: ChannelTypes.GuildStageVoice; - topic?: string; -} - -/** ThreadChannel */ -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; - - if (data.member) { - this.member = new ThreadMember(session, data.member); - } - } - - override type: ChannelTypes.GuildNewsThread | ChannelTypes.GuildPrivateThread | ChannelTypes.GuildPublicThread; - archived?: boolean; - archiveTimestamp?: string; - autoArchiveDuration?: number; - locked?: boolean; - messageCount?: number; - memberCount?: number; - member?: ThreadMember; - ownerId?: Snowflake; - - async joinThread() { - await this.session.rest.runMethod( - this.session.rest, - "PUT", - Routes.THREAD_ME(this.id), - ); - } - - async addToThread(guildMemberId: Snowflake) { - await this.session.rest.runMethod( - this.session.rest, - "PUT", - Routes.THREAD_USER(this.id, guildMemberId), - ); - } - - async leaveToThread(guildMemberId: Snowflake) { - await this.session.rest.runMethod( - this.session.rest, - "DELETE", - Routes.THREAD_USER(this.id, guildMemberId), - ); - } - - removeMember(memberId: Snowflake = this.session.botId) { - return ThreadMember.prototype.quitThread.call({ id: this.id, session: this.session }, memberId); - } - - fetchMember(memberId: Snowflake = this.session.botId) { - return ThreadMember.prototype.fetchMember.call({ id: this.id, session: this.session }, memberId); - } - - async fetchMembers(): Promise { - const members = await this.session.rest.runMethod( - this.session.rest, - "GET", - Routes.THREAD_MEMBERS(this.id), - ); - - return members.map((threadMember) => new ThreadMember(this.session, threadMember)); - } -} - -TextChannel.applyTo(ThreadChannel); - -export interface ThreadChannel extends Omit, Omit {} - -/** ChannelFactory */ -export type Channel = - | TextChannel - | VoiceChannel - | DMChannel - | NewsChannel - | ThreadChannel - | StageChannel; - -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!); - case ChannelTypes.GuildStageVoice: - return new StageChannel(session, channel, channel.guild_id!); - default: - if (textBasedChannels.includes(channel.type)) { - return new TextChannel(session, channel); - } - throw new Error("Channel was not implemented"); - } - } -} +/** Types */ +import type { Model } from "./Base.ts"; +import type { Snowflake } from "../Snowflake.ts"; +import type { Session } from "../Session.ts"; +import type { PermissionsOverwrites } from "../Util.ts"; + +/** External from vendor */ +import { + ChannelTypes, + DiscordChannel, + DiscordInvite, + DiscordInviteMetadata, + DiscordListArchivedThreads, + DiscordMessage, + DiscordThreadMember, + DiscordWebhook, + GatewayOpcodes, + TargetTypes, + VideoQualityModes, +} from "../../discordeno/mod.ts"; + +/** Functions and others */ +import { calculateShardId } from "../../discordeno/gateway/calculateShardId.ts"; +import { urlToBase64 } from "../util/urlToBase64.ts"; + +/** Classes and routes */ +import * as Routes from "../Routes.ts"; +import Message, { CreateMessage, EditMessage, ReactionResolvable } from "./Message.ts"; +import Invite from "./Invite.ts"; +import Webhook from "./Webhook.ts"; +import User from "./User.ts"; +import ThreadMember from "./ThreadMember.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; + } + + isStage(): this is StageChannel { + return this.type === ChannelTypes.GuildStageVoice; + } + + toString(): string { + return `<#${this.id}>`; + } +} + +/** TextChannel */ +/** + * 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.GuildVoice, + ChannelTypes.GuildText, +]; + +export type TextBasedChannels = + | ChannelTypes.DM + | ChannelTypes.GroupDm + | ChannelTypes.GuildPrivateThread + | ChannelTypes.GuildPublicThread + | ChannelTypes.GuildNews + | ChannelTypes.GuildVoice + | 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 + */ + // deno-lint-ignore ban-types + static applyTo(klass: Function, ignore: Array = []) { + const methods: Array = [ + "fetchPins", + "createInvite", + "fetchMessages", + "sendTyping", + "pinMessage", + "unpinMessage", + "addReaction", + "removeReaction", + "nukeReactions", + "fetchPins", + "sendMessage", + "editMessage", + "createWebhook", + ]; + + for (const method of methods) { + if (ignore.includes(method)) continue; + + klass.prototype[method] = TextChannel.prototype[method]; + } + } + + 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?: Routes.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?: Routes.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); + } +} + +/** GuildChannel */ +/** + * 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; + rateLimitPerUser?: number; + reason?: string; +} + +/** + * Representations of the objects to edit a guild channel + * @link https://discord.com/developers/docs/resources/channel#modify-channel-json-params-guild-channel + */ +export interface EditGuildChannelOptions { + name?: string; + position?: number; + permissionOverwrites?: PermissionsOverwrites[]; +} + +export interface EditNewsChannelOptions extends EditGuildChannelOptions { + type?: ChannelTypes.GuildNews | ChannelTypes.GuildText; + topic?: string | null; + nfsw?: boolean | null; + parentId?: Snowflake | null; + defaultAutoArchiveDuration?: number | null; +} + +export interface EditGuildTextChannelOptions extends EditNewsChannelOptions { + rateLimitPerUser?: number | null; +} + +export interface EditStageChannelOptions extends EditGuildChannelOptions { + bitrate?: number | null; + rtcRegion?: Snowflake | null; +} + +export interface EditVoiceChannelOptions extends EditStageChannelOptions { + nsfw?: boolean | null; + userLimit?: number | null; + parentId?: Snowflake | null; + videoQualityMode?: VideoQualityModes | null; +} + +/** + * Represents the option object to create a thread channel from a message + * @link https://discord.com/developers/docs/resources/channel#start-thread-from-message + */ +export interface ThreadCreateOptions { + name: string; + autoArchiveDuration?: 60 | 1440 | 4320 | 10080; + rateLimitPerUser?: number; + messageId: Snowflake; +} + +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 edit(options: EditNewsChannelOptions): Promise; + async edit(options: EditStageChannelOptions): Promise; + async edit(options: EditVoiceChannelOptions): Promise; + async edit( + options: EditGuildTextChannelOptions | EditNewsChannelOptions | EditVoiceChannelOptions, + ): Promise { + const channel = await this.session.rest.runMethod( + this.session.rest, + "PATCH", + Routes.CHANNEL(this.id), + { + name: options.name, + type: "type" in options ? options.type : undefined, + position: options.position, + topic: "topic" in options ? options.topic : undefined, + nsfw: "nfsw" in options ? options.nfsw : undefined, + rate_limit_per_user: "rateLimitPerUser" in options ? options.rateLimitPerUser : undefined, + bitrate: "bitrate" in options ? options.bitrate : undefined, + user_limit: "userLimit" in options ? options.userLimit : undefined, + permissions_overwrites: options.permissionOverwrites, + parent_id: "parentId" in options ? options.parentId : undefined, + rtc_region: "rtcRegion" in options ? options.rtcRegion : undefined, + video_quality_mode: "videoQualityMode" in options ? options.videoQualityMode : undefined, + default_auto_archive_duration: "defaultAutoArchiveDuration" in options + ? options.defaultAutoArchiveDuration + : undefined, + }, + ); + return ChannelFactory.from(this.session, channel); + } + + async getArchivedThreads( + options: Routes.ListArchivedThreads & { type: "public" | "private" | "privateJoinedThreads" }, + ) { + let func: (channelId: Snowflake, options: Routes.ListArchivedThreads) => string; + + switch (options.type) { + case "public": + func = Routes.THREAD_ARCHIVED_PUBLIC; + break; + case "private": + func = Routes.THREAD_START_PRIVATE; + break; + case "privateJoinedThreads": + func = Routes.THREAD_ARCHIVED_PRIVATE_JOINED; + break; + } + + const { threads, members, has_more } = await this.session.rest.runMethod( + this.session.rest, + "GET", + func(this.id, options), + ); + + return { + threads: Object.fromEntries( + threads.map((thread) => [thread.id, new ThreadChannel(this.session, thread, this.id)]), + ) as Record, + members: Object.fromEntries( + members.map((threadMember) => [threadMember.id, new ThreadMember(this.session, threadMember)]), + ) as Record, + hasMore: has_more, + }; + } + + async createThread(options: ThreadCreateOptions): Promise { + const thread = await this.session.rest.runMethod( + this.session.rest, + "POST", + "messageId" in options + ? Routes.THREAD_START_PUBLIC(this.id, options.messageId) + : Routes.THREAD_START_PRIVATE(this.id), + { + name: options.name, + auto_archive_duration: options.autoArchiveDuration, + }, + ); + + return new ThreadChannel(this.session, thread, thread.guild_id ?? this.guildId); + } +} + +/** BaseVoiceChannel */ +/** + * @link https://discord.com/developers/docs/topics/gateway#update-voice-state + */ +export interface UpdateVoiceState { + guildId: string; + channelId?: string; + selfMute: boolean; + selfDeaf: boolean; +} + +export abstract class BaseVoiceChannel 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; + this.type = data.type as number; + + if (data.rtc_region) { + this.rtcRegion = data.rtc_region; + } + } + override type: ChannelTypes.GuildVoice | ChannelTypes.GuildStageVoice; + 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, + }, + }); + } +} + +/** DMChannel */ +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 Omit, Omit {} + +/** VoiceChannel */ +export class VoiceChannel extends BaseVoiceChannel { + constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { + super(session, data, guildId); + this.type = data.type as number; + } + override type: ChannelTypes.GuildVoice; +} + +export interface VoiceChannel extends TextChannel, BaseVoiceChannel {} + +TextChannel.applyTo(VoiceChannel); + +/** NewsChannel */ +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 {} + +/** StageChannel */ +export class StageChannel extends BaseVoiceChannel { + constructor(session: Session, data: DiscordChannel, guildId: Snowflake) { + super(session, data, guildId); + this.type = data.type as number; + this.topic = data.topic ? data.topic : undefined; + } + override type: ChannelTypes.GuildStageVoice; + topic?: string; +} + +/** ThreadChannel */ +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; + + if (data.member) { + this.member = new ThreadMember(session, data.member); + } + } + + override type: ChannelTypes.GuildNewsThread | ChannelTypes.GuildPrivateThread | ChannelTypes.GuildPublicThread; + archived?: boolean; + archiveTimestamp?: string; + autoArchiveDuration?: number; + locked?: boolean; + messageCount?: number; + memberCount?: number; + member?: ThreadMember; + ownerId?: Snowflake; + + async joinThread() { + await this.session.rest.runMethod( + this.session.rest, + "PUT", + Routes.THREAD_ME(this.id), + ); + } + + async addToThread(guildMemberId: Snowflake) { + await this.session.rest.runMethod( + this.session.rest, + "PUT", + Routes.THREAD_USER(this.id, guildMemberId), + ); + } + + async leaveToThread(guildMemberId: Snowflake) { + await this.session.rest.runMethod( + this.session.rest, + "DELETE", + Routes.THREAD_USER(this.id, guildMemberId), + ); + } + + removeMember(memberId: Snowflake = this.session.botId) { + return ThreadMember.prototype.quitThread.call({ id: this.id, session: this.session }, memberId); + } + + fetchMember(memberId: Snowflake = this.session.botId) { + return ThreadMember.prototype.fetchMember.call({ id: this.id, session: this.session }, memberId); + } + + async fetchMembers(): Promise { + const members = await this.session.rest.runMethod( + this.session.rest, + "GET", + Routes.THREAD_MEMBERS(this.id), + ); + + return members.map((threadMember) => new ThreadMember(this.session, threadMember)); + } +} + +TextChannel.applyTo(ThreadChannel); + +export interface ThreadChannel extends Omit, Omit {} + +/** ChannelFactory */ +export type Channel = + | TextChannel + | VoiceChannel + | DMChannel + | NewsChannel + | ThreadChannel + | StageChannel; + +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!); + case ChannelTypes.GuildStageVoice: + return new StageChannel(session, channel, channel.guild_id!); + default: + if (textBasedChannels.includes(channel.type)) { + return new TextChannel(session, channel); + } + throw new Error("Channel was not implemented"); + } + } +} diff --git a/packages/biscuit/structures/components/ComponentFactory.ts b/packages/biscuit/structures/components/ComponentFactory.ts index 9b4c51e..395f453 100644 --- a/packages/biscuit/structures/components/ComponentFactory.ts +++ b/packages/biscuit/structures/components/ComponentFactory.ts @@ -1,5 +1,5 @@ import type { Session } from "../../Session.ts"; -import type { DiscordComponent } from "../../../discordeno/mod.ts" +import type { DiscordComponent } from "../../../discordeno/mod.ts"; import type { Component } from "./Component.ts"; import { ButtonStyles, MessageComponentTypes } from "../../../discordeno/mod.ts"; import ActionRow from "./ActionRowComponent.ts"; From c1c358a4118180c1b567a159de2154fcfb4baf08 Mon Sep 17 00:00:00 2001 From: socram03 Date: Fri, 8 Jul 2022 23:00:59 -0400 Subject: [PATCH 4/5] StageInstance & GuildScheduledEvents --- packages/biscuit/Actions.ts | 35 ++++++++++++- .../biscuit/structures/GuildScheduledEvent.ts | 49 +++++++++++++++++++ packages/biscuit/structures/StageInstance.ts | 2 +- 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 packages/biscuit/structures/GuildScheduledEvent.ts diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index 0b7a170..3096fe9 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -32,7 +32,8 @@ import type { DiscordUser, DiscordWebhookUpdate, DiscordInviteCreate, - DiscordInviteDelete + DiscordInviteDelete, + DiscordScheduledEvent } from "../discordeno/mod.ts"; import type { Snowflake } from "./Snowflake.ts"; @@ -42,6 +43,8 @@ import type { Interaction } from "./structures/interactions/InteractionFactory.t import { AutoModerationRule } from "./structures/AutoModerationRule.ts"; import { AutoModerationExecution } from "./structures/AutoModerationExecution.ts"; import { type Channel, ChannelFactory, GuildChannel, ThreadChannel } from "./structures/channels.ts"; +import { StageInstance, type DiscordStageInstance } from "./structures/StageInstance.ts"; +import { ScheduledEvent } from "./structures/GuildScheduledEvent.ts"; import ThreadMember from "./structures/ThreadMember.ts"; import Member from "./structures/Member.ts"; @@ -285,6 +288,30 @@ export const INVITE_DELETE: RawHandler = (session, _shardId session.emit("inviteDelete", { channelId: data.channel_id, guildId: data.guild_id, code: data.code }); } +export const STAGE_INSTANCE_CREATE: RawHandler = (session, _shardId, payload) => { + session.emit("stageInstanceCreate", new StageInstance(session, payload)); +}; + +export const STAGE_INSTANCE_UPDATE: RawHandler = (session, _shardId, payload) => { + session.emit("stageInstanceUpdate", new StageInstance(session, payload)); +}; + +export const STAGE_INSTANCE_DELETE: RawHandler = (session, _shardId, payload) => { + session.emit("stageInstanceDelete", new StageInstance(session, payload)); +}; + +export const GUILD_SCHEDULED_EVENT_CREATE: RawHandler = (session, _shardId, payload) => { + session.emit("guildScheduledEventCreate", new ScheduledEvent(session, payload)) +} + +export const GUILD_SCHEDULED_EVENT_UPDATE: RawHandler = (session, _shardId, payload) => { + session.emit("guildScheduledEventUpdate", new ScheduledEvent(session, payload)) +} + +export const GUILD_SCHEDULED_EVENT_DELETE: RawHandler = (session, _shardId, payload) => { + session.emit("guildScheduledEventDelete", new ScheduledEvent(session, payload)) +} + export const raw: RawHandler = (session, shardId, data) => { session.emit("raw", data, shardId); }; @@ -336,6 +363,12 @@ export interface Events { "autoModerationRuleUpdate": Handler<[AutoModerationRule]>; "autoModerationRuleDelete": Handler<[AutoModerationRule]>; "autoModerationActionExecution":Handler<[AutoModerationExecution]> + "stageInstanceCreate": Handler<[StageInstance]>; + "stageInstanceUpdate": Handler<[StageInstance]>; + "stageInstanceDelete": Handler<[StageInstance]>; + "guildScheduledEventCreate": Handler<[ScheduledEvent]>; + "guildScheduledEventUpdate": Handler<[ScheduledEvent]>; + "guildScheduledEventDelete": Handler<[ScheduledEvent]>; "raw": Handler<[unknown, number]>; "webhooksUpdate": Handler<[{ guildId: Snowflake, channelId: Snowflake }]>; "userUpdate": Handler<[User]>; diff --git a/packages/biscuit/structures/GuildScheduledEvent.ts b/packages/biscuit/structures/GuildScheduledEvent.ts new file mode 100644 index 0000000..b03ed64 --- /dev/null +++ b/packages/biscuit/structures/GuildScheduledEvent.ts @@ -0,0 +1,49 @@ +import type { Model } from "./Base.ts"; +import type { Snowflake } from "../Snowflake.ts"; +import type { Session } from "../Session.ts"; +import { PrivacyLevels } from "./StageInstance.ts"; +import type { + DiscordScheduledEvent, + ScheduledEventStatus, + ScheduledEventEntityType, + DiscordScheduledEventEntityMetadata +} from "../../discordeno/mod.ts"; +import User from "./User.ts"; + +export class ScheduledEvent implements Model { + constructor(session: Session, data: DiscordScheduledEvent) { + this.session = session; + this.id = data.id; + this.guildId = data.guild_id; + this.channelId = data.channel_id; + this.creatorId = data.creator_id ? data.creator_id : undefined; + this.name = data.name; + this.description = data.description; + this.scheduledStartTime = data.scheduled_start_time; + this.scheduledEndTime = data.scheduled_end_time; + this.privacyLevel = PrivacyLevels.GuildOnly; + this.status = data.status; + this.entityType = data.entity_type; + this.entityMetadata = data.entity_metadata ? data.entity_metadata : undefined; + this.creator = data.creator ? new User(session, data.creator) : undefined; + this.userCount = data.user_count; + this.image = data.image ? data.image : undefined; + } + session: Session; + id: Snowflake; + guildId: Snowflake; + channelId: Snowflake | null; + creatorId?: Snowflake; + name: string; + description?: string; + scheduledStartTime: string; + scheduledEndTime: string | null; + privacyLevel: PrivacyLevels; + status: ScheduledEventStatus; + entityType: ScheduledEventEntityType; + entityMetadata?: DiscordScheduledEventEntityMetadata; + creator?: User; + userCount?: number; + image?: string; + +} \ No newline at end of file diff --git a/packages/biscuit/structures/StageInstance.ts b/packages/biscuit/structures/StageInstance.ts index b087228..5c74c61 100644 --- a/packages/biscuit/structures/StageInstance.ts +++ b/packages/biscuit/structures/StageInstance.ts @@ -4,7 +4,7 @@ import type { Snowflake } from "../Snowflake.ts"; import type { DiscordStageInstance as DiscordAutoClosingStageInstance } from "../../discordeno/mod.ts"; import * as Routes from "../Routes.ts"; -interface DiscordStageInstance extends DiscordAutoClosingStageInstance { +export interface DiscordStageInstance extends DiscordAutoClosingStageInstance { privacy_level: PrivacyLevels; discoverable_disabled: boolean; guild_scheduled_event_id: Snowflake; From 22b27c0874e3ea82b083cd84a2cab34b2361f914 Mon Sep 17 00:00:00 2001 From: socram03 Date: Fri, 8 Jul 2022 23:10:49 -0400 Subject: [PATCH 5/5] fix: fmt --- packages/biscuit/Actions.ts | 60 +++-- packages/biscuit/structures/Application.ts | 213 +++++++++--------- .../biscuit/structures/GuildScheduledEvent.ts | 7 +- packages/biscuit/structures/Invite.ts | 20 +- 4 files changed, 165 insertions(+), 135 deletions(-) diff --git a/packages/biscuit/Actions.ts b/packages/biscuit/Actions.ts index 3096fe9..b709299 100644 --- a/packages/biscuit/Actions.ts +++ b/packages/biscuit/Actions.ts @@ -16,6 +16,8 @@ import type { DiscordIntegration, DiscordIntegrationDelete, DiscordInteraction, + DiscordInviteCreate, + DiscordInviteDelete, DiscordMemberWithUser, DiscordMessage, DiscordMessageDelete, @@ -25,15 +27,15 @@ import type { DiscordMessageReactionRemoveEmoji, DiscordReady, DiscordRole, + DiscordScheduledEvent, + DiscordScheduledEventUserAdd, + DiscordScheduledEventUserRemove, // DiscordThreadMemberUpdate, // DiscordThreadMembersUpdate, DiscordThreadListSync, DiscordTypingStart, DiscordUser, DiscordWebhookUpdate, - DiscordInviteCreate, - DiscordInviteDelete, - DiscordScheduledEvent } from "../discordeno/mod.ts"; import type { Snowflake } from "./Snowflake.ts"; @@ -43,7 +45,7 @@ import type { Interaction } from "./structures/interactions/InteractionFactory.t import { AutoModerationRule } from "./structures/AutoModerationRule.ts"; import { AutoModerationExecution } from "./structures/AutoModerationExecution.ts"; import { type Channel, ChannelFactory, GuildChannel, ThreadChannel } from "./structures/channels.ts"; -import { StageInstance, type DiscordStageInstance } from "./structures/StageInstance.ts"; +import { type DiscordStageInstance, StageInstance } from "./structures/StageInstance.ts"; import { ScheduledEvent } from "./structures/GuildScheduledEvent.ts"; import ThreadMember from "./structures/ThreadMember.ts"; @@ -53,7 +55,7 @@ import User from "./structures/User.ts"; import Integration from "./structures/Integration.ts"; import Guild from "./structures/guilds/Guild.ts"; import InteractionFactory from "./structures/interactions/InteractionFactory.ts"; -import { NewInviteCreate, InviteCreate } from "./structures/Invite.ts"; +import { InviteCreate, NewInviteCreate } from "./structures/Invite.ts"; export type RawHandler = (...args: [Session, number, T]) => void; export type Handler = (...args: T) => unknown; @@ -282,35 +284,59 @@ export const MESSAGE_REACTION_REMOVE_EMOJI: RawHandler = (session, _shardId, invite) => { session.emit("inviteCreate", NewInviteCreate(session, invite)); -} +}; export const INVITE_DELETE: RawHandler = (session, _shardId, data) => { session.emit("inviteDelete", { channelId: data.channel_id, guildId: data.guild_id, code: data.code }); -} +}; -export const STAGE_INSTANCE_CREATE: RawHandler = (session, _shardId, payload) => { +export const STAGE_INSTANCE_CREATE: RawHandler = (session, _shardId, payload) => { session.emit("stageInstanceCreate", new StageInstance(session, payload)); }; -export const STAGE_INSTANCE_UPDATE: RawHandler = (session, _shardId, payload) => { +export const STAGE_INSTANCE_UPDATE: RawHandler = (session, _shardId, payload) => { session.emit("stageInstanceUpdate", new StageInstance(session, payload)); }; -export const STAGE_INSTANCE_DELETE: RawHandler = (session, _shardId, payload) => { +export const STAGE_INSTANCE_DELETE: RawHandler = (session, _shardId, payload) => { session.emit("stageInstanceDelete", new StageInstance(session, payload)); }; export const GUILD_SCHEDULED_EVENT_CREATE: RawHandler = (session, _shardId, payload) => { - session.emit("guildScheduledEventCreate", new ScheduledEvent(session, payload)) -} + session.emit("guildScheduledEventCreate", new ScheduledEvent(session, payload)); +}; export const GUILD_SCHEDULED_EVENT_UPDATE: RawHandler = (session, _shardId, payload) => { - session.emit("guildScheduledEventUpdate", new ScheduledEvent(session, payload)) -} + session.emit("guildScheduledEventUpdate", new ScheduledEvent(session, payload)); +}; export const GUILD_SCHEDULED_EVENT_DELETE: RawHandler = (session, _shardId, payload) => { - session.emit("guildScheduledEventDelete", new ScheduledEvent(session, payload)) -} + session.emit("guildScheduledEventDelete", new ScheduledEvent(session, payload)); +}; + +export const GUILD_SCHEDULED_EVENT_USER_ADD: RawHandler = ( + session, + _shardId, + payload, +) => { + session.emit("guildScheduledEventUserAdd", { + scheduledEventId: payload.guild_scheduled_event_id, + userId: payload.user_id, + guildId: payload.guild_id, + }); +}; + +export const GUILD_SCHEDULED_EVENT_USER_REMOVE: RawHandler = ( + session, + _shardId, + payload, +) => { + session.emit("guildScheduledEventUserRemove", { + scheduledEventId: payload.guild_scheduled_event_id, + userId: payload.user_id, + guildId: payload.guild_id, + }); +}; export const raw: RawHandler = (session, shardId, data) => { session.emit("raw", data, shardId); @@ -369,6 +395,8 @@ export interface Events { "guildScheduledEventCreate": Handler<[ScheduledEvent]>; "guildScheduledEventUpdate": Handler<[ScheduledEvent]>; "guildScheduledEventDelete": Handler<[ScheduledEvent]>; + "guildScheduledEventUserAdd": Handler<[{scheduledEventId: Snowflake, userId: Snowflake, guildId: Snowflake}]> + "guildScheduledEventUserRemove": Handler<[{scheduledEventId: Snowflake, userId: Snowflake, guildId: Snowflake}]> "raw": Handler<[unknown, number]>; "webhooksUpdate": Handler<[{ guildId: Snowflake, channelId: Snowflake }]>; "userUpdate": Handler<[User]>; diff --git a/packages/biscuit/structures/Application.ts b/packages/biscuit/structures/Application.ts index 0fed63b..359a7aa 100644 --- a/packages/biscuit/structures/Application.ts +++ b/packages/biscuit/structures/Application.ts @@ -1,107 +1,106 @@ -import { Model } from "./Base.ts"; -import type { Snowflake } from "../Snowflake.ts"; -import type { Session } from "../Session.ts"; -import { - DiscordApplication, - TeamMembershipStates, - DiscordInstallParams, - DiscordUser, - DiscordTeam -} from "../../discordeno/mod.ts"; -import User from "./User.ts"; - -type SummaryDeprecated = "" - -export interface Team { - /** a hash of the image of the team's icon */ - icon?: string; - /** the unique id of the team */ - id: string; - /** the members of the team */ - members: TeamMember[]; - /** user id of the current team owner */ - ownerUserId: string; - /** team name */ - name: string; -} - -export interface TeamMember { - /** the user's membership state on the team */ - membershipState: TeamMembershipStates; - permissions: "*"[]; - - teamId: string; - - user: Partial & Pick -} - -// NewTeam create a new Team object for discord applications -export function NewTeam(session: Session, data: DiscordTeam): Team { - return { - icon: data.icon ? data.icon : undefined, - id: data.id, - members: data.members.map(member => { - return { - membershipState: member.membership_state, - permissions: member.permissions, - teamId: member.team_id, - user: new User(session, member.user) - } - }), - ownerUserId: data.owner_user_id, - name: data.name, - } -} -/** - * @link https://discord.com/developers/docs/resources/application#application-object - */ -export class Application implements Model { - - constructor(session: Session, data: DiscordApplication) { - this.id = data.id; - this.session = session; - - this.name = data.name; - this.icon = data.icon || undefined; - this.description = data.description; - this.rpcOrigins = data.rpc_origins; - this.botPublic = data.bot_public; - this.botRequireCodeGrant = data.bot_require_code_grant; - this.termsOfServiceURL = data.terms_of_service_url; - this.privacyPolicyURL = data.privacy_policy_url; - this.owner = data.owner ? new User(session, data.owner as DiscordUser) : undefined; - this.summary = ""; - this.verifyKey = data.verify_key; - this.team = data.team ? NewTeam(session, data.team) : undefined; - this.guildId = data.guild_id; - this.coverImage = data.cover_image; - this.tags = data.tags; - this.installParams = data.install_params; - this.customInstallURL = data.custom_install_url; - } - - readonly session: Session; - id: Snowflake; - name: string; - icon?: string; - description: string; - rpcOrigins?: string[]; - botPublic: boolean; - botRequireCodeGrant: boolean; - termsOfServiceURL?: string; - privacyPolicyURL?: string; - owner?: Partial; - summary: SummaryDeprecated; - verifyKey: string; - team?: Team; - guildId?: Snowflake; - primarySkuId?: Snowflake; - slug?: string; - coverImage?: string; - flags?: number; - tags?: string[]; - installParams?: DiscordInstallParams; - customInstallURL?: string; -} - -export default Application; \ No newline at end of file +import { Model } from "./Base.ts"; +import type { Snowflake } from "../Snowflake.ts"; +import type { Session } from "../Session.ts"; +import { + DiscordApplication, + DiscordInstallParams, + DiscordTeam, + DiscordUser, + TeamMembershipStates, +} from "../../discordeno/mod.ts"; +import User from "./User.ts"; + +type SummaryDeprecated = ""; + +export interface Team { + /** a hash of the image of the team's icon */ + icon?: string; + /** the unique id of the team */ + id: string; + /** the members of the team */ + members: TeamMember[]; + /** user id of the current team owner */ + ownerUserId: string; + /** team name */ + name: string; +} + +export interface TeamMember { + /** the user's membership state on the team */ + membershipState: TeamMembershipStates; + permissions: "*"[]; + + teamId: string; + + user: Partial & Pick; +} + +// NewTeam create a new Team object for discord applications +export function NewTeam(session: Session, data: DiscordTeam): Team { + return { + icon: data.icon ? data.icon : undefined, + id: data.id, + members: data.members.map((member) => { + return { + membershipState: member.membership_state, + permissions: member.permissions, + teamId: member.team_id, + user: new User(session, member.user), + }; + }), + ownerUserId: data.owner_user_id, + name: data.name, + }; +} +/** + * @link https://discord.com/developers/docs/resources/application#application-object + */ +export class Application implements Model { + constructor(session: Session, data: DiscordApplication) { + this.id = data.id; + this.session = session; + + this.name = data.name; + this.icon = data.icon || undefined; + this.description = data.description; + this.rpcOrigins = data.rpc_origins; + this.botPublic = data.bot_public; + this.botRequireCodeGrant = data.bot_require_code_grant; + this.termsOfServiceURL = data.terms_of_service_url; + this.privacyPolicyURL = data.privacy_policy_url; + this.owner = data.owner ? new User(session, data.owner as DiscordUser) : undefined; + this.summary = ""; + this.verifyKey = data.verify_key; + this.team = data.team ? NewTeam(session, data.team) : undefined; + this.guildId = data.guild_id; + this.coverImage = data.cover_image; + this.tags = data.tags; + this.installParams = data.install_params; + this.customInstallURL = data.custom_install_url; + } + + readonly session: Session; + id: Snowflake; + name: string; + icon?: string; + description: string; + rpcOrigins?: string[]; + botPublic: boolean; + botRequireCodeGrant: boolean; + termsOfServiceURL?: string; + privacyPolicyURL?: string; + owner?: Partial; + summary: SummaryDeprecated; + verifyKey: string; + team?: Team; + guildId?: Snowflake; + primarySkuId?: Snowflake; + slug?: string; + coverImage?: string; + flags?: number; + tags?: string[]; + installParams?: DiscordInstallParams; + customInstallURL?: string; +} + +export default Application; diff --git a/packages/biscuit/structures/GuildScheduledEvent.ts b/packages/biscuit/structures/GuildScheduledEvent.ts index b03ed64..d7f184a 100644 --- a/packages/biscuit/structures/GuildScheduledEvent.ts +++ b/packages/biscuit/structures/GuildScheduledEvent.ts @@ -4,9 +4,9 @@ import type { Session } from "../Session.ts"; import { PrivacyLevels } from "./StageInstance.ts"; import type { DiscordScheduledEvent, - ScheduledEventStatus, + DiscordScheduledEventEntityMetadata, ScheduledEventEntityType, - DiscordScheduledEventEntityMetadata + ScheduledEventStatus, } from "../../discordeno/mod.ts"; import User from "./User.ts"; @@ -45,5 +45,4 @@ export class ScheduledEvent implements Model { creator?: User; userCount?: number; image?: string; - -} \ No newline at end of file +} diff --git a/packages/biscuit/structures/Invite.ts b/packages/biscuit/structures/Invite.ts index 1aa857d..4bf1b61 100644 --- a/packages/biscuit/structures/Invite.ts +++ b/packages/biscuit/structures/Invite.ts @@ -1,15 +1,15 @@ import type { Session } from "../Session.ts"; import type { Snowflake } from "../Snowflake.ts"; import type { + DiscordApplication, DiscordChannel, DiscordInvite, + DiscordInviteCreate, DiscordMemberWithUser, DiscordScheduledEventEntityMetadata, ScheduledEventEntityType, ScheduledEventPrivacyLevel, ScheduledEventStatus, - DiscordApplication, - DiscordInviteCreate } from "../../discordeno/mod.ts"; import { TargetTypes } from "../../discordeno/mod.ts"; import { GuildChannel } from "./channels.ts"; @@ -75,10 +75,12 @@ export function NewInviteCreate(session: Session, invite: DiscordInviteCreate): maxUses: invite.max_uses, targetType: invite.target_type, targetUser: invite.target_user ? new User(session, invite.target_user) : undefined, - targetApplication: invite.target_application ? new Application(session, invite.target_application as DiscordApplication) : undefined, + targetApplication: invite.target_application + ? new Application(session, invite.target_application as DiscordApplication) + : undefined, temporary: invite.temporary, - uses: invite.uses - } + uses: invite.uses, + }; } /** @@ -95,9 +97,11 @@ export class Invite { this.expiresAt = data.expires_at ? Number.parseInt(data.expires_at) : undefined; this.inviter = data.inviter ? new User(session, data.inviter) : undefined; this.targetUser = data.target_user ? new User(session, data.target_user) : undefined; - this.targetApplication = data.target_application ? new Application(session, data.target_application as DiscordApplication) : undefined; + this.targetApplication = data.target_application + ? new Application(session, data.target_application as DiscordApplication) + : undefined; this.targetType = data.target_type; - + if (data.channel) { const guildId = (data.guild && data.guild?.id) ? data.guild.id : ""; this.channel = new GuildChannel(session, data.channel as DiscordChannel, guildId); @@ -157,7 +161,7 @@ export class Invite { channel?: Partial; stageInstance?: InviteStageInstance; guildScheduledEvent?: InviteScheduledEvent; - targetApplication?: Partial + targetApplication?: Partial; async delete(): Promise { await Guild.prototype.deleteInvite.call(this.guild, this.code);