UNPKG

snow-forecast-sfr

Version:

An NPM module that scrapes snow-forecast for the relevant resort and returns its information.

181 lines (180 loc) 8.76 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const bent_1 = __importDefault(require("bent")); const cheerio_1 = __importDefault(require("cheerio")); const TimeUtil_1 = __importDefault(require("./utils/TimeUtil")); const UnitUtil_1 = __importDefault(require("./utils/UnitUtil")); const SnowRequest = function () { const coreURL = 'https://www.snow-forecast.com/resorts/'; const MAX_CELLS = 18; let unitsInMetric = true; function pMakeRequest(url, cb, opts) { return __awaiter(this, void 0, void 0, function* () { try { const requestOpts = (opts === null || opts === void 0 ? void 0 : opts.proxyOpts) || {}; const stringStream = bent_1.default('string', requestOpts); const response = yield stringStream(url); unitsInMetric = !opts || typeof opts.inMetric === 'undefined' ? true : opts.inMetric; try { if (response.length < 1500 && response.indexOf("The page you were looking for doesn't exist") > -1) { return cb([ 'Invalid page(404)', 'Unable to find relevant resort forecast, ' + 'please check the spelling and try again', url, ]); } const $ = cheerio_1.default.load(response); cb($); } catch (ex) { cb(['Parse error', ex, url]); } } catch (error) { return cb(['Remote server error', 'Unable to get response from snow-forecast' + error, url]); } }); } function pBuildErrorJSON(error, message, url) { return { error, message, url, }; } function pBuildForecast($, forecastOpt, cb) { const firstTime = $($('.forecast-table-time__period')[0]).text(); const snowForecast = $('span.snow'); const rainForecast = $('span.rain'); const freezingLevel = $('span.heightfl'); const winds = $('table tr[data-row="wind"] .forecast-table-wind__container svg text'); const windChillTempContainer = $('table tr[data-row="temperature-chill"]'); const maxTempContainer = $('table tr[data-row="temperature-max"]'); const minTempContainer = $('table tr[data-row="temperature-min"]'); const maxTemp = $(maxTempContainer).find('span.temp'); const minTemp = $(minTempContainer).find('span.temp'); const windChill = $(windChillTempContainer).find('span.temp'); const summary = $('table tr[data-row="phrases"] span'); const forecastObj = { name: forecastOpt.resort, url: forecastOpt.url, issuedDate: forecastOpt.issuedDate, elevation: forecastOpt.elevation, units: unitsInMetric ? 'metric' : 'imperial', forecast: [], }; const forecastArr = []; for (let i = 0; i < MAX_CELLS; i++) { let cellObj = { date: TimeUtil_1.default.getDay(forecastOpt.lastUpdateDate, TimeUtil_1.default.getTimeOffset(firstTime), i), time: TimeUtil_1.default.getTime(TimeUtil_1.default.getTimeOffset(firstTime), forecastOpt.startDay, i), summary: $(summary[i]).text(), wind: parseInt($(winds[i]).text(), 10), windDirection: pGetWindDirection($, winds[i]), snow: parseInt($(snowForecast[i]).text(), 10) || 0, rain: parseInt($(rainForecast[i]).text(), 10) || 0, freezingLevel: parseInt($(freezingLevel[i]).text(), 10), minTemp: parseInt($(minTemp[i]).text(), 10), maxTemp: parseInt($(maxTemp[i]).text(), 10), windChill: parseInt($(windChill[i]).text(), 10), }; if (forecastOpt.isMetric !== unitsInMetric) { cellObj = pConvertUnits(cellObj, unitsInMetric); } forecastArr.push(cellObj); } forecastObj.forecast = forecastArr; return cb(forecastObj); } function pGetWindDirection($, cell) { const img = $(cell).find('img'); const MAX_DESC_PARTS = 2; if (!img || img.length === 0) { return ''; } const imageCellAlt = $(img[0]).attr('alt'); if (imageCellAlt && imageCellAlt.split(' ').length === MAX_DESC_PARTS) { return imageCellAlt.split(' ')[1]; } return ''; } function pConvertUnits(obj, toMetric) { if (toMetric) { obj.wind = UnitUtil_1.default.speedToMetric(obj.wind); obj.snow = UnitUtil_1.default.volumeToMetric(obj.snow) || 0; obj.rain = UnitUtil_1.default.volumeToMetric(obj.rain / 10) || 0; obj.freezingLevel = UnitUtil_1.default.distanceToMetric(obj.freezingLevel); obj.minTemp = UnitUtil_1.default.temperatureToMetric(obj.minTemp); obj.maxTemp = UnitUtil_1.default.temperatureToMetric(obj.maxTemp); } else { obj.wind = UnitUtil_1.default.speedToImperial(obj.wind); obj.snow = UnitUtil_1.default.volumeToImperial(obj.snow) || 0; obj.rain = UnitUtil_1.default.volumeToImperial(obj.rain / 10) || 0; obj.freezingLevel = UnitUtil_1.default.distanceToImperial(obj.freezingLevel); obj.minTemp = UnitUtil_1.default.temperatureToImperial(obj.minTemp); obj.maxTemp = UnitUtil_1.default.temperatureToImperial(obj.maxTemp); } return obj; } const parseResort = function (resort, elevation, cb, opts) { if (arguments.length < 3) { return pBuildErrorJSON('Insufficient parameters', 'Please pass the resort, elevation, callback and if you wish the options object into the method', ''); } let url = coreURL + resort + '/6day/' + elevation; if (opts === null || opts === void 0 ? void 0 : opts.proxyUrl) { url = `${opts.proxyUrl}${url}`; } pMakeRequest(url, function ($) { if ($ instanceof Array) { cb(pBuildErrorJSON($[0], $[1], $[2])); return; } const isMetric = $('.deg-c input').attr('checked') === 'checked'; const issuedDate = TimeUtil_1.default.fixIssueDateFormat($($('.location-issued__no-wrap')[5]).text() + $($('.location-issued__no-wrap')[6]).text()); const firstTime = $($('.forecast-table-time__period')[0]).text(); let startDay = $($('.forecast-table-days__name')[0]).text(); const lastUpdateDate = $($('.location-issued__no-wrap')[6]).text(); if (firstTime === 'night') { startDay = TimeUtil_1.default.getPrevDay(startDay); } const forecastRequest = { resort, elevation, url, issuedDate, lastUpdateDate, startDay, isMetric, }; const match = issuedDate.match(/^\d+/); const time = []; const timeIndex = issuedDate.indexOf(match[0]) + match[0].length; time.push(issuedDate.substr(issuedDate.indexOf(match[0]), match[0].length)); time.push(issuedDate.substr(timeIndex, timeIndex + 2)); time.push(issuedDate.substr(timeIndex + 3).split(/[\s]+/)); pBuildForecast($, forecastRequest, function (obj) { if (!obj) { return cb(pBuildErrorJSON('JSON Construction Error', 'Internal error occurred, please try again', url)); } return cb(obj); }); }, opts); }; return { parseResort, }; }; exports.default = SnowRequest;