- Integrated trigger handling directly into the Manager, consolidating the logic for clock, human intervention, and event triggers. - Updated the Manager to include methods for processing human interventions and event triggers, ensuring robust execution control. - Refactored the trigger package to provide validation and utility functions, enhancing the overall structure and clarity of trigger-related logic. - Improved documentation and comments throughout the Manager and trigger implementations for better understanding and maintainability. - Updated tests to cover new functionalities, ensuring comprehensive validation of the Manager's behavior with various trigger types.
126 lines
3.5 KiB
Go
126 lines
3.5 KiB
Go
package trigger
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/yaoapp/yao/agent/robot/types"
|
|
)
|
|
|
|
// ClockMatcher provides clock trigger matching logic
|
|
// This is extracted from Manager for reuse and testing
|
|
type ClockMatcher struct{}
|
|
|
|
// NewClockMatcher creates a new clock matcher
|
|
func NewClockMatcher() *ClockMatcher {
|
|
return &ClockMatcher{}
|
|
}
|
|
|
|
// ShouldTrigger checks if a robot should be triggered based on its clock config
|
|
func (cm *ClockMatcher) ShouldTrigger(robot *types.Robot, now time.Time) bool {
|
|
if robot == nil || robot.Config == nil || robot.Config.Clock == nil {
|
|
return false
|
|
}
|
|
|
|
clock := robot.Config.Clock
|
|
|
|
// Get time in robot's timezone
|
|
loc := clock.GetLocation()
|
|
localNow := now.In(loc)
|
|
|
|
switch clock.Mode {
|
|
case types.ClockTimes:
|
|
return cm.shouldTriggerTimes(robot, clock, localNow)
|
|
case types.ClockInterval:
|
|
return cm.shouldTriggerInterval(robot, clock, localNow)
|
|
case types.ClockDaemon:
|
|
return cm.shouldTriggerDaemon(robot, clock, localNow)
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// shouldTriggerTimes checks if current time matches any configured times
|
|
// times mode: run at specific times (e.g., ["09:00", "14:00", "17:00"])
|
|
func (cm *ClockMatcher) shouldTriggerTimes(robot *types.Robot, clock *types.Clock, now time.Time) bool {
|
|
// Check day of week first
|
|
if !cm.matchesDay(clock, now) {
|
|
return false
|
|
}
|
|
|
|
// Check if current time matches any configured time
|
|
currentTime := now.Format("15:04")
|
|
for _, t := range clock.Times {
|
|
if t == currentTime {
|
|
// Check if already triggered in this minute
|
|
if !robot.LastRun.IsZero() {
|
|
lastRunInLoc := robot.LastRun.In(now.Location())
|
|
if lastRunInLoc.Format("15:04") == currentTime && lastRunInLoc.Day() == now.Day() {
|
|
return false // Already triggered this minute today
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// shouldTriggerInterval checks if enough time has passed since last run
|
|
// interval mode: run every X duration (e.g., "30m", "2h")
|
|
func (cm *ClockMatcher) shouldTriggerInterval(robot *types.Robot, clock *types.Clock, now time.Time) bool {
|
|
interval, err := time.ParseDuration(clock.Every)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
// First run if never executed
|
|
if robot.LastRun.IsZero() {
|
|
return true
|
|
}
|
|
|
|
// Check if interval has passed
|
|
return now.Sub(robot.LastRun) >= interval
|
|
}
|
|
|
|
// shouldTriggerDaemon checks if robot can restart immediately after last run
|
|
// daemon mode: restart immediately after each run completes
|
|
func (cm *ClockMatcher) shouldTriggerDaemon(robot *types.Robot, clock *types.Clock, now time.Time) bool {
|
|
// Daemon mode: trigger if not currently running
|
|
// CanRun() checks if robot has available execution slots
|
|
return robot.CanRun()
|
|
}
|
|
|
|
// matchesDay checks if current day matches the configured days
|
|
func (cm *ClockMatcher) matchesDay(clock *types.Clock, now time.Time) bool {
|
|
// Empty days or ["*"] means all days
|
|
if len(clock.Days) == 0 {
|
|
return true
|
|
}
|
|
|
|
for _, day := range clock.Days {
|
|
if day == "*" {
|
|
return true
|
|
}
|
|
// Match day name (Mon, Tue, Wed, Thu, Fri, Sat, Sun)
|
|
// or full name (Monday, Tuesday, etc.)
|
|
weekday := now.Weekday().String()
|
|
shortDay := weekday[:3] // Mon, Tue, etc.
|
|
if day == weekday || day == shortDay {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ParseTime parses a time string in "HH:MM" format
|
|
func ParseTime(timeStr string) (hour, minute int, err error) {
|
|
t, err := time.Parse("15:04", timeStr)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
return t.Hour(), t.Minute(), nil
|
|
}
|
|
|
|
// FormatTime formats hour and minute to "HH:MM" string
|
|
func FormatTime(hour, minute int) string {
|
|
return time.Date(0, 1, 1, hour, minute, 0, 0, time.UTC).Format("15:04")
|
|
}
|