wechat_ai/agent/task.go

163 lines
4.4 KiB
Go

package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strings"
"time"
)
var allowedTaskTypes = map[string]bool{
"wechat_reply": true,
"wechat_observe": true,
"noop": true,
}
var allowedActionTypes = map[string]bool{
"click": true,
"type_text": true,
"press_enter": true,
"scroll": true,
"wait": true,
"noop": true,
}
func finalizeTask(task Task, summaries []RegionSummary, config Config, regionsPath string, screenshotPath string) Task {
now := time.Now()
if task.TaskID == "" {
task.TaskID = "task_" + now.Format("20060102_150405")
}
if task.TaskName == "" {
task.TaskName = "识别微信聊天并生成回复"
}
if task.TaskType == "" || !allowedTaskTypes[task.TaskType] {
task.TaskType = "wechat_reply"
}
if task.TaskStatus == "" {
task.TaskStatus = "planned"
}
if config.Agent.DryRun {
task.TaskStatus = "dry_run"
}
if task.CreatedAt == "" {
task.CreatedAt = now.Format(time.RFC3339)
}
task.Source = TaskSource{RegionsPath: regionsPath, ScreenshotPath: screenshotPath}
task.Regions = summaries
regionIndex := regionByType(summaries)
if len(task.Actions) == 0 {
task.Actions = defaultActions(task.LLMSummary.ReplyText)
}
for index := range task.Actions {
action := &task.Actions[index]
if action.ActionID == "" {
action.ActionID = fmt.Sprintf("action_%03d", index+1)
}
if action.Name == "" {
action.Name = action.Type
}
if !allowedActionTypes[action.Type] {
action.Type = "noop"
action.Reason = "unsupported action type replaced by noop"
}
if action.Type == "click" || action.Type == "scroll" {
if summary, exists := regionIndex[action.TargetRegion]; exists {
point := summary.CenterScreen
action.Point = &point
} else if action.Type == "click" {
action.Type = "noop"
action.Reason = "target_region not found"
}
}
if action.Type == "type_text" && len([]rune(action.Text)) > 500 {
runes := []rune(action.Text)
action.Text = string(runes[:500])
}
if config.Agent.DryRun {
action.Status = "skipped"
if action.Reason == "" {
action.Reason = "dry_run enabled"
}
} else if action.Status == "" {
action.Status = "pending"
}
task.ExecutionLog = append(task.ExecutionLog, ExecutionLog{
ActionID: action.ActionID,
Status: action.Status,
Message: executionMessage(*action),
Time: time.Now().Format(time.RFC3339),
})
}
return task
}
func defaultActions(replyText string) []Action {
if strings.TrimSpace(replyText) == "" {
return []Action{{ActionID: "action_001", Type: "noop", Name: "无需回复", Status: "pending"}}
}
return []Action{
{ActionID: "action_001", Type: "click", Name: "点击输入框", Status: "pending", TargetRegion: "input_box"},
{ActionID: "action_002", Type: "type_text", Name: "输入回复内容", Status: "pending", Text: replyText},
{ActionID: "action_003", Type: "press_enter", Name: "按回车发送", Status: "pending"},
}
}
func executionMessage(action Action) string {
switch action.Type {
case "click":
return fmt.Sprintf("dry_run: 跳过点击 %s", action.TargetRegion)
case "type_text":
return "dry_run: 跳过输入回复内容"
case "press_enter":
return "dry_run: 跳过按回车发送"
case "scroll":
return fmt.Sprintf("dry_run: 跳过滑动 %s delta_y=%d", action.TargetRegion, action.DeltaY)
case "wait":
return fmt.Sprintf("dry_run: 跳过等待 %dms", action.DurationMs)
default:
return "dry_run: noop"
}
}
func saveTaskFiles(task Task, config Config) (string, string, error) {
latestPath := appDataPath(config, config.Agent.TaskOutputPath)
historyDir := appDataPath(config, config.Agent.TaskHistoryDir)
if err := os.MkdirAll(filepath.Dir(latestPath), 0755); err != nil {
return "", "", err
}
if err := os.MkdirAll(historyDir, 0755); err != nil {
return "", "", err
}
data, err := json.MarshalIndent(task, "", " ")
if err != nil {
return "", "", err
}
if err := os.WriteFile(latestPath, data, 0644); err != nil {
return "", "", err
}
historyPath := filepath.Join(historyDir, task.TaskID+".json")
if err := os.WriteFile(historyPath, data, 0644); err != nil {
return "", "", err
}
return latestPath, historyPath, nil
}
func saveRawResponse(config Config, raw string) error {
path := appDataPath(config, "data/tasks/latest_task_raw.txt")
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
return err
}
return os.WriteFile(path, []byte(raw), 0644)
}