Skip to content

Commit 5d132bf

Browse files
committed
feat(custom-payload): add now datetime variables
1 parent ca88c3d commit 5d132bf

5 files changed

Lines changed: 125 additions & 6 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ Easily manage and trigger webhooks directly from your browser! Compatible with F
4141
**🧪 Test a Webhook:**
4242
- When adding or editing a webhook, click the 'Test' button to send a test payload to your URL.
4343

44+
**🕒 DateTime Variables in Custom Payload:**
45+
- You can use `{{now.*}}` placeholders in custom payload JSON, including UTC values like `{{now.iso}}`, `{{now.date}}`, `{{now.time}}`, `{{now.unix}}`, and local values like `{{now.local.iso}}`.
46+
- Existing `{{triggeredAt}}` remains available as an alias for `{{now.iso}}` for backward compatibility.
47+
4448
**🗂️ Organize into Groups:**
4549
- Use the group management dialog to add, delete, rename, or reorder groups via drag-and-drop.
4650

options/options.html

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,27 @@ <h2>__MSG_optionsAddWebhookHeader__</h2>
131131
<li><code>{{tab.audible}}</code> - Is tab playing audio</li>
132132
<li><code>{{tab.incognito}}</code> - Is tab in incognito mode</li>
133133
<li><code>{{tab.status}}</code> - Tab loading status</li>
134-
<li><code>{{platform.arch}}</code> - Plaform architecture</li>
134+
<li><code>{{platform.arch}}</code> - Platform architecture</li>
135135
<li><code>{{platform.os}}</code> - Operating system</li>
136136
<li><code>{{platform.version}}</code> - Platform version</li>
137137
<li><code>{{browser}}</code> - Browser information</li>
138138
<li><code>{{platform}}</code> - Platform information</li>
139-
<li><code>{{triggeredAt}}</code> - Timestamp when triggered</li>
139+
<li><code>{{now.iso}}</code> - Current UTC time as ISO 8601</li>
140+
<li><code>{{now.date}}</code> - Current UTC date (YYYY-MM-DD)</li>
141+
<li><code>{{now.time}}</code> - Current UTC time (HH:mm:ss)</li>
142+
<li><code>{{now.unix}}</code> - Current UTC Unix timestamp in seconds</li>
143+
<li><code>{{now.unix_ms}}</code> - Current UTC Unix timestamp in milliseconds</li>
144+
<li><code>{{now.year}}</code> - Current UTC year</li>
145+
<li><code>{{now.month}}</code> - Current UTC month (1-12)</li>
146+
<li><code>{{now.day}}</code> - Current UTC day of month (1-31)</li>
147+
<li><code>{{now.hour}}</code> - Current UTC hour (0-23)</li>
148+
<li><code>{{now.minute}}</code> - Current UTC minute (0-59)</li>
149+
<li><code>{{now.second}}</code> - Current UTC second (0-59)</li>
150+
<li><code>{{now.millisecond}}</code> - Current UTC millisecond (0-999)</li>
151+
<li><code>{{now.local.iso}}</code> - Current local time as ISO 8601</li>
152+
<li><code>{{now.local.date}}</code> - Current local date (YYYY-MM-DD)</li>
153+
<li><code>{{now.local.time}}</code> - Current local time (HH:mm:ss)</li>
154+
<li><code>{{triggeredAt}}</code> - Alias for <code>{{now.iso}}</code></li>
140155
<li><code>{{identifier}}</code> - Custom identifier</li>
141156
</ul>
142157
</div>

options/options.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,10 @@ const availableVariables = [
663663
"{{tab.index}}", "{{tab.pinned}}", "{{tab.audible}}", "{{tab.incognito}}",
664664
"{{tab.status}}", "{{browser}}", "{{platform}}", "{{triggeredAt}}", "{{identifier}}",
665665
"{{platform.arch}}", "{{platform.os}}", "{{platform.version}}",
666+
"{{now.iso}}", "{{now.date}}", "{{now.time}}", "{{now.unix}}", "{{now.unix_ms}}",
667+
"{{now.year}}", "{{now.month}}", "{{now.day}}", "{{now.hour}}", "{{now.minute}}",
668+
"{{now.second}}", "{{now.millisecond}}", "{{now.local.iso}}", "{{now.local.date}}",
669+
"{{now.local.time}}",
666670
];
667671

