diff --git a/server/package.json b/server/package.json index 096790e0..0b978a45 100644 --- a/server/package.json +++ b/server/package.json @@ -31,6 +31,7 @@ "async-lock": "^1.4.1", "axios": "^1.6.1", "body-parser": "^1.20.2", + "chrono-node": "^2.7.7", "cloudflare": "^3.2.0", "colord": "^2.9.3", "colorette": "^2.0.20", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index 3a505e4d..6163540a 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: body-parser: specifier: ^1.20.2 version: 1.20.2 + chrono-node: + specifier: ^2.7.7 + version: 2.7.7 cloudflare: specifier: ^3.2.0 version: 3.2.0 @@ -819,6 +822,10 @@ packages: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} + chrono-node@2.7.7: + resolution: {integrity: sha512-p3S7gotuTPu5oqhRL2p1fLwQXGgdQaRTtWR3e8Di9P1Pa9mzkK5DWR5AWBieMUh2ZdOnPgrK+zCrbbtyuA+D/Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + cloudflare@3.2.0: resolution: {integrity: sha512-L6X3ky8rj+vFv6wf+/MxVVkwzMcxm+rjHtPAznHejYp3UC3ESovv+z8a6SZv6uXIXkAXHbCMCsbegiM4NNvXsg==} @@ -937,6 +944,9 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -3172,6 +3182,10 @@ snapshots: ansi-styles: 4.3.0 supports-color: 7.2.0 + chrono-node@2.7.7: + dependencies: + dayjs: 1.11.13 + cloudflare@3.2.0: dependencies: '@types/node': 18.19.33 @@ -3318,6 +3332,8 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + dayjs@1.11.13: {} + debug@2.6.9: dependencies: ms: 2.0.0 diff --git a/server/src/bot/commands/reminder/index.js b/server/src/bot/commands/reminder/index.js index 59526a65..7bb0c524 100644 --- a/server/src/bot/commands/reminder/index.js +++ b/server/src/bot/commands/reminder/index.js @@ -9,7 +9,7 @@ module.exports = { .setDescription('reminder') .addSubcommand(subcommand => subcommand.setName('create').setDescription('Create a reminder about something.') .addStringOption(option => option.setName('about').setDescription('What is the reminder about?').setRequired(true)) - .addStringOption(option => option.setName('when').setDescription('When should the reminder be sent?').setRequired(true))) + .addStringOption(option => option.setName('when').setDescription('Examples: tomorrow, in 9 hours, next week, next Friday at 3pm').setRequired(true))) .addSubcommand(subcommand => subcommand.setName('delete').setDescription('Delete a reminder.') .addStringOption(option => option.setName('reminder').setDescription('Select the reminder to delete.').setRequired(true).setAutocomplete(true))), execute: async interaction => { @@ -23,7 +23,7 @@ module.exports = { if (about.length > 512) return interaction.reply({ content: 'The reminder description must be 512 characters or less.', ephemeral: true }); var reminderTime = parseTimeDuration(when); - if (!reminderTime) return interaction.reply({ content: 'Invalid time duration. You can use `1d`, `1h`, `1m`, `1s` for days, hours, minutes, and seconds respectively.', ephemeral: true }); + if (!reminderTime) return interaction.reply({ content: 'Invalid time duration. Examples: `tomorrow`, `in 9 hours`, `next week`, `next Friday at 3pm`', ephemeral: true }); if (reminderTime < (60000 * 5)) return interaction.reply({ content: 'The reminder time must be at least 5 minutes.', ephemeral: true }); if (!interaction.deferred && !interaction.replied) await interaction.deferReply({ ephemeral: true }); diff --git a/server/src/utils/parseTimeDuration.js b/server/src/utils/parseTimeDuration.js index 3183f8fc..f0183f54 100644 --- a/server/src/utils/parseTimeDuration.js +++ b/server/src/utils/parseTimeDuration.js @@ -1,32 +1,7 @@ -function parseTimeDuration(duration) { - const regex = /(\d+)\s*(s|sec|second|m|min|minute|h|hour|d|day|w|week)/; - const match = duration.match(regex); - if (!match) return null; +const chrono = require('chrono-node/en'); - const value = parseInt(match[1]); - const unit = match[2].toLowerCase(); - switch (unit) { - case 's': - case 'sec': - case 'second': - return value * 1000; - case 'm': - case 'min': - case 'minute': - return value * 60 * 1000; - case 'h': - case 'hour': - return value * 60 * 60 * 1000; - case 'd': - case 'day': - return value * 24 * 60 * 60 * 1000; - case 'w': - case 'week': - return value * 7 * 24 * 60 * 60 * 1000; - default: - return null; - } +function parseTimeDuration(duration) { + return chrono.parseDate(duration); } - module.exports = parseTimeDuration; \ No newline at end of file