fix(qqbot): use Gateway timezone for reminders (#116294)

This commit is contained in:
Vincent Koc
2026-07-30 16:32:18 +08:00
committed by GitHub
parent 835dafb5a8
commit 91dca69dae
2 changed files with 57 additions and 6 deletions

View File

@@ -59,6 +59,54 @@ describe("engine/tools/remind-logic", () => {
});
});
it.each([
{
name: "uses the Gateway timezone when omitted",
timezone: undefined,
expectedSchedule: { kind: "cron", expr: "0 9 * * *" },
expectedSummary: '⏰ Recurring reminder: "test reminder" (0 9 * * *, tz=gateway local)',
},
{
name: "preserves an explicit IANA timezone",
timezone: " America/New_York ",
expectedSchedule: {
kind: "cron",
expr: "0 9 * * *",
tz: "America/New_York",
},
expectedSummary: '⏰ Recurring reminder: "test reminder" (0 9 * * *, tz=America/New_York)',
},
])("$name for recurring reminders", async ({ timezone, expectedSchedule, expectedSummary }) => {
const calls: RemindCronAction[] = [];
const result = await executeScheduledRemind(
{
action: "add",
content: "test reminder",
to: "qqbot:c2c:123",
time: "0 9 * * *",
...(timezone ? { timezone } : {}),
},
{},
async (params) => {
calls.push(params);
return { id: "job-cron" };
},
);
const call = calls[0];
expect(call?.action).toBe("add");
if (call?.action !== "add") {
throw new Error("expected add cron action");
}
expect(call.job.schedule).toEqual(expectedSchedule);
expect(result.details).toEqual({
ok: true,
action: "add",
summary: expectedSummary,
cronResult: { id: "job-cron" },
});
});
it("runs cron list and remove through the scheduler", async () => {
const calls: unknown[] = [];
await executeScheduledRemind({ action: "list" }, {}, async (params) => {

View File

@@ -27,8 +27,6 @@ export interface RemindParams {
jobId?: string;
}
const QQBOT_DEFAULT_REMINDER_TIMEZONE = "Asia/Shanghai";
/**
* Context supplied by the bridge layer so the engine can remain free of
* framework / AsyncLocalStorage dependencies. `fallbackTo` and
@@ -99,7 +97,7 @@ export const RemindSchema = {
timezone: {
type: "string",
description:
"Optional IANA timezone used for cron reminders. Include it when the user provides or confirms a timezone; if omitted, QQBot preserves its existing default timezone.",
"Optional IANA timezone used for cron reminders. Include it when the user provides or confirms a timezone; if omitted, Gateway cron uses the host timezone.",
},
name: {
type: "string",
@@ -226,12 +224,16 @@ function buildOnceJob(params: RemindParams, atMs: number, to: string, accountId:
function buildCronJob(params: RemindParams, to: string, accountId: string) {
const content = params.content!;
const name = params.name || generateJobName(content);
const tz = params.timezone || QQBOT_DEFAULT_REMINDER_TIMEZONE;
const timezone = params.timezone?.trim();
return {
action: "add" as const,
job: {
name,
schedule: { kind: "cron" as const, expr: params.time!.trim(), tz },
schedule: {
kind: "cron" as const,
expr: params.time!.trim(),
...(timezone ? { tz: timezone } : {}),
},
sessionTarget: "isolated" as const,
wakeMode: "now" as const,
payload: {
@@ -309,11 +311,12 @@ function prepareRemindCronAction(
const resolvedAccountId = ctx.fallbackAccountId || "default";
if (isCronExpression(params.time)) {
const timezone = params.timezone?.trim();
return {
ok: true,
action: "add",
cronAction: buildCronJob(params, resolvedTo, resolvedAccountId),
summary: `⏰ Recurring reminder: "${params.content}" (${params.time}, tz=${params.timezone || QQBOT_DEFAULT_REMINDER_TIMEZONE})`,
summary: `⏰ Recurring reminder: "${params.content}" (${params.time}, tz=${timezone || "gateway local"})`,
};
}