Files
Qlyra/test/conversation_params_test.dart
T
klockky 760cdcd877 feat: звонки 1:1 (аудио) — WebRTC + ws2-сигналинг
Реализованы голосовые звонки 1:1 поверх инфраструктуры VK/OK
(flutter_webrtc + сигналинг ws2 wss://videowebrtc.okcdn.ru). Протокол
восстановлен реверсом захваченных пакетов и декомпиляцией
ru.ok.android.externcalls.sdk.

Ядро (lib/core/calls/):
- conversation_params.dart — декодер vcp (LZ4-блок), с unit-тестом
- ws2_signaling.dart — клиент сигналинга ws2 (transmit-data, ICE,
  accept-call, hangup, change-media-settings, ping/pong)
- call_session.dart — WebRTC-контроллер для звонящего и вызываемого
- call_controller.dart — глобальный оркестратор: пуш opcode 137,
  исходящие, приём/отклонение, управление активной сессией

Бэкенд:
- CallsModule.initiateCall — инициация исходящего (opcode 78)
- общий LZ4-block декодер вынесен в core/protocol/lz4_block.dart
  (используется и транспортом, и vcp)

UI / обвязка:
- CallScreen переписан под живой CallSession (+ кнопка «свернуть»
  без завершения звонка, возврат повторным нажатием 📞)
- кнопка звонка в шапке диалога
- инициализация CallController на логине, показ входящего по пушу
- дев-тумблер: принудительный сигнал состояния микрофона без
  изменения реального аудиотрека

Прочее:
- зависимость flutter_webrtc ^0.12.5
- Android: RECORD_AUDIO / MODIFY_AUDIO_SETTINGS / BLUETOOTH
2026-06-10 18:21:27 +03:00

48 lines
2.2 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:komet/core/calls/conversation_params.dart';
void main() {
// Реальный `vcp` из захвата входящего звонка (docs/PCAPdroid_10_июн._15_32_04).
const sampleVcp =
'532:8Ux7InRrbiI6IjZ5OHFHbkx4czJ0TXk5d1dOZjZFVms2OEN6QlR3Vmg3OGxBaDZZem4zems9Iiwid3NlIjoid3NzOi8vdmlkZW93ZWJydGMub2tjZG4ucnUvd3My'
'JwD6B2lwIjpbIjE1NS4yMTIuMjA0LjExIiwRAIA5NiJdLCJ3dFMAT2h0dHBVAAWQOjIzNDU2L3d0gQAfdFoAFzh2Y2FbAFVjYWxsc6oAESIgAA9NAAAsOTWoAADSAAnKABEzuQDwFHNyY3AiOiJvbmVfbWUi'
'LCJldCI6MTc4MTA5NDc1Mywic3RufwBWc3R1bjo/AJA1LjgyOjE5MzBWACF0ciMAP3R1ciMAAx0sGgAoMTQ9AEB1IjoicwDyBDEyMzM3Mzo5MTAyMTUzNDUyOTdeAAChAPAMOVhWbTduUnoxMEFuVkZWN2t0M003aGdDL3hR0wGgaXYiOmZhbHNlfQ==';
test('decodes the captured vcp blob', () {
final params = ConversationParams.decode(sampleVcp);
expect(params, isNotNull);
expect(params!.token, '6y8qGnLxs2tMy9wWNf6EVk68CzBTwVh78lAh6Yzn3zk=');
expect(params.wsEndpoint, 'wss://videowebrtc.okcdn.ru/ws2');
expect(params.wtEndpoint, 'https://videowebrtc.okcdn.ru:23456/wt');
expect(params.callsApiEndpoint, 'https://calls.okcdn.ru');
expect(params.clientType, 'one_me');
expect(params.expiresAt, 1781094753);
expect(params.stun, 'stun:155.212.205.82:19302');
expect(params.turn, [
'turn:155.212.205.82:19302',
'turn:155.212.205.14:19302',
]);
expect(params.turnUser, '1781123373:910215345297');
expect(params.turnPassword, '9XVm7nRz10AnVFV7kt3M7hgC/xQ=');
expect(params.isVideo, false);
});
test('builds ice servers for flutter_webrtc', () {
final params = ConversationParams.decode(sampleVcp)!;
final ice = params.iceServers;
expect(ice, hasLength(2));
expect(ice[0]['urls'], 'stun:155.212.205.82:19302');
expect(ice[1]['urls'], isA<List<String>>());
expect(ice[1]['username'], '1781123373:910215345297');
expect(ice[1]['credential'], '9XVm7nRz10AnVFV7kt3M7hgC/xQ=');
});
test('rejects malformed input', () {
expect(ConversationParams.decode('not-a-vcp'), isNull);
expect(ConversationParams.decode(''), isNull);
expect(ConversationParams.decode('0:'), isNull);
});
}