"""Feishu scheduler card rendering and callback handling.""" from __future__ import annotations from typing import Any, Dict, Iterable, List, Set _MAX_TASKS = 20 def tasks_for_receivers(tasks: Iterable[dict], receivers: Set[str]) -> List[dict]: """Return Feishu tasks owned by one of the callback's trusted receivers.""" visible = [] for task in tasks: action = task.get("action") or {} if action.get("channel_type") != "feishu": continue if action.get("receiver") in receivers: visible.append(task) return visible def build_scheduler_card(tasks: Iterable[dict]) -> Dict[str, Any]: """Build a Card 2.0 task list with explicit, idempotent actions.""" task_list = list(tasks) elements: List[Dict[str, Any]] = [] if not task_list: elements.append({"tag": "markdown", "content": "No scheduled tasks in this chat."}) else: for index, task in enumerate(task_list[:_MAX_TASKS]): if index: elements.append({"tag": "hr"}) task_id = str(task.get("id") or "") receiver = str((task.get("action") or {}).get("receiver") or "") enabled = task.get("enabled", True) status = "Enabled" if enabled else "Disabled" next_run = str(task.get("next_run_at") or "Unknown").replace("T", " ") elements.append( { "tag": "markdown", "content": "**{}** · {}\n`{}` · {}\nNext: {}".format( task.get("name") or "Unnamed task", status, task_id, _format_schedule(task.get("schedule") or {}), next_run, ), } ) toggle_action = "disable" if enabled else "enable" toggle_text = "Disable" if enabled else "Enable" toggle_type = "default" if enabled else "primary" elements.append( { "tag": "column_set", "columns": [ { "tag": "column", "elements": [ _button( toggle_text, toggle_type, toggle_action, task_id, receiver, ) ], }, { "tag": "column", "elements": [_button("Delete", "danger", "delete", task_id, receiver)], }, ], } ) hidden = len(task_list) - _MAX_TASKS if hidden > 0: elements.extend( [ {"tag": "hr"}, { "tag": "markdown", "content": "{} more tasks are hidden.".format(hidden), "text_size": "notation", }, ] ) return { "schema": "2.0", "config": {"update_multi": True, "enable_forward_interaction": False}, "header": { "template": "blue", "title": {"tag": "plain_text", "content": "Scheduled tasks"}, }, "body": {"elements": elements}, } def handle_scheduler_action( value: Dict[str, Any], task_store: Any, allowed_receivers: Set[str] ) -> Dict[str, Any]: """Apply an owned scheduler action and return a Feishu callback response.""" if value.get("cowagent") != "scheduler": return {} task_id = str(value.get("task_id") or "") action = value.get("action") if not task_id or action not in {"enable", "disable", "delete"}: return _toast("error", "Invalid scheduler action") task = task_store.get_task(task_id) if not task: return _response( "info", "Task no longer exists", build_scheduler_card(tasks_for_receivers(task_store.list_tasks(), allowed_receivers)), ) task_receiver = (task.get("action") or {}).get("receiver") task_channel = (task.get("action") or {}).get("channel_type") value_receiver = value.get("receiver") if ( task_channel != "feishu" or task_receiver not in allowed_receivers or (value_receiver and value_receiver != task_receiver) ): return _toast("error", "Task is not available in this chat") try: if action == "delete": task_store.delete_task(task_id) message = "Task deleted" else: enabled = action == "enable" task_store.enable_task(task_id, enabled) message = "Task enabled" if enabled else "Task disabled" except (OSError, ValueError) as exc: return _toast("error", "Task update failed: {}".format(exc)) visible = tasks_for_receivers(task_store.list_tasks(), allowed_receivers) return _response("success", message, build_scheduler_card(visible)) def _response(toast_type: str, content: str, card: Dict[str, Any]) -> Dict[str, Any]: response = _toast(toast_type, content) response["card"] = {"type": "raw", "data": card} return response def _toast(toast_type: str, content: str) -> Dict[str, Any]: return {"toast": {"type": toast_type, "content": content}} def _button( text: str, button_type: str, action: str, task_id: str, receiver: str, ) -> Dict[str, Any]: return { "tag": "button", "text": {"tag": "plain_text", "content": text}, "type": button_type, "value": { "cowagent": "scheduler", "action": action, "task_id": task_id, "receiver": receiver, }, } def _format_schedule(schedule: Dict[str, Any]) -> str: schedule_type = schedule.get("type") if schedule_type == "cron": return "cron {}".format(schedule.get("expression") or "?") if schedule_type == "interval": return "every {}s".format(schedule.get("seconds") or "?") if schedule_type == "once": return "once at {}".format(schedule.get("run_at") or "?") return str(schedule_type or "unknown schedule")