@tastekim/chat-cli
Version:
๐ฌConnect with developers worldwide through an interactive terminal chat experience while you code!๐ป
165 lines โข 5.67 kB
JavaScript
import https from 'https';
export class LocationDetector {
/**
* ์ฌ์ฉ์์ ์ง์ญ ์ ๋ณด๋ฅผ ๊ฐ์งํฉ๋๋ค.
* IP ๊ธฐ๋ฐ ๊ฐ์ง๋ฅผ ์ฐ์ ์๋ํ๊ณ , ์คํจ ์ ๋ก์ผ์ผ ๊ธฐ๋ฐ์ผ๋ก fallbackํฉ๋๋ค.
*/
static async detectLocation() {
try {
// 1์ฐจ: IP ๊ธฐ๋ฐ ๊ฐ์ง (๋ ์ ํ)
const ipLocation = await this.getLocationFromIP();
return {
country: ipLocation.country,
countryCode: ipLocation.country_code,
source: 'ip'
};
}
catch (error) {
// 2์ฐจ: ๋ก์ผ์ผ ๊ธฐ๋ฐ ๊ฐ์ง (์คํ๋ผ์ธ ๊ฐ๋ฅ)
return this.getLocationFromLocale();
}
}
/**
* IP ๊ธฐ๋ฐ์ผ๋ก ์ง์ญ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
*/
static async getLocationFromIP() {
return new Promise((resolve, reject) => {
const request = https.get('https://ipapi.co/json/', {
timeout: this.TIMEOUT_MS,
headers: {
'User-Agent': 'chat-cli-location-detector'
}
}, (response) => {
let data = '';
response.on('data', (chunk) => {
data += chunk;
});
response.on('end', () => {
try {
const locationData = JSON.parse(data);
if (locationData.country_code && locationData.country_name) {
resolve({
country: locationData.country_name,
country_code: locationData.country_code
});
}
else {
reject(new Error('Invalid response format'));
}
}
catch (error) {
reject(new Error('Failed to parse location response'));
}
});
});
request.on('error', (error) => {
reject(error);
});
request.on('timeout', () => {
request.destroy();
reject(new Error('Request timeout'));
});
});
}
/**
* ์์คํ
๋ก์ผ์ผ ๊ธฐ๋ฐ์ผ๋ก ์ง์ญ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
*/
static getLocationFromLocale() {
try {
// Intl API๋ฅผ ์ฌ์ฉํ ๋ก์ผ์ผ ๊ฐ์ง
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
const intlLocale = new Intl.Locale(locale);
if (intlLocale.region) {
return {
country: this.getCountryNameFromCode(intlLocale.region),
countryCode: intlLocale.region,
source: 'locale'
};
}
}
catch (error) {
// Intl API ์คํจ ์ ํ๊ฒฝ๋ณ์ ์ฌ์ฉ
}
// ํ๊ฒฝ๋ณ์์์ ๋ก์ผ์ผ ์ถ์ถ
const envLocale = process.env.LANG || process.env.LC_ALL || 'en_US.UTF-8';
const countryCode = envLocale.split('_')[1]?.split('.')[0] || 'US';
return {
country: this.getCountryNameFromCode(countryCode),
countryCode,
source: 'locale'
};
}
/**
* ๊ตญ๊ฐ ์ฝ๋๋ฅผ ๊ตญ๊ฐ๋ช
์ผ๋ก ๋ณํํฉ๋๋ค.
*/
static getCountryNameFromCode(countryCode) {
const countryMap = {
'US': 'United States',
'KR': 'South Korea',
'JP': 'Japan',
'CN': 'China',
'GB': 'United Kingdom',
'DE': 'Germany',
'FR': 'France',
'CA': 'Canada',
'AU': 'Australia',
'IN': 'India',
'BR': 'Brazil',
'RU': 'Russia',
'IT': 'Italy',
'ES': 'Spain',
'NL': 'Netherlands',
'SE': 'Sweden',
'NO': 'Norway',
'DK': 'Denmark',
'FI': 'Finland',
'PL': 'Poland',
'CZ': 'Czech Republic',
'AT': 'Austria',
'CH': 'Switzerland',
'BE': 'Belgium',
'PT': 'Portugal',
'IE': 'Ireland',
'IL': 'Israel',
'TR': 'Turkey',
'ZA': 'South Africa',
'EG': 'Egypt',
'NG': 'Nigeria',
'KE': 'Kenya',
'MA': 'Morocco',
'TH': 'Thailand',
'VN': 'Vietnam',
'SG': 'Singapore',
'MY': 'Malaysia',
'ID': 'Indonesia',
'PH': 'Philippines',
'TW': 'Taiwan',
'HK': 'Hong Kong',
'MX': 'Mexico',
'AR': 'Argentina',
'CL': 'Chile',
'CO': 'Colombia',
'PE': 'Peru',
'VE': 'Venezuela'
};
return countryMap[countryCode.toUpperCase()] || countryCode;
}
/**
* ๊ตญ๊ฐ ์ฝ๋๋ฅผ ์ด๋ชจ์ง ํ๋๊ทธ๋ก ๋ณํํฉ๋๋ค.
*/
static getCountryFlag(countryCode) {
if (!countryCode || countryCode.length !== 2)
return '๐';
const codePoints = countryCode.toUpperCase().split('').map(char => 127397 + char.charCodeAt(0));
return String.fromCodePoint(...codePoints);
}
/**
* ์ง์ญ ์ ๋ณด๋ฅผ ์ฝ๊ธฐ ์ฌ์ด ํํ๋ก ํฌ๋งทํฉ๋๋ค.
*/
static formatLocation(locationInfo) {
const flag = this.getCountryFlag(locationInfo.countryCode);
return `${flag}${locationInfo.countryCode}`;
}
}
LocationDetector.TIMEOUT_MS = 3000;
//# sourceMappingURL=location-detector.js.map