Group
Extension

Geo-IPinfo/lib/Geo/IPinfoLite.pm

package Geo::IPinfoLite;

use 5.006;
use strict;
use warnings;
use Cache::LRU;
use LWP::UserAgent;
use HTTP::Headers;
use JSON;
use Geo::DetailsLite;
use Net::CIDR;
use Net::CIDR::Set;

our $VERSION = '3.2.0';
use constant DEFAULT_CACHE_MAX_SIZE => 4096;
use constant DEFAULT_CACHE_TTL      => 86_400;
use constant DEFAULT_TIMEOUT        => 2;
use constant HTTP_TOO_MANY_REQUEST  => 429;

my %valid_fields = (
    ip              => 1,
    asn             => 1,
    as_name         => 1,
    as_domain       => 1,
    country_code    => 1,
    country         => 1,
    continent_code  => 1,
    continent       => 1,
);
my $base_url          = 'https://api.ipinfo.io/lite/';
my $country_flag_url  = 'https://cdn.ipinfo.io/static/images/countries-flags/';
my $cache_ttl    = 0;
my $custom_cache = 0;
my %default_countries = (
    "BD" => "Bangladesh",
    "BE" => "Belgium",
    "BF" => "Burkina Faso",
    "BG" => "Bulgaria",
    "BA" => "Bosnia and Herzegovina",
    "BB" => "Barbados",
    "WF" => "Wallis and Futuna",
    "BL" => "Saint Barthelemy",
    "BM" => "Bermuda",
    "BN" => "Brunei",
    "BO" => "Bolivia",
    "BH" => "Bahrain",
    "BI" => "Burundi",
    "BJ" => "Benin",
    "BT" => "Bhutan",
    "JM" => "Jamaica",
    "BV" => "Bouvet Island",
    "BW" => "Botswana",
    "WS" => "Samoa",
    "BQ" => "Bonaire, Saint Eustatius and Saba ",
    "BR" => "Brazil",
    "BS" => "Bahamas",
    "JE" => "Jersey",
    "BY" => "Belarus",
    "BZ" => "Belize",
    "RU" => "Russia",
    "RW" => "Rwanda",
    "RS" => "Serbia",
    "TL" => "East Timor",
    "RE" => "Reunion",
    "TM" => "Turkmenistan",
    "TJ" => "Tajikistan",
    "RO" => "Romania",
    "TK" => "Tokelau",
    "GW" => "Guinea-Bissau",
    "GU" => "Guam",
    "GT" => "Guatemala",
    "GS" => "South Georgia and the South Sandwich Islands",
    "GR" => "Greece",
    "GQ" => "Equatorial Guinea",
    "GP" => "Guadeloupe",
    "JP" => "Japan",
    "GY" => "Guyana",
    "GG" => "Guernsey",
    "GF" => "French Guiana",
    "GE" => "Georgia",
    "GD" => "Grenada",
    "GB" => "United Kingdom",
    "GA" => "Gabon",
    "SV" => "El Salvador",
    "GN" => "Guinea",
    "GM" => "Gambia",
    "GL" => "Greenland",
    "GI" => "Gibraltar",
    "GH" => "Ghana",
    "OM" => "Oman",
    "TN" => "Tunisia",
    "JO" => "Jordan",
    "HR" => "Croatia",
    "HT" => "Haiti",
    "HU" => "Hungary",
    "HK" => "Hong Kong",
    "HN" => "Honduras",
    "HM" => "Heard Island and McDonald Islands",
    "VE" => "Venezuela",
    "PR" => "Puerto Rico",
    "PS" => "Palestinian Territory",
    "PW" => "Palau",
    "PT" => "Portugal",
    "SJ" => "Svalbard and Jan Mayen",
    "PY" => "Paraguay",
    "IQ" => "Iraq",
    "PA" => "Panama",
    "PF" => "French Polynesia",
    "PG" => "Papua New Guinea",
    "PE" => "Peru",
    "PK" => "Pakistan",
    "PH" => "Philippines",
    "PN" => "Pitcairn",
    "PL" => "Poland",
    "PM" => "Saint Pierre and Miquelon",
    "ZM" => "Zambia",
    "EH" => "Western Sahara",
    "EE" => "Estonia",
    "EG" => "Egypt",
    "ZA" => "South Africa",
    "EC" => "Ecuador",
    "IT" => "Italy",
    "VN" => "Vietnam",
    "SB" => "Solomon Islands",
    "ET" => "Ethiopia",
    "SO" => "Somalia",
    "ZW" => "Zimbabwe",
    "SA" => "Saudi Arabia",
    "ES" => "Spain",
    "ER" => "Eritrea",
    "ME" => "Montenegro",
    "MD" => "Moldova",
    "MG" => "Madagascar",
    "MF" => "Saint Martin",
    "MA" => "Morocco",
    "MC" => "Monaco",
    "UZ" => "Uzbekistan",
    "MM" => "Myanmar",
    "ML" => "Mali",
    "MO" => "Macao",
    "MN" => "Mongolia",
    "MH" => "Marshall Islands",
    "MK" => "Macedonia",
    "MU" => "Mauritius",
    "MT" => "Malta",
    "MW" => "Malawi",
    "MV" => "Maldives",
    "MQ" => "Martinique",
    "MP" => "Northern Mariana Islands",
    "MS" => "Montserrat",
    "MR" => "Mauritania",
    "IM" => "Isle of Man",
    "UG" => "Uganda",
    "TZ" => "Tanzania",
    "MY" => "Malaysia",
    "MX" => "Mexico",
    "IL" => "Israel",
    "FR" => "France",
    "IO" => "British Indian Ocean Territory",
    "SH" => "Saint Helena",
    "FI" => "Finland",
    "FJ" => "Fiji",
    "FK" => "Falkland Islands",
    "FM" => "Micronesia",
    "FO" => "Faroe Islands",
    "NI" => "Nicaragua",
    "NL" => "Netherlands",
    "NO" => "Norway",
    "NA" => "Namibia",
    "VU" => "Vanuatu",
    "NC" => "New Caledonia",
    "NE" => "Niger",
    "NF" => "Norfolk Island",
    "NG" => "Nigeria",
    "NZ" => "New Zealand",
    "NP" => "Nepal",
    "NR" => "Nauru",
    "NU" => "Niue",
    "CK" => "Cook Islands",
    "XK" => "Kosovo",
    "CI" => "Ivory Coast",
    "CH" => "Switzerland",
    "CO" => "Colombia",
    "CN" => "China",
    "CM" => "Cameroon",
    "CL" => "Chile",
    "CC" => "Cocos Islands",
    "CA" => "Canada",
    "CG" => "Republic of the Congo",
    "CF" => "Central African Republic",
    "CD" => "Democratic Republic of the Congo",
    "CZ" => "Czech Republic",
    "CY" => "Cyprus",
    "CX" => "Christmas Island",
    "CR" => "Costa Rica",
    "CW" => "Curacao",
    "CV" => "Cape Verde",
    "CU" => "Cuba",
    "SZ" => "Swaziland",
    "SY" => "Syria",
    "SX" => "Sint Maarten",
    "KG" => "Kyrgyzstan",
    "KE" => "Kenya",
    "SS" => "South Sudan",
    "SR" => "Suriname",
    "KI" => "Kiribati",
    "KH" => "Cambodia",
    "KN" => "Saint Kitts and Nevis",
    "KM" => "Comoros",
    "ST" => "Sao Tome and Principe",
    "SK" => "Slovakia",
    "KR" => "South Korea",
    "SI" => "Slovenia",
    "KP" => "North Korea",
    "KW" => "Kuwait",
    "SN" => "Senegal",
    "SM" => "San Marino",
    "SL" => "Sierra Leone",
    "SC" => "Seychelles",
    "KZ" => "Kazakhstan",
    "KY" => "Cayman Islands",
    "SG" => "Singapore",
    "SE" => "Sweden",
    "SD" => "Sudan",
    "DO" => "Dominican Republic",
    "DM" => "Dominica",
    "DJ" => "Djibouti",
    "DK" => "Denmark",
    "VG" => "British Virgin Islands",
    "DE" => "Germany",
    "YE" => "Yemen",
    "DZ" => "Algeria",
    "US" => "United States",
    "UY" => "Uruguay",
    "YT" => "Mayotte",
    "UM" => "United States Minor Outlying Islands",
    "LB" => "Lebanon",
    "LC" => "Saint Lucia",
    "LA" => "Laos",
    "TV" => "Tuvalu",
    "TW" => "Taiwan",
    "TT" => "Trinidad and Tobago",
    "TR" => "Turkey",
    "LK" => "Sri Lanka",
    "LI" => "Liechtenstein",
    "LV" => "Latvia",
    "TO" => "Tonga",
    "LT" => "Lithuania",
    "LU" => "Luxembourg",
    "LR" => "Liberia",
    "LS" => "Lesotho",
    "TH" => "Thailand",
    "TF" => "French Southern Territories",
    "TG" => "Togo",
    "TD" => "Chad",
    "TC" => "Turks and Caicos Islands",
    "LY" => "Libya",
    "VA" => "Vatican",
    "VC" => "Saint Vincent and the Grenadines",
    "AE" => "United Arab Emirates",
    "AD" => "Andorra",
    "AG" => "Antigua and Barbuda",
    "AF" => "Afghanistan",
    "AI" => "Anguilla",
    "VI" => "U.S. Virgin Islands",
    "IS" => "Iceland",
    "IR" => "Iran",
    "AM" => "Armenia",
    "AL" => "Albania",
    "AO" => "Angola",
    "AQ" => "Antarctica",
    "AS" => "American Samoa",
    "AR" => "Argentina",
    "AU" => "Australia",
    "AT" => "Austria",
    "AW" => "Aruba",
    "IN" => "India",
    "AX" => "Aland Islands",
    "AZ" => "Azerbaijan",
    "IE" => "Ireland",
    "ID" => "Indonesia",
    "UA" => "Ukraine",
    "QA" => "Qatar",
    "MZ" => "Mozambique"
);
my %default_countries_flags = (
    'AD' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฉ', 'unicode' => 'U+1F1E6 U+1F1E9' },
    'AE' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ช', 'unicode' => 'U+1F1E6 U+1F1EA' },
    'AF' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ซ', 'unicode' => 'U+1F1E6 U+1F1EB' },
    'AG' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฌ', 'unicode' => 'U+1F1E6 U+1F1EC' },
    'AI' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฎ', 'unicode' => 'U+1F1E6 U+1F1EE' },
    'AL' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฑ', 'unicode' => 'U+1F1E6 U+1F1F1' },
    'AM' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฒ', 'unicode' => 'U+1F1E6 U+1F1F2' },
    'AO' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ด', 'unicode' => 'U+1F1E6 U+1F1F4' },
    'AQ' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ถ', 'unicode' => 'U+1F1E6 U+1F1F6' },
    'AR' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ท', 'unicode' => 'U+1F1E6 U+1F1F7' },
    'AS' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ธ', 'unicode' => 'U+1F1E6 U+1F1F8' },
    'AT' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡น', 'unicode' => 'U+1F1E6 U+1F1F9' },
    'AU' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡บ', 'unicode' => 'U+1F1E6 U+1F1FA' },
    'AW' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ผ', 'unicode' => 'U+1F1E6 U+1F1FC' },
    'AX' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฝ', 'unicode' => 'U+1F1E6 U+1F1FD' },
    'AZ' => { 'emoji' => '๐Ÿ‡ฆ๐Ÿ‡ฟ', 'unicode' => 'U+1F1E6 U+1F1FF' },
    'BA' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฆ', 'unicode' => 'U+1F1E7 U+1F1E6' },
    'BB' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ง', 'unicode' => 'U+1F1E7 U+1F1E7' },
    'BD' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฉ', 'unicode' => 'U+1F1E7 U+1F1E9' },
    'BE' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ช', 'unicode' => 'U+1F1E7 U+1F1EA' },
    'BF' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ซ', 'unicode' => 'U+1F1E7 U+1F1EB' },
    'BG' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฌ', 'unicode' => 'U+1F1E7 U+1F1EC' },
    'BH' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ญ', 'unicode' => 'U+1F1E7 U+1F1ED' },
    'BI' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฎ', 'unicode' => 'U+1F1E7 U+1F1EE' },
    'BJ' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฏ', 'unicode' => 'U+1F1E7 U+1F1EF' },
    'BL' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฑ', 'unicode' => 'U+1F1E7 U+1F1F1' },
    'BM' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฒ', 'unicode' => 'U+1F1E7 U+1F1F2' },
    'BN' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ณ', 'unicode' => 'U+1F1E7 U+1F1F3' },
    'BO' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ด', 'unicode' => 'U+1F1E7 U+1F1F4' },
    'BQ' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ถ', 'unicode' => 'U+1F1E7 U+1F1F6' },
    'BR' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ท', 'unicode' => 'U+1F1E7 U+1F1F7' },
    'BS' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ธ', 'unicode' => 'U+1F1E7 U+1F1F8' },
    'BT' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡น', 'unicode' => 'U+1F1E7 U+1F1F9' },
    'BV' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ป', 'unicode' => 'U+1F1E7 U+1F1FB' },
    'BW' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ผ', 'unicode' => 'U+1F1E7 U+1F1FC' },
    'BY' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡พ', 'unicode' => 'U+1F1E7 U+1F1FE' },
    'BZ' => { 'emoji' => '๐Ÿ‡ง๐Ÿ‡ฟ', 'unicode' => 'U+1F1E7 U+1F1FF' },
    'CA' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฆ', 'unicode' => 'U+1F1E8 U+1F1E6' },
    'CC' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡จ', 'unicode' => 'U+1F1E8 U+1F1E8' },
    'CD' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฉ', 'unicode' => 'U+1F1E8 U+1F1E9' },
    'CF' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ซ', 'unicode' => 'U+1F1E8 U+1F1EB' },
    'CG' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฌ', 'unicode' => 'U+1F1E8 U+1F1EC' },
    'CH' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ญ', 'unicode' => 'U+1F1E8 U+1F1ED' },
    'CI' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฎ', 'unicode' => 'U+1F1E8 U+1F1EE' },
    'CK' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฐ', 'unicode' => 'U+1F1E8 U+1F1F0' },
    'CL' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฑ', 'unicode' => 'U+1F1E8 U+1F1F1' },
    'CM' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฒ', 'unicode' => 'U+1F1E8 U+1F1F2' },
    'CN' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ณ', 'unicode' => 'U+1F1E8 U+1F1F3' },
    'CO' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ด', 'unicode' => 'U+1F1E8 U+1F1F4' },
    'CR' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ท', 'unicode' => 'U+1F1E8 U+1F1F7' },
    'CU' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡บ', 'unicode' => 'U+1F1E8 U+1F1FA' },
    'CV' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ป', 'unicode' => 'U+1F1E8 U+1F1FB' },
    'CW' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ผ', 'unicode' => 'U+1F1E8 U+1F1FC' },
    'CX' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฝ', 'unicode' => 'U+1F1E8 U+1F1FD' },
    'CY' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡พ', 'unicode' => 'U+1F1E8 U+1F1FE' },
    'CZ' => { 'emoji' => '๐Ÿ‡จ๐Ÿ‡ฟ', 'unicode' => 'U+1F1E8 U+1F1FF' },
    'DE' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ช', 'unicode' => 'U+1F1E9 U+1F1EA' },
    'DJ' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ฏ', 'unicode' => 'U+1F1E9 U+1F1EF' },
    'DK' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ฐ', 'unicode' => 'U+1F1E9 U+1F1F0' },
    'DM' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ฒ', 'unicode' => 'U+1F1E9 U+1F1F2' },
    'DO' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ด', 'unicode' => 'U+1F1E9 U+1F1F4' },
    'DZ' => { 'emoji' => '๐Ÿ‡ฉ๐Ÿ‡ฟ', 'unicode' => 'U+1F1E9 U+1F1FF' },
    'EC' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡จ', 'unicode' => 'U+1F1EA U+1F1E8' },
    'EE' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡ช', 'unicode' => 'U+1F1EA U+1F1EA' },
    'EG' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡ฌ', 'unicode' => 'U+1F1EA U+1F1EC' },
    'EH' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡ญ', 'unicode' => 'U+1F1EA U+1F1ED' },
    'ER' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡ท', 'unicode' => 'U+1F1EA U+1F1F7' },
    'ES' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡ธ', 'unicode' => 'U+1F1EA U+1F1F8' },
    'ET' => { 'emoji' => '๐Ÿ‡ช๐Ÿ‡น', 'unicode' => 'U+1F1EA U+1F1F9' },
    'FI' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ฎ', 'unicode' => 'U+1F1EB U+1F1EE' },
    'FJ' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ฏ', 'unicode' => 'U+1F1EB U+1F1EF' },
    'FK' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ฐ', 'unicode' => 'U+1F1EB U+1F1F0' },
    'FM' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ฒ', 'unicode' => 'U+1F1EB U+1F1F2' },
    'FO' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ด', 'unicode' => 'U+1F1EB U+1F1F4' },
    'FR' => { 'emoji' => '๐Ÿ‡ซ๐Ÿ‡ท', 'unicode' => 'U+1F1EB U+1F1F7' },
    'GA' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฆ', 'unicode' => 'U+1F1EC U+1F1E6' },
    'GB' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ง', 'unicode' => 'U+1F1EC U+1F1E7' },
    'GD' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฉ', 'unicode' => 'U+1F1EC U+1F1E9' },
    'GE' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ช', 'unicode' => 'U+1F1EC U+1F1EA' },
    'GF' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ซ', 'unicode' => 'U+1F1EC U+1F1EB' },
    'GG' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฌ', 'unicode' => 'U+1F1EC U+1F1EC' },
    'GH' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ญ', 'unicode' => 'U+1F1EC U+1F1ED' },
    'GI' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฎ', 'unicode' => 'U+1F1EC U+1F1EE' },
    'GL' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฑ', 'unicode' => 'U+1F1EC U+1F1F1' },
    'GM' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ฒ', 'unicode' => 'U+1F1EC U+1F1F2' },
    'GN' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ณ', 'unicode' => 'U+1F1EC U+1F1F3' },
    'GP' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ต', 'unicode' => 'U+1F1EC U+1F1F5' },
    'GQ' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ถ', 'unicode' => 'U+1F1EC U+1F1F6' },
    'GR' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ท', 'unicode' => 'U+1F1EC U+1F1F7' },
    'GS' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ธ', 'unicode' => 'U+1F1EC U+1F1F8' },
    'GT' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡น', 'unicode' => 'U+1F1EC U+1F1F9' },
    'GU' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡บ', 'unicode' => 'U+1F1EC U+1F1FA' },
    'GW' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡ผ', 'unicode' => 'U+1F1EC U+1F1FC' },
    'GY' => { 'emoji' => '๐Ÿ‡ฌ๐Ÿ‡พ', 'unicode' => 'U+1F1EC U+1F1FE' },
    'HK' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡ฐ', 'unicode' => 'U+1F1ED U+1F1F0' },
    'HM' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡ฒ', 'unicode' => 'U+1F1ED U+1F1F2' },
    'HN' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡ณ', 'unicode' => 'U+1F1ED U+1F1F3' },
    'HR' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡ท', 'unicode' => 'U+1F1ED U+1F1F7' },
    'HT' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡น', 'unicode' => 'U+1F1ED U+1F1F9' },
    'HU' => { 'emoji' => '๐Ÿ‡ญ๐Ÿ‡บ', 'unicode' => 'U+1F1ED U+1F1FA' },
    'ID' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ฉ', 'unicode' => 'U+1F1EE U+1F1E9' },
    'IE' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ช', 'unicode' => 'U+1F1EE U+1F1EA' },
    'IL' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ฑ', 'unicode' => 'U+1F1EE U+1F1F1' },
    'IM' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ฒ', 'unicode' => 'U+1F1EE U+1F1F2' },
    'IN' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ณ', 'unicode' => 'U+1F1EE U+1F1F3' },
    'IO' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ด', 'unicode' => 'U+1F1EE U+1F1F4' },
    'IQ' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ถ', 'unicode' => 'U+1F1EE U+1F1F6' },
    'IR' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ท', 'unicode' => 'U+1F1EE U+1F1F7' },
    'IS' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡ธ', 'unicode' => 'U+1F1EE U+1F1F8' },
    'IT' => { 'emoji' => '๐Ÿ‡ฎ๐Ÿ‡น', 'unicode' => 'U+1F1EE U+1F1F9' },
    'JE' => { 'emoji' => '๐Ÿ‡ฏ๐Ÿ‡ช', 'unicode' => 'U+1F1EF U+1F1EA' },
    'JM' => { 'emoji' => '๐Ÿ‡ฏ๐Ÿ‡ฒ', 'unicode' => 'U+1F1EF U+1F1F2' },
    'JO' => { 'emoji' => '๐Ÿ‡ฏ๐Ÿ‡ด', 'unicode' => 'U+1F1EF U+1F1F4' },
    'JP' => { 'emoji' => '๐Ÿ‡ฏ๐Ÿ‡ต', 'unicode' => 'U+1F1EF U+1F1F5' },
    'KE' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ช', 'unicode' => 'U+1F1F0 U+1F1EA' },
    'KG' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ฌ', 'unicode' => 'U+1F1F0 U+1F1EC' },
    'KH' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ญ', 'unicode' => 'U+1F1F0 U+1F1ED' },
    'KI' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ฎ', 'unicode' => 'U+1F1F0 U+1F1EE' },
    'KM' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ฒ', 'unicode' => 'U+1F1F0 U+1F1F2' },
    'KN' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ณ', 'unicode' => 'U+1F1F0 U+1F1F3' },
    'KP' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ต', 'unicode' => 'U+1F1F0 U+1F1F5' },
    'KR' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ท', 'unicode' => 'U+1F1F0 U+1F1F7' },
    'KW' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ผ', 'unicode' => 'U+1F1F0 U+1F1FC' },
    'KY' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡พ', 'unicode' => 'U+1F1F0 U+1F1FE' },
    'KZ' => { 'emoji' => '๐Ÿ‡ฐ๐Ÿ‡ฟ', 'unicode' => 'U+1F1F0 U+1F1FF' },
    'LA' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ฆ', 'unicode' => 'U+1F1F1 U+1F1E6' },
    'LB' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ง', 'unicode' => 'U+1F1F1 U+1F1E7' },
    'LC' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡จ', 'unicode' => 'U+1F1F1 U+1F1E8' },
    'LI' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ฎ', 'unicode' => 'U+1F1F1 U+1F1EE' },
    'LK' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ฐ', 'unicode' => 'U+1F1F1 U+1F1F0' },
    'LR' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ท', 'unicode' => 'U+1F1F1 U+1F1F7' },
    'LS' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ธ', 'unicode' => 'U+1F1F1 U+1F1F8' },
    'LT' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡น', 'unicode' => 'U+1F1F1 U+1F1F9' },
    'LU' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡บ', 'unicode' => 'U+1F1F1 U+1F1FA' },
    'LV' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡ป', 'unicode' => 'U+1F1F1 U+1F1FB' },
    'LY' => { 'emoji' => '๐Ÿ‡ฑ๐Ÿ‡พ', 'unicode' => 'U+1F1F1 U+1F1FE' },
    'MA' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฆ', 'unicode' => 'U+1F1F2 U+1F1E6' },
    'MC' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡จ', 'unicode' => 'U+1F1F2 U+1F1E8' },
    'MD' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฉ', 'unicode' => 'U+1F1F2 U+1F1E9' },
    'ME' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ช', 'unicode' => 'U+1F1F2 U+1F1EA' },
    'MF' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ซ', 'unicode' => 'U+1F1F2 U+1F1EB' },
    'MG' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฌ', 'unicode' => 'U+1F1F2 U+1F1EC' },
    'MH' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ญ', 'unicode' => 'U+1F1F2 U+1F1ED' },
    'MK' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฐ', 'unicode' => 'U+1F1F2 U+1F1F0' },
    'ML' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฑ', 'unicode' => 'U+1F1F2 U+1F1F1' },
    'MM' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฒ', 'unicode' => 'U+1F1F2 U+1F1F2' },
    'MN' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ณ', 'unicode' => 'U+1F1F2 U+1F1F3' },
    'MO' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ด', 'unicode' => 'U+1F1F2 U+1F1F4' },
    'MP' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ต', 'unicode' => 'U+1F1F2 U+1F1F5' },
    'MQ' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ถ', 'unicode' => 'U+1F1F2 U+1F1F6' },
    'MR' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ท', 'unicode' => 'U+1F1F2 U+1F1F7' },
    'MS' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ธ', 'unicode' => 'U+1F1F2 U+1F1F8' },
    'MT' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡น', 'unicode' => 'U+1F1F2 U+1F1F9' },
    'MU' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡บ', 'unicode' => 'U+1F1F2 U+1F1FA' },
    'MV' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ป', 'unicode' => 'U+1F1F2 U+1F1FB' },
    'MW' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ผ', 'unicode' => 'U+1F1F2 U+1F1FC' },
    'MX' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฝ', 'unicode' => 'U+1F1F2 U+1F1FD' },
    'MY' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡พ', 'unicode' => 'U+1F1F2 U+1F1FE' },
    'MZ' => { 'emoji' => '๐Ÿ‡ฒ๐Ÿ‡ฟ', 'unicode' => 'U+1F1F2 U+1F1FF' },
    'NA' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ฆ', 'unicode' => 'U+1F1F3 U+1F1E6' },
    'NC' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡จ', 'unicode' => 'U+1F1F3 U+1F1E8' },
    'NE' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ช', 'unicode' => 'U+1F1F3 U+1F1EA' },
    'NF' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ซ', 'unicode' => 'U+1F1F3 U+1F1EB' },
    'NG' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ฌ', 'unicode' => 'U+1F1F3 U+1F1EC' },
    'NI' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ฎ', 'unicode' => 'U+1F1F3 U+1F1EE' },
    'NL' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ฑ', 'unicode' => 'U+1F1F3 U+1F1F1' },
    'NO' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ด', 'unicode' => 'U+1F1F3 U+1F1F4' },
    'NP' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ต', 'unicode' => 'U+1F1F3 U+1F1F5' },
    'NR' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ท', 'unicode' => 'U+1F1F3 U+1F1F7' },
    'NU' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡บ', 'unicode' => 'U+1F1F3 U+1F1FA' },
    'NZ' => { 'emoji' => '๐Ÿ‡ณ๐Ÿ‡ฟ', 'unicode' => 'U+1F1F3 U+1F1FF' },
    'OM' => { 'emoji' => '๐Ÿ‡ด๐Ÿ‡ฒ', 'unicode' => 'U+1F1F4 U+1F1F2' },
    'PA' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ฆ', 'unicode' => 'U+1F1F5 U+1F1E6' },
    'PE' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ช', 'unicode' => 'U+1F1F5 U+1F1EA' },
    'PF' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ซ', 'unicode' => 'U+1F1F5 U+1F1EB' },
    'PG' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ฌ', 'unicode' => 'U+1F1F5 U+1F1EC' },
    'PH' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ญ', 'unicode' => 'U+1F1F5 U+1F1ED' },
    'PK' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ฐ', 'unicode' => 'U+1F1F5 U+1F1F0' },
    'PL' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ฑ', 'unicode' => 'U+1F1F5 U+1F1F1' },
    'PM' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ฒ', 'unicode' => 'U+1F1F5 U+1F1F2' },
    'PN' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ณ', 'unicode' => 'U+1F1F5 U+1F1F3' },
    'PR' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ท', 'unicode' => 'U+1F1F5 U+1F1F7' },
    'PS' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ธ', 'unicode' => 'U+1F1F5 U+1F1F8' },
    'PT' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡น', 'unicode' => 'U+1F1F5 U+1F1F9' },
    'PW' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡ผ', 'unicode' => 'U+1F1F5 U+1F1FC' },
    'PY' => { 'emoji' => '๐Ÿ‡ต๐Ÿ‡พ', 'unicode' => 'U+1F1F5 U+1F1FE' },
    'QA' => { 'emoji' => '๐Ÿ‡ถ๐Ÿ‡ฆ', 'unicode' => 'U+1F1F6 U+1F1E6' },
    'RE' => { 'emoji' => '๐Ÿ‡ท๐Ÿ‡ช', 'unicode' => 'U+1F1F7 U+1F1EA' },
    'RO' => { 'emoji' => '๐Ÿ‡ท๐Ÿ‡ด', 'unicode' => 'U+1F1F7 U+1F1F4' },
    'RS' => { 'emoji' => '๐Ÿ‡ท๐Ÿ‡ธ', 'unicode' => 'U+1F1F7 U+1F1F8' },
    'RU' => { 'emoji' => '๐Ÿ‡ท๐Ÿ‡บ', 'unicode' => 'U+1F1F7 U+1F1FA' },
    'RW' => { 'emoji' => '๐Ÿ‡ท๐Ÿ‡ผ', 'unicode' => 'U+1F1F7 U+1F1FC' },
    'SA' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฆ', 'unicode' => 'U+1F1F8 U+1F1E6' },
    'SB' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ง', 'unicode' => 'U+1F1F8 U+1F1E7' },
    'SC' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡จ', 'unicode' => 'U+1F1F8 U+1F1E8' },
    'SD' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฉ', 'unicode' => 'U+1F1F8 U+1F1E9' },
    'SE' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ช', 'unicode' => 'U+1F1F8 U+1F1EA' },
    'SG' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฌ', 'unicode' => 'U+1F1F8 U+1F1EC' },
    'SH' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ญ', 'unicode' => 'U+1F1F8 U+1F1ED' },
    'SI' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฎ', 'unicode' => 'U+1F1F8 U+1F1EE' },
    'SJ' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฏ', 'unicode' => 'U+1F1F8 U+1F1EF' },
    'SK' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฐ', 'unicode' => 'U+1F1F8 U+1F1F0' },
    'SL' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฑ', 'unicode' => 'U+1F1F8 U+1F1F1' },
    'SM' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฒ', 'unicode' => 'U+1F1F8 U+1F1F2' },
    'SN' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ณ', 'unicode' => 'U+1F1F8 U+1F1F3' },
    'SO' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ด', 'unicode' => 'U+1F1F8 U+1F1F4' },
    'SR' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ท', 'unicode' => 'U+1F1F8 U+1F1F7' },
    'SS' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ธ', 'unicode' => 'U+1F1F8 U+1F1F8' },
    'ST' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡น', 'unicode' => 'U+1F1F8 U+1F1F9' },
    'SV' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ป', 'unicode' => 'U+1F1F8 U+1F1FB' },
    'SX' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฝ', 'unicode' => 'U+1F1F8 U+1F1FD' },
    'SY' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡พ', 'unicode' => 'U+1F1F8 U+1F1FE' },
    'SZ' => { 'emoji' => '๐Ÿ‡ธ๐Ÿ‡ฟ', 'unicode' => 'U+1F1F8 U+1F1FF' },
    'TC' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡จ', 'unicode' => 'U+1F1F9 U+1F1E8' },
    'TD' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฉ', 'unicode' => 'U+1F1F9 U+1F1E9' },
    'TF' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ซ', 'unicode' => 'U+1F1F9 U+1F1EB' },
    'TG' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฌ', 'unicode' => 'U+1F1F9 U+1F1EC' },
    'TH' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ญ', 'unicode' => 'U+1F1F9 U+1F1ED' },
    'TJ' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฏ', 'unicode' => 'U+1F1F9 U+1F1EF' },
    'TK' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฐ', 'unicode' => 'U+1F1F9 U+1F1F0' },
    'TL' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฑ', 'unicode' => 'U+1F1F9 U+1F1F1' },
    'TM' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฒ', 'unicode' => 'U+1F1F9 U+1F1F2' },
    'TN' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ณ', 'unicode' => 'U+1F1F9 U+1F1F3' },
    'TO' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ด', 'unicode' => 'U+1F1F9 U+1F1F4' },
    'TR' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ท', 'unicode' => 'U+1F1F9 U+1F1F7' },
    'TT' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡น', 'unicode' => 'U+1F1F9 U+1F1F9' },
    'TV' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ป', 'unicode' => 'U+1F1F9 U+1F1FB' },
    'TW' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ผ', 'unicode' => 'U+1F1F9 U+1F1FC' },
    'TZ' => { 'emoji' => '๐Ÿ‡น๐Ÿ‡ฟ', 'unicode' => 'U+1F1F9 U+1F1FF' },
    'UA' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡ฆ', 'unicode' => 'U+1F1FA U+1F1E6' },
    'UG' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡ฌ', 'unicode' => 'U+1F1FA U+1F1EC' },
    'UM' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡ฒ', 'unicode' => 'U+1F1FA U+1F1F2' },
    'US' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡ธ', 'unicode' => 'U+1F1FA U+1F1F8' },
    'UY' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡พ', 'unicode' => 'U+1F1FA U+1F1FE' },
    'UZ' => { 'emoji' => '๐Ÿ‡บ๐Ÿ‡ฟ', 'unicode' => 'U+1F1FA U+1F1FF' },
    'VA' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡ฆ', 'unicode' => 'U+1F1FB U+1F1E6' },
    'VC' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡จ', 'unicode' => 'U+1F1FB U+1F1E8' },
    'VE' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡ช', 'unicode' => 'U+1F1FB U+1F1EA' },
    'VG' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡ฌ', 'unicode' => 'U+1F1FB U+1F1EC' },
    'VI' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡ฎ', 'unicode' => 'U+1F1FB U+1F1EE' },
    'VN' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡ณ', 'unicode' => 'U+1F1FB U+1F1F3' },
    'VU' => { 'emoji' => '๐Ÿ‡ป๐Ÿ‡บ', 'unicode' => 'U+1F1FB U+1F1FA' },
    'WF' => { 'emoji' => '๐Ÿ‡ผ๐Ÿ‡ซ', 'unicode' => 'U+1F1FC U+1F1EB' },
    'WS' => { 'emoji' => '๐Ÿ‡ผ๐Ÿ‡ธ', 'unicode' => 'U+1F1FC U+1F1F8' },
    'XK' => { 'emoji' => '๐Ÿ‡ฝ๐Ÿ‡ฐ', 'unicode' => 'U+1F1FD U+1F1F0' },
    'YE' => { 'emoji' => '๐Ÿ‡พ๐Ÿ‡ช', 'unicode' => 'U+1F1FE U+1F1EA' },
    'YT' => { 'emoji' => '๐Ÿ‡พ๐Ÿ‡น', 'unicode' => 'U+1F1FE U+1F1F9' },
    'ZA' => { 'emoji' => '๐Ÿ‡ฟ๐Ÿ‡ฆ', 'unicode' => 'U+1F1FF U+1F1E6' },
    'ZM' => { 'emoji' => '๐Ÿ‡ฟ๐Ÿ‡ฒ', 'unicode' => 'U+1F1FF U+1F1F2' },
    'ZW' => { 'emoji' => '๐Ÿ‡ฟ๐Ÿ‡ผ', 'unicode' => 'U+1F1FF U+1F1FC' }
);
my @default_eu_countries = (
    "IE", "AT", "LT", "LU", "LV", "DE", "DK", "SE", "SI", "SK",
    "CZ", "CY", "NL", "FI", "FR", "MT", "ES", "IT", "EE", "PL",
    "PT", "HU", "HR", "GR", "RO", "BG", "BE"
);
my %default_countries_currencies = (
    'AD' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'AE' => { 'code' => 'AED', 'symbol' => 'ุฏ.ุฅ' },
    'AF' => { 'code' => 'AFN', 'symbol' => 'ุ‹' },
    'AG' => { 'code' => 'XCD', 'symbol' => '$' },
    'AI' => { 'code' => 'XCD', 'symbol' => '$' },
    'AL' => { 'code' => 'ALL', 'symbol' => 'L' },
    'AM' => { 'code' => 'AMD', 'symbol' => 'ึ' },
    'AO' => { 'code' => 'AOA', 'symbol' => 'Kz' },
    'AQ' => { 'code' => '',    'symbol' => '$' },
    'AR' => { 'code' => 'ARS', 'symbol' => '$' },
    'AS' => { 'code' => 'USD', 'symbol' => '$' },
    'AT' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'AU' => { 'code' => 'AUD', 'symbol' => '$' },
    'AW' => { 'code' => 'AWG', 'symbol' => 'ฦ’' },
    'AX' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'AZ' => { 'code' => 'AZN', 'symbol' => 'โ‚ผ' },
    'BA' => { 'code' => 'BAM', 'symbol' => 'KM' },
    'BB' => { 'code' => 'BBD', 'symbol' => '$' },
    'BD' => { 'code' => 'BDT', 'symbol' => 'เงณ' },
    'BE' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'BF' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'BG' => { 'code' => 'BGN', 'symbol' => 'ะปะฒ' },
    'BH' => { 'code' => 'BHD', 'symbol' => '.ุฏ.ุจ' },
    'BI' => { 'code' => 'BIF', 'symbol' => 'FBu' },
    'BJ' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'BL' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'BM' => { 'code' => 'BMD', 'symbol' => '$' },
    'BN' => { 'code' => 'BND', 'symbol' => '$' },
    'BO' => { 'code' => 'BOB', 'symbol' => '$b' },
    'BQ' => { 'code' => 'USD', 'symbol' => '$' },
    'BR' => { 'code' => 'BRL', 'symbol' => 'R$' },
    'BS' => { 'code' => 'BSD', 'symbol' => '$' },
    'BT' => { 'code' => 'BTN', 'symbol' => 'Nu.' },
    'BV' => { 'code' => 'NOK', 'symbol' => 'kr' },
    'BW' => { 'code' => 'BWP', 'symbol' => 'P' },
    'BY' => { 'code' => 'BYR', 'symbol' => 'Br' },
    'BZ' => { 'code' => 'BZD', 'symbol' => 'BZ$' },
    'CA' => { 'code' => 'CAD', 'symbol' => '$' },
    'CC' => { 'code' => 'AUD', 'symbol' => '$' },
    'CD' => { 'code' => 'CDF', 'symbol' => 'FC' },
    'CF' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'CG' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'CH' => { 'code' => 'CHF', 'symbol' => 'CHF' },
    'CI' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'CK' => { 'code' => 'NZD', 'symbol' => '$' },
    'CL' => { 'code' => 'CLP', 'symbol' => '$' },
    'CM' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'CN' => { 'code' => 'CNY', 'symbol' => 'ยฅ' },
    'CO' => { 'code' => 'COP', 'symbol' => '$' },
    'CR' => { 'code' => 'CRC', 'symbol' => 'โ‚ก' },
    'CU' => { 'code' => 'CUP', 'symbol' => 'โ‚ฑ' },
    'CV' => { 'code' => 'CVE', 'symbol' => '$' },
    'CW' => { 'code' => 'ANG', 'symbol' => 'ฦ’' },
    'CX' => { 'code' => 'AUD', 'symbol' => '$' },
    'CY' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'CZ' => { 'code' => 'CZK', 'symbol' => 'Kฤ' },
    'DE' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'DJ' => { 'code' => 'DJF', 'symbol' => 'Fdj' },
    'DK' => { 'code' => 'DKK', 'symbol' => 'kr' },
    'DM' => { 'code' => 'XCD', 'symbol' => '$' },
    'DO' => { 'code' => 'DOP', 'symbol' => 'RD$' },
    'DZ' => { 'code' => 'DZD', 'symbol' => 'ุฏุฌ' },
    'EC' => { 'code' => 'USD', 'symbol' => '$' },
    'EE' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'EG' => { 'code' => 'EGP', 'symbol' => 'ยฃ' },
    'EH' => { 'code' => 'MAD', 'symbol' => 'MAD' },
    'ER' => { 'code' => 'ERN', 'symbol' => 'Nfk' },
    'ES' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'ET' => { 'code' => 'ETB', 'symbol' => 'Br' },
    'FI' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'FJ' => { 'code' => 'FJD', 'symbol' => '$' },
    'FK' => { 'code' => 'FKP', 'symbol' => 'ยฃ' },
    'FM' => { 'code' => 'USD', 'symbol' => '$' },
    'FO' => { 'code' => 'DKK', 'symbol' => 'kr' },
    'FR' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'GA' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'GB' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
    'GD' => { 'code' => 'XCD', 'symbol' => '$' },
    'GE' => { 'code' => 'GEL', 'symbol' => 'แƒš' },
    'GF' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'GG' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
    'GH' => { 'code' => 'GHS', 'symbol' => 'GHโ‚ต' },
    'GI' => { 'code' => 'GIP', 'symbol' => 'ยฃ' },
    'GL' => { 'code' => 'DKK', 'symbol' => 'kr' },
    'GM' => { 'code' => 'GMD', 'symbol' => 'D' },
    'GN' => { 'code' => 'GNF', 'symbol' => 'FG' },
    'GP' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'GQ' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'GR' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'GS' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
    'GT' => { 'code' => 'GTQ', 'symbol' => 'Q' },
    'GU' => { 'code' => 'USD', 'symbol' => '$' },
    'GW' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'GY' => { 'code' => 'GYD', 'symbol' => '$' },
    'HK' => { 'code' => 'HKD', 'symbol' => '$' },
    'HM' => { 'code' => 'AUD', 'symbol' => '$' },
    'HN' => { 'code' => 'HNL', 'symbol' => 'L' },
    'HR' => { 'code' => 'HRK', 'symbol' => 'kn' },
    'HT' => { 'code' => 'HTG', 'symbol' => 'G' },
    'HU' => { 'code' => 'HUF', 'symbol' => 'Ft' },
    'ID' => { 'code' => 'IDR', 'symbol' => 'Rp' },
    'IE' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'IL' => { 'code' => 'ILS', 'symbol' => 'โ‚ช' },
    'IM' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
    'IN' => { 'code' => 'INR', 'symbol' => 'โ‚น' },
    'IO' => { 'code' => 'USD', 'symbol' => '$' },
    'IQ' => { 'code' => 'IQD', 'symbol' => 'ุน.ุฏ' },
    'IR' => { 'code' => 'IRR', 'symbol' => '๏ทผ' },
    'IS' => { 'code' => 'ISK', 'symbol' => 'kr' },
    'IT' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'JE' => { 'code' => 'GBP', 'symbol' => 'ยฃ' },
    'JM' => { 'code' => 'JMD', 'symbol' => 'J$' },
    'JO' => { 'code' => 'JOD', 'symbol' => 'JD' },
    'JP' => { 'code' => 'JPY', 'symbol' => 'ยฅ' },
    'KE' => { 'code' => 'KES', 'symbol' => 'KSh' },
    'KG' => { 'code' => 'KGS', 'symbol' => 'ะปะฒ' },
    'KH' => { 'code' => 'KHR', 'symbol' => 'แŸ›' },
    'KI' => { 'code' => 'AUD', 'symbol' => '$' },
    'KM' => { 'code' => 'KMF', 'symbol' => 'CF' },
    'KN' => { 'code' => 'XCD', 'symbol' => '$' },
    'KP' => { 'code' => 'KPW', 'symbol' => 'โ‚ฉ' },
    'KR' => { 'code' => 'KRW', 'symbol' => 'โ‚ฉ' },
    'KW' => { 'code' => 'KWD', 'symbol' => 'KD' },
    'KY' => { 'code' => 'KYD', 'symbol' => '$' },
    'KZ' => { 'code' => 'KZT', 'symbol' => 'โ‚ธ' },
    'LA' => { 'code' => 'LAK', 'symbol' => 'โ‚ญ' },
    'LB' => { 'code' => 'LBP', 'symbol' => 'ยฃ' },
    'LC' => { 'code' => 'XCD', 'symbol' => '$' },
    'LI' => { 'code' => 'CHF', 'symbol' => 'CHF' },
    'LK' => { 'code' => 'LKR', 'symbol' => 'โ‚จ' },
    'LR' => { 'code' => 'LRD', 'symbol' => '$' },
    'LS' => { 'code' => 'LSL', 'symbol' => 'M' },
    'LT' => { 'code' => 'LTL', 'symbol' => 'Lt' },
    'LU' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'LV' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'LY' => { 'code' => 'LYD', 'symbol' => 'LD' },
    'MA' => { 'code' => 'MAD', 'symbol' => 'MAD' },
    'MC' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'MD' => { 'code' => 'MDL', 'symbol' => 'lei' },
    'ME' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'MF' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'MG' => { 'code' => 'MGA', 'symbol' => 'Ar' },
    'MH' => { 'code' => 'USD', 'symbol' => '$' },
    'MK' => { 'code' => 'MKD', 'symbol' => 'ะดะตะฝ' },
    'ML' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'MM' => { 'code' => 'MMK', 'symbol' => 'K' },
    'MN' => { 'code' => 'MNT', 'symbol' => 'โ‚ฎ' },
    'MO' => { 'code' => 'MOP', 'symbol' => 'MOP$' },
    'MP' => { 'code' => 'USD', 'symbol' => '$' },
    'MQ' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'MR' => { 'code' => 'MRO', 'symbol' => 'UM' },
    'MS' => { 'code' => 'XCD', 'symbol' => '$' },
    'MT' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'MU' => { 'code' => 'MUR', 'symbol' => 'โ‚จ' },
    'MV' => { 'code' => 'MVR', 'symbol' => 'Rf' },
    'MW' => { 'code' => 'MWK', 'symbol' => 'MK' },
    'MX' => { 'code' => 'MXN', 'symbol' => '$' },
    'MY' => { 'code' => 'MYR', 'symbol' => 'RM' },
    'MZ' => { 'code' => 'MZN', 'symbol' => 'MT' },
    'NA' => { 'code' => 'NAD', 'symbol' => '$' },
    'NC' => { 'code' => 'XPF', 'symbol' => 'โ‚ฃ' },
    'NE' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'NF' => { 'code' => 'AUD', 'symbol' => '$' },
    'NG' => { 'code' => 'NGN', 'symbol' => 'โ‚ฆ' },
    'NI' => { 'code' => 'NIO', 'symbol' => 'C$' },
    'NL' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'NO' => { 'code' => 'NOK', 'symbol' => 'kr' },
    'NP' => { 'code' => 'NPR', 'symbol' => 'โ‚จ' },
    'NR' => { 'code' => 'AUD', 'symbol' => '$' },
    'NU' => { 'code' => 'NZD', 'symbol' => '$' },
    'NZ' => { 'code' => 'NZD', 'symbol' => '$' },
    'OM' => { 'code' => 'OMR', 'symbol' => '๏ทผ' },
    'PA' => { 'code' => 'PAB', 'symbol' => 'B/.' },
    'PE' => { 'code' => 'PEN', 'symbol' => 'S/.' },
    'PF' => { 'code' => 'XPF', 'symbol' => 'โ‚ฃ' },
    'PG' => { 'code' => 'PGK', 'symbol' => 'K' },
    'PH' => { 'code' => 'PHP', 'symbol' => 'โ‚ฑ' },
    'PK' => { 'code' => 'PKR', 'symbol' => 'โ‚จ' },
    'PL' => { 'code' => 'PLN', 'symbol' => 'zล‚' },
    'PM' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'PN' => { 'code' => 'NZD', 'symbol' => '$' },
    'PR' => { 'code' => 'USD', 'symbol' => '$' },
    'PS' => { 'code' => 'ILS', 'symbol' => 'โ‚ช' },
    'PT' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'PW' => { 'code' => 'USD', 'symbol' => '$' },
    'PY' => { 'code' => 'PYG', 'symbol' => 'Gs' },
    'QA' => { 'code' => 'QAR', 'symbol' => '๏ทผ' },
    'RE' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'RO' => { 'code' => 'RON', 'symbol' => 'lei' },
    'RS' => { 'code' => 'RSD', 'symbol' => 'ะ”ะธะฝ.' },
    'RU' => { 'code' => 'RUB', 'symbol' => 'โ‚ฝ' },
    'RW' => { 'code' => 'RWF', 'symbol' => 'Rโ‚ฃ' },
    'SA' => { 'code' => 'SAR', 'symbol' => '๏ทผ' },
    'SB' => { 'code' => 'SBD', 'symbol' => '$' },
    'SC' => { 'code' => 'SCR', 'symbol' => 'โ‚จ' },
    'SD' => { 'code' => 'SDG', 'symbol' => 'ุฌ.ุณ.' },
    'SE' => { 'code' => 'SEK', 'symbol' => 'kr' },
    'SG' => { 'code' => 'SGD', 'symbol' => 'S$' },
    'SH' => { 'code' => 'SHP', 'symbol' => 'ยฃ' },
    'SI' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'SJ' => { 'code' => 'NOK', 'symbol' => 'kr' },
    'SK' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'SL' => { 'code' => 'SLL', 'symbol' => 'Le' },
    'SM' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'SN' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'SO' => { 'code' => 'SOS', 'symbol' => 'S' },
    'SR' => { 'code' => 'SRD', 'symbol' => '$' },
    'SS' => { 'code' => 'SSP', 'symbol' => 'ยฃ' },
    'ST' => { 'code' => 'STD', 'symbol' => 'Db' },
    'SV' => { 'code' => 'USD', 'symbol' => '$' },
    'SX' => { 'code' => 'ANG', 'symbol' => 'ฦ’' },
    'SY' => { 'code' => 'SYP', 'symbol' => 'ยฃ' },
    'SZ' => { 'code' => 'SZL', 'symbol' => 'E' },
    'TC' => { 'code' => 'USD', 'symbol' => '$' },
    'TD' => { 'code' => 'XAF', 'symbol' => 'FCFA' },
    'TF' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'TG' => { 'code' => 'XOF', 'symbol' => 'CFA' },
    'TH' => { 'code' => 'THB', 'symbol' => 'เธฟ' },
    'TJ' => { 'code' => 'TJS', 'symbol' => 'SM' },
    'TK' => { 'code' => 'NZD', 'symbol' => '$' },
    'TL' => { 'code' => 'USD', 'symbol' => '$' },
    'TM' => { 'code' => 'TMT', 'symbol' => 'T' },
    'TN' => { 'code' => 'TND', 'symbol' => 'ุฏ.ุช' },
    'TO' => { 'code' => 'TOP', 'symbol' => 'T$' },
    'TR' => { 'code' => 'TRY', 'symbol' => 'โ‚บ' },
    'TT' => { 'code' => 'TTD', 'symbol' => 'TT$' },
    'TV' => { 'code' => 'AUD', 'symbol' => '$' },
    'TW' => { 'code' => 'TWD', 'symbol' => 'NT$' },
    'TZ' => { 'code' => 'TZS', 'symbol' => 'TSh' },
    'UA' => { 'code' => 'UAH', 'symbol' => 'โ‚ด' },
    'UG' => { 'code' => 'UGX', 'symbol' => 'USh' },
    'UM' => { 'code' => 'USD', 'symbol' => '$' },
    'US' => { 'code' => 'USD', 'symbol' => '$' },
    'UY' => { 'code' => 'UYU', 'symbol' => '$U' },
    'UZ' => { 'code' => 'UZS', 'symbol' => 'ะปะฒ' },
    'VA' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'VC' => { 'code' => 'XCD', 'symbol' => '$' },
    'VE' => { 'code' => 'VEF', 'symbol' => 'Bs' },
    'VG' => { 'code' => 'USD', 'symbol' => '$' },
    'VI' => { 'code' => 'USD', 'symbol' => '$' },
    'VN' => { 'code' => 'VND', 'symbol' => 'โ‚ซ' },
    'VU' => { 'code' => 'VUV', 'symbol' => 'VT' },
    'WF' => { 'code' => 'XPF', 'symbol' => 'โ‚ฃ' },
    'WS' => { 'code' => 'WST', 'symbol' => 'WS$' },
    'XK' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'YE' => { 'code' => 'YER', 'symbol' => '๏ทผ' },
    'YT' => { 'code' => 'EUR', 'symbol' => 'โ‚ฌ' },
    'ZA' => { 'code' => 'ZAR', 'symbol' => 'R' },
    'ZM' => { 'code' => 'ZMK', 'symbol' => 'ZK' },
    'ZW' => { 'code' => 'ZWL', 'symbol' => '$' }
);
my %default_continents = (
    "BD" => { "code" => "AS", "name" => "Asia" },
    "BE" => { "code" => "EU", "name" => "Europe" },
    "BF" => { "code" => "AF", "name" => "Africa" },
    "BG" => { "code" => "EU", "name" => "Europe" },
    "BA" => { "code" => "EU", "name" => "Europe" },
    "BB" => { "code" => "NA", "name" => "North America" },
    "WF" => { "code" => "OC", "name" => "Oceania" },
    "BL" => { "code" => "NA", "name" => "North America" },
    "BM" => { "code" => "NA", "name" => "North America" },
    "BN" => { "code" => "AS", "name" => "Asia" },
    "BO" => { "code" => "SA", "name" => "South America" },
    "BH" => { "code" => "AS", "name" => "Asia" },
    "BI" => { "code" => "AF", "name" => "Africa" },
    "BJ" => { "code" => "AF", "name" => "Africa" },
    "BT" => { "code" => "AS", "name" => "Asia" },
    "JM" => { "code" => "NA", "name" => "North America" },
    "BV" => { "code" => "AN", "name" => "Antarctica" },
    "BW" => { "code" => "AF", "name" => "Africa" },
    "WS" => { "code" => "OC", "name" => "Oceania" },
    "BQ" => { "code" => "NA", "name" => "North America" },
    "BR" => { "code" => "SA", "name" => "South America" },
    "BS" => { "code" => "NA", "name" => "North America" },
    "JE" => { "code" => "EU", "name" => "Europe" },
    "BY" => { "code" => "EU", "name" => "Europe" },
    "BZ" => { "code" => "NA", "name" => "North America" },
    "RU" => { "code" => "EU", "name" => "Europe" },
    "RW" => { "code" => "AF", "name" => "Africa" },
    "RS" => { "code" => "EU", "name" => "Europe" },
    "TL" => { "code" => "OC", "name" => "Oceania" },
    "RE" => { "code" => "AF", "name" => "Africa" },
    "TM" => { "code" => "AS", "name" => "Asia" },
    "TJ" => { "code" => "AS", "name" => "Asia" },
    "RO" => { "code" => "EU", "name" => "Europe" },
    "TK" => { "code" => "OC", "name" => "Oceania" },
    "GW" => { "code" => "AF", "name" => "Africa" },
    "GU" => { "code" => "OC", "name" => "Oceania" },
    "GT" => { "code" => "NA", "name" => "North America" },
    "GS" => { "code" => "AN", "name" => "Antarctica" },
    "GR" => { "code" => "EU", "name" => "Europe" },
    "GQ" => { "code" => "AF", "name" => "Africa" },
    "GP" => { "code" => "NA", "name" => "North America" },
    "JP" => { "code" => "AS", "name" => "Asia" },
    "GY" => { "code" => "SA", "name" => "South America" },
    "GG" => { "code" => "EU", "name" => "Europe" },
    "GF" => { "code" => "SA", "name" => "South America" },
    "GE" => { "code" => "AS", "name" => "Asia" },
    "GD" => { "code" => "NA", "name" => "North America" },
    "GB" => { "code" => "EU", "name" => "Europe" },
    "GA" => { "code" => "AF", "name" => "Africa" },
    "SV" => { "code" => "NA", "name" => "North America" },
    "GN" => { "code" => "AF", "name" => "Africa" },
    "GM" => { "code" => "AF", "name" => "Africa" },
    "GL" => { "code" => "NA", "name" => "North America" },
    "GI" => { "code" => "EU", "name" => "Europe" },
    "GH" => { "code" => "AF", "name" => "Africa" },
    "OM" => { "code" => "AS", "name" => "Asia" },
    "TN" => { "code" => "AF", "name" => "Africa" },
    "JO" => { "code" => "AS", "name" => "Asia" },
    "HR" => { "code" => "EU", "name" => "Europe" },
    "HT" => { "code" => "NA", "name" => "North America" },
    "HU" => { "code" => "EU", "name" => "Europe" },
    "HK" => { "code" => "AS", "name" => "Asia" },
    "HN" => { "code" => "NA", "name" => "North America" },
    "HM" => { "code" => "AN", "name" => "Antarctica" },
    "VE" => { "code" => "SA", "name" => "South America" },
    "PR" => { "code" => "NA", "name" => "North America" },
    "PS" => { "code" => "AS", "name" => "Asia" },
    "PW" => { "code" => "OC", "name" => "Oceania" },
    "PT" => { "code" => "EU", "name" => "Europe" },
    "SJ" => { "code" => "EU", "name" => "Europe" },
    "PY" => { "code" => "SA", "name" => "South America" },
    "IQ" => { "code" => "AS", "name" => "Asia" },
    "PA" => { "code" => "NA", "name" => "North America" },
    "PF" => { "code" => "OC", "name" => "Oceania" },
    "PG" => { "code" => "OC", "name" => "Oceania" },
    "PE" => { "code" => "SA", "name" => "South America" },
    "PK" => { "code" => "AS", "name" => "Asia" },
    "PH" => { "code" => "AS", "name" => "Asia" },
    "PN" => { "code" => "OC", "name" => "Oceania" },
    "PL" => { "code" => "EU", "name" => "Europe" },
    "PM" => { "code" => "NA", "name" => "North America" },
    "ZM" => { "code" => "AF", "name" => "Africa" },
    "EH" => { "code" => "AF", "name" => "Africa" },
    "EE" => { "code" => "EU", "name" => "Europe" },
    "EG" => { "code" => "AF", "name" => "Africa" },
    "ZA" => { "code" => "AF", "name" => "Africa" },
    "EC" => { "code" => "SA", "name" => "South America" },
    "IT" => { "code" => "EU", "name" => "Europe" },
    "VN" => { "code" => "AS", "name" => "Asia" },
    "SB" => { "code" => "OC", "name" => "Oceania" },
    "ET" => { "code" => "AF", "name" => "Africa" },
    "SO" => { "code" => "AF", "name" => "Africa" },
    "ZW" => { "code" => "AF", "name" => "Africa" },
    "SA" => { "code" => "AS", "name" => "Asia" },
    "ES" => { "code" => "EU", "name" => "Europe" },
    "ER" => { "code" => "AF", "name" => "Africa" },
    "ME" => { "code" => "EU", "name" => "Europe" },
    "MD" => { "code" => "EU", "name" => "Europe" },
    "MG" => { "code" => "AF", "name" => "Africa" },
    "MF" => { "code" => "NA", "name" => "North America" },
    "MA" => { "code" => "AF", "name" => "Africa" },
    "MC" => { "code" => "EU", "name" => "Europe" },
    "UZ" => { "code" => "AS", "name" => "Asia" },
    "MM" => { "code" => "AS", "name" => "Asia" },
    "ML" => { "code" => "AF", "name" => "Africa" },
    "MO" => { "code" => "AS", "name" => "Asia" },
    "MN" => { "code" => "AS", "name" => "Asia" },
    "MH" => { "code" => "OC", "name" => "Oceania" },
    "MK" => { "code" => "EU", "name" => "Europe" },
    "MU" => { "code" => "AF", "name" => "Africa" },
    "MT" => { "code" => "EU", "name" => "Europe" },
    "MW" => { "code" => "AF", "name" => "Africa" },
    "MV" => { "code" => "AS", "name" => "Asia" },
    "MQ" => { "code" => "NA", "name" => "North America" },
    "MP" => { "code" => "OC", "name" => "Oceania" },
    "MS" => { "code" => "NA", "name" => "North America" },
    "MR" => { "code" => "AF", "name" => "Africa" },
    "IM" => { "code" => "EU", "name" => "Europe" },
    "UG" => { "code" => "AF", "name" => "Africa" },
    "TZ" => { "code" => "AF", "name" => "Africa" },
    "MY" => { "code" => "AS", "name" => "Asia" },
    "MX" => { "code" => "NA", "name" => "North America" },
    "IL" => { "code" => "AS", "name" => "Asia" },
    "FR" => { "code" => "EU", "name" => "Europe" },
    "IO" => { "code" => "AS", "name" => "Asia" },
    "SH" => { "code" => "AF", "name" => "Africa" },
    "FI" => { "code" => "EU", "name" => "Europe" },
    "FJ" => { "code" => "OC", "name" => "Oceania" },
    "FK" => { "code" => "SA", "name" => "South America" },
    "FM" => { "code" => "OC", "name" => "Oceania" },
    "FO" => { "code" => "EU", "name" => "Europe" },
    "NI" => { "code" => "NA", "name" => "North America" },
    "NL" => { "code" => "EU", "name" => "Europe" },
    "NO" => { "code" => "EU", "name" => "Europe" },
    "NA" => { "code" => "AF", "name" => "Africa" },
    "VU" => { "code" => "OC", "name" => "Oceania" },
    "NC" => { "code" => "OC", "name" => "Oceania" },
    "NE" => { "code" => "AF", "name" => "Africa" },
    "NF" => { "code" => "OC", "name" => "Oceania" },
    "NG" => { "code" => "AF", "name" => "Africa" },
    "NZ" => { "code" => "OC", "name" => "Oceania" },
    "NP" => { "code" => "AS", "name" => "Asia" },
    "NR" => { "code" => "OC", "name" => "Oceania" },
    "NU" => { "code" => "OC", "name" => "Oceania" },
    "CK" => { "code" => "OC", "name" => "Oceania" },
    "XK" => { "code" => "EU", "name" => "Europe" },
    "CI" => { "code" => "AF", "name" => "Africa" },
    "CH" => { "code" => "EU", "name" => "Europe" },
    "CO" => { "code" => "SA", "name" => "South America" },
    "CN" => { "code" => "AS", "name" => "Asia" },
    "CM" => { "code" => "AF", "name" => "Africa" },
    "CL" => { "code" => "SA", "name" => "South America" },
    "CC" => { "code" => "AS", "name" => "Asia" },
    "CA" => { "code" => "NA", "name" => "North America" },
    "CG" => { "code" => "AF", "name" => "Africa" },
    "CF" => { "code" => "AF", "name" => "Africa" },
    "CD" => { "code" => "AF", "name" => "Africa" },
    "CZ" => { "code" => "EU", "name" => "Europe" },
    "CY" => { "code" => "EU", "name" => "Europe" },
    "CX" => { "code" => "AS", "name" => "Asia" },
    "CR" => { "code" => "NA", "name" => "North America" },
    "CW" => { "code" => "NA", "name" => "North America" },
    "CV" => { "code" => "AF", "name" => "Africa" },
    "CU" => { "code" => "NA", "name" => "North America" },
    "SZ" => { "code" => "AF", "name" => "Africa" },
    "SY" => { "code" => "AS", "name" => "Asia" },
    "SX" => { "code" => "NA", "name" => "North America" },
    "KG" => { "code" => "AS", "name" => "Asia" },
    "KE" => { "code" => "AF", "name" => "Africa" },
    "SS" => { "code" => "AF", "name" => "Africa" },
    "SR" => { "code" => "SA", "name" => "South America" },
    "KI" => { "code" => "OC", "name" => "Oceania" },
    "KH" => { "code" => "AS", "name" => "Asia" },
    "KN" => { "code" => "NA", "name" => "North America" },
    "KM" => { "code" => "AF", "name" => "Africa" },
    "ST" => { "code" => "AF", "name" => "Africa" },
    "SK" => { "code" => "EU", "name" => "Europe" },
    "KR" => { "code" => "AS", "name" => "Asia" },
    "SI" => { "code" => "EU", "name" => "Europe" },
    "KP" => { "code" => "AS", "name" => "Asia" },
    "KW" => { "code" => "AS", "name" => "Asia" },
    "SN" => { "code" => "AF", "name" => "Africa" },
    "SM" => { "code" => "EU", "name" => "Europe" },
    "SL" => { "code" => "AF", "name" => "Africa" },
    "SC" => { "code" => "AF", "name" => "Africa" },
    "KZ" => { "code" => "AS", "name" => "Asia" },
    "KY" => { "code" => "NA", "name" => "North America" },
    "SG" => { "code" => "AS", "name" => "Asia" },
    "SE" => { "code" => "EU", "name" => "Europe" },
    "SD" => { "code" => "AF", "name" => "Africa" },
    "DO" => { "code" => "NA", "name" => "North America" },
    "DM" => { "code" => "NA", "name" => "North America" },
    "DJ" => { "code" => "AF", "name" => "Africa" },
    "DK" => { "code" => "EU", "name" => "Europe" },
    "VG" => { "code" => "NA", "name" => "North America" },
    "DE" => { "code" => "EU", "name" => "Europe" },
    "YE" => { "code" => "AS", "name" => "Asia" },
    "DZ" => { "code" => "AF", "name" => "Africa" },
    "US" => { "code" => "NA", "name" => "North America" },
    "UY" => { "code" => "SA", "name" => "South America" },
    "YT" => { "code" => "AF", "name" => "Africa" },
    "UM" => { "code" => "OC", "name" => "Oceania" },
    "LB" => { "code" => "AS", "name" => "Asia" },
    "LC" => { "code" => "NA", "name" => "North America" },
    "LA" => { "code" => "AS", "name" => "Asia" },
    "TV" => { "code" => "OC", "name" => "Oceania" },
    "TW" => { "code" => "AS", "name" => "Asia" },
    "TT" => { "code" => "NA", "name" => "North America" },
    "TR" => { "code" => "AS", "name" => "Asia" },
    "LK" => { "code" => "AS", "name" => "Asia" },
    "LI" => { "code" => "EU", "name" => "Europe" },
    "LV" => { "code" => "EU", "name" => "Europe" },
    "TO" => { "code" => "OC", "name" => "Oceania" },
    "LT" => { "code" => "EU", "name" => "Europe" },
    "LU" => { "code" => "EU", "name" => "Europe" },
    "LR" => { "code" => "AF", "name" => "Africa" },
    "LS" => { "code" => "AF", "name" => "Africa" },
    "TH" => { "code" => "AS", "name" => "Asia" },
    "TF" => { "code" => "AN", "name" => "Antarctica" },
    "TG" => { "code" => "AF", "name" => "Africa" },
    "TD" => { "code" => "AF", "name" => "Africa" },
    "TC" => { "code" => "NA", "name" => "North America" },
    "LY" => { "code" => "AF", "name" => "Africa" },
    "VA" => { "code" => "EU", "name" => "Europe" },
    "VC" => { "code" => "NA", "name" => "North America" },
    "AE" => { "code" => "AS", "name" => "Asia" },
    "AD" => { "code" => "EU", "name" => "Europe" },
    "AG" => { "code" => "NA", "name" => "North America" },
    "AF" => { "code" => "AS", "name" => "Asia" },
    "AI" => { "code" => "NA", "name" => "North America" },
    "VI" => { "code" => "NA", "name" => "North America" },
    "IS" => { "code" => "EU", "name" => "Europe" },
    "IR" => { "code" => "AS", "name" => "Asia" },
    "AM" => { "code" => "AS", "name" => "Asia" },
    "AL" => { "code" => "EU", "name" => "Europe" },
    "AO" => { "code" => "AF", "name" => "Africa" },
    "AQ" => { "code" => "AN", "name" => "Antarctica" },
    "AS" => { "code" => "OC", "name" => "Oceania" },
    "AR" => { "code" => "SA", "name" => "South America" },
    "AU" => { "code" => "OC", "name" => "Oceania" },
    "AT" => { "code" => "EU", "name" => "Europe" },
    "AW" => { "code" => "NA", "name" => "North America" },
    "IN" => { "code" => "AS", "name" => "Asia" },
    "AX" => { "code" => "EU", "name" => "Europe" },
    "AZ" => { "code" => "AS", "name" => "Asia" },
    "IE" => { "code" => "EU", "name" => "Europe" },
    "ID" => { "code" => "AS", "name" => "Asia" },
    "UA" => { "code" => "EU", "name" => "Europe" },
    "QA" => { "code" => "AS", "name" => "Asia" },
    "MZ" => { "code" => "AF", "name" => "Africa" }
);

