- Add Telegram integration support by introducing a dispatcher for handling Telegram events and messages. - Implement event notifications for robot configuration changes (creation, update, deletion) to facilitate integration with external services. - Refactor the robot initialization process to load robots into cache and start the dispatcher, improving the overall system setup. - Update the delivery event structure to include additional metadata for better context during message handling. - Enhance logging capabilities for better observability during robot execution and event processing.
194 lines
6.4 KiB
Go
194 lines
6.4 KiB
Go
package events
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"sync"
|
|
|
|
agentcontext "github.com/yaoapp/yao/agent/context"
|
|
robottypes "github.com/yaoapp/yao/agent/robot/types"
|
|
)
|
|
|
|
// TriggerFunc is the callback for triggering a robot execution.
|
|
// Injected by the api package at startup to break the import cycle.
|
|
// Returns (executionID, accepted, error).
|
|
type TriggerFunc func(ctx *robottypes.Context, memberID string, triggerType robottypes.TriggerType, data interface{}) (string, bool, error)
|
|
|
|
// ReplyFunc is the callback for replying to the originating channel.
|
|
// Injected by the dispatcher at startup. The implementation routes the
|
|
// reply to the correct adapter based on metadata.Channel.
|
|
// msg is the standard assistant message (text, Content Parts, media, etc.).
|
|
type ReplyFunc func(ctx context.Context, msg *agentcontext.Message, metadata *MessageMetadata) error
|
|
|
|
var (
|
|
triggerFn TriggerFunc
|
|
triggerFnMu sync.RWMutex
|
|
|
|
replyFn ReplyFunc
|
|
replyFnMu sync.RWMutex
|
|
)
|
|
|
|
// RegisterTriggerFunc sets the function used by handleMessage to trigger
|
|
// robot execution when a confirmed action is detected.
|
|
func RegisterTriggerFunc(fn TriggerFunc) {
|
|
triggerFnMu.Lock()
|
|
defer triggerFnMu.Unlock()
|
|
triggerFn = fn
|
|
}
|
|
|
|
func getTriggerFunc() TriggerFunc {
|
|
triggerFnMu.RLock()
|
|
defer triggerFnMu.RUnlock()
|
|
return triggerFn
|
|
}
|
|
|
|
// RegisterReplyFunc sets the function used by handleMessage to reply
|
|
// to the originating channel after processing.
|
|
func RegisterReplyFunc(fn ReplyFunc) {
|
|
replyFnMu.Lock()
|
|
defer replyFnMu.Unlock()
|
|
replyFn = fn
|
|
}
|
|
|
|
func getReplyFunc() ReplyFunc {
|
|
replyFnMu.RLock()
|
|
defer replyFnMu.RUnlock()
|
|
return replyFn
|
|
}
|
|
|
|
// Robot event type constants for event.Push integration.
|
|
// Events are fire-and-forget; handlers are registered via event.Register().
|
|
const (
|
|
TaskNeedInput = "robot.task.need_input"
|
|
TaskFailed = "robot.task.failed"
|
|
TaskCompleted = "robot.task.completed"
|
|
ExecWaiting = "robot.exec.waiting"
|
|
ExecResumed = "robot.exec.resumed"
|
|
ExecCompleted = "robot.exec.completed"
|
|
ExecFailed = "robot.exec.failed"
|
|
ExecCancelled = "robot.exec.cancelled"
|
|
Delivery = "robot.delivery"
|
|
Message = "robot.message"
|
|
)
|
|
|
|
// Robot configuration change events (used by integrations Receiver).
|
|
const (
|
|
RobotConfigCreated = "robot.config.created"
|
|
RobotConfigUpdated = "robot.config.updated"
|
|
RobotConfigDeleted = "robot.config.deleted"
|
|
)
|
|
|
|
// NeedInputPayload is the event payload for TaskNeedInput / ExecWaiting events.
|
|
type NeedInputPayload struct {
|
|
ExecutionID string `json:"execution_id"`
|
|
MemberID string `json:"member_id"`
|
|
TeamID string `json:"team_id"`
|
|
TaskID string `json:"task_id"`
|
|
Question string `json:"question"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
}
|
|
|
|
// ExecPayload is a generic execution event payload.
|
|
type ExecPayload struct {
|
|
ExecutionID string `json:"execution_id"`
|
|
MemberID string `json:"member_id"`
|
|
TeamID string `json:"team_id"`
|
|
Status string `json:"status,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
}
|
|
|
|
// TaskPayload is the event payload for TaskFailed / TaskCompleted events.
|
|
type TaskPayload struct {
|
|
ExecutionID string `json:"execution_id"`
|
|
MemberID string `json:"member_id"`
|
|
TeamID string `json:"team_id"`
|
|
TaskID string `json:"task_id"`
|
|
Error string `json:"error,omitempty"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
}
|
|
|
|
// DeliveryPayload is the event payload for Delivery events.
|
|
type DeliveryPayload struct {
|
|
ExecutionID string `json:"execution_id"`
|
|
MemberID string `json:"member_id"`
|
|
TeamID string `json:"team_id"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
Content *robottypes.DeliveryContent `json:"content,omitempty"`
|
|
Preferences *robottypes.DeliveryPreferences `json:"preferences,omitempty"`
|
|
Extra map[string]any `json:"extra,omitempty"`
|
|
}
|
|
|
|
// MessagePayload is the event payload for Message events (external channel messages).
|
|
type MessagePayload struct {
|
|
RobotID string `json:"robot_id"`
|
|
Messages []agentcontext.Message `json:"messages"`
|
|
Metadata *MessageMetadata `json:"metadata"`
|
|
}
|
|
|
|
// MessageMetadata carries channel-specific information for routing and deduplication.
|
|
type MessageMetadata struct {
|
|
Channel string `json:"channel"`
|
|
MessageID string `json:"message_id,omitempty"`
|
|
AppID string `json:"app_id,omitempty"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
SenderID string `json:"sender_id,omitempty"`
|
|
SenderName string `json:"sender_name,omitempty"`
|
|
Locale string `json:"locale,omitempty"`
|
|
ReplyTo string `json:"reply_to,omitempty"`
|
|
Extra map[string]any `json:"extra,omitempty"`
|
|
}
|
|
|
|
// MessageResult is the result returned from handleMessage via event.Call.
|
|
type MessageResult struct {
|
|
Message *agentcontext.Message `json:"message,omitempty"`
|
|
Action *ActionResult `json:"action,omitempty"`
|
|
ExecutionID string `json:"execution_id,omitempty"`
|
|
Metadata *MessageMetadata `json:"metadata,omitempty"`
|
|
}
|
|
|
|
// ActionResult describes a detected action from the Host Agent's Next hook.
|
|
type ActionResult struct {
|
|
Name string `json:"name"`
|
|
Payload any `json:"payload,omitempty"`
|
|
}
|
|
|
|
// RobotConfigPayload is the event payload for robot.config.* events.
|
|
type RobotConfigPayload struct {
|
|
MemberID string `json:"member_id"`
|
|
TeamID string `json:"team_id"`
|
|
}
|
|
|
|
// NormalizeLocale converts various language code formats (IETF BCP 47, etc.)
|
|
// into the lowercase hyphenated form used by agentcontext (e.g. "zh-cn", "en-us").
|
|
//
|
|
// Mapping rules:
|
|
//
|
|
// "zh-hans", "zh-cn" → "zh-cn"
|
|
// "zh-hant", "zh-tw", "zh-hk" → "zh-tw"
|
|
// "zh" → "zh-cn"
|
|
// "en-us" → "en-us"
|
|
// "en-gb" → "en-gb"
|
|
// "en" → "en"
|
|
// "" → "en" (default)
|
|
// other → lowercased as-is
|
|
func NormalizeLocale(raw string) string {
|
|
code := strings.ToLower(strings.TrimSpace(raw))
|
|
if code == "" {
|
|
return "en"
|
|
}
|
|
|
|
// Normalize underscore to hyphen (e.g. zh_CN → zh-cn)
|
|
code = strings.ReplaceAll(code, "_", "-")
|
|
|
|
switch code {
|
|
case "zh-hans", "zh-cn":
|
|
return "zh-cn"
|
|
case "zh-hant", "zh-tw", "zh-hk":
|
|
return "zh-tw"
|
|
case "zh":
|
|
return "zh-cn"
|
|
default:
|
|
return code
|
|
}
|
|
}
|