Skip to content

Commit

Permalink
feat: recurring scheduled events
Browse files Browse the repository at this point in the history
  • Loading branch information
almeidx committed Aug 15, 2024
1 parent defb083 commit 4fd0f9d
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 1 deletion.
24 changes: 24 additions & 0 deletions packages/discord.js/src/managers/GuildScheduledEventManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const CachedManager = require('./CachedManager');
const { DiscordjsTypeError, DiscordjsError, ErrorCodes } = require('../errors');
const { GuildScheduledEvent } = require('../structures/GuildScheduledEvent');
const { resolveImage } = require('../util/DataResolver');
const { _transformGuildScheduledEventRecurrenceRule } = require('../util/Transformers');

/**
* Manages API methods for GuildScheduledEvents and stores their cache.
Expand Down Expand Up @@ -36,6 +37,21 @@ class GuildScheduledEventManager extends CachedManager {
* @typedef {Snowflake|GuildScheduledEvent} GuildScheduledEventResolvable
*/

/**
* Options for setting a recurrence rule for a guild scheduled event.
* @typedef {Object} GuildScheduledEventRecurrenceRuleOptions
* @property {DateResolvable} startAt The time the recurrence rule interval starts at
* @property {?DateResolvable} endAt The time the recurrence rule interval ends at
* @property {GuildScheduledEventRecurrenceRuleFrequency} frequency How often the event occurs
* @property {number} interval The spacing between the events
* @property {?(GuildScheduledEventRecurrenceRuleWeekday[])} byWeekday The days within a week to recur on
* @property {?(GuildScheduledEventRecurrenceRuleNWeekday[])} byNWeekday The days within a week to recur on
* @property {?(GuildScheduledEventRecurrenceRuleMonth[])} byMonth The months to recur on
* @property {?(number[])} byMonthDay The days within a month to recur on
* @property {?(number[])} byYearDay The days within a year to recur on
* @property {?number} count The total amount of times the event is allowed to recur before stopping
*/

/**
* Options used to create a guild scheduled event.
* @typedef {Object} GuildScheduledEventCreateOptions
Expand All @@ -54,6 +70,8 @@ class GuildScheduledEventManager extends CachedManager {
* <warn>This is required if `entityType` is {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* @property {string} [reason] The reason for creating the guild scheduled event
* @property {GuildScheduledEventRecurrenceRuleOptions} [recurrenceRule]
* The recurrence rule of the guild scheduled event
*/

/**
Expand Down Expand Up @@ -81,6 +99,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;

let entity_metadata, channel_id;
Expand All @@ -104,6 +123,7 @@ class GuildScheduledEventManager extends CachedManager {
entity_type: entityType,
entity_metadata,
image: image && (await resolveImage(image)),
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});
Expand Down Expand Up @@ -178,6 +198,8 @@ class GuildScheduledEventManager extends CachedManager {
* {@link GuildScheduledEventEntityType.External}</warn>
* @property {?(BufferResolvable|Base64Resolvable)} [image] The cover image of the guild scheduled event
* @property {string} [reason] The reason for editing the guild scheduled event
* @property {GuildScheduledEventRecurrenceRuleOptions} [recurrenceRule]
* The recurrence rule of the guild scheduled event
*/

/**
Expand All @@ -203,6 +225,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;

let entity_metadata;
Expand All @@ -224,6 +247,7 @@ class GuildScheduledEventManager extends CachedManager {
status,
image: image && (await resolveImage(image)),
entity_metadata,
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});
Expand Down
50 changes: 50 additions & 0 deletions packages/discord.js/src/structures/GuildScheduledEvent.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,56 @@ class GuildScheduledEvent extends Base {
} else {
this.image ??= null;
}

/**
* Represents the recurrence rule for a {@link GuildScheduledEvent}.
* @typedef {Object} GuildScheduledEventRecurrenceRule
* @property {number} startTimestamp The timestamp the recurrence rule interval starts at
* @property {Date} startAt The time the recurrence rule interval starts at
* @property {?number} endTimestamp The timestamp the recurrence rule interval ends at
* @property {?Date} endAt The time the recurrence rule interval ends at
* @property {GuildScheduledEventRecurrenceRuleFrequency} frequency How often the event occurs
* @property {number} interval The spacing between the events
* @property {?(GuildScheduledEventRecurrenceRuleWeekday[])} byWeekday The days within a week to recur on
* @property {?(GuildScheduledEventRecurrenceRuleNWeekday[])} byNWeekday The days within a week to recur on
* @property {?(GuildScheduledEventRecurrenceRuleMonth[])} byMonth The months to recur on
* @property {?(number[])} byMonthDay The days within a month to recur on
* @property {?(number[])} byYearDay The days within a year to recur on
* @property {?number} count The total amount of times the event is allowed to recur before stopping
*/

/**
* @typedef {Object} GuildScheduledEventRecurrenceRuleNWeekday
* @property {number} n The week to recur on
* @property {GuildScheduledEventRecurrenceRuleWeekday} day The day within the week to recur on
*/

if ('recurrence_rule' in data) {
/**
* The recurrence rule for this scheduled event
* @type {?GuildScheduledEventRecurrenceRule}
*/
this.recurrenceRule = {
startTimestamp: Date.parse(data.recurrence_rule.start),
get startAt() {
return new Date(this.startTimestamp);
},
endTimestamp: data.recurrence_rule.end && Date.parse(data.recurrence_rule.end),
get endAt() {
return this.endTimestamp && new Date(this.endTimestamp);
},
frequency: data.recurrence_rule.frequency,
interval: data.recurrence_rule.interval,
byWeekday: data.recurrence_rule.by_weekday,
byNWeekday: data.recurrence_rule.by_n_weekday,
byMonth: data.recurrence_rule.by_month,
byMonthDay: data.recurrence_rule.by_month_day,
byYearDay: data.recurrence_rule.by_year_day,
count: data.recurrence_rule.count,
};
} else {
this.recurrenceRule ??= null;
}
}

