107 lines
2.8 KiB
JavaScript
107 lines
2.8 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
const crypto = require("crypto");
|
||
|
|
const fs = require("fs");
|
||
|
|
const os = require("os");
|
||
|
|
const path = require("path");
|
||
|
|
|
||
|
|
const axios = require("axios");
|
||
|
|
const openingHours = require("opening_hours");
|
||
|
|
const yargs = require("yargs");
|
||
|
|
|
||
|
|
const CACHE_AGE = 12 * 60 * 60 * 1000;
|
||
|
|
|
||
|
|
const md5 = (text) => crypto.createHash("md5").update(text).digest("hex");
|
||
|
|
|
||
|
|
const getCachePath = (query) => {
|
||
|
|
const cacheDirectory =
|
||
|
|
process.env.CACHE_DIR ||
|
||
|
|
path.join(os.homedir(), ".cache", "osm-restaurants");
|
||
|
|
if (!fs.existsSync(cacheDirectory)) fs.mkdirSync(cacheDirectory);
|
||
|
|
return path.join(cacheDirectory, md5(query) + ".json");
|
||
|
|
};
|
||
|
|
|
||
|
|
const randomElement = (array) =>
|
||
|
|
array[Math.floor(array.length * Math.random())];
|
||
|
|
|
||
|
|
const argv = yargs
|
||
|
|
.option("checkOpen", {
|
||
|
|
alias: "o",
|
||
|
|
default: true,
|
||
|
|
type: "boolean",
|
||
|
|
description: "Only show restaurants that are open",
|
||
|
|
})
|
||
|
|
.option("radius", {
|
||
|
|
alias: "r",
|
||
|
|
description: "Radius around the location (metres)",
|
||
|
|
type: "number",
|
||
|
|
default: 500,
|
||
|
|
})
|
||
|
|
.option("center", {
|
||
|
|
alias: "c",
|
||
|
|
description: "The OSM ID to search around",
|
||
|
|
type: "number",
|
||
|
|
demandOption: true,
|
||
|
|
})
|
||
|
|
.option("endpoint", {
|
||
|
|
description: "The Overpass API endpoint",
|
||
|
|
type: "string",
|
||
|
|
default: "http://overpass-api.de/api/interpreter",
|
||
|
|
})
|
||
|
|
.option("country", {
|
||
|
|
description: "The country you are in (two letter code)",
|
||
|
|
type: "string",
|
||
|
|
default: "de",
|
||
|
|
})
|
||
|
|
.help()
|
||
|
|
.alias("help", "h").argv;
|
||
|
|
|
||
|
|
const overpassQuery = `
|
||
|
|
[out:json];
|
||
|
|
node(id:${argv.center})->.center;
|
||
|
|
(
|
||
|
|
node(around.center:'${argv.radius}')[amenity=fast_food];
|
||
|
|
node(around.center:'${argv.radius}')[amenity=restaurant];
|
||
|
|
);
|
||
|
|
out;
|
||
|
|
`;
|
||
|
|
|
||
|
|
const restaurantOpen = (restaurant) =>
|
||
|
|
"opening_hours" in restaurant.tags
|
||
|
|
? new openingHours(restaurant.tags.opening_hours, {
|
||
|
|
lat: restaurant.lat,
|
||
|
|
lon: restaurant.lon,
|
||
|
|
address: {
|
||
|
|
country_code:
|
||
|
|
"addr:country" in restaurant.tags
|
||
|
|
? restaurant.tags["addr:country"].toLowerCase()
|
||
|
|
: argv.country,
|
||
|
|
},
|
||
|
|
}).getState()
|
||
|
|
: true;
|
||
|
|
|
||
|
|
const withRestaurants = (callback) => {
|
||
|
|
const cachePath = getCachePath(overpassQuery);
|
||
|
|
if (
|
||
|
|
fs.existsSync(cachePath) &&
|
||
|
|
new Date() - fs.statSync(cachePath).mtime > CACHE_AGE
|
||
|
|
)
|
||
|
|
return JSON.parse(fs.readFileSync(cachePath));
|
||
|
|
else
|
||
|
|
return axios
|
||
|
|
.post(argv.endpoint, overpassQuery)
|
||
|
|
.then((response) => {
|
||
|
|
const restaurants = response.data.elements;
|
||
|
|
fs.writeFileSync(cachePath, JSON.stringify(restaurants));
|
||
|
|
callback(restaurants);
|
||
|
|
})
|
||
|
|
.catch(console.error);
|
||
|
|
};
|
||
|
|
|
||
|
|
withRestaurants((restaurants) => {
|
||
|
|
const randomOpenRestaurant = randomElement(
|
||
|
|
argv.checkOpen ? restaurants.filter(restaurantOpen) : restaurants
|
||
|
|
);
|
||
|
|
|
||
|
|
console.log(JSON.stringify(randomOpenRestaurant));
|
||
|
|
});
|