FUCKING REFACTOR
This commit is contained in:
861
lib/api/api_service_chats.dart
Normal file
861
lib/api/api_service_chats.dart
Normal file
@@ -0,0 +1,861 @@
|
||||
part of 'api_service.dart';
|
||||
|
||||
extension ApiServiceChats on ApiService {
|
||||
void createGroup(String name, List<int> participantIds) {
|
||||
final payload = {"name": name, "participantIds": participantIds};
|
||||
_sendMessage(48, payload);
|
||||
print('Создаем группу: $name с участниками: $participantIds');
|
||||
}
|
||||
|
||||
void updateGroup(int chatId, {String? name, List<int>? 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<int> 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');
|
||||
}
|
||||
|
||||
void addGroupMember(
|
||||
int chatId,
|
||||
List<int> 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<int> 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<Map<String, dynamic>> 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!;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
final payload = {"chatsCount": 100};
|
||||
|
||||
final int chatSeq = _sendMessage(48, payload);
|
||||
final chatResponse = await messages.firstWhere(
|
||||
(msg) => msg['seq'] == chatSeq,
|
||||
);
|
||||
|
||||
final List<dynamic> chatListJson =
|
||||
chatResponse['payload']?['chats'] ?? [];
|
||||
|
||||
if (chatListJson.isEmpty) {
|
||||
final result = {'chats': [], 'contacts': [], 'profile': null};
|
||||
_lastChatsPayload = result;
|
||||
_lastChatsAt = DateTime.now();
|
||||
return result;
|
||||
}
|
||||
|
||||
final contactIds = <int>{};
|
||||
for (var chatJson in chatListJson) {
|
||||
final participants =
|
||||
chatJson['participants'] as Map<String, dynamic>? ?? {};
|
||||
contactIds.addAll(participants.keys.map((id) => int.parse(id)));
|
||||
}
|
||||
|
||||
final int contactSeq = _sendMessage(32, {
|
||||
"contactIds": contactIds.toList(),
|
||||
});
|
||||
final contactResponse = await messages.firstWhere(
|
||||
(msg) => msg['seq'] == contactSeq,
|
||||
);
|
||||
|
||||
final List<dynamic> contactListJson =
|
||||
contactResponse['payload']?['contacts'] ?? [];
|
||||
|
||||
final result = {
|
||||
'chats': chatListJson,
|
||||
'contacts': contactListJson,
|
||||
'profile': null,
|
||||
'presence': null,
|
||||
};
|
||||
_lastChatsPayload = result;
|
||||
|
||||
final contacts =
|
||||
contactListJson.map((json) => Contact.fromJson(json)).toList();
|
||||
updateContactCache(contacts);
|
||||
_lastChatsAt = DateTime.now();
|
||||
return result;
|
||||
} catch (e) {
|
||||
print('Ошибка получения чатов: $e');
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>> getChatsAndContacts({bool force = false}) async {
|
||||
await waitUntilOnline();
|
||||
|
||||
if (authToken == null) {
|
||||
print("Токен авторизации не найден, требуется повторная авторизация");
|
||||
throw Exception("Auth token not found - please re-authenticate");
|
||||
}
|
||||
|
||||
if (!force && _lastChatsPayload != null && _lastChatsAt != null) {
|
||||
if (DateTime.now().difference(_lastChatsAt!) < _chatsCacheTtl) {
|
||||
return _lastChatsPayload!;
|
||||
}
|
||||
}
|
||||
|
||||
if (_chatsFetchedInThisSession && _lastChatsPayload != null && !force) {
|
||||
return _lastChatsPayload!;
|
||||
}
|
||||
|
||||
if (_inflightChatsCompleter != null) {
|
||||
return _inflightChatsCompleter!.future;
|
||||
}
|
||||
_inflightChatsCompleter = Completer<Map<String, dynamic>>();
|
||||
|
||||
if (_isSessionOnline &&
|
||||
_isSessionReady &&
|
||||
_lastChatsPayload != null &&
|
||||
!force) {
|
||||
_inflightChatsCompleter!.complete(_lastChatsPayload!);
|
||||
_inflightChatsCompleter = null;
|
||||
return _lastChatsPayload!;
|
||||
}
|
||||
|
||||
try {
|
||||
Map<String, dynamic> chatResponse;
|
||||
|
||||
final int opcode;
|
||||
final Map<String, dynamic> 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");
|
||||
|
||||
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<dynamic> 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,
|
||||
);
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
final contactIds = <int>{};
|
||||
for (var chatJson in chatListJson) {
|
||||
final participants = chatJson['participants'] as Map<String, dynamic>;
|
||||
contactIds.addAll(participants.keys.map((id) => int.parse(id)));
|
||||
}
|
||||
|
||||
final int contactSeq = _sendMessage(32, {
|
||||
"contactIds": contactIds.toList(),
|
||||
});
|
||||
final contactResponse = await messages.firstWhere(
|
||||
(msg) => msg['seq'] == contactSeq,
|
||||
);
|
||||
|
||||
final List<dynamic> 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 contacts =
|
||||
contactListJson.map((json) => Contact.fromJson(json)).toList();
|
||||
updateContactCache(contacts);
|
||||
_lastChatsAt = DateTime.now();
|
||||
_chatsFetchedInThisSession = true;
|
||||
_inflightChatsCompleter!.complete(result);
|
||||
_inflightChatsCompleter = null;
|
||||
return result;
|
||||
} catch (e) {
|
||||
final error = e;
|
||||
_inflightChatsCompleter?.completeError(error);
|
||||
_inflightChatsCompleter = null;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _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<List<Message>> getMessageHistory(
|
||||
int chatId, {
|
||||
bool force = false,
|
||||
}) async {
|
||||
if (!force && _messageCache.containsKey(chatId)) {
|
||||
print("Загружаем сообщения для чата $chatId из кэша.");
|
||||
return _messageCache[chatId]!;
|
||||
}
|
||||
|
||||
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<dynamic> messagesJson = response['payload']?['messages'] ?? [];
|
||||
final messagesList =
|
||||
messagesJson.map((json) => Message.fromJson(json)).toList()
|
||||
..sort((a, b) => a.time.compareTo(b.time));
|
||||
|
||||
_messageCache[chatId] = messagesList;
|
||||
|
||||
return messagesList;
|
||||
} catch (e) {
|
||||
print('Ошибка при получении истории сообщений: $e');
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, dynamic>?> 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<String, dynamic> 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<int>? include,
|
||||
List<dynamic>? 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<int>? include,
|
||||
List<dynamic>? 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);
|
||||
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<Map<String, dynamic>> 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<Contact> 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();
|
||||
print("Все кэши очищены из-за ошибки подключения.");
|
||||
}
|
||||
|
||||
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<void> 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<bool> 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<void> 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<bool> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user