diff --git a/astrbot/core/cron/manager.py b/astrbot/core/cron/manager.py index 9f3116f65..2f1ded3da 100644 --- a/astrbot/core/cron/manager.py +++ b/astrbot/core/cron/manager.py @@ -200,9 +200,18 @@ class CronJobManager: return None return aps_job.next_run_time.astimezone(timezone.utc) - async def _run_job(self, job_id: str) -> None: + async def run_job_now(self, job_id: str) -> None: + await self._run_job(job_id, ignore_enabled=True, delete_run_once=False) + + async def _run_job( + self, + job_id: str, + *, + ignore_enabled: bool = False, + delete_run_once: bool = True, + ) -> None: job = await self.db.get_cron_job(job_id) - if not job or not job.enabled: + if not job or (not job.enabled and not ignore_enabled): return start_time = datetime.now(timezone.utc) await self.db.update_cron_job( @@ -230,7 +239,7 @@ class CronJobManager: last_error=last_error, next_run_time=next_run, ) - if job.run_once: + if job.run_once and delete_run_once: # one-shot: remove after execution regardless of success await self.delete_job(job_id) diff --git a/astrbot/dashboard/routes/cron.py b/astrbot/dashboard/routes/cron.py index 8417c970c..5a86e11e7 100644 --- a/astrbot/dashboard/routes/cron.py +++ b/astrbot/dashboard/routes/cron.py @@ -1,3 +1,4 @@ +import asyncio import traceback from datetime import datetime, timezone @@ -20,6 +21,7 @@ class CronRoute(Route): ("/cron/jobs", ("POST", self.create_job)), ("/cron/jobs/", ("PATCH", self.update_job)), ("/cron/jobs/", ("DELETE", self.delete_job)), + ("/cron/jobs//run", ("POST", self.run_job_now)), ] self.register_routes() @@ -281,3 +283,19 @@ class CronRoute(Route): except Exception as e: # noqa: BLE001 logger.error(traceback.format_exc()) return jsonify(Response().error(f"Failed to delete job: {e!s}").__dict__) + + async def run_job_now(self, job_id: str): + try: + cron_mgr = self.core_lifecycle.cron_manager + if cron_mgr is None: + return jsonify( + Response().error("Cron manager not initialized").__dict__ + ) + job = await cron_mgr.db.get_cron_job(job_id) + if not job: + return jsonify(Response().error("Job not found").__dict__) + asyncio.create_task(cron_mgr.run_job_now(job_id)) + return jsonify(Response().ok(message="started").__dict__) + except Exception as e: # noqa: BLE001 + logger.error(traceback.format_exc()) + return jsonify(Response().error(f"Failed to run job: {e!s}").__dict__) diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css index 73e5e1b92..8e5fd76cd 100644 --- a/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css +++ b/dashboard/src/assets/mdi-subset/materialdesignicons-subset.css @@ -1,4 +1,4 @@ -/* Auto-generated MDI subset – 268 icons */ +/* Auto-generated MDI subset – 271 icons */ /* Do not edit manually. Run: pnpm run subset-icons */ @font-face { @@ -228,6 +228,10 @@ content: "\F0150"; } +.mdi-clock-time-four-outline::before { + content: "\F144E"; +} + .mdi-close::before { content: "\F0156"; } @@ -820,6 +824,10 @@ content: "\F040A"; } +.mdi-play-circle-outline::before { + content: "\F040D"; +} + .mdi-plus::before { content: "\F0415"; } @@ -892,6 +900,10 @@ content: "\F167A"; } +.mdi-send-outline::before { + content: "\F1165"; +} + .mdi-server::before { content: "\F048B"; } diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff index 79ae57535..181ce7f86 100644 Binary files a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff and b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff differ diff --git a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 index 13a0380b9..931625c6d 100644 Binary files a/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 and b/dashboard/src/assets/mdi-subset/materialdesignicons-webfont-subset.woff2 differ diff --git a/dashboard/src/i18n/locales/en-US/features/cron.json b/dashboard/src/i18n/locales/en-US/features/cron.json index a3bafd4d6..942bb02ff 100644 --- a/dashboard/src/i18n/locales/en-US/features/cron.json +++ b/dashboard/src/i18n/locales/en-US/features/cron.json @@ -6,8 +6,9 @@ "page": { "title": "Future Task Management", "beta": "Experimental", - "subtitle": "See scheduled tasks for AstrBot. AstrBot will wake up, run them, and deliver the results.", + "subtitle": "AstrBot wakes up at the scheduled time, completes the task, and sends the result back to the target conversation.", "proactive": { + "link": "Supported platforms", "supported": "Proactive delivery is available only on the configured platforms below", "unsupported": "No proactive messaging platforms enabled. Turn them on in Platform settings." } @@ -18,9 +19,34 @@ "refresh": "Refresh", "delete": "Delete", "cancel": "Cancel", + "close": "Close", + "more": "More actions", + "runNow": "Run now", "save": "Save", "submit": "Create" }, + "platformDialog": { + "title": "Supported Platforms", + "description": "Only the following platforms support proactive message delivery. AstrBot can send future task results back only to target conversations on these platforms." + }, + "card": { + "onceAt": "One-off · {time}", + "runAt": "Run at: {time}", + "nextRun": "Next run: {time}", + "dailyAt": "Daily at {time}", + "weeklyAt": "Every {day} at {time}", + "monthlyAt": "Monthly on day {day} at {time}", + "everyMinutes": "Every {count} minutes", + "everyHours": "Every {count} hours", + "everyDays": "Every {count} days", + "customCron": "Custom · {cron}" + }, + "filters": { + "search": "Search title and content", + "umo": "Filter by UMO", + "noUmos": "No UMOs", + "noMatches": "No matching future tasks." + }, "overview": { "totalTasks": "Total Tasks", "totalTasksNote": "Registered future tasks", @@ -83,11 +109,42 @@ "chatHint": "You can ask AstrBot in chat to create future tasks instead of adding them here.", "runOnce": "One-off task", "name": "Task name", - "note": "Task description", + "note": "Task requirements", + "scheduleMode": "Execution time", + "scheduleModes": { + "once": "One-off", + "interval": "Interval", + "daily": "Daily", + "weekly": "Weekly", + "monthly": "Monthly", + "cron": "Custom Cron" + }, + "intervalEvery": "Every", + "intervalUnit": "Unit", + "intervalUnits": { + "minutes": "Minutes", + "hours": "Hours", + "days": "Days" + }, + "dailyTime": "Time", + "weeklyDay": "Weekday", + "weeklyTime": "Time", + "monthlyDay": "Day", + "monthlyTime": "Time", + "weekdays": { + "sunday": "Sunday", + "monday": "Monday", + "tuesday": "Tuesday", + "wednesday": "Wednesday", + "thursday": "Thursday", + "friday": "Friday", + "saturday": "Saturday" + }, "cron": "Cron expression", "cronPlaceholder": "0 9 * * *", "runAt": "Run at", - "session": "Target session (platform_id:message_type:session_id)", + "session": "Deliver to", + "noUmos": "No sessions available", "timezone": "Timezone (optional, e.g. Asia/Shanghai)", "enabled": "Enabled" }, @@ -97,11 +154,14 @@ "updateFailed": "Failed to update", "deleteSuccess": "Deleted", "deleteFailed": "Failed to delete", - "sessionRequired": "Session is required", - "noteRequired": "Description is required", + "nameRequired": "Task name is required", + "sessionRequired": "Delivery target is required", + "noteRequired": "Task requirements are required", "cronRequired": "Cron expression is required", "runAtRequired": "Please select run time", "createSuccess": "Created successfully", - "createFailed": "Failed to create" + "createFailed": "Failed to create", + "runStarted": "Started", + "runFailed": "Failed to run" } } diff --git a/dashboard/src/i18n/locales/ru-RU/features/cron.json b/dashboard/src/i18n/locales/ru-RU/features/cron.json index 76b875ad1..63a767dde 100644 --- a/dashboard/src/i18n/locales/ru-RU/features/cron.json +++ b/dashboard/src/i18n/locales/ru-RU/features/cron.json @@ -1,107 +1,167 @@ { - "header": { - "eyebrow": "Automation", - "live": "Живая синхронизация" - }, - "page": { - "title": "Запланированные задачи", - "beta": "Экспериментальные функции", - "subtitle": "Управление будущими задачами AstrBot. Бот автоматически проснется, выполнит задачу и отправит результат. Требуется включить «Проактивные способности» в конфигурации.", - "proactive": { - "supported": "Отправка результатов поддерживается только на указанных ниже настроенных платформах", - "unsupported": "Нет платформ, поддерживающих проактивные сообщения. Включите их в настройках платформ." - } - }, - "actions": { - "create": "Новая задача", - "edit": "Изменить", - "refresh": "Обновить", - "delete": "Удалить", - "cancel": "Отмена", - "save": "Сохранить", - "submit": "Создать" - }, - "overview": { - "totalTasks": "Всего задач", - "totalTasksNote": "Все зарегистрированные будущие задачи", - "enabledTasks": "Активные задачи", - "enabledTasksNote": "Задачи, которые будут автоматически выполнены", - "oneOffTasks": "Разовые задачи", - "recurringTasksNote": "Повторяющихся задач: {count}", - "proactivePlatforms": "Проактивные платформы", - "proactivePlatformsNote": "Платформы, способные отправлять результат сами" - }, - "section": { - "registered": { - "title": "Список задач", - "subtitle": "Просматривайте зарегистрированные задачи, время запуска и состояние" - }, - "delivery": { - "title": "Статус доставки", - "subtitle": "После выполнения задачи результат будет отправлен обратно через поддерживаемые платформы", - "support": "Проактивная доставка", - "available": "Доступна", - "unavailable": "Недоступна", - "enabledPlatforms": "Включенные платформы" - }, - "quickCreate": { - "title": "Быстрое создание", - "runMode": "Режим задачи", - "target": "Целевой контекст" - }, - "platforms": { - "title": "Поддерживаемые платформы" - } - }, - "table": { - "title": "Список задач", - "subtitle": "Отслеживайте cron, целевую сессию, историю запусков и состояние", - "empty": "Задач пока нет.", - "headers": { - "name": "Имя", - "type": "Тип", - "cron": "Cron", - "session": "ID сессии", - "nextRun": "Следующий запуск", - "lastRun": "Последний запуск", - "note": "Описание", - "actions": "Действия" - }, - "type": { - "once": "Разовая", - "recurring": "Повторяющаяся", - "activeAgent": "Активный агент", - "workflow": "Рабочий процесс", - "unknown": "{type}" - }, - "timezoneLocal": "Местное время", - "notAvailable": "—" - }, - "form": { - "title": "Создать задачу", - "editTitle": "Редактировать задачу", - "chatHint": "Вы можете ставить задачи прямо в чате, AstrBot создаст их автоматически без заполнения этой формы.", - "runOnce": "Разовая задача", - "name": "Имя задачи", - "note": "Описание", - "cron": "Cron-выражения", - "cronPlaceholder": "0 9 * * *", - "runAt": "Время запуска", - "session": "Целевая сессия (platform_id:message_type:session_id)", - "timezone": "Часовой пояс (опционально, напр. Europe/Moscow)", - "enabled": "Включено" - }, - "messages": { - "loadFailed": "Ошибка загрузки задач", - "updateSuccess": "Задача обновлена", - "updateFailed": "Ошибка обновления", - "deleteSuccess": "Удалено", - "deleteFailed": "Ошибка удаления", - "sessionRequired": "Укажите сессию", - "noteRequired": "Заполните описание", - "cronRequired": "Укажите Cron-выражение", - "runAtRequired": "Выберите время запуска", - "createSuccess": "Задача создана", - "createFailed": "Ошибка создания" + "header": { + "eyebrow": "Automation", + "live": "Живая синхронизация" + }, + "page": { + "title": "Запланированные задачи", + "beta": "Экспериментальные функции", + "subtitle": "AstrBot проснется в заданное время, выполнит задачу и отправит результат в целевую беседу.", + "proactive": { + "link": "Поддерживаемые платформы", + "supported": "Отправка результатов поддерживается только на указанных ниже настроенных платформах", + "unsupported": "Нет платформ, поддерживающих проактивные сообщения. Включите их в настройках платформ." } + }, + "actions": { + "create": "Новая задача", + "edit": "Изменить", + "refresh": "Обновить", + "delete": "Удалить", + "cancel": "Отмена", + "close": "Закрыть", + "more": "Другие действия", + "runNow": "Запустить сейчас", + "save": "Сохранить", + "submit": "Создать" + }, + "platformDialog": { + "title": "Поддерживаемые платформы", + "description": "Только следующие платформы поддерживают проактивную отправку сообщений. AstrBot сможет отправить результат будущей задачи только в целевые беседы на этих платформах." + }, + "card": { + "onceAt": "Один раз · {time}", + "runAt": "Время запуска: {time}", + "nextRun": "Следующий запуск: {time}", + "dailyAt": "Каждый день в {time}", + "weeklyAt": "Каждый {day} в {time}", + "monthlyAt": "Каждый месяц, день {day}, {time}", + "everyMinutes": "Каждые {count} мин.", + "everyHours": "Каждые {count} ч.", + "everyDays": "Каждые {count} дн.", + "customCron": "Вручную · {cron}" + }, + "filters": { + "search": "Поиск по названию и содержанию", + "umo": "Фильтр по UMO", + "noUmos": "Нет UMO", + "noMatches": "Нет подходящих будущих задач." + }, + "overview": { + "totalTasks": "Всего задач", + "totalTasksNote": "Все зарегистрированные будущие задачи", + "enabledTasks": "Активные задачи", + "enabledTasksNote": "Задачи, которые будут автоматически выполнены", + "oneOffTasks": "Разовые задачи", + "recurringTasksNote": "Повторяющихся задач: {count}", + "proactivePlatforms": "Проактивные платформы", + "proactivePlatformsNote": "Платформы, способные отправлять результат сами" + }, + "section": { + "registered": { + "title": "Список задач", + "subtitle": "Просматривайте зарегистрированные задачи, время запуска и состояние" + }, + "delivery": { + "title": "Статус доставки", + "subtitle": "После выполнения задачи результат будет отправлен обратно через поддерживаемые платформы", + "support": "Проактивная доставка", + "available": "Доступна", + "unavailable": "Недоступна", + "enabledPlatforms": "Включенные платформы" + }, + "quickCreate": { + "title": "Быстрое создание", + "runMode": "Режим задачи", + "target": "Целевой контекст" + }, + "platforms": { + "title": "Поддерживаемые платформы" + } + }, + "table": { + "title": "Список задач", + "subtitle": "Отслеживайте cron, целевую сессию, историю запусков и состояние", + "empty": "Задач пока нет.", + "headers": { + "name": "Имя", + "type": "Тип", + "cron": "Cron", + "session": "ID сессии", + "nextRun": "Следующий запуск", + "lastRun": "Последний запуск", + "note": "Описание", + "actions": "Действия" + }, + "type": { + "once": "Разовая", + "recurring": "Повторяющаяся", + "activeAgent": "Активный агент", + "workflow": "Рабочий процесс", + "unknown": "{type}" + }, + "timezoneLocal": "Местное время", + "notAvailable": "—" + }, + "form": { + "title": "Создать задачу", + "editTitle": "Редактировать задачу", + "chatHint": "Вы можете ставить задачи прямо в чате, AstrBot создаст их автоматически без заполнения этой формы.", + "runOnce": "Разовая задача", + "name": "Имя задачи", + "note": "Требования задачи", + "scheduleMode": "Время выполнения", + "scheduleModes": { + "once": "Один раз", + "interval": "Интервал", + "daily": "Каждый день", + "weekly": "Каждую неделю", + "monthly": "Каждый месяц", + "cron": "Cron вручную" + }, + "intervalEvery": "Каждые", + "intervalUnit": "Единица", + "intervalUnits": { + "minutes": "Минуты", + "hours": "Часы", + "days": "Дни" + }, + "dailyTime": "Время", + "weeklyDay": "День недели", + "weeklyTime": "Время", + "monthlyDay": "День", + "monthlyTime": "Время", + "weekdays": { + "sunday": "Воскресенье", + "monday": "Понедельник", + "tuesday": "Вторник", + "wednesday": "Среда", + "thursday": "Четверг", + "friday": "Пятница", + "saturday": "Суббота" + }, + "cron": "Cron-выражения", + "cronPlaceholder": "0 9 * * *", + "runAt": "Время запуска", + "session": "Доставить в", + "noUmos": "Нет доступных сессий", + "timezone": "Часовой пояс (опционально, напр. Europe/Moscow)", + "enabled": "Включено" + }, + "messages": { + "loadFailed": "Ошибка загрузки задач", + "updateSuccess": "Задача обновлена", + "updateFailed": "Ошибка обновления", + "deleteSuccess": "Удалено", + "deleteFailed": "Ошибка удаления", + "nameRequired": "Укажите имя задачи", + "sessionRequired": "Укажите цель доставки", + "noteRequired": "Заполните требования задачи", + "cronRequired": "Укажите Cron-выражение", + "runAtRequired": "Выберите время запуска", + "createSuccess": "Задача создана", + "createFailed": "Ошибка создания", + "runStarted": "Запущено", + "runFailed": "Ошибка запуска" + } } diff --git a/dashboard/src/i18n/locales/zh-CN/features/cron.json b/dashboard/src/i18n/locales/zh-CN/features/cron.json index 2345efe98..d0e8d2450 100644 --- a/dashboard/src/i18n/locales/zh-CN/features/cron.json +++ b/dashboard/src/i18n/locales/zh-CN/features/cron.json @@ -4,10 +4,11 @@ "live": "实时同步" }, "page": { - "title": "未来任务管理", + "title": "未来任务", "beta": "实验性", - "subtitle": "AstrBot 可以被自动唤醒然后执行任务,并将结果告知任务布置方。需要先在配置文件中启用“主动型能力”。", + "subtitle": "AstrBot 会在设定时间自动唤醒,完成任务后把结果投递回目标会话。", "proactive": { + "link": "支持的消息平台", "supported": "主动发送结果仅支持以下您已配置的平台", "unsupported": "暂无支持主动消息的平台,请在平台设置中开启。" } @@ -18,9 +19,34 @@ "refresh": "刷新", "delete": "删除", "cancel": "取消", + "close": "关闭", + "more": "更多操作", + "runNow": "立即执行", "save": "保存", "submit": "创建" }, + "platformDialog": { + "title": "支持的消息平台", + "description": "只有以下平台支持主动推送消息。未来任务完成后,AstrBot 才能把结果投递回这些平台的目标会话。" + }, + "card": { + "onceAt": "一次性 · {time}", + "runAt": "执行时间:{time}", + "nextRun": "下次执行:{time}", + "dailyAt": "每天 {time}", + "weeklyAt": "每周{day} {time}", + "monthlyAt": "每月 {day} 日 {time}", + "everyMinutes": "每隔 {count} 分钟", + "everyHours": "每隔 {count} 小时", + "everyDays": "每隔 {count} 天", + "customCron": "自定义 · {cron}" + }, + "filters": { + "search": "搜索任务名称和内容", + "umo": "按 UMO 筛选", + "noUmos": "暂无 UMO", + "noMatches": "没有匹配的未来任务。" + }, "overview": { "totalTasks": "任务总数", "totalTasksNote": "当前已注册的未来任务", @@ -83,11 +109,42 @@ "chatHint": "你可以直接通过聊天的方式来让 AstrBot 创建未来任务,而不必在此添加。", "runOnce": "一次性任务", "name": "任务名称", - "note": "任务说明", + "note": "任务需求", + "scheduleMode": "执行时间", + "scheduleModes": { + "once": "一次性", + "interval": "间隔", + "daily": "每天", + "weekly": "每周", + "monthly": "每个月", + "cron": "自定义" + }, + "intervalEvery": "每隔", + "intervalUnit": "单位", + "intervalUnits": { + "minutes": "分钟", + "hours": "小时", + "days": "天" + }, + "dailyTime": "时间", + "weeklyDay": "星期", + "weeklyTime": "时间", + "monthlyDay": "日期", + "monthlyTime": "时间", + "weekdays": { + "sunday": "周日", + "monday": "周一", + "tuesday": "周二", + "wednesday": "周三", + "thursday": "周四", + "friday": "周五", + "saturday": "周六" + }, "cron": "Cron 表达式", "cronPlaceholder": "0 9 * * *", "runAt": "执行时间", - "session": "目标 session (platform_id:message_type:session_id)", + "session": "投递到", + "noUmos": "暂无可用会话", "timezone": "时区(可选,如 Asia/Shanghai)", "enabled": "启用" }, @@ -97,11 +154,14 @@ "updateFailed": "更新失败", "deleteSuccess": "已删除", "deleteFailed": "删除失败", - "sessionRequired": "请填写 session", - "noteRequired": "请填写说明", + "nameRequired": "请填写任务名称", + "sessionRequired": "请选择投递目标", + "noteRequired": "请填写任务需求", "cronRequired": "请填写 Cron 表达式", "runAtRequired": "请选择执行时间", "createSuccess": "创建成功", - "createFailed": "创建失败" + "createFailed": "创建失败", + "runStarted": "已开始执行", + "runFailed": "执行失败" } } diff --git a/dashboard/src/views/CronJobPage.vue b/dashboard/src/views/CronJobPage.vue index b59d3b0be..1e56f40ad 100644 --- a/dashboard/src/views/CronJobPage.vue +++ b/dashboard/src/views/CronJobPage.vue @@ -1,348 +1,713 @@ + + + + + + {{ tm("platformDialog.title") }} + + +

+ {{ tm("platformDialog.description") }} +

+
+
+
+ {{ platform.display_name || platform.name }} +
+
{{ platform.id }}
+
+
+
+ {{ tm("page.proactive.unsupported") }} +
+
+ + + {{ tm("actions.close") }} + + +
+
+ + + {{ snackbar.message }} + + + + + {{ + dialogTitle + }} + +
+ + + +
+ + + + +
+ + +
+ + + +
+ + +
+ +
+ + +
+ + +
+ + +
+
+ + {{ + tm("actions.cancel") + }} + + {{ dialogSubmitText }} + + +
+
+