sub new {
    my ( $pkg, $token, %options ) = @_;

    my $self = {};
    $token = defined $token ? $token : '';

    $self->{base_url}      = $base_url;
    $self->{ua}            = LWP::UserAgent->new;
    $self->{ua}->ssl_opts( 'verify_hostname' => 0 );
    $self->{ua}->default_headers(
        HTTP::Headers->new(
            Accept        => 'application/json',
            Authorization => 'Bearer ' . $token
        )
    );
    $self->{ua}->agent("IPinfoClient/Perl/$VERSION");

    my $timeout =
      defined $options{timeout} ? $options{timeout} : DEFAULT_TIMEOUT;
    $self->{ua}->timeout($timeout);

    $self->{message} = '';

    bless $self, $pkg;

    my $countries =
      exists $options{countries} ? $options{countries} : \%default_countries;
    my $eu_countries =
      exists $options{eu_countries}
      ? $options{eu_countries}
      : \@default_eu_countries;
    my $countries_flags =
      exists $options{countries_flags}
      ? $options{countries_flags}
      : \%default_countries_flags;
    my $countries_currencies =
      exists $options{countries_currencies}
      ? $options{countries_currencies}
      : \%default_countries_currencies;
    my $continents =
      exists $options{continents}
      ? $options{continents}
      : \%default_continents;

    $self->{countries}            = $countries;
    $self->{eu_countries}         = $eu_countries;
    $self->{countries_flags}      = $countries_flags;
    $self->{countries_currencies} = $countries_currencies;
    $self->{continents}           = $continents;
    $self->{cache}                = $self->_build_cache(%options);

    return $self;
}