668672
// Implement autocompletion for custom payload

tests/sendWebhook.test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ describe('sendWebhook', () => {
5050
global.fetch = originalFetch;
5151
global.browser = originalBrowser;
5252
console.error = originalConsoleError;
53+
jest.useRealTimers();
5354
jest.clearAllMocks();
5455
});
5556

@@ -174,4 +175,51 @@ describe('sendWebhook', () => {
174175
await expect(sendWebhook(webhook, false))
175176
.rejects.toThrow('popupErrorCustomPayloadJsonParseError');
176177
});
178+
179+
test('should replace all new now datetime placeholders', async () => {
180+
jest.useFakeTimers();
181+
jest.setSystemTime(new Date('2025-08-07T10:20:30.123Z'));
182+
183+
const webhook = {
184+
url: 'https://custom.com',
185+
customPayload: '{"iso":"{{now.iso}}","date":"{{now.date}}","time":"{{now.time}}","unix":{{now.unix}},"unixMs":{{now.unix_ms}},"year":{{now.year}},"month":{{now.month}},"day":{{now.day}},"hour":{{now.hour}},"minute":{{now.minute}},"second":{{now.second}},"millisecond":{{now.millisecond}},"localIso":"{{now.local.iso}}","localDate":"{{now.local.date}}","localTime":"{{now.local.time}}"}'
186+
};
187+
188+
await sendWebhook(webhook, false);
189+
190+
const fetchBody = JSON.parse(global.fetch.mock.calls[0][1].body);
191+
expect(fetchBody.iso).toBe('2025-08-07T10:20:30.123Z');
192+
expect(fetchBody.date).toBe('2025-08-07');
193+
expect(fetchBody.time).toBe('10:20:30');
194+
expect(fetchBody.unix).toBe(1754562030);
195+
expect(fetchBody.unixMs).toBe(1754562030123);
196+
expect(fetchBody.year).toBe(2025);
197+
expect(fetchBody.month).toBe(8);
198+
expect(fetchBody.day).toBe(7);
199+
expect(fetchBody.hour).toBe(10);
200+
expect(fetchBody.minute).toBe(20);
201+
expect(fetchBody.second).toBe(30);
202+
expect(fetchBody.millisecond).toBe(123);
203+
expect(fetchBody.localDate).toMatch(/^\d{4}-\d{2}-\d{2}$/);
204+
expect(fetchBody.localTime).toMatch(/^\d{2}:\d{2}:\d{2}$/);
205+
expect(fetchBody.localIso).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}[+-]\d{2}:\d{2}$/);
206+
207+
});
208+
209+
test('should keep triggeredAt as alias for now.iso', async () => {
210+
jest.useFakeTimers();
211+
jest.setSystemTime(new Date('2025-08-07T10:20:30.123Z'));
212+
213+
const webhook = {
214+
url: 'https://custom.com',
215+
customPayload: '{"now":"{{now.iso}}","legacy":"{{triggeredAt}}"}'
216+
};
217+
218+
await sendWebhook(webhook, false);
219+
220+
const fetchBody = JSON.parse(global.fetch.mock.calls[0][1].body);
221+
expect(fetchBody.now).toBe('2025-08-07T10:20:30.123Z');
222+
expect(fetchBody.legacy).toBe('2025-08-07T10:20:30.123Z');
223+
224+
});
177225
});

utils/utils.js

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,52 @@ function replaceI18nPlaceholders() {
8383
}
8484
}
8585

