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
JavaScript
;
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;