sub info {
    my ( $self, $ip ) = @_;

    return $self->_get_info( $ip, );
}


sub _get_info {
    my ( $self, $ip ) = @_;

    $ip    = defined $ip    ? $ip    : '';

    if ( $ip ne '' ) {
        my $validated_ip = Net::CIDR::cidrvalidate($ip);
        if ( !defined $validated_ip ) {
            $self->{message} = 'Invalid IP address';
            return undef;
        }
    }

    my ( $info, $message ) = $self->_lookup_info( $ip );
    $self->{message} = $message;
    return $info if eval { $info->isa('Geo::DetailsLite') };

    return defined $info ? Geo::DetailsLite->new( $info ) : undef;
}

sub _lookup_info {
    my ( $self, $ip ) = @_;

    # checking bogon IP and returning response locally.
    if ( $ip ne '' ) {
        if ( _is_bogon($ip) ) {
            my $details = {};
            $details->{ip}    = $ip;
            $details->{bogon} = "True";
            return ( $details, '' );
        }
    }

    my $cached_info = $self->_lookup_info_from_cache($ip);

    if ( defined $cached_info ) {
        return ( $cached_info, '' );
    }
    my ( $source_info, $message ) = $self->_lookup_info_from_source($ip);
    if ( not defined $source_info ) {
        return ( $source_info, $message );
    }

    if ( ref($source_info) eq '' ) {
        return ( $source_info, $message );
    }

    my $country_code = $source_info->{country_code};
    if ( defined $country_code ) {
        $source_info->{country_name} = $self->{countries}->{country_code};
        $source_info->{country_flag} = $self->{countries_flags}->{country_code};
        $source_info->{country_flag_url} =
          $country_flag_url . $country_code . ".svg";
        $source_info->{country_currency} =
          $self->{countries_currencies}->{country_code};
        $source_info->{continent} = $self->{continents}->{country_code};
        if ( grep { $_ eq $country_code } @{ $self->{eu_countries} } ) {
            $source_info->{is_eu} = "True";
        }
        else {
            $source_info->{is_eu} = undef;
        }
    }

    if ( defined $source_info->{'loc'} ) {
        my ( $lat, $lon ) = split /,/, $source_info->{loc};
        $source_info->{latitude}  = $lat;
        $source_info->{longitude} = $lon;
    }

    $source_info->{meta} = { time => time(), from_cache => 0 };
    $self->{cache}->set( $ip, $source_info );

    return ( $source_info, $message );
}

