fix: windows识别邀请码
This commit is contained in:
parent
d60f1d3c5d
commit
6da7649720
@ -91,6 +91,15 @@ class KRDeviceUtil {
|
|||||||
String? code = _kr_extractCode(executablePath);
|
String? code = _kr_extractCode(executablePath);
|
||||||
if (code != null) return code;
|
if (code != null) return code;
|
||||||
|
|
||||||
|
// 策略 1.5: (Windows 独有) 检测安装目录下的 channel.txt (支持 Inno Setup 方案)
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
code = await _kr_getInviteCodeFromChannelFile(executablePath);
|
||||||
|
if (code != null) {
|
||||||
|
KRLogUtil.kr_i('🎯 从 channel.txt 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Platform.isMacOS) {
|
if (Platform.isMacOS) {
|
||||||
// 策略 2: 如果在 /Volumes 下运行,尝试找 DMG 原文件名
|
// 策略 2: 如果在 /Volumes 下运行,尝试找 DMG 原文件名
|
||||||
if (executablePath.contains('/Volumes/')) {
|
if (executablePath.contains('/Volumes/')) {
|
||||||
@ -107,8 +116,9 @@ class KRDeviceUtil {
|
|||||||
KRLogUtil.kr_i('🎯 从 mdls (文件元数据) 获取到邀请码: $code', tag: 'DeviceUtil');
|
KRLogUtil.kr_i('🎯 从 mdls (文件元数据) 获取到邀请码: $code', tag: 'DeviceUtil');
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 策略 4: 在下载目录下寻找最近的带有 ic- 的 DMG
|
// 策略 4: 在下载目录下寻找最近的带有 ic- 的程序 (MacOS/Windows 通用)
|
||||||
code = await _kr_searchDownloadsForInviteCode();
|
code = await _kr_searchDownloadsForInviteCode();
|
||||||
if (code != null) {
|
if (code != null) {
|
||||||
KRLogUtil.kr_i('🎯 从下载目录搜索获取到邀请码: $code', tag: 'DeviceUtil');
|
KRLogUtil.kr_i('🎯 从下载目录搜索获取到邀请码: $code', tag: 'DeviceUtil');
|
||||||
@ -116,20 +126,21 @@ class KRDeviceUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
KRLogUtil.kr_i('⚠️ 深度检测完成,未发现有效的邀请码标识', tag: 'DeviceUtil');
|
KRLogUtil.kr_i('⚠️ 深度检测完成,未发现有效的邀请码标识', tag: 'DeviceUtil');
|
||||||
}
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
KRLogUtil.kr_e('解析桌面邀请码异常: $e', tag: 'DeviceUtil');
|
KRLogUtil.kr_e('解析桌面邀请码异常: $e', tag: 'DeviceUtil');
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// (MacOS) 在下载目录下搜寻最近的、符合命名规范的 DMG
|
/// (MacOS/Windows) 在下载目录下搜寻最近的、符合命名规范的安装包
|
||||||
Future<String?> _kr_searchDownloadsForInviteCode() async {
|
Future<String?> _kr_searchDownloadsForInviteCode() async {
|
||||||
try {
|
try {
|
||||||
final String home = Platform.environment['HOME'] ?? '';
|
// 适配不同平台的家目录环境变量
|
||||||
|
final String home = Platform.environment['HOME'] ??
|
||||||
|
Platform.environment['USERPROFILE'] ?? '';
|
||||||
if (home.isEmpty) return null;
|
if (home.isEmpty) return null;
|
||||||
|
|
||||||
final Directory downloads = Directory('$home/Downloads');
|
final Directory downloads = Directory('${home}${Platform.pathSeparator}Downloads');
|
||||||
if (!await downloads.exists()) return null;
|
if (!await downloads.exists()) return null;
|
||||||
|
|
||||||
final List<FileSystemEntity> files = await downloads.list().toList();
|
final List<FileSystemEntity> files = await downloads.list().toList();
|
||||||
@ -154,7 +165,31 @@ class KRDeviceUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 正则提取 ic- 模式
|
/// (Windows) 从安装目录下的 channel.txt 中读取邀请码 (针对 Inno Setup 方案)
|
||||||
|
Future<String?> _kr_getInviteCodeFromChannelFile(String execPath) async {
|
||||||
|
try {
|
||||||
|
final File exeFile = File(execPath);
|
||||||
|
final String dirPath = exeFile.parent.path;
|
||||||
|
final File channelFile = File('$dirPath${Platform.pathSeparator}channel.txt');
|
||||||
|
|
||||||
|
if (await channelFile.exists()) {
|
||||||
|
final String content = await channelFile.readAsString();
|
||||||
|
final String channel = content.trim();
|
||||||
|
if (channel.isNotEmpty) {
|
||||||
|
// 兼容两种格式:纯邀请码 或 带 ic- 标识的邀请码
|
||||||
|
if (channel.contains('ic-')) {
|
||||||
|
return _kr_extractCode(channel);
|
||||||
|
}
|
||||||
|
return channel; // 如果 Inno Setup 已经截取好了纯 code,直接返回
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
KRLogUtil.kr_e('读取 Windows channel.txt 失败: $e', tag: 'DeviceUtil');
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 正则提取 ic-模式
|
||||||
String? _kr_extractCode(String source) {
|
String? _kr_extractCode(String source) {
|
||||||
final RegExp regExp = RegExp(r'ic-([A-Za-z0-9-_]+)');
|
final RegExp regExp = RegExp(r'ic-([A-Za-z0-9-_]+)');
|
||||||
final Match? match = regExp.firstMatch(source);
|
final Match? match = regExp.firstMatch(source);
|
||||||
|
|||||||
@ -138,6 +138,35 @@ begin
|
|||||||
Result := True;
|
Result := True;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure CurStepChanged(CurStep: TSetupStep);
|
||||||
|
var
|
||||||
|
FileName: String;
|
||||||
|
ChannelName: String;
|
||||||
|
P: Integer;
|
||||||
|
begin
|
||||||
|
if CurStep = ssPostInstall then
|
||||||
|
begin
|
||||||
|
// 获取当前安装包的文件名 (例如 Setup_ic-888.exe)
|
||||||
|
FileName := ExpandConstant('{srcexe}');
|
||||||
|
FileName := ExtractFileName(FileName);
|
||||||
|
|
||||||
|
// 逻辑:寻找 ic- 标识并提取
|
||||||
|
P := Pos('ic-', FileName);
|
||||||
|
if P > 0 then
|
||||||
|
begin
|
||||||
|
// 提取从 ic- 开始到结尾的部分
|
||||||
|
ChannelName := Copy(FileName, P, Length(FileName) - P + 1);
|
||||||
|
// 去掉 .exe 后缀
|
||||||
|
P := Pos('.exe', LowerCase(ChannelName));
|
||||||
|
if P > 0 then
|
||||||
|
Delete(ChannelName, P, Length(ChannelName));
|
||||||
|
|
||||||
|
// 在安装目录下生成一个 channel.txt,供 Flutter 读取
|
||||||
|
SaveStringToFile(ExpandConstant('{app}\channel.txt'), ChannelName, False);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
function InitializeUninstall(): Boolean;
|
function InitializeUninstall(): Boolean;
|
||||||
begin
|
begin
|
||||||
TerminateProcesses();
|
TerminateProcesses();
|
||||||
|
|||||||
7
邀请文档.md
7
邀请文档.md
@ -23,9 +23,10 @@
|
|||||||
- **识别规范**:文件名或路径中包含 `ic-<code>`,例如 `HiFastVPN-ic-888.dmg`。
|
- **识别规范**:文件名或路径中包含 `ic-<code>`,例如 `HiFastVPN-ic-888.dmg`。
|
||||||
- **解析逻辑(分层检测策略)**:
|
- **解析逻辑(分层检测策略)**:
|
||||||
1. **直接路径匹配 (Win/Mac 通用)**:从当前运行的 `.exe` 或 `.app` 全路径中正则提取。这是最直接、最高效的手段。
|
1. **直接路径匹配 (Win/Mac 通用)**:从当前运行的 `.exe` 或 `.app` 全路径中正则提取。这是最直接、最高效的手段。
|
||||||
2. **挂载源追溯 (macOS 独有)**:通过 `hdiutil info` 关联运行中的 App 及其原始 `.dmg` 文件名。专门解决“程序未改名但在已命名的镜像中运行”的场景。
|
2. **安装渠道配置文件 (Windows 独有)**:读取程序安装目录下的 `channel.txt`。支持 Inno Setup 在安装时通过 `{srcexe}` 获取安装包名并自动生成该文件。这为 Windows 提供了最稳定的安装后识别方案。
|
||||||
3. **下载元数据提取 (macOS 独有)**:通过 `mdls` 读取文件扩展属性。即便安装包已清理,只要系统保留了下载来源记录,仍可识别。
|
3. **挂载源追溯 (macOS 独有)**:通过 `hdiutil info` 关联运行中的 App 及其原始 `.dmg` 文件名。专门解决“程序未改名但在已命名的镜像中运行”的场景。
|
||||||
4. **下载目录智能扫描 (Win/Mac 通用)**:作为最后兜底,扫描用户下载目录(Downloads)下最近修改的、符合命名规范的镜像或安装包(`.dmg`/`.exe`)。
|
4. **下载元数据提取 (macOS 独有)**:通过 `mdls` 读取文件扩展属性。即便安装包已清理,只要系统保留了下载来源记录,仍可识别。
|
||||||
|
5. **下载目录智能扫描 (Win/Mac 通用)**:作为最后兜底,扫描用户下载目录(Downloads)下最近修改的、符合命名规范的镜像或安装包(`.dmg`/`.exe`)。这一策略能有效解决用户下载后虽然安装程序没改名,但原始安装包依然留在下载文件夹中的场景。
|
||||||
- **调试支持**:当 `inviteDebugMode` 开启且识别到邀请码时,桌面端会弹出确认对话框。
|
- **调试支持**:当 `inviteDebugMode` 开启且识别到邀请码时,桌面端会弹出确认对话框。
|
||||||
|
|
||||||
### 网络层标识(silentInvite)
|
### 网络层标识(silentInvite)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user