feat: guild scheduled events

This commit is contained in:
Skillz4Killz
2021-11-16 21:15:05 +00:00
committed by GitHub
parent 889e9d197a
commit a1f7c47b7a
28 changed files with 724 additions and 13 deletions

View File

@@ -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<EventHandlers>): 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<Helpers>) {
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<Helpers>) {
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<Helpers>) {
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<Helpers>) {
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<Transformers>) {
@@ -829,7 +849,8 @@ export function createTransformers(options: Partial<Transformers>) {
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;

View File

@@ -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<ScheduledEvent>;
bot.events.scheduledEventCreate(bot, bot.transformers.scheduledEvent(bot, payload));
}

View File

@@ -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<ScheduledEvent>;
bot.events.scheduledEventDelete(bot, bot.transformers.scheduledEvent(bot, payload));
}

View File

@@ -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<ScheduledEvent>;
bot.events.scheduledEventUpdate(bot, bot.transformers.scheduledEvent(bot, payload));
}

View File

@@ -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<ScheduledEventUserAdd>;
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),
});
}

View File

@@ -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<ScheduledEventUserRemove>;
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),
});
}

View File

@@ -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,

View File

@@ -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<ScheduledEvent>(
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);
}

View File

@@ -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<undefined>(
bot.rest,
"delete",
bot.constants.endpoints.GUILD_SCHEDULED_EVENT(guildId, eventId)
);
}

View File

@@ -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<EditScheduledEvent>
) {
// TODO: validate name length
// TODO: validate description length
// TODO: validate location length
// TODO: validate speaker ids length
const event = await bot.rest.runMethod<ScheduledEvent>(
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);
}

View File

@@ -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<ScheduledEvent>(
bot.rest,
"get",
bot.constants.endpoints.GUILD_SCHEDULED_EVENT(guildId, eventId)
);
return bot.transformers.scheduledEvent(bot, event);
}

View File

@@ -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<Collection<bigint, DiscordenoUser>>;
export async function getScheduledEventUsers(
bot: Bot,
guildId: bigint,
eventId: bigint,
options?: { withMember: true }
): Promise<Collection<bigint, { user: DiscordenoUser; member: DiscordenoMember }>>;
export async function getScheduledEventUsers(
bot: Bot,
guildId: bigint,
eventId: bigint,
options?: GetScheduledEventUsers
): Promise<
Collection<bigint, DiscordenoUser> | Collection<bigint, { user: DiscordenoUser; member: DiscordenoMember }>
> {
// 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 }];
})
);
}

View File

@@ -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<ScheduledEvent[]>(
bot.rest,
"get",
bot.constants.endpoints.GUILD_SCHEDULED_EVENTS(guildId),
{
with_user_count: options?.withUserCount,
}
);
return new Collection<bigint, DiscordenoScheduledEvent>(
events.map((e) => {
const event = bot.transformers.scheduledEvent(bot, e);
return [event.id, event];
})
);
}

View File

@@ -7,5 +7,6 @@ export async function getInvite(bot: Bot, inviteCode: string, options?: GetInvit
return await bot.rest.runMethod<InviteMetadata>(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(),
});
}

View File

@@ -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,

View File

@@ -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<string, string>)[key] !== undefined)
.map(
(key) =>
`${encodeURIComponent(key)}=${encodeURIComponent(

View File

@@ -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,

View File

@@ -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<ScheduledEvent>
): 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;
}

View File

@@ -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<Role>;

View File

@@ -41,6 +41,9 @@ export enum AuditLogEvents {
StickerCreate = 90,
StickerUpdate,
StickerDelete,
GuildScheduledEventCreate = 100,
GuildScheduledEventUpdate,
GuildScheduledEventDelete,
ThreadCreate = 110,
ThreadUpdate,
ThreadDelete,

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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`,

View File

@@ -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);
}

View File

@@ -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([