menicka-cron/cli.mjs
2024-08-29 20:02:13 +02:00

146 lines
5.2 KiB
JavaScript
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env -S npx nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
const { version, description }= s.cat("package.json").xargs(JSON.parse);
const today= [ 6, 0, 1, 2, 3, 4, 5 ][new Date().getDay()];
$.api()
.version(version)
.describe(description)
.command("pull", "Update article list")
.option("--git", "Update git repository")
.option("--dry-run", "Dry run")
.action(async function pull({ git: is_git= false, ["dry-run"]: dryRun= false, ..._ }= {}){
if(dryRun) is_git= false;
if(is_git) s.run`git pull --rebase`;
const menicka= await Promise.allSettled([ menickoCukrovarka(), menickoGlobus(), menickoMamafoodbistro() ])
.then(results=> results.filter(p=> p.status!=="rejected").map(p=> p.value));
const days= [ "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota", "neděle" ];
const out_head= description+"\n\n" + days.map((v)=> `[${v}](#${v})`).join(" · "); // anchors for days
let out= "";
for(let i= today; i < days.length; i++){ // menicka
const day= days[i];
out+= `\n\n## ${day}\n`;
out+= menicka.map(dayToMd(day)).join("\n");
}
const gen= [ "<!-- generated -->", "<!-- /generated -->" ];
const target= "README.md";
const readme= s.cat(target);
const readme_before= readme.indexOf(gen[0])-1;
const readme_today= today===0 ? -1 : readme.indexOf(`## ${days[today]}`, readme_before)-2;
out= [
readme.slice(0, readme_before),
readme_today < 0 ? gen[0]+out_head : readme.slice(readme_before+1, readme_today),
out.replace(/(\*)/g, "\\$1"),
gen[1], readme.slice(readme.indexOf("\n", readme.indexOf(gen[1]))+1)
].join("\n");
if(dryRun) echo(out);
else s.echo(out).to(target);
if(is_git) gitCommit([ target ], "pull");
$.exit(0);
})
.parse();
function dayToMd(day){
return function({ symbol, name, url, dny }){
let has= Reflect.has(dny, day);
if(!has && (day==="sobota" || day==="neděle") && Reflect.has(dny, "víkend")){
has= true;
day= "víkend";
}
const section= `[${symbol} ${name}](${url})`;
if(!has) return `- ${section}: —`;
let out= `- ${section}:`;
const { polevky, hlavni }= dny[day];
const food= f=> out+= `\n - ${f}`;
polevky.forEach(food);
hlavni.forEach(food);
return out;
};
}
function gitCommit(files, des= "not specified"){
if(!files.length || !s.run`git diff --numstat`.trim())
return echo("Nothig todo");
echo("Diff to save");
s.run`git config user.name "Bot"`;
s.run`git config user.email "${"zc.murtnec@naj.elrdna".split("").reverse().join("")}"`;
s.run`git add ${files}`;
s.run`git commit -m "Updated by bot ${des}"`;
s.run`git push`;
s.run`git config --remove-section user`;
}
import { JSDOM, VirtualConsole } from "jsdom";
async function fetchHTML(url){
const res= await fetch(url);
if(res.status !== 200) throw new Error(`Fetch failed with status ${res.status}`);
let text;
if(res.headers.get("Content-Type").includes("charset=windows-1250"))
text= await res.arrayBuffer().then(buff=> new TextDecoder('windows-1250').decode(buff));
else
text= await res.text();
const virtualConsole = new VirtualConsole();
virtualConsole.on("error", () => {
// No-op to skip console errors.
});
return (new JSDOM(text, { virtualConsole })).window;
}
async function menickoMamafoodbistro(){
const url= "https://www.mamafoodbistro.cz/#poledne";
const { document }= await fetchHTML(url);
const menicko= document.querySelectorAll("section")[2].getElementsByClassName("sqs-html-content")[1];
const dny= {};
for(const den of menicko.getElementsByTagName("h4")){
const polevka= den.nextElementSibling;
let hi= polevka.nextElementSibling;
const hlavni= [ hi ];
while(( hi= hi.nextElementSibling ) && hi.tagName !== "H4")
hlavni.push(hi);
Reflect.set(dny, den.textContent.trim().toLowerCase(), {
polevky: [ polevka.textContent ],
hlavni: hlavni.map(el=> el.textContent),
});
}
return { name: "MAMAFOOD", symbol: "🍎", url, dny };
}
async function menickoCukrovarka(){
const url= "https://www.menicka.cz/api/iframe/?id=8542&continuous=true";
const { document }= await fetchHTML(url);
const food= el=> el.getElementsByClassName("food")[0].textContent;
const dny= {};
for(const den of document.getElementsByClassName("content")){
let name= den.getElementsByTagName("h2")[0].textContent.trim();
name= name.slice(0, name.indexOf(" ")).toLowerCase();
const [ polevka ]= den.getElementsByClassName("soup");
const hlavni= den.getElementsByClassName("main");
Reflect.set(dny, name, {
polevky: [ food(polevka) ],
hlavni: [ ...hlavni ].map(food),
});
if(name==="neděle") break;
}
return { name: "Jídelna Čakovice", symbol: "🏭", url, dny };
}
async function menickoGlobus(){
const url= "https://www.globus.cz/praha-cakovice/sluzby-a-produkty/restaurace#klasicke-menu";
const { document }= await fetchHTML(url);
const menicko= document.getElementById("klasicke-menu");
const dny= {};
for(const den of menicko.getElementsByTagName("li")){
const name= den.getElementsByTagName("h3")[0].textContent.trim();
const vse= [ ...den.getElementsByTagName("tr") ].map(el=> el.getElementsByTagName("td")[1].textContent.trim());
Reflect.set(dny, name, {
polevky: vse.slice(0, 2),
hlavni: vse.slice(2),
});
}
return { name: "Globus", symbol: "🏪", url, dny };
}