fix(scheduled-task): enable worker by default and add WeChat preflight
Memind CI / Test, build, and release guards (push) Has been cancelled
Memind CI / Test, build, and release guards (push) Has been cancelled
Scheduled automations were saved but never executed because the worker required an explicit env flag. Follow H5_REMINDER_WORKER_ENABLED when unset, add WeChat preflight to write tasks deterministically, and surface worker warnings on create. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import {
|
||||
buildScheduledTaskCreatePayload,
|
||||
formatScheduledTaskClarification,
|
||||
formatScheduledTaskCreateReply,
|
||||
formatScheduledTaskListReply,
|
||||
isScheduledTaskIntent,
|
||||
parseScheduledTaskIntent,
|
||||
} from '../../scheduled-task-intent.mjs';
|
||||
import {
|
||||
isScheduledTaskWorkerEnabled,
|
||||
scheduledTaskWorkerDisabledMessage,
|
||||
} from '../../scheduled-task-worker-config.mjs';
|
||||
|
||||
const WEEKDAY_NAMES = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
|
||||
export async function handleWechatScheduledTaskIntent({
|
||||
intent,
|
||||
user,
|
||||
scheduledTaskService,
|
||||
env = process.env,
|
||||
logger = console,
|
||||
}) {
|
||||
if (!scheduledTaskService) return null;
|
||||
|
||||
const timezone = env.H5_DEFAULT_TIMEZONE || 'Asia/Shanghai';
|
||||
const taskIntent = parseScheduledTaskIntent(intent.agentText, {
|
||||
timezone,
|
||||
});
|
||||
if (!isScheduledTaskIntent(taskIntent)) return null;
|
||||
|
||||
try {
|
||||
if (taskIntent.action === 'list_scheduled_tasks') {
|
||||
const tasks = await scheduledTaskService.listTasks({
|
||||
userId: user.userId,
|
||||
status: 'active',
|
||||
limit: 20,
|
||||
});
|
||||
return formatScheduledTaskListReply(tasks);
|
||||
}
|
||||
|
||||
if (taskIntent.action === 'cancel_scheduled_task') {
|
||||
const titleMatch = String(intent.agentText ?? '')
|
||||
.replace(/(?:取消|停止|关闭|删除|我的|定时|自动|任务)/gu, ' ')
|
||||
.trim();
|
||||
if (!titleMatch) {
|
||||
return '请告诉我要取消哪一条定时任务,或提供任务名称关键词。';
|
||||
}
|
||||
const task = await scheduledTaskService.cancelTask({
|
||||
userId: user.userId,
|
||||
titleMatch,
|
||||
});
|
||||
return `已取消定时任务:${task.title}`;
|
||||
}
|
||||
|
||||
if (taskIntent.action === 'create_scheduled_task') {
|
||||
if (taskIntent.needsClarification?.length) {
|
||||
return formatScheduledTaskClarification(taskIntent);
|
||||
}
|
||||
const payload = buildScheduledTaskCreatePayload(taskIntent, {
|
||||
userId: user.userId,
|
||||
sourceChannel: 'wechat',
|
||||
sourceMessageId: intent.msgId || null,
|
||||
sourceText: intent.agentText,
|
||||
timezone,
|
||||
});
|
||||
const task = await scheduledTaskService.createTask(payload);
|
||||
const workerWarning = isScheduledTaskWorkerEnabled(env)
|
||||
? null
|
||||
: scheduledTaskWorkerDisabledMessage(env);
|
||||
let reply = formatScheduledTaskCreateReply(task, { workerWarning });
|
||||
if (task.recurrence === 'weekly' && task.weekday != null) {
|
||||
reply = reply.replace(
|
||||
/执行时间:/,
|
||||
`执行时间:${WEEKDAY_NAMES[Number(task.weekday)] ?? ''} `,
|
||||
);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.warn?.(
|
||||
'[wechat-scheduled-task] preflight failed:',
|
||||
err instanceof Error ? err.message : err,
|
||||
);
|
||||
return `定时任务设置失败:${err instanceof Error ? err.message : String(err)}`;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { handleWechatScheduledTaskIntent } from './scheduled-task.mjs';
|
||||
|
||||
test('handleWechatScheduledTaskIntent creates daily automation task', async () => {
|
||||
const created = [];
|
||||
const reply = await handleWechatScheduledTaskIntent({
|
||||
intent: {
|
||||
msgType: 'text',
|
||||
agentText: '每天6点帮我做今日新闻页面',
|
||||
msgId: 'msg-1',
|
||||
},
|
||||
user: { userId: 'user-1' },
|
||||
scheduledTaskService: {
|
||||
async createTask(input) {
|
||||
created.push(input);
|
||||
return {
|
||||
...input,
|
||||
id: 'task-1',
|
||||
nextRunAt: Date.now() + 3600_000,
|
||||
};
|
||||
},
|
||||
},
|
||||
env: { H5_SCHEDULED_TASK_WORKER_ENABLED: '1' },
|
||||
});
|
||||
|
||||
assert.equal(created.length, 1);
|
||||
assert.equal(created[0].hour, 6);
|
||||
assert.match(created[0].taskSpec, /今日新闻页面/u);
|
||||
assert.match(reply, /已设置每天定时任务/u);
|
||||
});
|
||||
|
||||
test('handleWechatScheduledTaskIntent warns when worker disabled', async () => {
|
||||
const reply = await handleWechatScheduledTaskIntent({
|
||||
intent: {
|
||||
msgType: 'text',
|
||||
agentText: '每天6点帮我做今日新闻页面',
|
||||
msgId: 'msg-1',
|
||||
},
|
||||
user: { userId: 'user-1' },
|
||||
scheduledTaskService: {
|
||||
async createTask(input) {
|
||||
return { ...input, id: 'task-1', nextRunAt: Date.now() + 3600_000 };
|
||||
},
|
||||
},
|
||||
env: { H5_SCHEDULED_TASK_WORKER_ENABLED: '0', H5_REMINDER_WORKER_ENABLED: '0' },
|
||||
});
|
||||
|
||||
assert.match(reply, /⚠️/u);
|
||||
assert.match(reply, /未开启定时自动执行 Worker/u);
|
||||
});
|
||||
|
||||
test('handleWechatScheduledTaskIntent returns clarification for incomplete request', async () => {
|
||||
const reply = await handleWechatScheduledTaskIntent({
|
||||
intent: {
|
||||
msgType: 'text',
|
||||
agentText: '帮我设一个定时自动任务',
|
||||
msgId: 'msg-1',
|
||||
},
|
||||
user: { userId: 'user-1' },
|
||||
scheduledTaskService: {
|
||||
async createTask() {
|
||||
throw new Error('should not create');
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.match(reply, /执行时间和任务内容/u);
|
||||
});
|
||||
|
||||
test('handleWechatScheduledTaskIntent ignores non automation text', async () => {
|
||||
const reply = await handleWechatScheduledTaskIntent({
|
||||
intent: {
|
||||
msgType: 'text',
|
||||
agentText: '明天下午三点提醒我开会',
|
||||
msgId: 'msg-1',
|
||||
},
|
||||
user: { userId: 'user-1' },
|
||||
scheduledTaskService: {
|
||||
async createTask() {
|
||||
throw new Error('should not create');
|
||||
},
|
||||
},
|
||||
});
|
||||
assert.equal(reply, null);
|
||||
});
|
||||
Reference in New Issue
Block a user