2024-12-03 21:47:12 +08:00
|
|
|
import 'dart:async';
|
2025-01-13 19:08:17 +08:00
|
|
|
import 'dart:convert';
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
import 'package:fl_clash/clash/message.dart';
|
2025-02-09 18:39:38 +08:00
|
|
|
import 'package:fl_clash/common/common.dart';
|
2025-01-13 19:08:17 +08:00
|
|
|
import 'package:fl_clash/enum/enum.dart';
|
2024-12-03 21:47:12 +08:00
|
|
|
import 'package:fl_clash/models/models.dart';
|
|
|
|
|
|
|
|
|
|
mixin ClashInterface {
|
2025-04-09 16:46:14 +08:00
|
|
|
Future<bool> init(InitParams params);
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<bool> preload();
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<bool> shutdown();
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<bool> get isInit;
|
|
|
|
|
|
|
|
|
|
Future<bool> forceGc();
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
FutureOr<String> validateConfig(String data);
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<String> asyncTestDelay(String url, String proxyName);
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
FutureOr<String> updateConfig(UpdateConfigParams updateConfigParams);
|
|
|
|
|
|
|
|
|
|
FutureOr<String> getProxies();
|
|
|
|
|
|
|
|
|
|
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams);
|
|
|
|
|
|
|
|
|
|
Future<bool> startListener();
|
|
|
|
|
|
|
|
|
|
Future<bool> stopListener();
|
|
|
|
|
|
|
|
|
|
FutureOr<String> getExternalProviders();
|
|
|
|
|
|
|
|
|
|
FutureOr<String>? getExternalProvider(String externalProviderName);
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
Future<String> updateGeoData(UpdateGeoDataParams params);
|
2024-12-03 21:47:12 +08:00
|
|
|
|
|
|
|
|
Future<String> sideLoadExternalProvider({
|
|
|
|
|
required String providerName,
|
|
|
|
|
required String data,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Future<String> updateExternalProvider(String providerName);
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
FutureOr<String> getTraffic();
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
FutureOr<String> getTotalTraffic();
|
2024-12-03 21:47:12 +08:00
|
|
|
|
2024-12-09 01:40:39 +08:00
|
|
|
FutureOr<String> getCountryCode(String ip);
|
|
|
|
|
|
|
|
|
|
FutureOr<String> getMemory();
|
|
|
|
|
|
2024-12-03 21:47:12 +08:00
|
|
|
resetTraffic();
|
|
|
|
|
|
|
|
|
|
startLog();
|
|
|
|
|
|
|
|
|
|
stopLog();
|
|
|
|
|
|
|
|
|
|
FutureOr<String> getConnections();
|
|
|
|
|
|
|
|
|
|
FutureOr<bool> closeConnection(String id);
|
|
|
|
|
|
|
|
|
|
FutureOr<bool> closeConnections();
|
2025-02-09 18:39:38 +08:00
|
|
|
|
|
|
|
|
FutureOr<String> getProfile(String id);
|
|
|
|
|
|
|
|
|
|
Future<bool> setState(CoreState state);
|
2024-12-03 21:47:12 +08:00
|
|
|
}
|
2025-01-13 19:08:17 +08:00
|
|
|
|
|
|
|
|
mixin AndroidClashInterface {
|
|
|
|
|
Future<bool> setFdMap(int fd);
|
|
|
|
|
|
|
|
|
|
Future<bool> setProcessMap(ProcessMapItem item);
|
|
|
|
|
|
2025-04-09 16:46:14 +08:00
|
|
|
// Future<bool> stopTun();
|
2025-01-13 19:08:17 +08:00
|
|
|
|
|
|
|
|
Future<bool> updateDns(String value);
|
|
|
|
|
|
|
|
|
|
Future<AndroidVpnOptions?> getAndroidVpnOptions();
|
|
|
|
|
|
|
|
|
|
Future<String> getCurrentProfileName();
|
|
|
|
|
|
|
|
|
|
Future<DateTime?> getRunTime();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
abstract class ClashHandlerInterface with ClashInterface {
|
|
|
|
|
Map<String, Completer> callbackCompleterMap = {};
|
|
|
|
|
|
|
|
|
|
Future<bool> nextHandleResult(ActionResult result, Completer? completer) =>
|
|
|
|
|
Future.value(false);
|
|
|
|
|
|
|
|
|
|
handleResult(ActionResult result) async {
|
|
|
|
|
final completer = callbackCompleterMap[result.id];
|
|
|
|
|
try {
|
|
|
|
|
switch (result.method) {
|
|
|
|
|
case ActionMethod.initClash:
|
|
|
|
|
case ActionMethod.shutdown:
|
|
|
|
|
case ActionMethod.getIsInit:
|
|
|
|
|
case ActionMethod.startListener:
|
|
|
|
|
case ActionMethod.resetTraffic:
|
|
|
|
|
case ActionMethod.closeConnections:
|
|
|
|
|
case ActionMethod.closeConnection:
|
|
|
|
|
case ActionMethod.stopListener:
|
2025-02-09 18:39:38 +08:00
|
|
|
case ActionMethod.setState:
|
2025-01-13 19:08:17 +08:00
|
|
|
completer?.complete(result.data as bool);
|
|
|
|
|
return;
|
|
|
|
|
case ActionMethod.changeProxy:
|
|
|
|
|
case ActionMethod.getProxies:
|
|
|
|
|
case ActionMethod.getTraffic:
|
|
|
|
|
case ActionMethod.getTotalTraffic:
|
|
|
|
|
case ActionMethod.asyncTestDelay:
|
|
|
|
|
case ActionMethod.getConnections:
|
|
|
|
|
case ActionMethod.getExternalProviders:
|
|
|
|
|
case ActionMethod.getExternalProvider:
|
|
|
|
|
case ActionMethod.validateConfig:
|
|
|
|
|
case ActionMethod.updateConfig:
|
|
|
|
|
case ActionMethod.updateGeoData:
|
|
|
|
|
case ActionMethod.updateExternalProvider:
|
|
|
|
|
case ActionMethod.sideLoadExternalProvider:
|
|
|
|
|
case ActionMethod.getCountryCode:
|
|
|
|
|
case ActionMethod.getMemory:
|
|
|
|
|
completer?.complete(result.data as String);
|
|
|
|
|
return;
|
|
|
|
|
case ActionMethod.message:
|
|
|
|
|
clashMessage.controller.add(result.data as String);
|
|
|
|
|
completer?.complete(true);
|
|
|
|
|
return;
|
|
|
|
|
default:
|
|
|
|
|
final isHandled = await nextHandleResult(result, completer);
|
|
|
|
|
if (isHandled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
completer?.complete(result.data);
|
|
|
|
|
}
|
|
|
|
|
} catch (_) {
|
2025-02-09 18:39:38 +08:00
|
|
|
commonPrint.log(result.id);
|
2025-01-13 19:08:17 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendMessage(String message);
|
|
|
|
|
|
|
|
|
|
reStart();
|
|
|
|
|
|
|
|
|
|
FutureOr<bool> destroy();
|
|
|
|
|
|
|
|
|
|
Future<T> invoke<T>({
|
|
|
|
|
required ActionMethod method,
|
|
|
|
|
dynamic data,
|
|
|
|
|
Duration? timeout,
|
|
|
|
|
FutureOr<T> Function()? onTimeout,
|
|
|
|
|
}) async {
|
2025-04-09 16:46:14 +08:00
|
|
|
final id = "${method.name}#${utils.id}";
|
2025-01-13 19:08:17 +08:00
|
|
|
|
|
|
|
|
callbackCompleterMap[id] = Completer<T>();
|
|
|
|
|
|
|
|
|
|
dynamic defaultValue;
|
|
|
|
|
|
|
|
|
|
if (T == String) {
|
|
|
|
|
defaultValue = "";
|
|
|
|
|
}
|
|
|
|
|
if (T == bool) {
|
|
|
|
|
defaultValue = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sendMessage(
|
|
|
|
|
json.encode(
|
|
|
|
|
Action(
|
|
|
|
|
id: id,
|
|
|
|
|
method: method,
|
|
|
|
|
data: data,
|
|
|
|
|
defaultValue: defaultValue,
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (callbackCompleterMap[id] as Completer<T>).safeFuture(
|
|
|
|
|
timeout: timeout,
|
|
|
|
|
onLast: () {
|
|
|
|
|
callbackCompleterMap.remove(id);
|
|
|
|
|
},
|
|
|
|
|
onTimeout: onTimeout ??
|
|
|
|
|
() {
|
|
|
|
|
return defaultValue;
|
|
|
|
|
},
|
|
|
|
|
functionName: id,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
2025-04-09 16:46:14 +08:00
|
|
|
Future<bool> init(InitParams params) {
|
2025-01-13 19:08:17 +08:00
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.initClash,
|
2025-04-09 16:46:14 +08:00
|
|
|
data: json.encode(params),
|
2025-01-13 19:08:17 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-09 18:39:38 +08:00
|
|
|
@override
|
|
|
|
|
Future<bool> setState(CoreState state) {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.setState,
|
|
|
|
|
data: json.encode(state),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
@override
|
|
|
|
|
shutdown() async {
|
|
|
|
|
return await invoke<bool>(
|
|
|
|
|
method: ActionMethod.shutdown,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<bool> get isInit {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.getIsInit,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<bool> forceGc() {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.forceGc,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> validateConfig(String data) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.validateConfig,
|
|
|
|
|
data: data,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> updateConfig(UpdateConfigParams updateConfigParams) async {
|
|
|
|
|
return await invoke<String>(
|
|
|
|
|
method: ActionMethod.updateConfig,
|
|
|
|
|
data: json.encode(updateConfigParams),
|
2025-02-09 18:39:38 +08:00
|
|
|
timeout: Duration(minutes: 2),
|
2025-01-13 19:08:17 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> getProxies() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getProxies,
|
2025-02-09 18:39:38 +08:00
|
|
|
timeout: Duration(seconds: 5),
|
2025-01-13 19:08:17 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.changeProxy,
|
|
|
|
|
data: json.encode(changeProxyParams),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getExternalProviders() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getExternalProviders,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getExternalProvider(String externalProviderName) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getExternalProvider,
|
|
|
|
|
data: externalProviderName,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> updateGeoData(UpdateGeoDataParams params) {
|
|
|
|
|
return invoke<String>(
|
2025-02-03 23:32:00 +08:00
|
|
|
method: ActionMethod.updateGeoData,
|
|
|
|
|
data: json.encode(params),
|
|
|
|
|
timeout: Duration(minutes: 1));
|
2025-01-13 19:08:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> sideLoadExternalProvider({
|
|
|
|
|
required String providerName,
|
|
|
|
|
required String data,
|
|
|
|
|
}) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.sideLoadExternalProvider,
|
|
|
|
|
data: json.encode({
|
|
|
|
|
"providerName": providerName,
|
|
|
|
|
"data": data,
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> updateExternalProvider(String providerName) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.updateExternalProvider,
|
|
|
|
|
data: providerName,
|
2025-02-03 23:32:00 +08:00
|
|
|
timeout: Duration(minutes: 1),
|
2025-01-13 19:08:17 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getConnections() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getConnections,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<bool> closeConnections() {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.closeConnections,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<bool> closeConnection(String id) {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.closeConnection,
|
|
|
|
|
data: id,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-09 18:39:38 +08:00
|
|
|
@override
|
|
|
|
|
Future<String> getProfile(String id) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getProfile,
|
|
|
|
|
data: id,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-13 19:08:17 +08:00
|
|
|
@override
|
|
|
|
|
FutureOr<String> getTotalTraffic() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getTotalTraffic,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getTraffic() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getTraffic,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
resetTraffic() {
|
|
|
|
|
invoke(method: ActionMethod.resetTraffic);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
startLog() {
|
|
|
|
|
invoke(method: ActionMethod.startLog);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
stopLog() {
|
|
|
|
|
invoke<bool>(
|
|
|
|
|
method: ActionMethod.stopLog,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<bool> startListener() {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.startListener,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
stopListener() {
|
|
|
|
|
return invoke<bool>(
|
|
|
|
|
method: ActionMethod.stopListener,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
Future<String> asyncTestDelay(String url, String proxyName) {
|
|
|
|
|
final delayParams = {
|
|
|
|
|
"proxy-name": proxyName,
|
|
|
|
|
"timeout": httpTimeoutDuration.inMilliseconds,
|
|
|
|
|
"test-url": url,
|
|
|
|
|
};
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.asyncTestDelay,
|
|
|
|
|
data: json.encode(delayParams),
|
|
|
|
|
timeout: Duration(
|
|
|
|
|
milliseconds: 6000,
|
|
|
|
|
),
|
|
|
|
|
onTimeout: () {
|
|
|
|
|
return json.encode(
|
|
|
|
|
Delay(
|
|
|
|
|
name: proxyName,
|
|
|
|
|
value: -1,
|
|
|
|
|
url: url,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getCountryCode(String ip) {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getCountryCode,
|
|
|
|
|
data: ip,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
FutureOr<String> getMemory() {
|
|
|
|
|
return invoke<String>(
|
|
|
|
|
method: ActionMethod.getMemory,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|