Compare commits
3 Commits
19e0a368f7
...
deaafa530a
| Author | SHA1 | Date | |
|---|---|---|---|
|
deaafa530a
|
|||
|
af4243604b
|
|||
|
11a1b56514
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules
|
||||
*-metars.json
|
||||
data/
|
||||
@@ -3,9 +3,10 @@ This node app generates rss XML files that you can then serve as an RSS feed.
|
||||
|
||||
## Install and Setup (Linux)
|
||||
```sh
|
||||
# Clone the repo
|
||||
# Clone the repo and install modules
|
||||
sudo mkdir -p /opt
|
||||
cd /opt && sudo git clone https://git.entropic.pro/Aiden/metar-rss
|
||||
npm i
|
||||
|
||||
# Edit config, see below for details
|
||||
sudo nano /opt/metar-rss/config.json
|
||||
|
||||
127
index.js
127
index.js
@@ -3,11 +3,19 @@ import axios from "axios";
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const generatorURL = "https://git.entropic.pro/Aiden/metar-rss";
|
||||
const API_URL = "https://aviationweather.gov/api/data/";
|
||||
const METAR_SERVICE = "metar";
|
||||
const generatorURL = "https://git.entropic.pro/Aiden/metar-rss";
|
||||
const TAF_SERVICE = "taf";
|
||||
const SERVICES = [METAR_SERVICE, TAF_SERVICE];
|
||||
const SERVICE_FUNC = [newMetar, newTAF];
|
||||
|
||||
let defaultConfig = {
|
||||
const DATA_DIR = "data";
|
||||
if (!fs.existsSync(DATA_DIR)) {
|
||||
fs.mkdirSync(DATA_DIR);
|
||||
}
|
||||
|
||||
const defaultConfig = {
|
||||
"outputdir": "/var/www/weather",
|
||||
"icaos": ["KJFK"],
|
||||
"siteurl": "https://example.com",
|
||||
@@ -16,7 +24,7 @@ let defaultConfig = {
|
||||
"loglevel": 4
|
||||
};
|
||||
/** Load config file */
|
||||
const configFile = "./config.json";
|
||||
const configFile = "config.json";
|
||||
let configData;
|
||||
try {
|
||||
configData = JSON.parse(fs.readFileSync(configFile));
|
||||
@@ -75,69 +83,114 @@ const checkInterval = Number(configData["check-interval"]) * 1000 * 60;
|
||||
|
||||
|
||||
/** Start Service **/
|
||||
checkMetar(); //Inital check run
|
||||
const metarCheckJob = setInterval(checkMetar, checkInterval); //Run the check every interval
|
||||
checkAPI(); //Inital check run
|
||||
const metarCheckJob = setInterval(checkAPI, checkInterval); //Run the check every interval
|
||||
|
||||
/**
|
||||
* Pulls the current metar and adds it to the feed if it is new
|
||||
* Pulls the current METAR and TAF and adds it to the feed if it is new
|
||||
*/
|
||||
function checkMetar() {
|
||||
function checkAPI() {
|
||||
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}));
|
||||
for (let s = 0; s < SERVICES.length; s++) {
|
||||
let service = SERVICES[s];
|
||||
if (loglevel >= 7) console.log("Checking " + service.toUpperCase() + " for " + icao);
|
||||
|
||||
let items = [];
|
||||
for (let i = 0; i < metars.length; i++) {
|
||||
if (metars[i] != undefined)
|
||||
items.push(newMetar(metars[i]));
|
||||
getData(icao, service).then(res => {
|
||||
let dataFile = path.join(DATA_DIR, icao+"-"+service+".json");
|
||||
let data = new Array();
|
||||
if (fs.existsSync(dataFile))
|
||||
data = JSON.parse(fs.readFileSync(dataFile))[service];
|
||||
|
||||
//If the recived data is new
|
||||
if (data === undefined || res.data != data[0]) {
|
||||
if (loglevel >= 6) console.log("New " + service.toUpperCase() + " for " + icao);
|
||||
|
||||
if (data === undefined) data = [res.data];
|
||||
else data.unshift(res.data);
|
||||
|
||||
// Keep a max of 20 entries
|
||||
if (data.length > 20) data.pop();
|
||||
fs.writeFileSync(dataFile, JSON.stringify({[service]: data}));
|
||||
|
||||
let items = [];
|
||||
for (let j = 0; j < data.length; j++) {
|
||||
if (data[j] != undefined || data[j] != null) {
|
||||
items.push(SERVICE_FUNC[s](data[j]));
|
||||
}
|
||||
}
|
||||
|
||||
let outputPath = path.join(service, icao);
|
||||
writeFeed(generateFeed(service, icao, items, outputPath), outputPath);
|
||||
}
|
||||
|
||||
let outputPath = path.join(METAR_SERVICE, icao);
|
||||
writeFeed(generateFeed(METAR_SERVICE, icao, items, outputPath), outputPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new METAR to the given RSS Feed
|
||||
* @param {RSS} rss - RSS Feed to add the entry to
|
||||
* @param {String} metar - metar data to add
|
||||
* Generates METAR RSS Item
|
||||
* @param {String} metar - metar text/data to add
|
||||
* @returns {Object} - Object of RSS Item Data
|
||||
*/
|
||||
function newMetar(metar) {
|
||||
let metarSplit = metar.split(" ");
|
||||
let metarTime = metarSplit[2]; // time/date section of metar
|
||||
|
||||
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))));
|
||||
let eventDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(), Number(metarTime.substring(0,2)), Number(metarTime.substring(2,4)), Number(metarTime.substring(4,6))));
|
||||
|
||||
let item = {
|
||||
title: metarSplit[0] + " " + metarSplit[1] + " " + metarSplit[2],
|
||||
description: metar,
|
||||
url: API_URL + METAR_SERVICE,
|
||||
url: API_URL + METAR_SERVICE + "?ids=" + metarSplit[1],
|
||||
guid: metarSplit[1] + metarSplit[2],
|
||||
date: eventDate
|
||||
};
|
||||
return item
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates TAF RSS Item
|
||||
* @param {String} taf - taf text/data to add
|
||||
* @returns {Object} - Object of RSS Item Data
|
||||
*/
|
||||
function newTAF(taf) {
|
||||
let tafSplit = taf.split(" ");
|
||||
let tafTime = tafSplit[((tafSplit[1] == "AMD") ? 3 : 2)]; // index of the time/date section is pushed if TAF is amended
|
||||
|
||||
let date = new Date(Date.now());
|
||||
let eventDate = new Date(Date.UTC(date.getFullYear(), date.getMonth(),Number(tafTime.substring(0,2)), Number(tafTime.substring(2,4)), Number(tafTime.substring(4,6))));
|
||||
|
||||
let title;
|
||||
let id;
|
||||
if (tafSplit[1] == "AMD") {
|
||||
title = tafSplit[0] + " " + tafSplit[1] + " " + tafSplit[2] + " " + tafSplit[3];
|
||||
id = tafSplit[1] + tafSplit[2] + tafSplit[3];
|
||||
} else {
|
||||
title = tafSplit[0] + " " + tafSplit[1] + " " + tafSplit[2];
|
||||
id = tafSplit[1] + tafSplit[2];
|
||||
}
|
||||
|
||||
let item = {
|
||||
title: title,
|
||||
description: taf,
|
||||
url: API_URL + TAF_SERVICE + "?ids=" + ((tafSplit[1] == "AMD") ? tafSplit[2] : tafSplit[1]),
|
||||
guid: id,
|
||||
date: eventDate
|
||||
};
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls METAR data from the API
|
||||
* @param {String} icao ICAO Airport Code
|
||||
* @param {String} service service to fetch (eg. "metar" or "taf")
|
||||
* @returns Axios generated GET Request Response
|
||||
*/
|
||||
function getMetar(icao) {
|
||||
let requestURL = API_URL + METAR_SERVICE + "?ids=" + icao;
|
||||
function getData(icao, service) {
|
||||
let requestURL = API_URL + service + "?ids=" + icao;
|
||||
return axios.get(requestURL);
|
||||
}
|
||||
|
||||
@@ -154,8 +207,8 @@ function generateFeed(service, icao, items, outputPath) {
|
||||
feedURL.pathname = path.join(feedURL.pathname, outputPath, "rss.xml");
|
||||
|
||||
let feedOptions = {
|
||||
title: icao + " " + service,
|
||||
description: service + " feed for " + icao + " Airport",
|
||||
title: icao + " " + service.toUpperCase(),
|
||||
description: service.toUpperCase() + " feed for " + icao + " Airport",
|
||||
feed_url: feedURL,
|
||||
site_url: siteURL,
|
||||
generator: generatorURL
|
||||
|
||||
10
package-lock.json
generated
10
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "metar-rss",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "metar-rss",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"axios": "^1.15.0",
|
||||
@@ -124,9 +124,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
||||
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "metar-rss",
|
||||
"version": "1.0.1",
|
||||
"description": "Monitors, generates and updates an RSS Feed File for Airport METARs",
|
||||
"version": "1.1.0",
|
||||
"description": "Monitors, generates and updates an RSS Feed File for Airport METARs and TAFs",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://git.entropic.pro/Aiden/metar-rss"
|
||||
|
||||
Reference in New Issue
Block a user