sub _lookup_info_from_cache {
    my ( $self, $cache_key ) = @_;

    my $cached_info = $self->{cache}->get($cache_key);
    if ( defined $cached_info ) {
        my $timedelta = time() - $cached_info->{meta}->{time};
        if ( $timedelta <= $cache_ttl || $custom_cache == 1 ) {
            $cached_info->{meta}->{from_cache} = 1;

            return $cached_info;
        }
    }

    return;
}

sub _lookup_info_from_source {
    my ( $self, $ip ) = @_;

    my $url = '';
    if ( $ip ) {
        $url = $self->{base_url} . $ip;
    } else {
        $url = $self->{base_url} . "me";
    }

    my $response = $self->{ua}->get($url);

    if ( $response->is_success ) {

        my $content_type = $response->header('Content-Type') || '';
        my $info;

        if ( $content_type =~ m{application/json}i ) {
            eval { $info = from_json( $response->decoded_content ); };
            if ($@) {
                return ( undef, 'Error parsing JSON response.' );
            }
        }
        else {
            $info = $response->decoded_content;
            chomp($info);
        }

        return ( $info, '' );
    }
    if ( $response->code == HTTP_TOO_MANY_REQUEST ) {
        return ( undef, 'Your monthly request quota has been exceeded.' );
    }

    return ( undef, $response->status_line );
}

