feat(spoof): пер-аккаунтный спуф с тумблером и прокидыванием в веб-аппы
- спуф теперь свой на каждый аккаунт, вмораживается при входе - тумблер вкл/выкл в настройках (по умолчанию выключен) - новый аккаунт добавляется со своим включённым спуфом (другое устройство) - UA спуфа прокинут в Сферум, Цифровой ID (вебвью + HTTP) - генерация выбирает Android или iOS; iOS отдаёт deviceType IOS - экран спуфа показывает текущую личность, а не свежесгенерированную
This commit is contained in:
@@ -7,6 +7,7 @@ import '../../core/protocol/chat_cache_fingerprint.dart';
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
import '../../core/protocol/packet.dart';
|
||||
import '../../core/storage/app_database.dart';
|
||||
import '../../core/storage/spoofing_service.dart';
|
||||
import '../../core/storage/token_storage.dart';
|
||||
import '../../core/utils/logger.dart';
|
||||
import 'chats.dart';
|
||||
@@ -883,6 +884,7 @@ class AccountModule {
|
||||
if (sessionToken != null && accountId != null) {
|
||||
await TokenStorage.saveToken(sessionToken, accountId);
|
||||
await TokenStorage.setActiveAccount(accountId);
|
||||
await SpoofingService.commitPendingSpoof(accountId);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -938,6 +940,7 @@ class AccountModule {
|
||||
final profile = ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
|
||||
await AppDatabase.saveProfile(profile, isActive: true);
|
||||
await TokenStorage.setActiveAccount(accountId);
|
||||
await SpoofingService.commitPendingSpoof(accountId);
|
||||
|
||||
logger.i('Регистрация завершена, accountId=$accountId');
|
||||
return accountId;
|
||||
@@ -992,6 +995,7 @@ class AccountModule {
|
||||
}
|
||||
await TokenStorage.saveToken(authToken, resolvedAccountId);
|
||||
await TokenStorage.setActiveAccount(resolvedAccountId);
|
||||
await SpoofingService.commitPendingSpoof(resolvedAccountId);
|
||||
}
|
||||
|
||||
final result = await _processLoginResponse(dataMap, resolvedAccountId);
|
||||
@@ -1038,6 +1042,11 @@ class AccountModule {
|
||||
}
|
||||
|
||||
Future<void> beginAddAccount() async {
|
||||
final existing = await AppDatabase.loadAllProfiles();
|
||||
await SpoofingService.prepareNewAccountSpoof(
|
||||
existing.map((p) => p.id).toList(growable: false),
|
||||
);
|
||||
|
||||
try {
|
||||
await _api.disconnect();
|
||||
} catch (_) {}
|
||||
@@ -1088,6 +1097,7 @@ class AccountModule {
|
||||
Future<void> removeAccount(int accountId) async {
|
||||
await AppDatabase.deleteAccount(accountId);
|
||||
await TokenStorage.deleteAccount(accountId);
|
||||
await SpoofingService.clearAccountSpoof(accountId);
|
||||
logger.i('Аккаунт $accountId удалён локально');
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:math';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
|
||||
import '../../core/storage/app_database.dart';
|
||||
import '../../core/storage/spoofing_service.dart';
|
||||
import '../../core/storage/token_storage.dart';
|
||||
import '../../models/digital_id.dart';
|
||||
import 'webapp.dart';
|
||||
@@ -33,6 +34,7 @@ class DigitalIdModule {
|
||||
|
||||
String? _webAppData;
|
||||
String? _deviceId;
|
||||
String? _realUserAgent;
|
||||
|
||||
DigitalIdModule(this._webApp);
|
||||
|
||||
@@ -94,6 +96,33 @@ class DigitalIdModule {
|
||||
return hex;
|
||||
}
|
||||
|
||||
Future<String> _resolveUserAgent() async {
|
||||
final spoofed = await SpoofingService.getWebViewUserAgent();
|
||||
if (spoofed != null && spoofed.isNotEmpty) return spoofed;
|
||||
return _realUserAgent ??= await _buildRealUserAgent();
|
||||
}
|
||||
|
||||
Future<String> _buildRealUserAgent() async {
|
||||
try {
|
||||
final info = DeviceInfoPlugin();
|
||||
if (Platform.isAndroid) {
|
||||
final android = await info.androidInfo;
|
||||
return 'Mozilla/5.0 (Linux; Android ${android.version.release}; '
|
||||
'${android.model}) AppleWebKit/537.36 (KHTML, like Gecko) '
|
||||
'Chrome/124.0.0.0 Mobile Safari/537.36';
|
||||
}
|
||||
if (Platform.isIOS) {
|
||||
final ios = await info.iosInfo;
|
||||
final version = ios.systemVersion.replaceAll('.', '_');
|
||||
return 'Mozilla/5.0 (iPhone; CPU iPhone OS $version like Mac OS X) '
|
||||
'AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 '
|
||||
'Mobile/15E148 Safari/604.1';
|
||||
}
|
||||
} catch (_) {}
|
||||
return 'Mozilla/5.0 (Linux; Android 14; K) AppleWebKit/537.36 '
|
||||
'(KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36';
|
||||
}
|
||||
|
||||
Future<dynamic> _send(
|
||||
String method,
|
||||
String path, {
|
||||
@@ -108,11 +137,7 @@ class DigitalIdModule {
|
||||
request.headers.set('Referer', 'https://digital-id.max.ru/');
|
||||
request.headers.set('x-requested-with', 'ru.oneme.app');
|
||||
request.headers.set('Accept', 'application/json');
|
||||
request.headers.set(
|
||||
'User-Agent',
|
||||
'Mozilla/5.0 (Linux; Android 16; Pixel 7 Pro) AppleWebKit/537.36 '
|
||||
'(KHTML, like Gecko) Version/4.0 Chrome/148.0.0.0 Mobile Safari/537.36',
|
||||
);
|
||||
request.headers.set('User-Agent', await _resolveUserAgent());
|
||||
if (body != null) {
|
||||
request.headers.contentType = ContentType.json;
|
||||
request.add(utf8.encode(jsonEncode(body)));
|
||||
|
||||
Reference in New Issue
Block a user