From 8cf58ff15ac60444a13564f97fe2d81c34f11d95 Mon Sep 17 00:00:00 2001 From: cnderrauber Date: Thu, 16 Oct 2025 10:41:55 +0800 Subject: [PATCH] Add twilio connector (#1254) * Add twilio connector * empty lines * solve comments * Make optional field consistent with whatsapp connector * Remove stream direction, add call direction * generated protobuf --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- livekit/livekit_connector.pb.go | 30 ++- livekit/livekit_connector.twirp.go | 316 ++++++++++++++++++++-- livekit/livekit_connector_twilio.pb.go | 317 +++++++++++++++++++++++ magefile.go | 5 +- protobufs/livekit_connector.proto | 5 +- protobufs/livekit_connector_twilio.proto | 52 ++++ protobufs/rpc/connector.proto | 12 +- rpc/connector.pb.go | 125 ++++++--- rpc/connector.psrpc.go | 80 ++++-- 9 files changed, 852 insertions(+), 90 deletions(-) create mode 100644 livekit/livekit_connector_twilio.pb.go create mode 100644 protobufs/livekit_connector_twilio.proto diff --git a/livekit/livekit_connector.pb.go b/livekit/livekit_connector.pb.go index 1c326bc..42d39f5 100644 --- a/livekit/livekit_connector.pb.go +++ b/livekit/livekit_connector.pb.go @@ -38,34 +38,39 @@ var File_livekit_connector_proto protoreflect.FileDescriptor const file_livekit_connector_proto_rawDesc = "" + "\n" + - "\x17livekit_connector.proto\x12\alivekit\x1a livekit_connector_whatsapp.proto2\x90\x03\n" + + "\x17livekit_connector.proto\x12\alivekit\x1a livekit_connector_whatsapp.proto\x1a\x1elivekit_connector_twilio.proto2\xec\x03\n" + "\tConnector\x12W\n" + "\x10DialWhatsAppCall\x12 .livekit.DialWhatsAppCallRequest\x1a!.livekit.DialWhatsAppCallResponse\x12i\n" + "\x16DisconnectWhatsAppCall\x12&.livekit.DisconnectWhatsAppCallRequest\x1a'.livekit.DisconnectWhatsAppCallResponse\x12`\n" + "\x13ConnectWhatsAppCall\x12#.livekit.ConnectWhatsAppCallRequest\x1a$.livekit.ConnectWhatsAppCallResponse\x12]\n" + - "\x12AcceptWhatsAppCall\x12\".livekit.AcceptWhatsAppCallRequest\x1a#.livekit.AcceptWhatsAppCallResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3" + "\x12AcceptWhatsAppCall\x12\".livekit.AcceptWhatsAppCallRequest\x1a#.livekit.AcceptWhatsAppCallResponse\x12Z\n" + + "\x11ConnectTwilioCall\x12!.livekit.ConnectTwilioCallRequest\x1a\".livekit.ConnectTwilioCallResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3" var file_livekit_connector_proto_goTypes = []any{ (*DialWhatsAppCallRequest)(nil), // 0: livekit.DialWhatsAppCallRequest (*DisconnectWhatsAppCallRequest)(nil), // 1: livekit.DisconnectWhatsAppCallRequest (*ConnectWhatsAppCallRequest)(nil), // 2: livekit.ConnectWhatsAppCallRequest (*AcceptWhatsAppCallRequest)(nil), // 3: livekit.AcceptWhatsAppCallRequest - (*DialWhatsAppCallResponse)(nil), // 4: livekit.DialWhatsAppCallResponse - (*DisconnectWhatsAppCallResponse)(nil), // 5: livekit.DisconnectWhatsAppCallResponse - (*ConnectWhatsAppCallResponse)(nil), // 6: livekit.ConnectWhatsAppCallResponse - (*AcceptWhatsAppCallResponse)(nil), // 7: livekit.AcceptWhatsAppCallResponse + (*ConnectTwilioCallRequest)(nil), // 4: livekit.ConnectTwilioCallRequest + (*DialWhatsAppCallResponse)(nil), // 5: livekit.DialWhatsAppCallResponse + (*DisconnectWhatsAppCallResponse)(nil), // 6: livekit.DisconnectWhatsAppCallResponse + (*ConnectWhatsAppCallResponse)(nil), // 7: livekit.ConnectWhatsAppCallResponse + (*AcceptWhatsAppCallResponse)(nil), // 8: livekit.AcceptWhatsAppCallResponse + (*ConnectTwilioCallResponse)(nil), // 9: livekit.ConnectTwilioCallResponse } var file_livekit_connector_proto_depIdxs = []int32{ 0, // 0: livekit.Connector.DialWhatsAppCall:input_type -> livekit.DialWhatsAppCallRequest 1, // 1: livekit.Connector.DisconnectWhatsAppCall:input_type -> livekit.DisconnectWhatsAppCallRequest 2, // 2: livekit.Connector.ConnectWhatsAppCall:input_type -> livekit.ConnectWhatsAppCallRequest 3, // 3: livekit.Connector.AcceptWhatsAppCall:input_type -> livekit.AcceptWhatsAppCallRequest - 4, // 4: livekit.Connector.DialWhatsAppCall:output_type -> livekit.DialWhatsAppCallResponse - 5, // 5: livekit.Connector.DisconnectWhatsAppCall:output_type -> livekit.DisconnectWhatsAppCallResponse - 6, // 6: livekit.Connector.ConnectWhatsAppCall:output_type -> livekit.ConnectWhatsAppCallResponse - 7, // 7: livekit.Connector.AcceptWhatsAppCall:output_type -> livekit.AcceptWhatsAppCallResponse - 4, // [4:8] is the sub-list for method output_type - 0, // [0:4] is the sub-list for method input_type + 4, // 4: livekit.Connector.ConnectTwilioCall:input_type -> livekit.ConnectTwilioCallRequest + 5, // 5: livekit.Connector.DialWhatsAppCall:output_type -> livekit.DialWhatsAppCallResponse + 6, // 6: livekit.Connector.DisconnectWhatsAppCall:output_type -> livekit.DisconnectWhatsAppCallResponse + 7, // 7: livekit.Connector.ConnectWhatsAppCall:output_type -> livekit.ConnectWhatsAppCallResponse + 8, // 8: livekit.Connector.AcceptWhatsAppCall:output_type -> livekit.AcceptWhatsAppCallResponse + 9, // 9: livekit.Connector.ConnectTwilioCall:output_type -> livekit.ConnectTwilioCallResponse + 5, // [5:10] is the sub-list for method output_type + 0, // [0:5] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -77,6 +82,7 @@ func file_livekit_connector_proto_init() { return } file_livekit_connector_whatsapp_proto_init() + file_livekit_connector_twilio_proto_init() type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/livekit/livekit_connector.twirp.go b/livekit/livekit_connector.twirp.go index 37276ff..989d772 100644 --- a/livekit/livekit_connector.twirp.go +++ b/livekit/livekit_connector.twirp.go @@ -34,6 +34,8 @@ type Connector interface { ConnectWhatsAppCall(context.Context, *ConnectWhatsAppCallRequest) (*ConnectWhatsAppCallResponse, error) AcceptWhatsAppCall(context.Context, *AcceptWhatsAppCallRequest) (*AcceptWhatsAppCallResponse, error) + + ConnectTwilioCall(context.Context, *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) } // ========================= @@ -42,7 +44,7 @@ type Connector interface { type connectorProtobufClient struct { client HTTPClient - urls [4]string + urls [5]string interceptor twirp.Interceptor opts twirp.ClientOptions } @@ -70,11 +72,12 @@ func NewConnectorProtobufClient(baseURL string, client HTTPClient, opts ...twirp // Build method URLs: []/./ serviceURL := sanitizeBaseURL(baseURL) serviceURL += baseServicePath(pathPrefix, "livekit", "Connector") - urls := [4]string{ + urls := [5]string{ serviceURL + "DialWhatsAppCall", serviceURL + "DisconnectWhatsAppCall", serviceURL + "ConnectWhatsAppCall", serviceURL + "AcceptWhatsAppCall", + serviceURL + "ConnectTwilioCall", } return &connectorProtobufClient{ @@ -269,13 +272,59 @@ func (c *connectorProtobufClient) callAcceptWhatsAppCall(ctx context.Context, in return out, nil } +func (c *connectorProtobufClient) ConnectTwilioCall(ctx context.Context, in *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "Connector") + ctx = ctxsetters.WithMethodName(ctx, "ConnectTwilioCall") + caller := c.callConnectTwilioCall + if c.interceptor != nil { + caller = func(ctx context.Context, req *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*ConnectTwilioCallRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*ConnectTwilioCallRequest) when calling interceptor") + } + return c.callConnectTwilioCall(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*ConnectTwilioCallResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*ConnectTwilioCallResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *connectorProtobufClient) callConnectTwilioCall(ctx context.Context, in *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + out := new(ConnectTwilioCallResponse) + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[4], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + // ===================== // Connector JSON Client // ===================== type connectorJSONClient struct { client HTTPClient - urls [4]string + urls [5]string interceptor twirp.Interceptor opts twirp.ClientOptions } @@ -303,11 +352,12 @@ func NewConnectorJSONClient(baseURL string, client HTTPClient, opts ...twirp.Cli // Build method URLs: []/./ serviceURL := sanitizeBaseURL(baseURL) serviceURL += baseServicePath(pathPrefix, "livekit", "Connector") - urls := [4]string{ + urls := [5]string{ serviceURL + "DialWhatsAppCall", serviceURL + "DisconnectWhatsAppCall", serviceURL + "ConnectWhatsAppCall", serviceURL + "AcceptWhatsAppCall", + serviceURL + "ConnectTwilioCall", } return &connectorJSONClient{ @@ -502,6 +552,52 @@ func (c *connectorJSONClient) callAcceptWhatsAppCall(ctx context.Context, in *Ac return out, nil } +func (c *connectorJSONClient) ConnectTwilioCall(ctx context.Context, in *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + ctx = ctxsetters.WithPackageName(ctx, "livekit") + ctx = ctxsetters.WithServiceName(ctx, "Connector") + ctx = ctxsetters.WithMethodName(ctx, "ConnectTwilioCall") + caller := c.callConnectTwilioCall + if c.interceptor != nil { + caller = func(ctx context.Context, req *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + resp, err := c.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*ConnectTwilioCallRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*ConnectTwilioCallRequest) when calling interceptor") + } + return c.callConnectTwilioCall(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*ConnectTwilioCallResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*ConnectTwilioCallResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + return caller(ctx, in) +} + +func (c *connectorJSONClient) callConnectTwilioCall(ctx context.Context, in *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + out := new(ConnectTwilioCallResponse) + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[4], in, out) + if err != nil { + twerr, ok := err.(twirp.Error) + if !ok { + twerr = twirp.InternalErrorWith(err) + } + callClientError(ctx, c.opts.Hooks, twerr) + return nil, err + } + + callClientResponseReceived(ctx, c.opts.Hooks) + + return out, nil +} + // ======================== // Connector Server Handler // ======================== @@ -611,6 +707,9 @@ func (s *connectorServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) case "AcceptWhatsAppCall": s.serveAcceptWhatsAppCall(ctx, resp, req) return + case "ConnectTwilioCall": + s.serveConnectTwilioCall(ctx, resp, req) + return default: msg := fmt.Sprintf("no handler for path %q", req.URL.Path) s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) @@ -1338,6 +1437,186 @@ func (s *connectorServer) serveAcceptWhatsAppCallProtobuf(ctx context.Context, r callResponseSent(ctx, s.hooks) } +func (s *connectorServer) serveConnectTwilioCall(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + header := req.Header.Get("Content-Type") + i := strings.Index(header, ";") + if i == -1 { + i = len(header) + } + switch strings.TrimSpace(strings.ToLower(header[:i])) { + case "application/json": + s.serveConnectTwilioCallJSON(ctx, resp, req) + case "application/protobuf": + s.serveConnectTwilioCallProtobuf(ctx, resp, req) + default: + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) + twerr := badRouteError(msg, req.Method, req.URL.Path) + s.writeError(ctx, resp, twerr) + } +} + +func (s *connectorServer) serveConnectTwilioCallJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "ConnectTwilioCall") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + d := json.NewDecoder(req.Body) + rawReqBody := json.RawMessage{} + if err := d.Decode(&rawReqBody); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + reqContent := new(ConnectTwilioCallRequest) + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) + return + } + + handler := s.Connector.ConnectTwilioCall + if s.interceptor != nil { + handler = func(ctx context.Context, req *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*ConnectTwilioCallRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*ConnectTwilioCallRequest) when calling interceptor") + } + return s.Connector.ConnectTwilioCall(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*ConnectTwilioCallResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*ConnectTwilioCallResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *ConnectTwilioCallResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *ConnectTwilioCallResponse and nil error while calling ConnectTwilioCall. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} + respBytes, err := marshaler.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/json") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + +func (s *connectorServer) serveConnectTwilioCallProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { + var err error + ctx = ctxsetters.WithMethodName(ctx, "ConnectTwilioCall") + ctx, err = callRequestRouted(ctx, s.hooks) + if err != nil { + s.writeError(ctx, resp, err) + return + } + + buf, err := io.ReadAll(req.Body) + if err != nil { + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) + return + } + reqContent := new(ConnectTwilioCallRequest) + if err = proto.Unmarshal(buf, reqContent); err != nil { + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) + return + } + + handler := s.Connector.ConnectTwilioCall + if s.interceptor != nil { + handler = func(ctx context.Context, req *ConnectTwilioCallRequest) (*ConnectTwilioCallResponse, error) { + resp, err := s.interceptor( + func(ctx context.Context, req interface{}) (interface{}, error) { + typedReq, ok := req.(*ConnectTwilioCallRequest) + if !ok { + return nil, twirp.InternalError("failed type assertion req.(*ConnectTwilioCallRequest) when calling interceptor") + } + return s.Connector.ConnectTwilioCall(ctx, typedReq) + }, + )(ctx, req) + if resp != nil { + typedResp, ok := resp.(*ConnectTwilioCallResponse) + if !ok { + return nil, twirp.InternalError("failed type assertion resp.(*ConnectTwilioCallResponse) when calling interceptor") + } + return typedResp, err + } + return nil, err + } + } + + // Call service method + var respContent *ConnectTwilioCallResponse + func() { + defer ensurePanicResponses(ctx, resp, s.hooks) + respContent, err = handler(ctx, reqContent) + }() + + if err != nil { + s.writeError(ctx, resp, err) + return + } + if respContent == nil { + s.writeError(ctx, resp, twirp.InternalError("received a nil *ConnectTwilioCallResponse and nil error while calling ConnectTwilioCall. nil responses are not supported")) + return + } + + ctx = callResponsePrepared(ctx, s.hooks) + + respBytes, err := proto.Marshal(respContent) + if err != nil { + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) + return + } + + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) + resp.Header().Set("Content-Type", "application/protobuf") + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) + resp.WriteHeader(http.StatusOK) + if n, err := resp.Write(respBytes); err != nil { + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) + twerr := twirp.NewError(twirp.Unknown, msg) + ctx = callError(ctx, s.hooks, twerr) + } + callResponseSent(ctx, s.hooks) +} + func (s *connectorServer) ServiceDescriptor() ([]byte, int) { return twirpFileDescriptor7, 0 } @@ -1354,20 +1633,23 @@ func (s *connectorServer) PathPrefix() string { } var twirpFileDescriptor7 = []byte{ - // 238 bytes of a gzipped FileDescriptorProto + // 273 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0xc9, 0x2c, 0x4b, 0xcd, 0xce, 0x2c, 0x89, 0x4f, 0xce, 0xcf, 0xcb, 0x4b, 0x4d, 0x2e, 0xc9, 0x2f, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x87, 0x4a, 0x48, 0x29, 0x60, 0xa8, 0x88, 0x2f, 0xcf, 0x48, 0x2c, - 0x29, 0x4e, 0x2c, 0x28, 0x80, 0x28, 0x35, 0x9a, 0xc0, 0xcc, 0xc5, 0xe9, 0x0c, 0x93, 0x14, 0x0a, - 0xe7, 0x12, 0x70, 0xc9, 0x4c, 0xcc, 0x09, 0x07, 0xa9, 0x71, 0x2c, 0x28, 0x70, 0x4e, 0xcc, 0xc9, - 0x11, 0x52, 0xd0, 0x83, 0x1a, 0xa2, 0x87, 0x2e, 0x15, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0x22, - 0xa5, 0x88, 0x47, 0x45, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0xaa, 0x50, 0x26, 0x97, 0x98, 0x4b, 0x66, - 0x31, 0xd4, 0x15, 0x28, 0xc6, 0xab, 0x21, 0x69, 0xc6, 0xa6, 0x00, 0x66, 0x89, 0x3a, 0x41, 0x75, - 0x50, 0xab, 0x12, 0xb8, 0x84, 0x9d, 0xb1, 0xd8, 0xa3, 0x0c, 0xd7, 0xef, 0x8c, 0xdb, 0x12, 0x15, - 0xfc, 0x8a, 0xa0, 0x36, 0xc4, 0x72, 0x09, 0x39, 0x26, 0x27, 0xa7, 0x16, 0xa0, 0x5a, 0xa0, 0x04, - 0xd7, 0x8b, 0x29, 0x09, 0x33, 0x5f, 0x19, 0xaf, 0x1a, 0x88, 0xf1, 0x4e, 0x6e, 0x51, 0xca, 0xe9, - 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9, 0xfa, 0x50, 0x0d, 0xfa, 0xe0, 0xe8, 0x4a, - 0xce, 0xcf, 0x81, 0x09, 0xac, 0x62, 0xe2, 0xf5, 0xc9, 0x2c, 0x4b, 0xf5, 0xce, 0x2c, 0xd1, 0x0b, - 0x00, 0x49, 0xbd, 0x62, 0xe2, 0x83, 0xf2, 0xad, 0xac, 0xc0, 0x02, 0x49, 0x6c, 0x60, 0x2d, 0xc6, - 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7a, 0x7c, 0x2f, 0xe1, 0x27, 0x02, 0x00, 0x00, + 0x29, 0x4e, 0x2c, 0x28, 0x80, 0x28, 0x95, 0x92, 0xc3, 0x54, 0x51, 0x52, 0x9e, 0x99, 0x93, 0x99, + 0x0f, 0x91, 0x37, 0x7a, 0xc3, 0xcc, 0xc5, 0xe9, 0x0c, 0x93, 0x12, 0x0a, 0xe7, 0x12, 0x70, 0xc9, + 0x4c, 0xcc, 0x09, 0x07, 0x99, 0xe1, 0x58, 0x50, 0xe0, 0x9c, 0x98, 0x93, 0x23, 0xa4, 0xa0, 0x07, + 0x35, 0x42, 0x0f, 0x5d, 0x2a, 0x28, 0xb5, 0xb0, 0x34, 0xb5, 0xb8, 0x44, 0x4a, 0x11, 0x8f, 0x8a, + 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, 0xa1, 0x4c, 0x2e, 0x31, 0x97, 0xcc, 0x62, 0xa8, 0x1b, 0x50, + 0x8c, 0x57, 0x43, 0xd2, 0x8c, 0x4d, 0x01, 0xcc, 0x12, 0x75, 0x82, 0xea, 0xa0, 0x56, 0x25, 0x70, + 0x09, 0x3b, 0x63, 0xb1, 0x47, 0x19, 0xae, 0xdf, 0x19, 0xb7, 0x25, 0x2a, 0xf8, 0x15, 0x41, 0x6d, + 0x88, 0xe5, 0x12, 0x72, 0x4c, 0x4e, 0x4e, 0x2d, 0x40, 0xb5, 0x40, 0x09, 0xae, 0x17, 0x53, 0x12, + 0x66, 0xbe, 0x32, 0x5e, 0x35, 0x50, 0xe3, 0xa3, 0xb8, 0x04, 0xa1, 0xb6, 0x87, 0x80, 0x63, 0x0a, + 0x6c, 0xba, 0x22, 0xba, 0xcb, 0x10, 0x72, 0x30, 0xc3, 0x95, 0xf0, 0x29, 0x81, 0x98, 0xed, 0xe4, + 0x16, 0xa5, 0x9c, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, 0x9f, 0xab, 0x0f, 0x55, 0xaf, + 0x0f, 0x4e, 0x0a, 0xc9, 0xf9, 0x39, 0x30, 0x81, 0x55, 0x4c, 0xbc, 0x3e, 0x99, 0x65, 0xa9, 0xde, + 0x99, 0x25, 0x7a, 0x01, 0x20, 0xa9, 0x57, 0x4c, 0x7c, 0x50, 0xbe, 0x95, 0x15, 0x58, 0x20, 0x89, + 0x0d, 0xac, 0xc5, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x18, 0x8d, 0x1d, 0x42, 0xa3, 0x02, 0x00, + 0x00, } diff --git a/livekit/livekit_connector_twilio.pb.go b/livekit/livekit_connector_twilio.pb.go new file mode 100644 index 0000000..3225d75 --- /dev/null +++ b/livekit/livekit_connector_twilio.pb.go @@ -0,0 +1,317 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.10 +// protoc v4.23.4 +// source: livekit_connector_twilio.proto + +package livekit + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ConnectTwilioCallRequest_TwilioCallDirection int32 + +const ( + ConnectTwilioCallRequest_TWILIO_CALL_DIRECTION_INBOUND ConnectTwilioCallRequest_TwilioCallDirection = 0 + ConnectTwilioCallRequest_TWILIO_CALL_DIRECTION_OUTBOUND ConnectTwilioCallRequest_TwilioCallDirection = 1 +) + +// Enum value maps for ConnectTwilioCallRequest_TwilioCallDirection. +var ( + ConnectTwilioCallRequest_TwilioCallDirection_name = map[int32]string{ + 0: "TWILIO_CALL_DIRECTION_INBOUND", + 1: "TWILIO_CALL_DIRECTION_OUTBOUND", + } + ConnectTwilioCallRequest_TwilioCallDirection_value = map[string]int32{ + "TWILIO_CALL_DIRECTION_INBOUND": 0, + "TWILIO_CALL_DIRECTION_OUTBOUND": 1, + } +) + +func (x ConnectTwilioCallRequest_TwilioCallDirection) Enum() *ConnectTwilioCallRequest_TwilioCallDirection { + p := new(ConnectTwilioCallRequest_TwilioCallDirection) + *p = x + return p +} + +func (x ConnectTwilioCallRequest_TwilioCallDirection) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (ConnectTwilioCallRequest_TwilioCallDirection) Descriptor() protoreflect.EnumDescriptor { + return file_livekit_connector_twilio_proto_enumTypes[0].Descriptor() +} + +func (ConnectTwilioCallRequest_TwilioCallDirection) Type() protoreflect.EnumType { + return &file_livekit_connector_twilio_proto_enumTypes[0] +} + +func (x ConnectTwilioCallRequest_TwilioCallDirection) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use ConnectTwilioCallRequest_TwilioCallDirection.Descriptor instead. +func (ConnectTwilioCallRequest_TwilioCallDirection) EnumDescriptor() ([]byte, []int) { + return file_livekit_connector_twilio_proto_rawDescGZIP(), []int{0, 0} +} + +type ConnectTwilioCallRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The Direction of the call + TwilioCallDirection ConnectTwilioCallRequest_TwilioCallDirection `protobuf:"varint,1,opt,name=twilio_call_direction,json=twilioCallDirection,proto3,enum=livekit.ConnectTwilioCallRequest_TwilioCallDirection" json:"twilio_call_direction,omitempty"` + // What LiveKit room should this call be connected to + RoomName string `protobuf:"bytes,2,opt,name=room_name,json=roomName,proto3" json:"room_name,omitempty"` + // Optional agents to dispatch the call to + Agents []*RoomAgentDispatch `protobuf:"bytes,3,rep,name=agents,proto3" json:"agents,omitempty"` + // Optional identity of the participant in LiveKit room + ParticipantIdentity string `protobuf:"bytes,4,opt,name=participant_identity,json=participantIdentity,proto3" json:"participant_identity,omitempty"` + // Optional name of the participant in LiveKit room + ParticipantName string `protobuf:"bytes,5,opt,name=participant_name,json=participantName,proto3" json:"participant_name,omitempty"` + // Optional user-defined metadata. Will be attached to a created Participant in the room. + ParticipantMetadata string `protobuf:"bytes,6,opt,name=participant_metadata,json=participantMetadata,proto3" json:"participant_metadata,omitempty"` + // Optional user-defined attributes. Will be attached to a created Participant in the room. + ParticipantAttributes map[string]string `protobuf:"bytes,7,rep,name=participant_attributes,json=participantAttributes,proto3" json:"participant_attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // Country where the call terminates as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). This will be used by the livekit infrastructure to route calls. + DestinationCountry string `protobuf:"bytes,8,opt,name=destination_country,json=destinationCountry,proto3" json:"destination_country,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectTwilioCallRequest) Reset() { + *x = ConnectTwilioCallRequest{} + mi := &file_livekit_connector_twilio_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectTwilioCallRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectTwilioCallRequest) ProtoMessage() {} + +func (x *ConnectTwilioCallRequest) ProtoReflect() protoreflect.Message { + mi := &file_livekit_connector_twilio_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectTwilioCallRequest.ProtoReflect.Descriptor instead. +func (*ConnectTwilioCallRequest) Descriptor() ([]byte, []int) { + return file_livekit_connector_twilio_proto_rawDescGZIP(), []int{0} +} + +func (x *ConnectTwilioCallRequest) GetTwilioCallDirection() ConnectTwilioCallRequest_TwilioCallDirection { + if x != nil { + return x.TwilioCallDirection + } + return ConnectTwilioCallRequest_TWILIO_CALL_DIRECTION_INBOUND +} + +func (x *ConnectTwilioCallRequest) GetRoomName() string { + if x != nil { + return x.RoomName + } + return "" +} + +func (x *ConnectTwilioCallRequest) GetAgents() []*RoomAgentDispatch { + if x != nil { + return x.Agents + } + return nil +} + +func (x *ConnectTwilioCallRequest) GetParticipantIdentity() string { + if x != nil { + return x.ParticipantIdentity + } + return "" +} + +func (x *ConnectTwilioCallRequest) GetParticipantName() string { + if x != nil { + return x.ParticipantName + } + return "" +} + +func (x *ConnectTwilioCallRequest) GetParticipantMetadata() string { + if x != nil { + return x.ParticipantMetadata + } + return "" +} + +func (x *ConnectTwilioCallRequest) GetParticipantAttributes() map[string]string { + if x != nil { + return x.ParticipantAttributes + } + return nil +} + +func (x *ConnectTwilioCallRequest) GetDestinationCountry() string { + if x != nil { + return x.DestinationCountry + } + return "" +} + +type ConnectTwilioCallResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The websocket URL which twilio media stream will connect to + ConnectUrl string `protobuf:"bytes,1,opt,name=connect_url,json=connectUrl,proto3" json:"connect_url,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectTwilioCallResponse) Reset() { + *x = ConnectTwilioCallResponse{} + mi := &file_livekit_connector_twilio_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectTwilioCallResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectTwilioCallResponse) ProtoMessage() {} + +func (x *ConnectTwilioCallResponse) ProtoReflect() protoreflect.Message { + mi := &file_livekit_connector_twilio_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectTwilioCallResponse.ProtoReflect.Descriptor instead. +func (*ConnectTwilioCallResponse) Descriptor() ([]byte, []int) { + return file_livekit_connector_twilio_proto_rawDescGZIP(), []int{1} +} + +func (x *ConnectTwilioCallResponse) GetConnectUrl() string { + if x != nil { + return x.ConnectUrl + } + return "" +} + +var File_livekit_connector_twilio_proto protoreflect.FileDescriptor + +const file_livekit_connector_twilio_proto_rawDesc = "" + + "\n" + + "\x1elivekit_connector_twilio.proto\x12\alivekit\x1a\x1clivekit_agent_dispatch.proto\"\xb5\x05\n" + + "\x18ConnectTwilioCallRequest\x12i\n" + + "\x15twilio_call_direction\x18\x01 \x01(\x0e25.livekit.ConnectTwilioCallRequest.TwilioCallDirectionR\x13twilioCallDirection\x12\x1b\n" + + "\troom_name\x18\x02 \x01(\tR\broomName\x122\n" + + "\x06agents\x18\x03 \x03(\v2\x1a.livekit.RoomAgentDispatchR\x06agents\x121\n" + + "\x14participant_identity\x18\x04 \x01(\tR\x13participantIdentity\x12)\n" + + "\x10participant_name\x18\x05 \x01(\tR\x0fparticipantName\x121\n" + + "\x14participant_metadata\x18\x06 \x01(\tR\x13participantMetadata\x12s\n" + + "\x16participant_attributes\x18\a \x03(\v2<.livekit.ConnectTwilioCallRequest.ParticipantAttributesEntryR\x15participantAttributes\x12/\n" + + "\x13destination_country\x18\b \x01(\tR\x12destinationCountry\x1aH\n" + + "\x1aParticipantAttributesEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\\\n" + + "\x13TwilioCallDirection\x12!\n" + + "\x1dTWILIO_CALL_DIRECTION_INBOUND\x10\x00\x12\"\n" + + "\x1eTWILIO_CALL_DIRECTION_OUTBOUND\x10\x01\"<\n" + + "\x19ConnectTwilioCallResponse\x12\x1f\n" + + "\vconnect_url\x18\x01 \x01(\tR\n" + + "connectUrlBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3" + +var ( + file_livekit_connector_twilio_proto_rawDescOnce sync.Once + file_livekit_connector_twilio_proto_rawDescData []byte +) + +func file_livekit_connector_twilio_proto_rawDescGZIP() []byte { + file_livekit_connector_twilio_proto_rawDescOnce.Do(func() { + file_livekit_connector_twilio_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_livekit_connector_twilio_proto_rawDesc), len(file_livekit_connector_twilio_proto_rawDesc))) + }) + return file_livekit_connector_twilio_proto_rawDescData +} + +var file_livekit_connector_twilio_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_livekit_connector_twilio_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_livekit_connector_twilio_proto_goTypes = []any{ + (ConnectTwilioCallRequest_TwilioCallDirection)(0), // 0: livekit.ConnectTwilioCallRequest.TwilioCallDirection + (*ConnectTwilioCallRequest)(nil), // 1: livekit.ConnectTwilioCallRequest + (*ConnectTwilioCallResponse)(nil), // 2: livekit.ConnectTwilioCallResponse + nil, // 3: livekit.ConnectTwilioCallRequest.ParticipantAttributesEntry + (*RoomAgentDispatch)(nil), // 4: livekit.RoomAgentDispatch +} +var file_livekit_connector_twilio_proto_depIdxs = []int32{ + 0, // 0: livekit.ConnectTwilioCallRequest.twilio_call_direction:type_name -> livekit.ConnectTwilioCallRequest.TwilioCallDirection + 4, // 1: livekit.ConnectTwilioCallRequest.agents:type_name -> livekit.RoomAgentDispatch + 3, // 2: livekit.ConnectTwilioCallRequest.participant_attributes:type_name -> livekit.ConnectTwilioCallRequest.ParticipantAttributesEntry + 3, // [3:3] is the sub-list for method output_type + 3, // [3:3] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name +} + +func init() { file_livekit_connector_twilio_proto_init() } +func file_livekit_connector_twilio_proto_init() { + if File_livekit_connector_twilio_proto != nil { + return + } + file_livekit_agent_dispatch_proto_init() + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_livekit_connector_twilio_proto_rawDesc), len(file_livekit_connector_twilio_proto_rawDesc)), + NumEnums: 1, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_livekit_connector_twilio_proto_goTypes, + DependencyIndexes: file_livekit_connector_twilio_proto_depIdxs, + EnumInfos: file_livekit_connector_twilio_proto_enumTypes, + MessageInfos: file_livekit_connector_twilio_proto_msgTypes, + }.Build() + File_livekit_connector_twilio_proto = out.File + file_livekit_connector_twilio_proto_goTypes = nil + file_livekit_connector_twilio_proto_depIdxs = nil +} diff --git a/magefile.go b/magefile.go index ab1eebf..37f8051 100644 --- a/magefile.go +++ b/magefile.go @@ -52,12 +52,13 @@ func Proto() error { "livekit_phone_number.proto", "livekit_connector.proto", "livekit_connector_whatsapp.proto", + "livekit_connector_twilio.proto", } agentProtoFiles := []string{ "agent/livekit_agent_session.proto", } - + protoFiles := []string{ "livekit_agent.proto", "livekit_analytics.proto", @@ -173,7 +174,7 @@ func Proto() error { return err } } - + fmt.Println("generating grpc protobuf") args = append([]string{ "--go_out", ".", diff --git a/protobufs/livekit_connector.proto b/protobufs/livekit_connector.proto index c537572..81148b6 100644 --- a/protobufs/livekit_connector.proto +++ b/protobufs/livekit_connector.proto @@ -20,6 +20,7 @@ option csharp_namespace = "LiveKit.Proto"; option ruby_package = "LiveKit::Proto"; import "livekit_connector_whatsapp.proto"; +import "livekit_connector_twilio.proto"; service Connector { rpc DialWhatsAppCall(DialWhatsAppCallRequest) returns (DialWhatsAppCallResponse); @@ -29,4 +30,6 @@ service Connector { rpc ConnectWhatsAppCall(ConnectWhatsAppCallRequest) returns (ConnectWhatsAppCallResponse); rpc AcceptWhatsAppCall(AcceptWhatsAppCallRequest) returns (AcceptWhatsAppCallResponse); -} \ No newline at end of file + + rpc ConnectTwilioCall(ConnectTwilioCallRequest) returns (ConnectTwilioCallResponse); +} diff --git a/protobufs/livekit_connector_twilio.proto b/protobufs/livekit_connector_twilio.proto new file mode 100644 index 0000000..efe962c --- /dev/null +++ b/protobufs/livekit_connector_twilio.proto @@ -0,0 +1,52 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package livekit; +option go_package = "github.com/livekit/protocol/livekit"; +option csharp_namespace = "LiveKit.Proto"; +option ruby_package = "LiveKit::Proto"; + +import "livekit_agent_dispatch.proto"; + +message ConnectTwilioCallRequest { + + enum TwilioCallDirection { + TWILIO_CALL_DIRECTION_INBOUND = 0; + TWILIO_CALL_DIRECTION_OUTBOUND = 1; + } + + // The Direction of the call + TwilioCallDirection twilio_call_direction = 1; + // What LiveKit room should this call be connected to + string room_name = 2; + // Optional agents to dispatch the call to + repeated RoomAgentDispatch agents = 3; + // Optional identity of the participant in LiveKit room + string participant_identity= 4; + // Optional name of the participant in LiveKit room + string participant_name = 5; + // Optional user-defined metadata. Will be attached to a created Participant in the room. + string participant_metadata = 6; + // Optional user-defined attributes. Will be attached to a created Participant in the room. + map participant_attributes = 7; + // Country where the call terminates as ISO 3166-1 alpha-2 (https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). This will be used by the livekit infrastructure to route calls. + string destination_country = 8; +} + +message ConnectTwilioCallResponse { + // The websocket URL which twilio media stream will connect to + string connect_url = 1; +} diff --git a/protobufs/rpc/connector.proto b/protobufs/rpc/connector.proto index a1d78a4..7e78fb9 100644 --- a/protobufs/rpc/connector.proto +++ b/protobufs/rpc/connector.proto @@ -20,6 +20,7 @@ option go_package = "github.com/livekit/protocol/rpc"; import "options.proto"; import "livekit_connector_whatsapp.proto"; +import "livekit_connector_twilio.proto"; import "rpc/common.proto"; service ConnectorInternal { @@ -30,6 +31,10 @@ service ConnectorInternal { rpc AcceptWhatsAppCall(InternalAcceptWhatsAppCallRequest) returns (livekit.AcceptWhatsAppCallResponse) { option (psrpc.options).topics = true; }; + + rpc ConnectTwilioCall(InternalConnectTwilioCallRequest) returns (livekit.ConnectTwilioCallResponse) { + option (psrpc.options).topics = true; + }; } service ConnectorHandler { @@ -62,4 +67,9 @@ message InternalDialWhatsAppCallRequest { message InternalAcceptWhatsAppCallRequest { livekit.AcceptWhatsAppCallRequest request = 1; InternalRoomJoinInfo room_join_info = 2; -} \ No newline at end of file +} + +message InternalConnectTwilioCallRequest { + livekit.ConnectTwilioCallRequest request = 1; + InternalRoomJoinInfo room_join_info = 2; +} diff --git a/rpc/connector.pb.go b/rpc/connector.pb.go index 0cff5ad..a0868fe 100644 --- a/rpc/connector.pb.go +++ b/rpc/connector.pb.go @@ -141,20 +141,76 @@ func (x *InternalAcceptWhatsAppCallRequest) GetRoomJoinInfo() *InternalRoomJoinI return nil } +type InternalConnectTwilioCallRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Request *livekit.ConnectTwilioCallRequest `protobuf:"bytes,1,opt,name=request,proto3" json:"request,omitempty"` + RoomJoinInfo *InternalRoomJoinInfo `protobuf:"bytes,2,opt,name=room_join_info,json=roomJoinInfo,proto3" json:"room_join_info,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InternalConnectTwilioCallRequest) Reset() { + *x = InternalConnectTwilioCallRequest{} + mi := &file_rpc_connector_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InternalConnectTwilioCallRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalConnectTwilioCallRequest) ProtoMessage() {} + +func (x *InternalConnectTwilioCallRequest) ProtoReflect() protoreflect.Message { + mi := &file_rpc_connector_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use InternalConnectTwilioCallRequest.ProtoReflect.Descriptor instead. +func (*InternalConnectTwilioCallRequest) Descriptor() ([]byte, []int) { + return file_rpc_connector_proto_rawDescGZIP(), []int{2} +} + +func (x *InternalConnectTwilioCallRequest) GetRequest() *livekit.ConnectTwilioCallRequest { + if x != nil { + return x.Request + } + return nil +} + +func (x *InternalConnectTwilioCallRequest) GetRoomJoinInfo() *InternalRoomJoinInfo { + if x != nil { + return x.RoomJoinInfo + } + return nil +} + var File_rpc_connector_proto protoreflect.FileDescriptor const file_rpc_connector_proto_rawDesc = "" + "\n" + - "\x13rpc/connector.proto\x12\x03rpc\x1a\roptions.proto\x1a livekit_connector_whatsapp.proto\x1a\x10rpc/common.proto\"\x9e\x01\n" + + "\x13rpc/connector.proto\x12\x03rpc\x1a\roptions.proto\x1a livekit_connector_whatsapp.proto\x1a\x1elivekit_connector_twilio.proto\x1a\x10rpc/common.proto\"\x9e\x01\n" + "\x1fInternalDialWhatsAppCallRequest\x12:\n" + "\arequest\x18\x01 \x01(\v2 .livekit.DialWhatsAppCallRequestR\arequest\x12?\n" + "\x0eroom_join_info\x18\x02 \x01(\v2\x19.rpc.InternalRoomJoinInfoR\froomJoinInfo\"\xa2\x01\n" + "!InternalAcceptWhatsAppCallRequest\x12<\n" + "\arequest\x18\x01 \x01(\v2\".livekit.AcceptWhatsAppCallRequestR\arequest\x12?\n" + - "\x0eroom_join_info\x18\x02 \x01(\v2\x19.rpc.InternalRoomJoinInfoR\froomJoinInfo2\xe3\x01\n" + + "\x0eroom_join_info\x18\x02 \x01(\v2\x19.rpc.InternalRoomJoinInfoR\froomJoinInfo\"\xa0\x01\n" + + " InternalConnectTwilioCallRequest\x12;\n" + + "\arequest\x18\x01 \x01(\v2!.livekit.ConnectTwilioCallRequestR\arequest\x12?\n" + + "\x0eroom_join_info\x18\x02 \x01(\v2\x19.rpc.InternalRoomJoinInfoR\froomJoinInfo2\xcb\x02\n" + "\x11ConnectorInternal\x12c\n" + "\x10DialWhatsAppCall\x12$.rpc.InternalDialWhatsAppCallRequest\x1a!.livekit.DialWhatsAppCallResponse\"\x06\xb2\x89\x01\x02\x10\x01\x12i\n" + - "\x12AcceptWhatsAppCall\x12&.rpc.InternalAcceptWhatsAppCallRequest\x1a#.livekit.AcceptWhatsAppCallResponse\"\x06\xb2\x89\x01\x02\x10\x012\x98\x02\n" + + "\x12AcceptWhatsAppCall\x12&.rpc.InternalAcceptWhatsAppCallRequest\x1a#.livekit.AcceptWhatsAppCallResponse\"\x06\xb2\x89\x01\x02\x10\x01\x12f\n" + + "\x11ConnectTwilioCall\x12%.rpc.InternalConnectTwilioCallRequest\x1a\".livekit.ConnectTwilioCallResponse\"\x06\xb2\x89\x01\x02\x10\x012\x98\x02\n" + "\x10ConnectorHandler\x12|\n" + "\x13ConnectWhatsAppCall\x12#.livekit.ConnectWhatsAppCallRequest\x1a$.livekit.ConnectWhatsAppCallResponse\"\x1a\xb2\x89\x01\x16\x10\x01\x1a\x12\x12\x10whatsapp_call_id\x12\x85\x01\n" + "\x16DisconnectWhatsAppCall\x12&.livekit.DisconnectWhatsAppCallRequest\x1a'.livekit.DisconnectWhatsAppCallResponse\"\x1a\xb2\x89\x01\x16\x10\x01\x1a\x12\x12\x10whatsapp_call_idB!Z\x1fgithub.com/livekit/protocol/rpcb\x06proto3" @@ -171,38 +227,45 @@ func file_rpc_connector_proto_rawDescGZIP() []byte { return file_rpc_connector_proto_rawDescData } -var file_rpc_connector_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_rpc_connector_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_rpc_connector_proto_goTypes = []any{ (*InternalDialWhatsAppCallRequest)(nil), // 0: rpc.InternalDialWhatsAppCallRequest (*InternalAcceptWhatsAppCallRequest)(nil), // 1: rpc.InternalAcceptWhatsAppCallRequest - (*livekit.DialWhatsAppCallRequest)(nil), // 2: livekit.DialWhatsAppCallRequest - (*InternalRoomJoinInfo)(nil), // 3: rpc.InternalRoomJoinInfo - (*livekit.AcceptWhatsAppCallRequest)(nil), // 4: livekit.AcceptWhatsAppCallRequest - (*livekit.ConnectWhatsAppCallRequest)(nil), // 5: livekit.ConnectWhatsAppCallRequest - (*livekit.DisconnectWhatsAppCallRequest)(nil), // 6: livekit.DisconnectWhatsAppCallRequest - (*livekit.DialWhatsAppCallResponse)(nil), // 7: livekit.DialWhatsAppCallResponse - (*livekit.AcceptWhatsAppCallResponse)(nil), // 8: livekit.AcceptWhatsAppCallResponse - (*livekit.ConnectWhatsAppCallResponse)(nil), // 9: livekit.ConnectWhatsAppCallResponse - (*livekit.DisconnectWhatsAppCallResponse)(nil), // 10: livekit.DisconnectWhatsAppCallResponse + (*InternalConnectTwilioCallRequest)(nil), // 2: rpc.InternalConnectTwilioCallRequest + (*livekit.DialWhatsAppCallRequest)(nil), // 3: livekit.DialWhatsAppCallRequest + (*InternalRoomJoinInfo)(nil), // 4: rpc.InternalRoomJoinInfo + (*livekit.AcceptWhatsAppCallRequest)(nil), // 5: livekit.AcceptWhatsAppCallRequest + (*livekit.ConnectTwilioCallRequest)(nil), // 6: livekit.ConnectTwilioCallRequest + (*livekit.ConnectWhatsAppCallRequest)(nil), // 7: livekit.ConnectWhatsAppCallRequest + (*livekit.DisconnectWhatsAppCallRequest)(nil), // 8: livekit.DisconnectWhatsAppCallRequest + (*livekit.DialWhatsAppCallResponse)(nil), // 9: livekit.DialWhatsAppCallResponse + (*livekit.AcceptWhatsAppCallResponse)(nil), // 10: livekit.AcceptWhatsAppCallResponse + (*livekit.ConnectTwilioCallResponse)(nil), // 11: livekit.ConnectTwilioCallResponse + (*livekit.ConnectWhatsAppCallResponse)(nil), // 12: livekit.ConnectWhatsAppCallResponse + (*livekit.DisconnectWhatsAppCallResponse)(nil), // 13: livekit.DisconnectWhatsAppCallResponse } var file_rpc_connector_proto_depIdxs = []int32{ - 2, // 0: rpc.InternalDialWhatsAppCallRequest.request:type_name -> livekit.DialWhatsAppCallRequest - 3, // 1: rpc.InternalDialWhatsAppCallRequest.room_join_info:type_name -> rpc.InternalRoomJoinInfo - 4, // 2: rpc.InternalAcceptWhatsAppCallRequest.request:type_name -> livekit.AcceptWhatsAppCallRequest - 3, // 3: rpc.InternalAcceptWhatsAppCallRequest.room_join_info:type_name -> rpc.InternalRoomJoinInfo - 0, // 4: rpc.ConnectorInternal.DialWhatsAppCall:input_type -> rpc.InternalDialWhatsAppCallRequest - 1, // 5: rpc.ConnectorInternal.AcceptWhatsAppCall:input_type -> rpc.InternalAcceptWhatsAppCallRequest - 5, // 6: rpc.ConnectorHandler.ConnectWhatsAppCall:input_type -> livekit.ConnectWhatsAppCallRequest - 6, // 7: rpc.ConnectorHandler.DisconnectWhatsAppCall:input_type -> livekit.DisconnectWhatsAppCallRequest - 7, // 8: rpc.ConnectorInternal.DialWhatsAppCall:output_type -> livekit.DialWhatsAppCallResponse - 8, // 9: rpc.ConnectorInternal.AcceptWhatsAppCall:output_type -> livekit.AcceptWhatsAppCallResponse - 9, // 10: rpc.ConnectorHandler.ConnectWhatsAppCall:output_type -> livekit.ConnectWhatsAppCallResponse - 10, // 11: rpc.ConnectorHandler.DisconnectWhatsAppCall:output_type -> livekit.DisconnectWhatsAppCallResponse - 8, // [8:12] is the sub-list for method output_type - 4, // [4:8] is the sub-list for method input_type - 4, // [4:4] is the sub-list for extension type_name - 4, // [4:4] is the sub-list for extension extendee - 0, // [0:4] is the sub-list for field type_name + 3, // 0: rpc.InternalDialWhatsAppCallRequest.request:type_name -> livekit.DialWhatsAppCallRequest + 4, // 1: rpc.InternalDialWhatsAppCallRequest.room_join_info:type_name -> rpc.InternalRoomJoinInfo + 5, // 2: rpc.InternalAcceptWhatsAppCallRequest.request:type_name -> livekit.AcceptWhatsAppCallRequest + 4, // 3: rpc.InternalAcceptWhatsAppCallRequest.room_join_info:type_name -> rpc.InternalRoomJoinInfo + 6, // 4: rpc.InternalConnectTwilioCallRequest.request:type_name -> livekit.ConnectTwilioCallRequest + 4, // 5: rpc.InternalConnectTwilioCallRequest.room_join_info:type_name -> rpc.InternalRoomJoinInfo + 0, // 6: rpc.ConnectorInternal.DialWhatsAppCall:input_type -> rpc.InternalDialWhatsAppCallRequest + 1, // 7: rpc.ConnectorInternal.AcceptWhatsAppCall:input_type -> rpc.InternalAcceptWhatsAppCallRequest + 2, // 8: rpc.ConnectorInternal.ConnectTwilioCall:input_type -> rpc.InternalConnectTwilioCallRequest + 7, // 9: rpc.ConnectorHandler.ConnectWhatsAppCall:input_type -> livekit.ConnectWhatsAppCallRequest + 8, // 10: rpc.ConnectorHandler.DisconnectWhatsAppCall:input_type -> livekit.DisconnectWhatsAppCallRequest + 9, // 11: rpc.ConnectorInternal.DialWhatsAppCall:output_type -> livekit.DialWhatsAppCallResponse + 10, // 12: rpc.ConnectorInternal.AcceptWhatsAppCall:output_type -> livekit.AcceptWhatsAppCallResponse + 11, // 13: rpc.ConnectorInternal.ConnectTwilioCall:output_type -> livekit.ConnectTwilioCallResponse + 12, // 14: rpc.ConnectorHandler.ConnectWhatsAppCall:output_type -> livekit.ConnectWhatsAppCallResponse + 13, // 15: rpc.ConnectorHandler.DisconnectWhatsAppCall:output_type -> livekit.DisconnectWhatsAppCallResponse + 11, // [11:16] is the sub-list for method output_type + 6, // [6:11] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_rpc_connector_proto_init() } @@ -217,7 +280,7 @@ func file_rpc_connector_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_rpc_connector_proto_rawDesc), len(file_rpc_connector_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 2, }, diff --git a/rpc/connector.psrpc.go b/rpc/connector.psrpc.go index dc3eb65..bca4c39 100644 --- a/rpc/connector.psrpc.go +++ b/rpc/connector.psrpc.go @@ -14,6 +14,7 @@ import ( "github.com/livekit/psrpc/version" ) import livekit10 "github.com/livekit/protocol/livekit" +import livekit11 "github.com/livekit/protocol/livekit" var _ = version.PsrpcVersion_0_7 @@ -26,6 +27,8 @@ type ConnectorInternalClient interface { AcceptWhatsAppCall(ctx context.Context, topic string, req *InternalAcceptWhatsAppCallRequest, opts ...psrpc.RequestOption) (*livekit10.AcceptWhatsAppCallResponse, error) + ConnectTwilioCall(ctx context.Context, topic string, req *InternalConnectTwilioCallRequest, opts ...psrpc.RequestOption) (*livekit11.ConnectTwilioCallResponse, error) + // Close immediately, without waiting for pending RPCs Close() } @@ -38,6 +41,8 @@ type ConnectorInternalServerImpl interface { DialWhatsAppCall(context.Context, *InternalDialWhatsAppCallRequest) (*livekit10.DialWhatsAppCallResponse, error) AcceptWhatsAppCall(context.Context, *InternalAcceptWhatsAppCallRequest) (*livekit10.AcceptWhatsAppCallResponse, error) + + ConnectTwilioCall(context.Context, *InternalConnectTwilioCallRequest) (*livekit11.ConnectTwilioCallResponse, error) } // ================================== @@ -49,6 +54,8 @@ type ConnectorInternalServer interface { DeregisterDialWhatsAppCallTopic(topic string) RegisterAcceptWhatsAppCallTopic(topic string) error DeregisterAcceptWhatsAppCallTopic(topic string) + RegisterConnectTwilioCallTopic(topic string) error + DeregisterConnectTwilioCallTopic(topic string) // Close and wait for pending RPCs to complete Shutdown() @@ -74,6 +81,7 @@ func NewConnectorInternalClient(bus psrpc.MessageBus, opts ...psrpc.ClientOption sd.RegisterMethod("DialWhatsAppCall", false, false, true, true) sd.RegisterMethod("AcceptWhatsAppCall", false, false, true, true) + sd.RegisterMethod("ConnectTwilioCall", false, false, true, true) rpcClient, err := client.NewRPCClient(sd, bus, opts...) if err != nil { @@ -93,6 +101,10 @@ func (c *connectorInternalClient) AcceptWhatsAppCall(ctx context.Context, topic return client.RequestSingle[*livekit10.AcceptWhatsAppCallResponse](ctx, c.client, "AcceptWhatsAppCall", []string{topic}, req, opts...) } +func (c *connectorInternalClient) ConnectTwilioCall(ctx context.Context, topic string, req *InternalConnectTwilioCallRequest, opts ...psrpc.RequestOption) (*livekit11.ConnectTwilioCallResponse, error) { + return client.RequestSingle[*livekit11.ConnectTwilioCallResponse](ctx, c.client, "ConnectTwilioCall", []string{topic}, req, opts...) +} + func (s *connectorInternalClient) Close() { s.client.Close() } @@ -118,6 +130,7 @@ func NewConnectorInternalServer(svc ConnectorInternalServerImpl, bus psrpc.Messa sd.RegisterMethod("DialWhatsAppCall", false, false, true, true) sd.RegisterMethod("AcceptWhatsAppCall", false, false, true, true) + sd.RegisterMethod("ConnectTwilioCall", false, false, true, true) return &connectorInternalServer{ svc: svc, rpc: s, @@ -140,6 +153,14 @@ func (s *connectorInternalServer) DeregisterAcceptWhatsAppCallTopic(topic string s.rpc.DeregisterHandler("AcceptWhatsAppCall", []string{topic}) } +func (s *connectorInternalServer) RegisterConnectTwilioCallTopic(topic string) error { + return server.RegisterHandler(s.rpc, "ConnectTwilioCall", []string{topic}, s.svc.ConnectTwilioCall, nil) +} + +func (s *connectorInternalServer) DeregisterConnectTwilioCallTopic(topic string) { + s.rpc.DeregisterHandler("ConnectTwilioCall", []string{topic}) +} + func (s *connectorInternalServer) Shutdown() { s.rpc.Close(false) } @@ -162,6 +183,10 @@ func (UnimplementedConnectorInternalServer) AcceptWhatsAppCall(context.Context, return nil, psrpc.ErrUnimplemented } +func (UnimplementedConnectorInternalServer) ConnectTwilioCall(context.Context, *InternalConnectTwilioCallRequest) (*livekit11.ConnectTwilioCallResponse, error) { + return nil, psrpc.ErrUnimplemented +} + // ================================= // ConnectorHandler Client Interface // ================================= @@ -308,30 +333,33 @@ func (UnimplementedConnectorHandlerServer) DisconnectWhatsAppCall(context.Contex } var psrpcFileDescriptor12 = []byte{ - // 391 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x93, 0x3f, 0x6f, 0xda, 0x40, - 0x18, 0xc6, 0x75, 0x54, 0xa2, 0xd2, 0xf5, 0x8f, 0xdc, 0x43, 0x42, 0xd4, 0x0b, 0xe0, 0x22, 0xda, - 0xc9, 0x96, 0xe8, 0x56, 0x55, 0xaa, 0x28, 0x0c, 0xa5, 0xa3, 0x97, 0x48, 0x59, 0x2c, 0x73, 0x1c, - 0xe1, 0x92, 0xf3, 0xbd, 0x97, 0xf3, 0x91, 0x2c, 0x59, 0x33, 0xf0, 0x0d, 0x32, 0x65, 0xc8, 0x47, - 0xe0, 0x2b, 0xe5, 0x8b, 0x44, 0xc1, 0x3e, 0x82, 0x89, 0x0d, 0x4b, 0x36, 0xdf, 0xeb, 0xe7, 0x7d, - 0x9e, 0xdf, 0x3d, 0xb2, 0x71, 0x43, 0x2b, 0x1a, 0x50, 0x90, 0x92, 0x51, 0x03, 0xda, 0x57, 0x1a, - 0x0c, 0x90, 0x77, 0x5a, 0x51, 0xf7, 0x13, 0x28, 0xc3, 0x41, 0xa6, 0xd9, 0xcc, 0xed, 0x08, 0x7e, - 0xc5, 0x2e, 0xb8, 0x89, 0xb6, 0xe2, 0xe8, 0x7a, 0x11, 0x9b, 0x34, 0x56, 0x2a, 0x57, 0x38, 0x99, - 0x55, 0x92, 0x80, 0xcc, 0x26, 0xde, 0x3d, 0xc2, 0xed, 0x89, 0x34, 0x4c, 0xcb, 0x58, 0x8c, 0x79, - 0x2c, 0x4e, 0x9e, 0x17, 0x86, 0x4a, 0x8d, 0x62, 0x21, 0x42, 0x76, 0xb9, 0x64, 0xa9, 0x21, 0xbf, - 0xf0, 0x7b, 0x9d, 0x3d, 0xb6, 0x50, 0x07, 0xfd, 0xf8, 0x30, 0xe8, 0xf8, 0x79, 0x92, 0x5f, 0xb1, - 0x12, 0xda, 0x05, 0xf2, 0x07, 0x7f, 0xd6, 0x00, 0x49, 0x74, 0x0e, 0x5c, 0x46, 0x5c, 0xce, 0xa1, - 0x55, 0xdb, 0x58, 0x7c, 0xf5, 0xb5, 0xa2, 0xbe, 0x4d, 0x0e, 0x01, 0x92, 0xff, 0xc0, 0xe5, 0x44, - 0xce, 0x21, 0xfc, 0xa8, 0x77, 0x4e, 0xde, 0x03, 0xc2, 0x5d, 0x2b, 0x1b, 0x52, 0xca, 0x94, 0x29, - 0x43, 0xfc, 0xbd, 0x8f, 0xe8, 0x6d, 0x11, 0x2b, 0x97, 0xde, 0x0e, 0x72, 0xf0, 0x88, 0xf0, 0x97, - 0x91, 0x2d, 0xdd, 0xea, 0x09, 0xc5, 0xce, 0x7e, 0x3f, 0xa4, 0x57, 0xb0, 0xac, 0xa8, 0xcf, 0xed, - 0x1e, 0x28, 0x38, 0x55, 0x20, 0x53, 0xe6, 0xd5, 0xd7, 0x2b, 0x54, 0x73, 0x10, 0xe1, 0x98, 0xbc, - 0xbe, 0x21, 0xe9, 0x17, 0x62, 0x2a, 0x2b, 0x70, 0xbf, 0x1d, 0xac, 0xa9, 0x18, 0x35, 0xb8, 0xab, - 0x61, 0x67, 0x7b, 0xcb, 0x7f, 0xb1, 0x9c, 0x09, 0xa6, 0xc9, 0x0d, 0x6e, 0xe4, 0xb3, 0x02, 0xc0, - 0x8b, 0x71, 0xc9, 0x5b, 0x9b, 0xde, 0x3b, 0x2c, 0xca, 0xe3, 0xdd, 0xf5, 0x0a, 0x35, 0x1d, 0xe4, - 0x12, 0xe2, 0xd8, 0xcf, 0x39, 0xa2, 0xb1, 0x10, 0x11, 0x9f, 0x91, 0x5b, 0x84, 0x9b, 0x63, 0x9e, - 0xd2, 0x12, 0x82, 0xfe, 0x4e, 0x87, 0x65, 0x02, 0x0b, 0xf1, 0xfd, 0xa8, 0xee, 0x38, 0xc7, 0xdf, - 0xee, 0x69, 0xfb, 0x8c, 0x9b, 0xc5, 0x72, 0xea, 0x53, 0x48, 0x82, 0xdc, 0x30, 0xd8, 0xfc, 0x62, - 0x14, 0x44, 0xa0, 0x15, 0x9d, 0xd6, 0x37, 0xa7, 0x9f, 0x4f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x5c, - 0xdc, 0xdc, 0x82, 0xcf, 0x03, 0x00, 0x00, + // 444 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0x4f, 0x8f, 0x94, 0x30, + 0x18, 0xc6, 0x53, 0x4c, 0xd6, 0xa4, 0xfe, 0x09, 0x76, 0x93, 0xcd, 0xca, 0xc1, 0x05, 0x76, 0x5d, + 0x3d, 0x41, 0x32, 0xde, 0xd4, 0xc4, 0xac, 0xb3, 0x07, 0xd7, 0x23, 0x31, 0x31, 0xf1, 0x42, 0xd8, + 0x6e, 0xc7, 0xa9, 0x96, 0xbe, 0xb5, 0x74, 0x9c, 0x8b, 0x57, 0x0f, 0xf3, 0x0d, 0x3c, 0x19, 0xe3, + 0x47, 0x98, 0xaf, 0xe1, 0x97, 0x32, 0x02, 0xc5, 0x81, 0x01, 0xe6, 0x32, 0x37, 0x5a, 0x9e, 0xf7, + 0x7d, 0x7e, 0x7d, 0x9f, 0x02, 0x3e, 0xd4, 0x8a, 0xc6, 0x14, 0xa4, 0x64, 0xd4, 0x80, 0x8e, 0x94, + 0x06, 0x03, 0xe4, 0x96, 0x56, 0xd4, 0xbb, 0x07, 0xca, 0x70, 0x90, 0x45, 0xb5, 0xe7, 0xf9, 0x82, + 0x7f, 0x65, 0x9f, 0xb9, 0x49, 0x1b, 0x71, 0xba, 0x9c, 0x67, 0xa6, 0xc8, 0x94, 0xaa, 0x15, 0x8f, + 0xb6, 0x15, 0x66, 0xc9, 0x05, 0x87, 0xfa, 0xbd, 0x5b, 0x59, 0xe5, 0x39, 0xc8, 0x6a, 0x27, 0xfc, + 0x89, 0xf0, 0xc9, 0x95, 0x34, 0x4c, 0xcb, 0x4c, 0x5c, 0xf2, 0x4c, 0xbc, 0xff, 0xd7, 0xf0, 0x42, + 0xa9, 0x69, 0x26, 0x44, 0xc2, 0xbe, 0x2c, 0x58, 0x61, 0xc8, 0x73, 0x7c, 0x5b, 0x57, 0x8f, 0xc7, + 0xc8, 0x47, 0x4f, 0xef, 0x4c, 0xfc, 0xa8, 0xf6, 0x89, 0x06, 0x4a, 0x12, 0x5b, 0x40, 0x5e, 0xe1, + 0xfb, 0x1a, 0x20, 0x4f, 0x3f, 0x01, 0x97, 0x29, 0x97, 0x33, 0x38, 0x76, 0xca, 0x16, 0x0f, 0x23, + 0xad, 0x68, 0x64, 0x9d, 0x13, 0x80, 0xfc, 0x2d, 0x70, 0x79, 0x25, 0x67, 0x90, 0xdc, 0xd5, 0x1b, + 0xab, 0xf0, 0x37, 0xc2, 0x81, 0x95, 0x5d, 0x50, 0xca, 0x94, 0xe9, 0x43, 0x7c, 0xd9, 0x45, 0x0c, + 0x1b, 0xc4, 0xc1, 0xa2, 0x3d, 0x42, 0xfe, 0x42, 0xd8, 0xb7, 0xb2, 0x69, 0x35, 0xfa, 0x77, 0xe5, + 0xdc, 0x37, 0x19, 0x5f, 0x74, 0x19, 0x83, 0x86, 0x71, 0xa8, 0x66, 0x7f, 0x88, 0x93, 0x3f, 0x0e, + 0x7e, 0x30, 0xb5, 0xb7, 0xc2, 0xea, 0x09, 0xc5, 0x6e, 0x37, 0x42, 0x72, 0xd6, 0x6a, 0x39, 0x90, + 0xb0, 0x17, 0x8c, 0xdc, 0x81, 0x42, 0x81, 0x2c, 0x58, 0x78, 0xb0, 0x5e, 0x21, 0xc7, 0x45, 0x84, + 0x63, 0xb2, 0x1d, 0x02, 0x39, 0x6f, 0xd9, 0x0c, 0xa6, 0xe4, 0x9d, 0x8e, 0x26, 0xd9, 0xb1, 0x9a, + 0x35, 0x87, 0xfc, 0x3f, 0x4b, 0xf2, 0xb8, 0xe5, 0x34, 0x34, 0x6b, 0x2f, 0x1c, 0x8b, 0xa3, 0xed, + 0x33, 0xf9, 0xe1, 0x60, 0xb7, 0x99, 0xe6, 0x9b, 0x4c, 0xde, 0x08, 0xa6, 0xc9, 0x37, 0x7c, 0x58, + 0xef, 0xb5, 0x0e, 0x7a, 0xda, 0xed, 0xdb, 0x77, 0xca, 0xb3, 0x71, 0x51, 0x6d, 0xef, 0xad, 0x57, + 0xe8, 0xc8, 0x45, 0x1e, 0x21, 0xae, 0xfd, 0xf2, 0x53, 0x9a, 0x09, 0x91, 0xf2, 0x1b, 0xf2, 0x1d, + 0xe1, 0xa3, 0x4b, 0x5e, 0xd0, 0x1e, 0x82, 0xf3, 0x8d, 0xac, 0xfa, 0x04, 0x16, 0xe2, 0xc9, 0x4e, + 0xdd, 0x6e, 0x8e, 0xd7, 0xc1, 0x87, 0x93, 0x8f, 0xdc, 0xcc, 0x17, 0xd7, 0x11, 0x85, 0x3c, 0xae, + 0x1b, 0xc6, 0xe5, 0xdf, 0x86, 0x82, 0x88, 0xb5, 0xa2, 0xd7, 0x07, 0xe5, 0xea, 0xd9, 0xdf, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x7b, 0xb1, 0x7d, 0x3e, 0xfa, 0x04, 0x00, 0x00, }