mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-12 03:00:50 +08:00
feat(scheduler): confirm before deleting scheduled jobs
Require preview confirmation for schedule_delete, extra confirm_foreign for others' jobs in groups, and mirror the same flow in chat slash commands. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d036190195
commit
30202dd291
6 changed files with 692 additions and 63 deletions
|
|
@ -113,7 +113,15 @@ def _menu_text() -> str:
|
|||
)
|
||||
|
||||
|
||||
def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, session_id: str = "") -> str | None:
|
||||
def _handle_productivity_commands(
|
||||
*,
|
||||
text: str,
|
||||
tenant_id: str,
|
||||
user_id: str,
|
||||
session_id: str = "",
|
||||
creator_external_user_id: str = "",
|
||||
creator_push_name: str = "",
|
||||
) -> str | None:
|
||||
t = (text or "").strip()
|
||||
t_low = t.lower()
|
||||
if not t:
|
||||
|
|
@ -206,15 +214,95 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, se
|
|||
ok = store.scheduled_job_set_status(tenant_id=tenant_id, job_id=full, status="paused")
|
||||
return "已暂停。" if ok else "未找到该定时任务。"
|
||||
|
||||
if t.startswith("确认删除定时任务 ") or t.startswith("确认删除定时 "):
|
||||
prefix = "确认删除定时任务 " if t.startswith("确认删除定时任务 ") else "确认删除定时 "
|
||||
jid = t[len(prefix) :].strip()
|
||||
if not jid:
|
||||
return "请提供任务 id。示例:确认删除定时 1234abcd"
|
||||
from runtime.scheduler.job_delete import (
|
||||
job_delete_preview,
|
||||
job_is_foreign,
|
||||
resolve_scheduled_jobs_by_id,
|
||||
)
|
||||
|
||||
matches = resolve_scheduled_jobs_by_id(store, tenant_id=tenant_id, job_id=jid)
|
||||
if not matches:
|
||||
return "未找到该定时任务。"
|
||||
if len(matches) > 1:
|
||||
lines = [
|
||||
f"- {r.name} | id={r.id[:8]}… | {r.schedule_kind}:{r.schedule_expr}" for r in matches[:8]
|
||||
]
|
||||
return "匹配到多个任务,请用更完整的 id:\n" + "\n".join(lines)
|
||||
job = matches[0]
|
||||
foreign = job_is_foreign(
|
||||
job,
|
||||
actor_user_id=str(user_id or ""),
|
||||
actor_external_user_id=str(creator_external_user_id or ""),
|
||||
)
|
||||
if foreign:
|
||||
return (
|
||||
f"该任务看起来是其他人创建的({job.name} / id={job.id[:8]})。"
|
||||
"如需删除,请明确说「确认删除他人定时 <完整id>」。"
|
||||
)
|
||||
ok = store.scheduled_job_delete(tenant_id=tenant_id, job_id=str(job.id))
|
||||
if not ok:
|
||||
return "未找到该定时任务。"
|
||||
preview = job_delete_preview(job)
|
||||
return f"已删除:{preview.get('name')}(id={str(preview.get('id') or '')[:8]})"
|
||||
|
||||
if t.startswith("确认删除他人定时任务 ") or t.startswith("确认删除他人定时 "):
|
||||
prefix = "确认删除他人定时任务 " if t.startswith("确认删除他人定时任务 ") else "确认删除他人定时 "
|
||||
jid = t[len(prefix) :].strip()
|
||||
if not jid:
|
||||
return "请提供完整任务 id。示例:确认删除他人定时 1234abcd-...."
|
||||
from runtime.scheduler.job_delete import job_delete_preview, resolve_scheduled_jobs_by_id
|
||||
|
||||
matches = resolve_scheduled_jobs_by_id(store, tenant_id=tenant_id, job_id=jid)
|
||||
if not matches:
|
||||
return "未找到该定时任务。"
|
||||
if len(matches) > 1:
|
||||
return "删除他人任务须使用唯一完整 id,当前前缀匹配到多个任务。"
|
||||
job = matches[0]
|
||||
ok = store.scheduled_job_delete(tenant_id=tenant_id, job_id=str(job.id))
|
||||
if not ok:
|
||||
return "未找到该定时任务。"
|
||||
preview = job_delete_preview(job)
|
||||
return f"已删除他人任务:{preview.get('name')}(id={str(preview.get('id') or '')[:8]})"
|
||||
|
||||
if t.startswith("删除定时任务 ") or t.startswith("删除定时 "):
|
||||
prefix = "删除定时任务 " if t.startswith("删除定时任务 ") else "删除定时 "
|
||||
jid = t[len(prefix) :].strip()
|
||||
if not jid:
|
||||
return "请提供任务 id 前缀。示例:删除定时任务 1234abcd"
|
||||
rows = store.scheduled_job_list(tenant_id=tenant_id, status=None, limit=200)
|
||||
full = next((r.id for r in rows if str(r.id).startswith(jid)), jid)
|
||||
ok = store.scheduled_job_delete(tenant_id=tenant_id, job_id=full)
|
||||
return "已删除。" if ok else "未找到该定时任务。"
|
||||
return "请提供任务 id 前缀。示例:删除定时 1234abcd"
|
||||
from runtime.scheduler.job_delete import (
|
||||
job_delete_preview_markdown,
|
||||
job_delete_preview,
|
||||
job_is_foreign,
|
||||
resolve_scheduled_jobs_by_id,
|
||||
)
|
||||
|
||||
matches = resolve_scheduled_jobs_by_id(store, tenant_id=tenant_id, job_id=jid)
|
||||
if not matches:
|
||||
return "未找到该定时任务。"
|
||||
if len(matches) > 1:
|
||||
lines = [
|
||||
f"- {r.name} | id={r.id[:8]}… | {r.schedule_kind}:{r.schedule_expr}" for r in matches[:8]
|
||||
]
|
||||
return "匹配到多个任务,请指定更完整的 id:\n" + "\n".join(lines)
|
||||
job = matches[0]
|
||||
foreign = job_is_foreign(
|
||||
job,
|
||||
actor_user_id=str(user_id or ""),
|
||||
actor_external_user_id=str(creator_external_user_id or ""),
|
||||
)
|
||||
preview = job_delete_preview(job)
|
||||
md = job_delete_preview_markdown(preview, foreign=foreign, lang="zh")
|
||||
if foreign:
|
||||
return (
|
||||
md
|
||||
+ f"\n\n如确认删除,请发送:确认删除他人定时 {job.id}"
|
||||
)
|
||||
return md + f"\n\n如确认删除,请发送:确认删除定时 {job.id}"
|
||||
|
||||
schedule_prefix = ""
|
||||
if t.startswith("记定时 ") or t.startswith("创建定时 "):
|
||||
|
|
@ -245,6 +333,15 @@ def _handle_productivity_commands(*, text: str, tenant_id: str, user_id: str, se
|
|||
tenant_id=tenant_id,
|
||||
session_id=str(session_id or "").strip(),
|
||||
)
|
||||
from runtime.scheduler.job_delete import merge_delivery_creator
|
||||
|
||||
delivery = merge_delivery_creator(
|
||||
delivery,
|
||||
user_id=str(user_id or ""),
|
||||
external_user_id=str(creator_external_user_id or ""),
|
||||
push_name=str(creator_push_name or ""),
|
||||
session_id=str(session_id or "").strip(),
|
||||
)
|
||||
row = store.scheduled_job_create(
|
||||
tenant_id=tenant_id,
|
||||
name=prompt_text[:40] or "定时提醒",
|
||||
|
|
@ -1027,6 +1124,21 @@ def process_inbound_payload(payload: dict[str, Any]) -> dict[str, Any]:
|
|||
tenant_id=tenant_id,
|
||||
user_id=user_id,
|
||||
session_id=str(session_id),
|
||||
creator_external_user_id=str(getattr(inbound, "external_user_id", "") or ""),
|
||||
creator_push_name=str(
|
||||
(
|
||||
(inbound.metadata or {}).get("push_name")
|
||||
if isinstance(inbound.metadata, dict)
|
||||
else ""
|
||||
)
|
||||
or (
|
||||
((inbound.metadata or {}).get("raw") or {}).get("pushName")
|
||||
if isinstance(inbound.metadata, dict)
|
||||
and isinstance((inbound.metadata or {}).get("raw"), dict)
|
||||
else ""
|
||||
)
|
||||
or ""
|
||||
),
|
||||
)
|
||||
if cmd_reply is not None:
|
||||
reply = cmd_reply
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue