Merge pull request #6 from KometTeam/feature/server-connection-handshake
Feature/server connection handshake
This commit is contained in:
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cmake.sourceDirectory": "/run/media/invisedivine/Drive/! My projects/Komet/linux/runner"
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "Generated.xcconfig"
|
#include "Generated.xcconfig"
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# Uncomment this line to define a global platform for your project
|
||||||
|
# platform :ios, '13.0'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_ios_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_ios_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import '../core/config/config.dart';
|
||||||
|
import '../core/protocol/opcode_map.dart';
|
||||||
|
import '../core/protocol/packet.dart';
|
||||||
|
import '../core/transport/connection.dart';
|
||||||
|
import '../core/transport/dispatcher.dart';
|
||||||
|
import '../core/transport/receiver.dart';
|
||||||
|
import '../core/transport/sender.dart';
|
||||||
|
import '../core/utils/logger.dart';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'package:timezone/data/latest_all.dart' as tz;
|
||||||
|
import 'package:timezone/timezone.dart' as tz;
|
||||||
|
import 'package:flutter_timezone/flutter_timezone.dart';
|
||||||
|
|
||||||
|
enum SessionState {
|
||||||
|
disconnected,
|
||||||
|
connecting,
|
||||||
|
connected,
|
||||||
|
online
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Клиент API.
|
||||||
|
///
|
||||||
|
/// Подключение, хэндшейк, пинг, реконнект.
|
||||||
|
class Api {
|
||||||
|
final Connection _connection = Connection();
|
||||||
|
final PacketReceiver _receiver = PacketReceiver();
|
||||||
|
final PacketSender _sender = PacketSender();
|
||||||
|
final PacketDispatcher _dispatcher = PacketDispatcher();
|
||||||
|
|
||||||
|
SessionState _sessionState = SessionState.disconnected;
|
||||||
|
final _stateController = StreamController<SessionState>.broadcast();
|
||||||
|
|
||||||
|
Stream<SessionState> get stateStream => _stateController.stream;
|
||||||
|
SessionState get state => _sessionState;
|
||||||
|
|
||||||
|
StreamSubscription<Uint8List>? _dataSubscription;
|
||||||
|
StreamSubscription<SocketState>? _socketStateSubscription;
|
||||||
|
Timer? _pingTimer;
|
||||||
|
Timer? _reconnectTimer;
|
||||||
|
int _reconnectAttempts = 0;
|
||||||
|
bool _autoReconnect = false;
|
||||||
|
|
||||||
|
// Публичное API
|
||||||
|
|
||||||
|
/// Подключается к серверу, шлёт хэндшейк, запускает пинг.
|
||||||
|
Future<void> connect() async {
|
||||||
|
if (_sessionState != SessionState.disconnected) return;
|
||||||
|
// Ставим автоматический реконнект и статус подключения
|
||||||
|
_autoReconnect = true;
|
||||||
|
_setSessionState(SessionState.connecting);
|
||||||
|
|
||||||
|
_dataSubscription = _connection.dataStream.listen(_onDataReceived);
|
||||||
|
_socketStateSubscription = _connection.stateStream.listen((socketState) {
|
||||||
|
if (socketState == SocketState.disconnected &&
|
||||||
|
_sessionState != SessionState.disconnected) {
|
||||||
|
_onDisconnected();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _connection.connect(ServerConfig.host, ServerConfig.port);
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('Не удалось подключиться: $e');
|
||||||
|
_cleanup();
|
||||||
|
_setSessionState(SessionState.disconnected);
|
||||||
|
_scheduleReconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_setSessionState(SessionState.connected);
|
||||||
|
_reconnectAttempts = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await sendHandshake();
|
||||||
|
if (response.isOk) {
|
||||||
|
_setSessionState(SessionState.online);
|
||||||
|
_startPinging();
|
||||||
|
logger.i('Сессия онлайн, хэндшейк ок');
|
||||||
|
} else {
|
||||||
|
logger.e('Хэндшейк отклонён: ${response.payload}');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('Ошибка хэндшейка: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отключается без автореконнекта.
|
||||||
|
Future<void> disconnect() async {
|
||||||
|
_autoReconnect = false;
|
||||||
|
_reconnectTimer?.cancel();
|
||||||
|
_cleanup();
|
||||||
|
await _connection.disconnect();
|
||||||
|
_setSessionState(SessionState.disconnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправляет хэндшейк (opcode 6).
|
||||||
|
Future<Packet> sendHandshake() async {
|
||||||
|
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
|
||||||
|
|
||||||
|
// Если платформа Linux или Windows, то ставим DESKTOP, если нет, то проверяем на Android или IOS;
|
||||||
|
String deviceType = (Platform.isLinux || Platform.isWindows) ? "DESKTOP" : (Platform.isAndroid) ? "ANDROID" : "IOS";
|
||||||
|
String osVersion = "";
|
||||||
|
String deviceName = "Unknown";
|
||||||
|
String architecture = "arm64";
|
||||||
|
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
|
||||||
|
final now = DateTime.now();
|
||||||
|
String timezone = "Europe/Moscow";
|
||||||
|
|
||||||
|
tz.initializeTimeZones();
|
||||||
|
final timeZoneName = await FlutterTimezone.getLocalTimezone();
|
||||||
|
timezone = timeZoneName.identifier;
|
||||||
|
|
||||||
|
// На каждой платформе свое инфо, поэтому делаем такую проверку
|
||||||
|
if (Platform.isLinux) {
|
||||||
|
LinuxDeviceInfo linuxInfo = await deviceInfo.linuxInfo;
|
||||||
|
|
||||||
|
osVersion = linuxInfo.name;
|
||||||
|
// Platform.version содержит в себе что-то такое
|
||||||
|
// 3.11.1 (stable) (Tue Feb 24 00:03:07 2026 -0800) on "linux_x64"
|
||||||
|
// Поэтому мы находим '_', прибавляем к его индексу 1 и берем символы до length - 1
|
||||||
|
architecture = Platform.version.substring(Platform.version.indexOf('_') + 1, Platform.version.length - 1);
|
||||||
|
} else if (Platform.isIOS) {
|
||||||
|
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
|
||||||
|
|
||||||
|
osVersion = iosInfo.systemVersion;
|
||||||
|
deviceName = iosInfo.utsname.machine;
|
||||||
|
} else if (Platform.isAndroid) {
|
||||||
|
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
|
||||||
|
|
||||||
|
osVersion = "Android ${androidInfo.version.release}";
|
||||||
|
deviceName = "${androidInfo.manufacturer} ${androidInfo.model}";
|
||||||
|
architecture = androidInfo.supportedAbis.first;
|
||||||
|
} else if (Platform.isWindows) {
|
||||||
|
WindowsDeviceInfo windowsInfo = await deviceInfo.windowsInfo;
|
||||||
|
|
||||||
|
osVersion = windowsInfo.productName;
|
||||||
|
architecture = Platform.version.substring(Platform.version.indexOf('_') + 1, Platform.version.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
print(deviceType);
|
||||||
|
final payload = <dynamic, dynamic>{
|
||||||
|
'mt_instanceid': '550e8400-e29b-41d4-a716-446655440000',
|
||||||
|
'clientSessionId': 42,
|
||||||
|
'deviceId': 'a1b2c3d4e5f6a7b8',
|
||||||
|
'userAgent': {
|
||||||
|
'deviceType': deviceType,
|
||||||
|
// Первые два символа из locale это и есть нужный нам аргумент
|
||||||
|
'locale': "ru",
|
||||||
|
'deviceLocale': Platform.localeName.substring(0, 2),
|
||||||
|
'osVersion': osVersion,
|
||||||
|
'deviceName': deviceName,
|
||||||
|
'appVersion': '26.8.1',
|
||||||
|
'screen': '1920x1080',
|
||||||
|
// 'screen': screenSize.width + 'x' + screenSize.height,
|
||||||
|
'timezone': timezone,
|
||||||
|
'pushDeviceType': 'GCM',
|
||||||
|
'arch': architecture,
|
||||||
|
'buildNumber': 6606,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
print(payload);
|
||||||
|
print(Platform.version);
|
||||||
|
return sendRequest(Opcode.sessionInit, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправляет запрос и ждёт ответ от сервера.
|
||||||
|
Future<Packet> sendRequest(
|
||||||
|
int opcode,
|
||||||
|
Map<dynamic, dynamic> payload,
|
||||||
|
) {
|
||||||
|
final seq = _sender.send(_connection, opcode, payload);
|
||||||
|
return _dispatcher.registerPending(seq).timeout(
|
||||||
|
ServerConfig.requestTimeout,
|
||||||
|
onTimeout: () =>
|
||||||
|
throw TimeoutException('${Opcode.name(opcode)} таймаут'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Вешает обработчик на пуши с указанным опкодом.
|
||||||
|
void registerPushHandler(int opcode, void Function(Packet) handler) {
|
||||||
|
_dispatcher.registerHandler(opcode, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Стрим всех входящих пушей от сервера.
|
||||||
|
Stream<Packet> get pushStream => _dispatcher.pushStream;
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_autoReconnect = false;
|
||||||
|
_reconnectTimer?.cancel();
|
||||||
|
_cleanup();
|
||||||
|
_dispatcher.dispose();
|
||||||
|
_connection.dispose();
|
||||||
|
_stateController.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Внутрянка
|
||||||
|
|
||||||
|
|
||||||
|
void _setSessionState(SessionState state) {
|
||||||
|
if (_sessionState == state) return;
|
||||||
|
_sessionState = state;
|
||||||
|
_stateController.add(state);
|
||||||
|
logger.i('Сессия: ${state.name}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDataReceived(Uint8List data) {
|
||||||
|
for (final packet in _receiver.feed(data)) {
|
||||||
|
_dispatcher.dispatch(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onDisconnected() {
|
||||||
|
_cleanup();
|
||||||
|
_setSessionState(SessionState.disconnected);
|
||||||
|
if (_autoReconnect) _scheduleReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cleanup() {
|
||||||
|
_pingTimer?.cancel();
|
||||||
|
_dataSubscription?.cancel();
|
||||||
|
_socketStateSubscription?.cancel();
|
||||||
|
_dataSubscription = null;
|
||||||
|
_socketStateSubscription = null;
|
||||||
|
_receiver.reset();
|
||||||
|
_dispatcher.clearPending();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _startPinging() {
|
||||||
|
_pingTimer?.cancel();
|
||||||
|
_pingTimer = Timer.periodic(ServerConfig.pingInterval, (_) {
|
||||||
|
if (_connection.isConnected) {
|
||||||
|
_sender.send(_connection, Opcode.ping, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void _scheduleReconnect() {
|
||||||
|
if (_reconnectAttempts >= ServerConfig.maxReconnectAttempts) {
|
||||||
|
logger.e('Лимит попыток реконнекта');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final delaySec = (2 * (1 << _reconnectAttempts)).clamp(2, 30);
|
||||||
|
_reconnectAttempts++;
|
||||||
|
logger.i('Реконнект через ${delaySec}с (попытка $_reconnectAttempts)');
|
||||||
|
|
||||||
|
_reconnectTimer?.cancel();
|
||||||
|
_reconnectTimer = Timer(Duration(seconds: delaySec), connect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
abstract class ServerConfig {
|
||||||
|
static const String host = 'api.oneme.ru';
|
||||||
|
static const int port = 443;
|
||||||
|
static const Duration pingInterval = Duration(seconds: 30);
|
||||||
|
static const Duration requestTimeout = Duration(seconds: 30);
|
||||||
|
static const int maxReconnectAttempts = 50;
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,472 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
class CountryName {
|
||||||
|
final String code; // ISO 3166-1 alpha-2, например "RU"
|
||||||
|
final String en;
|
||||||
|
final String ru;
|
||||||
|
final String phoneCode; // Код страны для звонков, например "+7"
|
||||||
|
final int phoneDigits; // Количество цифр номера после кода страны
|
||||||
|
final String phoneMask; // Маска абонентского номера, например "(###) ###-##-##"
|
||||||
|
final List<int> phoneGroupSizes; // Размеры групп цифр, например [3, 3, 2, 2]
|
||||||
|
final List<String> phoneGroupSeparators; // Разделители вокруг групп, например ["(", ") ", "-", "-", ""]
|
||||||
|
|
||||||
|
const CountryName({
|
||||||
|
required this.code,
|
||||||
|
required this.en,
|
||||||
|
required this.ru,
|
||||||
|
required this.phoneCode,
|
||||||
|
required this.phoneDigits,
|
||||||
|
required this.phoneMask,
|
||||||
|
required this.phoneGroupSizes,
|
||||||
|
required this.phoneGroupSeparators,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Полный список стран (195 государств) с названием на русском и английском.
|
||||||
|
///
|
||||||
|
/// Источник данных:
|
||||||
|
/// https://github.com/stefangabos/world_countries
|
||||||
|
final List<CountryName> allCountries = _buildCountries();
|
||||||
|
|
||||||
|
/// Быстрый доступ по ISO-коду.
|
||||||
|
final Map<String, CountryName> countriesByCode = {
|
||||||
|
for (final country in allCountries) country.code: country,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Пример использования:
|
||||||
|
/// ```dart
|
||||||
|
/// final country = exampleCountryLookup('RU');
|
||||||
|
/// print(country?['ru']); // Россия
|
||||||
|
/// print(country?['en']); // Russian Federation
|
||||||
|
/// ```
|
||||||
|
Map<String, String>? exampleCountryLookup(String code) {
|
||||||
|
final country = countriesByCode[code.toUpperCase()];
|
||||||
|
if (country == null) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'code': country.code,
|
||||||
|
'ru': country.ru,
|
||||||
|
'en': country.en,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
List<CountryName> _buildCountries() {
|
||||||
|
final enList = (jsonDecode(_countriesEnJson) as List)
|
||||||
|
.cast<Map<String, dynamic>>();
|
||||||
|
final ruList = (jsonDecode(_countriesRuJson) as List)
|
||||||
|
.cast<Map<String, dynamic>>();
|
||||||
|
|
||||||
|
final ruByCode = <String, String>{
|
||||||
|
for (final item in ruList)
|
||||||
|
(item['alpha2'] as String).toUpperCase(): item['name'] as String,
|
||||||
|
};
|
||||||
|
|
||||||
|
final countries = enList.map((item) {
|
||||||
|
final code = (item['alpha2'] as String).toUpperCase();
|
||||||
|
final en = item['name'] as String;
|
||||||
|
final ru = ruByCode[code] ?? en;
|
||||||
|
|
||||||
|
final phoneCode = item['phoneCode'] as String;
|
||||||
|
final phoneDigits = item['phoneDigits'] as int;
|
||||||
|
final phoneMask = item['phoneMask'] as String;
|
||||||
|
final phoneGroupSizes = (item['phoneGroupSizes'] as List).cast<int>();
|
||||||
|
final phoneGroupSeparators = (item['phoneGroupSeparators'] as List).cast<String>();
|
||||||
|
|
||||||
|
return CountryName(code: code, en: en, ru: ru, phoneCode: phoneCode, phoneDigits: phoneDigits, phoneMask: phoneMask, phoneGroupSizes: phoneGroupSizes, phoneGroupSeparators: phoneGroupSeparators);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
countries.sort((a, b) => a.en.compareTo(b.en));
|
||||||
|
return countries;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String _countriesEnJson = '''[
|
||||||
|
{"id": 4, "alpha2": "af", "alpha3": "afg", "name": "Afghanistan", "phoneCode": "+93", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 8, "alpha2": "al", "alpha3": "alb", "name": "Albania", "phoneCode": "+355", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 12, "alpha2": "dz", "alpha3": "dza", "name": "Algeria", "phoneCode": "+213", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 20, "alpha2": "ad", "alpha3": "and", "name": "Andorra", "phoneCode": "+376", "phoneDigits": 6, "phoneMask": "######", "phoneGroupSizes": [6], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 24, "alpha2": "ao", "alpha3": "ago", "name": "Angola", "phoneCode": "+244", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 28, "alpha2": "ag", "alpha3": "atg", "name": "Antigua and Barbuda", "phoneCode": "+1268", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 32, "alpha2": "ar", "alpha3": "arg", "name": "Argentina", "phoneCode": "+54", "phoneDigits": 10, "phoneMask": "(##) ####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 51, "alpha2": "am", "alpha3": "arm", "name": "Armenia", "phoneCode": "+374", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 36, "alpha2": "au", "alpha3": "aus", "name": "Australia", "phoneCode": "+61", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 40, "alpha2": "at", "alpha3": "aut", "name": "Austria", "phoneCode": "+43", "phoneDigits": 10, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 31, "alpha2": "az", "alpha3": "aze", "name": "Azerbaijan", "phoneCode": "+994", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 44, "alpha2": "bs", "alpha3": "bhs", "name": "Bahamas", "phoneCode": "+1242", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 48, "alpha2": "bh", "alpha3": "bhr", "name": "Bahrain", "phoneCode": "+973", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 50, "alpha2": "bd", "alpha3": "bgd", "name": "Bangladesh", "phoneCode": "+880", "phoneDigits": 10, "phoneMask": "####-######", "phoneGroupSizes": [4, 6], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 52, "alpha2": "bb", "alpha3": "brb", "name": "Barbados", "phoneCode": "+1246", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 112, "alpha2": "by", "alpha3": "blr", "name": "Belarus", "phoneCode": "+375", "phoneDigits": 9, "phoneMask": "## ###-##-##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", "-", "-", ""]}
|
||||||
|
{"id": 56, "alpha2": "be", "alpha3": "bel", "name": "Belgium", "phoneCode": "+32", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 84, "alpha2": "bz", "alpha3": "blz", "name": "Belize", "phoneCode": "+501", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 204, "alpha2": "bj", "alpha3": "ben", "name": "Benin", "phoneCode": "+229", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 64, "alpha2": "bt", "alpha3": "btn", "name": "Bhutan", "phoneCode": "+975", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 68, "alpha2": "bo", "alpha3": "bol", "name": "Bolivia, Plurinational State of", "phoneCode": "+591", "phoneDigits": 8, "phoneMask": "########", "phoneGroupSizes": [8], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 70, "alpha2": "ba", "alpha3": "bih", "name": "Bosnia and Herzegovina", "phoneCode": "+387", "phoneDigits": 8, "phoneMask": "## ###-###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", "-", ""]}
|
||||||
|
{"id": 72, "alpha2": "bw", "alpha3": "bwa", "name": "Botswana", "phoneCode": "+267", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 76, "alpha2": "br", "alpha3": "bra", "name": "Brazil", "phoneCode": "+55", "phoneDigits": 11, "phoneMask": "(##) #####-####", "phoneGroupSizes": [2, 5, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 96, "alpha2": "bn", "alpha3": "brn", "name": "Brunei Darussalam", "phoneCode": "+673", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 100, "alpha2": "bg", "alpha3": "bgr", "name": "Bulgaria", "phoneCode": "+359", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 854, "alpha2": "bf", "alpha3": "bfa", "name": "Burkina Faso", "phoneCode": "+226", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 108, "alpha2": "bi", "alpha3": "bdi", "name": "Burundi", "phoneCode": "+257", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 132, "alpha2": "cv", "alpha3": "cpv", "name": "Cabo Verde", "phoneCode": "+238", "phoneDigits": 7, "phoneMask": "### ## ##", "phoneGroupSizes": [3, 2, 2], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 116, "alpha2": "kh", "alpha3": "khm", "name": "Cambodia", "phoneCode": "+855", "phoneDigits": 9, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 120, "alpha2": "cm", "alpha3": "cmr", "name": "Cameroon", "phoneCode": "+237", "phoneDigits": 9, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 124, "alpha2": "ca", "alpha3": "can", "name": "Canada", "phoneCode": "+1", "phoneDigits": 10, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 140, "alpha2": "cf", "alpha3": "caf", "name": "Central African Republic", "phoneCode": "+236", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 148, "alpha2": "td", "alpha3": "tcd", "name": "Chad", "phoneCode": "+235", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 152, "alpha2": "cl", "alpha3": "chl", "name": "Chile", "phoneCode": "+56", "phoneDigits": 9, "phoneMask": "# #### ####", "phoneGroupSizes": [1, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 156, "alpha2": "cn", "alpha3": "chn", "name": "China", "phoneCode": "+86", "phoneDigits": 11, "phoneMask": "### #### ####", "phoneGroupSizes": [3, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 170, "alpha2": "co", "alpha3": "col", "name": "Colombia", "phoneCode": "+57", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 174, "alpha2": "km", "alpha3": "com", "name": "Comoros", "phoneCode": "+269", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 178, "alpha2": "cg", "alpha3": "cog", "name": "Congo", "phoneCode": "+242", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 180, "alpha2": "cd", "alpha3": "cod", "name": "Congo, Democratic Republic of the", "phoneCode": "+243", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 188, "alpha2": "cr", "alpha3": "cri", "name": "Costa Rica", "phoneCode": "+506", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 384, "alpha2": "ci", "alpha3": "civ", "name": "Côte d'Ivoire", "phoneCode": "+225", "phoneDigits": 10, "phoneMask": "## ## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 191, "alpha2": "hr", "alpha3": "hrv", "name": "Croatia", "phoneCode": "+385", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 192, "alpha2": "cu", "alpha3": "cub", "name": "Cuba", "phoneCode": "+53", "phoneDigits": 8, "phoneMask": "########", "phoneGroupSizes": [8], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 196, "alpha2": "cy", "alpha3": "cyp", "name": "Cyprus", "phoneCode": "+357", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 203, "alpha2": "cz", "alpha3": "cze", "name": "Czechia", "phoneCode": "+420", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 208, "alpha2": "dk", "alpha3": "dnk", "name": "Denmark", "phoneCode": "+45", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 262, "alpha2": "dj", "alpha3": "dji", "name": "Djibouti", "phoneCode": "+253", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 212, "alpha2": "dm", "alpha3": "dma", "name": "Dominica", "phoneCode": "+1767", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 214, "alpha2": "do", "alpha3": "dom", "name": "Dominican Republic", "phoneCode": "+1809", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 218, "alpha2": "ec", "alpha3": "ecu", "name": "Ecuador", "phoneCode": "+593", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 818, "alpha2": "eg", "alpha3": "egy", "name": "Egypt", "phoneCode": "+20", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 222, "alpha2": "sv", "alpha3": "slv", "name": "El Salvador", "phoneCode": "+503", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 226, "alpha2": "gq", "alpha3": "gnq", "name": "Equatorial Guinea", "phoneCode": "+240", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 232, "alpha2": "er", "alpha3": "eri", "name": "Eritrea", "phoneCode": "+291", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 233, "alpha2": "ee", "alpha3": "est", "name": "Estonia", "phoneCode": "+372", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 748, "alpha2": "sz", "alpha3": "swz", "name": "Eswatini", "phoneCode": "+268", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 231, "alpha2": "et", "alpha3": "eth", "name": "Ethiopia", "phoneCode": "+251", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 242, "alpha2": "fj", "alpha3": "fji", "name": "Fiji", "phoneCode": "+679", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 246, "alpha2": "fi", "alpha3": "fin", "name": "Finland", "phoneCode": "+358", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 250, "alpha2": "fr", "alpha3": "fra", "name": "France", "phoneCode": "+33", "phoneDigits": 9, "phoneMask": "# ## ## ## ##", "phoneGroupSizes": [1, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 266, "alpha2": "ga", "alpha3": "gab", "name": "Gabon", "phoneCode": "+241", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 270, "alpha2": "gm", "alpha3": "gmb", "name": "Gambia", "phoneCode": "+220", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 268, "alpha2": "ge", "alpha3": "geo", "name": "Georgia", "phoneCode": "+995", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 276, "alpha2": "de", "alpha3": "deu", "name": "Germany", "phoneCode": "+49", "phoneDigits": 10, "phoneMask": "#### #######", "phoneGroupSizes": [4, 7], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 288, "alpha2": "gh", "alpha3": "gha", "name": "Ghana", "phoneCode": "+233", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 300, "alpha2": "gr", "alpha3": "grc", "name": "Greece", "phoneCode": "+30", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 308, "alpha2": "gd", "alpha3": "grd", "name": "Grenada", "phoneCode": "+1473", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 320, "alpha2": "gt", "alpha3": "gtm", "name": "Guatemala", "phoneCode": "+502", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 324, "alpha2": "gn", "alpha3": "gin", "name": "Guinea", "phoneCode": "+224", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 624, "alpha2": "gw", "alpha3": "gnb", "name": "Guinea-Bissau", "phoneCode": "+245", "phoneDigits": 9, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 328, "alpha2": "gy", "alpha3": "guy", "name": "Guyana", "phoneCode": "+592", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 332, "alpha2": "ht", "alpha3": "hti", "name": "Haiti", "phoneCode": "+509", "phoneDigits": 8, "phoneMask": "## ## ####", "phoneGroupSizes": [2, 2, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 340, "alpha2": "hn", "alpha3": "hnd", "name": "Honduras", "phoneCode": "+504", "phoneDigits": 8, "phoneMask": "####-####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 348, "alpha2": "hu", "alpha3": "hun", "name": "Hungary", "phoneCode": "+36", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 352, "alpha2": "is", "alpha3": "isl", "name": "Iceland", "phoneCode": "+354", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 356, "alpha2": "in", "alpha3": "ind", "name": "India", "phoneCode": "+91", "phoneDigits": 10, "phoneMask": "##### #####", "phoneGroupSizes": [5, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 360, "alpha2": "id", "alpha3": "idn", "name": "Indonesia", "phoneCode": "+62", "phoneDigits": 10, "phoneMask": "### #### ####", "phoneGroupSizes": [3, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 364, "alpha2": "ir", "alpha3": "irn", "name": "Iran, Islamic Republic of", "phoneCode": "+98", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 368, "alpha2": "iq", "alpha3": "irq", "name": "Iraq", "phoneCode": "+964", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 372, "alpha2": "ie", "alpha3": "irl", "name": "Ireland", "phoneCode": "+353", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 376, "alpha2": "il", "alpha3": "isr", "name": "Israel", "phoneCode": "+972", "phoneDigits": 9, "phoneMask": "##-###-####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 380, "alpha2": "it", "alpha3": "ita", "name": "Italy", "phoneCode": "+39", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 388, "alpha2": "jm", "alpha3": "jam", "name": "Jamaica", "phoneCode": "+1876", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 392, "alpha2": "jp", "alpha3": "jpn", "name": "Japan", "phoneCode": "+81", "phoneDigits": 10, "phoneMask": "##-####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 400, "alpha2": "jo", "alpha3": "jor", "name": "Jordan", "phoneCode": "+962", "phoneDigits": 9, "phoneMask": "# #### ####", "phoneGroupSizes": [1, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 398, "alpha2": "kz", "alpha3": "kaz", "name": "Kazakhstan", "phoneCode": "+7", "phoneDigits": 10, "phoneMask": "(###) ###-##-##", "phoneGroupSizes": [3, 3, 2, 2], "phoneGroupSeparators": ["(", ") ", "-", "-", ""]}
|
||||||
|
{"id": 404, "alpha2": "ke", "alpha3": "ken", "name": "Kenya", "phoneCode": "+254", "phoneDigits": 9, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 296, "alpha2": "ki", "alpha3": "kir", "name": "Kiribati", "phoneCode": "+686", "phoneDigits": 8, "phoneMask": "## ######", "phoneGroupSizes": [2, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 408, "alpha2": "kp", "alpha3": "prk", "name": "Korea, Democratic People's Republic of", "phoneCode": "+850", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 410, "alpha2": "kr", "alpha3": "kor", "name": "Korea, Republic of", "phoneCode": "+82", "phoneDigits": 10, "phoneMask": "##-####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 414, "alpha2": "kw", "alpha3": "kwt", "name": "Kuwait", "phoneCode": "+965", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 417, "alpha2": "kg", "alpha3": "kgz", "name": "Kyrgyzstan", "phoneCode": "+996", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 418, "alpha2": "la", "alpha3": "lao", "name": "Lao People's Democratic Republic", "phoneCode": "+856", "phoneDigits": 9, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 428, "alpha2": "lv", "alpha3": "lva", "name": "Latvia", "phoneCode": "+371", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 422, "alpha2": "lb", "alpha3": "lbn", "name": "Lebanon", "phoneCode": "+961", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 426, "alpha2": "ls", "alpha3": "lso", "name": "Lesotho", "phoneCode": "+266", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 430, "alpha2": "lr", "alpha3": "lbr", "name": "Liberia", "phoneCode": "+231", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 434, "alpha2": "ly", "alpha3": "lby", "name": "Libya", "phoneCode": "+218", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 438, "alpha2": "li", "alpha3": "lie", "name": "Liechtenstein", "phoneCode": "+423", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 440, "alpha2": "lt", "alpha3": "ltu", "name": "Lithuania", "phoneCode": "+370", "phoneDigits": 8, "phoneMask": "### #####", "phoneGroupSizes": [3, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 442, "alpha2": "lu", "alpha3": "lux", "name": "Luxembourg", "phoneCode": "+352", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 450, "alpha2": "mg", "alpha3": "mdg", "name": "Madagascar", "phoneCode": "+261", "phoneDigits": 9, "phoneMask": "## ## ### ##", "phoneGroupSizes": [2, 2, 3, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 454, "alpha2": "mw", "alpha3": "mwi", "name": "Malawi", "phoneCode": "+265", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 458, "alpha2": "my", "alpha3": "mys", "name": "Malaysia", "phoneCode": "+60", "phoneDigits": 10, "phoneMask": "##-#### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 462, "alpha2": "mv", "alpha3": "mdv", "name": "Maldives", "phoneCode": "+960", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 466, "alpha2": "ml", "alpha3": "mli", "name": "Mali", "phoneCode": "+223", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 470, "alpha2": "mt", "alpha3": "mlt", "name": "Malta", "phoneCode": "+356", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 584, "alpha2": "mh", "alpha3": "mhl", "name": "Marshall Islands", "phoneCode": "+692", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 478, "alpha2": "mr", "alpha3": "mrt", "name": "Mauritania", "phoneCode": "+222", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 480, "alpha2": "mu", "alpha3": "mus", "name": "Mauritius", "phoneCode": "+230", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 484, "alpha2": "mx", "alpha3": "mex", "name": "Mexico", "phoneCode": "+52", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 583, "alpha2": "fm", "alpha3": "fsm", "name": "Micronesia, Federated States of", "phoneCode": "+691", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 498, "alpha2": "md", "alpha3": "mda", "name": "Moldova, Republic of", "phoneCode": "+373", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 492, "alpha2": "mc", "alpha3": "mco", "name": "Monaco", "phoneCode": "+377", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 496, "alpha2": "mn", "alpha3": "mng", "name": "Mongolia", "phoneCode": "+976", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 499, "alpha2": "me", "alpha3": "mne", "name": "Montenegro", "phoneCode": "+382", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 504, "alpha2": "ma", "alpha3": "mar", "name": "Morocco", "phoneCode": "+212", "phoneDigits": 9, "phoneMask": "###-###-###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 508, "alpha2": "mz", "alpha3": "moz", "name": "Mozambique", "phoneCode": "+258", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 104, "alpha2": "mm", "alpha3": "mmr", "name": "Myanmar", "phoneCode": "+95", "phoneDigits": 9, "phoneMask": "# ### ####", "phoneGroupSizes": [1, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 516, "alpha2": "na", "alpha3": "nam", "name": "Namibia", "phoneCode": "+264", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 520, "alpha2": "nr", "alpha3": "nru", "name": "Nauru", "phoneCode": "+674", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 524, "alpha2": "np", "alpha3": "npl", "name": "Nepal", "phoneCode": "+977", "phoneDigits": 10, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 528, "alpha2": "nl", "alpha3": "nld", "name": "Netherlands", "phoneCode": "+31", "phoneDigits": 9, "phoneMask": "# ## ## ## ##", "phoneGroupSizes": [1, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 554, "alpha2": "nz", "alpha3": "nzl", "name": "New Zealand", "phoneCode": "+64", "phoneDigits": 9, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 558, "alpha2": "ni", "alpha3": "nic", "name": "Nicaragua", "phoneCode": "+505", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 562, "alpha2": "ne", "alpha3": "ner", "name": "Niger", "phoneCode": "+227", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 566, "alpha2": "ng", "alpha3": "nga", "name": "Nigeria", "phoneCode": "+234", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 807, "alpha2": "mk", "alpha3": "mkd", "name": "North Macedonia", "phoneCode": "+389", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 578, "alpha2": "no", "alpha3": "nor", "name": "Norway", "phoneCode": "+47", "phoneDigits": 8, "phoneMask": "### ## ###", "phoneGroupSizes": [3, 2, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 512, "alpha2": "om", "alpha3": "omn", "name": "Oman", "phoneCode": "+968", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 586, "alpha2": "pk", "alpha3": "pak", "name": "Pakistan", "phoneCode": "+92", "phoneDigits": 10, "phoneMask": "### #######", "phoneGroupSizes": [3, 7], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 585, "alpha2": "pw", "alpha3": "plw", "name": "Palau", "phoneCode": "+680", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 591, "alpha2": "pa", "alpha3": "pan", "name": "Panama", "phoneCode": "+507", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 598, "alpha2": "pg", "alpha3": "png", "name": "Papua New Guinea", "phoneCode": "+675", "phoneDigits": 8, "phoneMask": "### ## ###", "phoneGroupSizes": [3, 2, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 600, "alpha2": "py", "alpha3": "pry", "name": "Paraguay", "phoneCode": "+595", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 604, "alpha2": "pe", "alpha3": "per", "name": "Peru", "phoneCode": "+51", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 608, "alpha2": "ph", "alpha3": "phl", "name": "Philippines", "phoneCode": "+63", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 616, "alpha2": "pl", "alpha3": "pol", "name": "Poland", "phoneCode": "+48", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 620, "alpha2": "pt", "alpha3": "prt", "name": "Portugal", "phoneCode": "+351", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 634, "alpha2": "qa", "alpha3": "qat", "name": "Qatar", "phoneCode": "+974", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 642, "alpha2": "ro", "alpha3": "rou", "name": "Romania", "phoneCode": "+40", "phoneDigits": 10, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 643, "alpha2": "ru", "alpha3": "rus", "name": "Russian Federation", "phoneCode": "+7", "phoneDigits": 10, "phoneMask": "(###) ###-##-##", "phoneGroupSizes": [3, 3, 2, 2], "phoneGroupSeparators": ["(", ") ", "-", "-", ""]}
|
||||||
|
{"id": 646, "alpha2": "rw", "alpha3": "rwa", "name": "Rwanda", "phoneCode": "+250", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 659, "alpha2": "kn", "alpha3": "kna", "name": "Saint Kitts and Nevis", "phoneCode": "+1869", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 662, "alpha2": "lc", "alpha3": "lca", "name": "Saint Lucia", "phoneCode": "+1758", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 670, "alpha2": "vc", "alpha3": "vct", "name": "Saint Vincent and the Grenadines", "phoneCode": "+1784", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 882, "alpha2": "ws", "alpha3": "wsm", "name": "Samoa", "phoneCode": "+685", "phoneDigits": 7, "phoneMask": "## #####", "phoneGroupSizes": [2, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 674, "alpha2": "sm", "alpha3": "smr", "name": "San Marino", "phoneCode": "+378", "phoneDigits": 10, "phoneMask": "## ## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 678, "alpha2": "st", "alpha3": "stp", "name": "Sao Tome and Principe", "phoneCode": "+239", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 682, "alpha2": "sa", "alpha3": "sau", "name": "Saudi Arabia", "phoneCode": "+966", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 686, "alpha2": "sn", "alpha3": "sen", "name": "Senegal", "phoneCode": "+221", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 688, "alpha2": "rs", "alpha3": "srb", "name": "Serbia", "phoneCode": "+381", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 690, "alpha2": "sc", "alpha3": "syc", "name": "Seychelles", "phoneCode": "+248", "phoneDigits": 7, "phoneMask": "# ### ###", "phoneGroupSizes": [1, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 694, "alpha2": "sl", "alpha3": "sle", "name": "Sierra Leone", "phoneCode": "+232", "phoneDigits": 8, "phoneMask": "## ######", "phoneGroupSizes": [2, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 702, "alpha2": "sg", "alpha3": "sgp", "name": "Singapore", "phoneCode": "+65", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 703, "alpha2": "sk", "alpha3": "svk", "name": "Slovakia", "phoneCode": "+421", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 705, "alpha2": "si", "alpha3": "svn", "name": "Slovenia", "phoneCode": "+386", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 90, "alpha2": "sb", "alpha3": "slb", "name": "Solomon Islands", "phoneCode": "+677", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 706, "alpha2": "so", "alpha3": "som", "name": "Somalia", "phoneCode": "+252", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 710, "alpha2": "za", "alpha3": "zaf", "name": "South Africa", "phoneCode": "+27", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 728, "alpha2": "ss", "alpha3": "ssd", "name": "South Sudan", "phoneCode": "+211", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 724, "alpha2": "es", "alpha3": "esp", "name": "Spain", "phoneCode": "+34", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 144, "alpha2": "lk", "alpha3": "lka", "name": "Sri Lanka", "phoneCode": "+94", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 729, "alpha2": "sd", "alpha3": "sdn", "name": "Sudan", "phoneCode": "+249", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 740, "alpha2": "sr", "alpha3": "sur", "name": "Suriname", "phoneCode": "+597", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 752, "alpha2": "se", "alpha3": "swe", "name": "Sweden", "phoneCode": "+46", "phoneDigits": 9, "phoneMask": "##-### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", "-", " ", " ", ""]}
|
||||||
|
{"id": 756, "alpha2": "ch", "alpha3": "che", "name": "Switzerland", "phoneCode": "+41", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 760, "alpha2": "sy", "alpha3": "syr", "name": "Syrian Arab Republic", "phoneCode": "+963", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 762, "alpha2": "tj", "alpha3": "tjk", "name": "Tajikistan", "phoneCode": "+992", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 834, "alpha2": "tz", "alpha3": "tza", "name": "Tanzania, United Republic of", "phoneCode": "+255", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 764, "alpha2": "th", "alpha3": "tha", "name": "Thailand", "phoneCode": "+66", "phoneDigits": 9, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 626, "alpha2": "tl", "alpha3": "tls", "name": "Timor-Leste", "phoneCode": "+670", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 768, "alpha2": "tg", "alpha3": "tgo", "name": "Togo", "phoneCode": "+228", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 776, "alpha2": "to", "alpha3": "ton", "name": "Tonga", "phoneCode": "+676", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 780, "alpha2": "tt", "alpha3": "tto", "name": "Trinidad and Tobago", "phoneCode": "+1868", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 788, "alpha2": "tn", "alpha3": "tun", "name": "Tunisia", "phoneCode": "+216", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 792, "alpha2": "tr", "alpha3": "tur", "name": "Türkiye", "phoneCode": "+90", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 795, "alpha2": "tm", "alpha3": "tkm", "name": "Turkmenistan", "phoneCode": "+993", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 798, "alpha2": "tv", "alpha3": "tuv", "name": "Tuvalu", "phoneCode": "+688", "phoneDigits": 6, "phoneMask": "######", "phoneGroupSizes": [6], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 800, "alpha2": "ug", "alpha3": "uga", "name": "Uganda", "phoneCode": "+256", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 804, "alpha2": "ua", "alpha3": "ukr", "name": "Ukraine", "phoneCode": "+380", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 784, "alpha2": "ae", "alpha3": "are", "name": "United Arab Emirates", "phoneCode": "+971", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 826, "alpha2": "gb", "alpha3": "gbr", "name": "United Kingdom of Great Britain and Northern Ireland", "phoneCode": "+44", "phoneDigits": 10, "phoneMask": "#### ######", "phoneGroupSizes": [4, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 840, "alpha2": "us", "alpha3": "usa", "name": "United States of America", "phoneCode": "+1", "phoneDigits": 10, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 858, "alpha2": "uy", "alpha3": "ury", "name": "Uruguay", "phoneCode": "+598", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 860, "alpha2": "uz", "alpha3": "uzb", "name": "Uzbekistan", "phoneCode": "+998", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 548, "alpha2": "vu", "alpha3": "vut", "name": "Vanuatu", "phoneCode": "+678", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 862, "alpha2": "ve", "alpha3": "ven", "name": "Venezuela, Bolivarian Republic of", "phoneCode": "+58", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 704, "alpha2": "vn", "alpha3": "vnm", "name": "Viet Nam", "phoneCode": "+84", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 887, "alpha2": "ye", "alpha3": "yem", "name": "Yemen", "phoneCode": "+967", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 894, "alpha2": "zm", "alpha3": "zmb", "name": "Zambia", "phoneCode": "+260", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 716, "alpha2": "zw", "alpha3": "zwe", "name": "Zimbabwe", "phoneCode": "+263", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
]''';
|
||||||
|
|
||||||
|
const String _countriesRuJson = '''[
|
||||||
|
{"id": 36, "alpha2": "au", "alpha3": "aus", "name": "Австралия", "phoneCode": "+61", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 40, "alpha2": "at", "alpha3": "aut", "name": "Австрия", "phoneCode": "+43", "phoneDigits": 10, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 31, "alpha2": "az", "alpha3": "aze", "name": "Азербайджан", "phoneCode": "+994", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 8, "alpha2": "al", "alpha3": "alb", "name": "Албания", "phoneCode": "+355", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 12, "alpha2": "dz", "alpha3": "dza", "name": "Алжир", "phoneCode": "+213", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 24, "alpha2": "ao", "alpha3": "ago", "name": "Ангола", "phoneCode": "+244", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 20, "alpha2": "ad", "alpha3": "and", "name": "Андорра", "phoneCode": "+376", "phoneDigits": 6, "phoneMask": "######", "phoneGroupSizes": [6], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 28, "alpha2": "ag", "alpha3": "atg", "name": "Антигуа и Барбуда", "phoneCode": "+1268", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 32, "alpha2": "ar", "alpha3": "arg", "name": "Аргентина", "phoneCode": "+54", "phoneDigits": 10, "phoneMask": "(##) ####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 51, "alpha2": "am", "alpha3": "arm", "name": "Армения", "phoneCode": "+374", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 4, "alpha2": "af", "alpha3": "afg", "name": "Афганистан", "phoneCode": "+93", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 44, "alpha2": "bs", "alpha3": "bhs", "name": "Багамские Острова", "phoneCode": "+1242", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 50, "alpha2": "bd", "alpha3": "bgd", "name": "Бангладеш", "phoneCode": "+880", "phoneDigits": 10, "phoneMask": "####-######", "phoneGroupSizes": [4, 6], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 52, "alpha2": "bb", "alpha3": "brb", "name": "Барбадос", "phoneCode": "+1246", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 48, "alpha2": "bh", "alpha3": "bhr", "name": "Бахрейн", "phoneCode": "+973", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 84, "alpha2": "bz", "alpha3": "blz", "name": "Белиз", "phoneCode": "+501", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 112, "alpha2": "by", "alpha3": "blr", "name": "Беларусь", "phoneCode": "+375", "phoneDigits": 9, "phoneMask": "## ###-##-##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", "-", "-", ""]}
|
||||||
|
{"id": 56, "alpha2": "be", "alpha3": "bel", "name": "Бельгия", "phoneCode": "+32", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 204, "alpha2": "bj", "alpha3": "ben", "name": "Бенин", "phoneCode": "+229", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 100, "alpha2": "bg", "alpha3": "bgr", "name": "Болгария", "phoneCode": "+359", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 68, "alpha2": "bo", "alpha3": "bol", "name": "Боливия", "phoneCode": "+591", "phoneDigits": 8, "phoneMask": "########", "phoneGroupSizes": [8], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 70, "alpha2": "ba", "alpha3": "bih", "name": "Босния и Герцеговина", "phoneCode": "+387", "phoneDigits": 8, "phoneMask": "## ###-###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", "-", ""]}
|
||||||
|
{"id": 72, "alpha2": "bw", "alpha3": "bwa", "name": "Ботсвана", "phoneCode": "+267", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 76, "alpha2": "br", "alpha3": "bra", "name": "Бразилия", "phoneCode": "+55", "phoneDigits": 11, "phoneMask": "(##) #####-####", "phoneGroupSizes": [2, 5, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 96, "alpha2": "bn", "alpha3": "brn", "name": "Бруней", "phoneCode": "+673", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 854, "alpha2": "bf", "alpha3": "bfa", "name": "Буркина-Фасо", "phoneCode": "+226", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 108, "alpha2": "bi", "alpha3": "bdi", "name": "Бурунди", "phoneCode": "+257", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 64, "alpha2": "bt", "alpha3": "btn", "name": "Бутан", "phoneCode": "+975", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 548, "alpha2": "vu", "alpha3": "vut", "name": "Вануату", "phoneCode": "+678", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 826, "alpha2": "gb", "alpha3": "gbr", "name": "Великобритания", "phoneCode": "+44", "phoneDigits": 10, "phoneMask": "#### ######", "phoneGroupSizes": [4, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 348, "alpha2": "hu", "alpha3": "hun", "name": "Венгрия", "phoneCode": "+36", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 862, "alpha2": "ve", "alpha3": "ven", "name": "Венесуэла", "phoneCode": "+58", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 626, "alpha2": "tl", "alpha3": "tls", "name": "Восточный Тимор", "phoneCode": "+670", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 704, "alpha2": "vn", "alpha3": "vnm", "name": "Вьетнам", "phoneCode": "+84", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 266, "alpha2": "ga", "alpha3": "gab", "name": "Габон", "phoneCode": "+241", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 332, "alpha2": "ht", "alpha3": "hti", "name": "Гаити", "phoneCode": "+509", "phoneDigits": 8, "phoneMask": "## ## ####", "phoneGroupSizes": [2, 2, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 328, "alpha2": "gy", "alpha3": "guy", "name": "Гайана", "phoneCode": "+592", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 270, "alpha2": "gm", "alpha3": "gmb", "name": "Гамбия", "phoneCode": "+220", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 288, "alpha2": "gh", "alpha3": "gha", "name": "Гана", "phoneCode": "+233", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 320, "alpha2": "gt", "alpha3": "gtm", "name": "Гватемала", "phoneCode": "+502", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 324, "alpha2": "gn", "alpha3": "gin", "name": "Гвинея", "phoneCode": "+224", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 624, "alpha2": "gw", "alpha3": "gnb", "name": "Гвинея-Бисау", "phoneCode": "+245", "phoneDigits": 9, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 276, "alpha2": "de", "alpha3": "deu", "name": "Германия", "phoneCode": "+49", "phoneDigits": 10, "phoneMask": "#### #######", "phoneGroupSizes": [4, 7], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 340, "alpha2": "hn", "alpha3": "hnd", "name": "Гондурас", "phoneCode": "+504", "phoneDigits": 8, "phoneMask": "####-####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 308, "alpha2": "gd", "alpha3": "grd", "name": "Гренада", "phoneCode": "+1473", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 300, "alpha2": "gr", "alpha3": "grc", "name": "Греция", "phoneCode": "+30", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 268, "alpha2": "ge", "alpha3": "geo", "name": "Грузия", "phoneCode": "+995", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 208, "alpha2": "dk", "alpha3": "dnk", "name": "Дания", "phoneCode": "+45", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 262, "alpha2": "dj", "alpha3": "dji", "name": "Джибути", "phoneCode": "+253", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 212, "alpha2": "dm", "alpha3": "dma", "name": "Доминика", "phoneCode": "+1767", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 214, "alpha2": "do", "alpha3": "dom", "name": "Доминиканская Республика", "phoneCode": "+1809", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 180, "alpha2": "cd", "alpha3": "cod", "name": "ДР Конго", "phoneCode": "+243", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 818, "alpha2": "eg", "alpha3": "egy", "name": "Египет", "phoneCode": "+20", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 894, "alpha2": "zm", "alpha3": "zmb", "name": "Замбия", "phoneCode": "+260", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 716, "alpha2": "zw", "alpha3": "zwe", "name": "Зимбабве", "phoneCode": "+263", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 376, "alpha2": "il", "alpha3": "isr", "name": "Израиль", "phoneCode": "+972", "phoneDigits": 9, "phoneMask": "##-###-####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 356, "alpha2": "in", "alpha3": "ind", "name": "Индия", "phoneCode": "+91", "phoneDigits": 10, "phoneMask": "##### #####", "phoneGroupSizes": [5, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 360, "alpha2": "id", "alpha3": "idn", "name": "Индонезия", "phoneCode": "+62", "phoneDigits": 10, "phoneMask": "### #### ####", "phoneGroupSizes": [3, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 400, "alpha2": "jo", "alpha3": "jor", "name": "Иордания", "phoneCode": "+962", "phoneDigits": 9, "phoneMask": "# #### ####", "phoneGroupSizes": [1, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 368, "alpha2": "iq", "alpha3": "irq", "name": "Ирак", "phoneCode": "+964", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 364, "alpha2": "ir", "alpha3": "irn", "name": "Иран", "phoneCode": "+98", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 372, "alpha2": "ie", "alpha3": "irl", "name": "Ирландия", "phoneCode": "+353", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 352, "alpha2": "is", "alpha3": "isl", "name": "Исландия", "phoneCode": "+354", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 724, "alpha2": "es", "alpha3": "esp", "name": "Испания", "phoneCode": "+34", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 380, "alpha2": "it", "alpha3": "ita", "name": "Италия", "phoneCode": "+39", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 887, "alpha2": "ye", "alpha3": "yem", "name": "Йемен", "phoneCode": "+967", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 132, "alpha2": "cv", "alpha3": "cpv", "name": "Кабо-Верде", "phoneCode": "+238", "phoneDigits": 7, "phoneMask": "### ## ##", "phoneGroupSizes": [3, 2, 2], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 398, "alpha2": "kz", "alpha3": "kaz", "name": "Казахстан", "phoneCode": "+7", "phoneDigits": 10, "phoneMask": "(###) ###-##-##", "phoneGroupSizes": [3, 3, 2, 2], "phoneGroupSeparators": ["(", ") ", "-", "-", ""]}
|
||||||
|
{"id": 116, "alpha2": "kh", "alpha3": "khm", "name": "Камбоджа", "phoneCode": "+855", "phoneDigits": 9, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 120, "alpha2": "cm", "alpha3": "cmr", "name": "Камерун", "phoneCode": "+237", "phoneDigits": 9, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 124, "alpha2": "ca", "alpha3": "can", "name": "Канада", "phoneCode": "+1", "phoneDigits": 10, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 634, "alpha2": "qa", "alpha3": "qat", "name": "Катар", "phoneCode": "+974", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 404, "alpha2": "ke", "alpha3": "ken", "name": "Кения", "phoneCode": "+254", "phoneDigits": 9, "phoneMask": "### ######", "phoneGroupSizes": [3, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 196, "alpha2": "cy", "alpha3": "cyp", "name": "Кипр", "phoneCode": "+357", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 417, "alpha2": "kg", "alpha3": "kgz", "name": "Кыргызстан", "phoneCode": "+996", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 296, "alpha2": "ki", "alpha3": "kir", "name": "Кирибати", "phoneCode": "+686", "phoneDigits": 8, "phoneMask": "## ######", "phoneGroupSizes": [2, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 408, "alpha2": "kp", "alpha3": "prk", "name": "КНДР (Корейская Народно-Демократическая Республика)", "phoneCode": "+850", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 156, "alpha2": "cn", "alpha3": "chn", "name": "Китай (Китайская Народная Республика)", "phoneCode": "+86", "phoneDigits": 11, "phoneMask": "### #### ####", "phoneGroupSizes": [3, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 170, "alpha2": "co", "alpha3": "col", "name": "Колумбия", "phoneCode": "+57", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 174, "alpha2": "km", "alpha3": "com", "name": "Коморы", "phoneCode": "+269", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 188, "alpha2": "cr", "alpha3": "cri", "name": "Коста-Рика", "phoneCode": "+506", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 384, "alpha2": "ci", "alpha3": "civ", "name": "Кот-д’Ивуар", "phoneCode": "+225", "phoneDigits": 10, "phoneMask": "## ## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 192, "alpha2": "cu", "alpha3": "cub", "name": "Куба", "phoneCode": "+53", "phoneDigits": 8, "phoneMask": "########", "phoneGroupSizes": [8], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 414, "alpha2": "kw", "alpha3": "kwt", "name": "Кувейт", "phoneCode": "+965", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 418, "alpha2": "la", "alpha3": "lao", "name": "Лаос", "phoneCode": "+856", "phoneDigits": 9, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 428, "alpha2": "lv", "alpha3": "lva", "name": "Латвия", "phoneCode": "+371", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 426, "alpha2": "ls", "alpha3": "lso", "name": "Лесото", "phoneCode": "+266", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 430, "alpha2": "lr", "alpha3": "lbr", "name": "Либерия", "phoneCode": "+231", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 422, "alpha2": "lb", "alpha3": "lbn", "name": "Ливан", "phoneCode": "+961", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 434, "alpha2": "ly", "alpha3": "lby", "name": "Ливия", "phoneCode": "+218", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 440, "alpha2": "lt", "alpha3": "ltu", "name": "Литва", "phoneCode": "+370", "phoneDigits": 8, "phoneMask": "### #####", "phoneGroupSizes": [3, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 438, "alpha2": "li", "alpha3": "lie", "name": "Лихтенштейн", "phoneCode": "+423", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 442, "alpha2": "lu", "alpha3": "lux", "name": "Люксембург", "phoneCode": "+352", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 480, "alpha2": "mu", "alpha3": "mus", "name": "Маврикий", "phoneCode": "+230", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 478, "alpha2": "mr", "alpha3": "mrt", "name": "Мавритания", "phoneCode": "+222", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 450, "alpha2": "mg", "alpha3": "mdg", "name": "Мадагаскар", "phoneCode": "+261", "phoneDigits": 9, "phoneMask": "## ## ### ##", "phoneGroupSizes": [2, 2, 3, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 807, "alpha2": "mk", "alpha3": "mkd", "name": "Северная Македония", "phoneCode": "+389", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 454, "alpha2": "mw", "alpha3": "mwi", "name": "Малави", "phoneCode": "+265", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 458, "alpha2": "my", "alpha3": "mys", "name": "Малайзия", "phoneCode": "+60", "phoneDigits": 10, "phoneMask": "##-#### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 466, "alpha2": "ml", "alpha3": "mli", "name": "Мали", "phoneCode": "+223", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 462, "alpha2": "mv", "alpha3": "mdv", "name": "Мальдивы", "phoneCode": "+960", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 470, "alpha2": "mt", "alpha3": "mlt", "name": "Мальта", "phoneCode": "+356", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 504, "alpha2": "ma", "alpha3": "mar", "name": "Марокко", "phoneCode": "+212", "phoneDigits": 9, "phoneMask": "###-###-###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 584, "alpha2": "mh", "alpha3": "mhl", "name": "Маршалловы Острова", "phoneCode": "+692", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 484, "alpha2": "mx", "alpha3": "mex", "name": "Мексика", "phoneCode": "+52", "phoneDigits": 10, "phoneMask": "## #### ####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 583, "alpha2": "fm", "alpha3": "fsm", "name": "Микронезия", "phoneCode": "+691", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 508, "alpha2": "mz", "alpha3": "moz", "name": "Мозамбик", "phoneCode": "+258", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 498, "alpha2": "md", "alpha3": "mda", "name": "Молдова", "phoneCode": "+373", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 492, "alpha2": "mc", "alpha3": "mco", "name": "Монако", "phoneCode": "+377", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 496, "alpha2": "mn", "alpha3": "mng", "name": "Монголия", "phoneCode": "+976", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 104, "alpha2": "mm", "alpha3": "mmr", "name": "Мьянма", "phoneCode": "+95", "phoneDigits": 9, "phoneMask": "# ### ####", "phoneGroupSizes": [1, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 516, "alpha2": "na", "alpha3": "nam", "name": "Намибия", "phoneCode": "+264", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 520, "alpha2": "nr", "alpha3": "nru", "name": "Науру", "phoneCode": "+674", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 524, "alpha2": "np", "alpha3": "npl", "name": "Непал", "phoneCode": "+977", "phoneDigits": 10, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 562, "alpha2": "ne", "alpha3": "ner", "name": "Нигер", "phoneCode": "+227", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 566, "alpha2": "ng", "alpha3": "nga", "name": "Нигерия", "phoneCode": "+234", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 528, "alpha2": "nl", "alpha3": "nld", "name": "Нидерланды", "phoneCode": "+31", "phoneDigits": 9, "phoneMask": "# ## ## ## ##", "phoneGroupSizes": [1, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 558, "alpha2": "ni", "alpha3": "nic", "name": "Никарагуа", "phoneCode": "+505", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 554, "alpha2": "nz", "alpha3": "nzl", "name": "Новая Зеландия", "phoneCode": "+64", "phoneDigits": 9, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 578, "alpha2": "no", "alpha3": "nor", "name": "Норвегия", "phoneCode": "+47", "phoneDigits": 8, "phoneMask": "### ## ###", "phoneGroupSizes": [3, 2, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 784, "alpha2": "ae", "alpha3": "are", "name": "ОАЭ", "phoneCode": "+971", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 512, "alpha2": "om", "alpha3": "omn", "name": "Оман", "phoneCode": "+968", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 586, "alpha2": "pk", "alpha3": "pak", "name": "Пакистан", "phoneCode": "+92", "phoneDigits": 10, "phoneMask": "### #######", "phoneGroupSizes": [3, 7], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 585, "alpha2": "pw", "alpha3": "plw", "name": "Палау", "phoneCode": "+680", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 591, "alpha2": "pa", "alpha3": "pan", "name": "Панама", "phoneCode": "+507", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 598, "alpha2": "pg", "alpha3": "png", "name": "Папуа — Новая Гвинея", "phoneCode": "+675", "phoneDigits": 8, "phoneMask": "### ## ###", "phoneGroupSizes": [3, 2, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 600, "alpha2": "py", "alpha3": "pry", "name": "Парагвай", "phoneCode": "+595", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 604, "alpha2": "pe", "alpha3": "per", "name": "Перу", "phoneCode": "+51", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 616, "alpha2": "pl", "alpha3": "pol", "name": "Польша", "phoneCode": "+48", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 620, "alpha2": "pt", "alpha3": "prt", "name": "Португалия", "phoneCode": "+351", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 178, "alpha2": "cg", "alpha3": "cog", "name": "Республика Конго", "phoneCode": "+242", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 410, "alpha2": "kr", "alpha3": "kor", "name": "Республика Корея", "phoneCode": "+82", "phoneDigits": 10, "phoneMask": "##-####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
{"id": 643, "alpha2": "ru", "alpha3": "rus", "name": "Россия", "phoneCode": "+7", "phoneDigits": 10, "phoneMask": "(###) ###-##-##", "phoneGroupSizes": [3, 3, 2, 2], "phoneGroupSeparators": ["(", ") ", "-", "-", ""]}
|
||||||
|
{"id": 646, "alpha2": "rw", "alpha3": "rwa", "name": "Руанда", "phoneCode": "+250", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 642, "alpha2": "ro", "alpha3": "rou", "name": "Румыния", "phoneCode": "+40", "phoneDigits": 10, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 222, "alpha2": "sv", "alpha3": "slv", "name": "Сальвадор", "phoneCode": "+503", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 882, "alpha2": "ws", "alpha3": "wsm", "name": "Самоа", "phoneCode": "+685", "phoneDigits": 7, "phoneMask": "## #####", "phoneGroupSizes": [2, 5], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 674, "alpha2": "sm", "alpha3": "smr", "name": "Сан-Марино", "phoneCode": "+378", "phoneDigits": 10, "phoneMask": "## ## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 678, "alpha2": "st", "alpha3": "stp", "name": "Сан-Томе и Принсипи", "phoneCode": "+239", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 682, "alpha2": "sa", "alpha3": "sau", "name": "Саудовская Аравия", "phoneCode": "+966", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 690, "alpha2": "sc", "alpha3": "syc", "name": "Сейшельские Острова", "phoneCode": "+248", "phoneDigits": 7, "phoneMask": "# ### ###", "phoneGroupSizes": [1, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 686, "alpha2": "sn", "alpha3": "sen", "name": "Сенегал", "phoneCode": "+221", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 670, "alpha2": "vc", "alpha3": "vct", "name": "Сент-Винсент и Гренадины", "phoneCode": "+1784", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 659, "alpha2": "kn", "alpha3": "kna", "name": "Сент-Китс и Невис", "phoneCode": "+1869", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 662, "alpha2": "lc", "alpha3": "lca", "name": "Сент-Люсия", "phoneCode": "+1758", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 688, "alpha2": "rs", "alpha3": "srb", "name": "Сербия", "phoneCode": "+381", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 702, "alpha2": "sg", "alpha3": "sgp", "name": "Сингапур", "phoneCode": "+65", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 760, "alpha2": "sy", "alpha3": "syr", "name": "Сирия", "phoneCode": "+963", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 703, "alpha2": "sk", "alpha3": "svk", "name": "Словакия", "phoneCode": "+421", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 705, "alpha2": "si", "alpha3": "svn", "name": "Словения", "phoneCode": "+386", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 90, "alpha2": "sb", "alpha3": "slb", "name": "Соломоновы Острова", "phoneCode": "+677", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 706, "alpha2": "so", "alpha3": "som", "name": "Сомали", "phoneCode": "+252", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 729, "alpha2": "sd", "alpha3": "sdn", "name": "Судан", "phoneCode": "+249", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 740, "alpha2": "sr", "alpha3": "sur", "name": "Суринам", "phoneCode": "+597", "phoneDigits": 7, "phoneMask": "###-####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", "-", ""]}
|
||||||
|
{"id": 840, "alpha2": "us", "alpha3": "usa", "name": "США", "phoneCode": "+1", "phoneDigits": 10, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 694, "alpha2": "sl", "alpha3": "sle", "name": "Сьерра-Леоне", "phoneCode": "+232", "phoneDigits": 8, "phoneMask": "## ######", "phoneGroupSizes": [2, 6], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 762, "alpha2": "tj", "alpha3": "tjk", "name": "Таджикистан", "phoneCode": "+992", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 764, "alpha2": "th", "alpha3": "tha", "name": "Таиланд", "phoneCode": "+66", "phoneDigits": 9, "phoneMask": "##-### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", "-", " ", ""]}
|
||||||
|
{"id": 834, "alpha2": "tz", "alpha3": "tza", "name": "Танзания", "phoneCode": "+255", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 768, "alpha2": "tg", "alpha3": "tgo", "name": "Того", "phoneCode": "+228", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 776, "alpha2": "to", "alpha3": "ton", "name": "Тонга", "phoneCode": "+676", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 780, "alpha2": "tt", "alpha3": "tto", "name": "Тринидад и Тобаго", "phoneCode": "+1868", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 798, "alpha2": "tv", "alpha3": "tuv", "name": "Тувалу", "phoneCode": "+688", "phoneDigits": 6, "phoneMask": "######", "phoneGroupSizes": [6], "phoneGroupSeparators": ["", ""]}
|
||||||
|
{"id": 788, "alpha2": "tn", "alpha3": "tun", "name": "Тунис", "phoneCode": "+216", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 795, "alpha2": "tm", "alpha3": "tkm", "name": "Туркменистан", "phoneCode": "+993", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 792, "alpha2": "tr", "alpha3": "tur", "name": "Турция", "phoneCode": "+90", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 800, "alpha2": "ug", "alpha3": "uga", "name": "Уганда", "phoneCode": "+256", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 860, "alpha2": "uz", "alpha3": "uzb", "name": "Узбекистан", "phoneCode": "+998", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 804, "alpha2": "ua", "alpha3": "ukr", "name": "Украина", "phoneCode": "+380", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 858, "alpha2": "uy", "alpha3": "ury", "name": "Уругвай", "phoneCode": "+598", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 242, "alpha2": "fj", "alpha3": "fji", "name": "Фиджи", "phoneCode": "+679", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 608, "alpha2": "ph", "alpha3": "phl", "name": "Филиппины", "phoneCode": "+63", "phoneDigits": 10, "phoneMask": "### ### ####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 246, "alpha2": "fi", "alpha3": "fin", "name": "Финляндия", "phoneCode": "+358", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 250, "alpha2": "fr", "alpha3": "fra", "name": "Франция", "phoneCode": "+33", "phoneDigits": 9, "phoneMask": "# ## ## ## ##", "phoneGroupSizes": [1, 2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", " ", ""]}
|
||||||
|
{"id": 191, "alpha2": "hr", "alpha3": "hrv", "name": "Хорватия", "phoneCode": "+385", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 140, "alpha2": "cf", "alpha3": "caf", "name": "ЦАР", "phoneCode": "+236", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 148, "alpha2": "td", "alpha3": "tcd", "name": "Чад", "phoneCode": "+235", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 499, "alpha2": "me", "alpha3": "mne", "name": "Черногория", "phoneCode": "+382", "phoneDigits": 8, "phoneMask": "## ### ###", "phoneGroupSizes": [2, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 203, "alpha2": "cz", "alpha3": "cze", "name": "Чехия", "phoneCode": "+420", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 152, "alpha2": "cl", "alpha3": "chl", "name": "Чили", "phoneCode": "+56", "phoneDigits": 9, "phoneMask": "# #### ####", "phoneGroupSizes": [1, 4, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 756, "alpha2": "ch", "alpha3": "che", "name": "Швейцария", "phoneCode": "+41", "phoneDigits": 9, "phoneMask": "## ### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 752, "alpha2": "se", "alpha3": "swe", "name": "Швеция", "phoneCode": "+46", "phoneDigits": 9, "phoneMask": "##-### ## ##", "phoneGroupSizes": [2, 3, 2, 2], "phoneGroupSeparators": ["", "-", " ", " ", ""]}
|
||||||
|
{"id": 144, "alpha2": "lk", "alpha3": "lka", "name": "Шри-Ланка", "phoneCode": "+94", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 218, "alpha2": "ec", "alpha3": "ecu", "name": "Эквадор", "phoneCode": "+593", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 226, "alpha2": "gq", "alpha3": "gnq", "name": "Экваториальная Гвинея", "phoneCode": "+240", "phoneDigits": 9, "phoneMask": "### ## ## ##", "phoneGroupSizes": [3, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 232, "alpha2": "er", "alpha3": "eri", "name": "Эритрея", "phoneCode": "+291", "phoneDigits": 7, "phoneMask": "### ####", "phoneGroupSizes": [3, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 748, "alpha2": "sz", "alpha3": "swz", "name": "Эсватини", "phoneCode": "+268", "phoneDigits": 8, "phoneMask": "## ## ## ##", "phoneGroupSizes": [2, 2, 2, 2], "phoneGroupSeparators": ["", " ", " ", " ", ""]}
|
||||||
|
{"id": 233, "alpha2": "ee", "alpha3": "est", "name": "Эстония", "phoneCode": "+372", "phoneDigits": 8, "phoneMask": "#### ####", "phoneGroupSizes": [4, 4], "phoneGroupSeparators": ["", " ", ""]}
|
||||||
|
{"id": 231, "alpha2": "et", "alpha3": "eth", "name": "Эфиопия", "phoneCode": "+251", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 710, "alpha2": "za", "alpha3": "zaf", "name": "ЮАР", "phoneCode": "+27", "phoneDigits": 9, "phoneMask": "## ### ####", "phoneGroupSizes": [2, 3, 4], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 728, "alpha2": "ss", "alpha3": "ssd", "name": "Южный Судан", "phoneCode": "+211", "phoneDigits": 9, "phoneMask": "### ### ###", "phoneGroupSizes": [3, 3, 3], "phoneGroupSeparators": ["", " ", " ", ""]}
|
||||||
|
{"id": 388, "alpha2": "jm", "alpha3": "jam", "name": "Ямайка", "phoneCode": "+1876", "phoneDigits": 7, "phoneMask": "(###) ###-####", "phoneGroupSizes": [3, 3, 4], "phoneGroupSeparators": ["(", ") ", "-", ""]}
|
||||||
|
{"id": 392, "alpha2": "jp", "alpha3": "jpn", "name": "Япония", "phoneCode": "+81", "phoneDigits": 10, "phoneMask": "##-####-####", "phoneGroupSizes": [2, 4, 4], "phoneGroupSeparators": ["", "-", "-", ""]}
|
||||||
|
]''';
|
||||||
@@ -0,0 +1,356 @@
|
|||||||
|
/// All protocol operation codes.
|
||||||
|
///
|
||||||
|
/// Naming follows the server-side convention.
|
||||||
|
/// Use [Opcode.name] to get a human-readable label for logging.
|
||||||
|
///
|
||||||
|
/// файл писла нейронка (я че ебанутый чтоль чтобы вручную хуярить опкоды и их значения)
|
||||||
|
abstract class Opcode {
|
||||||
|
// ── Session ────────────────────────────────────────────────────────
|
||||||
|
static const int ping = 1; // Пинг
|
||||||
|
static const int debug = 2; // Отладка / понг
|
||||||
|
static const int reconnect = 3; // Реконнект
|
||||||
|
static const int log = 5; // Аналитика / события
|
||||||
|
static const int sessionInit = 6; // Инициализация сессии (хэндшейк)
|
||||||
|
|
||||||
|
// ── Profile ────────────────────────────────────────────────────────
|
||||||
|
static const int profile = 16; // Обновление профиля
|
||||||
|
|
||||||
|
// ── Auth ───────────────────────────────────────────────────────────
|
||||||
|
static const int authRequest = 17; // Запрос OTP-кода
|
||||||
|
static const int auth = 18; // Проверка OTP-кода
|
||||||
|
static const int login = 19; // Вход (загрузка чатов/контактов)
|
||||||
|
static const int logout = 20; // Выход из аккаунта
|
||||||
|
static const int sync = 21; // Синхронизация данных
|
||||||
|
static const int config = 22; // Настройки приватности / конфиг
|
||||||
|
static const int authConfirm = 23; // Завершение регистрации
|
||||||
|
|
||||||
|
// ── Auth 2FA ───────────────────────────────────────────────────────
|
||||||
|
static const int authLoginRestorePassword = 101; // Восстановление пароля
|
||||||
|
static const int auth2faDetails = 104; // Детали 2FA
|
||||||
|
static const int externalCallback = 105; // Внешний коллбэк
|
||||||
|
static const int authValidatePassword = 107; // Валидация пароля
|
||||||
|
static const int authValidateHint = 108; // Валидация подсказки пароля
|
||||||
|
static const int authVerifyEmail = 109; // Верификация email
|
||||||
|
static const int authCheckEmail = 110; // Проверка email
|
||||||
|
static const int authSet2fa = 111; // Установка 2FA
|
||||||
|
static const int authCreateTrack = 112; // Создание трека авторизации
|
||||||
|
static const int authCheckPassword = 113; // Проверка пароля при регистрации
|
||||||
|
static const int authLoginCheckPassword = 115; // Проверка пароля при входе
|
||||||
|
static const int authLoginProfileDelete = 116; // Удаление профиля при входе
|
||||||
|
|
||||||
|
// ── Assets (стикеры) ───────────────────────────────────────────────
|
||||||
|
static const int presetAvatars = 25; // Пресетные аватарки
|
||||||
|
static const int assetsGet = 26; // Получение стикерпаков
|
||||||
|
static const int assetsUpdate = 27; // Синхронизация стикеров
|
||||||
|
static const int assetsGetByIds = 28; // Стикерпаки по ID
|
||||||
|
static const int assetsAdd = 29; // Добавление стикерпака
|
||||||
|
static const int assetsRemove = 259; // Удаление стикерпака
|
||||||
|
static const int assetsMove = 260; // Перемещение стикерпака
|
||||||
|
static const int assetsListModify = 261; // Изменение списка стикерпаков
|
||||||
|
|
||||||
|
// ── Contacts ───────────────────────────────────────────────────────
|
||||||
|
static const int contactInfo = 32; // Информация о контакте
|
||||||
|
static const int contactAdd = 33; // Добавление контакта
|
||||||
|
static const int contactUpdate = 34; // Обновление контакта (блок и т.д.)
|
||||||
|
static const int contactPresence = 35; // Запрос присутствия по ID
|
||||||
|
static const int contactList = 36; // Список заблокированных
|
||||||
|
static const int contactSearch = 37; // Поиск контакта
|
||||||
|
static const int contactMutual = 38; // Общие контакты
|
||||||
|
static const int contactPhotos = 39; // Фото контакта
|
||||||
|
static const int contactSort = 40; // Сортировка контактов
|
||||||
|
static const int contactVerify = 42; // Верификация контакта
|
||||||
|
static const int removeContactPhoto = 43; // Удаление фото контакта
|
||||||
|
static const int contactInfoByPhone = 46; // Поиск контакта по номеру
|
||||||
|
|
||||||
|
// ── Chats ──────────────────────────────────────────────────────────
|
||||||
|
static const int chatInfo = 48; // Информация о чате / создание группы
|
||||||
|
static const int chatHistory = 49; // История сообщений
|
||||||
|
static const int chatMark = 50; // Отметка прочитанным
|
||||||
|
static const int chatMedia = 51; // Медиа чата
|
||||||
|
static const int chatDelete = 52; // Удаление чата
|
||||||
|
static const int chatsList = 53; // Список чатов
|
||||||
|
static const int chatClear = 54; // Очистка истории чата
|
||||||
|
static const int chatUpdate = 55; // Обновление настроек чата
|
||||||
|
static const int chatCheckLink = 56; // Проверка ссылки чата
|
||||||
|
static const int chatJoin = 57; // Вступление в группу / канал
|
||||||
|
static const int chatLeave = 58; // Выход из чата
|
||||||
|
static const int chatMembers = 59; // Участники группы
|
||||||
|
static const int publicSearch = 60; // Глобальный поиск
|
||||||
|
static const int chatPersonalConfig = 61; // Персональные настройки чата
|
||||||
|
static const int chatCreate = 63; // Создание чата
|
||||||
|
|
||||||
|
// ── Messages ───────────────────────────────────────────────────────
|
||||||
|
static const int msgSend = 64; // Отправка сообщения
|
||||||
|
static const int msgTyping = 65; // Индикатор набора текста
|
||||||
|
static const int msgDelete = 66; // Удаление сообщения
|
||||||
|
static const int msgEdit = 67; // Редактирование сообщения
|
||||||
|
static const int chatSearch = 68; // Поиск по чату
|
||||||
|
static const int msgSharePreview = 70; // Превью ссылки
|
||||||
|
static const int msgGet = 71; // Получение сообщения по ID
|
||||||
|
static const int msgSearchTouch = 72; // Точечный поиск сообщений
|
||||||
|
static const int msgSearch = 73; // Поиск сообщений
|
||||||
|
static const int msgGetStat = 74; // Статистика сообщения
|
||||||
|
static const int chatSubscribe = 75; // Подписка на обновления чата
|
||||||
|
static const int msgDeleteRange = 92; // Удаление диапазона сообщений
|
||||||
|
|
||||||
|
// ── Reactions ──────────────────────────────────────────────────────
|
||||||
|
static const int msgReaction = 178; // Отправка реакции
|
||||||
|
static const int msgCancelReaction = 179; // Отмена реакции
|
||||||
|
static const int msgGetReactions = 180; // Получение реакций
|
||||||
|
static const int msgGetDetailedReactions = 181; // Детальные реакции
|
||||||
|
static const int chatReactionsSettingsSet = 257; // Установка настроек реакций
|
||||||
|
static const int reactionsSettingsGetByChatId = 258; // Настройки реакций чата
|
||||||
|
|
||||||
|
// ── Calls & Video ──────────────────────────────────────────────────
|
||||||
|
static const int videoChatStart = 76; // Начало видеочата
|
||||||
|
static const int chatMembersUpdate = 77; // Обновление участников / добавление
|
||||||
|
static const int videoChatStartActive = 78; // Инициация активного звонка
|
||||||
|
static const int videoChatHistory = 79; // История звонков
|
||||||
|
static const int videoChatCreateJoinLink = 84; // Ссылка для входа в видеочат
|
||||||
|
static const int videoChatMembers = 195; // Участники видеочата
|
||||||
|
static const int getInboundCalls = 103; // Входящие звонки
|
||||||
|
|
||||||
|
// ── Media & Files ──────────────────────────────────────────────────
|
||||||
|
static const int photoUpload = 80; // Загрузка фото
|
||||||
|
static const int stickerUpload = 81; // Загрузка стикера
|
||||||
|
static const int videoUpload = 82; // Загрузка видео
|
||||||
|
static const int videoPlay = 83; // URL видео
|
||||||
|
static const int chatPinSetVisibility = 86; // Видимость закрепов
|
||||||
|
static const int fileUpload = 87; // Загрузка файла
|
||||||
|
static const int fileDownload = 88; // Скачивание файла
|
||||||
|
static const int linkInfo = 89; // Информация по ссылке / вход в канал
|
||||||
|
static const int audioPlay = 301; // Воспроизведение аудио
|
||||||
|
|
||||||
|
// ── Sessions ───────────────────────────────────────────────────────
|
||||||
|
static const int sessionsInfo = 96; // Запрос активных сессий
|
||||||
|
static const int sessionsClose = 97; // Закрытие всех сессий
|
||||||
|
static const int phoneBind_request = 98; // Запрос привязки телефона
|
||||||
|
static const int phoneBindConfirm = 99; // Подтверждение привязки телефона
|
||||||
|
|
||||||
|
// ── Bots ───────────────────────────────────────────────────────────
|
||||||
|
static const int chatComplain = 117; // Жалоба на чат
|
||||||
|
static const int msgSendCallback = 118; // Коллбэк бота
|
||||||
|
static const int suspendBot = 119; // Приостановка бота
|
||||||
|
static const int chatBotCommands = 144; // Команды бота
|
||||||
|
static const int botInfo = 145; // Информация о боте
|
||||||
|
|
||||||
|
// ── Location ───────────────────────────────────────────────────────
|
||||||
|
static const int locationStop = 124; // Остановка трансляции геолокации
|
||||||
|
|
||||||
|
// ── Mentions ───────────────────────────────────────────────────────
|
||||||
|
static const int getLastMentions = 127; // Последние упоминания
|
||||||
|
|
||||||
|
// ── Stickers (creation) ────────────────────────────────────────────
|
||||||
|
static const int stickerCreate = 193; // Создание стикера
|
||||||
|
static const int stickerSuggest = 194; // Предложение стикера
|
||||||
|
|
||||||
|
// ── Notifications (push от сервера) ────────────────────────────────
|
||||||
|
static const int notifMessage = 128; // Новое сообщение
|
||||||
|
static const int notifTyping = 129; // Индикатор набора
|
||||||
|
static const int notifMark = 130; // Прочитано
|
||||||
|
static const int notifContact = 131; // Обновление контакта
|
||||||
|
static const int notifPresence = 132; // Статус онлайн
|
||||||
|
static const int notifConfig = 134; // Обновление конфига
|
||||||
|
static const int notifChat = 135; // Обновление чата
|
||||||
|
static const int notifAttach = 136; // Загрузка файла завершена
|
||||||
|
static const int notifCallStart = 137; // Входящий звонок
|
||||||
|
static const int notifContactSort = 139; // Пересортировка контактов
|
||||||
|
static const int notifMsgDeleteRange = 140; // Удаление диапазона
|
||||||
|
static const int notifMsgDelete = 142; // Удаление сообщения
|
||||||
|
static const int notifCallbackAnswer = 143; // Ответ бота
|
||||||
|
static const int notifLocation = 147; // Обновление геолокации
|
||||||
|
static const int notifLocationRequest = 148; // Запрос геолокации
|
||||||
|
static const int notifAssetsUpdate = 150; // Обновление стикеров
|
||||||
|
static const int notifDraft = 152; // Черновик
|
||||||
|
static const int notifDraftDiscard = 153; // Сброс черновика
|
||||||
|
static const int notifMsgDelayed = 154; // Отложенное сообщение
|
||||||
|
static const int notifMsgReactionsChanged = 155; // Изменение реакций
|
||||||
|
static const int notifMsgYouReacted = 156; // Ваша реакция
|
||||||
|
static const int notifProfile = 159; // Обновление профиля
|
||||||
|
static const int notifBanners = 292; // Баннеры
|
||||||
|
static const int notifFolders = 277; // Обновление папок
|
||||||
|
|
||||||
|
// ── Misc ───────────────────────────────────────────────────────────
|
||||||
|
static const int okToken = 158; // OK-токен
|
||||||
|
static const int webAppInitData = 160; // Данные WebApp
|
||||||
|
static const int complain = 161; // Жалоба
|
||||||
|
static const int complainReasonsGet = 162; // Причины жалобы
|
||||||
|
static const int draftSave = 176; // Сохранение черновика
|
||||||
|
static const int draftDiscard = 177; // Удаление черновика
|
||||||
|
static const int chatHide = 196; // Скрытие чата
|
||||||
|
static const int chatSearchCommonParticipants = 198; // Общие участники
|
||||||
|
static const int profileDelete = 199; // Удаление профиля
|
||||||
|
static const int profileDeleteTime = 200; // Таймер удаления профиля
|
||||||
|
static const int authQrApprove = 290; // Подтверждение QR-входа
|
||||||
|
static const int chatSuggest = 300; // Предложения чатов
|
||||||
|
|
||||||
|
// ── Polls ──────────────────────────────────────────────────────────
|
||||||
|
static const int sendVote = 304; // Голосование
|
||||||
|
static const int votersListByAnswer = 305; // Список голосовавших
|
||||||
|
static const int getPollUpdates = 306; // Обновления опроса
|
||||||
|
|
||||||
|
// ── Folders ────────────────────────────────────────────────────────
|
||||||
|
static const int foldersGet = 272; // Получение папок
|
||||||
|
static const int foldersGetById = 273; // Папка по ID
|
||||||
|
static const int foldersUpdate = 274; // Обновление / создание папки
|
||||||
|
static const int foldersReorder = 275; // Сортировка папок
|
||||||
|
static const int foldersDelete = 276; // Удаление папки
|
||||||
|
|
||||||
|
// ── Human-readable names ───────────────────────────────────────────
|
||||||
|
|
||||||
|
static String name(int opcode) => _names[opcode] ?? 'UNKNOWN($opcode)';
|
||||||
|
|
||||||
|
static const Map<int, String> _names = {
|
||||||
|
ping: 'PING',
|
||||||
|
debug: 'DEBUG',
|
||||||
|
reconnect: 'RECONNECT',
|
||||||
|
log: 'LOG',
|
||||||
|
sessionInit: 'SESSION_INIT',
|
||||||
|
profile: 'PROFILE',
|
||||||
|
authRequest: 'AUTH_REQUEST',
|
||||||
|
auth: 'AUTH',
|
||||||
|
login: 'LOGIN',
|
||||||
|
logout: 'LOGOUT',
|
||||||
|
sync: 'SYNC',
|
||||||
|
config: 'CONFIG',
|
||||||
|
authConfirm: 'AUTH_CONFIRM',
|
||||||
|
authLoginRestorePassword: 'AUTH_LOGIN_RESTORE_PASSWORD',
|
||||||
|
auth2faDetails: 'AUTH_2FA_DETAILS',
|
||||||
|
externalCallback: 'EXTERNAL_CALLBACK',
|
||||||
|
authValidatePassword: 'AUTH_VALIDATE_PASSWORD',
|
||||||
|
authValidateHint: 'AUTH_VALIDATE_HINT',
|
||||||
|
authVerifyEmail: 'AUTH_VERIFY_EMAIL',
|
||||||
|
authCheckEmail: 'AUTH_CHECK_EMAIL',
|
||||||
|
authSet2fa: 'AUTH_SET_2FA',
|
||||||
|
authCreateTrack: 'AUTH_CREATE_TRACK',
|
||||||
|
authCheckPassword: 'AUTH_CHECK_PASSWORD',
|
||||||
|
authLoginCheckPassword: 'AUTH_LOGIN_CHECK_PASSWORD',
|
||||||
|
authLoginProfileDelete: 'AUTH_LOGIN_PROFILE_DELETE',
|
||||||
|
presetAvatars: 'PRESET_AVATARS',
|
||||||
|
assetsGet: 'ASSETS_GET',
|
||||||
|
assetsUpdate: 'ASSETS_UPDATE',
|
||||||
|
assetsGetByIds: 'ASSETS_GET_BY_IDS',
|
||||||
|
assetsAdd: 'ASSETS_ADD',
|
||||||
|
assetsRemove: 'ASSETS_REMOVE',
|
||||||
|
assetsMove: 'ASSETS_MOVE',
|
||||||
|
assetsListModify: 'ASSETS_LIST_MODIFY',
|
||||||
|
contactInfo: 'CONTACT_INFO',
|
||||||
|
contactAdd: 'CONTACT_ADD',
|
||||||
|
contactUpdate: 'CONTACT_UPDATE',
|
||||||
|
contactPresence: 'CONTACT_PRESENCE',
|
||||||
|
contactList: 'CONTACT_LIST',
|
||||||
|
contactSearch: 'CONTACT_SEARCH',
|
||||||
|
contactMutual: 'CONTACT_MUTUAL',
|
||||||
|
contactPhotos: 'CONTACT_PHOTOS',
|
||||||
|
contactSort: 'CONTACT_SORT',
|
||||||
|
contactVerify: 'CONTACT_VERIFY',
|
||||||
|
removeContactPhoto: 'REMOVE_CONTACT_PHOTO',
|
||||||
|
contactInfoByPhone: 'CONTACT_INFO_BY_PHONE',
|
||||||
|
chatInfo: 'CHAT_INFO',
|
||||||
|
chatHistory: 'CHAT_HISTORY',
|
||||||
|
chatMark: 'CHAT_MARK',
|
||||||
|
chatMedia: 'CHAT_MEDIA',
|
||||||
|
chatDelete: 'CHAT_DELETE',
|
||||||
|
chatsList: 'CHATS_LIST',
|
||||||
|
chatClear: 'CHAT_CLEAR',
|
||||||
|
chatUpdate: 'CHAT_UPDATE',
|
||||||
|
chatCheckLink: 'CHAT_CHECK_LINK',
|
||||||
|
chatJoin: 'CHAT_JOIN',
|
||||||
|
chatLeave: 'CHAT_LEAVE',
|
||||||
|
chatMembers: 'CHAT_MEMBERS',
|
||||||
|
publicSearch: 'PUBLIC_SEARCH',
|
||||||
|
chatPersonalConfig: 'CHAT_PERSONAL_CONFIG',
|
||||||
|
chatCreate: 'CHAT_CREATE',
|
||||||
|
msgSend: 'MSG_SEND',
|
||||||
|
msgTyping: 'MSG_TYPING',
|
||||||
|
msgDelete: 'MSG_DELETE',
|
||||||
|
msgEdit: 'MSG_EDIT',
|
||||||
|
chatSearch: 'CHAT_SEARCH',
|
||||||
|
msgSharePreview: 'MSG_SHARE_PREVIEW',
|
||||||
|
msgGet: 'MSG_GET',
|
||||||
|
msgSearchTouch: 'MSG_SEARCH_TOUCH',
|
||||||
|
msgSearch: 'MSG_SEARCH',
|
||||||
|
msgGetStat: 'MSG_GET_STAT',
|
||||||
|
chatSubscribe: 'CHAT_SUBSCRIBE',
|
||||||
|
msgDeleteRange: 'MSG_DELETE_RANGE',
|
||||||
|
msgReaction: 'MSG_REACTION',
|
||||||
|
msgCancelReaction: 'MSG_CANCEL_REACTION',
|
||||||
|
msgGetReactions: 'MSG_GET_REACTIONS',
|
||||||
|
msgGetDetailedReactions: 'MSG_GET_DETAILED_REACTIONS',
|
||||||
|
chatReactionsSettingsSet: 'CHAT_REACTIONS_SETTINGS_SET',
|
||||||
|
reactionsSettingsGetByChatId: 'REACTIONS_SETTINGS_GET_BY_CHAT_ID',
|
||||||
|
videoChatStart: 'VIDEO_CHAT_START',
|
||||||
|
chatMembersUpdate: 'CHAT_MEMBERS_UPDATE',
|
||||||
|
videoChatStartActive: 'VIDEO_CHAT_START_ACTIVE',
|
||||||
|
videoChatHistory: 'VIDEO_CHAT_HISTORY',
|
||||||
|
videoChatCreateJoinLink: 'VIDEO_CHAT_CREATE_JOIN_LINK',
|
||||||
|
videoChatMembers: 'VIDEO_CHAT_MEMBERS',
|
||||||
|
getInboundCalls: 'GET_INBOUND_CALLS',
|
||||||
|
photoUpload: 'PHOTO_UPLOAD',
|
||||||
|
stickerUpload: 'STICKER_UPLOAD',
|
||||||
|
videoUpload: 'VIDEO_UPLOAD',
|
||||||
|
videoPlay: 'VIDEO_PLAY',
|
||||||
|
chatPinSetVisibility: 'CHAT_PIN_SET_VISIBILITY',
|
||||||
|
fileUpload: 'FILE_UPLOAD',
|
||||||
|
fileDownload: 'FILE_DOWNLOAD',
|
||||||
|
linkInfo: 'LINK_INFO',
|
||||||
|
audioPlay: 'AUDIO_PLAY',
|
||||||
|
sessionsInfo: 'SESSIONS_INFO',
|
||||||
|
sessionsClose: 'SESSIONS_CLOSE',
|
||||||
|
phoneBind_request: 'PHONE_BIND_REQUEST',
|
||||||
|
phoneBindConfirm: 'PHONE_BIND_CONFIRM',
|
||||||
|
chatComplain: 'CHAT_COMPLAIN',
|
||||||
|
msgSendCallback: 'MSG_SEND_CALLBACK',
|
||||||
|
suspendBot: 'SUSPEND_BOT',
|
||||||
|
chatBotCommands: 'CHAT_BOT_COMMANDS',
|
||||||
|
botInfo: 'BOT_INFO',
|
||||||
|
locationStop: 'LOCATION_STOP',
|
||||||
|
getLastMentions: 'GET_LAST_MENTIONS',
|
||||||
|
stickerCreate: 'STICKER_CREATE',
|
||||||
|
stickerSuggest: 'STICKER_SUGGEST',
|
||||||
|
notifMessage: 'NOTIF_MESSAGE',
|
||||||
|
notifTyping: 'NOTIF_TYPING',
|
||||||
|
notifMark: 'NOTIF_MARK',
|
||||||
|
notifContact: 'NOTIF_CONTACT',
|
||||||
|
notifPresence: 'NOTIF_PRESENCE',
|
||||||
|
notifConfig: 'NOTIF_CONFIG',
|
||||||
|
notifChat: 'NOTIF_CHAT',
|
||||||
|
notifAttach: 'NOTIF_ATTACH',
|
||||||
|
notifCallStart: 'NOTIF_CALL_START',
|
||||||
|
notifContactSort: 'NOTIF_CONTACT_SORT',
|
||||||
|
notifMsgDeleteRange: 'NOTIF_MSG_DELETE_RANGE',
|
||||||
|
notifMsgDelete: 'NOTIF_MSG_DELETE',
|
||||||
|
notifCallbackAnswer: 'NOTIF_CALLBACK_ANSWER',
|
||||||
|
notifLocation: 'NOTIF_LOCATION',
|
||||||
|
notifLocationRequest: 'NOTIF_LOCATION_REQUEST',
|
||||||
|
notifAssetsUpdate: 'NOTIF_ASSETS_UPDATE',
|
||||||
|
notifDraft: 'NOTIF_DRAFT',
|
||||||
|
notifDraftDiscard: 'NOTIF_DRAFT_DISCARD',
|
||||||
|
notifMsgDelayed: 'NOTIF_MSG_DELAYED',
|
||||||
|
notifMsgReactionsChanged: 'NOTIF_MSG_REACTIONS_CHANGED',
|
||||||
|
notifMsgYouReacted: 'NOTIF_MSG_YOU_REACTED',
|
||||||
|
notifProfile: 'NOTIF_PROFILE',
|
||||||
|
notifBanners: 'NOTIF_BANNERS',
|
||||||
|
notifFolders: 'NOTIF_FOLDERS',
|
||||||
|
okToken: 'OK_TOKEN',
|
||||||
|
webAppInitData: 'WEB_APP_INIT_DATA',
|
||||||
|
complain: 'COMPLAIN',
|
||||||
|
complainReasonsGet: 'COMPLAIN_REASONS_GET',
|
||||||
|
draftSave: 'DRAFT_SAVE',
|
||||||
|
draftDiscard: 'DRAFT_DISCARD',
|
||||||
|
chatHide: 'CHAT_HIDE',
|
||||||
|
chatSearchCommonParticipants: 'CHAT_SEARCH_COMMON_PARTICIPANTS',
|
||||||
|
profileDelete: 'PROFILE_DELETE',
|
||||||
|
profileDeleteTime: 'PROFILE_DELETE_TIME',
|
||||||
|
authQrApprove: 'AUTH_QR_APPROVE',
|
||||||
|
chatSuggest: 'CHAT_SUGGEST',
|
||||||
|
sendVote: 'SEND_VOTE',
|
||||||
|
votersListByAnswer: 'VOTERS_LIST_BY_ANSWER',
|
||||||
|
getPollUpdates: 'GET_POLL_UPDATES',
|
||||||
|
foldersGet: 'FOLDERS_GET',
|
||||||
|
foldersGetById: 'FOLDERS_GET_BY_ID',
|
||||||
|
foldersUpdate: 'FOLDERS_UPDATE',
|
||||||
|
foldersReorder: 'FOLDERS_REORDER',
|
||||||
|
foldersDelete: 'FOLDERS_DELETE',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+120
-70
@@ -4,132 +4,182 @@ import 'package:msgpack_dart/msgpack_dart.dart' as msgpack;
|
|||||||
|
|
||||||
import '../utils/logger.dart';
|
import '../utils/logger.dart';
|
||||||
|
|
||||||
final int apiVersion = 10;
|
/// ver(1) + cmd(1) + seq(2) + opcode(2) + packedLen(4) = 10
|
||||||
int seq = 1;
|
const int headerSize = 10;
|
||||||
|
const int _maxDecompressedSize = 1048576; // 1 MB
|
||||||
|
|
||||||
/*
|
/// Типы команд в протоколе
|
||||||
Базовый класс пакета
|
abstract class CmdType {
|
||||||
|
static const int request = 0; // запрос клиента
|
||||||
|
static const int push = 1; // пуш от сервера
|
||||||
|
|
||||||
api - версия API (по умолчанию 10 для сокета)
|
static const int ok = 1; // ответ: ок
|
||||||
cmd - от кого был прислан пакет (
|
static const int notFound = 2; // ответ: не найдено
|
||||||
0 - клиент (если отправляется серверу)
|
static const int error = 3; // ответ: ошибка
|
||||||
1 - сервер
|
}
|
||||||
3 - отправляется вместе с ошибкой)
|
|
||||||
seq - счёт пакетов (начинается с 1)
|
/// Распакованный бинарный пакет
|
||||||
opcode - показывает, для чего отправляется пакет (подробнее в /lib/core/protocol/opcode_map.dart)
|
///
|
||||||
payload - содержимое пакета (отправляется с сервера ввиде msgpack)
|
/// Формат заголовка (10 байт):
|
||||||
*/
|
/// ```
|
||||||
|
/// [0] ver — версия протокола (uint8) (по умолчанию 10)
|
||||||
|
/// [1] cmd — тип команды (uint8) (при отправке от клиента равно 0)
|
||||||
|
/// [2..3] seq — порядковый номер (uint16 BE)
|
||||||
|
/// [4..5] opcode — код операции (uint16 BE)
|
||||||
|
/// [6..9] packedLen — флаг сжатия [6] + длина payload [7..9] (uint32 BE)
|
||||||
|
/// [10..] payload — данные в MsgPack, опционально сжатые LZ4
|
||||||
|
/// ```
|
||||||
class Packet {
|
class Packet {
|
||||||
int api;
|
int api;
|
||||||
int cmd;
|
int cmd;
|
||||||
int seq;
|
int seq;
|
||||||
int opcode;
|
int opcode;
|
||||||
Map<dynamic, dynamic> payload;
|
dynamic payload;
|
||||||
|
|
||||||
Packet({
|
Packet({
|
||||||
this.api = 10,
|
this.api = 10,
|
||||||
this.cmd = 0,
|
this.cmd = 0,
|
||||||
this.seq = 1,
|
this.seq = 0,
|
||||||
this.opcode = 0,
|
this.opcode = 0,
|
||||||
this.payload = const {},
|
this.payload,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Выводит пакет в консоль
|
bool get isOk => cmd == CmdType.ok;
|
||||||
void printPacket() {
|
bool get isError => cmd == CmdType.error;
|
||||||
logger.i(
|
bool get isPush => cmd == CmdType.push;
|
||||||
"Api: $api\nCmd: $cmd\nSeq: $seq\nOPCode: $opcode\nPayload: $payload",
|
|
||||||
);
|
@override
|
||||||
}
|
String toString() =>
|
||||||
|
'Packet(ver=$api cmd=$cmd seq=$seq opcode=$opcode payload=$payload)';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Функция запаковки пакета
|
/// Упаковка пакета для отправки на сервер
|
||||||
/// Принимает только opcode и payload, так как api и cmd
|
Uint8List packPacket(int opcode, Map<dynamic, dynamic> payload, {int seq = 0}) {
|
||||||
/// константы (10 и 0 соответственно), а seq считается менеджером сокета
|
final header = ByteData(headerSize);
|
||||||
/// Возращает запакованный пакет
|
header.setUint8(0, 10);
|
||||||
Uint8List packPacket(int opcode, Map<dynamic, dynamic> payload) {
|
header.setUint8(1, CmdType.request);
|
||||||
// Объяснение каждой переменной смотри в классе Packet
|
header.setUint16(2, seq, Endian.big);
|
||||||
|
header.setUint16(4, opcode, Endian.big);
|
||||||
|
|
||||||
final apiVerB = Uint8List(1)..[0] = apiVersion;
|
|
||||||
final cmdB = Uint8List(1)..[0] = 0;
|
|
||||||
final seqB = Uint8List(2)..buffer.asByteData().setUint16(0, seq, Endian.big);
|
|
||||||
final opcodeB = Uint8List(2)
|
|
||||||
..buffer.asByteData().setUint16(0, opcode, Endian.big);
|
|
||||||
|
|
||||||
// Перед получением длины пакуем в msgpack
|
|
||||||
final payloadBytes = msgpack.serialize(payload);
|
final payloadBytes = msgpack.serialize(payload);
|
||||||
final payloadLen = payloadBytes.length & 0xFFFFFF;
|
final payloadLen = payloadBytes.length & 0xFFFFFF;
|
||||||
|
header.setUint32(6, payloadLen, Endian.big);
|
||||||
|
|
||||||
final payloadLenB = Uint8List(4)
|
return Uint8List.fromList(header.buffer.asUint8List() + payloadBytes);
|
||||||
..buffer.asByteData().setUint32(0, payloadLen, Endian.big);
|
|
||||||
|
|
||||||
return Uint8List.fromList(
|
|
||||||
apiVerB + cmdB + seqB + opcodeB + payloadLenB + payloadBytes,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Функция распаковки пакета
|
/// Распаковка пакета от сервера
|
||||||
/// Принимает байты пакета, которые отправляет сервер
|
|
||||||
/// Возращает Packet
|
|
||||||
Packet unpackPacket(Uint8List packet) {
|
Packet unpackPacket(Uint8List packet) {
|
||||||
// Для удобства расшифровки пакета переводим в ByteData
|
// Для удобства расшифровки пакета переводим в ByteData
|
||||||
ByteData packetData = ByteData.view(packet.buffer);
|
ByteData packetData = ByteData.view(
|
||||||
|
packet.buffer,
|
||||||
|
packet.offsetInBytes,
|
||||||
|
packet.lengthInBytes,
|
||||||
|
);
|
||||||
|
|
||||||
// Объяснение каждой переменной смотри в классе Packet
|
// Объяснение каждой переменной смотри в классе Packet
|
||||||
|
|
||||||
// API версия и cmd представляют из себя 8 битные числа
|
// API версия и cmd представляют из себя 8 битные числа
|
||||||
var apiVer = packetData.getUint8(0) & 0xFF;
|
final apiVer = packetData.getUint8(0) & 0xFF;
|
||||||
var cmd = packetData.getUint8(1) & 0xFF;
|
final cmd = packetData.getUint8(1) & 0xFF;
|
||||||
|
|
||||||
// Sequence и OPCode представляют из себя 16 битные числа
|
// Sequence и OPCode представляют из себя 16 битные числа
|
||||||
var seq = packetData.getUint16(2) & 0xFFFF;
|
final seq = packetData.getUint16(2) & 0xFFFF;
|
||||||
var opcode = packetData.getUint16(4) & 0xFFFF;
|
final opcode = packetData.getUint16(4) & 0xFFFF;
|
||||||
|
|
||||||
// После базовых переменных идет длина пакета, является 32 битным числом
|
// После базовых переменных идет длина пакета, является 32 битным числом
|
||||||
var packedLen = packetData.getUint32(6);
|
final packedLen = packetData.getUint32(6);
|
||||||
|
|
||||||
// Compression flag показывает, сжат ли payload
|
// Compression flag показывает, сжат ли payload
|
||||||
var compFlag = packedLen >> 24;
|
final compFlag = packedLen >> 24;
|
||||||
|
|
||||||
// Длина payload'а
|
// Длина payload'а
|
||||||
var payloadLength = packedLen & 0xFFFFFF;
|
final payloadLength = packedLen & 0xFFFFFF;
|
||||||
|
|
||||||
// Байты payload'а, могут быть сжаты LZ4
|
// Байты payload'а, могут быть сжаты LZ4
|
||||||
var payloadBytes = packet.buffer.asUint8List(10, payloadLength);
|
var payloadBytes = packet.buffer.asUint8List(10, payloadLength);
|
||||||
|
|
||||||
var payload = {};
|
dynamic payload;
|
||||||
|
print(compFlag);
|
||||||
// Если payload пустой, ничего не делаем (так может быть при получении пинга)
|
// Если payload пустой, ничего не делаем (так может быть при получении пинга)
|
||||||
if (payloadBytes.buffer.lengthInBytes > 0) {
|
if (payloadBytes.isNotEmpty) {
|
||||||
// Если пакет сжат, используем LZ4
|
|
||||||
if (compFlag != 0) {
|
if (compFlag != 0) {
|
||||||
try {
|
try {
|
||||||
final decompressedBytes = lz4Decompress(
|
payloadBytes = lz4Decompress(
|
||||||
payloadBytes,
|
payloadBytes,
|
||||||
decompressedSize: 99999,
|
decompressedSize: _maxDecompressedSize,
|
||||||
);
|
);
|
||||||
|
|
||||||
payloadBytes = decompressedBytes;
|
} catch (_) {
|
||||||
} catch (e) {
|
// dart_lz4 не умеет block-формат, фолбэк на ручной декомпрессор
|
||||||
logger.e("Ошибка при декомпрессировании: $e", error: e);
|
try {
|
||||||
|
payloadBytes = _lz4BlockDecompress(payloadBytes, _maxDecompressedSize);
|
||||||
|
} catch (e) {
|
||||||
|
logger.e("Ошибка декомпрессии LZ4: $e", error: e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Пробуем десериализовать msgpack
|
|
||||||
try {
|
try {
|
||||||
payload = msgpack.deserialize(payloadBytes);
|
payload = msgpack.deserialize(payloadBytes);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.e("Ошибка при десериализации msgpack: $e", error: e);
|
logger.e("Ошибка десериализации MsgPack: $e", error: e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Собираем целый пакет и возращаем его
|
return Packet(
|
||||||
final fullPacket = Packet(
|
|
||||||
api: apiVer,
|
api: apiVer,
|
||||||
cmd: cmd,
|
cmd: cmd,
|
||||||
seq: seq,
|
seq: seq,
|
||||||
opcode: opcode,
|
opcode: opcode,
|
||||||
payload: payload,
|
payload: payload,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return fullPacket;
|
/// LZ4 block декомпрессия (без frame-заголовка).
|
||||||
|
/// Сервер шлёт именно block-формат, dart_lz4 его не поддерживает.
|
||||||
|
Uint8List _lz4BlockDecompress(Uint8List src, int maxSize) {
|
||||||
|
final dst = BytesBuilder(copy: false);
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while (pos < src.length) {
|
||||||
|
final token = src[pos++];
|
||||||
|
var litLen = token >> 4;
|
||||||
|
|
||||||
|
if (litLen == 15) {
|
||||||
|
while (pos < src.length) {
|
||||||
|
final b = src[pos++];
|
||||||
|
litLen += b;
|
||||||
|
if (b != 255) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (litLen > 0) {
|
||||||
|
dst.add(src.sublist(pos, pos + litLen));
|
||||||
|
pos += litLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos >= src.length) break;
|
||||||
|
|
||||||
|
final offset = src[pos] | (src[pos + 1] << 8);
|
||||||
|
pos += 2;
|
||||||
|
if (offset == 0) throw StateError('LZ4: offset = 0');
|
||||||
|
|
||||||
|
var matchLen = (token & 0x0F) + 4;
|
||||||
|
if ((token & 0x0F) == 0x0F) {
|
||||||
|
while (pos < src.length) {
|
||||||
|
final b = src[pos++];
|
||||||
|
matchLen += b;
|
||||||
|
if (b != 255) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final out = dst.toBytes();
|
||||||
|
final start = out.length - offset;
|
||||||
|
dst.add(List<int>.generate(matchLen, (i) => out[start + (i % offset)]));
|
||||||
|
|
||||||
|
if (dst.length > maxSize) throw StateError('LZ4: превышен лимит');
|
||||||
|
}
|
||||||
|
|
||||||
|
return dst.toBytes();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import '../utils/logger.dart';
|
||||||
|
|
||||||
|
enum SocketState { disconnected, connecting, connected }
|
||||||
|
|
||||||
|
/// Обёртка над TCP + TLS сокетом.
|
||||||
|
/// Отдаёт сырые байты через [dataStream], сборкой пакетов занимается [PacketReceiver].
|
||||||
|
class Connection {
|
||||||
|
SecureSocket? _socket;
|
||||||
|
SocketState _state = SocketState.disconnected;
|
||||||
|
|
||||||
|
final _dataController = StreamController<Uint8List>.broadcast();
|
||||||
|
final _stateController = StreamController<SocketState>.broadcast();
|
||||||
|
|
||||||
|
Stream<Uint8List> get dataStream => _dataController.stream;
|
||||||
|
Stream<SocketState> get stateStream => _stateController.stream;
|
||||||
|
SocketState get state => _state;
|
||||||
|
bool get isConnected => _state == SocketState.connected;
|
||||||
|
|
||||||
|
void _setState(SocketState newState) {
|
||||||
|
if (_state == newState) return;
|
||||||
|
_state = newState;
|
||||||
|
_stateController.add(newState);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> connect(String host, int port) async {
|
||||||
|
if (_state != SocketState.disconnected) return;
|
||||||
|
_setState(SocketState.connecting);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final socket = await Socket.connect(host, port);
|
||||||
|
_socket = await SecureSocket.secure(
|
||||||
|
socket,
|
||||||
|
onBadCertificate: (_) => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
_setState(SocketState.connected);
|
||||||
|
logger.i('Подключено к $host:$port');
|
||||||
|
|
||||||
|
_socket!.listen(
|
||||||
|
(data) => _dataController.add(Uint8List.fromList(data)),
|
||||||
|
onError: (Object error) {
|
||||||
|
logger.e('Ошибка сокета: $error');
|
||||||
|
disconnect();
|
||||||
|
},
|
||||||
|
onDone: () {
|
||||||
|
logger.w('Сокет закрыт сервером');
|
||||||
|
disconnect();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('Не удалось подключиться: $e');
|
||||||
|
_setState(SocketState.disconnected);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(Uint8List data) {
|
||||||
|
if (_socket == null || !isConnected) {
|
||||||
|
throw StateError('Нельзя писать: сокет не подключён');
|
||||||
|
}
|
||||||
|
_socket!.add(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> disconnect() async {
|
||||||
|
final socket = _socket;
|
||||||
|
_socket = null;
|
||||||
|
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.destroy();
|
||||||
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
_setState(SocketState.disconnected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
disconnect();
|
||||||
|
_dataController.close();
|
||||||
|
_stateController.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import '../protocol/packet.dart';
|
||||||
|
import '../protocol/opcode_map.dart';
|
||||||
|
import '../utils/logger.dart';
|
||||||
|
|
||||||
|
typedef PacketHandler = void Function(Packet packet);
|
||||||
|
|
||||||
|
/// Роутер входящих пакетов.
|
||||||
|
///
|
||||||
|
/// Ответы на запросы матчатся по seq (через [registerPending]),
|
||||||
|
/// пуши — по opcode (через [registerHandler]).
|
||||||
|
class PacketDispatcher {
|
||||||
|
final Map<int, Completer<Packet>> _pendingRequests = {};
|
||||||
|
final Map<int, DateTime> _requestTimestamps = {};
|
||||||
|
final Map<int, PacketHandler> _pushHandlers = {};
|
||||||
|
|
||||||
|
final _pushController = StreamController<Packet>.broadcast();
|
||||||
|
|
||||||
|
/// Стрим всех входящих пушей (cmd == 1)
|
||||||
|
Stream<Packet> get pushStream => _pushController.stream;
|
||||||
|
|
||||||
|
Timer? _cleanupTimer;
|
||||||
|
|
||||||
|
PacketDispatcher() {
|
||||||
|
_cleanupTimer = Timer.periodic(
|
||||||
|
const Duration(seconds: 10),
|
||||||
|
(_) => _cleanupStaleRequests(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Регистрирует ожидание ответа — future завершится когда
|
||||||
|
/// придёт пакет с совпадающим seq.
|
||||||
|
Future<Packet> registerPending(int seq) {
|
||||||
|
final completer = Completer<Packet>();
|
||||||
|
_pendingRequests[seq] = completer;
|
||||||
|
_requestTimestamps[seq] = DateTime.now();
|
||||||
|
return completer.future;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Вешает обработчик на пуши с конкретным опкодом.
|
||||||
|
void registerHandler(int opcode, PacketHandler handler) {
|
||||||
|
_pushHandlers[opcode] = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregisterHandler(int opcode) {
|
||||||
|
_pushHandlers.remove(opcode);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispatch(Packet packet) {
|
||||||
|
final tag = Opcode.name(packet.opcode);
|
||||||
|
|
||||||
|
if (packet.cmd == CmdType.ok ||
|
||||||
|
packet.cmd == CmdType.error ||
|
||||||
|
packet.cmd == CmdType.notFound) {
|
||||||
|
final status = packet.isOk ? 'OK' : packet.isError ? 'ERR' : 'NOT_FOUND';
|
||||||
|
logger.i('<= [$tag] seq=${packet.seq} $status\n payload: ${packet.payload}');
|
||||||
|
|
||||||
|
final completer = _pendingRequests.remove(packet.seq);
|
||||||
|
_requestTimestamps.remove(packet.seq);
|
||||||
|
|
||||||
|
if (completer == null) {
|
||||||
|
if (packet.opcode != Opcode.ping) {
|
||||||
|
logger.w('Нет ожидающего запроса для seq=${packet.seq} [$tag]');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (packet.isError) {
|
||||||
|
completer.completeError(packet);
|
||||||
|
} else {
|
||||||
|
completer.complete(packet);
|
||||||
|
}
|
||||||
|
} else if (packet.isPush) {
|
||||||
|
logger.i('<= push [$tag] ${packet.payload}');
|
||||||
|
_pushHandlers[packet.opcode]?.call(packet);
|
||||||
|
_pushController.add(packet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Чистит зависшие запросы старше 30 секунд
|
||||||
|
void _cleanupStaleRequests() {
|
||||||
|
final now = DateTime.now();
|
||||||
|
final staleKeys = <int>[];
|
||||||
|
|
||||||
|
_requestTimestamps.forEach((seq, ts) {
|
||||||
|
if (now.difference(ts).inSeconds > 30) staleKeys.add(seq);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (final seq in staleKeys) {
|
||||||
|
final completer = _pendingRequests.remove(seq);
|
||||||
|
_requestTimestamps.remove(seq);
|
||||||
|
if (completer != null && !completer.isCompleted) {
|
||||||
|
completer.completeError(TimeoutException('Таймаут запроса seq=$seq'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Обрывает все ожидающие запросы (при дисконнекте)
|
||||||
|
void clearPending() {
|
||||||
|
for (final entry in _pendingRequests.entries) {
|
||||||
|
if (!entry.value.isCompleted) {
|
||||||
|
entry.value.completeError(StateError('Соединение закрыто'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_pendingRequests.clear();
|
||||||
|
_requestTimestamps.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
_cleanupTimer?.cancel();
|
||||||
|
clearPending();
|
||||||
|
_pushController.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import '../protocol/packet.dart';
|
||||||
|
import '../utils/logger.dart';
|
||||||
|
|
||||||
|
/// Буфер входящих данных.
|
||||||
|
/// Копит сырые байты из сокета, собирает из них целые пакеты.
|
||||||
|
class PacketReceiver {
|
||||||
|
Uint8List _buffer = Uint8List(0);
|
||||||
|
|
||||||
|
static const int _maxBufferSize = 2 * 1024 * 1024; // 2 мегабуйта
|
||||||
|
|
||||||
|
/// Добавляет байты в буфер, возвращает все собранные пакеты.
|
||||||
|
/// Неполные данные остаются в буфере до следующего вызова.
|
||||||
|
List<Packet> feed(Uint8List data) {
|
||||||
|
final newBuffer = Uint8List(_buffer.length + data.length);
|
||||||
|
newBuffer.setAll(0, _buffer);
|
||||||
|
newBuffer.setAll(_buffer.length, data);
|
||||||
|
_buffer = newBuffer;
|
||||||
|
|
||||||
|
if (_buffer.length > _maxBufferSize) {
|
||||||
|
logger.e('PacketReceiver: переполнение буфера (${_buffer.length} B), сброс');
|
||||||
|
reset();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
final packets = <Packet>[];
|
||||||
|
|
||||||
|
while (_buffer.length >= headerSize) {
|
||||||
|
final bd = ByteData.view(
|
||||||
|
_buffer.buffer,
|
||||||
|
_buffer.offsetInBytes,
|
||||||
|
headerSize,
|
||||||
|
);
|
||||||
|
final packedLen = bd.getUint32(6, Endian.big);
|
||||||
|
final payloadLength = packedLen & 0xFFFFFF;
|
||||||
|
final totalLength = headerSize + payloadLength;
|
||||||
|
|
||||||
|
if (_buffer.length < totalLength) break;
|
||||||
|
|
||||||
|
final packetBytes = Uint8List.sublistView(_buffer, 0, totalLength);
|
||||||
|
_buffer = _buffer.sublist(totalLength);
|
||||||
|
|
||||||
|
try {
|
||||||
|
packets.add(unpackPacket(packetBytes));
|
||||||
|
} catch (e) {
|
||||||
|
logger.e('PacketReceiver: ошибка распаковки: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
_buffer = Uint8List(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import '../protocol/packet.dart';
|
||||||
|
import '../protocol/opcode_map.dart';
|
||||||
|
import '../utils/logger.dart';
|
||||||
|
import 'connection.dart';
|
||||||
|
|
||||||
|
/// Упаковывает и отправляет пакеты, ведёт счётчик seq.
|
||||||
|
class PacketSender {
|
||||||
|
int _seq = 0;
|
||||||
|
|
||||||
|
int get currentSeq => _seq;
|
||||||
|
|
||||||
|
int _nextSeq() {
|
||||||
|
_seq = (_seq + 1) % 256;
|
||||||
|
return _seq;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Отправляет пакет, возвращает присвоенный seq.
|
||||||
|
int send(Connection connection, int opcode, Map<dynamic, dynamic> payload) {
|
||||||
|
final seq = _nextSeq();
|
||||||
|
final data = packPacket(opcode, payload, seq: seq);
|
||||||
|
connection.write(data);
|
||||||
|
logger.i('=> [${Opcode.name(opcode)}] seq=$seq\n payload: $payload');
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:logger/logger.dart';
|
|||||||
final logger = Logger(
|
final logger = Logger(
|
||||||
// устанавливает минимальный уровень логов.
|
// устанавливает минимальный уровень логов.
|
||||||
// доступны уровни: all, trace, debug, info, warning, error, fatal, off
|
// доступны уровни: all, trace, debug, info, warning, error, fatal, off
|
||||||
level: Level.info,
|
level: Level.all,
|
||||||
printer: PrettyPrinter(
|
printer: PrettyPrinter(
|
||||||
methodCount: 0,
|
methodCount: 0,
|
||||||
errorMethodCount: 5,
|
errorMethodCount: 5,
|
||||||
|
|||||||
+6
-1
@@ -1,6 +1,11 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'backend/api.dart';
|
||||||
|
|
||||||
void main() {
|
final api = Api();
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
await api.connect();
|
||||||
runApp(const MyApp());
|
runApp(const MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||||
#include "ephemeral/Flutter-Generated.xcconfig"
|
#include "ephemeral/Flutter-Generated.xcconfig"
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
platform :osx, '10.15'
|
||||||
|
|
||||||
|
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
|
||||||
|
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
|
||||||
|
|
||||||
|
project 'Runner', {
|
||||||
|
'Debug' => :debug,
|
||||||
|
'Profile' => :release,
|
||||||
|
'Release' => :release,
|
||||||
|
}
|
||||||
|
|
||||||
|
def flutter_root
|
||||||
|
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)
|
||||||
|
unless File.exist?(generated_xcode_build_settings_path)
|
||||||
|
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first"
|
||||||
|
end
|
||||||
|
|
||||||
|
File.foreach(generated_xcode_build_settings_path) do |line|
|
||||||
|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
|
||||||
|
return matches[1].strip if matches
|
||||||
|
end
|
||||||
|
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\""
|
||||||
|
end
|
||||||
|
|
||||||
|
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
|
||||||
|
|
||||||
|
flutter_macos_podfile_setup
|
||||||
|
|
||||||
|
target 'Runner' do
|
||||||
|
use_frameworks!
|
||||||
|
|
||||||
|
flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))
|
||||||
|
target 'RunnerTests' do
|
||||||
|
inherit! :search_paths
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
|
installer.pods_project.targets.each do |target|
|
||||||
|
flutter_additional_macos_build_settings(target)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,5 +8,7 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.network.server</key>
|
<key>com.apple.security.network.server</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -4,5 +4,7 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
+118
-1
@@ -57,6 +57,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
|
device_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: device_info_plus
|
||||||
|
sha256: "4df8babf73058181227e18b08e6ea3520cf5fc5d796888d33b7cb0f33f984b7c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.3.0"
|
||||||
|
device_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: device_info_plus_platform_interface
|
||||||
|
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.3"
|
||||||
|
equatable:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: equatable
|
||||||
|
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.8"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -65,6 +89,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
|
file:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: file
|
||||||
|
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -83,6 +123,35 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_timezone:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_timezone
|
||||||
|
sha256: "978192f2f9ea6d019a4de4f0211d76a9af955ca24865828fa98ca4e20cf0cb3c"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.0.1"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
|
http_parser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http_parser
|
||||||
|
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.2"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -163,6 +232,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -216,6 +293,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.7"
|
version: "0.7.7"
|
||||||
|
timezone:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: "784a5e34d2eb62e1326f24d6f600aaaee452eb8ca8ef2f384a59244e292d158b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.0"
|
||||||
|
typed_data:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: typed_data
|
||||||
|
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.4.0"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -232,6 +325,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "15.0.2"
|
version: "15.0.2"
|
||||||
|
web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: web
|
||||||
|
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.15.0"
|
||||||
|
win32_registry:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32_registry
|
||||||
|
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.10.4 <4.0.0"
|
dart: ">=3.10.4 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.29.0"
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ dependencies:
|
|||||||
dart_lz4: ^1.0.0
|
dart_lz4: ^1.0.0
|
||||||
msgpack_dart: ^1.0.1
|
msgpack_dart: ^1.0.1
|
||||||
logger: ^2.6.2
|
logger: ^2.6.2
|
||||||
|
device_info_plus: ^12.3.0
|
||||||
|
flutter_timezone: ^5.0.1
|
||||||
|
timezone: ^0.11.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user