86+
const padDatePart = (value, length = 2) => String(value).padStart(length, '0');
87+
88+
const toLocalIsoString = (date) => {
89+
const year = date.getFullYear();
90+
const month = padDatePart(date.getMonth() + 1);
91+
const day = padDatePart(date.getDate());
92+
const hour = padDatePart(date.getHours());
93+
const minute = padDatePart(date.getMinutes());
94+
const second = padDatePart(date.getSeconds());
95+
const millisecond = padDatePart(date.getMilliseconds(), 3);
96+
const offsetMinutes = -date.getTimezoneOffset();
97+
const sign = offsetMinutes >= 0 ? '+' : '-';
98+
const absoluteOffset = Math.abs(offsetMinutes);
99+
const offsetHour = padDatePart(Math.floor(absoluteOffset / 60));
100+
const offsetMinute = padDatePart(absoluteOffset % 60);
101+
102+
return `${year}-${month}-${day}T${hour}:${minute}:${second}.${millisecond}${sign}${offsetHour}:${offsetMinute}`;
103+
};
104+
105+
const buildDateTimeVariables = (date = new Date()) => {
106+
const nowIso = date.toISOString();
107+
const timestampMs = date.getTime();
108+
109+
return {
110+
nowIso,
111+
values: {
112+
"{{now.iso}}": nowIso,
113+
"{{now.date}}": nowIso.slice(0, 10),
114+
"{{now.time}}": nowIso.slice(11, 19),
115+
"{{now.unix}}": Math.floor(timestampMs / 1000),
116+
"{{now.unix_ms}}": timestampMs,
117+
"{{now.year}}": date.getUTCFullYear(),
118+
"{{now.month}}": date.getUTCMonth() + 1,
119+
"{{now.day}}": date.getUTCDate(),
120+
"{{now.hour}}": date.getUTCHours(),
121+
"{{now.minute}}": date.getUTCMinutes(),
122+
"{{now.second}}": date.getUTCSeconds(),
123+
"{{now.millisecond}}": date.getUTCMilliseconds(),
124+
"{{now.local.iso}}": toLocalIsoString(date),
125+
"{{now.local.date}}": `${date.getFullYear()}-${padDatePart(date.getMonth() + 1)}-${padDatePart(date.getDate())}`,
126+
"{{now.local.time}}": `${padDatePart(date.getHours())}:${padDatePart(date.getMinutes())}:${padDatePart(date.getSeconds())}`,
127+
"{{triggeredAt}}": nowIso,
128+
},
129+
};
130+
};
131+
86132
async function sendWebhook(webhook, isTest = false) {
87133
const browserAPI = getBrowserAPI();
88134
let selectors = Array.isArray(webhook?.selectors) ? [...webhook.selectors] : [];
@@ -91,11 +137,13 @@ async function sendWebhook(webhook, isTest = false) {
91137
try {
92138
let payload;
93139

140+
const dateTimeVariables = buildDateTimeVariables();
141+
94142
if (isTest) {
95143
payload = {
96144
url: "https://example.com",
97145
test: true,
98-
triggeredAt: new Date().toISOString(),
146+
triggeredAt: dateTimeVariables.nowIso,
99147
};
100148
} else {
101149
// Get info about the active tab
@@ -185,7 +233,7 @@ async function sendWebhook(webhook, isTest = false) {
185233
},
186234
browser: browserInfo,
187235
platform: platformInfo,
188-
triggeredAt: new Date().toISOString(),
236+
triggeredAt: dateTimeVariables.nowIso,
189237
};
190238

191239
if (webhook && webhook.identifier) {
@@ -212,9 +260,9 @@ async function sendWebhook(webhook, isTest = false) {
212260
"{{platform.arch}}": platformInfo.arch || "unknown",
213261
"{{platform.os}}": platformInfo.os || "unknown",
214262
"{{platform.version}}": platformInfo.version,
215-
"{{triggeredAt}}": new Date().toISOString(),
216263
"{{identifier}}": webhook.identifier || "",
217-
"{{selectorContent}}": selectorContent
264+
"{{selectorContent}}": selectorContent,
265+
...dateTimeVariables.values,
218266
};
219267

220268
let customPayloadStr = webhook.customPayload;

0 commit comments

Comments
 (0)