Skip to content

Commit 296a482

Browse files
committed
feat: support Discord timestamps in reminders
1 parent 6795bf7 commit 296a482

3 files changed

Lines changed: 41 additions & 7 deletions

File tree

backend/src/plugins/Reminders/commands/RemindCmd.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { commandTypeHelpers as ct } from "../../../commandTypes.js";
33
import { registerUpcomingReminder } from "../../../data/loops/upcomingRemindersLoop.js";
44
import { humanizeDuration } from "../../../humanizeDuration.js";
55
import { convertDelayStringToMS, messageLink } from "../../../utils.js";
6+
import { parseDiscordTimestampToMS } from "../../../utils/parseDiscordTimestampToMS.js";
67
import { TimeAndDatePlugin } from "../../TimeAndDate/TimeAndDatePlugin.js";
78
import { remindersCmd } from "../types.js";
89

@@ -34,14 +35,19 @@ export const RemindCmd = remindersCmd({
3435
// Date and time in YYYY-MM-DD[T]HH:mm format
3536
reminderTime = moment.tz(args.time, "YYYY-M-D[T]HH:mm", tz).second(0);
3637
} else {
37-
// "Delay string" i.e. e.g. "2h30m"
38-
const ms = convertDelayStringToMS(args.time);
39-
if (ms === null) {
40-
void pluginData.state.common.sendErrorMessage(msg, "Invalid reminder time");
41-
return;
42-
}
38+
const timestampMs = parseDiscordTimestampToMS(args.time);
39+
if (timestampMs !== null) {
40+
reminderTime = moment.utc(timestampMs);
41+
} else {
42+
// "Delay string" i.e. e.g. "2h30m"
43+
const ms = convertDelayStringToMS(args.time);
44+
if (ms === null) {
45+
void pluginData.state.common.sendErrorMessage(msg, "Invalid reminder time");
46+
return;
47+
}
4348

44-
reminderTime = moment.utc().add(ms, "millisecond");
49+
reminderTime = moment.utc().add(ms, "millisecond");
50+
}
4551
}
4652

4753
if (!reminderTime.isValid() || reminderTime.isBefore(now)) {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import test from "ava";
2+
import { parseDiscordTimestampToMS } from "./parseDiscordTimestampToMS.js";
3+
4+
test("parseDiscordTimestampToMS: accepts timestamp markup", (t) => {
5+
t.is(parseDiscordTimestampToMS("<t:1767200400>"), 1_767_200_400_000);
6+
t.is(parseDiscordTimestampToMS("<t:1767200400:F>"), 1_767_200_400_000);
7+
t.is(parseDiscordTimestampToMS("<t:1767200400:s>"), 1_767_200_400_000);
8+
t.is(parseDiscordTimestampToMS("<t:1767200400:S>"), 1_767_200_400_000);
9+
});
10+
11+
test("parseDiscordTimestampToMS: rejects invalid timestamp markup", (t) => {
12+
t.is(parseDiscordTimestampToMS("1767200400"), null);
13+
t.is(parseDiscordTimestampToMS("<t:1767200400:invalid>"), null);
14+
t.is(parseDiscordTimestampToMS("<t:-1767200400>"), null);
15+
});
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function parseDiscordTimestampToMS(str: string): number | null {
2+
const match = str.match(/^<t:(\d+)(?::[tTdDfFsSR])?>$/);
3+
if (!match) {
4+
return null;
5+
}
6+
7+
const timestamp = Number(match[1]);
8+
if (!Number.isSafeInteger(timestamp)) {
9+
return null;
10+
}
11+
12+
return timestamp * 1000;
13+
}

0 commit comments

Comments
 (0)