diff --git a/.changeset/sip-pin-answer-time-attrs.md b/.changeset/sip-pin-answer-time-attrs.md new file mode 100644 index 000000000..5a6bd301e --- /dev/null +++ b/.changeset/sip-pin-answer-time-attrs.md @@ -0,0 +1,6 @@ +--- +"github.com/livekit/protocol": patch +"@livekit/protocol": patch +--- + +Add `lk.sip.pinEnteredTime` and `lk.sip.answerTime` SIP participant attributes, with helpers to set and parse them diff --git a/livekit/attrs.go b/livekit/attrs.go index c1fc28f28..a36246f43 100644 --- a/livekit/attrs.go +++ b/livekit/attrs.go @@ -32,6 +32,10 @@ const ( AttrSIPHeaderPrefix = AttrSIPPrefix + "h." // AttrSIPInviteTime attribute contains the time the inbound INVITE was received, in Unix milliseconds. AttrSIPInviteTime = "lk.sip.inviteTime" + // AttrSIPPinEnteredTime attribute contains the time the caller finished entering the PIN on an inbound PIN call, in Unix milliseconds. + AttrSIPPinEnteredTime = "lk.sip.pinEnteredTime" + // AttrSIPAnswerTime attribute contains the time the callee answered an outbound call, in Unix milliseconds. + AttrSIPAnswerTime = "lk.sip.answerTime" // AttrIngressPrefix is shared for all Ingress attributes AttrIngressPrefix = "ingress." diff --git a/sip/attrs.go b/sip/attrs.go index 3efd75718..1d0b69ef2 100644 --- a/sip/attrs.go +++ b/sip/attrs.go @@ -8,14 +8,40 @@ import ( ) func InviteTimeAttributes(t time.Time) map[string]string { - return map[string]string{ - livekit.AttrSIPInviteTime: strconv.FormatInt(t.UnixMilli(), 10), - } + return timeAttributes(livekit.AttrSIPInviteTime, t) } // ParseInviteTime returns the zero time if the attribute is missing or malformed. func ParseInviteTime(attrs map[string]string) time.Time { - ms, err := strconv.ParseInt(attrs[livekit.AttrSIPInviteTime], 10, 64) + return parseTimeAttribute(attrs, livekit.AttrSIPInviteTime) +} + +func PinEnteredTimeAttributes(t time.Time) map[string]string { + return timeAttributes(livekit.AttrSIPPinEnteredTime, t) +} + +// ParsePinEnteredTime returns the zero time if the attribute is missing or malformed. +func ParsePinEnteredTime(attrs map[string]string) time.Time { + return parseTimeAttribute(attrs, livekit.AttrSIPPinEnteredTime) +} + +func AnswerTimeAttributes(t time.Time) map[string]string { + return timeAttributes(livekit.AttrSIPAnswerTime, t) +} + +// ParseAnswerTime returns the zero time if the attribute is missing or malformed. +func ParseAnswerTime(attrs map[string]string) time.Time { + return parseTimeAttribute(attrs, livekit.AttrSIPAnswerTime) +} + +func timeAttributes(key string, t time.Time) map[string]string { + return map[string]string{ + key: strconv.FormatInt(t.UnixMilli(), 10), + } +} + +func parseTimeAttribute(attrs map[string]string, key string) time.Time { + ms, err := strconv.ParseInt(attrs[key], 10, 64) if err != nil { return time.Time{} } diff --git a/sip/attrs_test.go b/sip/attrs_test.go index 697aca263..a8752fa96 100644 --- a/sip/attrs_test.go +++ b/sip/attrs_test.go @@ -9,10 +9,25 @@ import ( "github.com/livekit/protocol/livekit" ) -func TestInviteTimeAttributes(t *testing.T) { +func TestTimeAttributes(t *testing.T) { + cases := []struct { + key string + format func(time.Time) map[string]string + parse func(map[string]string) time.Time + }{ + {livekit.AttrSIPInviteTime, InviteTimeAttributes, ParseInviteTime}, + {livekit.AttrSIPPinEnteredTime, PinEnteredTimeAttributes, ParsePinEnteredTime}, + {livekit.AttrSIPAnswerTime, AnswerTimeAttributes, ParseAnswerTime}, + } at := time.UnixMilli(time.Now().UnixMilli()) - require.True(t, at.Equal(ParseInviteTime(InviteTimeAttributes(at)))) + for _, c := range cases { + t.Run(c.key, func(t *testing.T) { + attrs := c.format(at) + require.Contains(t, attrs, c.key) + require.True(t, at.Equal(c.parse(attrs))) - require.True(t, ParseInviteTime(nil).IsZero()) - require.True(t, ParseInviteTime(map[string]string{livekit.AttrSIPInviteTime: "bad"}).IsZero()) + require.True(t, c.parse(nil).IsZero()) + require.True(t, c.parse(map[string]string{c.key: "bad"}).IsZero()) + }) + } }