feat: add support for GuildScheduledEvent (#6493)

Co-authored-by: Rodry <38259440+ImRodry@users.noreply.github.com>
Co-authored-by: Sugden <28943913+NotSugden@users.noreply.github.com>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: GoldenAngel <50855202+GoldenAngel2@users.noreply.github.com>
Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
Co-authored-by: Antonio Román <kyradiscord@gmail.com>
Co-authored-by: SpaceEEC <spaceeec@yahoo.com>
This commit is contained in:
Shubham Parihar
2021-12-23 18:09:09 +05:30
committed by GitHub
parent aa7c1b2081
commit 1316fd4c6a
29 changed files with 1261 additions and 13 deletions

View File

@@ -15,6 +15,7 @@ const GuildChannelManager = require('../managers/GuildChannelManager');
const GuildEmojiManager = require('../managers/GuildEmojiManager');
const GuildInviteManager = require('../managers/GuildInviteManager');
const GuildMemberManager = require('../managers/GuildMemberManager');
const GuildScheduledEventManager = require('../managers/GuildScheduledEventManager');
const GuildStickerManager = require('../managers/GuildStickerManager');
const PresenceManager = require('../managers/PresenceManager');
const RoleManager = require('../managers/RoleManager');
@@ -109,6 +110,12 @@ class Guild extends AnonymousGuild {
*/
this.invites = new GuildInviteManager(this);
/**
* A manager of the scheduled events of this guild
* @type {GuildScheduledEventManager}
*/
this.scheduledEvents = new GuildScheduledEventManager(this);
if (!data) return;
if (data.unavailable) {
/**
@@ -461,6 +468,13 @@ class Guild extends AnonymousGuild {
}
}
if (data.guild_scheduled_events) {
this.scheduledEvents.cache.clear();
for (const scheduledEvent of data.guild_scheduled_events) {
this.scheduledEvents._add(scheduledEvent);
}
}
if (data.voice_states) {
this.voiceStates.cache.clear();
for (const voiceState of data.voice_states) {

View File

@@ -1,6 +1,7 @@
'use strict';
const { Collection } = require('@discordjs/collection');
const { GuildScheduledEvent } = require('./GuildScheduledEvent');
const Integration = require('./Integration');
const Invite = require('./Invite');
const { StageInstance } = require('./StageInstance');
@@ -24,6 +25,7 @@ const Util = require('../util/Util');
* * STAGE_INSTANCE
* * STICKER
* * THREAD
* * GUILD_SCHEDULED_EVENT
* @typedef {string} AuditLogTargetType
*/
@@ -35,6 +37,7 @@ const Util = require('../util/Util');
const Targets = {
ALL: 'ALL',
GUILD: 'GUILD',
GUILD_SCHEDULED_EVENT: 'GUILD_SCHEDULED_EVENT',
CHANNEL: 'CHANNEL',
USER: 'USER',
ROLE: 'ROLE',
@@ -93,6 +96,9 @@ const Targets = {
* * STICKER_CREATE: 90
* * STICKER_UPDATE: 91
* * STICKER_DELETE: 92
* * GUILD_SCHEDULED_EVENT_CREATE: 100
* * GUILD_SCHEDULED_EVENT_UPDATE: 101
* * GUILD_SCHEDULED_EVENT_DELETE: 102
* * THREAD_CREATE: 110
* * THREAD_UPDATE: 111
* * THREAD_DELETE: 112
@@ -148,6 +154,9 @@ const Actions = {
STICKER_CREATE: 90,
STICKER_UPDATE: 91,
STICKER_DELETE: 92,
GUILD_SCHEDULED_EVENT_CREATE: 100,
GUILD_SCHEDULED_EVENT_UPDATE: 101,
GUILD_SCHEDULED_EVENT_DELETE: 102,
THREAD_CREATE: 110,
THREAD_UPDATE: 111,
THREAD_DELETE: 112,
@@ -218,11 +227,12 @@ class GuildAuditLogs {
* * An integration
* * A stage instance
* * A sticker
* * A guild scheduled event
* * A thread
* * An object with an id key if target was deleted
* * An object where the keys represent either the new value or the old value
* @typedef {?(Object|Guild|Channel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker)}
* AuditLogEntryTarget
* @typedef {?(Object|Guild|Channel|User|Role|Invite|Webhook|GuildEmoji|Message|Integration|StageInstance|Sticker|
* GuildScheduledEvent)} AuditLogEntryTarget
*/
/**
@@ -242,7 +252,7 @@ class GuildAuditLogs {
if (target < 83) return Targets.INTEGRATION;
if (target < 86) return Targets.STAGE_INSTANCE;
if (target < 100) return Targets.STICKER;
if (target < 110) return Targets.UNKNOWN;
if (target < 110) return Targets.GUILD_SCHEDULED_EVENT;
if (target < 120) return Targets.THREAD;
return Targets.UNKNOWN;
}
@@ -276,6 +286,7 @@ class GuildAuditLogs {
Actions.INTEGRATION_CREATE,
Actions.STAGE_INSTANCE_CREATE,
Actions.STICKER_CREATE,
Actions.GUILD_SCHEDULED_EVENT_CREATE,
Actions.THREAD_CREATE,
].includes(action)
) {
@@ -300,6 +311,7 @@ class GuildAuditLogs {
Actions.INTEGRATION_DELETE,
Actions.STAGE_INSTANCE_DELETE,
Actions.STICKER_DELETE,
Actions.GUILD_SCHEDULED_EVENT_DELETE,
Actions.THREAD_DELETE,
].includes(action)
) {
@@ -321,6 +333,7 @@ class GuildAuditLogs {
Actions.INTEGRATION_UPDATE,
Actions.STAGE_INSTANCE_UPDATE,
Actions.STICKER_UPDATE,
Actions.GUILD_SCHEDULED_EVENT_UPDATE,
Actions.THREAD_UPDATE,
].includes(action)
) {
@@ -577,6 +590,19 @@ class GuildAuditLogsEntry {
{ id: data.target_id },
),
);
} else if (targetType === Targets.GUILD_SCHEDULED_EVENT) {
this.target =
guild.scheduledEvents.cache.get(data.target_id) ??
new GuildScheduledEvent(
guild.client,
this.changes.reduce(
(o, c) => {
o[c.key] = c.new ?? c.old;
return o;
},
{ id: data.target_id, guild_id: guild.id },
),
);
} else if (data.target_id) {
this.target = guild[`${targetType.toLowerCase()}s`]?.cache.get(data.target_id) ?? { id: data.target_id };
}

View File

@@ -0,0 +1,422 @@
'use strict';
const Base = require('./Base');
const { Error } = require('../errors');
const {
GuildScheduledEventEntityTypes,
GuildScheduledEventStatuses,
GuildScheduledEventPrivacyLevels,
Endpoints,
} = require('../util/Constants');
const SnowflakeUtil = require('../util/SnowflakeUtil');
/**
* Represents a scheduled event in a {@link Guild}.
* @extends {Base}
*/
class GuildScheduledEvent extends Base {
constructor(client, data) {
super(client);
/**
* The id of the guild scheduled event
* @type {Snowflake}
*/
this.id = data.id;
/**
* The id of the guild this guild scheduled event belongs to
* @type {Snowflake}
*/
this.guildId = data.guild_id;
this._patch(data);
}
_patch(data) {
if ('channel_id' in data) {
/**
* The channel id in which the scheduled event will be hosted, or `null` if entity type is `EXTERNAL`
* @type {?Snowflake}
*/
this.channelId = data.channel_id;
} else {
this.channelId ??= null;
}
if ('creator_id' in data) {
/**
* The id of the user that created this guild scheduled event
* @type {?Snowflake}
*/
this.creatorId = data.creator_id;
} else {
this.creatorId ??= null;
}
/**
* The name of the guild scheduled event
* @type {string}
*/
this.name = data.name;
if ('description' in data) {
/**
* The description of the guild scheduled event
* @type {?string}
*/
this.description = data.description;
} else {
this.description ??= null;
}
/**
* The timestamp the guild scheduled event will start at
* <info>This can be potentially `null` only when it's an {@link AuditLogEntryTarget}</info>
* @type {?number}
*/
this.scheduledStartTimestamp = data.scheduled_start_time ? Date.parse(data.scheduled_start_time) : null;
/**
* The timestamp the guild scheduled event will end at,
* or `null` if the event does not have a scheduled time to end
* @type {?number}
*/
this.scheduledEndTimestamp = data.scheduled_end_time ? Date.parse(data.scheduled_end_time) : null;
/**
* The privacy level of the guild scheduled event
* @type {PrivacyLevel}
*/
this.privacyLevel = GuildScheduledEventPrivacyLevels[data.privacy_level];
/**
* The status of the guild scheduled event
* @type {GuildScheduledEventStatus}
*/
this.status = GuildScheduledEventStatuses[data.status];
/**
* The type of hosting entity associated with the scheduled event
* @type {GuildScheduledEventEntityType}
*/
this.entityType = GuildScheduledEventEntityTypes[data.entity_type];
if ('entity_id' in data) {
/**
* The id of the hosting entity associated with the scheduled event
* @type {?Snowflake}
*/
this.entityId = data.entity_id;
} else {
this.entityId ??= null;
}
if ('user_count' in data) {
/**
* The number of users who are subscribed to this guild scheduled event
* @type {?number}
*/
this.userCount = data.user_count;
} else {
this.userCount ??= null;
}
if ('creator' in data) {
/**
* The user that created this guild scheduled event
* @type {?User}
*/
this.creator = this.client.users._add(data.creator);
} else {
this.creator ??= this.client.users.resolve(this.creatorId);
}
/* eslint-disable max-len */
/**
* Represents the additional metadata for a {@link GuildScheduledEvent}
* @see {@link https://discord.com/developers/docs/resources/guild-scheduled-event#guild-scheduled-event-object-guild-scheduled-event-entity-metadata}
* @typedef {Object} GuildScheduledEventEntityMetadata
* @property {?string} location The location of the guild scheduled event
*/
/* eslint-enable max-len */
if ('entity_metadata' in data) {
if (data.entity_metadata) {
/**
* Additional metadata
* @type {?GuildScheduledEventEntityMetadata}
*/
this.entityMetadata = {
location: data.entity_metadata.location ?? this.entityMetadata?.location ?? null,
};
} else {
this.entityMetadata = null;
}
} else {
this.entityMetadata ??= null;
}
}
/**
* The timestamp the guild scheduled event was created at
* @type {number}
* @readonly
*/
get createdTimestamp() {
return SnowflakeUtil.timestampFrom(this.id);
}
/**
* The time the guild scheduled event was created at
* @type {Date}
* @readonly
*/
get createdAt() {
return new Date(this.createdTimestamp);
}
/**
* The time the guild scheduled event will start at
* @type {Date}
* @readonly
*/
get scheduledStartAt() {
return new Date(this.scheduledStartTimestamp);
}
/**
* The time the guild scheduled event will end at,
* or `null` if the event does not have a scheduled time to end
* @type {?Date}
* @readonly
*/
get scheduledEndAt() {
return this.scheduledEndTimestamp && new Date(this.scheduledEndTimestamp);
}
/**
* The channel associated with this scheduled event
* @type {?(VoiceChannel|StageChannel)}
* @readonly
*/
get channel() {
return this.client.channels.resolve(this.channelId);
}
/**
* The guild this scheduled event belongs to
* @type {?Guild}
* @readonly
*/
get guild() {
return this.client.guilds.resolve(this.guildId);
}
/**
* The URL to the guild scheduled event
* @type {string}
* @readonly
*/
get url() {
return Endpoints.scheduledEvent(this.client.options.http.scheduledEvent, this.guildId, this.id);
}
/**
* Options used to create an invite URL to a {@link GuildScheduledEvent}
* @typedef {CreateInviteOptions} CreateGuildScheduledEventInviteURLOptions
* @property {GuildInvitableChannelResolvable} [channel] The channel to create the invite in.
* <warn>This is required when the `entityType` of `GuildScheduledEvent` is `EXTERNAL`, gets ignored otherwise</warn>
*/
/**
* Creates an invite URL to this guild scheduled event.
* @param {CreateGuildScheduledEventInviteURLOptions} [options] The options to create the invite
* @returns {Promise<string>}
*/
async createInviteURL(options) {
let channelId = this.channelId;
if (this.entityType === 'EXTERNAL') {
if (!options?.channel) throw new Error('INVITE_OPTIONS_MISSING_CHANNEL');
channelId = this.guild.channels.resolveId(options.channel);
if (!channelId) throw new Error('GUILD_CHANNEL_RESOLVE');
}
const invite = await this.guild.invites.create(channelId, options);
return Endpoints.invite(this.client.options.http.invite, invite.code, this.id);
}
/**
* Edits this guild scheduled event.
* @param {GuildScheduledEventEditOptions} options The options to edit the guild scheduled event
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Edit a guild scheduled event
* guildScheduledEvent.edit({ name: 'Party' })
* .then(guildScheduledEvent => console.log(guildScheduledEvent))
* .catch(console.error);
*/
edit(options) {
return this.guild.scheduledEvents.edit(this.id, options);
}
/**
* Deletes this guild scheduled event.
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Delete a guild scheduled event
* guildScheduledEvent.delete()
* .then(guildScheduledEvent => console.log(guildScheduledEvent))
* .catch(console.error);
*/
async delete() {
await this.guild.scheduledEvents.delete(this.id);
return this;
}
/**
* Sets a new name for the guild scheduled event.
* @param {string} name The new name of the guild scheduled event
* @param {string} [reason] The reason for changing the name
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set name of a guild scheduled event
* guildScheduledEvent.setName('Birthday Party')
* .then(guildScheduledEvent => console.log(`Set the name to: ${guildScheduledEvent.name}`))
* .catch(console.error);
*/
setName(name, reason) {
return this.edit({ name, reason });
}
/**
* Sets a new time to schedule the event at.
* @param {DateResolvable} scheduledStartTime The time to schedule the event at
* @param {string} [reason] The reason for changing the scheduled start time
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set start time of a guild scheduled event
* guildScheduledEvent.setScheduledStartTime('2022-09-24T00:00:00+05:30')
* .then(guildScheduledEvent => console.log(`Set the start time to: ${guildScheduledEvent.scheduledStartTime}`))
* .catch(console.error);
*/
setScheduledStartTime(scheduledStartTime, reason) {
return this.edit({ scheduledStartTime, reason });
}
// TODO: scheduledEndTime gets reset on passing null but it hasn't been documented
/**
* Sets a new time to end the event at.
* @param {DateResolvable} scheduledEndTime The time to end the event at
* @param {string} [reason] The reason for changing the scheduled end time
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set end time of a guild scheduled event
* guildScheduledEvent.setScheduledEndTime('2022-09-25T00:00:00+05:30')
* .then(guildScheduledEvent => console.log(`Set the end time to: ${guildScheduledEvent.scheduledEndTime}`))
* .catch(console.error);
*/
setScheduledEndTime(scheduledEndTime, reason) {
return this.edit({ scheduledEndTime, reason });
}
/**
* Sets the new description of the guild scheduled event.
* @param {string} description The description of the guild scheduled event
* @param {string} [reason] The reason for changing the description
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set description of a guild scheduled event
* guildScheduledEvent.setDescription('A virtual birthday party')
* .then(guildScheduledEvent => console.log(`Set the description to: ${guildScheduledEvent.description}`))
* .catch(console.error);
*/
setDescription(description, reason) {
return this.edit({ description, reason });
}
/**
* Sets the new status of the guild scheduled event.
* <info>If you're working with TypeScript, use this method in conjunction with status type-guards
* like {@link GuildScheduledEvent#isScheduled} to get only valid status as suggestion</info>
* @param {GuildScheduledEventStatus|number} status The status of the guild scheduled event
* @param {string} [reason] The reason for changing the status
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set status of a guild scheduled event
* guildScheduledEvent.setStatus('ACTIVE')
* .then(guildScheduledEvent => console.log(`Set the status to: ${guildScheduledEvent.status}`))
* .catch(console.error);
*/
setStatus(status, reason) {
return this.edit({ status, reason });
}
/**
* Sets the new location of the guild scheduled event.
* @param {string} location The location of the guild scheduled event
* @param {string} [reason] The reason for changing the location
* @returns {Promise<GuildScheduledEvent>}
* @example
* // Set location of a guild scheduled event
* guildScheduledEvent.setLocation('Earth')
* .then(guildScheduledEvent => console.log(`Set the location to: ${guildScheduledEvent.entityMetadata.location}`))
* .catch(console.error);
*/
setLocation(location, reason) {
return this.edit({ entityMetadata: { location }, reason });
}
/**
* Fetches subscribers of this guild scheduled event.
* @param {FetchGuildScheduledEventSubscribersOptions} [options] Options for fetching the subscribers
* @returns {Promise<Collection<Snowflake, GuildScheduledEventUser>>}
*/
fetchSubscribers(options) {
return this.guild.scheduledEvents.fetchSubscribers(this.id, options);
}
/**
* When concatenated with a string, this automatically concatenates the event's URL instead of the object.
* @returns {string}
* @example
* // Logs: Event: https://discord.com/events/412345678901234567/499876543211234567
* console.log(`Event: ${guildScheduledEvent}`);
*/
toString() {
return this.url;
}
/**
* Indicates whether this guild scheduled event has an `ACTIVE` status.
* @returns {boolean}
*/
isActive() {
return GuildScheduledEventStatuses[this.status] === GuildScheduledEventStatuses.ACTIVE;
}
/**
* Indicates whether this guild scheduled event has a `CANCELED` status.
* @returns {boolean}
*/
isCanceled() {
return GuildScheduledEventStatuses[this.status] === GuildScheduledEventStatuses.CANCELED;
}
/**
* Indicates whether this guild scheduled event has a `COMPLETED` status.
* @returns {boolean}
*/
isCompleted() {
return GuildScheduledEventStatuses[this.status] === GuildScheduledEventStatuses.COMPLETED;
}
/**
* Indicates whether this guild scheduled event has a `SCHEDULED` status.
* @returns {boolean}
*/
isScheduled() {
return GuildScheduledEventStatuses[this.status] === GuildScheduledEventStatuses.SCHEDULED;
}
}
exports.GuildScheduledEvent = GuildScheduledEvent;

View File

@@ -1,6 +1,7 @@
'use strict';
const Base = require('./Base');
const { GuildScheduledEvent } = require('./GuildScheduledEvent');
const IntegrationApplication = require('./IntegrationApplication');
const InviteStageInstance = require('./InviteStageInstance');
const { Error } = require('../errors');
@@ -208,6 +209,16 @@ class Invite extends Base {
} else {
this.stageInstance ??= null;
}
if ('guild_scheduled_event' in data) {
/**
* The guild scheduled event data if there is a {@link GuildScheduledEvent} in the channel this invite is for
* @type {?GuildScheduledEvent}
*/
this.guildScheduledEvent = new GuildScheduledEvent(this.client, data.guild_scheduled_event);
} else {
this.guildScheduledEvent ??= null;
}
}
/**