import 'dart:convert'; import 'dart:io'; import 'package:http/http.dart' as http; class GeolocationData { final String ip; final String network; final String version; final String city; final String region; final String regionCode; final String country; final String countryName; final String countryCode; final String countryCodeIso3; final String countryCapital; final String countryTld; final String continentCode; final bool inEu; final String postal; final double latitude; final double longitude; final String timezone; final String utcOffset; final String countryCallingCode; final String currency; final String currencyName; final String languages; final double countryArea; final int countryPopulation; final String asn; final String org; GeolocationData({ required this.ip, required this.network, required this.version, required this.city, required this.region, required this.regionCode, required this.country, required this.countryName, required this.countryCode, required this.countryCodeIso3, required this.countryCapital, required this.countryTld, required this.continentCode, required this.inEu, required this.postal, required this.latitude, required this.longitude, required this.timezone, required this.utcOffset, required this.countryCallingCode, required this.currency, required this.currencyName, required this.languages, required this.countryArea, required this.countryPopulation, required this.asn, required this.org, }); factory GeolocationData.fromJson(Map json) { return GeolocationData( ip: json['ip'], network: json['network'], version: json['version'], city: json['city'], region: json['region'], regionCode: json['region_code'], country: json['country'], countryName: json['country_name'], countryCode: json['country_code'], countryCodeIso3: json['country_code_iso3'], countryCapital: json['country_capital'], countryTld: json['country_tld'], continentCode: json['continent_code'], inEu: json['in_eu'], postal: json['postal'], latitude: json['latitude'].toDouble(), longitude: json['longitude'].toDouble(), timezone: json['timezone'], utcOffset: json['utc_offset'], countryCallingCode: json['country_calling_code'], currency: json['currency'], currencyName: json['currency_name'], languages: json['languages'], countryArea: json['country_area'].toDouble(), countryPopulation: json['country_population'], asn: json['asn'], org: json['org'], ); } Map toJson() { return { 'ip': ip, 'network': network, 'version': version, 'city': city, 'region': region, 'region_code': regionCode, 'country': country, 'country_name': countryName, 'country_code': countryCode, 'country_code_iso3': countryCodeIso3, 'country_capital': countryCapital, 'country_tld': countryTld, 'continent_code': continentCode, 'in_eu': inEu, 'postal': postal, 'latitude': latitude, 'longitude': longitude, 'timezone': timezone, 'utc_offset': utcOffset, 'country_calling_code': countryCallingCode, 'currency': currency, 'currency_name': currencyName, 'languages': languages, 'country_area': countryArea, 'country_population': countryPopulation, 'asn': asn, 'org': org, }; } } Future getGeolocationData() async { // List of API configurations with their respective mappers final apiConfigs = [ _ApiConfig( url: 'https://ipapi.co/json/', mapper: _mapIpapiCoResponse, name: 'ipapi.co', ), _ApiConfig( url: 'http://ip-api.com/json/', mapper: _mapIpApiResponse, name: 'ip-api.com', ), _ApiConfig( url: 'https://freeipapi.com/api/json/', mapper: _mapFreeIpApiResponse, name: 'freeipapi.com', ), _ApiConfig( url: 'http://ipwhois.app/json/', mapper: _mapIpWhoisResponse, name: 'ipwhois.app', ), ]; Exception? lastException; for (final config in apiConfigs) { try { print('Trying ${config.name}...'); final response = await http.get( Uri.parse(config.url), headers: {'User-Agent': 'Dart/3.0'}, ).timeout(const Duration(seconds: 10)); if (response.statusCode == 200) { final data = jsonDecode(response.body); // Check if the response indicates success (for APIs that include status) if (data is Map) { final status = data['status']?.toString().toLowerCase(); if (status != null && status != 'success' && status != 'ok') { throw Exception('API returned error status: $status'); } } final geolocationData = config.mapper(data); print('Successfully got data from ${config.name}'); return geolocationData; } else { throw Exception('HTTP ${response.statusCode}: ${response.reasonPhrase}'); } } catch (e) { print('Failed to get data from ${config.name}: $e'); lastException = Exception('${config.name}: $e'); // Add delay before trying next API to avoid hitting rate limits if (config != apiConfigs.last) { await Future.delayed(const Duration(milliseconds: 500)); } } } throw lastException ?? Exception('All geolocation APIs failed'); } class _ApiConfig { final String url; final GeolocationData Function(Map) mapper; final String name; _ApiConfig({ required this.url, required this.mapper, required this.name, }); } // Mapper for ipapi.co (primary API - has most complete data) GeolocationData _mapIpapiCoResponse(Map json) { return GeolocationData( ip: json['ip'] ?? '', network: json['network'] ?? '', version: json['version'] ?? 'IPv4', city: json['city'] ?? '', region: json['region'] ?? '', regionCode: json['region_code'] ?? '', country: json['country'] ?? '', countryName: json['country_name'] ?? '', countryCode: json['country_code'] ?? '', countryCodeIso3: json['country_code_iso3'] ?? '', countryCapital: json['country_capital'] ?? '', countryTld: json['country_tld'] ?? '', continentCode: json['continent_code'] ?? '', inEu: json['in_eu'] ?? false, postal: json['postal'] ?? '', latitude: (json['latitude'] ?? 0.0).toDouble(), longitude: (json['longitude'] ?? 0.0).toDouble(), timezone: json['timezone'] ?? '', utcOffset: json['utc_offset'] ?? '', countryCallingCode: json['country_calling_code'] ?? '', currency: json['currency'] ?? '', currencyName: json['currency_name'] ?? '', languages: json['languages'] ?? '', countryArea: (json['country_area'] ?? 0.0).toDouble(), countryPopulation: json['country_population'] ?? 0, asn: json['asn'] ?? '', org: json['org'] ?? '', ); } // Mapper for ip-api.com GeolocationData _mapIpApiResponse(Map json) { return GeolocationData( ip: json['query'] ?? '', network: '', // Not provided by ip-api.com version: json['query']?.toString().contains(':') == true ? 'IPv6' : 'IPv4', city: json['city'] ?? '', region: json['regionName'] ?? '', regionCode: json['region'] ?? '', country: json['country'] ?? '', countryName: json['country'] ?? '', countryCode: json['countryCode'] ?? '', countryCodeIso3: '', // Not provided by ip-api.com countryCapital: '', // Not provided by ip-api.com countryTld: '', // Not provided by ip-api.com continentCode: '', // Not provided by ip-api.com inEu: false, // Not provided by ip-api.com postal: json['zip'] ?? '', latitude: (json['lat'] ?? 0.0).toDouble(), longitude: (json['lon'] ?? 0.0).toDouble(), timezone: json['timezone'] ?? '', utcOffset: '', // Not provided by ip-api.com countryCallingCode: '', // Not provided by ip-api.com currency: '', // Not provided by ip-api.com currencyName: '', // Not provided by ip-api.com languages: '', // Not provided by ip-api.com countryArea: 0.0, // Not provided by ip-api.com countryPopulation: 0, // Not provided by ip-api.com asn: json['as'] ?? '', org: json['org'] ?? json['isp'] ?? '', ); } // Mapper for freeipapi.com GeolocationData _mapFreeIpApiResponse(Map json) { return GeolocationData( ip: json['ipAddress'] ?? '', network: '', // Not provided by freeipapi.com version: json['ipVersion'] ?? 'IPv4', city: json['cityName'] ?? '', region: json['regionName'] ?? '', regionCode: '', // Not provided by freeipapi.com country: json['countryName'] ?? '', countryName: json['countryName'] ?? '', countryCode: json['countryCode'] ?? '', countryCodeIso3: '', // Not provided by freeipapi.com countryCapital: '', // Not provided by freeipapi.com countryTld: '', // Not provided by freeipapi.com continentCode: '', // Not provided by freeipapi.com inEu: false, // Not provided by freeipapi.com postal: json['zipCode'] ?? '', latitude: (json['latitude'] ?? 0.0).toDouble(), longitude: (json['longitude'] ?? 0.0).toDouble(), timezone: json['timeZone'] ?? '', utcOffset: '', // Not provided by freeipapi.com countryCallingCode: '', // Not provided by freeipapi.com currency: '', // Not provided by freeipapi.com currencyName: '', // Not provided by freeipapi.com languages: '', // Not provided by freeipapi.com countryArea: 0.0, // Not provided by freeipapi.com countryPopulation: 0, // Not provided by freeipapi.com asn: '', // Not provided by freeipapi.com org: json['isProxy'] == true ? 'Proxy Service' : '', ); } // Mapper for ipwhois.app GeolocationData _mapIpWhoisResponse(Map json) { return GeolocationData( ip: json['ip'] ?? '', network: '', // Not provided by ipwhois.app version: json['type'] ?? 'IPv4', city: json['city'] ?? '', region: json['region'] ?? '', regionCode: json['region_code'] ?? '', country: json['country'] ?? '', countryName: json['country'] ?? '', countryCode: json['country_code'] ?? '', countryCodeIso3: '', // Not provided by ipwhois.app countryCapital: json['country_capital'] ?? '', countryTld: '', // Not provided by ipwhois.app continentCode: json['continent_code'] ?? '', inEu: json['country_eu'] ?? false, postal: '', // Not provided by ipwhois.app latitude: (json['latitude'] ?? 0.0).toDouble(), longitude: (json['longitude'] ?? 0.0).toDouble(), timezone: json['timezone_name'] ?? '', utcOffset: json['timezone_gmt'] ?? '', countryCallingCode: json['country_phone'] ?? '', currency: json['currency'] ?? '', currencyName: json['currency_name'] ?? '', languages: '', // Not provided by ipwhois.app countryArea: 0.0, // Not provided by ipwhois.app countryPopulation: 0, // Not provided by ipwhois.app asn: json['asn'] ?? '', org: json['org'] ?? json['isp'] ?? '', ); } /// Check if Wireguard is installed on the system. Future isWireguardInstalled() async { try { var result = await Process.run('wg', ['--version']); return result.exitCode == 0; } catch (e) { print('Error checking WireGuard installation: $e'); return false; } } bool get kIsRunningInDocker => File('/.dockerenv').existsSync();