UNPKG

geoshell

Version:

A CLI to fetch real-time geo-data from your terminal

162 lines (138 loc) 4.63 kB
/** * Weather command implementation */ const api = require('../utils/api'); const { LocationNotFound } = require('../utils/errors'); /** * Get weather information * * @param {string} location - City name or coordinates * @param {Object} options - Options * @param {number} options.forecast - Number of forecast days (0-7) * @returns {Promise<Object>} Weather information */ async function weather(location, options = {}) { // Check if we have an API key const apiKey = process.env.WEATHER_API_KEY; if (!apiKey) { // Return mock data if no API key return getMockWeather(location, options); } try { const url = `${api.BASE_URLS.weather}/weather`; const params = { q: location, appid: apiKey, units: 'metric' }; const data = await api.makeRequest(url, { params }); if (!data) { throw new LocationNotFound(`Location '${location}' not found`); } const weatherInfo = { location: data.name, temperature: data.main.temp, condition: data.weather[0].description.charAt(0).toUpperCase() + data.weather[0].description.slice(1), humidity: data.main.humidity, windSpeed: data.wind.speed, pressure: data.main.pressure, visibility: data.visibility / 1000, // Convert to km uvIndex: 0 // Would need separate UV API call }; // Add forecast if requested if (options.forecast && options.forecast > 0) { const forecastUrl = `${api.BASE_URLS.weather}/forecast`; const forecastData = await api.makeRequest(forecastUrl, { params }); if (forecastData && forecastData.list) { weatherInfo.forecast = processForecast(forecastData.list, options.forecast); } } return weatherInfo; } catch (error) { if (error instanceof LocationNotFound) { throw error; } // Return mock data if API fails return getMockWeather(location, options); } } /** * Process forecast data * * @param {Array} forecastList - List of forecast data points * @param {number} days - Number of days to include * @returns {Array} Processed forecast */ function processForecast(forecastList, days) { const forecast = []; const daysMap = {}; // Group by day forecastList.forEach(item => { const date = item.dt_txt.split(' ')[0]; if (!daysMap[date]) { daysMap[date] = { date, temps: [], conditions: [] }; } daysMap[date].temps.push(item.main.temp); daysMap[date].conditions.push(item.weather[0].main); }); // Process each day Object.values(daysMap).slice(0, days).forEach(day => { forecast.push({ date: day.date, high: Math.max(...day.temps), low: Math.min(...day.temps), condition: getMostFrequent(day.conditions) }); }); return forecast; } /** * Get most frequent item in array * * @param {Array} arr - Array of items * @returns {*} Most frequent item */ function getMostFrequent(arr) { return arr.sort((a, b) => arr.filter(v => v === a).length - arr.filter(v => v === b).length ).pop(); } /** * Get mock weather data * * @param {string} location - Location name * @param {Object} options - Options * @returns {Object} Mock weather data */ function getMockWeather(location, options = {}) { const mockWeather = { location: location, temperature: Math.round(Math.random() * 30 + 5), // 5-35°C condition: ['Sunny', 'Cloudy', 'Partly Cloudy', 'Rainy', 'Clear'][Math.floor(Math.random() * 5)], humidity: Math.round(Math.random() * 40 + 40), // 40-80% windSpeed: Math.round(Math.random() * 20 + 5), // 5-25 km/h pressure: Math.round(Math.random() * 50 + 1000), // 1000-1050 hPa visibility: Math.round(Math.random() * 10 + 5), // 5-15 km uvIndex: Math.round(Math.random() * 10) // 0-10 }; if (options.forecast && options.forecast > 0) { const forecast = []; for (let i = 1; i <= Math.min(options.forecast, 7); i++) { const date = new Date(); date.setDate(date.getDate() + i); forecast.push({ date: date.toISOString().split('T')[0], high: Math.round(Math.random() * 30 + 10), low: Math.round(Math.random() * 15), condition: ['Sunny', 'Cloudy', 'Partly Cloudy', 'Rainy', 'Clear'][Math.floor(Math.random() * 5)] }); } mockWeather.forecast = forecast; } return mockWeather; } module.exports = weather;