@adamlui/geolocate
Version:
Fetch IP geolocation data from the CLI.
6 lines • 3.58 kB
JavaScript
/**
* © 2024–2026 Adam Lui & contributors under the MIT license.
* Source: https://github.com/adamlui/js-utils/tree/main/geolocate/src
* Documentation: https://github.com/adamlui/js-utils/tree/main/geolocate/docs
*/
async function geolocate(e,n={}){var o,t={verbose:!0};_log.prefix="geolocate()",(e=[].concat(e))[0]||=await getOwnIP();for(o of e){n.verbose&&_log.info(`Validating ${o}...`);let t;try{t=require("generate-ip").ipv4.validate}catch(e){await import("https://cdn.jsdelivr.net/npm/generate-ip/dist/generate-ip.min.js"),t=window.ipv4.validate}if(t&&!t(o,{verbose:!1}))return _log.error(o+" is not a valid IPv4 address.")}if(_validateOptions({options:n,defaultOptions:t,helpURL:"https://github.com/adamlui/js-utils/tree/main/geolocate/docs/#locateips-options",exampleCall:"geolocate('8.8.8.8', { verbose: false })"})){n={...t,...n};try{var s,l=[];for(s of e){n.verbose&&_log.info(`Fetching geolocation data for ${s}...`);let e=await fetchData("http://ip-api.com/json/"+s),{status:t,org:o,as:r,query:i,...a}=await e.json();l.push({ip:s,...a})}return n.verbose&&"undefined"!=typeof window&&_log.info("Success!","Check returned array."),l}catch(e){_log.error(e.message)}}}function fetchData(r){return"undefined"==typeof fetch?new Promise((o,t)=>{try{var e=r.match(/^([^:]+):\/\//)[1];/^https?$/.test(e)||t(new Error("Invalid fetchData() URL.")),require(e).get(r,e=>{let t="";e.on("data",e=>t+=e),e.on("end",()=>o({json:()=>JSON.parse(t),text:()=>t}))}).on("error",e=>t(new Error(e.message)))}catch(e){t(new Error("Environment not supported."))}}):fetch(r)}async function getOwnIP(){return fetchData("https://ifconfig.me/ip").then(e=>e.text()).catch(()=>fetchData("http://ip-api.com/json/").then(e=>e.json()).then(e=>e.query)).catch(async()=>{try{var e=require("child_process").exec,t=require("util").promisify,{stdout:o,stderr:r}=await t(e)("curl -s ifconfig.me");return r?_log.error(r):o.trim()}catch(e){_log.error(e.message)}})}function _validateOptions({options:e,defaultOptions:t,helpURL:o,exampleCall:r}){var i,a,n=Object.keys(t).filter(e=>"boolean"==typeof t[e]),s=Object.keys(t).filter(e=>Number.isInteger(t[e]));if("object"!=typeof e)return i=r.split(",").findIndex(e=>e.trim().startsWith("{"))+1,i+=["st","nd","rd"][i-1]||"th",_log.error(`${"0th"==i?"[O":i+" arg [o"}ptions] can only be an object of key/vals.`),_log.info("Example valid call:",r),_log.validOptions(t),_log.helpURL(o),!1;for(a in e){if(!Object.prototype.hasOwnProperty.call(t,a))return _log.error(`\`${a}\` is an invalid option.`),_log.validOptions(t),_log.helpURL(o),!1;if(n.includes(a)&&"boolean"!=typeof e[a])return _log.error(`[${a}] option can only be \`true\` or \`false\`.`),_log.helpURL(o),!1;if(s.includes(a)&&(e[a]=parseInt(e[a],10),isNaN(e[a])||e[a]<1))return _log.error(`[${a}] option can only be an integer > 0.`),_log.helpURL(o),!1}return!0}Object.assign(globalThis.api??={},{name:"geolocate",regex:{geolocate:/^(?:geo)?locate$/i}});let _log={prefix:api.name,error(...e){console.error(this.prefix+" » ERROR:",...e)},helpURL(e=api.urls?.docs){this.info("For more help, please visit",e)},info(...e){console.info(this.prefix+" »",...e)},validOptions(e){var t=Object.keys(e).join(", "),e=JSON.stringify(e,null,2).replace(/"([^"]+)":/g,"$1:").replace(/"/g,"'").replace(/\n\s*/g," ");this.info(`Valid options: [${t}]`),this.info("If omitted, default settings are: "+e)}};api.exports=new Proxy({geolocate:geolocate},{get(e,t){for(var[o,r]of Object.entries(api.regex))if(r.test(t))return e[o]}});try{module.exports=api.exports}catch(e){}try{Object.assign(window,api.exports)}catch(e){}