sub _build_cache {
    my ( $pkg, %options ) = @_;

    if ( defined $options{cache} ) {
        $custom_cache = 1;

        return $options{cache};
    }

    $cache_ttl = DEFAULT_CACHE_TTL;
    if ( defined $options{cache_ttl} ) {
        $cache_ttl = $options{cache_ttl};
    }

    return Cache::LRU->new(
        size => defined $options{cache_max_size}
        ? $options{cache_max_size}
        : DEFAULT_CACHE_MAX_SIZE
    );
}

# Lists of bogon CIDRs.
my @ip4_bogon_networks = (
    "0.0.0.0/8",       "10.0.0.0/8",
    "100.64.0.0/10",   "127.0.0.0/8",
    "169.254.0.0/16",  "172.16.0.0/12",
    "192.0.0.0/24",    "192.0.2.0/24",
    "192.168.0.0/16",  "198.18.0.0/15",
    "198.51.100.0/24", "203.0.113.0/24",
    "224.0.0.0/4",     "240.0.0.0/4",
    "255.255.255.255/32"
);

my @ip6_bogon_networks = (
    "0:0:0:0:0:0:0:0/128",   "0:0:0:0:0:0:0:1/128",
    "0:0:0:0:0:ffff:0:0/96", "0:0:0:0:0:0:0:0/96",
    "100::/64",              "2001:10::/28",
    "2001:db8::/32",         "fc00::/7",
    "fe80::/10",             "fec0::/10",
    "ff00::/8",              "2002::/24",
    "2002:a00::/24",         "2002:7f00::/24",
    "2002:a9fe::/32",        "2002:ac10::/28",
    "2002:c000::/40",        "2002:c000:200::/40",
    "2002:c0a8::/32",        "2002:c612::/31",
    "2002:c633:6400::/40",   "2002:cb00:7100::/40",
    "2002:e000::/20",        "2002:f000::/20",
    "2002:ffff:ffff::/48",   "2001::/40",
    "2001:0:a00::/40",       "2001:0:7f00::/40",
    "2001:0:a9fe::/48",      "2001:0:ac10::/44",
    "2001:0:c000::/56",      "2001:0:c000:200::/56",
    "2001:0:c0a8::/48",      "2001:0:c612::/47",
    "2001:0:c633:6400::/56", "2001:0:cb00:7100::/56",
    "2001:0:e000::/36",      "2001:0:f000::/36",
    "2001:0:ffff:ffff::/64"
);

# Check if an IP address is a bogon.
sub _is_bogon {
    my $ip = shift;

    my $ip_is_bogon = 0;

    if ( $ip =~ /:/ ) {    # IPv6 address
        my $ip6_bogon_cidr_set = Net::CIDR::Set->new();
        $ip6_bogon_cidr_set->add($_) foreach (@ip6_bogon_networks);
        $ip_is_bogon = $ip6_bogon_cidr_set->contains($ip);
    }
    else {                 # IPv4 address
        my $ip4_bogon_cidr_set = Net::CIDR::Set->new();
        $ip4_bogon_cidr_set->add($_) foreach (@ip4_bogon_networks);
        $ip_is_bogon = $ip4_bogon_cidr_set->contains($ip);
    }

    return $ip_is_bogon;
}


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.