part of 'api_service.dart'; extension ApiServiceChats on ApiService { Future _sendAuthRequestAfterHandshake() async { if (authToken == null) { print("Токен не найден, пропускаем автоматическую авторизацию"); return; } if (_chatsFetchedInThisSession) { print("Авторизация уже выполнена в этой сессии, пропускаем"); return; } try { await _ensureCacheServicesInitialized(); final prefs = await SharedPreferences.getInstance(); final deviceId = prefs.getString('spoof_deviceid') ?? generateRandomDeviceId(); if (prefs.getString('spoof_deviceid') == null) { await prefs.setString('spoof_deviceid', deviceId); } final payload = { "chatsCount": 100, "chatsSync": 0, "contactsSync": 0, "draftsSync": 0, "interactive": true, "presenceSync": 0, "token": authToken, }; if (userId != null) { payload["userId"] = userId; } print("Автоматически отправляем opcode 19 для авторизации..."); final int chatSeq = _sendMessage(19, payload); final chatResponse = await messages.firstWhere( (msg) => msg['seq'] == chatSeq, ); if (chatResponse['cmd'] == 1) { print("✅ Авторизация (opcode 19) успешна. Сессия ГОТОВА."); _isSessionReady = true; _connectionStatusController.add("ready"); _updateConnectionState( conn_state.ConnectionState.ready, message: 'Авторизация успешна', ); final profile = chatResponse['payload']?['profile']; final contactProfile = profile?['contact']; if (contactProfile != null && contactProfile['id'] != null) { print( "[_sendAuthRequestAfterHandshake] ✅ Профиль и ID пользователя найдены. ID: ${contactProfile['id']}. ЗАПУСКАЕМ АНАЛИТИКУ.", ); _userId = contactProfile['id']; await prefs.setString('userId', _userId.toString()); _sessionId = DateTime.now().millisecondsSinceEpoch; _lastActionTime = _sessionId; sendNavEvent('COLD_START'); _sendInitialSetupRequests(); } if (profile != null && authToken != null) { try { final accountManager = AccountManager(); await accountManager.initialize(); final currentAccount = accountManager.currentAccount; if (currentAccount != null && currentAccount.token == authToken) { final profileObj = Profile.fromJson(profile); await accountManager.updateAccountProfile( currentAccount.id, profileObj, ); try { final profileCache = ProfileCacheService(); await profileCache.initialize(); await profileCache.syncWithServerProfile(profileObj); } catch (e) { print('[ProfileCache] Ошибка синхронизации профиля: $e'); } print( '[_sendAuthRequestAfterHandshake] ✅ Профиль сохранен в AccountManager', ); } } catch (e) { print( '[_sendAuthRequestAfterHandshake] Ошибка сохранения профиля в AccountManager: $e', ); } } if (_onlineCompleter != null && !_onlineCompleter!.isCompleted) { _onlineCompleter!.complete(); } final chatListJson = chatResponse['payload']?['chats'] ?? []; final contactListJson = chatResponse['payload']?['contacts'] ?? []; final presence = chatResponse['payload']?['presence']; final config = chatResponse['payload']?['config']; if (presence != null) { updatePresenceData(presence); } if (config != null) { _processServerPrivacyConfig(config); } final result = { 'chats': chatListJson, 'contacts': contactListJson, 'profile': profile, 'presence': presence, 'config': config, }; _lastChatsPayload = result; final contacts = (contactListJson as List) .map((json) => Contact.fromJson(json as Map)) .toList() .cast(); updateContactCache(contacts); _lastChatsAt = DateTime.now(); _preloadContactAvatars(contacts); unawaited( _chatCacheService.cacheChats( chatListJson.cast>(), ), ); unawaited(_chatCacheService.cacheContacts(contacts)); _chatsFetchedInThisSession = true; } } catch (e) { print("Ошибка при автоматической авторизации: $e"); } } void createGroup(String name, List participantIds) { final payload = {"name": name, "participantIds": participantIds}; _sendMessage(48, payload); print('Создаем группу: $name с участниками: $participantIds'); } void updateGroup(int chatId, {String? name, List? participantIds}) { final payload = { "chatId": chatId, if (name != null) "name": name, if (participantIds != null) "participantIds": participantIds, }; _sendMessage(272, payload); print('Обновляем группу $chatId: $payload'); } void createGroupWithMessage(String name, List participantIds) { final cid = DateTime.now().millisecondsSinceEpoch; final payload = { "message": { "cid": cid, "attaches": [ { "_type": "CONTROL", "event": "new", "chatType": "CHAT", "title": name, "userIds": participantIds, }, ], }, "notify": true, }; _sendMessage(64, payload); print('Создаем группу: $name с участниками: $participantIds'); } void renameGroup(int chatId, String newName) { final payload = {"chatId": chatId, "theme": newName}; _sendMessage(55, payload); print('Переименовываем группу $chatId в: $newName'); } /// Создает/перегенерирует пригласительную ссылку для группы. /// Сервер ожидает payload вида: /// {"chatId": -69330645868731, "revokePrivateLink": true} /// В ответ приходит объект с обновленным chat, где есть поле "link". Future createGroupInviteLink( int chatId, { bool revokePrivateLink = true, }) async { final payload = { "chatId": chatId, "revokePrivateLink": revokePrivateLink, }; print('Создаем пригласительную ссылку для группы $chatId: $payload'); final int seq = _sendMessage(55, payload); try { final response = await messages .firstWhere((msg) => msg['seq'] == seq) .timeout(const Duration(seconds: 15)); if (response['cmd'] == 3) { final error = response['payload']; print('Ошибка создания пригласительной ссылки: $error'); final message = error?['localizedMessage'] ?? error?['message'] ?? 'Неизвестная ошибка'; throw Exception(message); } final chat = response['payload']?['chat']; final link = chat?['link'] as String?; if (link == null || link.isEmpty) { print( 'Пригласительная ссылка не найдена в ответе: ${response['payload']}', ); return null; } // Обновим кэш чатов, если сервер вернул полный объект чата try { if (chat != null) { final chats = _lastChatsPayload?['chats'] as List?; if (chats != null) { final index = chats.indexWhere( (c) => c is Map && c['id'] == chat['id'], ); if (index >= 0) { chats[index] = chat; } } } } catch (e) { print('Не удалось обновить кэш чатов после создания ссылки: $e'); } return link; } catch (e) { print('Ошибка при создании пригласительной ссылки: $e'); rethrow; } } void addGroupMember( int chatId, List userIds, { bool showHistory = true, }) { final payload = { "chatId": chatId, "userIds": userIds, "showHistory": showHistory, "operation": "add", }; _sendMessage(77, payload); print('Добавляем участников $userIds в группу $chatId'); } void removeGroupMember( int chatId, List userIds, { int cleanMsgPeriod = 0, }) { final payload = { "chatId": chatId, "userIds": userIds, "operation": "remove", "cleanMsgPeriod": cleanMsgPeriod, }; _sendMessage(77, payload); print('Удаляем участников $userIds из группы $chatId'); } void leaveGroup(int chatId) { final payload = {"chatId": chatId}; _sendMessage(58, payload); print('Выходим из группы $chatId'); } void getGroupMembers(int chatId, {int marker = 0, int count = 50}) { final payload = { "type": "MEMBER", "marker": marker, "chatId": chatId, "count": count, }; _sendMessage(59, payload); print( 'Запрашиваем участников группы $chatId (marker: $marker, count: $count)', ); } Future> getChatsOnly({bool force = false}) async { if (authToken == null) { await _loadTokenFromAccountManager(); } if (authToken == null) throw Exception("Auth token not found"); if (!force && _lastChatsPayload != null && _lastChatsAt != null) { if (DateTime.now().difference(_lastChatsAt!) < _chatsCacheTtl) { return _lastChatsPayload!; } } await _ensureCacheServicesInitialized(); if (!force && _lastChatsPayload == null) { final cachedChats = await _chatCacheService.getCachedChats(); final cachedContacts = await _chatCacheService.getCachedContacts(); if (cachedChats != null && cachedContacts != null && cachedChats.isNotEmpty) { final result = { 'chats': cachedChats, 'contacts': cachedContacts.map(_contactToMap).toList(), 'profile': null, 'presence': null, }; _lastChatsPayload = result; _lastChatsAt = DateTime.now(); updateContactCache(cachedContacts); _preloadContactAvatars(cachedContacts); return result; } } try { // Используем opcode 48 для запроса конкретных чатов // chatIds:[0] - это "Избранное" (Saved Messages) final payload = { "chatIds": [0], }; final int chatSeq = _sendMessage(48, payload); final chatResponse = await messages.firstWhere( (msg) => msg['seq'] == chatSeq, ); final List chatListJson = chatResponse['payload']?['chats'] ?? []; if (chatListJson.isEmpty) { final result = {'chats': [], 'contacts': [], 'profile': null}; _lastChatsPayload = result; _lastChatsAt = DateTime.now(); return result; } final contactIds = {}; for (var chatJson in chatListJson) { final participants = chatJson['participants'] as Map? ?? {}; contactIds.addAll(participants.keys.map((id) => int.parse(id))); } List contactListJson = []; if (contactIds.isNotEmpty) { final int contactSeq = _sendMessage(32, { "contactIds": contactIds.toList(), }); final contactResponse = await messages.firstWhere( (msg) => msg['seq'] == contactSeq, ); contactListJson = contactResponse['payload']?['contacts'] ?? []; } final result = { 'chats': chatListJson, 'contacts': contactListJson, 'profile': null, 'presence': null, }; _lastChatsPayload = result; final List contacts = contactListJson .map((json) => Contact.fromJson(json as Map)) .toList(); updateContactCache(contacts); _lastChatsAt = DateTime.now(); _preloadContactAvatars(contacts); unawaited( _chatCacheService.cacheChats(chatListJson.cast>()), ); unawaited(_chatCacheService.cacheContacts(contacts)); return result; } catch (e) { print('Ошибка получения чатов через opcode 48: $e'); rethrow; } } Future> getChatsAndContacts({bool force = false}) async { await waitUntilOnline(); if (authToken == null) { print("Токен авторизации не найден, требуется повторная авторизация"); throw Exception("Auth token not found - please re-authenticate"); } await _ensureCacheServicesInitialized(); if (!force && _lastChatsPayload != null && _lastChatsAt != null) { if (DateTime.now().difference(_lastChatsAt!) < _chatsCacheTtl) { return _lastChatsPayload!; } } if (!force && !_chatsFetchedInThisSession && _lastChatsPayload == null) { final cachedChats = await _chatCacheService.getCachedChats(); final cachedContacts = await _chatCacheService.getCachedContacts(); if (cachedChats != null && cachedContacts != null && cachedChats.isNotEmpty) { final cachedResult = { 'chats': cachedChats, 'contacts': cachedContacts.map(_contactToMap).toList(), 'profile': null, 'presence': null, }; _lastChatsPayload = cachedResult; _lastChatsAt = DateTime.now(); updateContactCache(cachedContacts); _preloadContactAvatars(cachedContacts); return cachedResult; } } if (_chatsFetchedInThisSession && _lastChatsPayload != null && !force) { return _lastChatsPayload!; } if (_inflightChatsCompleter != null) { return _inflightChatsCompleter!.future; } _inflightChatsCompleter = Completer>(); if (_isSessionOnline && _isSessionReady && _lastChatsPayload != null && !force) { _inflightChatsCompleter!.complete(_lastChatsPayload!); _inflightChatsCompleter = null; return _lastChatsPayload!; } try { Map chatResponse; final int opcode; final Map payload; final prefs = await SharedPreferences.getInstance(); final deviceId = prefs.getString('spoof_deviceid') ?? generateRandomDeviceId(); if (prefs.getString('spoof_deviceid') == null) { await prefs.setString('spoof_deviceid', deviceId); } if (!_chatsFetchedInThisSession) { opcode = 19; payload = { "chatsCount": 100, "chatsSync": 0, "contactsSync": 0, "draftsSync": 0, "interactive": true, "presenceSync": 0, "token": authToken, }; if (userId != null) { payload["userId"] = userId; } } else { return await getChatsOnly(force: force); } final int chatSeq = _sendMessage(opcode, payload); chatResponse = await messages.firstWhere((msg) => msg['seq'] == chatSeq); if (opcode == 19 && chatResponse['cmd'] == 1) { print("✅ Авторизация (opcode 19) успешна. Сессия ГОТОВА."); _isSessionReady = true; _connectionStatusController.add("ready"); _updateConnectionState( conn_state.ConnectionState.ready, message: 'Авторизация успешна', ); final profile = chatResponse['payload']?['profile']; final contactProfile = profile?['contact']; if (contactProfile != null && contactProfile['id'] != null) { print( "[getChatsAndContacts] ✅ Профиль и ID пользователя найдены. ID: ${contactProfile['id']}. ЗАПУСКАЕМ АНАЛИТИКУ.", ); _userId = contactProfile['id']; _sessionId = DateTime.now().millisecondsSinceEpoch; _lastActionTime = _sessionId; sendNavEvent('COLD_START'); _sendInitialSetupRequests(); } else { print( "[getChatsAndContacts] ❌ ВНИМАНИЕ: Профиль или ID в ответе пустой, аналитика не будет отправлена.", ); } if (_onlineCompleter != null && !_onlineCompleter!.isCompleted) { _onlineCompleter!.complete(); } _startPinging(); _processMessageQueue(); } final profile = chatResponse['payload']?['profile']; final presence = chatResponse['payload']?['presence']; final config = chatResponse['payload']?['config']; final List chatListJson = chatResponse['payload']?['chats'] ?? []; if (profile != null && authToken != null) { try { final accountManager = AccountManager(); await accountManager.initialize(); final currentAccount = accountManager.currentAccount; if (currentAccount != null && currentAccount.token == authToken) { final profileObj = Profile.fromJson(profile); await accountManager.updateAccountProfile( currentAccount.id, profileObj, ); try { final profileCache = ProfileCacheService(); await profileCache.initialize(); await profileCache.syncWithServerProfile(profileObj); } catch (e) { print('[ProfileCache] Ошибка синхронизации профиля: $e'); } } } catch (e) { print('Ошибка сохранения профиля в AccountManager: $e'); } } if (chatListJson.isEmpty) { if (config != null) { _processServerPrivacyConfig(config); } final result = { 'chats': [], 'contacts': [], 'profile': profile, 'config': config, }; _lastChatsPayload = result; _lastChatsAt = DateTime.now(); _chatsFetchedInThisSession = true; _inflightChatsCompleter!.complete(_lastChatsPayload!); _inflightChatsCompleter = null; return result; } List contactListJson = chatResponse['payload']?['contacts'] ?? []; if (contactListJson.isEmpty) { final contactIds = {}; for (var chatJson in chatListJson) { final participants = chatJson['participants'] as Map? ?? {}; contactIds.addAll(participants.keys.map((id) => int.parse(id))); } if (contactIds.isNotEmpty) { final int contactSeq = _sendMessage(32, { "contactIds": contactIds.toList(), }); final contactResponse = await messages.firstWhere( (msg) => msg['seq'] == contactSeq, ); contactListJson = contactResponse['payload']?['contacts'] ?? []; } } if (presence != null) { updatePresenceData(presence); } if (config != null) { _processServerPrivacyConfig(config); } final result = { 'chats': chatListJson, 'contacts': contactListJson, 'profile': profile, 'presence': presence, 'config': config, }; _lastChatsPayload = result; final List contacts = contactListJson .map((json) => Contact.fromJson(json as Map)) .toList(); updateContactCache(contacts); _lastChatsAt = DateTime.now(); _preloadContactAvatars(contacts); unawaited( _chatCacheService.cacheChats(chatListJson.cast>()), ); unawaited(_chatCacheService.cacheContacts(contacts)); _chatsFetchedInThisSession = true; _inflightChatsCompleter!.complete(result); _inflightChatsCompleter = null; return result; } catch (e) { final error = e; _inflightChatsCompleter?.completeError(error); _inflightChatsCompleter = null; rethrow; } } Future _sendInitialSetupRequests() async { print("Запускаем отправку единичных запросов при старте..."); if (!_isSessionOnline || !_isSessionReady) { print("Сессия еще не готова, ждем..."); await waitUntilOnline(); } await Future.delayed(const Duration(seconds: 2)); if (!_isSessionOnline || !_isSessionReady) { print("Сессия не готова для отправки запросов, пропускаем"); return; } _sendMessage(272, {"folderSync": 0}); await Future.delayed(const Duration(milliseconds: 500)); _sendMessage(27, {"sync": 0, "type": "STICKER"}); await Future.delayed(const Duration(milliseconds: 500)); _sendMessage(27, {"sync": 0, "type": "FAVORITE_STICKER"}); await Future.delayed(const Duration(milliseconds: 500)); _sendMessage(79, {"forward": false, "count": 100}); await Future.delayed(const Duration(seconds: 5)); _sendMessage(26, { "sectionId": "NEW_STICKER_SETS", "from": 5, "count": 100, }); print("Единичные запросы отправлены."); } Future> getMessageHistory( int chatId, { bool force = false, }) async { await _ensureCacheServicesInitialized(); if (!force && _messageCache.containsKey(chatId)) { print("Загружаем сообщения для чата $chatId из кэша."); return _messageCache[chatId]!; } if (!force) { final cachedMessages = await _chatCacheService.getCachedChatMessages( chatId, ); if (cachedMessages != null && cachedMessages.isNotEmpty) { print( "История сообщений для чата $chatId загружена из ChatCacheService.", ); _messageCache[chatId] = cachedMessages; return cachedMessages; } } print("Запрашиваем историю для чата $chatId с сервера."); final payload = { "chatId": chatId, "from": DateTime.now() .add(const Duration(days: 1)) .millisecondsSinceEpoch, "forward": 0, "backward": 1000, "getMessages": true, }; try { final int seq = _sendMessage(49, payload); final response = await messages .firstWhere((msg) => msg['seq'] == seq) .timeout(const Duration(seconds: 15)); if (response['cmd'] == 3) { final error = response['payload']; print('Ошибка получения истории сообщений: $error'); if (error['error'] == 'proto.state') { print( 'Ошибка состояния сессии при получении истории, переподключаемся...', ); await reconnect(); await waitUntilOnline(); return getMessageHistory(chatId, force: true); } throw Exception('Ошибка получения истории: ${error['message']}'); } final List messagesJson = response['payload']?['messages'] ?? []; final messagesList = messagesJson.map((json) => Message.fromJson(json)).toList() ..sort((a, b) => a.time.compareTo(b.time)); _messageCache[chatId] = messagesList; _preloadMessageImages(messagesList); unawaited(_chatCacheService.cacheChatMessages(chatId, messagesList)); return messagesList; } catch (e) { print('Ошибка при получении истории сообщений: $e'); return []; } } Future?> loadOldMessages( int chatId, String fromMessageId, int count, ) async { print( "Запрашиваем старые сообщения для чата $chatId начиная с $fromMessageId", ); final payload = { "chatId": chatId, "from": int.parse(fromMessageId), "forward": 0, "backward": count, "getMessages": true, }; try { final int seq = _sendMessage(49, payload); final response = await messages .firstWhere((msg) => msg['seq'] == seq) .timeout(const Duration(seconds: 15)); if (response['cmd'] == 3) { final error = response['payload']; print('Ошибка получения старых сообщений: $error'); return null; } return response['payload']; } catch (e) { print('Ошибка при получении старых сообщений: $e'); return null; } } void sendNavEvent(String event, {int? screenTo, int? screenFrom}) { if (_userId == null) return; final now = DateTime.now().millisecondsSinceEpoch; final Map params = { 'session_id': _sessionId, 'action_id': _actionId++, }; switch (event) { case 'COLD_START': if (_isColdStartSent) return; params['screen_to'] = 150; params['source_id'] = 1; _isColdStartSent = true; break; case 'WARM_START': params['screen_to'] = 150; params['screen_from'] = 1; params['prev_time'] = _lastActionTime; break; case 'GO': params['screen_to'] = screenTo; params['screen_from'] = screenFrom; params['prev_time'] = _lastActionTime; break; } _lastActionTime = now; _sendMessage(5, { "events": [ { "type": "NAV", "event": event, "userId": _userId, "time": now, "params": params, }, ], }); } void createFolder( String title, { List? include, List? filters, }) { final folderId = const Uuid().v4(); final payload = { "id": folderId, "title": title, "include": include ?? [], "filters": filters ?? [], }; _sendMessage(274, payload); print('Создаем папку: $title (ID: $folderId)'); } void updateFolder( String folderId, { String? title, List? include, List? filters, }) { final payload = { "id": folderId, if (title != null) "title": title, if (include != null) "include": include, if (filters != null) "filters": filters, }; _sendMessage(274, payload); print('Обновляем папку: $folderId'); } void deleteFolder(String folderId) { final payload = { "folderIds": [folderId], }; _sendMessage(276, payload); print('Удаляем папку: $folderId'); } void requestFolderSync() { _sendMessage(272, {"folderSync": 0}); print('Запрос на обновление папок отправлен'); } void clearCacheForChat(int chatId) { _messageCache.remove(chatId); if (_cacheServicesInitialized) { unawaited(_chatCacheService.clearChatCache(chatId)); } print("Кэш для чата $chatId очищен."); } void clearChatsCache() { _lastChatsPayload = null; _lastChatsAt = null; print("Кэш чатов очищен."); } Contact? getCachedContact(int contactId) { if (_contactCache.containsKey(contactId)) { final contact = _contactCache[contactId]!; print('Контакт $contactId получен из кэша: ${contact.name}'); return contact; } return null; } Future> getNetworkStatistics() async { final prefs = await SharedPreferences.getInstance(); final totalTraffic = prefs.getDouble('network_total_traffic') ?? (150.0 * 1024 * 1024); final messagesTraffic = prefs.getDouble('network_messages_traffic') ?? (totalTraffic * 0.15); final mediaTraffic = prefs.getDouble('network_media_traffic') ?? (totalTraffic * 0.6); final syncTraffic = prefs.getDouble('network_sync_traffic') ?? (totalTraffic * 0.1); final currentSpeed = _isSessionOnline ? 512.0 * 1024 : 0.0; final ping = 25; return { 'totalTraffic': totalTraffic, 'messagesTraffic': messagesTraffic, 'mediaTraffic': mediaTraffic, 'syncTraffic': syncTraffic, 'otherTraffic': totalTraffic * 0.15, 'currentSpeed': currentSpeed, 'isConnected': _isSessionOnline, 'connectionType': 'Wi-Fi', 'signalStrength': 85, 'ping': ping, 'jitter': 2.5, 'packetLoss': 0.01, 'hourlyStats': [], }; } bool isContactCacheValid() { if (_lastContactsUpdate == null) return false; return DateTime.now().difference(_lastContactsUpdate!) < ApiService._contactCacheExpiry; } void updateContactCache(List contacts) { _contactCache.clear(); for (final contact in contacts) { _contactCache[contact.id] = contact; } _lastContactsUpdate = DateTime.now(); print('Кэш контактов обновлен: ${contacts.length} контактов'); } void updateCachedContact(Contact contact) { _contactCache[contact.id] = contact; print('Контакт ${contact.id} обновлен в кэше: ${contact.name}'); } void clearContactCache() { _contactCache.clear(); _lastContactsUpdate = null; print("Кэш контактов очищен."); } void clearAllCaches() { clearContactCache(); clearChatsCache(); _messageCache.clear(); clearPasswordAuthData(); if (_cacheServicesInitialized) { unawaited(_cacheService.clear()); unawaited(_chatCacheService.clearAllChatCache()); unawaited(_avatarCacheService.clearAvatarCache()); unawaited(ImageCacheService.instance.clearCache()); } print("Все кэши очищены из-за ошибки подключения."); } Future> getStatistics() async { await _ensureCacheServicesInitialized(); final cacheStats = await _cacheService.getCacheStats(); final chatCacheStats = await _chatCacheService.getChatCacheStats(); final avatarStats = await _avatarCacheService.getAvatarCacheStats(); final imageStats = await ImageCacheService.instance.getCacheStats(); return { 'api_service': { 'is_online': _isSessionOnline, 'is_ready': _isSessionReady, 'cached_chats': (_lastChatsPayload?['chats'] as List?)?.length ?? 0, 'contacts_in_memory': _contactCache.length, 'message_cache_entries': _messageCache.length, 'message_queue_length': _messageQueue.length, }, 'connection': { 'current_url': _currentUrlIndex < _wsUrls.length ? _wsUrls[_currentUrlIndex] : null, 'reconnect_attempts': _reconnectAttempts, 'last_action_time': _lastActionTime, }, 'cache_service': cacheStats, 'chat_cache': chatCacheStats, 'avatar_cache': avatarStats, 'image_cache': imageStats, }; } void _preloadContactAvatars(List contacts) { if (!_cacheServicesInitialized || contacts.isEmpty) return; final photoUrls = contacts.map((c) => c.photoBaseUrl).toList(); if (photoUrls.isEmpty) return; unawaited(ImageCacheService.instance.preloadContactAvatars(photoUrls)); } void _preloadMessageImages(List messages) { if (!_cacheServicesInitialized || messages.isEmpty) return; final urls = {}; for (final message in messages) { for (final attach in message.attaches) { final url = attach['url'] ?? attach['baseUrl']; if (url is String && url.isNotEmpty) { urls.add(url); } } } for (final url in urls) { unawaited(ImageCacheService.instance.preloadImage(url)); } } Map _contactToMap(Contact contact) { return { 'id': contact.id, 'name': contact.name, 'firstName': contact.firstName, 'lastName': contact.lastName, 'description': contact.description, 'photoBaseUrl': contact.photoBaseUrl, 'isBlocked': contact.isBlocked, 'isBlockedByMe': contact.isBlockedByMe, 'accountStatus': contact.accountStatus, 'status': contact.status, 'options': contact.options, }; } void sendMessage( int chatId, String text, { String? replyToMessageId, int? cid, }) { final int clientMessageId = cid ?? DateTime.now().millisecondsSinceEpoch; final payload = { "chatId": chatId, "message": { "text": text, "cid": clientMessageId, "elements": [], "attaches": [], if (replyToMessageId != null) "link": {"type": "REPLY", "messageId": replyToMessageId}, }, "notify": true, }; clearChatsCache(); if (_isSessionOnline) { _sendMessage(64, payload); } else { print("Сессия не онлайн. Сообщение добавлено в очередь."); _messageQueue.add({'opcode': 64, 'payload': payload}); } } void forwardMessage(int targetChatId, String messageId, int sourceChatId) { final int clientMessageId = DateTime.now().millisecondsSinceEpoch; final payload = { "chatId": targetChatId, "message": { "cid": clientMessageId, "link": { "type": "FORWARD", "messageId": messageId, "chatId": sourceChatId, }, "attaches": [], }, "notify": true, }; if (_isSessionOnline) { _sendMessage(64, payload); } else { _messageQueue.add({'opcode': 64, 'payload': payload}); } } Future editMessage(int chatId, String messageId, String newText) async { final payload = { "chatId": chatId, "messageId": messageId, "text": newText, "elements": [], "attachments": [], }; clearChatsCache(); await waitUntilOnline(); if (!_isSessionOnline) { print('Сессия не онлайн, пытаемся переподключиться...'); await reconnect(); await waitUntilOnline(); } Future sendOnce() async { try { final int seq = _sendMessage(67, payload); final response = await messages .firstWhere((msg) => msg['seq'] == seq) .timeout(const Duration(seconds: 10)); if (response['cmd'] == 3) { final error = response['payload']; print('Ошибка редактирования сообщения: $error'); if (error['error'] == 'proto.state') { print('Ошибка состояния сессии, переподключаемся...'); await reconnect(); await waitUntilOnline(); return false; } if (error['error'] == 'error.edit.invalid.message') { print( 'Сообщение не может быть отредактировано: ${error['localizedMessage']}', ); throw Exception( 'Сообщение не может быть отредактировано: ${error['localizedMessage']}', ); } return false; } return response['cmd'] == 1; } catch (e) { print('Ошибка при редактировании сообщения: $e'); return false; } } for (int attempt = 0; attempt < 3; attempt++) { print( 'Попытка редактирования сообщения $messageId (попытка ${attempt + 1}/3)', ); bool ok = await sendOnce(); if (ok) { print('Сообщение $messageId успешно отредактировано'); return; } if (attempt < 2) { print( 'Повторяем запрос редактирования для сообщения $messageId через 2 секунды...', ); await Future.delayed(const Duration(seconds: 2)); } } print('Не удалось отредактировать сообщение $messageId после 3 попыток'); } Future deleteMessage( int chatId, String messageId, { bool forMe = false, }) async { final payload = { "chatId": chatId, "messageIds": [messageId], "forMe": forMe, }; clearChatsCache(); await waitUntilOnline(); if (!_isSessionOnline) { print('Сессия не онлайн, пытаемся переподключиться...'); await reconnect(); await waitUntilOnline(); } Future sendOnce() async { try { final int seq = _sendMessage(66, payload); final response = await messages .firstWhere((msg) => msg['seq'] == seq) .timeout(const Duration(seconds: 10)); if (response['cmd'] == 3) { final error = response['payload']; print('Ошибка удаления сообщения: $error'); if (error['error'] == 'proto.state') { print('Ошибка состояния сессии, переподключаемся...'); await reconnect(); await waitUntilOnline(); return false; } return false; } return response['cmd'] == 1; } catch (e) { print('Ошибка при удалении сообщения: $e'); return false; } } for (int attempt = 0; attempt < 3; attempt++) { print('Попытка удаления сообщения $messageId (попытка ${attempt + 1}/3)'); bool ok = await sendOnce(); if (ok) { print('Сообщение $messageId успешно удалено'); return; } if (attempt < 2) { print( 'Повторяем запрос удаления для сообщения $messageId через 2 секунды...', ); await Future.delayed(const Duration(seconds: 2)); } } print('Не удалось удалить сообщение $messageId после 3 попыток'); } void sendTyping(int chatId, {String type = "TEXT"}) { final payload = {"chatId": chatId, "type": type}; if (_isSessionOnline) { _sendMessage(65, payload); } } }