mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-10 14:10:52 +00:00
aeadb20ba7
Remove premature EventTeamTaskAssigned broadcast in executeCreate() that caused duplicate "assigned to" Telegram notifications. Assignment notification now only fires at actual dispatch (post-turn, fallback, or unblocked). Add TeamNotifyQueue (2s debounce, cap 20) to batch rapid-fire task notifications per chat — reduces N messages to 1 when leader dispatches multiple tasks at once. In leader mode this also reduces agent turns from N to 1. Also fix: ResetTaskStatus now clears progress_percent/progress_step on retry, and retry broadcast includes TaskNumber/Subject for correct notification formatting.
103 lines
2.4 KiB
Go
103 lines
2.4 KiB
Go
package tools
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// NotifyRoutingMeta carries routing info for batched team notifications.
|
|
type NotifyRoutingMeta struct {
|
|
Mode string // "direct" or "leader"
|
|
Channel string
|
|
ChatID string
|
|
UserID string
|
|
LeadAgent string // agent key (only used in leader mode)
|
|
}
|
|
|
|
// TeamNotifyQueue batches team task notifications per chat with debounce,
|
|
// following the same pattern as AnnounceQueue for subagent results.
|
|
type TeamNotifyQueue struct {
|
|
mu sync.Mutex
|
|
batches map[string]*notifyBatch // key: "teamID:chatID"
|
|
debounce time.Duration
|
|
cap int // immediate drain threshold
|
|
onDrain func(items []string, meta NotifyRoutingMeta)
|
|
}
|
|
|
|
type notifyBatch struct {
|
|
items []string
|
|
timer *time.Timer
|
|
meta NotifyRoutingMeta
|
|
}
|
|
|
|
// NewTeamNotifyQueue creates a notification queue with debounce and drain callback.
|
|
func NewTeamNotifyQueue(debounceMs int, onDrain func(items []string, meta NotifyRoutingMeta)) *TeamNotifyQueue {
|
|
if debounceMs <= 0 {
|
|
debounceMs = 2000
|
|
}
|
|
return &TeamNotifyQueue{
|
|
batches: make(map[string]*notifyBatch),
|
|
debounce: time.Duration(debounceMs) * time.Millisecond,
|
|
cap: 20,
|
|
onDrain: onDrain,
|
|
}
|
|
}
|
|
|
|
// Enqueue adds a formatted notification line to the batch for the given key.
|
|
// Resets debounce timer. Drains immediately if cap is reached.
|
|
func (q *TeamNotifyQueue) Enqueue(key string, content string, meta NotifyRoutingMeta) {
|
|
q.mu.Lock()
|
|
defer q.mu.Unlock()
|
|
|
|
b, ok := q.batches[key]
|
|
if !ok {
|
|
b = ¬ifyBatch{meta: meta}
|
|
q.batches[key] = b
|
|
}
|
|
b.items = append(b.items, content)
|
|
|
|
// Immediate drain if cap reached.
|
|
if len(b.items) >= q.cap {
|
|
if b.timer != nil {
|
|
b.timer.Stop()
|
|
}
|
|
items := b.items
|
|
bMeta := b.meta
|
|
delete(q.batches, key)
|
|
go q.drain(items, bMeta)
|
|
return
|
|
}
|
|
|
|
// Reset debounce timer.
|
|
if b.timer != nil {
|
|
b.timer.Stop()
|
|
}
|
|
b.timer = time.AfterFunc(q.debounce, func() {
|
|
q.mu.Lock()
|
|
b, ok := q.batches[key]
|
|
if !ok {
|
|
q.mu.Unlock()
|
|
return
|
|
}
|
|
items := b.items
|
|
bMeta := b.meta
|
|
delete(q.batches, key)
|
|
q.mu.Unlock()
|
|
|
|
q.drain(items, bMeta)
|
|
})
|
|
}
|
|
|
|
func (q *TeamNotifyQueue) drain(items []string, meta NotifyRoutingMeta) {
|
|
if len(items) == 0 || q.onDrain == nil {
|
|
return
|
|
}
|
|
q.onDrain(items, meta)
|
|
}
|
|
|
|
// FormatBatchedNotify joins notification lines into a single message.
|
|
func FormatBatchedNotify(items []string) string {
|
|
return strings.Join(items, "\n")
|
|
}
|