feat: recurring scheduled events (#10447)

* feat: recurring scheduled events

* fix: nullable on patch

* docs: remove unnecessary parenthesis

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>

---------

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
This commit is contained in:
Almeida
2024-09-29 19:41:57 +01:00
committed by Jiralite
parent c12217829b
commit 97c3237a70
7 changed files with 169 additions and 4 deletions

View File

@@ -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.
@@ -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
@@ -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
*/
/**
@@ -81,6 +99,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;
let entity_metadata, channel_id;
@@ -104,6 +123,7 @@ class GuildScheduledEventManager extends CachedManager {
entity_type: entityType,
entity_metadata,
image: image && (await resolveImage(image)),
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});
@@ -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
*/
/**
@@ -203,6 +225,7 @@ class GuildScheduledEventManager extends CachedManager {
entityMetadata,
reason,
image,
recurrenceRule,
} = options;
let entity_metadata;
@@ -224,6 +247,7 @@ class GuildScheduledEventManager extends CachedManager {
status,
image: image && (await resolveImage(image)),
entity_metadata,
recurrence_rule: recurrenceRule && _transformGuildScheduledEventRecurrenceRule(recurrenceRule),
},
reason,
});

View File

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

View File

@@ -100,6 +100,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}
@@ -390,6 +395,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}

View File

@@ -54,4 +54,31 @@ function _transformAPIMessageInteractionMetadata(client, messageInteractionMetad
};
}
module.exports = { toSnakeCase, _transformAPIAutoModerationAction, _transformAPIMessageInteractionMetadata };
/**
* 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,
_transformAPIMessageInteractionMetadata,
_transformGuildScheduledEventRecurrenceRule,
};

View File

@@ -2,7 +2,7 @@
'use strict';
const { token } = require('./auth.js');
const { token, owner } = require('./auth.js');
const { Client } = require('../src');
const { ChannelType, GatewayIntentBits } = require('discord-api-types/v10');
@@ -14,6 +14,7 @@ const client = new Client({
GatewayIntentBits.GuildMessages,
GatewayIntentBits.GuildMessageReactions,
GatewayIntentBits.GuildMembers,
GatewayIntentBits.MessageContent,
],
});
@@ -186,7 +187,7 @@ client.on('messageCreate', msg => {
msg.channel.send(`\`\`\`${msg.content}\`\`\``);
}
if (msg.content.startsWith('#eval') && msg.author.id === '66564597481480192') {
if (msg.content.startsWith('#eval') && msg.author.id === owner) {
try {
const com = eval(msg.content.split(' ').slice(1).join(' '));
msg.channel.send(`\`\`\`\n${com}\`\`\``);

View File

@@ -186,6 +186,9 @@ import {
ReactionType,
APIAuthorizingIntegrationOwnersMap,
MessageReferenceType,
GuildScheduledEventRecurrenceRuleWeekday,
GuildScheduledEventRecurrenceRuleMonth,
GuildScheduledEventRecurrenceRuleFrequency,
} from 'discord-api-types/v10';
import { ChildProcess } from 'node:child_process';
import { EventEmitter } from 'node:events';
@@ -1779,6 +1782,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;
@@ -1817,6 +1821,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;
@@ -6167,14 +6191,29 @@ 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<
Status extends GuildScheduledEventStatus,
AcceptableStatus extends GuildScheduledEventSetStatusArg<Status>,
> extends Omit<Partial<GuildScheduledEventCreateOptions>, 'channel'> {
> extends Omit<Partial<GuildScheduledEventCreateOptions>, 'channel' | 'recurrenceRule'> {
channel?: GuildVoiceChannelResolvable | null;
status?: AcceptableStatus;
recurrenceRule?: GuildScheduledEventRecurrenceRuleOptions | null;
}
export interface GuildScheduledEventEntityMetadata {

View File

@@ -209,6 +209,7 @@ import {
ApplicationEmoji,
ApplicationEmojiManager,
StickerPack,
GuildScheduledEventManager,
SendableChannels,
PollData,
} from '.';
@@ -2618,3 +2619,6 @@ client.on('interactionCreate', interaction => {
interaction.channel.send({ embeds: [] });
}
});
declare const guildScheduledEventManager: GuildScheduledEventManager;
await guildScheduledEventManager.edit(snowflake, { recurrenceRule: null });