feat(qq): download task tracking with qq_get_download_tasks, sync re-added

This commit is contained in:
root
2026-07-23 10:41:08 +08:00
parent e182b89983
commit 5e0c8d5a40

View File

@ -17,7 +17,7 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"sync"
"time" "time"
"gitcode.com/JianFeeeee/homeagent-sdk/sdk" "gitcode.com/JianFeeeee/homeagent-sdk/sdk"
@ -74,6 +74,15 @@ func rconPacket(id, typ int32, body string) []byte {
return pkt return pkt
} }
type DownloadTask struct {
FileID string `json:"file_id"`
Filename string `json:"filename"`
Status string `json:"status"`
Path string `json:"path,omitempty"`
Error string `json:"error,omitempty"`
CreatedAt string `json:"created_at"`
}
type Plugin struct { type Plugin struct {
name string name string
sdk *sdk.PluginSDK sdk *sdk.PluginSDK
@ -91,6 +100,8 @@ type Plugin struct {
groupAllowFrom map[int64]struct{} groupAllowFrom map[int64]struct{}
srv *http.Server srv *http.Server
agentfsDir string agentfsDir string
downloadMu sync.Mutex
downloadTasks []*DownloadTask
} }
func (p *Plugin) Name() string { return p.name } func (p *Plugin) Name() string { return p.name }
@ -273,6 +284,10 @@ type 枚举: text文字/ voice语音转文字后发送/ image
}, "required": []string{"file_id"}, }, "required": []string{"file_id"},
}, p.handleDownloadFile) }, p.handleDownloadFile)
p.regTool(s, tp+"get_download_tasks", "查看所有下载任务及状态running/done/failed包含文件名、保存路径、错误信息等", map[string]interface{}{
"type": "object", "properties": map[string]interface{}{},
}, p.handleGetDownloadTasks)
p.regTool(s, tp+"upload_group_file", "上传文件到QQ群通过base64编码发送同时出现在群消息和群文件柜", map[string]interface{}{ p.regTool(s, tp+"upload_group_file", "上传文件到QQ群通过base64编码发送同时出现在群消息和群文件柜", map[string]interface{}{
"type": "object", "properties": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{
"group_id": map[string]interface{}{"type": "integer", "description": "目标群号"}, "group_id": map[string]interface{}{"type": "integer", "description": "目标群号"},
@ -1478,6 +1493,25 @@ func (p *Plugin) handleGetGroupFiles(args map[string]interface{}) (interface{},
} }
} }
func (p *Plugin) addDownloadTask(fileID, filename string) *DownloadTask {
p.downloadMu.Lock()
defer p.downloadMu.Unlock()
t := &DownloadTask{FileID: fileID, Filename: filename, Status: "running", CreatedAt: time.Now().Format("15:04:05")}
p.downloadTasks = append(p.downloadTasks, t)
if len(p.downloadTasks) > 100 {
p.downloadTasks = p.downloadTasks[len(p.downloadTasks)-100:]
}
return t
}
func (p *Plugin) updateDownloadTask(t *DownloadTask, status, path, errMsg string) {
p.downloadMu.Lock()
defer p.downloadMu.Unlock()
t.Status = status
t.Path = path
t.Error = errMsg
}
func (p *Plugin) handleDownloadFile(args map[string]interface{}) (interface{}, error) { func (p *Plugin) handleDownloadFile(args map[string]interface{}) (interface{}, error) {
fileID, _ := args["file_id"].(string) fileID, _ := args["file_id"].(string)
if fileID == "" { if fileID == "" {
@ -1488,41 +1522,53 @@ func (p *Plugin) handleDownloadFile(args map[string]interface{}) (interface{}, e
groupID, _ := convInt64(args["group_id"]) groupID, _ := convInt64(args["group_id"])
userID, _ := convInt64(args["user_id"]) userID, _ := convInt64(args["user_id"])
dispName := filename task := p.addDownloadTask(fileID, filename)
if dispName == "" {
dispName = fileID
if len(dispName) > 16 {
dispName = dispName[:16] + "…"
}
}
go func() { go func(t *DownloadTask, fid, fname, furl string, gid, uid int64) {
savePath := "" savePath := ""
if fileURL != "" { errMsg := ""
savePath = p.downloadURL(fileURL, filename) if furl != "" {
savePath = p.downloadURL(furl, fname)
} }
if savePath == "" && groupID > 0 { if savePath == "" && gid > 0 {
savePath = p.downloadGroupFile(groupID, fileID, filename) savePath = p.downloadGroupFile(gid, fid, fname)
} }
if savePath == "" && userID > 0 { if savePath == "" && uid > 0 {
savePath = p.downloadPrivateFile(userID, fileID, filename) savePath = p.downloadPrivateFile(uid, fid, fname)
} }
if savePath == "" { if savePath == "" {
savePath = p.downloadFromNapCat(fileID, filename) savePath = p.downloadFromNapCat(fid, fname)
} }
if savePath != "" { if savePath != "" {
p.updateDownloadTask(t, "done", savePath, "")
log.Printf("[qq] 文件下载完成: %s", savePath) log.Printf("[qq] 文件下载完成: %s", savePath)
if p.sdk != nil { if p.sdk != nil {
p.sdk.InjectInterruptText(p.name, p.name, p.sdk.InjectInterruptText(p.name, p.name,
fmt.Sprintf("文件下载完成: %s保存在 %s", filepath.Base(savePath), savePath)) fmt.Sprintf("文件下载完成: %s保存在 %s", filepath.Base(savePath), savePath))
} }
} else { } else {
log.Printf("[qq] 文件下载失败: %s", fileID) errMsg = "下载失败,文件可能已过期"
p.updateDownloadTask(t, "failed", "", errMsg)
log.Printf("[qq] 文件下载失败: %s", fid)
} }
}() }(task, fileID, filename, fileURL, groupID, userID)
return map[string]interface{}{"status": "started", "file_id": fileID, "filename": dispName, return map[string]interface{}{
"hint": "下载已后台启动,完成后会推送通知。若长时间未收到完成通知表示文件已过期无法下载,需让发送者重新发送"}, nil "status": "started", "file_id": fileID, "filename": filename,
"hint": "下载已后台启动。使用 qq_get_download_tasks 查看进度。"}, nil
}
func (p *Plugin) handleGetDownloadTasks(args map[string]interface{}) (interface{}, error) {
p.downloadMu.Lock()
defer p.downloadMu.Unlock()
// 返回最近 50 条
tasks := p.downloadTasks
if len(tasks) > 50 {
tasks = tasks[len(tasks)-50:]
}
return map[string]interface{}{
"tasks": tasks, "total": len(p.downloadTasks),
"hint": "status 为 running 表示下载中done 已完成failed 已失败。用 qq_download_file 重新下载失败的任务。"}, nil
} }
func (p *Plugin) handleUploadGroupFile(args map[string]interface{}) (interface{}, error) { func (p *Plugin) handleUploadGroupFile(args map[string]interface{}) (interface{}, error) {
@ -2037,6 +2083,7 @@ func NewPlugin(name string, config map[string]interface{}) (sdk.Plugin, error) {
name: name, name: name,
allowFrom: make(map[int64]struct{}), allowFrom: make(map[int64]struct{}),
groupAllowFrom: make(map[int64]struct{}), groupAllowFrom: make(map[int64]struct{}),
downloadTasks: make([]*DownloadTask, 0),
dmPolicy: "open", dmPolicy: "open",
groupPolicy: "open", groupPolicy: "open",
}, nil }, nil