93 lines
3.3 KiB
Go
93 lines
3.3 KiB
Go
package handoff
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
type Manager struct {
|
|
Config Config
|
|
Store *Store
|
|
Executor Executor
|
|
}
|
|
|
|
type RequestInput struct {
|
|
CallID string
|
|
AsteriskChannelID string
|
|
State string
|
|
Language string
|
|
RegionCode string
|
|
Route string
|
|
ReasonCode HandoffReasonCode
|
|
ReasonText string
|
|
Summary string
|
|
Metadata map[string]string
|
|
}
|
|
|
|
func NewManager(cfg Config, executor Executor) *Manager {
|
|
if cfg.Mode == "" {
|
|
cfg = DefaultConfig()
|
|
}
|
|
if executor == nil {
|
|
executor = SafeExecutor{}
|
|
}
|
|
return &Manager{Config: cfg, Store: NewStore(), Executor: executor}
|
|
}
|
|
|
|
func (m *Manager) Request(ctx context.Context, in RequestInput) (HandoffRequest, HandoffResult, error) {
|
|
if existing, ok := m.Store.GetByCall(in.CallID); ok {
|
|
result := HandoffResult{RequestID: existing.ID, Status: existing.Status, Mode: existing.Mode, MessageKey: "handoff.already_requested", Message: Message("handoff.already_requested", in.Language)}
|
|
return existing, result, nil
|
|
}
|
|
mode := m.Config.Mode
|
|
hasTarget := mode == HandoffModeHangupAfterMessage || mode == HandoffModeDisabledStub ||
|
|
(mode == HandoffModeARIRedirect && m.Config.TargetEndpoint != "") ||
|
|
(mode == HandoffModeDialplanContinue && m.Config.DialplanContext != "" && m.Config.DialplanExtension != "")
|
|
decision := Authorize(PolicyContext{State: in.State, Route: in.Route, HandoffEnabled: m.Config.Enabled, HandoffMode: mode, AllowInTestRouteOnly: m.Config.AllowInTestRouteOnly, HasTarget: hasTarget})
|
|
now := time.Now().UTC()
|
|
req := HandoffRequest{
|
|
ID: fmt.Sprintf("handoff-%d", now.UnixNano()),
|
|
CallID: in.CallID,
|
|
AsteriskChannelID: in.AsteriskChannelID,
|
|
Mode: mode,
|
|
Status: HandoffStatusRequested,
|
|
ReasonCode: in.ReasonCode,
|
|
ReasonText: SanitizeText(in.ReasonText, m.Config.MaxSummaryChars),
|
|
Summary: SanitizeText(in.Summary, m.Config.MaxSummaryChars),
|
|
Language: in.Language,
|
|
RegionCode: in.RegionCode,
|
|
Route: in.Route,
|
|
TargetEndpoint: m.Config.TargetEndpoint,
|
|
DialplanContext: m.Config.DialplanContext,
|
|
DialplanExtension: m.Config.DialplanExtension,
|
|
DialplanPriority: m.Config.DialplanPriority,
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
Metadata: in.Metadata,
|
|
}
|
|
if !decision.Allowed {
|
|
req.Status = HandoffStatusFailed
|
|
result := HandoffResult{RequestID: req.ID, Status: req.Status, Mode: req.Mode, MessageKey: decision.MessageKey, Message: Message(decision.MessageKey, in.Language), Error: decision.ReasonCode}
|
|
m.Store.Put(req)
|
|
return req, result, fmt.Errorf(decision.ReasonCode)
|
|
}
|
|
if decision.StubOnly {
|
|
req.Mode = HandoffModeDisabledStub
|
|
req.Status = HandoffStatusStubbed
|
|
result := HandoffResult{RequestID: req.ID, Status: req.Status, Mode: req.Mode, MessageKey: decision.MessageKey, Message: Message(decision.MessageKey, in.Language)}
|
|
m.Store.Put(req)
|
|
return req, result, nil
|
|
}
|
|
result, err := m.Executor.Execute(ctx, req)
|
|
req.Status = result.Status
|
|
req.Error = result.Error
|
|
completed := time.Now().UTC()
|
|
if result.Status == HandoffStatusCompleted || result.Status == HandoffStatusFailed || result.Status == HandoffStatusStubbed {
|
|
req.CompletedAt = &completed
|
|
}
|
|
req.UpdatedAt = completed
|
|
m.Store.Put(req)
|
|
return req, result, err
|
|
}
|