package v2 import ( "context" "time" pb "github.com/hiddify/hiddify-core/hiddifyrpc" "github.com/sagernet/sing-box/experimental/libbox" "google.golang.org/grpc" ) var ( systemInfoObserver = NewObserver[pb.SystemInfo](10) outboundsInfoObserver = NewObserver[pb.OutboundGroupList](10) mainOutboundsInfoObserver = NewObserver[pb.OutboundGroupList](10) ) var ( statusClient *libbox.CommandClient groupClient *libbox.CommandClient groupInfoOnlyClient *libbox.CommandClient ) func (s *CoreService) GetSystemInfo(req *pb.Empty, stream grpc.ServerStreamingServer[pb.SystemInfo]) error { if statusClient == nil { statusClient = libbox.NewCommandClient( &CommandClientHandler{}, &libbox.CommandClientOptions{ Command: libbox.CommandStatus, StatusInterval: 1000000000, // 1000ms debounce }, ) defer func() { statusClient.Disconnect() statusClient = nil }() statusClient.Connect() } sub, done, _ := systemInfoObserver.Subscribe() for { select { case <-stream.Context().Done(): return nil case <-done: return nil case info := <-sub: stream.Send(&info) case <-time.After(1000 * time.Millisecond): } } } func (s *CoreService) OutboundsInfo(req *pb.Empty, stream grpc.ServerStreamingServer[pb.OutboundGroupList]) error { if groupClient == nil { groupClient = libbox.NewCommandClient( &CommandClientHandler{}, &libbox.CommandClientOptions{ Command: libbox.CommandGroup, StatusInterval: 500000000, // 500ms debounce }, ) defer func() { groupClient.Disconnect() groupClient = nil }() groupClient.Connect() } sub, done, _ := outboundsInfoObserver.Subscribe() for { select { case <-stream.Context().Done(): return nil case <-done: return nil case info := <-sub: stream.Send(&info) case <-time.After(500 * time.Millisecond): } } } func (s *CoreService) MainOutboundsInfo(req *pb.Empty, stream grpc.ServerStreamingServer[pb.OutboundGroupList]) error { if groupInfoOnlyClient == nil { groupInfoOnlyClient = libbox.NewCommandClient( &CommandClientHandler{}, &libbox.CommandClientOptions{ Command: libbox.CommandGroupInfoOnly, StatusInterval: 500000000, // 500ms debounce }, ) defer func() { groupInfoOnlyClient.Disconnect() groupInfoOnlyClient = nil }() groupInfoOnlyClient.Connect() } sub, stopch, _ := mainOutboundsInfoObserver.Subscribe() for { select { case <-stream.Context().Done(): return nil case <-stopch: return nil case info := <-sub: stream.Send(&info) case <-time.After(500 * time.Millisecond): } } } func (s *CoreService) SelectOutbound(ctx context.Context, in *pb.SelectOutboundRequest) (*pb.Response, error) { return SelectOutbound(in) } func SelectOutbound(in *pb.SelectOutboundRequest) (*pb.Response, error) { err := libbox.NewStandaloneCommandClient().SelectOutbound(in.GroupTag, in.OutboundTag) if err != nil { return &pb.Response{ ResponseCode: pb.ResponseCode_FAILED, Message: err.Error(), }, err } return &pb.Response{ ResponseCode: pb.ResponseCode_OK, Message: "", }, nil } func (s *CoreService) UrlTest(ctx context.Context, in *pb.UrlTestRequest) (*pb.Response, error) { return UrlTest(in) } func UrlTest(in *pb.UrlTestRequest) (*pb.Response, error) { err := libbox.NewStandaloneCommandClient().URLTest(in.GroupTag) if err != nil { return &pb.Response{ ResponseCode: pb.ResponseCode_FAILED, Message: err.Error(), }, err } return &pb.Response{ ResponseCode: pb.ResponseCode_OK, Message: "", }, nil }