fix 一键组合只上传邮箱列表,服务端再从数据库加载完整账号
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
setlocal
|
||||
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%~dp0Install-Clean.ps1"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Installation failed. Read the message above, then try again.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo.
|
||||
echo Installation completed successfully.
|
||||
pause
|
||||
@@ -0,0 +1,136 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[switch]$NoLaunch,
|
||||
[switch]$SkipShortcuts,
|
||||
[switch]$SkipProcessCheck,
|
||||
[string]$InstallRoot = '',
|
||||
[string]$RoamingRoot = '',
|
||||
[string]$LocalRoot = ''
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
function Copy-Tree {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string]$Source,
|
||||
[Parameter(Mandatory = $true)][string]$Destination
|
||||
)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Source -PathType Container)) {
|
||||
throw "Required source directory is missing: $Source"
|
||||
}
|
||||
New-Item -ItemType Directory -Path $Destination -Force | Out-Null
|
||||
& robocopy.exe $Source $Destination /E /COPY:DAT /DCOPY:DAT /R:2 /W:1 /XJ /NFL /NDL /NP
|
||||
$code = $LASTEXITCODE
|
||||
if ($code -gt 7) {
|
||||
throw "Copy failed with robocopy exit code $code ($Source)"
|
||||
}
|
||||
}
|
||||
|
||||
function Move-ToBackup {
|
||||
param([Parameter(Mandatory = $true)][string]$Path)
|
||||
|
||||
if (-not (Test-Path -LiteralPath $Path)) {
|
||||
return $null
|
||||
}
|
||||
$parent = Split-Path -Parent $Path
|
||||
$leaf = Split-Path -Leaf $Path
|
||||
$stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
|
||||
$suffix = [Guid]::NewGuid().ToString('N').Substring(0, 6)
|
||||
$backup = Join-Path $parent ($leaf + '.backup-' + $stamp + '-' + $suffix)
|
||||
Move-Item -LiteralPath $Path -Destination $backup
|
||||
return $backup
|
||||
}
|
||||
|
||||
$packageRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||
$sourceApp = Join-Path $packageRoot 'App'
|
||||
$sourceRoaming = Join-Path $packageRoot 'Seed\Roaming\BitBrowser'
|
||||
$sourceLocal = Join-Path $packageRoot 'Seed\Local\BitBrowser'
|
||||
$targetApp = if ($InstallRoot) { [System.IO.Path]::GetFullPath($InstallRoot) } else { Join-Path $env:LOCALAPPDATA 'Programs\BitBrowser' }
|
||||
$targetRoaming = if ($RoamingRoot) { [System.IO.Path]::GetFullPath($RoamingRoot) } else { Join-Path $env:APPDATA 'BitBrowser' }
|
||||
$targetLocal = if ($LocalRoot) { [System.IO.Path]::GetFullPath($LocalRoot) } else { Join-Path $env:LOCALAPPDATA 'BitBrowser' }
|
||||
$targetExe = Join-Path $targetApp ([string][char]0x6BD4 + [char]0x7279 + [char]0x6D4F + [char]0x89C8 + [char]0x5668 + '.exe')
|
||||
$expectedExtensionId = 'jiofmdifioeejeilfkpegipdjiopiekl'
|
||||
$sourceDb = Join-Path $sourceRoaming 'db.sqlite'
|
||||
$sourceExtensionManifest = Join-Path $sourceRoaming "BitExtensions\$expectedExtensionId\manifest.json"
|
||||
|
||||
if (-not $SkipProcessCheck) {
|
||||
$running = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
|
||||
$_.Name -eq ([string][char]0x6BD4 + [char]0x7279 + [char]0x6D4F + [char]0x89C8 + [char]0x5668 + '.exe') -or
|
||||
($_.ExecutablePath -and $_.ExecutablePath.StartsWith($targetApp, [System.StringComparison]::OrdinalIgnoreCase))
|
||||
})
|
||||
if ($running.Count -gt 0) {
|
||||
throw 'BitBrowser is running. Close it and run this installer again.'
|
||||
}
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $sourceDb -PathType Leaf)) {
|
||||
throw 'The clean seed database is missing from this package.'
|
||||
}
|
||||
if (-not (Test-Path -LiteralPath $sourceExtensionManifest -PathType Leaf)) {
|
||||
throw 'The YesCaptcha extension is missing from this package.'
|
||||
}
|
||||
|
||||
Write-Host 'Installing BitBrowser application files...'
|
||||
Copy-Tree -Source $sourceApp -Destination $targetApp
|
||||
|
||||
$backups = @()
|
||||
$backup = Move-ToBackup -Path $targetRoaming
|
||||
if ($backup) { $backups += $backup }
|
||||
$backup = Move-ToBackup -Path $targetLocal
|
||||
if ($backup) { $backups += $backup }
|
||||
|
||||
Write-Host 'Installing clean user data and preserved extensions...'
|
||||
Copy-Tree -Source $sourceRoaming -Destination $targetRoaming
|
||||
if (Test-Path -LiteralPath $sourceLocal -PathType Container) {
|
||||
Copy-Tree -Source $sourceLocal -Destination $targetLocal
|
||||
}
|
||||
|
||||
if (-not (Test-Path -LiteralPath $targetExe -PathType Leaf)) {
|
||||
throw "Installed executable was not found: $targetExe"
|
||||
}
|
||||
$targetDb = Join-Path $targetRoaming 'db.sqlite'
|
||||
$targetExtensionManifest = Join-Path $targetRoaming "BitExtensions\$expectedExtensionId\manifest.json"
|
||||
if (-not (Test-Path -LiteralPath $targetExtensionManifest -PathType Leaf)) {
|
||||
throw "YesCaptcha was not installed: $targetExtensionManifest"
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $sourceDb -Algorithm SHA256).Hash -ne (Get-FileHash -LiteralPath $targetDb -Algorithm SHA256).Hash) {
|
||||
throw 'The clean database copy failed verification.'
|
||||
}
|
||||
if ((Get-FileHash -LiteralPath $sourceExtensionManifest -Algorithm SHA256).Hash -ne (Get-FileHash -LiteralPath $targetExtensionManifest -Algorithm SHA256).Hash) {
|
||||
throw 'The YesCaptcha extension copy failed verification.'
|
||||
}
|
||||
|
||||
if (-not $SkipShortcuts) {
|
||||
$shell = New-Object -ComObject WScript.Shell
|
||||
$desktop = [Environment]::GetFolderPath('Desktop')
|
||||
$shortcutPath = Join-Path $desktop 'BitBrowser.lnk'
|
||||
$shortcut = $shell.CreateShortcut($shortcutPath)
|
||||
$shortcut.TargetPath = $targetExe
|
||||
$shortcut.WorkingDirectory = $targetApp
|
||||
$shortcut.IconLocation = $targetExe + ',0'
|
||||
$shortcut.Save()
|
||||
|
||||
$startMenu = Join-Path $env:APPDATA 'Microsoft\Windows\Start Menu\Programs'
|
||||
$startShortcutPath = Join-Path $startMenu 'BitBrowser.lnk'
|
||||
$startShortcut = $shell.CreateShortcut($startShortcutPath)
|
||||
$startShortcut.TargetPath = $targetExe
|
||||
$startShortcut.WorkingDirectory = $targetApp
|
||||
$startShortcut.IconLocation = $targetExe + ',0'
|
||||
$startShortcut.Save()
|
||||
}
|
||||
|
||||
Write-Host ''
|
||||
Write-Host "Installed application: $targetApp"
|
||||
Write-Host "Installed clean data: $targetRoaming"
|
||||
Write-Host "Verified extension: YesCaptcha 1.4.7 ($expectedExtensionId)"
|
||||
if ($backups.Count -gt 0) {
|
||||
Write-Host 'Previous data was preserved at:'
|
||||
foreach ($item in $backups) { Write-Host " $item" }
|
||||
}
|
||||
Write-Host 'No browser profiles, cookies, history, cache, proxy settings, or login token were installed.'
|
||||
|
||||
if (-not $NoLaunch) {
|
||||
Start-Process -FilePath $targetExe -WorkingDirectory $targetApp
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
比特浏览器干净初始化分享包
|
||||
==========================
|
||||
|
||||
使用方法
|
||||
--------
|
||||
1. 解压整个 ZIP,不要只在压缩包里直接运行。
|
||||
2. 先关闭比特浏览器。
|
||||
3. 双击“一键安装_保留YesCaptcha人机助手.cmd”(不要直接运行 App 目录里的 EXE)。
|
||||
4. 首次打开后,使用新机器自己的比特账号登录。
|
||||
|
||||
包内保留
|
||||
--------
|
||||
- 比特浏览器程序本体
|
||||
- Chromium 浏览器内核和 chromedriver
|
||||
- YesCaptcha 验证码助手 1.4.7
|
||||
- 比特的“服务助手(勿关闭)” 2.0.6
|
||||
- 插件和内核必需的本地登记
|
||||
|
||||
已清空/未包含
|
||||
------------
|
||||
- 比特账号登录令牌
|
||||
- 浏览器环境和窗口列表
|
||||
- Cookie、历史记录、缓存、会话和站点存储
|
||||
- 代理设置、日志和任务记录
|
||||
- 插件的登录状态和使用数据
|
||||
|
||||
安全说明
|
||||
--------
|
||||
- 如果目标机器已有比特用户数据,安装脚本会将其改名为 BitBrowser.backup-时间戳-随机码,不会直接删除。
|
||||
- 安装脚本不会复制原机器的 BrowserCache,因此不会携带原有环境资料。
|
||||
- 程序默认安装到 %LOCALAPPDATA%\Programs\BitBrowser。
|
||||
- 干净用户数据安装到 %APPDATA%\BitBrowser。
|
||||
- 已有的外置缓存目录(例如 D:\bitbrowser-cache)不会被删除,新安装也不会引用它。
|
||||
|
||||
首次登录后建议新建一个浏览器环境,确认 YesCaptcha 插件已加载。插件本身的账号需要在新机器重新登录。
|
||||
@@ -0,0 +1,142 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Create a sanitized BitBrowser database while preserving extensions and kernels."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def quote_identifier(value: str) -> str:
|
||||
return '"' + value.replace('"', '""') + '"'
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--source", required=True)
|
||||
parser.add_argument("--output", required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
source_path = Path(args.source).resolve()
|
||||
output_path = Path(args.output).resolve()
|
||||
if source_path == output_path:
|
||||
raise RuntimeError("The output database must differ from the source database")
|
||||
if not source_path.is_file():
|
||||
raise FileNotFoundError(source_path)
|
||||
output_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
if output_path.exists():
|
||||
output_path.unlink()
|
||||
|
||||
source = sqlite3.connect(f"file:{source_path.as_posix()}?mode=ro", uri=True)
|
||||
target = sqlite3.connect(str(output_path))
|
||||
try:
|
||||
source.backup(target)
|
||||
target.execute("PRAGMA foreign_keys = OFF")
|
||||
|
||||
tables = [
|
||||
row[0]
|
||||
for row in target.execute(
|
||||
"SELECT name FROM sqlite_master "
|
||||
"WHERE type='table' AND name NOT LIKE 'sqlite_%' ORDER BY name"
|
||||
)
|
||||
]
|
||||
|
||||
extension_rows = []
|
||||
if "local_extensions" in tables:
|
||||
extension_rows = target.execute(
|
||||
"SELECT name, desc, extension_id, icon, version, is_use "
|
||||
"FROM local_extensions ORDER BY id"
|
||||
).fetchall()
|
||||
|
||||
chrome_rows = []
|
||||
if "chrome_versions" in tables:
|
||||
chrome_rows = target.execute(
|
||||
"SELECT chrome, version FROM chrome_versions ORDER BY id"
|
||||
).fetchall()
|
||||
|
||||
firefox_rows = []
|
||||
if "firefox_versions" in tables:
|
||||
firefox_rows = target.execute(
|
||||
"SELECT firefox, version FROM firefox_versions ORDER BY id"
|
||||
).fetchall()
|
||||
|
||||
for table in tables:
|
||||
target.execute(f"DELETE FROM {quote_identifier(table)}")
|
||||
target.execute("DELETE FROM sqlite_sequence")
|
||||
|
||||
if extension_rows:
|
||||
target.executemany(
|
||||
"INSERT INTO local_extensions "
|
||||
"(name, desc, extension_id, icon, version, is_use, cloud_id, main_user_id) "
|
||||
"VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)",
|
||||
extension_rows,
|
||||
)
|
||||
if chrome_rows:
|
||||
target.executemany(
|
||||
"INSERT INTO chrome_versions (chrome, version) VALUES (?, ?)",
|
||||
chrome_rows,
|
||||
)
|
||||
if firefox_rows:
|
||||
target.executemany(
|
||||
"INSERT INTO firefox_versions (firefox, version) VALUES (?, ?)",
|
||||
firefox_rows,
|
||||
)
|
||||
|
||||
target.commit()
|
||||
target.execute("VACUUM")
|
||||
target.execute("PRAGMA optimize")
|
||||
|
||||
counts = {
|
||||
table: target.execute(
|
||||
f"SELECT COUNT(*) FROM {quote_identifier(table)}"
|
||||
).fetchone()[0]
|
||||
for table in tables
|
||||
}
|
||||
allowed_nonempty = {"local_extensions", "chrome_versions", "firefox_versions"}
|
||||
unexpected = {
|
||||
table: count
|
||||
for table, count in counts.items()
|
||||
if count and table not in allowed_nonempty
|
||||
}
|
||||
if unexpected:
|
||||
raise RuntimeError(f"Sanitization failed; unexpected rows remain: {unexpected}")
|
||||
|
||||
integrity = target.execute("PRAGMA integrity_check").fetchone()[0]
|
||||
if integrity != "ok":
|
||||
raise RuntimeError(f"SQLite integrity check failed: {integrity}")
|
||||
|
||||
report = {
|
||||
"source": str(source_path),
|
||||
"output": str(output_path),
|
||||
"extensions": [
|
||||
{
|
||||
"name": row[0],
|
||||
"extension_id": row[2],
|
||||
"version": row[4],
|
||||
"enabled": bool(row[5]),
|
||||
}
|
||||
for row in extension_rows
|
||||
],
|
||||
"chrome_versions": [
|
||||
{"chrome": row[0], "version": row[1]} for row in chrome_rows
|
||||
],
|
||||
"firefox_versions": [
|
||||
{"firefox": row[0], "version": row[1]} for row in firefox_rows
|
||||
],
|
||||
"table_counts": counts,
|
||||
"integrity_check": integrity,
|
||||
"size_bytes": os.path.getsize(output_path),
|
||||
}
|
||||
print(json.dumps(report, ensure_ascii=False, indent=2))
|
||||
finally:
|
||||
target.close()
|
||||
source.close()
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
setlocal
|
||||
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%~dp0Install-Clean.ps1"
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo Installation failed. Close BitBrowser and run this file again.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
echo.
|
||||
echo BitBrowser and YesCaptcha were installed and verified.
|
||||
pause
|
||||
Reference in New Issue
Block a user