Added config file import and checking and reworked to support multiple airports
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
node_modules
|
||||
*-metars.json
|
||||
8
config.json
Normal file
8
config.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"outputdir": "/var/www/weather",
|
||||
"icaos": ["CYWG", "KJFK"],
|
||||
"siteurl": "https://example.com",
|
||||
"rss-baseurl": "https://example.com/weather",
|
||||
"check-interval": 10,
|
||||
"loglevel": 4
|
||||
}
|
||||
165
index.js
165
index.js
@@ -1,24 +1,71 @@
|
||||
import RSS from "rss";
|
||||
import axios from "axios";
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const API_URL = "https://aviationweather.gov/api/data/";
|
||||
const METAR_SERVICE = "metar";
|
||||
const feedOutput = process.env.METAR_FEED_OUTPUT;
|
||||
const icao = process.env.METAR_ICAO;
|
||||
const rssMetarOptions = {
|
||||
title: icao + " METAR",
|
||||
description: "METAR feed for " + icao + " Airport",
|
||||
feed_url: process.env.METAR_FEED_URL,
|
||||
site_url: process.env.METAR_SITE_URL,
|
||||
generator: "https://git.entropic.pro/Aiden/metar-rss"
|
||||
const generatorURL = "https://git.entropic.pro/Aiden/metar-rss";
|
||||
|
||||
/** Load config file */
|
||||
const configFile = "./config.json";
|
||||
let configData;
|
||||
try {
|
||||
configData = JSON.parse(fs.readFileSync(configFile));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.warn("Unable to read config file, using defaults");
|
||||
}
|
||||
const checkInterval = 10 * 1000 * 60; //10 Minute Check Interval
|
||||
|
||||
/** Verify config data **/
|
||||
if (Number(configData["loglevel"]) == NaN) {
|
||||
console.warn("loglevel is not a number, using 4");
|
||||
configData["loglevel"] = 4;
|
||||
}
|
||||
const loglevel = Number(configData["loglevel"]);
|
||||
|
||||
try {
|
||||
if (!fs.lstatSync(configData["outputdir"]).isDirectory()) {
|
||||
if (loglevel >= 4) console.warn("outputdir is not a directory, using current folder");
|
||||
configData["outputdir"] = __dirname;
|
||||
}
|
||||
} catch (err) {
|
||||
if (loglevel >= 3) console.error(err);
|
||||
if (loglevel >= 4) console.warn("error finding outputdir, using current folder");
|
||||
configData["outputdir"] = __dirname;
|
||||
}
|
||||
const outputDir = configData["outputdir"]
|
||||
|
||||
if (!Array.isArray(configData["icaos"])) {
|
||||
if (loglevel >= 4) console.warn("icao config must be an array of ICAO codes, using default");
|
||||
configData["icaos"] = ["KJFK"];
|
||||
}
|
||||
const icaos = configData["icaos"];
|
||||
|
||||
try {
|
||||
new URL(configData["siteurl"]);
|
||||
} catch (err) {
|
||||
if (loglevel >= 4) console.warn("siteurl is not a valid URL, using example.com");
|
||||
configData["siteurl"] = "https://example.com"
|
||||
}
|
||||
const siteURL = new URL(configData["siteurl"]);
|
||||
|
||||
try {
|
||||
new URL(configData["rss-baseurl"]);
|
||||
} catch (err) {
|
||||
if (loglevel >= 4) console.warn("rss-baseurl is not a valid URL, using example.com");
|
||||
configData["rss-baseurl"] = "https://example.com"
|
||||
}
|
||||
const rssBaseURL = new URL(configData["rss-baseurl"]);
|
||||
|
||||
if (Number(configData["check-interval"]) == NaN) {
|
||||
if (loglevel >= 4) console.warn("check-interval is NaN, using 10 minutes");
|
||||
configData["check-interval"] = 10;
|
||||
}
|
||||
const checkInterval = Number(configData["check-interval"]) * 1000 * 60;
|
||||
|
||||
|
||||
let lastMetar;
|
||||
let metarRSS = new RSS(rssMetarOptions);
|
||||
|
||||
/** Start Service **/
|
||||
checkMetar(); //Inital check run
|
||||
const metarCheckJob = setInterval(checkMetar, checkInterval); //Run the check every interval
|
||||
|
||||
@@ -26,53 +73,109 @@ const metarCheckJob = setInterval(checkMetar, checkInterval); //Run the check ev
|
||||
* Pulls the current metar and adds it to the feed if it is new
|
||||
*/
|
||||
function checkMetar() {
|
||||
console.log("Checking METAR for " + icao);
|
||||
getMetar().then(res => {
|
||||
if (res.data != lastMetar) {
|
||||
console.log("New METAR: " + res.data);
|
||||
newMetar(metarRSS, res.data);
|
||||
writeFeed(metarRSS);
|
||||
lastMetar = res.data;
|
||||
}
|
||||
});
|
||||
for (let i = 0; i < icaos.length; i++) {
|
||||
let icao = icaos[i];
|
||||
if (loglevel >= 7) console.log("Checking METAR for " + icao);
|
||||
getMetar(icao).then(res => {
|
||||
let metarFile = icao+"-metars.json";
|
||||
let metars = [];
|
||||
if (fs.existsSync(metarFile))
|
||||
metars = JSON.parse(fs.readFileSync(icao+"-metars.json"))["data"];
|
||||
|
||||
if (res.data != metars[0]) {
|
||||
if (loglevel >= 6) console.log("New METAR: " + res.data);
|
||||
metars.unshift(res.data);
|
||||
if (metars.length > 20) metars.pop();
|
||||
fs.writeFileSync(metarFile, JSON.stringify({"data": metars}));
|
||||
|
||||
let items = [];
|
||||
for (let i = 0; i < metars.length; i++) {
|
||||
if (newMetar(metars[i]) != undefined)
|
||||
items.push(newMetar(metars[i]));
|
||||
}
|
||||
|
||||
let outputPath = METAR_SERVICE + "/" + icao;
|
||||
writeFeed(generateFeed(METAR_SERVICE, icao, items, outputPath), outputPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new METAR to the given RSS Feed
|
||||
* @param {*} rss - RSS Feed to add the entry to
|
||||
* @param {*} metar - metar data to add
|
||||
* @param {RSS} rss - RSS Feed to add the entry to
|
||||
* @param {String} metar - metar data to add
|
||||
* @returns {Object} - Object of RSS Item Data
|
||||
*/
|
||||
function newMetar(rss, metar) {
|
||||
function newMetar(metar) {
|
||||
let metarSplit = metar.split(" ");
|
||||
let date = new Date(Date.now());
|
||||
let eventDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), Number(metarSplit[2].substring(0,2)), Number(metarSplit[2].substring(2,4)), Number(metarSplit[2].substring(4,6))));
|
||||
|
||||
rss.item({
|
||||
let item = {
|
||||
title: metarSplit[0] + " " + metarSplit[1] + " " + metarSplit[2],
|
||||
description: metar,
|
||||
url: API_URL + METAR_SERVICE,
|
||||
guid: metarSplit[1] + metarSplit[2],
|
||||
date: eventDate
|
||||
});
|
||||
};
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls METAR data from the API
|
||||
* @param {String} icao ICAO Airport Code
|
||||
* @returns Axios generated GET Request Response
|
||||
*/
|
||||
function getMetar() {
|
||||
function getMetar(icao) {
|
||||
let requestURL = API_URL + METAR_SERVICE + "?ids=" + icao;
|
||||
return axios.get(requestURL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given RSS feed to the specified file
|
||||
* @param {*} rss - RSS Feed to Write
|
||||
* Generates and populates a feed for the given details
|
||||
* @param {String} service eg. "metar"
|
||||
* @param {String} icao ICAO Airpot Code
|
||||
* @param {Array} items Array of Objects containing RSS item details
|
||||
* @param {String} outputPath path for this Service + ICAO
|
||||
* @returns {RSS} RSS Feed with populated items
|
||||
*/
|
||||
function writeFeed(rss) {
|
||||
function generateFeed(service, icao, items, outputPath) {
|
||||
let feedURL = rssBaseURL;
|
||||
feedURL.pathname = feedURL.pathname.concat(outputPath);
|
||||
|
||||
let feedOptions = {
|
||||
title: icao + " " + service,
|
||||
description: service + " feed for " + icao + " Airport",
|
||||
feed_url: feedURL,
|
||||
site_url: siteURL,
|
||||
generator: generatorURL
|
||||
}
|
||||
let feed = new RSS(feedOptions);
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
feed.item(items[i]);
|
||||
}
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given RSS feed to the specified file
|
||||
* @param {RSS} rss - RSS Feed to Write
|
||||
* @param {String} outputPath path for this Service + ICAO
|
||||
*/
|
||||
function writeFeed(rss, outputPath) {
|
||||
let xmlFeed = rss.xml({indent: true})
|
||||
let fullPath = path.join(outputDir, outputPath);
|
||||
let fullPathFile = path.join(fullPath, "rss.xml");
|
||||
|
||||
if (!fs.existsSync(fullPath)){
|
||||
fs.mkdirSync(fullPath, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(feedOutput, xmlFeed);
|
||||
fs.writeFileSync(fullPathFile, xmlFeed);
|
||||
} catch(err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user