Skip to content

Commit

Permalink
feat: event-synced events
Browse files Browse the repository at this point in the history
  • Loading branch information
Clembs committed Jul 31, 2024
1 parent 7e45164 commit 2f891a2
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 33 deletions.
2 changes: 2 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ enum ReminderTypes {
MESSAGE
// reminder set for someone's birthday (deprecated feature)
BIRTHDAY
// reminder synced with a discord event
EVENT
}

enum RewardTypes {
Expand Down
25 changes: 21 additions & 4 deletions src/lib/timeouts/handleReminder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { formatUsername } from '$lib/functions/formatUsername';
import { getColor } from '$lib/functions/getColor';
import { t } from '$lib/language';
import { Reminder } from '@prisma/client';
import { snowflakeToDate, timestampMention } from '@purplet/utils';
import { formatGuildEventCoverURL, snowflakeToDate, timestampMention } from '@purplet/utils';
import { APIEmbedAuthor } from 'discord-api-types/v10';
import { Client, MessageButton, MessageOptions } from 'discord.js';
import { components, row } from 'purplet';
Expand All @@ -27,7 +27,9 @@ export interface LowBudgetMessage {

export async function handleReminder(reminder: Reminder, client: Client) {
const data = await extractReminder(reminder, client);
const createdAt = snowflakeToDate(data.messageId);
const createdAt = snowflakeToDate(data.messageId || data.event?.id);

if (data.event && data.event.status === 'CANCELED') return;

const message = {
embeds: [
Expand All @@ -36,13 +38,26 @@ export async function handleReminder(reminder: Reminder, client: Client) {
name: t(reminder.locale, 'REMINDER'),
icon_url: icons.reminder,
},
title: getReminderSubject(reminder, client, 0),
title: data.event?.name ?? getReminderSubject(reminder, client, 0),
description: data.event?.description,
fields: [
...(data.event?.scheduledEndAt
? [
{
name: t(reminder.locale, 'END_DATE'),
value: timestampMention(data.event.scheduledEndAt),
},
]
: []),
{
name: t(reminder.locale, 'CREATED_ON'),
value: `${timestampMention(createdAt)}${timestampMention(createdAt, 'R')}`,
},
],
image:
data.event && data.event.image
? { url: formatGuildEventCoverURL(data.event.id, data.event.image, { size: '512' }) }
: null,
color: await getColor(data.user),
},
...renderLowBudgetMessage(data, reminder.locale),
Expand All @@ -51,7 +66,9 @@ export async function handleReminder(reminder: Reminder, client: Client) {
row(
new MessageButton()
.setStyle('LINK')
.setLabel(t(reminder?.locale, 'JUMP_TO_MSG'))
.setLabel(
data.event ? t(reminder.locale, 'VIEW_DETAILS') : t(reminder.locale, 'JUMP_TO_MSG'),
)
.setURL(data.url),
// new SnoozeButton()
// .setStyle('SECONDARY')
Expand Down
31 changes: 24 additions & 7 deletions src/modules/tools/reminders/_helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { formatUsername } from '$lib/functions/formatUsername';
import { t } from '$lib/language';
import { LowBudgetMessage } from '$lib/timeouts/handleReminder';
import { Reminder, ReminderTypes } from '@prisma/client';
import { Client, GuildTextBasedChannel } from 'discord.js';
import { Client, Guild, GuildTextBasedChannel } from 'discord.js';

export async function getUserReminders(userId: string, force = false) {
return await fetchWithCache(
Expand Down Expand Up @@ -54,6 +54,7 @@ export async function extractReminder(reminder: Reminder, client: Client) {
let id: string;
let messageId: string;
let channelId: string;
let eventId: string;
let guildId: string;

if (reminder.type === ReminderTypes.MESSAGE) {
Expand All @@ -68,16 +69,28 @@ export async function extractReminder(reminder: Reminder, client: Client) {
id = reminder.id;
[guildId, channelId, messageId] = id.split('/');
}
if (reminder.type === ReminderTypes.EVENT) {
id = reminder.id;
[guildId, eventId] = id.split('/');
}

const channel = (await client.channels
.fetch(channelId)
.catch((e) => null)) as GuildTextBasedChannel;
const channel = channelId
? ((await client.channels.fetch(channelId).catch((e) => null)) as GuildTextBasedChannel)
: null;

const guild = channel?.guild || { id: '@me' };
const guild = channel?.guild || (await client.guilds.fetch(guildId)) || { id: '@me' };

const event =
reminder.type === ReminderTypes.EVENT && eventId && guild instanceof Guild
? await guild.scheduledEvents.fetch(eventId)
: null;

const user = await client.users.fetch(reminder.userId).catch((e) => null);

const url = `https://discord.com/channels/${guild.id}/${channel?.id ?? channelId}/${messageId}`;
const url =
channelId && !event
? `https://discord.com/channels/${guild.id}/${channel?.id ?? channelId}/${messageId}`
: event.url;
const details: LowBudgetMessage = reminder.details ? JSON.parse(reminder.details) : null;

const author = details
Expand All @@ -88,7 +101,9 @@ export async function extractReminder(reminder: Reminder, client: Client) {
return {
...reminder,
raw: reminder,
subject: getReminderSubject(reminder, client),
event,
endDate: event?.scheduledEndAt ?? reminder.endDate,
subject: event?.name ?? getReminderSubject(reminder, client),
guildId: guild.id,
channelId,
messageId,
Expand All @@ -101,3 +116,5 @@ export async function extractReminder(reminder: Reminder, client: Client) {
url,
};
}

export const EditableReminderTypes: ReminderTypes[] = [ReminderTypes.NORMAL, ReminderTypes.MESSAGE];
38 changes: 29 additions & 9 deletions src/modules/tools/reminders/reminder_list/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,18 @@ import { formatUsername } from '$lib/functions/formatUsername';
import { getColor } from '$lib/functions/getColor';
import { getAllLanguages, t } from '$lib/language';
import { renderLowBudgetMessage } from '$lib/timeouts/handleReminder';
import { Reminder } from '@prisma/client';
import { snowflakeToDate, timestampMention } from '@purplet/utils';
import { Reminder, ReminderTypes } from '@prisma/client';
import { formatGuildEventCoverURL, snowflakeToDate, timestampMention } from '@purplet/utils';
import dayjs from 'dayjs';
import dedent from 'dedent';
import { Interaction, MessageButton } from 'discord.js';
import { ButtonComponent, ChatCommand, components, row } from 'purplet';
import { extractReminder, getReminderSubject, getUserReminders } from '../_helpers';
import {
EditableReminderTypes,
extractReminder,
getReminderSubject,
getUserReminders,
} from '../_helpers';
import { DeleteReminderButton } from './DeleteReminderButton';
import { EditReminderButton } from './EditReminderButton';
import { ReminderSelectMenu } from './ReminderSelectMenu';
Expand Down Expand Up @@ -40,7 +45,8 @@ export const BackButton = ButtonComponent({
export async function renderReminder(this: Interaction, reminder: Reminder) {
const reminders = await getUserReminders(this.user.id);
const data = await extractReminder(reminder, this.client);
const created = data.messageId ? snowflakeToDate(data.messageId) : null;
const created =
data.messageId || data.event?.id ? snowflakeToDate(data.messageId || data.event?.id) : null;

return {
embeds: [
Expand All @@ -52,15 +58,24 @@ export async function renderReminder(this: Interaction, reminder: Reminder) {
)} (${reminders.length.toLocaleString(this.locale)})`,
icon_url: avatar(this.user, 64),
},
title: getReminderSubject(reminder, this.client, 0),
title: data.event?.name ?? getReminderSubject(reminder, this.client, 0),
description: dedent`
${timestampMention(data.endDate, 'R')}
${data.event?.description ?? ''}
${
data.event && data.event.scheduledEndAt
? `${timestampMention(data.event.scheduledStartAt)} - ${timestampMention(data.event.scheduledEndAt)}`
: timestampMention(data.endDate, 'R')
}
${t(this, 'REMINDER_DESTINATION')} ${
data.raw.destination === 'dm' ? t(this, 'DMS') : `${data.channel}`
}
${t(this, 'CREATED_ON')} ${created ? timestampMention(created, 'D') : '??'}${
created ? timestampMention(created, 'R') : '??'
}`,
image:
data.event && data.event.image
? { url: formatGuildEventCoverURL(data.event.id, data.event.image, { size: '512' }) }
: null,
color: await getColor(this.user),
},
...renderLowBudgetMessage(data, this.locale),
Expand All @@ -71,20 +86,25 @@ export async function renderReminder(this: Interaction, reminder: Reminder) {
new EditReminderButton(reminder.id)
.setLabel(t(this, 'EDIT'))
.setEmoji(emojis.buttons.edit)
.setStyle('PRIMARY'),
.setStyle('PRIMARY')
.setDisabled(!EditableReminderTypes.includes(data.type)),
new DeleteReminderButton(reminder.id)
.setLabel(t(this, 'DELETE'))
.setEmoji(emojis.buttons.trash)
.setStyle('DANGER'),
),
row(
...(data.id.endsWith('BIRTHDAY')
...(data.type === ReminderTypes.BIRTHDAY
? []
: [
new MessageButton()
.setStyle('LINK')
.setURL(data.url)
.setLabel(t(this, 'JUMP_TO_MSG')),
.setLabel(
data.type === ReminderTypes.EVENT
? t(this, 'VIEW_DETAILS')
: t(this, 'JUMP_TO_MSG'),
),
]),
new MessageButton()
.setStyle('LINK')
Expand Down
69 changes: 56 additions & 13 deletions src/modules/tools/reminders/reminder_new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,45 @@ export default ChatCommand({
description: t('en-US', 'remind me.meta.description'),
descriptionLocalizations: getAllLanguages('remind me.meta.description'),
options: new OptionBuilder()
.string('when', t('en-US', 'remind me.meta.options.0.description' as any), {
nameLocalizations: getAllLanguages('remind me.meta.options.0.name' as any, localeLower),
descriptionLocalizations: getAllLanguages('remind me.meta.options.0.description' as any),
autocomplete({ when }) {
return timeAutocomplete.call(this, when, '2y');
},
required: true,
})
.string('subject', t('en-US', 'remind me.meta.options.1.description' as any), {
nameLocalizations: getAllLanguages('SUBJECT', localeLower),
descriptionLocalizations: getAllLanguages('remind me.meta.options.1.description' as any),
async autocomplete({ subject }) {
const cachedEvents = this.guild.scheduledEvents.cache;

const events = (
cachedEvents.size ? cachedEvents : await this.guild.scheduledEvents.fetch()
).filter(
(event) =>
event.status !== 'COMPLETED' &&
event.status !== 'CANCELED' &&
event.name.toLowerCase().includes(subject.toLowerCase()),
);

return events.map((event) => ({
name: `Discord Event: ${event.name} (${event.scheduledStartAt.toLocaleString(this.locale)})`,
value: `event=${event.url}`,
}));
},
required: true,
minLength: 1,
maxLength: 512,
})
.string('when', t('en-US', 'remind me.meta.options.0.description' as any), {
nameLocalizations: getAllLanguages('remind me.meta.options.0.name' as any, localeLower),
descriptionLocalizations: getAllLanguages('remind me.meta.options.0.description' as any),
async autocomplete({ when, subject }) {
if (subject && subject.startsWith('event=')) {
const eventUrl = subject.split('=')[1];
const event = this.guild.scheduledEvents.cache.find((event) => event.url === eventUrl);

when = event?.scheduledStartAt.toISOString() ?? when;
}

return timeAutocomplete.call(this, when, '2y');
},
required: true,
})
.channel('destination', t('en-US', 'remind me.meta.options.2.description' as any), {
nameLocalizations: getAllLanguages('REMINDER_DESTINATION', localeLower),
descriptionLocalizations: getAllLanguages('remind me.meta.options.2.description' as any),
Expand All @@ -62,7 +86,7 @@ export default ChatCommand({
this,
t(this, 'ERROR_INVALID_DURATION', {
relativeTime: '2 years',
})
}),
);
}

Expand All @@ -73,7 +97,7 @@ export default ChatCommand({
this,
t(this, 'ERROR_MISSING_PERMISSIONS', {
PERMISSIONS: 'Send Messages',
})
}),
);
} else if (
!hasPerms(channel.permissionsFor(this.guild.me), PermissionFlagsBits.SendMessages)
Expand All @@ -82,7 +106,7 @@ export default ChatCommand({
this,
t(this, 'ERROR_MISSING_PERMISSIONS', {
PERMISSIONS: 'Send Messages',
})
}),
);
}
}
Expand All @@ -96,12 +120,31 @@ export default ChatCommand({

await this.deferReply();

const isEvent = subject.startsWith('event=https://discord.com/events/');

const msg = await this.fetchReply();
const url =
let url =
msg instanceof Message
? `${msg.guildId ?? '@me'}/${msg.channelId}/${msg.id}`
: `${msg.guild_id ?? '@me'}/${msg.channel_id}/${msg.id}`;

if (isEvent) {
const [guildId, eventId] = subject
.replace('event=https://discord.com/events/', '')
.split('/');

const guild = await this.client.guilds.fetch(guildId);
const event = await guild.scheduledEvents.fetch(eventId);

if (!event || event.status === 'COMPLETED' || event.status === 'CANCELED') {
return CRBTError(this, errors.EVENT_NOT_FOUND);
}

subject = event.name;
url = `${guildId}/${eventId}`;
endDate = dayjs(event.scheduledStartAt);
}

try {
const reminder = await dbTimeout(TimeoutTypes.Reminder, {
id: url,
Expand All @@ -110,7 +153,7 @@ export default ChatCommand({
userId: this.user.id,
subject,
locale: this.locale,
type: ReminderTypes.NORMAL,
type: isEvent ? ReminderTypes.EVENT : ReminderTypes.NORMAL,
details: null,
});
await getUserReminders(this.user.id, true);
Expand Down

0 comments on commit 2f891a2

Please sign in to comment.