From ce718aad9a208d9226be78d62882730de41f190b Mon Sep 17 00:00:00 2001 From: klockky Date: Sat, 14 Mar 2026 22:29:53 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=A1=D0=B4=D0=B5=D0=BB=D0=B0=D0=BB=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=BA=20=D1=81=D0=B5=D1=80=D0=B2=D0=B5=D1=80=D1=83,?= =?UTF-8?q?=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=BE=D0=BF?= =?UTF-8?q?=D0=BA=D0=BE=D0=B4=D1=8B=20=D0=B2=20opcode=5Fmap.dart=20=D0=B8?= =?UTF-8?q?=20=D0=B8=D1=85=20=D0=B7=D0=BD=D0=B0=D1=87=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5,=20=D0=BE=D1=82=D0=BF=D1=80=D0=B0=D0=B2=D0=BA=D1=83=20se?= =?UTF-8?q?ssionInit=20=D0=B8=20=D0=BF=D0=B8=D0=BD=D0=B3=D0=B8=20=D0=BA?= =?UTF-8?q?=D0=B0=D0=B6=D0=B4=D1=8B=D0=B5=2030=20=D1=81=D0=B5=D0=BA=D1=83?= =?UTF-8?q?=D0=BD=D0=B4=20=D1=82=D0=B0=D0=BA=D0=B6=D0=B5=20=D0=B4=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D0=BB=20packet.dart=20(?= =?UTF-8?q?=D1=82.=D0=BA.=20=D1=81=D1=82=D0=B0=D0=BD=D0=B4=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D0=BD=D0=B0=D1=8F=20=D0=B1=D0=B8=D0=B1=D0=BB=D0=B0=20dar?= =?UTF-8?q?t=5Flz4=20=D0=BD=D0=B5=20=D1=83=D0=BC=D0=B5=D0=B5=D1=82=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=82=D1=8C=20=D1=81=20?= =?UTF-8?q?=D0=B1=D0=BB=D0=BE=D0=BA=D0=B0=D0=BC=D0=B8)=20=D0=B4=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB=20countries.dart=20(=D0=B2=20?= =?UTF-8?q?=D0=B1=D1=83=D0=B4=D1=83=D1=89=D0=B5=D0=BC=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=B3=D0=BE=D0=B4=D0=B8=D1=82=D1=81=D1=8F=20=D0=B4=D0=BB=D1=8F?= =?UTF-8?q?=20=D0=BF=D0=BE=D0=B4=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7?= =?UTF-8?q?=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B5=20=D0=B4=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D1=83=D0=BF=D0=B5=D0=BD=20=D0=B2=D1=85=D0=BE=D0=B4?= =?UTF-8?q?=20=D0=B2=20MAX)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/backend/api.dart | 198 +++++++++++ lib/core/config/config.dart | 7 + lib/core/config/countries.dart | 456 +++++++++++++++++++++++++ lib/core/protocol/opcode_map.dart | 356 +++++++++++++++++++ lib/core/protocol/packet.dart | 200 ++++++----- lib/core/transport/connection.dart | 86 +++++ lib/core/transport/dispatcher.dart | 115 +++++++ lib/core/transport/receiver.dart | 57 ++++ lib/core/transport/sender.dart | 25 ++ lib/core/utils/logger.dart | 2 +- lib/main.dart | 7 +- macos/Runner/DebugProfile.entitlements | 2 + macos/Runner/Release.entitlements | 2 + 13 files changed, 1430 insertions(+), 83 deletions(-) create mode 100644 lib/core/config/countries.dart diff --git a/lib/backend/api.dart b/lib/backend/api.dart index e69de29..92f4e2c 100644 --- a/lib/backend/api.dart +++ b/lib/backend/api.dart @@ -0,0 +1,198 @@ +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'; + +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.broadcast(); + + Stream get stateStream => _stateController.stream; + SessionState get state => _sessionState; + + StreamSubscription? _dataSubscription; + StreamSubscription? _socketStateSubscription; + Timer? _pingTimer; + Timer? _reconnectTimer; + int _reconnectAttempts = 0; + bool _autoReconnect = false; + + // Публичное API + + /// Подключается к серверу, шлёт хэндшейк, запускает пинг. + Future 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 disconnect() async { + _autoReconnect = false; + _reconnectTimer?.cancel(); + _cleanup(); + await _connection.disconnect(); + _setSessionState(SessionState.disconnected); + } + + /// Отправляет хэндшейк (opcode 6). + Future sendHandshake() { + // TODO: заменить захардкоженные данные на реальные + final payload = { + 'mt_instanceid': '550e8400-e29b-41d4-a716-446655440000', + 'clientSessionId': 42, + 'deviceId': 'a1b2c3d4e5f6a7b8', + 'userAgent': { + 'deviceType': "ANDROID", + 'locale': 'en', + 'deviceLocale': 'en_US', + 'osVersion': 'Ondroid 14', + 'deviceName': 'KometPhone', + 'appVersion': '26.8.1', + 'screen': '1920x1080', + 'timezone': 'Europe/Moscow', + 'pushDeviceType': 'GCM', + 'arch': 'arm64', + 'buildNumber': 6606, + }, + }; + + return sendRequest(Opcode.sessionInit, payload); + } + + /// Отправляет запрос и ждёт ответ от сервера. + Future sendRequest( + int opcode, + Map 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 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); + } +} diff --git a/lib/core/config/config.dart b/lib/core/config/config.dart index e69de29..4ef18b3 100644 --- a/lib/core/config/config.dart +++ b/lib/core/config/config.dart @@ -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; +} diff --git a/lib/core/config/countries.dart b/lib/core/config/countries.dart new file mode 100644 index 0000000..860f069 --- /dev/null +++ b/lib/core/config/countries.dart @@ -0,0 +1,456 @@ +import 'dart:convert'; + +class CountryName { + final String code; // ISO 3166-1 alpha-2, например "RU" + final String en; + final String ru; + + const CountryName({ + required this.code, + required this.en, + required this.ru, + }); +} + +/// Полный список стран (195 государств) с названием на русском и английском. +/// +/// Источник данных: +/// https://github.com/stefangabos/world_countries +final List allCountries = _buildCountries(); + +/// Быстрый доступ по ISO-коду. +final Map countriesByCode = { + for (final country in allCountries) country.code: country, +}; + +/// Пример использования: +/// ```dart +/// final country = exampleCountryLookup('RU'); +/// print(country?['ru']); // Россия +/// print(country?['en']); // Russian Federation +/// ``` +Map? exampleCountryLookup(String code) { + final country = countriesByCode[code.toUpperCase()]; + if (country == null) return null; + + return { + 'code': country.code, + 'ru': country.ru, + 'en': country.en, + }; +} + +List _buildCountries() { + final enList = (jsonDecode(_countriesEnJson) as List) + .cast>(); + final ruList = (jsonDecode(_countriesRuJson) as List) + .cast>(); + + final ruByCode = { + 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; + + return CountryName(code: code, en: en, ru: ru); + }).toList(); + + countries.sort((a, b) => a.en.compareTo(b.en)); + return countries; +} + +const String _countriesEnJson = '''[ +{"id":4,"alpha2":"af","alpha3":"afg","name":"Afghanistan"}, +{"id":8,"alpha2":"al","alpha3":"alb","name":"Albania"}, +{"id":12,"alpha2":"dz","alpha3":"dza","name":"Algeria"}, +{"id":20,"alpha2":"ad","alpha3":"and","name":"Andorra"}, +{"id":24,"alpha2":"ao","alpha3":"ago","name":"Angola"}, +{"id":28,"alpha2":"ag","alpha3":"atg","name":"Antigua and Barbuda"}, +{"id":32,"alpha2":"ar","alpha3":"arg","name":"Argentina"}, +{"id":51,"alpha2":"am","alpha3":"arm","name":"Armenia"}, +{"id":36,"alpha2":"au","alpha3":"aus","name":"Australia"}, +{"id":40,"alpha2":"at","alpha3":"aut","name":"Austria"}, +{"id":31,"alpha2":"az","alpha3":"aze","name":"Azerbaijan"}, +{"id":44,"alpha2":"bs","alpha3":"bhs","name":"Bahamas"}, +{"id":48,"alpha2":"bh","alpha3":"bhr","name":"Bahrain"}, +{"id":50,"alpha2":"bd","alpha3":"bgd","name":"Bangladesh"}, +{"id":52,"alpha2":"bb","alpha3":"brb","name":"Barbados"}, +{"id":112,"alpha2":"by","alpha3":"blr","name":"Belarus"}, +{"id":56,"alpha2":"be","alpha3":"bel","name":"Belgium"}, +{"id":84,"alpha2":"bz","alpha3":"blz","name":"Belize"}, +{"id":204,"alpha2":"bj","alpha3":"ben","name":"Benin"}, +{"id":64,"alpha2":"bt","alpha3":"btn","name":"Bhutan"}, +{"id":68,"alpha2":"bo","alpha3":"bol","name":"Bolivia, Plurinational State of"}, +{"id":70,"alpha2":"ba","alpha3":"bih","name":"Bosnia and Herzegovina"}, +{"id":72,"alpha2":"bw","alpha3":"bwa","name":"Botswana"}, +{"id":76,"alpha2":"br","alpha3":"bra","name":"Brazil"}, +{"id":96,"alpha2":"bn","alpha3":"brn","name":"Brunei Darussalam"}, +{"id":100,"alpha2":"bg","alpha3":"bgr","name":"Bulgaria"}, +{"id":854,"alpha2":"bf","alpha3":"bfa","name":"Burkina Faso"}, +{"id":108,"alpha2":"bi","alpha3":"bdi","name":"Burundi"}, +{"id":132,"alpha2":"cv","alpha3":"cpv","name":"Cabo Verde"}, +{"id":116,"alpha2":"kh","alpha3":"khm","name":"Cambodia"}, +{"id":120,"alpha2":"cm","alpha3":"cmr","name":"Cameroon"}, +{"id":124,"alpha2":"ca","alpha3":"can","name":"Canada"}, +{"id":140,"alpha2":"cf","alpha3":"caf","name":"Central African Republic"}, +{"id":148,"alpha2":"td","alpha3":"tcd","name":"Chad"}, +{"id":152,"alpha2":"cl","alpha3":"chl","name":"Chile"}, +{"id":156,"alpha2":"cn","alpha3":"chn","name":"China"}, +{"id":170,"alpha2":"co","alpha3":"col","name":"Colombia"}, +{"id":174,"alpha2":"km","alpha3":"com","name":"Comoros"}, +{"id":178,"alpha2":"cg","alpha3":"cog","name":"Congo"}, +{"id":180,"alpha2":"cd","alpha3":"cod","name":"Congo, Democratic Republic of the"}, +{"id":188,"alpha2":"cr","alpha3":"cri","name":"Costa Rica"}, +{"id":384,"alpha2":"ci","alpha3":"civ","name":"Côte d'Ivoire"}, +{"id":191,"alpha2":"hr","alpha3":"hrv","name":"Croatia"}, +{"id":192,"alpha2":"cu","alpha3":"cub","name":"Cuba"}, +{"id":196,"alpha2":"cy","alpha3":"cyp","name":"Cyprus"}, +{"id":203,"alpha2":"cz","alpha3":"cze","name":"Czechia"}, +{"id":208,"alpha2":"dk","alpha3":"dnk","name":"Denmark"}, +{"id":262,"alpha2":"dj","alpha3":"dji","name":"Djibouti"}, +{"id":212,"alpha2":"dm","alpha3":"dma","name":"Dominica"}, +{"id":214,"alpha2":"do","alpha3":"dom","name":"Dominican Republic"}, +{"id":218,"alpha2":"ec","alpha3":"ecu","name":"Ecuador"}, +{"id":818,"alpha2":"eg","alpha3":"egy","name":"Egypt"}, +{"id":222,"alpha2":"sv","alpha3":"slv","name":"El Salvador"}, +{"id":226,"alpha2":"gq","alpha3":"gnq","name":"Equatorial Guinea"}, +{"id":232,"alpha2":"er","alpha3":"eri","name":"Eritrea"}, +{"id":233,"alpha2":"ee","alpha3":"est","name":"Estonia"}, +{"id":748,"alpha2":"sz","alpha3":"swz","name":"Eswatini"}, +{"id":231,"alpha2":"et","alpha3":"eth","name":"Ethiopia"}, +{"id":242,"alpha2":"fj","alpha3":"fji","name":"Fiji"}, +{"id":246,"alpha2":"fi","alpha3":"fin","name":"Finland"}, +{"id":250,"alpha2":"fr","alpha3":"fra","name":"France"}, +{"id":266,"alpha2":"ga","alpha3":"gab","name":"Gabon"}, +{"id":270,"alpha2":"gm","alpha3":"gmb","name":"Gambia"}, +{"id":268,"alpha2":"ge","alpha3":"geo","name":"Georgia"}, +{"id":276,"alpha2":"de","alpha3":"deu","name":"Germany"}, +{"id":288,"alpha2":"gh","alpha3":"gha","name":"Ghana"}, +{"id":300,"alpha2":"gr","alpha3":"grc","name":"Greece"}, +{"id":308,"alpha2":"gd","alpha3":"grd","name":"Grenada"}, +{"id":320,"alpha2":"gt","alpha3":"gtm","name":"Guatemala"}, +{"id":324,"alpha2":"gn","alpha3":"gin","name":"Guinea"}, +{"id":624,"alpha2":"gw","alpha3":"gnb","name":"Guinea-Bissau"}, +{"id":328,"alpha2":"gy","alpha3":"guy","name":"Guyana"}, +{"id":332,"alpha2":"ht","alpha3":"hti","name":"Haiti"}, +{"id":340,"alpha2":"hn","alpha3":"hnd","name":"Honduras"}, +{"id":348,"alpha2":"hu","alpha3":"hun","name":"Hungary"}, +{"id":352,"alpha2":"is","alpha3":"isl","name":"Iceland"}, +{"id":356,"alpha2":"in","alpha3":"ind","name":"India"}, +{"id":360,"alpha2":"id","alpha3":"idn","name":"Indonesia"}, +{"id":364,"alpha2":"ir","alpha3":"irn","name":"Iran, Islamic Republic of"}, +{"id":368,"alpha2":"iq","alpha3":"irq","name":"Iraq"}, +{"id":372,"alpha2":"ie","alpha3":"irl","name":"Ireland"}, +{"id":376,"alpha2":"il","alpha3":"isr","name":"Israel"}, +{"id":380,"alpha2":"it","alpha3":"ita","name":"Italy"}, +{"id":388,"alpha2":"jm","alpha3":"jam","name":"Jamaica"}, +{"id":392,"alpha2":"jp","alpha3":"jpn","name":"Japan"}, +{"id":400,"alpha2":"jo","alpha3":"jor","name":"Jordan"}, +{"id":398,"alpha2":"kz","alpha3":"kaz","name":"Kazakhstan"}, +{"id":404,"alpha2":"ke","alpha3":"ken","name":"Kenya"}, +{"id":296,"alpha2":"ki","alpha3":"kir","name":"Kiribati"}, +{"id":408,"alpha2":"kp","alpha3":"prk","name":"Korea, Democratic People's Republic of"}, +{"id":410,"alpha2":"kr","alpha3":"kor","name":"Korea, Republic of"}, +{"id":414,"alpha2":"kw","alpha3":"kwt","name":"Kuwait"}, +{"id":417,"alpha2":"kg","alpha3":"kgz","name":"Kyrgyzstan"}, +{"id":418,"alpha2":"la","alpha3":"lao","name":"Lao People's Democratic Republic"}, +{"id":428,"alpha2":"lv","alpha3":"lva","name":"Latvia"}, +{"id":422,"alpha2":"lb","alpha3":"lbn","name":"Lebanon"}, +{"id":426,"alpha2":"ls","alpha3":"lso","name":"Lesotho"}, +{"id":430,"alpha2":"lr","alpha3":"lbr","name":"Liberia"}, +{"id":434,"alpha2":"ly","alpha3":"lby","name":"Libya"}, +{"id":438,"alpha2":"li","alpha3":"lie","name":"Liechtenstein"}, +{"id":440,"alpha2":"lt","alpha3":"ltu","name":"Lithuania"}, +{"id":442,"alpha2":"lu","alpha3":"lux","name":"Luxembourg"}, +{"id":450,"alpha2":"mg","alpha3":"mdg","name":"Madagascar"}, +{"id":454,"alpha2":"mw","alpha3":"mwi","name":"Malawi"}, +{"id":458,"alpha2":"my","alpha3":"mys","name":"Malaysia"}, +{"id":462,"alpha2":"mv","alpha3":"mdv","name":"Maldives"}, +{"id":466,"alpha2":"ml","alpha3":"mli","name":"Mali"}, +{"id":470,"alpha2":"mt","alpha3":"mlt","name":"Malta"}, +{"id":584,"alpha2":"mh","alpha3":"mhl","name":"Marshall Islands"}, +{"id":478,"alpha2":"mr","alpha3":"mrt","name":"Mauritania"}, +{"id":480,"alpha2":"mu","alpha3":"mus","name":"Mauritius"}, +{"id":484,"alpha2":"mx","alpha3":"mex","name":"Mexico"}, +{"id":583,"alpha2":"fm","alpha3":"fsm","name":"Micronesia, Federated States of"}, +{"id":498,"alpha2":"md","alpha3":"mda","name":"Moldova, Republic of"}, +{"id":492,"alpha2":"mc","alpha3":"mco","name":"Monaco"}, +{"id":496,"alpha2":"mn","alpha3":"mng","name":"Mongolia"}, +{"id":499,"alpha2":"me","alpha3":"mne","name":"Montenegro"}, +{"id":504,"alpha2":"ma","alpha3":"mar","name":"Morocco"}, +{"id":508,"alpha2":"mz","alpha3":"moz","name":"Mozambique"}, +{"id":104,"alpha2":"mm","alpha3":"mmr","name":"Myanmar"}, +{"id":516,"alpha2":"na","alpha3":"nam","name":"Namibia"}, +{"id":520,"alpha2":"nr","alpha3":"nru","name":"Nauru"}, +{"id":524,"alpha2":"np","alpha3":"npl","name":"Nepal"}, +{"id":528,"alpha2":"nl","alpha3":"nld","name":"Netherlands"}, +{"id":554,"alpha2":"nz","alpha3":"nzl","name":"New Zealand"}, +{"id":558,"alpha2":"ni","alpha3":"nic","name":"Nicaragua"}, +{"id":562,"alpha2":"ne","alpha3":"ner","name":"Niger"}, +{"id":566,"alpha2":"ng","alpha3":"nga","name":"Nigeria"}, +{"id":807,"alpha2":"mk","alpha3":"mkd","name":"North Macedonia"}, +{"id":578,"alpha2":"no","alpha3":"nor","name":"Norway"}, +{"id":512,"alpha2":"om","alpha3":"omn","name":"Oman"}, +{"id":586,"alpha2":"pk","alpha3":"pak","name":"Pakistan"}, +{"id":585,"alpha2":"pw","alpha3":"plw","name":"Palau"}, +{"id":591,"alpha2":"pa","alpha3":"pan","name":"Panama"}, +{"id":598,"alpha2":"pg","alpha3":"png","name":"Papua New Guinea"}, +{"id":600,"alpha2":"py","alpha3":"pry","name":"Paraguay"}, +{"id":604,"alpha2":"pe","alpha3":"per","name":"Peru"}, +{"id":608,"alpha2":"ph","alpha3":"phl","name":"Philippines"}, +{"id":616,"alpha2":"pl","alpha3":"pol","name":"Poland"}, +{"id":620,"alpha2":"pt","alpha3":"prt","name":"Portugal"}, +{"id":634,"alpha2":"qa","alpha3":"qat","name":"Qatar"}, +{"id":642,"alpha2":"ro","alpha3":"rou","name":"Romania"}, +{"id":643,"alpha2":"ru","alpha3":"rus","name":"Russian Federation"}, +{"id":646,"alpha2":"rw","alpha3":"rwa","name":"Rwanda"}, +{"id":659,"alpha2":"kn","alpha3":"kna","name":"Saint Kitts and Nevis"}, +{"id":662,"alpha2":"lc","alpha3":"lca","name":"Saint Lucia"}, +{"id":670,"alpha2":"vc","alpha3":"vct","name":"Saint Vincent and the Grenadines"}, +{"id":882,"alpha2":"ws","alpha3":"wsm","name":"Samoa"}, +{"id":674,"alpha2":"sm","alpha3":"smr","name":"San Marino"}, +{"id":678,"alpha2":"st","alpha3":"stp","name":"Sao Tome and Principe"}, +{"id":682,"alpha2":"sa","alpha3":"sau","name":"Saudi Arabia"}, +{"id":686,"alpha2":"sn","alpha3":"sen","name":"Senegal"}, +{"id":688,"alpha2":"rs","alpha3":"srb","name":"Serbia"}, +{"id":690,"alpha2":"sc","alpha3":"syc","name":"Seychelles"}, +{"id":694,"alpha2":"sl","alpha3":"sle","name":"Sierra Leone"}, +{"id":702,"alpha2":"sg","alpha3":"sgp","name":"Singapore"}, +{"id":703,"alpha2":"sk","alpha3":"svk","name":"Slovakia"}, +{"id":705,"alpha2":"si","alpha3":"svn","name":"Slovenia"}, +{"id":90,"alpha2":"sb","alpha3":"slb","name":"Solomon Islands"}, +{"id":706,"alpha2":"so","alpha3":"som","name":"Somalia"}, +{"id":710,"alpha2":"za","alpha3":"zaf","name":"South Africa"}, +{"id":728,"alpha2":"ss","alpha3":"ssd","name":"South Sudan"}, +{"id":724,"alpha2":"es","alpha3":"esp","name":"Spain"}, +{"id":144,"alpha2":"lk","alpha3":"lka","name":"Sri Lanka"}, +{"id":729,"alpha2":"sd","alpha3":"sdn","name":"Sudan"}, +{"id":740,"alpha2":"sr","alpha3":"sur","name":"Suriname"}, +{"id":752,"alpha2":"se","alpha3":"swe","name":"Sweden"}, +{"id":756,"alpha2":"ch","alpha3":"che","name":"Switzerland"}, +{"id":760,"alpha2":"sy","alpha3":"syr","name":"Syrian Arab Republic"}, +{"id":762,"alpha2":"tj","alpha3":"tjk","name":"Tajikistan"}, +{"id":834,"alpha2":"tz","alpha3":"tza","name":"Tanzania, United Republic of"}, +{"id":764,"alpha2":"th","alpha3":"tha","name":"Thailand"}, +{"id":626,"alpha2":"tl","alpha3":"tls","name":"Timor-Leste"}, +{"id":768,"alpha2":"tg","alpha3":"tgo","name":"Togo"}, +{"id":776,"alpha2":"to","alpha3":"ton","name":"Tonga"}, +{"id":780,"alpha2":"tt","alpha3":"tto","name":"Trinidad and Tobago"}, +{"id":788,"alpha2":"tn","alpha3":"tun","name":"Tunisia"}, +{"id":792,"alpha2":"tr","alpha3":"tur","name":"Türkiye"}, +{"id":795,"alpha2":"tm","alpha3":"tkm","name":"Turkmenistan"}, +{"id":798,"alpha2":"tv","alpha3":"tuv","name":"Tuvalu"}, +{"id":800,"alpha2":"ug","alpha3":"uga","name":"Uganda"}, +{"id":804,"alpha2":"ua","alpha3":"ukr","name":"Ukraine"}, +{"id":784,"alpha2":"ae","alpha3":"are","name":"United Arab Emirates"}, +{"id":826,"alpha2":"gb","alpha3":"gbr","name":"United Kingdom of Great Britain and Northern Ireland"}, +{"id":840,"alpha2":"us","alpha3":"usa","name":"United States of America"}, +{"id":858,"alpha2":"uy","alpha3":"ury","name":"Uruguay"}, +{"id":860,"alpha2":"uz","alpha3":"uzb","name":"Uzbekistan"}, +{"id":548,"alpha2":"vu","alpha3":"vut","name":"Vanuatu"}, +{"id":862,"alpha2":"ve","alpha3":"ven","name":"Venezuela, Bolivarian Republic of"}, +{"id":704,"alpha2":"vn","alpha3":"vnm","name":"Viet Nam"}, +{"id":887,"alpha2":"ye","alpha3":"yem","name":"Yemen"}, +{"id":894,"alpha2":"zm","alpha3":"zmb","name":"Zambia"}, +{"id":716,"alpha2":"zw","alpha3":"zwe","name":"Zimbabwe"} +]'''; + +const String _countriesRuJson = '''[ +{"id":36,"alpha2":"au","alpha3":"aus","name":"Австралия"}, +{"id":40,"alpha2":"at","alpha3":"aut","name":"Австрия"}, +{"id":31,"alpha2":"az","alpha3":"aze","name":"Азербайджан"}, +{"id":8,"alpha2":"al","alpha3":"alb","name":"Албания"}, +{"id":12,"alpha2":"dz","alpha3":"dza","name":"Алжир"}, +{"id":24,"alpha2":"ao","alpha3":"ago","name":"Ангола"}, +{"id":20,"alpha2":"ad","alpha3":"and","name":"Андорра"}, +{"id":28,"alpha2":"ag","alpha3":"atg","name":"Антигуа и Барбуда"}, +{"id":32,"alpha2":"ar","alpha3":"arg","name":"Аргентина"}, +{"id":51,"alpha2":"am","alpha3":"arm","name":"Армения"}, +{"id":4,"alpha2":"af","alpha3":"afg","name":"Афганистан"}, +{"id":44,"alpha2":"bs","alpha3":"bhs","name":"Багамские Острова"}, +{"id":50,"alpha2":"bd","alpha3":"bgd","name":"Бангладеш"}, +{"id":52,"alpha2":"bb","alpha3":"brb","name":"Барбадос"}, +{"id":48,"alpha2":"bh","alpha3":"bhr","name":"Бахрейн"}, +{"id":84,"alpha2":"bz","alpha3":"blz","name":"Белиз"}, +{"id":112,"alpha2":"by","alpha3":"blr","name":"Беларусь"}, +{"id":56,"alpha2":"be","alpha3":"bel","name":"Бельгия"}, +{"id":204,"alpha2":"bj","alpha3":"ben","name":"Бенин"}, +{"id":100,"alpha2":"bg","alpha3":"bgr","name":"Болгария"}, +{"id":68,"alpha2":"bo","alpha3":"bol","name":"Боливия"}, +{"id":70,"alpha2":"ba","alpha3":"bih","name":"Босния и Герцеговина"}, +{"id":72,"alpha2":"bw","alpha3":"bwa","name":"Ботсвана"}, +{"id":76,"alpha2":"br","alpha3":"bra","name":"Бразилия"}, +{"id":96,"alpha2":"bn","alpha3":"brn","name":"Бруней"}, +{"id":854,"alpha2":"bf","alpha3":"bfa","name":"Буркина-Фасо"}, +{"id":108,"alpha2":"bi","alpha3":"bdi","name":"Бурунди"}, +{"id":64,"alpha2":"bt","alpha3":"btn","name":"Бутан"}, +{"id":548,"alpha2":"vu","alpha3":"vut","name":"Вануату"}, +{"id":826,"alpha2":"gb","alpha3":"gbr","name":"Великобритания"}, +{"id":348,"alpha2":"hu","alpha3":"hun","name":"Венгрия"}, +{"id":862,"alpha2":"ve","alpha3":"ven","name":"Венесуэла"}, +{"id":626,"alpha2":"tl","alpha3":"tls","name":"Восточный Тимор"}, +{"id":704,"alpha2":"vn","alpha3":"vnm","name":"Вьетнам"}, +{"id":266,"alpha2":"ga","alpha3":"gab","name":"Габон"}, +{"id":332,"alpha2":"ht","alpha3":"hti","name":"Гаити"}, +{"id":328,"alpha2":"gy","alpha3":"guy","name":"Гайана"}, +{"id":270,"alpha2":"gm","alpha3":"gmb","name":"Гамбия"}, +{"id":288,"alpha2":"gh","alpha3":"gha","name":"Гана"}, +{"id":320,"alpha2":"gt","alpha3":"gtm","name":"Гватемала"}, +{"id":324,"alpha2":"gn","alpha3":"gin","name":"Гвинея"}, +{"id":624,"alpha2":"gw","alpha3":"gnb","name":"Гвинея-Бисау"}, +{"id":276,"alpha2":"de","alpha3":"deu","name":"Германия"}, +{"id":340,"alpha2":"hn","alpha3":"hnd","name":"Гондурас"}, +{"id":308,"alpha2":"gd","alpha3":"grd","name":"Гренада"}, +{"id":300,"alpha2":"gr","alpha3":"grc","name":"Греция"}, +{"id":268,"alpha2":"ge","alpha3":"geo","name":"Грузия"}, +{"id":208,"alpha2":"dk","alpha3":"dnk","name":"Дания"}, +{"id":262,"alpha2":"dj","alpha3":"dji","name":"Джибути"}, +{"id":212,"alpha2":"dm","alpha3":"dma","name":"Доминика"}, +{"id":214,"alpha2":"do","alpha3":"dom","name":"Доминиканская Республика"}, +{"id":180,"alpha2":"cd","alpha3":"cod","name":"ДР Конго"}, +{"id":818,"alpha2":"eg","alpha3":"egy","name":"Египет"}, +{"id":894,"alpha2":"zm","alpha3":"zmb","name":"Замбия"}, +{"id":716,"alpha2":"zw","alpha3":"zwe","name":"Зимбабве"}, +{"id":376,"alpha2":"il","alpha3":"isr","name":"Израиль"}, +{"id":356,"alpha2":"in","alpha3":"ind","name":"Индия"}, +{"id":360,"alpha2":"id","alpha3":"idn","name":"Индонезия"}, +{"id":400,"alpha2":"jo","alpha3":"jor","name":"Иордания"}, +{"id":368,"alpha2":"iq","alpha3":"irq","name":"Ирак"}, +{"id":364,"alpha2":"ir","alpha3":"irn","name":"Иран"}, +{"id":372,"alpha2":"ie","alpha3":"irl","name":"Ирландия"}, +{"id":352,"alpha2":"is","alpha3":"isl","name":"Исландия"}, +{"id":724,"alpha2":"es","alpha3":"esp","name":"Испания"}, +{"id":380,"alpha2":"it","alpha3":"ita","name":"Италия"}, +{"id":887,"alpha2":"ye","alpha3":"yem","name":"Йемен"}, +{"id":132,"alpha2":"cv","alpha3":"cpv","name":"Кабо-Верде"}, +{"id":398,"alpha2":"kz","alpha3":"kaz","name":"Казахстан"}, +{"id":116,"alpha2":"kh","alpha3":"khm","name":"Камбоджа"}, +{"id":120,"alpha2":"cm","alpha3":"cmr","name":"Камерун"}, +{"id":124,"alpha2":"ca","alpha3":"can","name":"Канада"}, +{"id":634,"alpha2":"qa","alpha3":"qat","name":"Катар"}, +{"id":404,"alpha2":"ke","alpha3":"ken","name":"Кения"}, +{"id":196,"alpha2":"cy","alpha3":"cyp","name":"Кипр"}, +{"id":417,"alpha2":"kg","alpha3":"kgz","name":"Кыргызстан"}, +{"id":296,"alpha2":"ki","alpha3":"kir","name":"Кирибати"}, +{"id":408,"alpha2":"kp","alpha3":"prk","name":"КНДР (Корейская Народно-Демократическая Республика)"}, +{"id":156,"alpha2":"cn","alpha3":"chn","name":"Китай (Китайская Народная Республика)"}, +{"id":170,"alpha2":"co","alpha3":"col","name":"Колумбия"}, +{"id":174,"alpha2":"km","alpha3":"com","name":"Коморы"}, +{"id":188,"alpha2":"cr","alpha3":"cri","name":"Коста-Рика"}, +{"id":384,"alpha2":"ci","alpha3":"civ","name":"Кот-д’Ивуар"}, +{"id":192,"alpha2":"cu","alpha3":"cub","name":"Куба"}, +{"id":414,"alpha2":"kw","alpha3":"kwt","name":"Кувейт"}, +{"id":418,"alpha2":"la","alpha3":"lao","name":"Лаос"}, +{"id":428,"alpha2":"lv","alpha3":"lva","name":"Латвия"}, +{"id":426,"alpha2":"ls","alpha3":"lso","name":"Лесото"}, +{"id":430,"alpha2":"lr","alpha3":"lbr","name":"Либерия"}, +{"id":422,"alpha2":"lb","alpha3":"lbn","name":"Ливан"}, +{"id":434,"alpha2":"ly","alpha3":"lby","name":"Ливия"}, +{"id":440,"alpha2":"lt","alpha3":"ltu","name":"Литва"}, +{"id":438,"alpha2":"li","alpha3":"lie","name":"Лихтенштейн"}, +{"id":442,"alpha2":"lu","alpha3":"lux","name":"Люксембург"}, +{"id":480,"alpha2":"mu","alpha3":"mus","name":"Маврикий"}, +{"id":478,"alpha2":"mr","alpha3":"mrt","name":"Мавритания"}, +{"id":450,"alpha2":"mg","alpha3":"mdg","name":"Мадагаскар"}, +{"id":807,"alpha2":"mk","alpha3":"mkd","name":"Северная Македония"}, +{"id":454,"alpha2":"mw","alpha3":"mwi","name":"Малави"}, +{"id":458,"alpha2":"my","alpha3":"mys","name":"Малайзия"}, +{"id":466,"alpha2":"ml","alpha3":"mli","name":"Мали"}, +{"id":462,"alpha2":"mv","alpha3":"mdv","name":"Мальдивы"}, +{"id":470,"alpha2":"mt","alpha3":"mlt","name":"Мальта"}, +{"id":504,"alpha2":"ma","alpha3":"mar","name":"Марокко"}, +{"id":584,"alpha2":"mh","alpha3":"mhl","name":"Маршалловы Острова"}, +{"id":484,"alpha2":"mx","alpha3":"mex","name":"Мексика"}, +{"id":583,"alpha2":"fm","alpha3":"fsm","name":"Микронезия"}, +{"id":508,"alpha2":"mz","alpha3":"moz","name":"Мозамбик"}, +{"id":498,"alpha2":"md","alpha3":"mda","name":"Молдова"}, +{"id":492,"alpha2":"mc","alpha3":"mco","name":"Монако"}, +{"id":496,"alpha2":"mn","alpha3":"mng","name":"Монголия"}, +{"id":104,"alpha2":"mm","alpha3":"mmr","name":"Мьянма"}, +{"id":516,"alpha2":"na","alpha3":"nam","name":"Намибия"}, +{"id":520,"alpha2":"nr","alpha3":"nru","name":"Науру"}, +{"id":524,"alpha2":"np","alpha3":"npl","name":"Непал"}, +{"id":562,"alpha2":"ne","alpha3":"ner","name":"Нигер"}, +{"id":566,"alpha2":"ng","alpha3":"nga","name":"Нигерия"}, +{"id":528,"alpha2":"nl","alpha3":"nld","name":"Нидерланды"}, +{"id":558,"alpha2":"ni","alpha3":"nic","name":"Никарагуа"}, +{"id":554,"alpha2":"nz","alpha3":"nzl","name":"Новая Зеландия"}, +{"id":578,"alpha2":"no","alpha3":"nor","name":"Норвегия"}, +{"id":784,"alpha2":"ae","alpha3":"are","name":"ОАЭ"}, +{"id":512,"alpha2":"om","alpha3":"omn","name":"Оман"}, +{"id":586,"alpha2":"pk","alpha3":"pak","name":"Пакистан"}, +{"id":585,"alpha2":"pw","alpha3":"plw","name":"Палау"}, +{"id":591,"alpha2":"pa","alpha3":"pan","name":"Панама"}, +{"id":598,"alpha2":"pg","alpha3":"png","name":"Папуа — Новая Гвинея"}, +{"id":600,"alpha2":"py","alpha3":"pry","name":"Парагвай"}, +{"id":604,"alpha2":"pe","alpha3":"per","name":"Перу"}, +{"id":616,"alpha2":"pl","alpha3":"pol","name":"Польша"}, +{"id":620,"alpha2":"pt","alpha3":"prt","name":"Португалия"}, +{"id":178,"alpha2":"cg","alpha3":"cog","name":"Республика Конго"}, +{"id":410,"alpha2":"kr","alpha3":"kor","name":"Республика Корея"}, +{"id":643,"alpha2":"ru","alpha3":"rus","name":"Россия"}, +{"id":646,"alpha2":"rw","alpha3":"rwa","name":"Руанда"}, +{"id":642,"alpha2":"ro","alpha3":"rou","name":"Румыния"}, +{"id":222,"alpha2":"sv","alpha3":"slv","name":"Сальвадор"}, +{"id":882,"alpha2":"ws","alpha3":"wsm","name":"Самоа"}, +{"id":674,"alpha2":"sm","alpha3":"smr","name":"Сан-Марино"}, +{"id":678,"alpha2":"st","alpha3":"stp","name":"Сан-Томе и Принсипи"}, +{"id":682,"alpha2":"sa","alpha3":"sau","name":"Саудовская Аравия"}, +{"id":690,"alpha2":"sc","alpha3":"syc","name":"Сейшельские Острова"}, +{"id":686,"alpha2":"sn","alpha3":"sen","name":"Сенегал"}, +{"id":670,"alpha2":"vc","alpha3":"vct","name":"Сент-Винсент и Гренадины"}, +{"id":659,"alpha2":"kn","alpha3":"kna","name":"Сент-Китс и Невис"}, +{"id":662,"alpha2":"lc","alpha3":"lca","name":"Сент-Люсия"}, +{"id":688,"alpha2":"rs","alpha3":"srb","name":"Сербия"}, +{"id":702,"alpha2":"sg","alpha3":"sgp","name":"Сингапур"}, +{"id":760,"alpha2":"sy","alpha3":"syr","name":"Сирия"}, +{"id":703,"alpha2":"sk","alpha3":"svk","name":"Словакия"}, +{"id":705,"alpha2":"si","alpha3":"svn","name":"Словения"}, +{"id":90,"alpha2":"sb","alpha3":"slb","name":"Соломоновы Острова"}, +{"id":706,"alpha2":"so","alpha3":"som","name":"Сомали"}, +{"id":729,"alpha2":"sd","alpha3":"sdn","name":"Судан"}, +{"id":740,"alpha2":"sr","alpha3":"sur","name":"Суринам"}, +{"id":840,"alpha2":"us","alpha3":"usa","name":"США"}, +{"id":694,"alpha2":"sl","alpha3":"sle","name":"Сьерра-Леоне"}, +{"id":762,"alpha2":"tj","alpha3":"tjk","name":"Таджикистан"}, +{"id":764,"alpha2":"th","alpha3":"tha","name":"Таиланд"}, +{"id":834,"alpha2":"tz","alpha3":"tza","name":"Танзания"}, +{"id":768,"alpha2":"tg","alpha3":"tgo","name":"Того"}, +{"id":776,"alpha2":"to","alpha3":"ton","name":"Тонга"}, +{"id":780,"alpha2":"tt","alpha3":"tto","name":"Тринидад и Тобаго"}, +{"id":798,"alpha2":"tv","alpha3":"tuv","name":"Тувалу"}, +{"id":788,"alpha2":"tn","alpha3":"tun","name":"Тунис"}, +{"id":795,"alpha2":"tm","alpha3":"tkm","name":"Туркменистан"}, +{"id":792,"alpha2":"tr","alpha3":"tur","name":"Турция"}, +{"id":800,"alpha2":"ug","alpha3":"uga","name":"Уганда"}, +{"id":860,"alpha2":"uz","alpha3":"uzb","name":"Узбекистан"}, +{"id":804,"alpha2":"ua","alpha3":"ukr","name":"Украина"}, +{"id":858,"alpha2":"uy","alpha3":"ury","name":"Уругвай"}, +{"id":242,"alpha2":"fj","alpha3":"fji","name":"Фиджи"}, +{"id":608,"alpha2":"ph","alpha3":"phl","name":"Филиппины"}, +{"id":246,"alpha2":"fi","alpha3":"fin","name":"Финляндия"}, +{"id":250,"alpha2":"fr","alpha3":"fra","name":"Франция"}, +{"id":191,"alpha2":"hr","alpha3":"hrv","name":"Хорватия"}, +{"id":140,"alpha2":"cf","alpha3":"caf","name":"ЦАР"}, +{"id":148,"alpha2":"td","alpha3":"tcd","name":"Чад"}, +{"id":499,"alpha2":"me","alpha3":"mne","name":"Черногория"}, +{"id":203,"alpha2":"cz","alpha3":"cze","name":"Чехия"}, +{"id":152,"alpha2":"cl","alpha3":"chl","name":"Чили"}, +{"id":756,"alpha2":"ch","alpha3":"che","name":"Швейцария"}, +{"id":752,"alpha2":"se","alpha3":"swe","name":"Швеция"}, +{"id":144,"alpha2":"lk","alpha3":"lka","name":"Шри-Ланка"}, +{"id":218,"alpha2":"ec","alpha3":"ecu","name":"Эквадор"}, +{"id":226,"alpha2":"gq","alpha3":"gnq","name":"Экваториальная Гвинея"}, +{"id":232,"alpha2":"er","alpha3":"eri","name":"Эритрея"}, +{"id":748,"alpha2":"sz","alpha3":"swz","name":"Эсватини"}, +{"id":233,"alpha2":"ee","alpha3":"est","name":"Эстония"}, +{"id":231,"alpha2":"et","alpha3":"eth","name":"Эфиопия"}, +{"id":710,"alpha2":"za","alpha3":"zaf","name":"ЮАР"}, +{"id":728,"alpha2":"ss","alpha3":"ssd","name":"Южный Судан"}, +{"id":388,"alpha2":"jm","alpha3":"jam","name":"Ямайка"}, +{"id":392,"alpha2":"jp","alpha3":"jpn","name":"Япония"} +]'''; diff --git a/lib/core/protocol/opcode_map.dart b/lib/core/protocol/opcode_map.dart index e69de29..75cc983 100644 --- a/lib/core/protocol/opcode_map.dart +++ b/lib/core/protocol/opcode_map.dart @@ -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 _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', + }; +} diff --git a/lib/core/protocol/packet.dart b/lib/core/protocol/packet.dart index 34fc2ba..d7c95f9 100644 --- a/lib/core/protocol/packet.dart +++ b/lib/core/protocol/packet.dart @@ -4,132 +4,170 @@ import 'package:msgpack_dart/msgpack_dart.dart' as msgpack; import '../utils/logger.dart'; -final int apiVersion = 10; -int seq = 1; +/// ver(1) + cmd(2) + seq(1) + opcode(2) + packedLen(4) = 10 +const int headerSize = 10; +const int _maxDecompressedSize = 1048576; // 1 MB -/* - Базовый класс пакета +/// Типы команд в протоколе +abstract class CmdType { + static const int request = 0; // запрос клиента + static const int push = 1; // пуш от сервера + static const int ok = 0x100; // ответ: ок + static const int notFound = 0x200; // ответ: не найдено + static const int error = 0x300; // ответ: ошибка +} - api - версия API (по умолчанию 10 для сокета) - cmd - от кого был прислан пакет ( - 0 - клиент (если отправляется серверу) - 1 - сервер - 3 - отправляется вместе с ошибкой) - seq - счёт пакетов (начинается с 1) - opcode - показывает, для чего отправляется пакет (подробнее в /lib/core/protocol/opcode_map.dart) - payload - содержимое пакета (отправляется с сервера ввиде msgpack) -*/ +/// Бинарный пакет +/// +/// Формат заголовка (10 байт): +/// ``` +/// [0] ver — версия протокола (uint8) +/// [1..2] cmd — тип команды (uint16 BE) +/// [3] seq — порядковый номер 0..255 (uint8) +/// [4..5] opcode — код операции (uint16 BE) +/// [6..9] packedLen — флаг сжатия [6] + длина payload [7..9] (uint32 BE) +/// [10..] payload — данные в MsgPack, опционально сжатые LZ4 +/// ``` class Packet { int api; int cmd; int seq; int opcode; - Map payload; + dynamic payload; Packet({ this.api = 10, this.cmd = 0, - this.seq = 1, + this.seq = 0, this.opcode = 0, - this.payload = const {}, + this.payload, }); - /// Выводит пакет в консоль - void printPacket() { - logger.i( - "Api: $api\nCmd: $cmd\nSeq: $seq\nOPCode: $opcode\nPayload: $payload", - ); - } + bool get isOk => cmd == CmdType.ok; + bool get isError => cmd == CmdType.error; + bool get isPush => cmd == CmdType.push; + + @override + String toString() => + 'Packet(ver=$api cmd=$cmd seq=$seq opcode=$opcode payload=$payload)'; } -/// Функция запаковки пакета -/// Принимает только opcode и payload, так как api и cmd -/// константы (10 и 0 соответственно), а seq считается менеджером сокета -/// Возращает запакованный пакет -Uint8List packPacket(int opcode, Map payload) { - // Объяснение каждой переменной смотри в классе Packet +/// Упаковка пакета для отправки на сервер +Uint8List packPacket(int opcode, Map payload, {int seq = 0}) { + final header = ByteData(headerSize); + header.setUint8(0, 10); + header.setUint16(1, CmdType.request, Endian.big); + header.setUint8(3, seq); + 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 payloadLen = payloadBytes.length & 0xFFFFFF; + header.setUint32(6, payloadLen, Endian.big); - final payloadLenB = Uint8List(4) - ..buffer.asByteData().setUint32(0, payloadLen, Endian.big); - - return Uint8List.fromList( - apiVerB + cmdB + seqB + opcodeB + payloadLenB + payloadBytes, - ); + return Uint8List.fromList(header.buffer.asUint8List() + payloadBytes); } -/// Функция распаковки пакета -/// Принимает байты пакета, которые отправляет сервер -/// Возращает Packet +/// Распаковка пакета от сервера Packet unpackPacket(Uint8List packet) { - // Для удобства расшифровки пакета переводим в ByteData - ByteData packetData = ByteData.view(packet.buffer); + final data = ByteData.view( + packet.buffer, + packet.offsetInBytes, + packet.lengthInBytes, + ); - // Объяснение каждой переменной смотри в классе Packet + final apiVer = data.getUint8(0); + final cmd = data.getUint16(1, Endian.big); + final seq = data.getUint8(3); + final opcode = data.getUint16(4, Endian.big); + final packedLen = data.getUint32(6, Endian.big); - // API версия и cmd представляют из себя 8 битные числа - var apiVer = packetData.getUint8(0) & 0xFF; - var cmd = packetData.getUint8(1) & 0xFF; + final compFlag = packedLen >> 24; + final payloadLength = packedLen & 0xFFFFFF; - // Sequence и OPCode представляют из себя 16 битные числа - var seq = packetData.getUint16(2) & 0xFFFF; - var opcode = packetData.getUint16(4) & 0xFFFF; + var payloadBytes = Uint8List.sublistView( + packet, + headerSize, + headerSize + payloadLength, + ); + dynamic payload; - // После базовых переменных идет длина пакета, является 32 битным числом - var packedLen = packetData.getUint32(6); - - // Compression flag показывает, сжат ли payload - var compFlag = packedLen >> 24; - - // Длина payload'а - var payloadLength = packedLen & 0xFFFFFF; - - // Байты payload'а, могут быть сжаты LZ4 - var payloadBytes = packet.buffer.asUint8List(10, payloadLength); - - var payload = {}; - - // Если payload пустой, ничего не делаем (так может быть при получении пинга) - if (payloadBytes.buffer.lengthInBytes > 0) { - // Если пакет сжат, используем LZ4 + if (payloadBytes.isNotEmpty) { if (compFlag != 0) { try { - final decompressedBytes = lz4Decompress( + payloadBytes = lz4Decompress( payloadBytes, - decompressedSize: 99999, + decompressedSize: _maxDecompressedSize, ); - - payloadBytes = decompressedBytes; - } catch (e) { - logger.e("Ошибка при декомпрессировании: $e", error: e); + } catch (_) { + // dart_lz4 не умеет block-формат, фолбэк на ручной декомпрессор + try { + payloadBytes = _lz4BlockDecompress(payloadBytes, _maxDecompressedSize); + } catch (e) { + logger.e("Ошибка декомпрессии LZ4: $e", error: e); + } } } - // Пробуем десериализовать msgpack try { payload = msgpack.deserialize(payloadBytes); } catch (e) { - logger.e("Ошибка при десериализации msgpack: $e", error: e); + logger.e("Ошибка десериализации MsgPack: $e", error: e); } } - // Собираем целый пакет и возращаем его - final fullPacket = Packet( + return Packet( api: apiVer, cmd: cmd, seq: seq, opcode: opcode, 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.generate(matchLen, (i) => out[start + (i % offset)])); + + if (dst.length > maxSize) throw StateError('LZ4: превышен лимит'); + } + + return dst.toBytes(); } diff --git a/lib/core/transport/connection.dart b/lib/core/transport/connection.dart index e69de29..16c4444 100644 --- a/lib/core/transport/connection.dart +++ b/lib/core/transport/connection.dart @@ -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.broadcast(); + final _stateController = StreamController.broadcast(); + + Stream get dataStream => _dataController.stream; + Stream 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 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 disconnect() async { + final socket = _socket; + _socket = null; + + if (socket != null) { + try { + socket.destroy(); + } catch (_) {} + } + + _setState(SocketState.disconnected); + } + + void dispose() { + disconnect(); + _dataController.close(); + _stateController.close(); + } +} diff --git a/lib/core/transport/dispatcher.dart b/lib/core/transport/dispatcher.dart index e69de29..efeec15 100644 --- a/lib/core/transport/dispatcher.dart +++ b/lib/core/transport/dispatcher.dart @@ -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> _pendingRequests = {}; + final Map _requestTimestamps = {}; + final Map _pushHandlers = {}; + + final _pushController = StreamController.broadcast(); + + /// Стрим всех входящих пушей (cmd == 1) + Stream get pushStream => _pushController.stream; + + Timer? _cleanupTimer; + + PacketDispatcher() { + _cleanupTimer = Timer.periodic( + const Duration(seconds: 10), + (_) => _cleanupStaleRequests(), + ); + } + + /// Регистрирует ожидание ответа — future завершится когда + /// придёт пакет с совпадающим seq. + Future registerPending(int seq) { + final completer = Completer(); + _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 = []; + + _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(); + } +} diff --git a/lib/core/transport/receiver.dart b/lib/core/transport/receiver.dart index e69de29..25af729 100644 --- a/lib/core/transport/receiver.dart +++ b/lib/core/transport/receiver.dart @@ -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 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 = []; + + 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); + } +} diff --git a/lib/core/transport/sender.dart b/lib/core/transport/sender.dart index e69de29..31a5c2c 100644 --- a/lib/core/transport/sender.dart +++ b/lib/core/transport/sender.dart @@ -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 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; + } +} diff --git a/lib/core/utils/logger.dart b/lib/core/utils/logger.dart index 5fbcd7b..580e134 100644 --- a/lib/core/utils/logger.dart +++ b/lib/core/utils/logger.dart @@ -3,7 +3,7 @@ import 'package:logger/logger.dart'; final logger = Logger( // устанавливает минимальный уровень логов. // доступны уровни: all, trace, debug, info, warning, error, fatal, off - level: Level.info, + level: Level.all, printer: PrettyPrinter( methodCount: 0, errorMethodCount: 5, diff --git a/lib/main.dart b/lib/main.dart index 244a702..429dfbf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,11 @@ 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()); } diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index dddb8a3..08c3ab1 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.server + com.apple.security.network.client + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index 852fa1a..ee95ab7 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -4,5 +4,7 @@ com.apple.security.app-sandbox + com.apple.security.network.client + From 91c8cf9278887448927c96b6cba09959405aaf14 Mon Sep 17 00:00:00 2001 From: InviseDivine Date: Sun, 15 Mar 2026 17:27:54 +0200 Subject: [PATCH 2/3] =?UTF-8?q?FIX:=20cmd=20=D0=B2=20=D0=BF=D0=B0=D0=BA?= =?UTF-8?q?=D0=B5=D1=82=D0=B0=D1=85=20FEAT:=20=D0=BF=D0=BE=D0=BB=D1=83?= =?UTF-8?q?=D1=87=D0=B5=D0=BD=D0=B8=D0=B5=20user=20agent'=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/settings.json | 3 + lib/backend/api.dart | 84 ++++++++++++++++++--- lib/core/protocol/packet.dart | 58 +++++++++------ pubspec.lock | 135 +++++++++++++++++++++++++++++++--- pubspec.yaml | 3 + 5 files changed, 239 insertions(+), 44 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..08303a5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.sourceDirectory": "/run/media/invisedivine/Drive/! My projects/Komet/linux/runner" +} \ No newline at end of file diff --git a/lib/backend/api.dart b/lib/backend/api.dart index 92f4e2c..f014ad8 100644 --- a/lib/backend/api.dart +++ b/lib/backend/api.dart @@ -10,7 +10,18 @@ import '../core/transport/receiver.dart'; import '../core/transport/sender.dart'; import '../core/utils/logger.dart'; -enum SessionState { disconnected, connecting, connected, online } +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. /// @@ -39,7 +50,7 @@ class Api { /// Подключается к серверу, шлёт хэндшейк, запускает пинг. Future connect() async { if (_sessionState != SessionState.disconnected) return; - + // Ставим автоматический реконнект и статус подключения _autoReconnect = true; _setSessionState(SessionState.connecting); @@ -88,27 +99,75 @@ class Api { } /// Отправляет хэндшейк (opcode 6). - Future sendHandshake() { - // TODO: заменить захардкоженные данные на реальные + Future 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 = { 'mt_instanceid': '550e8400-e29b-41d4-a716-446655440000', 'clientSessionId': 42, 'deviceId': 'a1b2c3d4e5f6a7b8', 'userAgent': { - 'deviceType': "ANDROID", - 'locale': 'en', - 'deviceLocale': 'en_US', - 'osVersion': 'Ondroid 14', - 'deviceName': 'KometPhone', + 'deviceType': deviceType, + // Первые два символа из locale это и есть нужный нам аргумент + 'locale': "ru", + 'deviceLocale': Platform.localeName.substring(0, 2), + 'osVersion': osVersion, + 'deviceName': deviceName, 'appVersion': '26.8.1', 'screen': '1920x1080', - 'timezone': 'Europe/Moscow', + // 'screen': screenSize.width + 'x' + screenSize.height, + 'timezone': timezone, 'pushDeviceType': 'GCM', - 'arch': 'arm64', + 'arch': architecture, 'buildNumber': 6606, }, }; + print(payload); + print(Platform.version); return sendRequest(Opcode.sessionInit, payload); } @@ -142,7 +201,8 @@ class Api { _stateController.close(); } - // Внутрянка + // Внутрянка + void _setSessionState(SessionState state) { if (_sessionState == state) return; diff --git a/lib/core/protocol/packet.dart b/lib/core/protocol/packet.dart index d7c95f9..a7c2d62 100644 --- a/lib/core/protocol/packet.dart +++ b/lib/core/protocol/packet.dart @@ -4,7 +4,7 @@ import 'package:msgpack_dart/msgpack_dart.dart' as msgpack; import '../utils/logger.dart'; -/// ver(1) + cmd(2) + seq(1) + opcode(2) + packedLen(4) = 10 +/// ver(1) + cmd(1) + seq(2) + opcode(2) + packedLen(4) = 10 const int headerSize = 10; const int _maxDecompressedSize = 1048576; // 1 MB @@ -12,18 +12,19 @@ const int _maxDecompressedSize = 1048576; // 1 MB abstract class CmdType { static const int request = 0; // запрос клиента static const int push = 1; // пуш от сервера - static const int ok = 0x100; // ответ: ок - static const int notFound = 0x200; // ответ: не найдено - static const int error = 0x300; // ответ: ошибка + + static const int ok = 1; // ответ: ок + static const int notFound = 2; // ответ: не найдено + static const int error = 3; // ответ: ошибка } -/// Бинарный пакет +/// Распакованный бинарный пакет /// /// Формат заголовка (10 байт): /// ``` -/// [0] ver — версия протокола (uint8) -/// [1..2] cmd — тип команды (uint16 BE) -/// [3] seq — порядковый номер 0..255 (uint8) +/// [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 @@ -56,8 +57,8 @@ class Packet { Uint8List packPacket(int opcode, Map payload, {int seq = 0}) { final header = ByteData(headerSize); header.setUint8(0, 10); - header.setUint16(1, CmdType.request, Endian.big); - header.setUint8(3, seq); + header.setUint8(1, CmdType.request); + header.setUint16(2, seq, Endian.big); header.setUint16(4, opcode, Endian.big); final payloadBytes = msgpack.serialize(payload); @@ -69,28 +70,38 @@ Uint8List packPacket(int opcode, Map payload, {int seq = 0}) { /// Распаковка пакета от сервера Packet unpackPacket(Uint8List packet) { - final data = ByteData.view( + // Для удобства расшифровки пакета переводим в ByteData + ByteData packetData = ByteData.view( packet.buffer, packet.offsetInBytes, packet.lengthInBytes, ); - final apiVer = data.getUint8(0); - final cmd = data.getUint16(1, Endian.big); - final seq = data.getUint8(3); - final opcode = data.getUint16(4, Endian.big); - final packedLen = data.getUint32(6, Endian.big); + // Объяснение каждой переменной смотри в классе Packet + // API версия и cmd представляют из себя 8 битные числа + final apiVer = packetData.getUint8(0) & 0xFF; + final cmd = packetData.getUint8(1) & 0xFF; + + // Sequence и OPCode представляют из себя 16 битные числа + final seq = packetData.getUint16(2) & 0xFFFF; + final opcode = packetData.getUint16(4) & 0xFFFF; + + // После базовых переменных идет длина пакета, является 32 битным числом + final packedLen = packetData.getUint32(6); + + // Compression flag показывает, сжат ли payload final compFlag = packedLen >> 24; + + // Длина payload'а final payloadLength = packedLen & 0xFFFFFF; - var payloadBytes = Uint8List.sublistView( - packet, - headerSize, - headerSize + payloadLength, - ); - dynamic payload; + // Байты payload'а, могут быть сжаты LZ4 + var payloadBytes = packet.buffer.asUint8List(10, payloadLength); + dynamic payload; + print(compFlag); + // Если payload пустой, ничего не делаем (так может быть при получении пинга) if (payloadBytes.isNotEmpty) { if (compFlag != 0) { try { @@ -98,6 +109,7 @@ Packet unpackPacket(Uint8List packet) { payloadBytes, decompressedSize: _maxDecompressedSize, ); + } catch (_) { // dart_lz4 не умеет block-формат, фолбэк на ручной декомпрессор try { @@ -110,7 +122,7 @@ Packet unpackPacket(Uint8List packet) { try { payload = msgpack.deserialize(payloadBytes); - } catch (e) { + } catch (e) { logger.e("Ошибка десериализации MsgPack: $e", error: e); } } diff --git a/pubspec.lock b/pubspec.lock index 3e29bd2..bc155f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.4.1" clock: dependency: transitive description: @@ -57,6 +57,30 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: @@ -65,6 +89,22 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: "direct main" description: flutter @@ -83,6 +123,35 @@ packages: description: flutter source: sdk 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: dependency: transitive description: @@ -127,18 +196,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.19" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.13.0" meta: dependency: transitive description: @@ -163,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: flutter @@ -212,10 +289,26 @@ packages: dependency: transitive description: name: test_api - sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" url: "https://pub.dev" source: hosted - version: "0.7.7" + version: "0.7.10" + 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: dependency: transitive description: @@ -232,6 +325,30 @@ packages: url: "https://pub.dev" source: hosted 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: dart: ">=3.10.4 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.29.0" diff --git a/pubspec.yaml b/pubspec.yaml index a704a79..252d74e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: dart_lz4: ^1.0.0 msgpack_dart: ^1.0.1 logger: ^2.6.2 + device_info_plus: ^12.3.0 + flutter_timezone: ^5.0.1 + timezone: ^0.11.0 dev_dependencies: flutter_test: From 807ea0553ad70be21bfecaa36954c76d169446c2 Mon Sep 17 00:00:00 2001 From: Klockky Date: Sun, 15 Mar 2026 19:36:51 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D1=81=D0=BF=D0=B8=D1=81=D0=BE=D0=BA=20=D1=81=D1=82=D1=80?= =?UTF-8?q?=D0=B0=D0=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + ios/Podfile | 43 ++ lib/core/config/countries.dart | 790 +++++++++++++------------ macos/Flutter/Flutter-Debug.xcconfig | 1 + macos/Flutter/Flutter-Release.xcconfig | 1 + macos/Podfile | 42 ++ pubspec.lock | 16 +- 8 files changed, 500 insertions(+), 395 deletions(-) create mode 100644 ios/Podfile create mode 100644 macos/Podfile diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile new file mode 100644 index 0000000..620e46e --- /dev/null +++ b/ios/Podfile @@ -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 diff --git a/lib/core/config/countries.dart b/lib/core/config/countries.dart index 860f069..1c65b81 100644 --- a/lib/core/config/countries.dart +++ b/lib/core/config/countries.dart @@ -4,11 +4,21 @@ 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 phoneGroupSizes; // Размеры групп цифр, например [3, 3, 2, 2] + final List 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, }); } @@ -56,7 +66,13 @@ List _buildCountries() { final en = item['name'] as String; final ru = ruByCode[code] ?? en; - return CountryName(code: code, en: en, ru: ru); + 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(); + final phoneGroupSeparators = (item['phoneGroupSeparators'] as List).cast(); + + 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)); @@ -64,393 +80,393 @@ List _buildCountries() { } const String _countriesEnJson = '''[ -{"id":4,"alpha2":"af","alpha3":"afg","name":"Afghanistan"}, -{"id":8,"alpha2":"al","alpha3":"alb","name":"Albania"}, -{"id":12,"alpha2":"dz","alpha3":"dza","name":"Algeria"}, -{"id":20,"alpha2":"ad","alpha3":"and","name":"Andorra"}, -{"id":24,"alpha2":"ao","alpha3":"ago","name":"Angola"}, -{"id":28,"alpha2":"ag","alpha3":"atg","name":"Antigua and Barbuda"}, -{"id":32,"alpha2":"ar","alpha3":"arg","name":"Argentina"}, -{"id":51,"alpha2":"am","alpha3":"arm","name":"Armenia"}, -{"id":36,"alpha2":"au","alpha3":"aus","name":"Australia"}, -{"id":40,"alpha2":"at","alpha3":"aut","name":"Austria"}, -{"id":31,"alpha2":"az","alpha3":"aze","name":"Azerbaijan"}, -{"id":44,"alpha2":"bs","alpha3":"bhs","name":"Bahamas"}, -{"id":48,"alpha2":"bh","alpha3":"bhr","name":"Bahrain"}, -{"id":50,"alpha2":"bd","alpha3":"bgd","name":"Bangladesh"}, -{"id":52,"alpha2":"bb","alpha3":"brb","name":"Barbados"}, -{"id":112,"alpha2":"by","alpha3":"blr","name":"Belarus"}, -{"id":56,"alpha2":"be","alpha3":"bel","name":"Belgium"}, -{"id":84,"alpha2":"bz","alpha3":"blz","name":"Belize"}, -{"id":204,"alpha2":"bj","alpha3":"ben","name":"Benin"}, -{"id":64,"alpha2":"bt","alpha3":"btn","name":"Bhutan"}, -{"id":68,"alpha2":"bo","alpha3":"bol","name":"Bolivia, Plurinational State of"}, -{"id":70,"alpha2":"ba","alpha3":"bih","name":"Bosnia and Herzegovina"}, -{"id":72,"alpha2":"bw","alpha3":"bwa","name":"Botswana"}, -{"id":76,"alpha2":"br","alpha3":"bra","name":"Brazil"}, -{"id":96,"alpha2":"bn","alpha3":"brn","name":"Brunei Darussalam"}, -{"id":100,"alpha2":"bg","alpha3":"bgr","name":"Bulgaria"}, -{"id":854,"alpha2":"bf","alpha3":"bfa","name":"Burkina Faso"}, -{"id":108,"alpha2":"bi","alpha3":"bdi","name":"Burundi"}, -{"id":132,"alpha2":"cv","alpha3":"cpv","name":"Cabo Verde"}, -{"id":116,"alpha2":"kh","alpha3":"khm","name":"Cambodia"}, -{"id":120,"alpha2":"cm","alpha3":"cmr","name":"Cameroon"}, -{"id":124,"alpha2":"ca","alpha3":"can","name":"Canada"}, -{"id":140,"alpha2":"cf","alpha3":"caf","name":"Central African Republic"}, -{"id":148,"alpha2":"td","alpha3":"tcd","name":"Chad"}, -{"id":152,"alpha2":"cl","alpha3":"chl","name":"Chile"}, -{"id":156,"alpha2":"cn","alpha3":"chn","name":"China"}, -{"id":170,"alpha2":"co","alpha3":"col","name":"Colombia"}, -{"id":174,"alpha2":"km","alpha3":"com","name":"Comoros"}, -{"id":178,"alpha2":"cg","alpha3":"cog","name":"Congo"}, -{"id":180,"alpha2":"cd","alpha3":"cod","name":"Congo, Democratic Republic of the"}, -{"id":188,"alpha2":"cr","alpha3":"cri","name":"Costa Rica"}, -{"id":384,"alpha2":"ci","alpha3":"civ","name":"Côte d'Ivoire"}, -{"id":191,"alpha2":"hr","alpha3":"hrv","name":"Croatia"}, -{"id":192,"alpha2":"cu","alpha3":"cub","name":"Cuba"}, -{"id":196,"alpha2":"cy","alpha3":"cyp","name":"Cyprus"}, -{"id":203,"alpha2":"cz","alpha3":"cze","name":"Czechia"}, -{"id":208,"alpha2":"dk","alpha3":"dnk","name":"Denmark"}, -{"id":262,"alpha2":"dj","alpha3":"dji","name":"Djibouti"}, -{"id":212,"alpha2":"dm","alpha3":"dma","name":"Dominica"}, -{"id":214,"alpha2":"do","alpha3":"dom","name":"Dominican Republic"}, -{"id":218,"alpha2":"ec","alpha3":"ecu","name":"Ecuador"}, -{"id":818,"alpha2":"eg","alpha3":"egy","name":"Egypt"}, -{"id":222,"alpha2":"sv","alpha3":"slv","name":"El Salvador"}, -{"id":226,"alpha2":"gq","alpha3":"gnq","name":"Equatorial Guinea"}, -{"id":232,"alpha2":"er","alpha3":"eri","name":"Eritrea"}, -{"id":233,"alpha2":"ee","alpha3":"est","name":"Estonia"}, -{"id":748,"alpha2":"sz","alpha3":"swz","name":"Eswatini"}, -{"id":231,"alpha2":"et","alpha3":"eth","name":"Ethiopia"}, -{"id":242,"alpha2":"fj","alpha3":"fji","name":"Fiji"}, -{"id":246,"alpha2":"fi","alpha3":"fin","name":"Finland"}, -{"id":250,"alpha2":"fr","alpha3":"fra","name":"France"}, -{"id":266,"alpha2":"ga","alpha3":"gab","name":"Gabon"}, -{"id":270,"alpha2":"gm","alpha3":"gmb","name":"Gambia"}, -{"id":268,"alpha2":"ge","alpha3":"geo","name":"Georgia"}, -{"id":276,"alpha2":"de","alpha3":"deu","name":"Germany"}, -{"id":288,"alpha2":"gh","alpha3":"gha","name":"Ghana"}, -{"id":300,"alpha2":"gr","alpha3":"grc","name":"Greece"}, -{"id":308,"alpha2":"gd","alpha3":"grd","name":"Grenada"}, -{"id":320,"alpha2":"gt","alpha3":"gtm","name":"Guatemala"}, -{"id":324,"alpha2":"gn","alpha3":"gin","name":"Guinea"}, -{"id":624,"alpha2":"gw","alpha3":"gnb","name":"Guinea-Bissau"}, -{"id":328,"alpha2":"gy","alpha3":"guy","name":"Guyana"}, -{"id":332,"alpha2":"ht","alpha3":"hti","name":"Haiti"}, -{"id":340,"alpha2":"hn","alpha3":"hnd","name":"Honduras"}, -{"id":348,"alpha2":"hu","alpha3":"hun","name":"Hungary"}, -{"id":352,"alpha2":"is","alpha3":"isl","name":"Iceland"}, -{"id":356,"alpha2":"in","alpha3":"ind","name":"India"}, -{"id":360,"alpha2":"id","alpha3":"idn","name":"Indonesia"}, -{"id":364,"alpha2":"ir","alpha3":"irn","name":"Iran, Islamic Republic of"}, -{"id":368,"alpha2":"iq","alpha3":"irq","name":"Iraq"}, -{"id":372,"alpha2":"ie","alpha3":"irl","name":"Ireland"}, -{"id":376,"alpha2":"il","alpha3":"isr","name":"Israel"}, -{"id":380,"alpha2":"it","alpha3":"ita","name":"Italy"}, -{"id":388,"alpha2":"jm","alpha3":"jam","name":"Jamaica"}, -{"id":392,"alpha2":"jp","alpha3":"jpn","name":"Japan"}, -{"id":400,"alpha2":"jo","alpha3":"jor","name":"Jordan"}, -{"id":398,"alpha2":"kz","alpha3":"kaz","name":"Kazakhstan"}, -{"id":404,"alpha2":"ke","alpha3":"ken","name":"Kenya"}, -{"id":296,"alpha2":"ki","alpha3":"kir","name":"Kiribati"}, -{"id":408,"alpha2":"kp","alpha3":"prk","name":"Korea, Democratic People's Republic of"}, -{"id":410,"alpha2":"kr","alpha3":"kor","name":"Korea, Republic of"}, -{"id":414,"alpha2":"kw","alpha3":"kwt","name":"Kuwait"}, -{"id":417,"alpha2":"kg","alpha3":"kgz","name":"Kyrgyzstan"}, -{"id":418,"alpha2":"la","alpha3":"lao","name":"Lao People's Democratic Republic"}, -{"id":428,"alpha2":"lv","alpha3":"lva","name":"Latvia"}, -{"id":422,"alpha2":"lb","alpha3":"lbn","name":"Lebanon"}, -{"id":426,"alpha2":"ls","alpha3":"lso","name":"Lesotho"}, -{"id":430,"alpha2":"lr","alpha3":"lbr","name":"Liberia"}, -{"id":434,"alpha2":"ly","alpha3":"lby","name":"Libya"}, -{"id":438,"alpha2":"li","alpha3":"lie","name":"Liechtenstein"}, -{"id":440,"alpha2":"lt","alpha3":"ltu","name":"Lithuania"}, -{"id":442,"alpha2":"lu","alpha3":"lux","name":"Luxembourg"}, -{"id":450,"alpha2":"mg","alpha3":"mdg","name":"Madagascar"}, -{"id":454,"alpha2":"mw","alpha3":"mwi","name":"Malawi"}, -{"id":458,"alpha2":"my","alpha3":"mys","name":"Malaysia"}, -{"id":462,"alpha2":"mv","alpha3":"mdv","name":"Maldives"}, -{"id":466,"alpha2":"ml","alpha3":"mli","name":"Mali"}, -{"id":470,"alpha2":"mt","alpha3":"mlt","name":"Malta"}, -{"id":584,"alpha2":"mh","alpha3":"mhl","name":"Marshall Islands"}, -{"id":478,"alpha2":"mr","alpha3":"mrt","name":"Mauritania"}, -{"id":480,"alpha2":"mu","alpha3":"mus","name":"Mauritius"}, -{"id":484,"alpha2":"mx","alpha3":"mex","name":"Mexico"}, -{"id":583,"alpha2":"fm","alpha3":"fsm","name":"Micronesia, Federated States of"}, -{"id":498,"alpha2":"md","alpha3":"mda","name":"Moldova, Republic of"}, -{"id":492,"alpha2":"mc","alpha3":"mco","name":"Monaco"}, -{"id":496,"alpha2":"mn","alpha3":"mng","name":"Mongolia"}, -{"id":499,"alpha2":"me","alpha3":"mne","name":"Montenegro"}, -{"id":504,"alpha2":"ma","alpha3":"mar","name":"Morocco"}, -{"id":508,"alpha2":"mz","alpha3":"moz","name":"Mozambique"}, -{"id":104,"alpha2":"mm","alpha3":"mmr","name":"Myanmar"}, -{"id":516,"alpha2":"na","alpha3":"nam","name":"Namibia"}, -{"id":520,"alpha2":"nr","alpha3":"nru","name":"Nauru"}, -{"id":524,"alpha2":"np","alpha3":"npl","name":"Nepal"}, -{"id":528,"alpha2":"nl","alpha3":"nld","name":"Netherlands"}, -{"id":554,"alpha2":"nz","alpha3":"nzl","name":"New Zealand"}, -{"id":558,"alpha2":"ni","alpha3":"nic","name":"Nicaragua"}, -{"id":562,"alpha2":"ne","alpha3":"ner","name":"Niger"}, -{"id":566,"alpha2":"ng","alpha3":"nga","name":"Nigeria"}, -{"id":807,"alpha2":"mk","alpha3":"mkd","name":"North Macedonia"}, -{"id":578,"alpha2":"no","alpha3":"nor","name":"Norway"}, -{"id":512,"alpha2":"om","alpha3":"omn","name":"Oman"}, -{"id":586,"alpha2":"pk","alpha3":"pak","name":"Pakistan"}, -{"id":585,"alpha2":"pw","alpha3":"plw","name":"Palau"}, -{"id":591,"alpha2":"pa","alpha3":"pan","name":"Panama"}, -{"id":598,"alpha2":"pg","alpha3":"png","name":"Papua New Guinea"}, -{"id":600,"alpha2":"py","alpha3":"pry","name":"Paraguay"}, -{"id":604,"alpha2":"pe","alpha3":"per","name":"Peru"}, -{"id":608,"alpha2":"ph","alpha3":"phl","name":"Philippines"}, -{"id":616,"alpha2":"pl","alpha3":"pol","name":"Poland"}, -{"id":620,"alpha2":"pt","alpha3":"prt","name":"Portugal"}, -{"id":634,"alpha2":"qa","alpha3":"qat","name":"Qatar"}, -{"id":642,"alpha2":"ro","alpha3":"rou","name":"Romania"}, -{"id":643,"alpha2":"ru","alpha3":"rus","name":"Russian Federation"}, -{"id":646,"alpha2":"rw","alpha3":"rwa","name":"Rwanda"}, -{"id":659,"alpha2":"kn","alpha3":"kna","name":"Saint Kitts and Nevis"}, -{"id":662,"alpha2":"lc","alpha3":"lca","name":"Saint Lucia"}, -{"id":670,"alpha2":"vc","alpha3":"vct","name":"Saint Vincent and the Grenadines"}, -{"id":882,"alpha2":"ws","alpha3":"wsm","name":"Samoa"}, -{"id":674,"alpha2":"sm","alpha3":"smr","name":"San Marino"}, -{"id":678,"alpha2":"st","alpha3":"stp","name":"Sao Tome and Principe"}, -{"id":682,"alpha2":"sa","alpha3":"sau","name":"Saudi Arabia"}, -{"id":686,"alpha2":"sn","alpha3":"sen","name":"Senegal"}, -{"id":688,"alpha2":"rs","alpha3":"srb","name":"Serbia"}, -{"id":690,"alpha2":"sc","alpha3":"syc","name":"Seychelles"}, -{"id":694,"alpha2":"sl","alpha3":"sle","name":"Sierra Leone"}, -{"id":702,"alpha2":"sg","alpha3":"sgp","name":"Singapore"}, -{"id":703,"alpha2":"sk","alpha3":"svk","name":"Slovakia"}, -{"id":705,"alpha2":"si","alpha3":"svn","name":"Slovenia"}, -{"id":90,"alpha2":"sb","alpha3":"slb","name":"Solomon Islands"}, -{"id":706,"alpha2":"so","alpha3":"som","name":"Somalia"}, -{"id":710,"alpha2":"za","alpha3":"zaf","name":"South Africa"}, -{"id":728,"alpha2":"ss","alpha3":"ssd","name":"South Sudan"}, -{"id":724,"alpha2":"es","alpha3":"esp","name":"Spain"}, -{"id":144,"alpha2":"lk","alpha3":"lka","name":"Sri Lanka"}, -{"id":729,"alpha2":"sd","alpha3":"sdn","name":"Sudan"}, -{"id":740,"alpha2":"sr","alpha3":"sur","name":"Suriname"}, -{"id":752,"alpha2":"se","alpha3":"swe","name":"Sweden"}, -{"id":756,"alpha2":"ch","alpha3":"che","name":"Switzerland"}, -{"id":760,"alpha2":"sy","alpha3":"syr","name":"Syrian Arab Republic"}, -{"id":762,"alpha2":"tj","alpha3":"tjk","name":"Tajikistan"}, -{"id":834,"alpha2":"tz","alpha3":"tza","name":"Tanzania, United Republic of"}, -{"id":764,"alpha2":"th","alpha3":"tha","name":"Thailand"}, -{"id":626,"alpha2":"tl","alpha3":"tls","name":"Timor-Leste"}, -{"id":768,"alpha2":"tg","alpha3":"tgo","name":"Togo"}, -{"id":776,"alpha2":"to","alpha3":"ton","name":"Tonga"}, -{"id":780,"alpha2":"tt","alpha3":"tto","name":"Trinidad and Tobago"}, -{"id":788,"alpha2":"tn","alpha3":"tun","name":"Tunisia"}, -{"id":792,"alpha2":"tr","alpha3":"tur","name":"Türkiye"}, -{"id":795,"alpha2":"tm","alpha3":"tkm","name":"Turkmenistan"}, -{"id":798,"alpha2":"tv","alpha3":"tuv","name":"Tuvalu"}, -{"id":800,"alpha2":"ug","alpha3":"uga","name":"Uganda"}, -{"id":804,"alpha2":"ua","alpha3":"ukr","name":"Ukraine"}, -{"id":784,"alpha2":"ae","alpha3":"are","name":"United Arab Emirates"}, -{"id":826,"alpha2":"gb","alpha3":"gbr","name":"United Kingdom of Great Britain and Northern Ireland"}, -{"id":840,"alpha2":"us","alpha3":"usa","name":"United States of America"}, -{"id":858,"alpha2":"uy","alpha3":"ury","name":"Uruguay"}, -{"id":860,"alpha2":"uz","alpha3":"uzb","name":"Uzbekistan"}, -{"id":548,"alpha2":"vu","alpha3":"vut","name":"Vanuatu"}, -{"id":862,"alpha2":"ve","alpha3":"ven","name":"Venezuela, Bolivarian Republic of"}, -{"id":704,"alpha2":"vn","alpha3":"vnm","name":"Viet Nam"}, -{"id":887,"alpha2":"ye","alpha3":"yem","name":"Yemen"}, -{"id":894,"alpha2":"zm","alpha3":"zmb","name":"Zambia"}, -{"id":716,"alpha2":"zw","alpha3":"zwe","name":"Zimbabwe"} +{"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":"Австралия"}, -{"id":40,"alpha2":"at","alpha3":"aut","name":"Австрия"}, -{"id":31,"alpha2":"az","alpha3":"aze","name":"Азербайджан"}, -{"id":8,"alpha2":"al","alpha3":"alb","name":"Албания"}, -{"id":12,"alpha2":"dz","alpha3":"dza","name":"Алжир"}, -{"id":24,"alpha2":"ao","alpha3":"ago","name":"Ангола"}, -{"id":20,"alpha2":"ad","alpha3":"and","name":"Андорра"}, -{"id":28,"alpha2":"ag","alpha3":"atg","name":"Антигуа и Барбуда"}, -{"id":32,"alpha2":"ar","alpha3":"arg","name":"Аргентина"}, -{"id":51,"alpha2":"am","alpha3":"arm","name":"Армения"}, -{"id":4,"alpha2":"af","alpha3":"afg","name":"Афганистан"}, -{"id":44,"alpha2":"bs","alpha3":"bhs","name":"Багамские Острова"}, -{"id":50,"alpha2":"bd","alpha3":"bgd","name":"Бангладеш"}, -{"id":52,"alpha2":"bb","alpha3":"brb","name":"Барбадос"}, -{"id":48,"alpha2":"bh","alpha3":"bhr","name":"Бахрейн"}, -{"id":84,"alpha2":"bz","alpha3":"blz","name":"Белиз"}, -{"id":112,"alpha2":"by","alpha3":"blr","name":"Беларусь"}, -{"id":56,"alpha2":"be","alpha3":"bel","name":"Бельгия"}, -{"id":204,"alpha2":"bj","alpha3":"ben","name":"Бенин"}, -{"id":100,"alpha2":"bg","alpha3":"bgr","name":"Болгария"}, -{"id":68,"alpha2":"bo","alpha3":"bol","name":"Боливия"}, -{"id":70,"alpha2":"ba","alpha3":"bih","name":"Босния и Герцеговина"}, -{"id":72,"alpha2":"bw","alpha3":"bwa","name":"Ботсвана"}, -{"id":76,"alpha2":"br","alpha3":"bra","name":"Бразилия"}, -{"id":96,"alpha2":"bn","alpha3":"brn","name":"Бруней"}, -{"id":854,"alpha2":"bf","alpha3":"bfa","name":"Буркина-Фасо"}, -{"id":108,"alpha2":"bi","alpha3":"bdi","name":"Бурунди"}, -{"id":64,"alpha2":"bt","alpha3":"btn","name":"Бутан"}, -{"id":548,"alpha2":"vu","alpha3":"vut","name":"Вануату"}, -{"id":826,"alpha2":"gb","alpha3":"gbr","name":"Великобритания"}, -{"id":348,"alpha2":"hu","alpha3":"hun","name":"Венгрия"}, -{"id":862,"alpha2":"ve","alpha3":"ven","name":"Венесуэла"}, -{"id":626,"alpha2":"tl","alpha3":"tls","name":"Восточный Тимор"}, -{"id":704,"alpha2":"vn","alpha3":"vnm","name":"Вьетнам"}, -{"id":266,"alpha2":"ga","alpha3":"gab","name":"Габон"}, -{"id":332,"alpha2":"ht","alpha3":"hti","name":"Гаити"}, -{"id":328,"alpha2":"gy","alpha3":"guy","name":"Гайана"}, -{"id":270,"alpha2":"gm","alpha3":"gmb","name":"Гамбия"}, -{"id":288,"alpha2":"gh","alpha3":"gha","name":"Гана"}, -{"id":320,"alpha2":"gt","alpha3":"gtm","name":"Гватемала"}, -{"id":324,"alpha2":"gn","alpha3":"gin","name":"Гвинея"}, -{"id":624,"alpha2":"gw","alpha3":"gnb","name":"Гвинея-Бисау"}, -{"id":276,"alpha2":"de","alpha3":"deu","name":"Германия"}, -{"id":340,"alpha2":"hn","alpha3":"hnd","name":"Гондурас"}, -{"id":308,"alpha2":"gd","alpha3":"grd","name":"Гренада"}, -{"id":300,"alpha2":"gr","alpha3":"grc","name":"Греция"}, -{"id":268,"alpha2":"ge","alpha3":"geo","name":"Грузия"}, -{"id":208,"alpha2":"dk","alpha3":"dnk","name":"Дания"}, -{"id":262,"alpha2":"dj","alpha3":"dji","name":"Джибути"}, -{"id":212,"alpha2":"dm","alpha3":"dma","name":"Доминика"}, -{"id":214,"alpha2":"do","alpha3":"dom","name":"Доминиканская Республика"}, -{"id":180,"alpha2":"cd","alpha3":"cod","name":"ДР Конго"}, -{"id":818,"alpha2":"eg","alpha3":"egy","name":"Египет"}, -{"id":894,"alpha2":"zm","alpha3":"zmb","name":"Замбия"}, -{"id":716,"alpha2":"zw","alpha3":"zwe","name":"Зимбабве"}, -{"id":376,"alpha2":"il","alpha3":"isr","name":"Израиль"}, -{"id":356,"alpha2":"in","alpha3":"ind","name":"Индия"}, -{"id":360,"alpha2":"id","alpha3":"idn","name":"Индонезия"}, -{"id":400,"alpha2":"jo","alpha3":"jor","name":"Иордания"}, -{"id":368,"alpha2":"iq","alpha3":"irq","name":"Ирак"}, -{"id":364,"alpha2":"ir","alpha3":"irn","name":"Иран"}, -{"id":372,"alpha2":"ie","alpha3":"irl","name":"Ирландия"}, -{"id":352,"alpha2":"is","alpha3":"isl","name":"Исландия"}, -{"id":724,"alpha2":"es","alpha3":"esp","name":"Испания"}, -{"id":380,"alpha2":"it","alpha3":"ita","name":"Италия"}, -{"id":887,"alpha2":"ye","alpha3":"yem","name":"Йемен"}, -{"id":132,"alpha2":"cv","alpha3":"cpv","name":"Кабо-Верде"}, -{"id":398,"alpha2":"kz","alpha3":"kaz","name":"Казахстан"}, -{"id":116,"alpha2":"kh","alpha3":"khm","name":"Камбоджа"}, -{"id":120,"alpha2":"cm","alpha3":"cmr","name":"Камерун"}, -{"id":124,"alpha2":"ca","alpha3":"can","name":"Канада"}, -{"id":634,"alpha2":"qa","alpha3":"qat","name":"Катар"}, -{"id":404,"alpha2":"ke","alpha3":"ken","name":"Кения"}, -{"id":196,"alpha2":"cy","alpha3":"cyp","name":"Кипр"}, -{"id":417,"alpha2":"kg","alpha3":"kgz","name":"Кыргызстан"}, -{"id":296,"alpha2":"ki","alpha3":"kir","name":"Кирибати"}, -{"id":408,"alpha2":"kp","alpha3":"prk","name":"КНДР (Корейская Народно-Демократическая Республика)"}, -{"id":156,"alpha2":"cn","alpha3":"chn","name":"Китай (Китайская Народная Республика)"}, -{"id":170,"alpha2":"co","alpha3":"col","name":"Колумбия"}, -{"id":174,"alpha2":"km","alpha3":"com","name":"Коморы"}, -{"id":188,"alpha2":"cr","alpha3":"cri","name":"Коста-Рика"}, -{"id":384,"alpha2":"ci","alpha3":"civ","name":"Кот-д’Ивуар"}, -{"id":192,"alpha2":"cu","alpha3":"cub","name":"Куба"}, -{"id":414,"alpha2":"kw","alpha3":"kwt","name":"Кувейт"}, -{"id":418,"alpha2":"la","alpha3":"lao","name":"Лаос"}, -{"id":428,"alpha2":"lv","alpha3":"lva","name":"Латвия"}, -{"id":426,"alpha2":"ls","alpha3":"lso","name":"Лесото"}, -{"id":430,"alpha2":"lr","alpha3":"lbr","name":"Либерия"}, -{"id":422,"alpha2":"lb","alpha3":"lbn","name":"Ливан"}, -{"id":434,"alpha2":"ly","alpha3":"lby","name":"Ливия"}, -{"id":440,"alpha2":"lt","alpha3":"ltu","name":"Литва"}, -{"id":438,"alpha2":"li","alpha3":"lie","name":"Лихтенштейн"}, -{"id":442,"alpha2":"lu","alpha3":"lux","name":"Люксембург"}, -{"id":480,"alpha2":"mu","alpha3":"mus","name":"Маврикий"}, -{"id":478,"alpha2":"mr","alpha3":"mrt","name":"Мавритания"}, -{"id":450,"alpha2":"mg","alpha3":"mdg","name":"Мадагаскар"}, -{"id":807,"alpha2":"mk","alpha3":"mkd","name":"Северная Македония"}, -{"id":454,"alpha2":"mw","alpha3":"mwi","name":"Малави"}, -{"id":458,"alpha2":"my","alpha3":"mys","name":"Малайзия"}, -{"id":466,"alpha2":"ml","alpha3":"mli","name":"Мали"}, -{"id":462,"alpha2":"mv","alpha3":"mdv","name":"Мальдивы"}, -{"id":470,"alpha2":"mt","alpha3":"mlt","name":"Мальта"}, -{"id":504,"alpha2":"ma","alpha3":"mar","name":"Марокко"}, -{"id":584,"alpha2":"mh","alpha3":"mhl","name":"Маршалловы Острова"}, -{"id":484,"alpha2":"mx","alpha3":"mex","name":"Мексика"}, -{"id":583,"alpha2":"fm","alpha3":"fsm","name":"Микронезия"}, -{"id":508,"alpha2":"mz","alpha3":"moz","name":"Мозамбик"}, -{"id":498,"alpha2":"md","alpha3":"mda","name":"Молдова"}, -{"id":492,"alpha2":"mc","alpha3":"mco","name":"Монако"}, -{"id":496,"alpha2":"mn","alpha3":"mng","name":"Монголия"}, -{"id":104,"alpha2":"mm","alpha3":"mmr","name":"Мьянма"}, -{"id":516,"alpha2":"na","alpha3":"nam","name":"Намибия"}, -{"id":520,"alpha2":"nr","alpha3":"nru","name":"Науру"}, -{"id":524,"alpha2":"np","alpha3":"npl","name":"Непал"}, -{"id":562,"alpha2":"ne","alpha3":"ner","name":"Нигер"}, -{"id":566,"alpha2":"ng","alpha3":"nga","name":"Нигерия"}, -{"id":528,"alpha2":"nl","alpha3":"nld","name":"Нидерланды"}, -{"id":558,"alpha2":"ni","alpha3":"nic","name":"Никарагуа"}, -{"id":554,"alpha2":"nz","alpha3":"nzl","name":"Новая Зеландия"}, -{"id":578,"alpha2":"no","alpha3":"nor","name":"Норвегия"}, -{"id":784,"alpha2":"ae","alpha3":"are","name":"ОАЭ"}, -{"id":512,"alpha2":"om","alpha3":"omn","name":"Оман"}, -{"id":586,"alpha2":"pk","alpha3":"pak","name":"Пакистан"}, -{"id":585,"alpha2":"pw","alpha3":"plw","name":"Палау"}, -{"id":591,"alpha2":"pa","alpha3":"pan","name":"Панама"}, -{"id":598,"alpha2":"pg","alpha3":"png","name":"Папуа — Новая Гвинея"}, -{"id":600,"alpha2":"py","alpha3":"pry","name":"Парагвай"}, -{"id":604,"alpha2":"pe","alpha3":"per","name":"Перу"}, -{"id":616,"alpha2":"pl","alpha3":"pol","name":"Польша"}, -{"id":620,"alpha2":"pt","alpha3":"prt","name":"Португалия"}, -{"id":178,"alpha2":"cg","alpha3":"cog","name":"Республика Конго"}, -{"id":410,"alpha2":"kr","alpha3":"kor","name":"Республика Корея"}, -{"id":643,"alpha2":"ru","alpha3":"rus","name":"Россия"}, -{"id":646,"alpha2":"rw","alpha3":"rwa","name":"Руанда"}, -{"id":642,"alpha2":"ro","alpha3":"rou","name":"Румыния"}, -{"id":222,"alpha2":"sv","alpha3":"slv","name":"Сальвадор"}, -{"id":882,"alpha2":"ws","alpha3":"wsm","name":"Самоа"}, -{"id":674,"alpha2":"sm","alpha3":"smr","name":"Сан-Марино"}, -{"id":678,"alpha2":"st","alpha3":"stp","name":"Сан-Томе и Принсипи"}, -{"id":682,"alpha2":"sa","alpha3":"sau","name":"Саудовская Аравия"}, -{"id":690,"alpha2":"sc","alpha3":"syc","name":"Сейшельские Острова"}, -{"id":686,"alpha2":"sn","alpha3":"sen","name":"Сенегал"}, -{"id":670,"alpha2":"vc","alpha3":"vct","name":"Сент-Винсент и Гренадины"}, -{"id":659,"alpha2":"kn","alpha3":"kna","name":"Сент-Китс и Невис"}, -{"id":662,"alpha2":"lc","alpha3":"lca","name":"Сент-Люсия"}, -{"id":688,"alpha2":"rs","alpha3":"srb","name":"Сербия"}, -{"id":702,"alpha2":"sg","alpha3":"sgp","name":"Сингапур"}, -{"id":760,"alpha2":"sy","alpha3":"syr","name":"Сирия"}, -{"id":703,"alpha2":"sk","alpha3":"svk","name":"Словакия"}, -{"id":705,"alpha2":"si","alpha3":"svn","name":"Словения"}, -{"id":90,"alpha2":"sb","alpha3":"slb","name":"Соломоновы Острова"}, -{"id":706,"alpha2":"so","alpha3":"som","name":"Сомали"}, -{"id":729,"alpha2":"sd","alpha3":"sdn","name":"Судан"}, -{"id":740,"alpha2":"sr","alpha3":"sur","name":"Суринам"}, -{"id":840,"alpha2":"us","alpha3":"usa","name":"США"}, -{"id":694,"alpha2":"sl","alpha3":"sle","name":"Сьерра-Леоне"}, -{"id":762,"alpha2":"tj","alpha3":"tjk","name":"Таджикистан"}, -{"id":764,"alpha2":"th","alpha3":"tha","name":"Таиланд"}, -{"id":834,"alpha2":"tz","alpha3":"tza","name":"Танзания"}, -{"id":768,"alpha2":"tg","alpha3":"tgo","name":"Того"}, -{"id":776,"alpha2":"to","alpha3":"ton","name":"Тонга"}, -{"id":780,"alpha2":"tt","alpha3":"tto","name":"Тринидад и Тобаго"}, -{"id":798,"alpha2":"tv","alpha3":"tuv","name":"Тувалу"}, -{"id":788,"alpha2":"tn","alpha3":"tun","name":"Тунис"}, -{"id":795,"alpha2":"tm","alpha3":"tkm","name":"Туркменистан"}, -{"id":792,"alpha2":"tr","alpha3":"tur","name":"Турция"}, -{"id":800,"alpha2":"ug","alpha3":"uga","name":"Уганда"}, -{"id":860,"alpha2":"uz","alpha3":"uzb","name":"Узбекистан"}, -{"id":804,"alpha2":"ua","alpha3":"ukr","name":"Украина"}, -{"id":858,"alpha2":"uy","alpha3":"ury","name":"Уругвай"}, -{"id":242,"alpha2":"fj","alpha3":"fji","name":"Фиджи"}, -{"id":608,"alpha2":"ph","alpha3":"phl","name":"Филиппины"}, -{"id":246,"alpha2":"fi","alpha3":"fin","name":"Финляндия"}, -{"id":250,"alpha2":"fr","alpha3":"fra","name":"Франция"}, -{"id":191,"alpha2":"hr","alpha3":"hrv","name":"Хорватия"}, -{"id":140,"alpha2":"cf","alpha3":"caf","name":"ЦАР"}, -{"id":148,"alpha2":"td","alpha3":"tcd","name":"Чад"}, -{"id":499,"alpha2":"me","alpha3":"mne","name":"Черногория"}, -{"id":203,"alpha2":"cz","alpha3":"cze","name":"Чехия"}, -{"id":152,"alpha2":"cl","alpha3":"chl","name":"Чили"}, -{"id":756,"alpha2":"ch","alpha3":"che","name":"Швейцария"}, -{"id":752,"alpha2":"se","alpha3":"swe","name":"Швеция"}, -{"id":144,"alpha2":"lk","alpha3":"lka","name":"Шри-Ланка"}, -{"id":218,"alpha2":"ec","alpha3":"ecu","name":"Эквадор"}, -{"id":226,"alpha2":"gq","alpha3":"gnq","name":"Экваториальная Гвинея"}, -{"id":232,"alpha2":"er","alpha3":"eri","name":"Эритрея"}, -{"id":748,"alpha2":"sz","alpha3":"swz","name":"Эсватини"}, -{"id":233,"alpha2":"ee","alpha3":"est","name":"Эстония"}, -{"id":231,"alpha2":"et","alpha3":"eth","name":"Эфиопия"}, -{"id":710,"alpha2":"za","alpha3":"zaf","name":"ЮАР"}, -{"id":728,"alpha2":"ss","alpha3":"ssd","name":"Южный Судан"}, -{"id":388,"alpha2":"jm","alpha3":"jam","name":"Ямайка"}, -{"id":392,"alpha2":"jp","alpha3":"jpn","name":"Япония"} +{"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": ["", "-", "-", ""]} ]'''; diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig index c2efd0b..4b81f9b 100644 --- a/macos/Flutter/Flutter-Debug.xcconfig +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig index c2efd0b..5caa9d1 100644 --- a/macos/Flutter/Flutter-Release.xcconfig +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..ff5ddb3 --- /dev/null +++ b/macos/Podfile @@ -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 diff --git a/pubspec.lock b/pubspec.lock index bc155f7..6bb2187 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: characters - sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.0" clock: dependency: transitive description: @@ -196,18 +196,18 @@ packages: dependency: transitive description: name: matcher - sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861 + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.19" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.13.0" + version: "0.11.1" meta: dependency: transitive description: @@ -289,10 +289,10 @@ packages: dependency: transitive description: name: test_api - sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.10" + version: "0.7.7" timezone: dependency: "direct main" description: