yao/agent/robot/trigger/clock.go
Max d0d70814f5 Enhance Manager and Trigger Functionality
- 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.
2026-01-15 10:43:44 +08:00

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")
}