163 lines
4.4 KiB
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)
|
|
}
|