2023-09-01 15:00:41 +03:30
|
|
|
import 'dart:convert';
|
|
|
|
|
|
2023-08-19 22:27:23 +03:30
|
|
|
import 'package:flutter/services.dart';
|
|
|
|
|
import 'package:fpdart/fpdart.dart';
|
2023-09-10 20:25:04 +03:30
|
|
|
import 'package:hiddify/domain/connectivity/connection_status.dart';
|
2023-09-01 15:00:41 +03:30
|
|
|
import 'package:hiddify/domain/singbox/config_options.dart';
|
2023-09-10 20:25:04 +03:30
|
|
|
import 'package:hiddify/services/singbox/shared.dart';
|
2023-08-19 22:27:23 +03:30
|
|
|
import 'package:hiddify/services/singbox/singbox_service.dart';
|
|
|
|
|
import 'package:hiddify/utils/utils.dart';
|
2023-09-10 20:25:04 +03:30
|
|
|
import 'package:rxdart/rxdart.dart';
|
2023-08-19 22:27:23 +03:30
|
|
|
|
2023-09-10 20:25:04 +03:30
|
|
|
class MobileSingboxService
|
|
|
|
|
with ServiceStatus, InfraLogger
|
|
|
|
|
implements SingboxService {
|
|
|
|
|
late final _methodChannel = const MethodChannel("com.hiddify.app/method");
|
|
|
|
|
late final _statusChannel =
|
|
|
|
|
const EventChannel("com.hiddify.app/service.status");
|
|
|
|
|
late final _alertsChannel =
|
|
|
|
|
const EventChannel("com.hiddify.app/service.alerts");
|
|
|
|
|
late final _logsChannel = const EventChannel("com.hiddify.app/service.logs");
|
|
|
|
|
|
|
|
|
|
late final ValueStream<ConnectionStatus> _connectionStatus;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<void> init() async {
|
|
|
|
|
loggy.debug("initializing");
|
|
|
|
|
final status =
|
|
|
|
|
_statusChannel.receiveBroadcastStream().map(mapEventToStatus);
|
|
|
|
|
final alerts =
|
|
|
|
|
_alertsChannel.receiveBroadcastStream().map(mapEventToStatus);
|
|
|
|
|
_connectionStatus =
|
|
|
|
|
ValueConnectableStream(Rx.merge([status, alerts])).autoConnect();
|
|
|
|
|
await _connectionStatus.first;
|
|
|
|
|
}
|
2023-08-19 22:27:23 +03:30
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
TaskEither<String, Unit> setup(
|
|
|
|
|
String baseDir,
|
|
|
|
|
String workingDir,
|
|
|
|
|
String tempDir,
|
|
|
|
|
) =>
|
|
|
|
|
TaskEither.of(unit);
|
|
|
|
|
|
|
|
|
|
@override
|
2023-09-22 23:52:20 +03:30
|
|
|
TaskEither<String, Unit> parseConfig(
|
|
|
|
|
String path,
|
|
|
|
|
String tempPath,
|
|
|
|
|
bool debug,
|
|
|
|
|
) {
|
2023-08-19 22:27:23 +03:30
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
|
|
|
|
final message = await _methodChannel.invokeMethod<String>(
|
|
|
|
|
"parse_config",
|
2023-09-22 23:52:20 +03:30
|
|
|
{"path": path, "tempPath": tempPath, "debug": debug},
|
2023-08-19 22:27:23 +03:30
|
|
|
);
|
|
|
|
|
if (message == null || message.isEmpty) return right(unit);
|
|
|
|
|
return left(message);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-01 15:00:41 +03:30
|
|
|
@override
|
|
|
|
|
TaskEither<String, Unit> changeConfigOptions(ConfigOptions options) {
|
|
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
|
|
|
|
await _methodChannel.invokeMethod(
|
|
|
|
|
"change_config_options",
|
|
|
|
|
jsonEncode(options.toJson()),
|
|
|
|
|
);
|
|
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-19 22:27:23 +03:30
|
|
|
@override
|
2023-09-10 20:25:04 +03:30
|
|
|
TaskEither<String, Unit> start(String configPath) {
|
2023-08-19 22:27:23 +03:30
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
2023-09-10 20:25:04 +03:30
|
|
|
loggy.debug("starting");
|
2023-08-19 22:27:23 +03:30
|
|
|
await _methodChannel.invokeMethod(
|
2023-09-10 20:25:04 +03:30
|
|
|
"start",
|
2023-08-19 22:27:23 +03:30
|
|
|
{"path": configPath},
|
|
|
|
|
);
|
|
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
2023-09-10 20:25:04 +03:30
|
|
|
TaskEither<String, Unit> stop() {
|
2023-08-19 22:27:23 +03:30
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
2023-09-10 20:25:04 +03:30
|
|
|
loggy.debug("stopping");
|
|
|
|
|
await _methodChannel.invokeMethod("stop");
|
2023-08-19 22:27:23 +03:30
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
2023-09-10 20:25:04 +03:30
|
|
|
TaskEither<String, Unit> restart(String configPath) {
|
2023-08-19 22:27:23 +03:30
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
2023-09-10 20:25:04 +03:30
|
|
|
loggy.debug("restarting");
|
|
|
|
|
await _methodChannel.invokeMethod(
|
|
|
|
|
"restart",
|
|
|
|
|
{"path": configPath},
|
|
|
|
|
);
|
2023-08-19 22:27:23 +03:30
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-29 19:32:31 +03:30
|
|
|
@override
|
|
|
|
|
Stream<String> watchOutbounds() {
|
|
|
|
|
const channel = EventChannel("com.hiddify.app/groups");
|
|
|
|
|
loggy.debug("watching outbounds");
|
|
|
|
|
return channel.receiveBroadcastStream().map(
|
|
|
|
|
(event) {
|
|
|
|
|
if (event case String _) {
|
|
|
|
|
return event;
|
|
|
|
|
}
|
2023-10-05 22:47:24 +03:30
|
|
|
loggy.error("[group client] unexpected type, msg: $event");
|
2023-08-29 19:32:31 +03:30
|
|
|
throw "invalid type";
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-28 13:15:57 +03:30
|
|
|
@override
|
2023-09-10 20:25:04 +03:30
|
|
|
Stream<ConnectionStatus> watchConnectionStatus() => _connectionStatus;
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Stream<String> watchStats() {
|
2023-08-28 13:15:57 +03:30
|
|
|
// TODO: implement watchStatus
|
|
|
|
|
return const Stream.empty();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-29 19:32:31 +03:30
|
|
|
@override
|
|
|
|
|
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag) {
|
|
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
|
|
|
|
loggy.debug("selecting outbound");
|
|
|
|
|
await _methodChannel.invokeMethod(
|
|
|
|
|
"select_outbound",
|
|
|
|
|
{"groupTag": groupTag, "outboundTag": outboundTag},
|
|
|
|
|
);
|
|
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
TaskEither<String, Unit> urlTest(String groupTag) {
|
|
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
|
|
|
|
await _methodChannel.invokeMethod(
|
|
|
|
|
"url_test",
|
|
|
|
|
{"groupTag": groupTag},
|
|
|
|
|
);
|
|
|
|
|
return right(unit);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-19 22:27:23 +03:30
|
|
|
@override
|
2023-10-24 11:26:57 +02:00
|
|
|
Stream<List<String>> watchLogs(String path) async* {
|
|
|
|
|
yield* _logsChannel
|
|
|
|
|
.receiveBroadcastStream()
|
|
|
|
|
.map((event) => (event as List).map((e) => e as String).toList());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
TaskEither<String, Unit> clearLogs() {
|
|
|
|
|
return TaskEither(
|
|
|
|
|
() async {
|
|
|
|
|
await _methodChannel.invokeMethod("clear_logs");
|
|
|
|
|
return right(unit);
|
2023-08-19 22:27:23 +03:30
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|