package asterisk import ( "context" "errors" "fmt" "time" "github.com/CyCoreSystems/ari/v5" ) // awaitDialUp only runs after Dial was submitted. Create enters Stasis before // any SIP request is sent, so StasisStart alone can never prove an answer. func awaitDialUp(ctx context.Context, sub ari.Subscription, channelID string) (time.Time, error) { if ctx == nil || sub == nil || channelID == "" { return time.Time{}, errors.New("native answer wait has no channel subscription") } stasis, up := false, false var answeredAt time.Time for { select { case <-ctx.Done(): return time.Time{}, ctx.Err() case event, ok := <-sub.Events(): if !ok { return time.Time{}, errors.New("native answer subscription closed before confirmed answer") } if event == nil { continue } switch e := event.(type) { case *ari.StasisStart: if e.Channel.ID == channelID { stasis = true } case *ari.ChannelStateChange: if e.Channel.ID == channelID && e.Channel.State == "Up" { if !up { answeredAt = time.Time(e.Timestamp) } up = true } case *ari.ChannelDestroyed: if e.Channel.ID == channelID { return time.Time{}, fmt.Errorf("native outbound channel destroyed before verified answer: cause=%d", e.Cause) } case *ari.ChannelHangupRequest: if e.Channel.ID == channelID { return time.Time{}, fmt.Errorf("native outbound channel requested hangup before verified answer: cause=%d", e.Cause) } case *ari.StasisEnd: if e.Channel.ID == channelID { return time.Time{}, errors.New("native outbound channel left Stasis before verified answer") } } if stasis && up { return answeredAt, nil } } } } func eventBelongsToChannel(event ari.Event, channelID string) bool { for _, key := range event.Keys() { if key != nil && key.Kind == "channel" && key.ID == channelID { return true } } return false }