/**
Expand Down
20 changes: 20 additions & 0 deletions packages/discord.js/src/util/APITypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuildMember}
*/

/**
* @external APIGuildScheduledEventRecurrenceRule
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/interface/APIGuildScheduledEventRecurrenceRule}
*/

/**
* @external APIInteraction
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10#APIInteraction}
Expand Down Expand Up @@ -375,6 +380,21 @@
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildScheduledEventPrivacyLevel}
*/

/**
* @external GuildScheduledEventRecurrenceRuleFrequency
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildScheduledEventRecurrenceRuleFrequency}
*/

/**
* @external GuildScheduledEventRecurrenceRuleMonth
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildScheduledEventRecurrenceRuleMonth}
*/

/**
* @external GuildScheduledEventRecurrenceRuleWeekday
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildScheduledEventRecurrenceRuleWeekday}
*/

/**
* @external GuildScheduledEventStatus
* @see {@link https://discord-api-types.dev/api/discord-api-types-v10/enum/GuildScheduledEventStatus}
Expand Down
24 changes: 23 additions & 1 deletion packages/discord.js/src/util/Transformers.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,26 @@ function _transformAPIAutoModerationAction(autoModerationAction) {
};
}

module.exports = { toSnakeCase, _transformAPIAutoModerationAction };
/**
* Transforms a guild scheduled event recurrence rule object to a snake-cased variant.
* @param {GuildScheduledEventRecurrenceRuleOptions} recurrenceRule The recurrence rule to transform
* @returns {APIGuildScheduledEventRecurrenceRule}
* @ignore
*/
function _transformGuildScheduledEventRecurrenceRule(recurrenceRule) {
return {
start: new Date(recurrenceRule.startAt).toISOString(),
// eslint-disable-next-line eqeqeq
end: recurrenceRule.endAt != null ? new Date(recurrenceRule.endAt).toISOString() : recurrenceRule.endAt,
frequency: recurrenceRule.frequency,
interval: recurrenceRule.interval,
by_weekday: recurrenceRule.byWeekday,
by_n_weekday: recurrenceRule.byNWeekday,
by_month: recurrenceRule.byMonth,
by_month_day: recurrenceRule.byMonthDay,
by_year_day: recurrenceRule.byYearDay,
count: recurrenceRule.count,
};
}

module.exports = { toSnakeCase, _transformAPIAutoModerationAction, _transformGuildScheduledEventRecurrenceRule };
38 changes: 38 additions & 0 deletions packages/discord.js/typings/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ import {
APISelectMenuDefaultValue,
SelectMenuDefaultValueType,
InviteType,
GuildScheduledEventRecurrenceRuleWeekday,
GuildScheduledEventRecurrenceRuleMonth,
GuildScheduledEventRecurrenceRuleFrequency,
} from 'discord-api-types/v10';
import { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
Expand Down Expand Up @@ -1728,6 +1731,7 @@ export class GuildScheduledEvent<Status extends GuildScheduledEventStatus = Guil
public entityMetadata: GuildScheduledEventEntityMetadata | null;
public userCount: number | null;
public creator: User | null;
public recurrenceRule: GuildScheduledEventRecurrenceRule | null;
public get createdTimestamp(): number;
public get createdAt(): Date;
public get scheduledStartAt(): Date | null;
Expand Down Expand Up @@ -1766,6 +1770,26 @@ export class GuildScheduledEvent<Status extends GuildScheduledEventStatus = Guil
public isScheduled(): this is GuildScheduledEvent<GuildScheduledEventStatus.Scheduled>;
}

export interface GuildScheduledEventRecurrenceRule {
startTimestamp: number;
get startAt(): Date;
endTimestamp: number | null;
get endAt(): Date | null;
frequency: GuildScheduledEventRecurrenceRuleFrequency;
interval: number;
byWeekday: readonly GuildScheduledEventRecurrenceRuleWeekday[] | null;
byNWeekday: readonly GuildScheduledEventRecurrenceRuleNWeekday[] | null;
byMonth: readonly GuildScheduledEventRecurrenceRuleMonth[] | null;
byMonthDay: readonly number[] | null;
byYearDay: readonly number[] | null;
count: number | null;
}

export interface GuildScheduledEventRecurrenceRuleNWeekday {
n: number;
day: GuildScheduledEventRecurrenceRuleWeekday;
}

export class GuildTemplate extends Base {
private constructor(client: Client<true>, data: RawGuildTemplateData);
public createdTimestamp: number;
Expand Down Expand Up @@ -6069,6 +6093,20 @@ export interface GuildScheduledEventCreateOptions {
entityMetadata?: GuildScheduledEventEntityMetadataOptions;
image?: BufferResolvable | Base64Resolvable | null;
reason?: string;
recurrenceRule?: GuildScheduledEventRecurrenceRuleOptions;
}

export interface GuildScheduledEventRecurrenceRuleOptions {
startAt: DateResolvable;
endAt: DateResolvable;
frequency: GuildScheduledEventRecurrenceRuleFrequency;
interval: number;
byWeekday: readonly GuildScheduledEventRecurrenceRuleWeekday[];
byNWeekday: readonly GuildScheduledEventRecurrenceRuleNWeekday[];
byMonth: readonly GuildScheduledEventRecurrenceRuleMonth[];
byMonthDay: readonly number[];
byYearDay: readonly number[];
count: number;
}

export interface GuildScheduledEventEditOptions<
Expand Down

0 comments on commit 4fd0f9d

Please sign in to comment.