бля а как мне тестить у меня темпблок

This commit is contained in:
Ваше Имя
2026-04-06 22:04:22 +07:00
parent 9a1dc8e82b
commit c4623d9dc7
6 changed files with 2843 additions and 100 deletions
+476
View File
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import '../api.dart';
import '../../core/protocol/opcode_map.dart';
import '../../core/protocol/packet.dart';
@@ -9,6 +10,184 @@ import 'chats.dart';
import 'contacts.dart';
import 'folders.dart';
class PrivacyConfig {
final String searchByPhone;
final String incomingCall;
final bool doubleTapReactionDisabled;
final bool safeModeNoPin;
final String? doubleTapReactionValue;
final String familyProtection;
final bool pushDetails;
final bool hidden;
final String chatsInvite;
final bool pushNewContacts;
final bool unsafeFiles;
final String inactiveTtl;
final bool showReadMark;
final bool altKeyboard;
final bool contentLevelAccess;
final String stickersSuggest;
final bool safeMode;
final bool audioTranscriptionEnabled;
final String hash;
const PrivacyConfig({
required this.searchByPhone,
required this.incomingCall,
required this.doubleTapReactionDisabled,
required this.safeModeNoPin,
this.doubleTapReactionValue,
required this.familyProtection,
required this.pushDetails,
required this.hidden,
required this.chatsInvite,
required this.pushNewContacts,
required this.unsafeFiles,
required this.inactiveTtl,
required this.showReadMark,
required this.altKeyboard,
required this.contentLevelAccess,
required this.stickersSuggest,
required this.safeMode,
required this.audioTranscriptionEnabled,
required this.hash,
});
factory PrivacyConfig.fromMap(Map<dynamic, dynamic> map) {
return PrivacyConfig(
searchByPhone: map['SEARCH_BY_PHONE']?.toString() ?? 'ALL',
incomingCall: map['INCOMING_CALL']?.toString() ?? 'CONTACTS',
doubleTapReactionDisabled: map['DOUBLE_TAP_REACTION_DISABLED'] ?? false,
safeModeNoPin: map['SAFE_MODE_NO_PIN'] ?? false,
doubleTapReactionValue: map['DOUBLE_TAP_REACTION_VALUE']?.toString(),
familyProtection: map['FAMILY_PROTECTION']?.toString() ?? 'OFF',
pushDetails: map['PUSH_DETAILS'] ?? false,
hidden: map['HIDDEN'] ?? true,
chatsInvite: map['CHATS_INVITE']?.toString() ?? 'CONTACTS',
pushNewContacts: map['PUSH_NEW_CONTACTS'] ?? false,
unsafeFiles: map['UNSAFE_FILES'] ?? true,
inactiveTtl: map['INACTIVE_TTL']?.toString() ?? '6M',
showReadMark: map['SHOW_READ_MARK'] ?? true,
altKeyboard: map['ALT_KEYBOARD'] ?? false,
contentLevelAccess: map['CONTENT_LEVEL_ACCESS'] ?? false,
stickersSuggest: map['STICKERS_SUGGEST']?.toString() ?? 'ON',
safeMode: map['SAFE_MODE'] ?? false,
audioTranscriptionEnabled: map['AUDIO_TRANSCRIPTION_ENABLED'] ?? true,
hash: map['hash']?.toString() ?? '',
);
}
String toJson() => jsonEncode({
'SEARCH_BY_PHONE': searchByPhone,
'INCOMING_CALL': incomingCall,
'DOUBLE_TAP_REACTION_DISABLED': doubleTapReactionDisabled,
'SAFE_MODE_NO_PIN': safeModeNoPin,
'DOUBLE_TAP_REACTION_VALUE': doubleTapReactionValue,
'FAMILY_PROTECTION': familyProtection,
'PUSH_DETAILS': pushDetails,
'HIDDEN': hidden,
'CHATS_INVITE': chatsInvite,
'PUSH_NEW_CONTACTS': pushNewContacts,
'UNSAFE_FILES': unsafeFiles,
'INACTIVE_TTL': inactiveTtl,
'SHOW_READ_MARK': showReadMark,
'ALT_KEYBOARD': altKeyboard,
'CONTENT_LEVEL_ACCESS': contentLevelAccess,
'STICKERS_SUGGEST': stickersSuggest,
'SAFE_MODE': safeMode,
'AUDIO_TRANSCRIPTION_ENABLED': audioTranscriptionEnabled,
'hash': hash,
});
factory PrivacyConfig.fromJson(String json) {
try {
final map = jsonDecode(json) as Map<String, dynamic>;
return PrivacyConfig.fromMap(map);
} catch (_) {
return PrivacyConfig.empty();
}
}
static PrivacyConfig empty() {
return const PrivacyConfig(
searchByPhone: 'ALL',
incomingCall: 'CONTACTS',
doubleTapReactionDisabled: false,
safeModeNoPin: false,
familyProtection: 'OFF',
pushDetails: false,
hidden: true,
chatsInvite: 'CONTACTS',
pushNewContacts: false,
unsafeFiles: true,
inactiveTtl: '6M',
showReadMark: true,
altKeyboard: false,
contentLevelAccess: false,
stickersSuggest: 'ON',
safeMode: false,
audioTranscriptionEnabled: true,
hash: '',
);
}
}
class BlockedContact {
final int id;
final String? firstName;
final String? lastName;
final String? baseUrl;
final int? photoId;
final String status;
final int registrationTime;
final int updateTime;
const BlockedContact({
required this.id,
this.firstName,
this.lastName,
this.baseUrl,
this.photoId,
required this.status,
required this.registrationTime,
required this.updateTime,
});
factory BlockedContact.fromMap(Map<dynamic, dynamic> map) {
String? firstName;
String? lastName;
final names = map['names'] as List?;
if (names != null && names.isNotEmpty) {
for (final n in names) {
if (n is Map) {
firstName = n['firstName'] as String?;
lastName = n['lastName'] as String?;
if (n['type'] == 'ONEME') break;
}
}
}
return BlockedContact(
id: map['id'] as int? ?? 0,
firstName: firstName,
lastName: lastName,
baseUrl: map['baseUrl'] as String?,
photoId: map['photoId'] as int?,
status: map['status']?.toString() ?? 'BLOCKED',
registrationTime: map['registrationTime'] as int? ?? 0,
updateTime: map['updateTime'] as int? ?? 0,
);
}
}
class TwoFactorDetails {
final bool enabled;
final String? email;
final String? hint;
const TwoFactorDetails({required this.enabled, this.email, this.hint});
}
enum AuthRequestType {
startAuth('START_AUTH'),
resend('RESEND'),
@@ -182,6 +361,303 @@ class AccountModule {
Stream<LoginStatus> get loginStatusStream => _loginStatusController.stream;
Future<PrivacyConfig> getPrivacyConfig() async {
final accountId = await TokenStorage.getActiveAccountId();
if (accountId != null) {
final saved = await AppDatabase.getPrivacyConfig(accountId);
if (saved != null) {
return PrivacyConfig.fromJson(saved);
}
}
return PrivacyConfig.empty();
}
Future<List<BlockedContact>> getBlockedContacts() async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.contactList, {
'status': 'BLOCKED',
'count': 100,
'from': 0,
});
_checkPacketError(packet, 'getBlockedContacts');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'getBlockedContacts: неожиданный тип payload: ${data.runtimeType}',
);
}
final contacts = data['contacts'] as List?;
if (contacts == null) return [];
return contacts
.whereType<Map>()
.map((c) => BlockedContact.fromMap(c.cast<dynamic, dynamic>()))
.toList();
}
Future<PrivacyConfig> updatePrivacyConfig(
Map<String, dynamic> settings,
) async {
_ensureOnline();
final payload = <dynamic, dynamic>{
'settings': {'user': settings},
};
final packet = await _api.sendRequest(Opcode.config, payload);
_checkPacketError(packet, 'updatePrivacyConfig');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'updatePrivacyConfig: неожиданный тип payload: ${data.runtimeType}',
);
}
final user = data['user'];
if (user is! Map) {
throw Exception('updatePrivacyConfig: отсутствует user в payload');
}
final config = PrivacyConfig.fromMap(user.cast<dynamic, dynamic>());
final accountId = await TokenStorage.getActiveAccountId();
if (accountId != null) {
await AppDatabase.savePrivacyConfig(accountId, config.toJson());
}
return config;
}
// 2FA Creation (when not set)
Future<String> create2faTrack() async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authCreateTrack, {'type': 0});
_checkPacketError(packet, 'create2faTrack');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'create2faTrack: неожиданный тип payload: ${data.runtimeType}',
);
}
final trackId = data['trackId'] as String?;
if (trackId == null) {
throw Exception('create2faTrack: отсутствует trackId');
}
return trackId;
}
Future<void> set2faPassword(String trackId, String password) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authValidatePassword, {
'trackId': trackId,
'password': password,
});
_checkPacketError(packet, 'set2faPassword');
if (packet.payload != null && packet.payload is! Map) {
throw Exception('set2faPassword: неожиданный ответ');
}
}
Future<void> set2faHint(String trackId, String hint) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authValidateHint, {
'trackId': trackId,
'hint': hint,
});
_checkPacketError(packet, 'set2faHint');
if (packet.payload != null && packet.payload is! Map) {
throw Exception('set2faHint: неожиданный ответ');
}
}
Future<int> verify2faEmail(String trackId, String email) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authVerifyEmail, {
'trackId': trackId,
'email': email,
});
_checkPacketError(packet, 'verify2faEmail');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'verify2faEmail: неожиданный тип payload: ${data.runtimeType}',
);
}
final blockingDuration = data['blockingDuration'] as int? ?? 60;
return blockingDuration;
}
Future<String> verify2faCode(String trackId, String code) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authCheckEmail, {
'trackId': trackId,
'verifyCode': code,
});
_checkPacketError(packet, 'verify2faCode');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'verify2faCode: неожиданный тип payload: ${data.runtimeType}',
);
}
final email = data['email'] as String? ?? '';
return email;
}
Future<ProfileData> confirm2fa({
required String trackId,
required String password,
String? hint,
}) async {
_ensureOnline();
final payload = <dynamic, dynamic>{
'expectedCapabilities': [0, 3, 4],
'trackId': trackId,
'password': password,
};
if (hint != null) payload['hint'] = hint;
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
_checkPacketError(packet, 'confirm2fa');
return _processProfileUpdate(packet);
}
// 2FA Management (when already set)
Future<String> enter2faPanel() async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authCreateTrack, {'type': 0});
_checkPacketError(packet, 'enter2faPanel');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'enter2faPanel: неожиданный тип payload: ${data.runtimeType}',
);
}
final trackId = data['trackId'] as String?;
if (trackId == null) {
throw Exception('enter2faPanel: отсутствует trackId');
}
return trackId;
}
Future<TwoFactorDetails> get2faDetails(String trackId) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.auth2faDetails, {
'trackId': trackId,
});
_checkPacketError(packet, 'get2faDetails');
final data = packet.payload;
if (data is! Map) {
throw Exception(
'get2faDetails: неожиданный тип payload: ${data.runtimeType}',
);
}
final password = data['password'] as Map?;
return TwoFactorDetails(
enabled: password?['enabled'] ?? false,
email: password?['email'] as String?,
hint: password?['hint'] as String?,
);
}
Future<void> check2faPassword(String trackId, String password) async {
_ensureOnline();
final packet = await _api.sendRequest(Opcode.authLoginCheckPassword, {
'trackId': trackId,
'password': password,
});
_checkPacketError(packet, 'check2faPassword');
final data = packet.payload;
if (data is Map && data['error'] != null) {
throw Exception('Неверный пароль');
}
}
Future<ProfileData> update2faPassword({
required String trackId,
required String newPassword,
String? hint,
}) async {
_ensureOnline();
final validatePacket = await _api.sendRequest(Opcode.authValidatePassword, {
'trackId': trackId,
'password': newPassword,
});
_checkPacketError(validatePacket, 'update2faPassword: validate');
if (validatePacket.payload != null && validatePacket.payload is! Map) {
throw Exception('update2faPassword: неожиданный ответ при валидации');
}
if (hint != null) {
final hintPacket = await _api.sendRequest(Opcode.authValidateHint, {
'trackId': trackId,
'hint': hint,
});
_checkPacketError(hintPacket, 'update2faPassword: hint');
}
final payload = <dynamic, dynamic>{
'expectedCapabilities': [1, 3],
'trackId': trackId,
'password': newPassword,
};
if (hint != null) payload['hint'] = hint;
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
_checkPacketError(packet, 'update2faPassword');
return _processProfileUpdate(packet);
}
Future<ProfileData> update2faEmail({
required String trackId,
required String email,
required String code,
}) async {
_ensureOnline();
final verifyPacket = await _api.sendRequest(Opcode.authVerifyEmail, {
'trackId': trackId,
'email': email,
});
_checkPacketError(verifyPacket, 'update2faEmail: verify');
final codePacket = await _api.sendRequest(Opcode.authCheckEmail, {
'trackId': trackId,
'verifyCode': code,
});
_checkPacketError(codePacket, 'update2faEmail: code');
final payload = <dynamic, dynamic>{
'expectedCapabilities': [4],
'trackId': trackId,
};
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
_checkPacketError(packet, 'update2faEmail');
return _processProfileUpdate(packet);
}
Future<ProfileData> remove2fa(String trackId) async {
_ensureOnline();
final payload = <dynamic, dynamic>{
'expectedCapabilities': [5],
'trackId': trackId,
'remove2fa': true,
};
final packet = await _api.sendRequest(Opcode.authSet2fa, payload);
_checkPacketError(packet, 'remove2fa');
return _processProfileUpdate(packet);
}
Future<ProfileData> _processProfileUpdate(Packet packet) async {
_api.registerPushHandler(Opcode.notifProfile, (p) {});
await for (final push in _api.pushStream.where(
(p) => p.opcode == Opcode.notifProfile,
)) {
final payload = push.payload;
if (payload is Map) {
final profile = payload['profile'];
if (profile is Map) {
final contact = profile['contact'];
if (contact is Map) {
return ProfileData.fromServerMap(contact.cast<dynamic, dynamic>());
}
}
}
}
throw Exception('Не удалось получить обновлённый профиль');
}
Future<RequestCodeResult> requestCode(
String phone, {
String language = 'ru',