From a1f7c47b7ab41cfd5fc89ac40cc0b045b935d7aa Mon Sep 17 00:00:00 2001 From: Skillz4Killz <23035000+Skillz4Killz@users.noreply.github.com> Date: Tue, 16 Nov 2021 21:15:05 +0000 Subject: [PATCH] feat: guild scheduled events --- src/bot.ts | 49 +++++- .../GUILD_SCHEDULED_EVENT_CREATE.ts | 9 + .../GUILD_SCHEDULED_EVENT_DELETE.ts | 9 + .../GUILD_SCHEDULED_EVENT_UPDATE.ts | 9 + .../GUILD_SCHEDULED_EVENT_USER_ADD.ts | 14 ++ .../GUILD_SCHEDULED_EVENT_USER_REMOVE.ts | 14 ++ src/handlers/mod.ts | 10 ++ .../scheduledEvents/createScheduledEvent.ts | 31 ++++ .../scheduledEvents/deleteScheduledEvent.ts | 10 ++ .../scheduledEvents/editScheduledEvent.ts | 37 ++++ .../scheduledEvents/getScheduledEvent.ts | 13 ++ .../scheduledEvents/getScheduledEventUsers.ts | 58 +++++++ .../scheduledEvents/getScheduledEvents.ts | 23 +++ src/helpers/invites/getInvite.ts | 1 + src/helpers/mod.ts | 12 ++ src/rest/processQueue.ts | 1 + src/transformers/auditlogEntry.ts | 3 + src/transformers/scheduledEvent.ts | 69 ++++++++ src/types/auditLog/auditLogChange.ts | 7 +- src/types/auditLog/auditLogEvents.ts | 3 + src/types/gateway/gatewayIntents.ts | 9 + src/types/guilds/scheduledEvents.ts | 159 ++++++++++++++++++ src/types/invites/getInvite.ts | 2 + src/types/invites/invite.ts | 3 + .../permissions/bitwisePermissionFlags.ts | 2 + src/util/constants.ts | 3 + .../scheduledEvents/createScheduledEvent.ts | 36 ++++ tests/mod.ts | 141 ++++++++++++++-- 28 files changed, 724 insertions(+), 13 deletions(-) create mode 100644 src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_CREATE.ts create mode 100644 src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_DELETE.ts create mode 100644 src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_UPDATE.ts create mode 100644 src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_ADD.ts create mode 100644 src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_REMOVE.ts create mode 100644 src/helpers/guilds/scheduledEvents/createScheduledEvent.ts create mode 100644 src/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts create mode 100644 src/helpers/guilds/scheduledEvents/editScheduledEvent.ts create mode 100644 src/helpers/guilds/scheduledEvents/getScheduledEvent.ts create mode 100644 src/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts create mode 100644 src/helpers/guilds/scheduledEvents/getScheduledEvents.ts create mode 100644 src/transformers/scheduledEvent.ts create mode 100644 src/types/guilds/scheduledEvents.ts create mode 100644 tests/helpers/guilds/scheduledEvents/createScheduledEvent.ts diff --git a/src/bot.ts b/src/bot.ts index c60585f39..61604162c 100644 --- a/src/bot.ts +++ b/src/bot.ts @@ -92,6 +92,8 @@ import { transformAuditlogEntry } from "./transformers/auditlogEntry.ts"; import { transformApplicationCommandPermission } from "./transformers/applicationCommandPermission.ts"; import { StatusUpdate } from "./types/gateway/statusUpdate.ts"; import { calculateBits, calculatePermissions } from "./util/permissions.ts"; +import { transformScheduledEvent } from "./transformers/scheduledEvent.ts"; +import { DiscordenoScheduledEvent } from "./transformers/scheduledEvent.ts"; type CacheOptions = | { @@ -133,6 +135,11 @@ export function createEventHandlers(events: Partial): EventHandle return { debug: events.debug ?? ignore, + scheduledEventCreate: events.scheduledEventCreate ?? ignore, + scheduledEventUpdate: events.scheduledEventUpdate ?? ignore, + scheduledEventDelete: events.scheduledEventDelete ?? ignore, + scheduledEventUserAdd: events.scheduledEventUserAdd ?? ignore, + scheduledEventUserRemove: events.scheduledEventUserRemove ?? ignore, ready: events.ready ?? ignore, dispatchRequirements: events.dispatchRequirements ?? ignore, integrationCreate: events.integrationCreate ?? ignore, @@ -478,6 +485,7 @@ export interface Helpers { createGuildTemplate: typeof helpers.createGuildTemplate; createInvite: typeof helpers.createInvite; createRole: typeof helpers.createRole; + createScheduledEvent: typeof helpers.createScheduledEvent; createSlashCommand: typeof helpers.createSlashCommand; createStageInstance: typeof helpers.createStageInstance; createWebhook: typeof helpers.createWebhook; @@ -491,6 +499,7 @@ export interface Helpers { deleteMessage: typeof helpers.deleteMessage; deleteMessages: typeof helpers.deleteMessages; deleteRole: typeof helpers.deleteRole; + deleteScheduledEvent: typeof helpers.deleteScheduledEvent; deleteSlashCommand: typeof helpers.deleteSlashCommand; deleteSlashResponse: typeof helpers.deleteSlashResponse; deleteStageInstance: typeof helpers.deleteStageInstance; @@ -510,6 +519,7 @@ export interface Helpers { editMember: typeof helpers.editMember; editMessage: typeof helpers.editMessage; editRole: typeof helpers.editRole; + editScheduledEvent: typeof helpers.editScheduledEvent; editSlashResponse: typeof helpers.editSlashResponse; editSlashCommandPermissions: typeof helpers.editSlashCommandPermissions; editWebhook: typeof helpers.editWebhook; @@ -547,6 +557,9 @@ export interface Helpers { getPruneCount: typeof helpers.getPruneCount; getReactions: typeof helpers.getReactions; getRoles: typeof helpers.getRoles; + getScheduledEvent: typeof helpers.getScheduledEvent; + getScheduledEvents: typeof helpers.getScheduledEvents; + getScheduledEventUsers: typeof helpers.getScheduledEventUsers; getSlashCommand: typeof helpers.getSlashCommand; getSlashCommandPermission: typeof helpers.getSlashCommandPermission; getSlashCommandPermissions: typeof helpers.getSlashCommandPermissions; @@ -644,6 +657,7 @@ export function createBaseHelpers(options: Partial) { createGuildTemplate: options.createGuildTemplate || helpers.createGuildTemplate, createInvite: options.createInvite || helpers.createInvite, createRole: options.createRole || helpers.createRole, + createScheduledEvent: options.createScheduledEvent || helpers.createScheduledEvent, createSlashCommand: options.createSlashCommand || helpers.createSlashCommand, createStageInstance: options.createStageInstance || helpers.createStageInstance, createWebhook: options.createWebhook || helpers.createWebhook, @@ -657,6 +671,7 @@ export function createBaseHelpers(options: Partial) { deleteMessage: options.deleteMessage || helpers.deleteMessage, deleteMessages: options.deleteMessages || helpers.deleteMessages, deleteRole: options.deleteRole || helpers.deleteRole, + deleteScheduledEvent: options.deleteScheduledEvent || helpers.deleteScheduledEvent, deleteSlashCommand: options.deleteSlashCommand || helpers.deleteSlashCommand, deleteSlashResponse: options.deleteSlashResponse || helpers.deleteSlashResponse, deleteStageInstance: options.deleteStageInstance || helpers.deleteStageInstance, @@ -676,6 +691,7 @@ export function createBaseHelpers(options: Partial) { editMember: options.editMember || helpers.editMember, editMessage: options.editMessage || helpers.editMessage, editRole: options.editRole || helpers.editRole, + editScheduledEvent: options.editScheduledEvent || helpers.editScheduledEvent, editSlashResponse: options.editSlashResponse || helpers.editSlashResponse, editSlashCommandPermissions: options.editSlashCommandPermissions || helpers.editSlashCommandPermissions, editWebhook: options.editWebhook || helpers.editWebhook, @@ -713,6 +729,9 @@ export function createBaseHelpers(options: Partial) { getPruneCount: options.getPruneCount || helpers.getPruneCount, getReactions: options.getReactions || helpers.getReactions, getRoles: options.getRoles || helpers.getRoles, + getScheduledEvent: options.getScheduledEvent || helpers.getScheduledEvent, + getScheduledEventUsers: options.getScheduledEventUsers || helpers.getScheduledEventUsers, + getScheduledEvents: options.getScheduledEvents || helpers.getScheduledEvents, getSlashCommand: options.getSlashCommand || helpers.getSlashCommand, getSlashCommandPermission: options.getSlashCommandPermission || helpers.getSlashCommandPermission, getSlashCommandPermissions: options.getSlashCommandPermissions || helpers.getSlashCommandPermissions, @@ -803,6 +822,7 @@ export interface Transformers { webhook: typeof transformWebhook; auditlogEntry: typeof transformAuditlogEntry; applicationCommandPermission: typeof transformApplicationCommandPermission; + scheduledEvent: typeof transformScheduledEvent; } export function createTransformers(options: Partial) { @@ -829,7 +849,8 @@ export function createTransformers(options: Partial) { snowflake: options.snowflake || snowflakeToBigint, webhook: options.webhook || transformWebhook, auditlogEntry: options.auditlogEntry || transformAuditlogEntry, - applicationCommandPermission: transformApplicationCommandPermission, + applicationCommandPermission: options.applicationCommandPermission || transformApplicationCommandPermission, + scheduledEvent: options.scheduledEvent || transformScheduledEvent, }; } @@ -939,6 +960,27 @@ export interface GatewayManager { export interface EventHandlers { debug: (text: string, ...args: any[]) => unknown; + scheduledEventCreate: (bot: Bot, event: DiscordenoScheduledEvent) => unknown; + scheduledEventUpdate: (bot: Bot, event: DiscordenoScheduledEvent) => unknown; + scheduledEventDelete: (bot: Bot, event: DiscordenoScheduledEvent) => unknown; + /** Sent when a user has subscribed to a guild scheduled event. EXPERIMENTAL! */ + scheduledEventUserAdd: ( + bot: Bot, + payload: { + guildScheduledEventId: bigint; + guildId: bigint; + userId: bigint; + } + ) => unknown; + /** Sent when a user has unsubscribed to a guild scheduled event. EXPERIMENTAL! */ + scheduledEventUserRemove: ( + bot: Bot, + payload: { + guildScheduledEventId: bigint; + guildId: bigint; + userId: bigint; + } + ) => unknown; ready: ( bot: Bot, payload: { @@ -1156,6 +1198,11 @@ export interface BotGatewayHandlerOptions { GUILD_ROLE_CREATE: typeof handlers.handleGuildRoleCreate; GUILD_ROLE_DELETE: typeof handlers.handleGuildRoleDelete; GUILD_ROLE_UPDATE: typeof handlers.handleGuildRoleUpdate; + GUILD_SCHEDULED_EVENT_CREATE: typeof handlers.handleGuildScheduledEventCreate; + GUILD_SCHEDULED_EVENT_UPDATE: typeof handlers.handleGuildScheduledEventUpdate; + GUILD_SCHEDULED_EVENT_DELETE: typeof handlers.handleGuildScheduledEventDelete; + GUILD_SCHEDULED_EVENT_USER_ADD: typeof handlers.handleGuildScheduledEventUserAdd; + GUILD_SCHEDULED_EVENT_USER_REMOVE: typeof handlers.handleGuildScheduledEventUserRemove; GUILD_UPDATE: typeof handlers.handleGuildUpdate; INTERACTION_CREATE: typeof handlers.handleInteractionCreate; INVITE_CREATE: typeof handlers.handleInviteCreate; diff --git a/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_CREATE.ts b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_CREATE.ts new file mode 100644 index 000000000..f6dd9699b --- /dev/null +++ b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_CREATE.ts @@ -0,0 +1,9 @@ +import { ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; +import type { Bot } from "../../../bot.ts"; +import type { DiscordGatewayPayload } from "../../../types/gateway/gatewayPayload.ts"; +import { SnakeCasedPropertiesDeep } from "../../../types/util.ts"; + +export function handleGuildScheduledEventCreate(bot: Bot, data: DiscordGatewayPayload, shardId: number) { + const payload = data.d as SnakeCasedPropertiesDeep; + bot.events.scheduledEventCreate(bot, bot.transformers.scheduledEvent(bot, payload)); +} diff --git a/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_DELETE.ts b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_DELETE.ts new file mode 100644 index 000000000..321ebd721 --- /dev/null +++ b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_DELETE.ts @@ -0,0 +1,9 @@ +import { ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; +import type { Bot } from "../../../bot.ts"; +import type { DiscordGatewayPayload } from "../../../types/gateway/gatewayPayload.ts"; +import { SnakeCasedPropertiesDeep } from "../../../types/util.ts"; + +export function handleGuildScheduledEventDelete(bot: Bot, data: DiscordGatewayPayload) { + const payload = data.d as SnakeCasedPropertiesDeep; + bot.events.scheduledEventDelete(bot, bot.transformers.scheduledEvent(bot, payload)); +} diff --git a/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_UPDATE.ts b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_UPDATE.ts new file mode 100644 index 000000000..cf11bae1d --- /dev/null +++ b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_UPDATE.ts @@ -0,0 +1,9 @@ +import { ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; +import type { Bot } from "../../../bot.ts"; +import type { DiscordGatewayPayload } from "../../../types/gateway/gatewayPayload.ts"; +import { SnakeCasedPropertiesDeep } from "../../../types/util.ts"; + +export function handleGuildScheduledEventUpdate(bot: Bot, data: DiscordGatewayPayload) { + const payload = data.d as SnakeCasedPropertiesDeep; + bot.events.scheduledEventUpdate(bot, bot.transformers.scheduledEvent(bot, payload)); +} diff --git a/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_ADD.ts b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_ADD.ts new file mode 100644 index 000000000..06cf75d76 --- /dev/null +++ b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_ADD.ts @@ -0,0 +1,14 @@ +import type { Bot } from "../../../bot.ts"; +import type { DiscordGatewayPayload } from "../../../types/gateway/gatewayPayload.ts"; +import { ScheduledEventUserAdd } from "../../../types/guilds/scheduledEvents.ts"; +import { SnakeCasedPropertiesDeep } from "../../../types/util.ts"; + +export function handleGuildScheduledEventUserAdd(bot: Bot, data: DiscordGatewayPayload) { + const payload = data.d as SnakeCasedPropertiesDeep; + + return bot.events.scheduledEventUserAdd(bot, { + guildScheduledEventId: bot.transformers.snowflake(payload.guild_scheduled_event_id), + userId: bot.transformers.snowflake(payload.user_id), + guildId: bot.transformers.snowflake(payload.guild_id), + }); +} diff --git a/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_REMOVE.ts b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_REMOVE.ts new file mode 100644 index 000000000..5b4b1cf1b --- /dev/null +++ b/src/handlers/guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_REMOVE.ts @@ -0,0 +1,14 @@ +import type { Bot } from "../../../bot.ts"; +import type { DiscordGatewayPayload } from "../../../types/gateway/gatewayPayload.ts"; +import { ScheduledEventUserRemove } from "../../../types/guilds/scheduledEvents.ts"; +import { SnakeCasedPropertiesDeep } from "../../../types/util.ts"; + +export function handleGuildScheduledEventUserRemove(bot: Bot, data: DiscordGatewayPayload) { + const payload = data.d as SnakeCasedPropertiesDeep; + + return bot.events.scheduledEventUserRemove(bot, { + guildScheduledEventId: bot.transformers.snowflake(payload.guild_scheduled_event_id), + userId: bot.transformers.snowflake(payload.user_id), + guildId: bot.transformers.snowflake(payload.guild_id), + }); +} diff --git a/src/handlers/mod.ts b/src/handlers/mod.ts index 7535358e6..f02adcbb1 100644 --- a/src/handlers/mod.ts +++ b/src/handlers/mod.ts @@ -46,6 +46,11 @@ import { handleVoiceServerUpdate } from "./voice/VOICE_SERVER_UPDATE.ts"; import { handleVoiceStateUpdate } from "./voice/VOICE_STATE_UPDATE.ts"; import { handleWebhooksUpdate } from "./webhooks/WEBHOOKS_UPDATE.ts"; import { handleGuildLoaded } from "./guilds/GUILD_LOADED_DD.ts"; +import { handleGuildScheduledEventCreate } from "./guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_CREATE.ts"; +import { handleGuildScheduledEventDelete } from "./guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_DELETE.ts"; +import { handleGuildScheduledEventUpdate } from "./guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_UPDATE.ts"; +import { handleGuildScheduledEventUserAdd } from "./guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_ADD.ts"; +import { handleGuildScheduledEventUserRemove } from "./guilds/scheduledEvents/GUILD_SCHEDULED_EVENT_USER_REMOVE.ts"; export { handleChannelCreate, @@ -67,6 +72,11 @@ export { handleGuildRoleDelete, handleGuildRoleUpdate, handleGuildUpdate, + handleGuildScheduledEventCreate, + handleGuildScheduledEventDelete, + handleGuildScheduledEventUpdate, + handleGuildScheduledEventUserAdd, + handleGuildScheduledEventUserRemove, handleIntegrationCreate, handleIntegrationDelete, handleIntegrationUpdate, diff --git a/src/helpers/guilds/scheduledEvents/createScheduledEvent.ts b/src/helpers/guilds/scheduledEvents/createScheduledEvent.ts new file mode 100644 index 000000000..a09c94237 --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/createScheduledEvent.ts @@ -0,0 +1,31 @@ +import { Bot } from "../../../bot.ts"; +import { CreateScheduledEvent, ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; + +/** Create a guild scheduled event in the guild. A guild can have a maximum of 100 events with `SCHEDULED` or `ACTIVE` status at any time. */ +export async function createScheduledEvent(bot: Bot, guildId: bigint, options: CreateScheduledEvent) { + // TODO: validate name length + // TODO: validate description length + // TODO: validate location length + // TODO: validate speaker ids length + + const event = await bot.rest.runMethod( + bot.rest, + "post", + bot.constants.endpoints.GUILD_SCHEDULED_EVENTS(guildId), + { + channel_id: options.channelId?.toString(), + entity_metadata: + options.location || options.speakerIds + ? { location: options.location, speakerIds: options.speakerIds?.map((id) => id.toString()) } + : undefined, + name: options.name, + description: options.description, + scheduled_start_time: new Date(options.scheduledStartTime).toISOString(), + scheduled_end_time: options.scheduledEndTime ? new Date(options.scheduledEndTime).toISOString() : undefined, + privacy_level: options.privacyLevel, + entity_type: options.entityType, + } + ); + + return bot.transformers.scheduledEvent(bot, event); +} diff --git a/src/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts b/src/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts new file mode 100644 index 000000000..b440e4351 --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/deleteScheduledEvent.ts @@ -0,0 +1,10 @@ +import { Bot } from "../../../bot.ts"; + +/** Delete a scheduled event. */ +export async function deleteScheduledEvent(bot: Bot, guildId: bigint, eventId: bigint) { + await bot.rest.runMethod( + bot.rest, + "delete", + bot.constants.endpoints.GUILD_SCHEDULED_EVENT(guildId, eventId) + ); +} diff --git a/src/helpers/guilds/scheduledEvents/editScheduledEvent.ts b/src/helpers/guilds/scheduledEvents/editScheduledEvent.ts new file mode 100644 index 000000000..f78d03c0a --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/editScheduledEvent.ts @@ -0,0 +1,37 @@ +import { Bot } from "../../../bot.ts"; +import { EditScheduledEvent, ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; + +/** Modify a guild scheduled event. To start or end an event, use this endpoint to modify the event's status. */ +export async function editScheduledEvent( + bot: Bot, + guildId: bigint, + eventId: bigint, + options: Partial +) { + // TODO: validate name length + // TODO: validate description length + // TODO: validate location length + // TODO: validate speaker ids length + + const event = await bot.rest.runMethod( + bot.rest, + "patch", + bot.constants.endpoints.GUILD_SCHEDULED_EVENT(guildId, eventId), + { + channel_id: options.channelId?.toString(), + entity_metadata: + options.location || options.speakerIds + ? { location: options.location, speakerIds: options.speakerIds?.map((id) => id.toString()) } + : undefined, + name: options.name, + description: options.description, + scheduled_start_time: options.scheduledStartTime ? new Date(options.scheduledStartTime).toISOString() : undefined, + scheduled_end_time: options.scheduledEndTime ? new Date(options.scheduledEndTime).toISOString() : undefined, + privacy_level: options.privacyLevel, + entity_type: options.entityType, + status: options.status, + } + ); + + return bot.transformers.scheduledEvent(bot, event); +} diff --git a/src/helpers/guilds/scheduledEvents/getScheduledEvent.ts b/src/helpers/guilds/scheduledEvents/getScheduledEvent.ts new file mode 100644 index 000000000..27662be37 --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/getScheduledEvent.ts @@ -0,0 +1,13 @@ +import { Bot } from "../../../bot.ts"; +import { ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; + +/** Get a guild scheduled event. */ +export async function getScheduledEvent(bot: Bot, guildId: bigint, eventId: bigint) { + const event = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_SCHEDULED_EVENT(guildId, eventId) + ); + + return bot.transformers.scheduledEvent(bot, event); +} diff --git a/src/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts b/src/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts new file mode 100644 index 000000000..b795a0842 --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/getScheduledEventUsers.ts @@ -0,0 +1,58 @@ +import { Bot } from "../../../bot.ts"; +import { DiscordenoMember, DiscordenoUser } from "../../../transformers/member.ts"; +import { GetScheduledEventUsers } from "../../../types/guilds/scheduledEvents.ts"; +import { GuildMember } from "../../../types/members/guildMember.ts"; +import { User } from "../../../types/users/user.ts"; +import { Collection } from "../../../util/collection.ts"; + +export async function getScheduledEventUsers( + bot: Bot, + guildId: bigint, + eventId: bigint, + options?: { withMember?: false } +): Promise>; +export async function getScheduledEventUsers( + bot: Bot, + guildId: bigint, + eventId: bigint, + options?: { withMember: true } +): Promise>; +export async function getScheduledEventUsers( + bot: Bot, + guildId: bigint, + eventId: bigint, + options?: GetScheduledEventUsers +): Promise< + Collection | Collection +> { + // TODO: validate limit + // TODO: is the guild member omit user + + const result = await bot.rest.runMethod<(User & { member?: GuildMember })[]>( + bot.rest, + "get", + bot.constants.endpoints.GUILD_SCHEDULED_EVENT_USERS(guildId, eventId), + { + limit: options?.limit, + with_members: options?.withMember, + } + ); + + if (!options?.withMember) { + return new Collection( + result.map((res) => { + const user = bot.transformers.user(bot, res); + return [user.id, user]; + }) + ); + } + + return new Collection( + result.map((res) => { + const user = bot.transformers.user(bot, res); + const member = bot.transformers.member(bot, res.member!, guildId, user.id); + + return [user.id, { member, user }]; + }) + ); +} diff --git a/src/helpers/guilds/scheduledEvents/getScheduledEvents.ts b/src/helpers/guilds/scheduledEvents/getScheduledEvents.ts new file mode 100644 index 000000000..0a1a916c7 --- /dev/null +++ b/src/helpers/guilds/scheduledEvents/getScheduledEvents.ts @@ -0,0 +1,23 @@ +import { Bot } from "../../../bot.ts"; +import { DiscordenoScheduledEvent } from "../../../transformers/scheduledEvent.ts"; +import { GetScheduledEvents, ScheduledEvent } from "../../../types/guilds/scheduledEvents.ts"; +import { Collection } from "../../../util/collection.ts"; + +/** Get a list of guild scheduled event for the given guild. */ +export async function getScheduledEvents(bot: Bot, guildId: bigint, options?: GetScheduledEvents) { + const events = await bot.rest.runMethod( + bot.rest, + "get", + bot.constants.endpoints.GUILD_SCHEDULED_EVENTS(guildId), + { + with_user_count: options?.withUserCount, + } + ); + + return new Collection( + events.map((e) => { + const event = bot.transformers.scheduledEvent(bot, e); + return [event.id, event]; + }) + ); +} diff --git a/src/helpers/invites/getInvite.ts b/src/helpers/invites/getInvite.ts index da2755bbe..654c6d7bd 100644 --- a/src/helpers/invites/getInvite.ts +++ b/src/helpers/invites/getInvite.ts @@ -7,5 +7,6 @@ export async function getInvite(bot: Bot, inviteCode: string, options?: GetInvit return await bot.rest.runMethod(bot.rest, "get", bot.constants.endpoints.INVITE(inviteCode), { with_counts: options?.withCounts || false, with_expiration: options?.withExpiration || false, + guild_scheduled_event_id: options?.scheduledEventId?.toString(), }); } diff --git a/src/helpers/mod.ts b/src/helpers/mod.ts index f44bfb3af..caa43bd97 100644 --- a/src/helpers/mod.ts +++ b/src/helpers/mod.ts @@ -149,6 +149,12 @@ import { startThreadWithoutMessage } from "./channels/threads/startThreadWithout import { unarchiveThread } from "./channels/threads/unarchiveThread.ts"; import { unlockThread } from "./channels/threads/unlockThread.ts"; import { cloneChannel } from "./channels/cloneChannel.ts"; +import { createScheduledEvent } from "./guilds/scheduledEvents/createScheduledEvent.ts"; +import { deleteScheduledEvent } from "./guilds/scheduledEvents/deleteScheduledEvent.ts"; +import { editScheduledEvent } from "./guilds/scheduledEvents/editScheduledEvent.ts"; +import { getScheduledEvent } from "./guilds/scheduledEvents/getScheduledEvent.ts"; +import { getScheduledEvents } from "./guilds/scheduledEvents/getScheduledEvents.ts"; +import { getScheduledEventUsers } from "./guilds/scheduledEvents/getScheduledEventUsers.ts"; export { addDiscoverySubcategory, @@ -168,6 +174,7 @@ export { createGuildTemplate, createInvite, createRole, + createScheduledEvent, createSlashCommand, createStageInstance, createWebhook, @@ -181,6 +188,7 @@ export { deleteMessage, deleteMessages, deleteRole, + deleteScheduledEvent, deleteSlashCommand, deleteSlashResponse, deleteStageInstance, @@ -200,6 +208,7 @@ export { editMember, editMessage, editRole, + editScheduledEvent, editSlashResponse, editSlashCommandPermissions, editWebhook, @@ -237,6 +246,9 @@ export { getPruneCount, getReactions, getRoles, + getScheduledEvent, + getScheduledEvents, + getScheduledEventUsers, getSlashCommand, getSlashCommandPermission, getSlashCommandPermissions, diff --git a/src/rest/processQueue.ts b/src/rest/processQueue.ts index c9dbc1a89..a555e76c7 100644 --- a/src/rest/processQueue.ts +++ b/src/rest/processQueue.ts @@ -45,6 +45,7 @@ export function processQueue(rest: RestManager, id: string) { const query = queuedRequest.request.method.toUpperCase() === "GET" && queuedRequest.payload.body ? Object.keys(queuedRequest.payload.body) + .filter((key) => (queuedRequest.payload.body as Record)[key] !== undefined) .map( (key) => `${encodeURIComponent(key)}=${encodeURIComponent( diff --git a/src/transformers/auditlogEntry.ts b/src/transformers/auditlogEntry.ts index d44178629..5357ed1dc 100644 --- a/src/transformers/auditlogEntry.ts +++ b/src/transformers/auditlogEntry.ts @@ -59,6 +59,7 @@ export function transformAuditlogEntry( case "topic": case "code": case "nick": + case "location": return { key: change.key, old: change.old_value, @@ -81,6 +82,8 @@ export function transformAuditlogEntry( case "expire_grace_period": case "user_limit": case "privacy_level": + case "entity_type": + case "status": return { key: change.key, old: change.old_value ? Number(change.old_value) : undefined, diff --git a/src/transformers/scheduledEvent.ts b/src/transformers/scheduledEvent.ts new file mode 100644 index 000000000..2ff48acd5 --- /dev/null +++ b/src/transformers/scheduledEvent.ts @@ -0,0 +1,69 @@ +import { Bot } from "../bot.ts"; +import { + ScheduledEvent, + ScheduledEventEntityType, + ScheduledEventPrivacyLevel, + ScheduledEventStatus, +} from "../types/guilds/scheduledEvents.ts"; +import { SnakeCasedPropertiesDeep } from "../types/util.ts"; +import { DiscordenoUser } from "./member.ts"; + +export function transformScheduledEvent( + bot: Bot, + payload: SnakeCasedPropertiesDeep +): DiscordenoScheduledEvent { + return { + id: bot.transformers.snowflake(payload.id), + guildId: bot.transformers.snowflake(payload.guild_id), + channelId: payload.channel_id ? bot.transformers.snowflake(payload.channel_id) : undefined, + creatorId: payload.creator_id ? bot.transformers.snowflake(payload.creator_id) : undefined, + scheduledStartTime: Date.parse(payload.scheduled_start_time), + scheduledEndTime: payload.scheduled_end_time ? Date.parse(payload.scheduled_end_time) : undefined, + entityId: payload.entity_id ? bot.transformers.snowflake(payload.entity_id) : undefined, + speakerIds: payload.entity_metadata?.speaker_ids?.map((id) => bot.transformers.snowflake(id)), + creator: payload.creator ? bot.transformers.user(bot, payload.creator) : undefined, + + name: payload.name, + description: payload.description, + privacyLevel: payload.privacy_level, + status: payload.status, + entityType: payload.entity_type, + userCount: payload.user_count || 0, + location: payload.entity_metadata?.location, + }; +} + +export interface DiscordenoScheduledEvent { + /** the id of the scheduled event */ + id: bigint; + /** the guild id which the scheduled event belongs to */ + guildId: bigint; + /** the channel id in which the scheduled event will be hosted if specified */ + channelId?: bigint; + /** the id of the user that created the scheduled event */ + creatorId?: bigint; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description: string; + /** the time the scheduled event will start */ + scheduledStartTime: number; + /** the time the scheduled event will end if it does end. */ + scheduledEndTime?: number; + /** the privacy level of the scheduled event */ + privacyLevel: ScheduledEventPrivacyLevel; + /** the status of the scheduled event */ + status: ScheduledEventStatus; + /** the type of hosting entity associated with a scheduled event */ + entityType: ScheduledEventEntityType; + /** any additional id of the hosting entity associated with event */ + entityId?: bigint; + /** the speakers of the stage channel */ + speakerIds?: bigint[]; + /** location of the event */ + location?: string; + /** the user that created the scheduled event */ + creator?: DiscordenoUser; + /** the number of users subscribed to the scheduled event */ + userCount: number; +} diff --git a/src/types/auditLog/auditLogChange.ts b/src/types/auditLog/auditLogChange.ts index c5f321410..8d6de21f8 100644 --- a/src/types/auditLog/auditLogChange.ts +++ b/src/types/auditLog/auditLogChange.ts @@ -32,7 +32,8 @@ export type AuditLogChange = | "inviter_id" | "nick" | "avatar_hash" - | "id"; + | "id" + | "location"; } | { newValue: number; @@ -56,7 +57,9 @@ export type AuditLogChange = | "user_limit" | "privacy_level" | "auto_archive_duration" - | "default_auto_archive_duration"; + | "default_auto_archive_duration" + | "entity_type" + | "status"; } | { newValue: Partial; diff --git a/src/types/auditLog/auditLogEvents.ts b/src/types/auditLog/auditLogEvents.ts index bfa7f1fdd..08c35d9a9 100644 --- a/src/types/auditLog/auditLogEvents.ts +++ b/src/types/auditLog/auditLogEvents.ts @@ -41,6 +41,9 @@ export enum AuditLogEvents { StickerCreate = 90, StickerUpdate, StickerDelete, + GuildScheduledEventCreate = 100, + GuildScheduledEventUpdate, + GuildScheduledEventDelete, ThreadCreate = 110, ThreadUpdate, ThreadDelete, diff --git a/src/types/gateway/gatewayIntents.ts b/src/types/gateway/gatewayIntents.ts index 9e7c1b634..ee8f07d50 100644 --- a/src/types/gateway/gatewayIntents.ts +++ b/src/types/gateway/gatewayIntents.ts @@ -96,6 +96,15 @@ export enum GatewayIntents { * - TYPING_START */ DirectMessageTyping = 1 << 14, + + /** + * - GUILD_SCHEDULED_EVENT_CREATE + * - GUILD_SCHEDULED_EVENT_UPDATE + * - GUILD_SCHEDULED_EVENT_DELETE + * - GUILD_SCHEDULED_EVENT_USER_ADD this is experimental and unstable. + * - GUILD_SCHEDULED_EVENT_USER_REMOVE this is experimental and unstable. + */ + GuildScheduledEvents = (1 << 16), } export type Intents = GatewayIntents; diff --git a/src/types/guilds/scheduledEvents.ts b/src/types/guilds/scheduledEvents.ts new file mode 100644 index 000000000..4f233c36e --- /dev/null +++ b/src/types/guilds/scheduledEvents.ts @@ -0,0 +1,159 @@ +import { User } from "../users/user.ts"; + +export interface ScheduledEvent { + /** the id of the scheduled event */ + id: string; + /** the guild id which the scheduled event belongs to */ + guildId: string; + /** the channel id in which the scheduled event will be hosted if specified */ + channelId: string | null; + /** the id of the user that created the scheduled event */ + creatorId?: string; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description: string; + /** the time the scheduled event will start */ + scheduledStartTime: string; + /** the time the scheduled event will end if it does end. */ + scheduledEndTime: string | null; + /** the privacy level of the scheduled event */ + privacyLevel: ScheduledEventPrivacyLevel; + /** the status of the scheduled event */ + status: ScheduledEventStatus; + /** the type of hosting entity associated with a scheduled event */ + entityType: ScheduledEventEntityType; + /** any additional id of the hosting entity associated with event */ + entityId: string | null; + /** the entity metadata for the scheduled event */ + entityMetadata: ScheduledEventEntityMetadata | null; + /** the user that created the scheduled event */ + creator?: User; + /** the number of users subscribed to the scheduled event */ + userCount?: number; +} + +export enum ScheduledEventPrivacyLevel { + /** the scheduled event is public and available in discovery */ + Public = 1, + /** the scheduled event is only accessible to guild members */ + GuildOnly, +} + +export enum ScheduledEventEntityType { + None, + StageInstance, + Voice, + External, +} + +export enum ScheduledEventStatus { + Scheduled = 1, + Active, + Completed, + Canceled, +} + +export interface ScheduledEventEntityMetadata { + /** the speakers of the stage channel */ + speakerIds?: string[]; + /** location of the event */ + location?: string; +} + +export interface ScheduledEventUserRemove { + /** id of the guild scheduled event */ + guildScheduledEventId: string; + /** id of the user */ + userId: string; + /** id of the guild */ + guildId: string; +} + +export interface ScheduledEventUserAdd { + /** id of the guild scheduled event */ + guildScheduledEventId: string; + /** id of the user */ + userId: string; + /** id of the guild */ + guildId: string; +} + +export interface CreateScheduledEvent { + /** the channel id of the scheduled event */ + channelId?: bigint; + /** the speakers of the stage channel */ + speakerIds?: bigint[]; + /** location of the event */ + location?: string; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description: string; + /** the time the scheduled event will start */ + scheduledStartTime: number; + /** the time the scheduled event will end if it does end. */ + scheduledEndTime?: number; + /** the privacy level of the scheduled event */ + privacyLevel: ScheduledEventPrivacyLevel; + /** the type of hosting entity associated with a scheduled event */ + entityType: ScheduledEventEntityType; +} + +export interface EditScheduledEvent { + /** the channel id of the scheduled event */ + channelId: bigint; + /** the speakers of the stage channel */ + speakerIds: bigint[]; + /** location of the event */ + location: string; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description: string; + /** the time the scheduled event will start */ + scheduledStartTime: number; + /** the time the scheduled event will end if it does end. */ + scheduledEndTime?: number; + /** the privacy level of the scheduled event */ + privacyLevel: ScheduledEventPrivacyLevel; + /** the type of hosting entity associated with a scheduled event */ + entityType: ScheduledEventEntityType; + /** the status of the scheduled event */ + status: ScheduledEventStatus; +} + +export interface EditScheduledEvent { + /** the channel id of the scheduled event */ + channelId: bigint; + /** the speakers of the stage channel */ + speakerIds: bigint[]; + /** location of the event */ + location: string; + /** the name of the scheduled event */ + name: string; + /** the description of the scheduled event */ + description: string; + /** the time the scheduled event will start */ + scheduledStartTime: number; + /** the time the scheduled event will end if it does end. */ + scheduledEndTime?: number; + /** the privacy level of the scheduled event */ + privacyLevel: ScheduledEventPrivacyLevel; + /** the type of hosting entity associated with a scheduled event */ + entityType: ScheduledEventEntityType; + /** the status of the scheduled event */ + status: ScheduledEventStatus; +} + +export interface GetScheduledEvents { + /** include number of users subscribed to each event */ + withUserCount?: boolean; +} + +export interface GetScheduledEventUsers { + /** how many users to receive from the event. Defaults to 100. */ + limit?: number; + /** Whether to also have member objects provided. Defaults to false. */ + withMember?: boolean; +} diff --git a/src/types/invites/getInvite.ts b/src/types/invites/getInvite.ts index d071d1569..a42933bfd 100644 --- a/src/types/invites/getInvite.ts +++ b/src/types/invites/getInvite.ts @@ -4,4 +4,6 @@ export interface GetInvite { withCounts?: boolean; /** Whether the invite should contain the expiration date */ withExpiration?: boolean; + /** the guild scheduled event to include with the invite */ + scheduledEventId?: bigint; } diff --git a/src/types/invites/invite.ts b/src/types/invites/invite.ts index ecdeaef16..a795158ec 100644 --- a/src/types/invites/invite.ts +++ b/src/types/invites/invite.ts @@ -4,6 +4,7 @@ import { Application } from "../applications/application.ts"; import { User } from "../users/user.ts"; import { TargetTypes } from "./targetTypes.ts"; import { InviteStageInstance } from "./inviteStageInstance.ts"; +import { ScheduledEvent } from "../guilds/scheduledEvents.ts"; /** https://discord.com/developers/docs/resources/invite#invite-object */ export interface Invite { @@ -29,4 +30,6 @@ export interface Invite { expiresAt?: string | null; /** Stage instance data if there is a public Stage instance in the Stage channel this invite is for */ stageInstance?: InviteStageInstance; + /** guild scheduled event data */ + guildScheduledEvent?: ScheduledEvent; } diff --git a/src/types/permissions/bitwisePermissionFlags.ts b/src/types/permissions/bitwisePermissionFlags.ts index 5b5791da5..81a864294 100644 --- a/src/types/permissions/bitwisePermissionFlags.ts +++ b/src/types/permissions/bitwisePermissionFlags.ts @@ -66,6 +66,8 @@ export enum BitwisePermissionFlags { USE_SLASH_COMMANDS = 0x80000000, /** Allows for requesting to speak in stage channels. */ REQUEST_TO_SPEAK = 0x0100000000, + /** Allows for creating, editing, and deleting scheduled events */ + MANAGE_EVENTS = 0x0200000000, /** Allows for deleting and archiving threads, and viewing all private threads */ MANAGE_THREADS = 0x0400000000, /** Allows for creating threads */ diff --git a/src/util/constants.ts b/src/util/constants.ts index 0efd73de7..f8e080f2d 100644 --- a/src/util/constants.ts +++ b/src/util/constants.ts @@ -105,6 +105,9 @@ export const endpoints = { GUILD_PREVIEW: (guildId: bigint) => `${GUILDS_BASE(guildId)}/preview`, UPDATE_VOICE_STATE: (guildId: bigint, userId?: bigint) => `${GUILDS_BASE(guildId)}/voice-states/${userId ?? "@me"}`, GUILD_WELCOME_SCREEN: (guildId: bigint) => `${GUILDS_BASE(guildId)}/welcome-screen`, + GUILD_SCHEDULED_EVENTS: (guildId: bigint) => `${GUILDS_BASE(guildId)}/scheduled-events`, + GUILD_SCHEDULED_EVENT: (guildId: bigint, eventId: bigint) => `${GUILDS_BASE(guildId)}/scheduled-events/${eventId}`, + GUILD_SCHEDULED_EVENT_USERS: (guildId: bigint, eventId: bigint) => `${GUILDS_BASE(guildId)}/scheduled-events/${eventId}/users`, // Voice VOICE_REGIONS: `${baseEndpoints.BASE_URL}/voice/regions`, diff --git a/tests/helpers/guilds/scheduledEvents/createScheduledEvent.ts b/tests/helpers/guilds/scheduledEvents/createScheduledEvent.ts new file mode 100644 index 000000000..309965cfd --- /dev/null +++ b/tests/helpers/guilds/scheduledEvents/createScheduledEvent.ts @@ -0,0 +1,36 @@ +import { Bot } from "../../../../src/bot.ts"; +import { ChannelTypes } from "../../../../src/types/channels/channelTypes.ts"; +import { CreateScheduledEvent, ScheduledEventEntityType } from "../../../../src/types/guilds/scheduledEvents.ts"; +import { assertEquals, assertExists } from "../../../deps.ts"; + +export async function createScheduledEventTests( + bot: Bot, + guildId: bigint, + options: CreateScheduledEvent, + t: Deno.TestContext +) { + if ([ScheduledEventEntityType.StageInstance, ScheduledEventEntityType.Voice].includes(options.entityType)) { + const channel = await bot.helpers.createChannel(guildId, { + name: "entity", + type: + options.entityType === ScheduledEventEntityType.Voice ? ChannelTypes.GuildVoice : ChannelTypes.GuildStageVoice, + }); + + options.channelId = channel.id; + } + + const event = await bot.helpers.createScheduledEvent(guildId, options); + + // Assertions + assertExists(event.id); + + assertEquals(event.channelId, options.channelId); + assertEquals(event.speakerIds?.length, options.speakerIds?.length); + assertEquals(event.location, options.location); + assertEquals(event.name, options.name); + assertEquals(event.description, options.description); + assertEquals(event.scheduledStartTime, options.scheduledStartTime); + assertEquals(event.scheduledEndTime, options.scheduledEndTime); + assertEquals(event.privacyLevel, options.privacyLevel); + assertEquals(event.entityType, options.entityType); +} diff --git a/tests/mod.ts b/tests/mod.ts index 1189f2255..3c6d86092 100644 --- a/tests/mod.ts +++ b/tests/mod.ts @@ -1,4 +1,4 @@ -// import { UNITTEST_TOKEN } from "../configs.ts"; +import { UNITTEST_TOKEN } from "../configs.ts"; import { memoryBenchmarks } from "../benchmarks/index.ts"; import { channelOverwriteHasPermission, @@ -63,6 +63,8 @@ import { channelOverwriteHasPermissionTest } from "./helpers/channels/channelOve import { cloneChannelTests } from "./helpers/channels/cloneChannel.ts"; import { deleteChannelOverwriteTests } from "./helpers/channels/deleteChannelOverwrite.ts"; import { editChannelTests } from "./helpers/channels/editChannel.ts"; +import { createScheduledEventTests } from "./helpers/guilds/scheduledEvents/createScheduledEvent.ts"; +import { ScheduledEventEntityType, ScheduledEventPrivacyLevel } from "../src/types/guilds/scheduledEvents.ts"; // CHANGE TO TRUE WHEN DEBUGGING SANITIZATION ERRORS const sanitizeMode = { @@ -74,18 +76,18 @@ const sanitizeMode = { Deno.test({ name: "[Bot] - Starting Tests", fn: async (t) => { - const token = Deno.env.get("DISCORD_TOKEN")!; - if (!token) { - throw new Error("DISCORD_TOKEN not found"); - } + // const token = Deno.env.get("DISCORD_TOKEN")!; + // if (!token) { + // throw new Error("DISCORD_TOKEN not found"); + // } - // const botId = BigInt(atob(UNITTEST_TOKEN.split(".")[0])); - const botId = BigInt(atob(token.split(".")[0])); + const botId = BigInt(atob(UNITTEST_TOKEN.split(".")[0])); + // const botId = BigInt(atob(token.split(".")[0])); let startedAt = 0; const bot = createBot({ - // token: UNITTEST_TOKEN || Deno.env.get("DISCORD_TOKEN"), - token: Deno.env.get("DISCORD_TOKEN")!, + token: UNITTEST_TOKEN || Deno.env.get("DISCORD_TOKEN"), + // token: Deno.env.get("DISCORD_TOKEN")!, botId, events: createEventHandlers({ ready: () => { @@ -93,7 +95,7 @@ Deno.test({ }, // debug: console.log, }), - intents: ["Guilds", "GuildEmojis", "GuildMessages", "GuildMessageReactions", "GuildBans", "GuildMembers"], + intents: ["Guilds", "GuildEmojis", "GuildMessages", "GuildMessageReactions", "GuildBans", "GuildMembers", "GuildScheduledEvents"], cache: { isAsync: false, }, @@ -132,6 +134,125 @@ Deno.test({ throw new Error(`The guild seemed to be created but it was not cached. ${guild.id.toString()}`); } + // GUILD SCHEDULED EVENTS TESTS + await t.step("Guild Scheduled Event related tests", async (t) => { + await Promise.all([ + // channelId?: bigint; + // speakerIds?: bigint[]; + // location?: string; + // name: string; + // description: string; + // scheduledStartTime: number; + // scheduledEndTime?: number; + // privacyLevel: ScheduledEventPrivacyLevel; + // entityType: ScheduledEventEntityType; + // t.step({ + // name: "[scheduled event] create a public scheduled event with no entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.Public, + // entityType: ScheduledEventEntityType.None, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a guild scheduled event with no entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.GuildOnly, + // entityType: ScheduledEventEntityType.None, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a public scheduled event with stage entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.Public, + // entityType: ScheduledEventEntityType.StageInstance, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a guild scheduled event with stage entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.GuildOnly, + // entityType: ScheduledEventEntityType.StageInstance, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a public scheduled event with voice entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.Public, + // entityType: ScheduledEventEntityType.Voice, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a guild scheduled event with voice entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.GuildOnly, + // entityType: ScheduledEventEntityType.Voice, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a public scheduled event with external entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.Public, + // entityType: ScheduledEventEntityType.External, + // }, t); + // }, + // ...sanitizeMode, + // }), + // t.step({ + // name: "[scheduled event] create a guild scheduled event with external entity", + // fn: async (t) => { + // await createScheduledEventTests(bot, guild.id, { + // name: "lfg", + // description: "lfg it is", + // scheduledStartTime: Date.now() + 600000, + // privacyLevel: ScheduledEventPrivacyLevel.GuildOnly, + // entityType: ScheduledEventEntityType.External, + // }, t); + // }, + // ...sanitizeMode, + // }), + ]); + }); + // GUILD TESTS GROUPED await t.step("Guild related tests", async (t) => { await Promise.all([