add bin (scripts)

This commit is contained in:
Jan Andrle 2024-02-19 20:37:09 +01:00
parent 075f2deaf0
commit b54b08442a
Signed by: jaandrle
GPG Key ID: B3A25AED155AFFAB
26 changed files with 2056 additions and 0 deletions

9
.config/uurc Normal file
View File

@ -0,0 +1,9 @@
#! /usr/bin/
uu_w_alias="wttr.in/${1:-prague}?M"
uu_w_is_raw=0
uu_h_alias="cheat.sh/$1"
uu_h_is_raw=0
uu_surl_alias="tinyurl.com/api-create.php?url=$1"
uu_surl_is_raw=1
uu_ip_alias="ifconfig.co/json"
uu_ip_is_raw=1

76
bin/bw-ftp.js Executable file
View File

@ -0,0 +1,76 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
$.is_fatal= true;
const css= echo.css`
.code, .url{ color: lightblue; }
.code::before, .code::after{ content: "\`"; }
`;
testRequirements();
$.api()
.version("2023-03-21")
.describe([
"Small utility to find out FTP url with credentials using Bitwarden CLI.",
echo.format("The idea is to use saved login %cusername%c, %cpassword%c and %curl%c.",
css.code, css.unset, css.code, css.unset, css.code, css.unset)
])
.command("get [name]", "Get url with credentials.")
.alias("item")
.option("--copy", echo.format("Uses %cxclip -selection clipboard%c.", css.code))
.action(get)
.command("list", echo.format("List all %cftp-*%c.", css.code))
.option("--json", "Print output in JSON format.")
.action(list)
.parse();
async function get(name, { copy: is_copy= false }){
if(!name)
name= await $.read({
"-p": "Name",
completions: list({ is_internal: true }).map(o=> o.name)
});
const item= s.$().run`bw get item ${name}`;
if(!item.trim())
$.error(`No record found for ${name}.`);
const { uris, username, password }= item
.xargs(JSON.parse)
.login;
const url= urlFromUris(uris).replace('://', `://${username}:${password}@`);
if(!is_copy){
echo(url);
$.exit(0);
}
s.echo(url).run`xclip -selection clipboard 2>1 > /dev/null`;
$.exit(0);
}
function list({ json= false, is_internal= false }){
const list= s.$().run`bw list items --search="ftp"`
.xargs(JSON.parse)
.filter(o=> o.name.startsWith("ftp-"))
.map(({ name, note, login: { uris } })=> ({ name, url: urlFromUris(uris), note }))
.filter(o=> o.url);
if(is_internal)
return list;
if(json)
$.exit(0, echo(JSON.stringify(list)));
list.forEach(pipe(
line=> echo.format(line),
t=> t.replaceAll("\n", " ").slice(2, -2),
echo
));
$.exit(0);
}
function urlFromUris(uris){ return uris.find(o=> o.uri)?.uri; }
function testRequirements(){
if(!s.which("bw"))
$.error([
echo.format("The %cbw%c utility has not been found.", css.code),
echo.format("Please install it using %cnpm i @bitwarden/cli --location=global%c.", css.code),
echo.format("Respectively, follow the instructions at %chttps://github.com/bitwarden/clients/tree/master/apps/cli", css.url)
].join("\n"));
}
// vim: set tabstop=4 shiftwidth=4 textwidth=250 noexpandtab :
// vim>60: set foldmethod=indent foldlevel=1 foldnestmax=2:

82
bin/chrome-autoinspect.mjs Executable file
View File

@ -0,0 +1,82 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
const css= echo.css`
.url{ color: lightblue; }
.code { font-style: italic; }
.code::before, .code::after { content: "\`"; }
`;
$.api()
.version("2023-03-23")
.describe([
echo.format("This is small utility around %cchrome-remote-interface%c¹ to auto open inspect for cordova apps.", css.code),
echo.format("[1] %chttps://github.com/cyrus-and/chrome-remote-interface", css.url)
])
.command("open", "This open browser and after 30secs enable auto inspect openning.", true)
.option("--browser, -B", "Browser exec. Use one of supported browsers, such as chrome, chromium, opera, edge, …", "chromium")
.option("--port, -P", "Chanhe debugging port", 9222)
.action(async function({ browser, port }){
s.runA`${browser} --remote-debugging-port=${port}`;
const { setTimeout }= await import("node:timers/promises");
await setTimeout(30_000);
await register();
$.exit(0);
})
.command("enable", "Enable auto inspect openning for already running browser.")
.action(async function(){
await register();
$.exit(0);
})
.parse();
async function register(){
const { default: CDP } = await import($.xdg.globalPackage`chrome-remote-interface`);
let client;
try{
await CDP.New();
client= await CDP();
const { Network, Page, Runtime }= client;
await Network.enable();
await Page.enable();
await Page.navigate({url: 'chrome://inspect/#devices'});
await Page.loadEventFired();
await Runtime.evaluate({ expression: `const debugCordova= (${autoRunCordovaAppInspect.toString()})();` });
} finally {
if(!client) return;
return await client.close();
}
}
function autoRunCordovaAppInspect(){
const { filter, map }= Array.prototype;
let /* filters */
device_filter, app_filter, last_state;
const /* get elements */
devicesElements= ()=> filter.call(document.getElementsByClassName("device"), el=> el.id!=="device:localhost"),
getApp= el=> el.getElementsByClassName("browser-name")[0].textContent,
appTest= browser_candidate_el=> getApp(browser_candidate_el).indexOf(app_filter)!==-1,
browsersElements= wrapper_el=> wrapper_el.getElementsByClassName("browser"),
actionElementFilter= wrapper_el=> filter.call(wrapper_el.getElementsByClassName("action"), el=> el.textContent==="inspect")[0];
function run(){
const device= !device_filter ? (d=> d&&d[d.length-1])(devicesElements()) : filter.call(devicesElements(), el=> el.id===device_filter)[0];
if(!device) return false;
const app= !app_filter ? (a=> a&&a[a.length-1])(browsersElements(device)) : filter.call(browsersElements(document), appTest)[0];
if(!app) return false;
const { id }= app;
if(last_state===id) return false;
last_state= id;
const action= actionElementFilter(app);
if(!action) return false;
action.click();
}
const observer= new MutationObserver(run);
observer.observe(document.getElementById("devices-list"), { childList: true, subtree: true })
return {
/* Set string to filter, typically app id or ip */
setApp: _app_filter=> app_filter= _app_filter,
/* Apply only for given device (see debugCordova.devices). */
setDevice: _device_filter=> device_filter= _device_filter,
get apps(){ return map.call(browsersElements(document), getApp); },
get devices(){ return map.call(devicesElements(), el=> el.id); }
};
}

418
bin/github-releases.js Executable file
View File

@ -0,0 +1,418 @@
#!/usr/bin/env node
/* jshint esversion: 8,-W097, -W040, node: true, expr: true, undef: true */
const /* dependencies */
[ fs, readline, https, { spawn } ]= [ "fs", "readline", "https", "child_process" ].map(p=> require(p));
const /* helper for coloring console | main program params */
colors= { e: "\x1b[38;2;252;76;76m", s: "\x1b[38;2;76;252;125m", w: "\x1b[33m", R: "\x1b[0m", y: "\x1b[38;2;200;190;90m", g: "\x1b[38;2;150;150;150m" },
info= {
name: __filename.slice(__filename.lastIndexOf("/")+1, __filename.lastIndexOf(".")),
version: "1.2.1",
description: "Helper for working with “packages” stored in GitHub releases.",
config: `${__filename.slice(0, __filename.lastIndexOf("."))}.json`,
folder: __filename.slice(0, __filename.lastIndexOf("/")+1),
commands: [
{
cmd: "help", args: [ "--help", "-h" ],
desc: "Shows this text"
},
{
cmd: "config", args: [ "--config" ],
desc: "Opens config file in terminal editor (defaults to vim)"
},
{
cmd: "check", args: [ "--check", "-c" ],
desc: "Shows/checks updates for registered packages"
},
{
cmd: "update", args: [ "--update", "-u" ], param: "group",
desc: "Installs lates versions of registered packages"
},
{
cmd: "uninstall", args: [ "--uninstall", "-u" ], param: "package",
desc: "Deletes downloaded file and moves package to the 'skip' group"
},
{
cmd: "register", args: [ "--register", "--change" ], param: "package",
desc: "Add package infos to internal list to be able installing/updating"
},
{
cmd: "remove", args: [ "--remove" ], param: "package",
desc: ([
"Uninstall package if needed (see `-u`)",
"And remove it from internal list (see `--config`)"
]).join(". ")
}
],
params: {
group: ([
"You can label each package to update only choosen one",
"There are sereved options:",
" - '' (empty): these packages are includes in all groups",
" - 'all': in case of `--update` process all packages (except skipped)",
" - 'skip': these packages are “uninstalled”",
" No updates will be downloaded",
"Group can be setted via '--register'"
]).join(". "),
package: ([
"Represents package identificator, it is in fact GitHub repository path",
"So, it schould be in the form `username/repository`"
]).join(". ")
}
};
printMain();
const current= getCurrent(process.argv.slice(2));
(function main_(){
const { cmd }= current.command;
if(!cmd) return Promise.resolve("No arguments (use `--help` for showing all oprions).");
switch(cmd){
case "help": return Promise.resolve(printHelp());
case "config": return vim_(info.config);
}
const config= getConfig();
switch(cmd){
case "register": return register_(config);
}
if(!config.packages) return Promise.resolve("No packages yet!");
switch(cmd){
case "check": return check_(config);
case "update": return update_(config);
case "uninstall":
case "remove":
return uninstall_(cmd, config);
}
})()
.then(function(message){
if(message)
log(1, `Operation '${current.command.cmd}' successfull: @s_${message}`);
process.exit();
})
.catch(error);
async function uninstall_(cmd, config){
const progress= [
[ "Deleting file", "not needed" ],
[ "Check out from updates", "yes" ],
[ "Remove from packages list", "no" ]
];
const pkg_name= current.param;
const pkg_index= config.packages.findIndex(({ repository })=> repository===pkg_name);
if(pkg_index===-1) return "nothing to do (maybe typo)";
const pkg= config.packages[pkg_index];
const { downloads }= pkg;
if(downloads&&fs.existsSync(downloads)){
try{ fs.unlinkSync(downloads); progress[0][1]= "done"; }
catch (_){ progress[0][1]= colors.e+"error, try manually "+downloads; }
}
Reflect.deleteProperty(pkg, "last_update");
Reflect.set(pkg, "group", "skip");
progress[1][1]= "done";
if(cmd!=="remove") return gotoEnd();
const y= await promt_(`Are you realy want to remove package ${pkg.repository} (yes/no)`, "no");
if(y!=="yes") return gotoEnd();
config.packages.splice(pkg_index, 1);
progress[2][1]= "done";
return gotoEnd();
function gotoEnd(){
const o= progress.reduce((o, [ k, v ])=> Reflect.set(o, k, v)&&o, {});
logSection(" ", pkg_name, o);
save(config);
}
}
function vim_(file){ return new Promise(function(resolve, reject){
const cmd= spawn((process.env.EDITOR||"vim")+(process.platform==="win32"?".bat":""), [ file ], { stdio: 'inherit' });
cmd.on('exit', e=> e ? reject("Editor error, try manually: "+file) : resolve("OK"));
});}
async function update_(config){
const filter= current.param;
const is_all= filter==="all";
let updates= [];
log(1, "Collecting packages to download:");
for(const [
i, { repository, last_update, group, file_name, exec, downloaded, tag_name_regex }
] of Object.entries(config.packages)){
if(group==="skip") continue;
if(!is_all&&group&&filter!==group) continue;
const { tag_name, published_at, html_url, assets_url }= await githubRelease_(repository, tag_name_regex);
const status= packageStatus(last_update, published_at);
if(status!==3) continue;
const assets= await downloadJSON_(repository, assets_url);
if(!assets.length){
console.log(" Nothing to download: Visit "+html_url);
continue;
}
const options= assets.map(({ name, download_count, size })=>
`${name} | size: ${Math.round(size/1048576)}MB | downloads: ${download_count}`);
logSection(" ", " "+repository, {
"Version": tag_name,
"Url": html_url
});
logSection(" ", " Available assets:", options);
const choose= await promt_(" Choose (empty for skip)", "");
if(choose==="") continue;
const { browser_download_url: url, name: remote_name, size }= assets[choose];
updates.push({
index: i,
file_name, exec, downloaded,
repository, version: tag_name, last_update: published_at,
url, remote_name, size
});
}
if(!updates.length){
log(2, "No packages in "+`group ${filter} needs updates.`);
return Promise.resolve("nothing to update");
}
log(1, "Downloading:");
return applySequentially_(updates, async function(todo){
const to= todo.file_name ? info.folder+todo.file_name : (
todo.downloaded ? todo.downloaded : info.folder+todo.remote_name);
const d= await downloadFile_(to, todo);
return Object.assign(todo, d);
})
.then(function(dones){
log(1, "Finalizing:");
let e= 0;
for(const nth of dones){
if(!nth.success){
e+= 1;
log(2, `${nth.repository}: @e_${nth.message}`);
continue;
}
Object.assign(config.packages[nth.index], registerDownloads(nth));
}
save(config);
const { length }= dones;
const msg= `updated ${length-e} of ${length} packages.`;
return e ? Promise.reject(msg) : Promise.resolve(msg);
});
}
function registerDownloads({ repository, last_update, message: downloads, exec, version }){
let msg= colors.s+"OK";
if(exec==="yes"){
try{ fs.chmodSync(downloads, 0o755); }
catch(e){ msg= colors.e+"try manual `chmod+x` for '"+downloads+"'"; }
}
log(2, `${repository}: ${msg}`);
return { last_update, downloads, version };
}
async function check_({ packages }){
let updates= 0, skipped= 0;
for(const { repository, name, version, last_update, group, tag_name_regex } of packages){
const { tag_name, published_at }= await githubRelease_(repository, tag_name_regex);
const status= packageStatus(last_update, published_at);
updates+= status===3;
const skip= group==="skip";
skipped+= skip;
log(2, `@g_${repository} [${group}]: `+( !version ? "not installed" : packageStatusText(status, skip) ));
}
const u= updates-skipped;
const s= skipped ? ` (inc. skipped: ${updates})` : "";
return (!u ? "" : colors.w)+u+" update(s) available"+s;
}
async function register_(config){
const { param: repository }= current;
if(!Reflect.has(config, "packages")) Reflect.set(config, "packages", []);
const packages= Reflect.get(config, "packages");
let local_id= packages.findIndex(p=> p.repository===repository);
if(local_id===-1)
local_id= packages.push({ repository })-1;
const local= config.packages[local_id];
const remote= await githubRepo_(repository) || {};
log(1, "Registering: "+repository);
const spaces= " ";
local.name= await promt_(spaces+"Name", local.name || remote.name || "");
if(!local.description) local.description= remote.description;
logLines(2, [
"@g_Group info:",
"- you can update specific packages by using their group name",
"- There some reserved options:",
" - '' (empty): will be included in all groups",
" - 'skip': will be always skipped"
]);
local.group= await promt_(spaces+"Group", local.group || "");
local.file_name= await promt_(spaces+"File Name", local.file_name || local.name.toLowerCase().replace(/\s/g, "-") || "");
local.exec= await promt_(spaces+"Make executable (yes/no)", local.exec || "no");
save(config);
return `${repository}: saved`;
}
function packageStatusText(status, skip){
const s= skip ? colors.R+"skipped "+colors.g : "";
switch(status){
case 0: return s+"nothing to compare";
case 1: return s+"@s_up-to-date";
case 2: return s+"newer";
case 3: return s+"@e_outdated/not instaled";
}
}
function packageStatus(local, remote){
if(!remote) return 0;
if(!local) return 3;
if(remote===local) return 1;
return 2+(local<remote);
}
function logSection(spaces, name, data){
console.log(spaces+name);
for(const [ key, value ] of Object.entries(data))
console.log(spaces.repeat(2)+colors.g+key+": "+value.replace(/@(\w)_/g, (_, m)=> colors[m])+colors.R);
}
function githubRelease_(repository, tag_name_regex= ""){
return downloadJSON_(repository, "https://api.github.com/repos/"+repository+"/releases")
.then(data=> data.find(function find({ draft, published_at, tag_name }){
if(draft||!published_at) return false;
if(!tag_name_regex) return true;
return (new RegExp(tag_name_regex, 'g')).test(tag_name);
})||{});
}
function githubRepo_(repository){ return downloadJSON_(repository, "https://api.github.com/repos/"+repository); }
function promt_(q, def){
const rl= readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise(function(resolve){
rl.question(q+": ", a=> { rl.close(); resolve(a); });
rl.write(def);
});
}
function getConfig(){
let config;
try{ config= JSON.parse(fs.readFileSync(info.config)); }
catch(e){ config= {}; log(1, "@w_Missing or corrupted config file. Creates empty one."); }
return config;
}
function save(config){
return fs.writeFileSync(info.config, JSON.stringify(config, null, " "));
}
function getCurrent(args){
let command, command_arg, param;
const hasArg= arg=> ({ args })=> args.includes(arg);
for(let i=0, { length }= args, arg; i<length; i++){
arg= args[i];
if(!command){
command= info.commands.find(hasArg(arg));
command_arg= arg;
continue;
}
if(!command.param||typeof param!=="undefined")
break;
param= arg;
}
if(!command)
command= { cmd: "" };
if(command.param&&typeof param==="undefined")
return error(`Missign arguments for '${command_arg}'.`);
return { command, param };
}
function downloadJSON_(repository, url){
return downloadText_(url)
.then(function(data){
try{
const response= JSON.parse(data);
if(Reflect.has(response, "message")) throw new Error(response.message);
return Promise.resolve(JSON.parse(data));
} catch(e){
log(1, "Received data: "+data);
log(1, "@e_"+e);
return Promise.reject(`JSON from '${repository}' failed.`);
}
});
}
function downloadText_(url){
return get_(url)
.then(function(response){ return new Promise(function(resolve){
let data= "";
response.on("data", chunk=> data+= chunk);
response.on("end", ()=> resolve(data));
}); });
}
function downloadFile_(to, { url, repository, size }){
const file= fs.createWriteStream(to);
return get_(url)
.then(r=> get_(r.headers.location))
.then(function(response){ return new Promise(function(resolve){
let progress= 0, pc_prev= 0, avg= 0;
const start= new Date();
const i= setInterval(function(){
readline.clearLine(process.stdout);
const pc= (100*progress/size).toFixed(2);
if(!pc_prev) pc_prev= pc;
else {
avg= ((100-pc)/(60*(pc-pc_prev))).toFixed(2);
pc_prev= 0;
}
const running= ((new Date()-start)/60000).toFixed(2);
log(2, repository+": "+pc+"%"+` (end in ~${avg} mins, running ${running} mins)`);
readline.moveCursor(process.stdout, 0, -1);
}, 500);
response.on('data', function(chunk){
file.write(chunk);
progress+= chunk.length;
});
response.on('end', function(){
clearInterval(i);
readline.clearLine(process.stdout);
log(2, repository+": @s_OK");
file.close(()=> resolve({ success: 1, message: to })); /* close() is async, call cb after close completes. */
});
}); })
.catch(({ message })=> {
fs.unlink(to); // Delete the file async. (But we don't check the result)
return { success: 0, message };
});
}
function get_(url){ return new Promise(function(resolve, reject){
https.get(
url,
{ headers: { 'Cache-Control': 'no-cache', 'User-Agent': 'node' } },
resolve
).on("error", reject);
});}
function applySequentially_(input, pF){
const data= [];
let p= pF(input[0]);
const tie= nth=> result_mth=> ( data.push(result_mth), pF(input[nth]) );
for(let i= 1, { length }= input; i<length; i++)
p= p.then(tie(i));
return p.then(o=> (data.push(o), data));
}
function error(message){
const help_text= `@w_See help using '${info.commands[0].args[0]}'.`;
log(1, `@e_Error: ${message} ${help_text}`);
return process.exit(1);
}
function printMain(){
const { name, version, description }= info;
log(1, `@w_${name}@${version}`);
log(1, description);
const cmds= info.commands.map(({args})=> args[0].replace("--", "")).join(", ");
log(1, `@w_Usage: ${name} --[cmd] [param]`);
log(2, `…cmd: ${cmds}`);
log(2, "…param: Based on cmd\n");
}
function printHelp(){
log(1, "@s_Help:");
log(2, "Commands:");
info.commands.forEach(({ args, param, desc })=> {
const args_text= args.join("|");
param= param ? " "+param : "";
log(3, `@g_${args_text}@R_${param}`);
logLines(4, desc);
});
log(2, "Params:");
for(const [ param, desc ] of Object.entries(info.params)){
log(3, `@g_${param}`);
logLines(4, desc);
}
}
function log(tab, text){
return console.log(" ".repeat(tab)+text.replace(/@(\w)_/g, (_, m)=> colors[m])+colors.R);
}
function logLines(tab, multiline_text){
if(!Array.isArray(multiline_text)) multiline_text= multiline_text.split(/(?<=\.) /g);
return log(tab, multiline_text.join("\n"+" ".repeat(tab)));
}

216
bin/github-releases.json Normal file
View File

@ -0,0 +1,216 @@
{
"packages": [
{
"repository": "shiftkey/desktop",
"name": "GitHub Desktop",
"group": "dev",
"file_name": "github-desktop",
"exec": "yes",
"description": "Fork of GitHub Desktop to support various Linux distributions",
"last_update": "2023-12-20T15:25:06Z",
"downloads": "/home/jaandrle/bin/github-desktop",
"version": "release-3.3.6-linux3"
},
{
"repository": "jaandrle/jaaCSS-cli",
"name": "jaaCSS",
"description": "EXPERIMENT Helper for managing functional CSS classes",
"group": "dev",
"file_name": "jaaCSS.js",
"exec": "yes",
"downloads": "/home/jaandrle/bin/jaaCSS.js",
"version": "v1.3.2",
"last_update": "2022-09-02T13:33:16Z"
},
{
"repository": "th-ch/youtube-music",
"name": "youtube-music",
"description": "YouTube Music Desktop App bundled with custom plugins (and built-in ad blocker / downloader)",
"group": "nondev",
"file_name": "youtube-music",
"exec": "yes",
"last_update": "2024-01-05T14:44:27Z",
"downloads": "/home/jaandrle/bin/youtube-music",
"version": "v3.2.2"
},
{
"repository": "ArchGPT/insomnium",
"name": "insomnium",
"description": "Insomnium is a fast local API testing tool that is privacy-focused and 100% local. For testing GraphQL, REST, WebSockets and gRPC. This is a fork of Kong/insomnia",
"group": "dev",
"file_name": "insomnium",
"exec": "yes",
"last_update": "2023-11-13T10:03:28Z",
"downloads": "/home/jaandrle/bin/insomnium",
"tag_name_regex": "core@.*",
"version": "core@0.2.3-a"
},
{
"repository": "Kong/insomnia",
"name": "insomnia",
"description": "The open-source, cross-platform API client for GraphQL, REST, and gRPC.",
"group": "skip",
"file_name": "insomnia",
"exec": "yes",
"last_update": "2023-10-16T10:03:28Z",
"downloads": "/home/jaandrle/bin/insomnia",
"tag_name_regex": "core@.*",
"version": "core@8.3.0"
},
{
"repository": "rvpanoz/luna",
"name": "luna",
"description": "Manage npm dependencies through a modern UI.",
"group": "skip",
"file_name": "luna",
"exec": "yes"
},
{
"repository": "angela-d/wifi-channel-watcher",
"name": "wifi-channel-watcher",
"group": "skip",
"file_name": "wifi-channel-watcher",
"exec": "no",
"description": "Monitor channel usage of neighboring routers & get an alert if your active channel is not optimal.\tTroubleshoot wifi without lifting a finger!"
},
{
"repository": "vinceliuice/Tela-circle-icon-theme",
"name": "Tela-circle-icon-theme",
"description": "Tela-circle-icon-theme",
"group": "themes",
"file_name": "tela-circle-icon-theme.zip",
"last_update": "2021-07-19T14:12:05Z",
"exec": "no"
},
{
"repository": "AppImage/AppImageKit",
"name": "AppImageKit",
"group": "skip",
"file_name": "appimagekit",
"exec": "yes",
"description": "Package desktop applications as AppImages that run on common Linux-based operating systems, such as RHEL, CentOS, openSUSE, SLED, Ubuntu, Fedora, debian and derivatives. Join #AppImage on irc.freenode.net"
},
{
"repository": "dynobo/normcap",
"name": "NormCap",
"description": "OCR powered screen-capture tool to capture information instead of images",
"group": "nondev",
"file_name": "normcap",
"exec": "yes",
"last_update": "2023-12-12T22:23:37Z",
"downloads": "/home/jaandrle/bin/normcap",
"version": "v0.5.2"
},
{
"repository": "upscayl/upscayl",
"name": "upscayl",
"description": "🆙 Upscayl - Free and Open Source AI Image Upscaler for Linux, MacOS and Windows built with Linux-First philosophy.",
"group": "nondev",
"file_name": "upscayl",
"exec": "yes",
"last_update": "2024-01-16T09:54:25Z",
"downloads": "/home/jaandrle/bin/upscayl",
"version": "v2.9.8"
},
{
"repository": "RasmusLindroth/tut",
"name": "tut",
"description": "TUI for Mastodon with vim inspired keys",
"group": "nondev",
"file_name": "tut",
"exec": "yes",
"last_update": "2023-01-26T17:48:00Z",
"downloads": "/home/jaandrle/bin/tut",
"version": "2.0.1"
},
{
"repository": "sunner/ChatALL",
"name": "ChatALL",
"description": " Concurrently chat with ChatGPT, Bing Chat, bard, Alpaca, Vincuna, Claude, ChatGLM, MOSS, iFlytek Spark, ERNIE and more, discover the best answers",
"group": "skip",
"file_name": "chatall",
"exec": "yes",
"last_update": "2023-09-30T14:08:00Z",
"downloads": "/home/jaandrle/bin/chatall",
"version": "v1.50.73"
},
{
"repository": "jaandrle/bs",
"name": "bs",
"description": "The simplest possible build system using executables",
"group": "dev",
"file_name": "bs",
"exec": "yes",
"last_update": "2023-06-30T07:48:58Z",
"downloads": "/home/jaandrle/bin/bs",
"version": "v0.7.3"
},
{
"repository": "h3poteto/fedistar",
"name": "Fedistar",
"description": "Multi-column Mastodon, Pleroma, and Friendica client for desktop",
"group": "nondev",
"file_name": "fedistar",
"exec": "yes",
"last_update": "2024-01-29T10:29:58Z",
"downloads": "/home/jaandrle/bin/fedistar",
"version": "v1.8.3"
},
{
"repository": "ollama/ollama",
"name": "ollama",
"description": "Get up and running with Llama 2 and other large language models locally",
"group": "ai",
"file_name": "ollama",
"exec": "yes",
"last_update": "2024-01-26T18:19:36Z",
"downloads": "/home/jaandrle/bin/ollama",
"version": "v0.1.22"
},
{
"repository": "neovim/neovim",
"name": "neovim",
"tag_name_regex": "v.*",
"description": "Vim-fork focused on extensibility and usability",
"group": "dev-test",
"file_name": "nvim",
"exec": "yes",
"downloads": "/home/jaandrle/bin/nvim",
"version": "v0.9.5",
"last_update": "2023-12-30T13:31:47Z"
},
{
"repository": "viarotel-org/escrcpy",
"name": "Escrcpy",
"description": "📱 Graphical Scrcpy to display and control Android, devices powered by Electron. | 使用图形化的 Scrcpy 显示和控制您的 Android 设备,由 Electron 驱动。",
"group": "dev",
"file_name": "escrcpy",
"exec": "yes",
"last_update": "2023-12-27T01:18:21Z",
"downloads": "/home/jaandrle/bin/escrcpy",
"version": "v1.16.8"
},
{
"repository": "drovp/drovp",
"name": "drovp",
"description": "Desktop app for encoding, converting, upscaling, and much more.",
"group": "dev-test",
"file_name": "drovp",
"exec": "yes",
"last_update": "2023-12-06T11:30:02Z",
"downloads": "/home/jaandrle/bin/drovp",
"version": "0.8.0"
},
{
"repository": "janhq/jan",
"name": "Jan",
"description": "Jan is an open source alternative to ChatGPT that runs 100% offline on your computer",
"group": "ai",
"file_name": "jan",
"exec": "yes",
"last_update": "2024-01-29T05:19:22Z",
"downloads": "/home/jaandrle/bin/jan",
"version": "v0.4.5"
}
]
}

8
bin/jsconfig.json Normal file
View File

@ -0,0 +1,8 @@
{
"include": [
"/home/jaandrle/.nvm/versions/node/v18.18.0/lib/node_modules/nodejsscript/index.d.ts",
"./*.mjs",
"./*.js",
"chrome-autoinspect.mjs"
]
}

19
bin/nocodb.mjs Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
const url_api= "https://nocodb.jaandrle.cz/api/v1/db/data/v1/Linky/Linky";
$.api()
.command("add-md")
.action(async function addMd(){
const { clipboard }= $;
const [ title, url ]= clipboard.split("](");
const res= await fetch(url_api, {
body: JSON.stringify({ Popis: title.slice(1), Url: url.slice(0, -1) }),
headers: { "xc-token": "Js7Qu0oT_wTmGJDFKlIZEBhwk87WF0L1wLmQO01F",
"accept": "application/json", "Content-Type": "application/json"
},
method: "post"
}).then(res=>res.json());
echo(res);
})
.parse();

9
bin/onedrive Executable file
View File

@ -0,0 +1,9 @@
#!/bin/bash
if [[ $(mount | grep OneDrive) ]]; then
fusermount -uz ~/Vzdálené/OneDrive
notify-send -i system-file-manager "OneDrive disk ODPOJEN"
else
rclone --vfs-cache-mode full mount "onedrive": ~/Vzdálené/OneDrive &
dolphin ~/Vzdálené/OneDrive/ &
notify-send -i system-file-manager "OneDrive disk PŘIPOJEN"
fi

71
bin/piper.mjs Executable file
View File

@ -0,0 +1,71 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
const p_home= $.xdg.home`Applications/piper/`
const p_models= p_home+"models/"
$.api("", true)
.version("2023-05-31")
.describe([
"This is a wrapper around the piper CLI.",
"It allows you to get the list of available models, or to simplify `piper` callings.",
"",
"Visit: https://github.com/rhasspy/piper",
"Original help:",
...s.$().run`${p_home}piper --help`.stderr.split("\n").filter(Boolean).map(l=> "\t"+l)
])
.option("--models", "prints available models")
.option("--model, -m", "chooses voice model by it's index or by text search (full name)")
.option("--input_file, -I", "path to input text file (defaults to stdin)")
.action(function main({
models: is_models,
model= 0,
input_file,
_,
...pass
}){
if(is_models){
models()
.forEach(l=> echo(l));
echo(helpTextModels("…for more models"))
$.exit(0);
}
model= getModel(model);
pass= Object.entries(pass)
.map(([ name, value ])=> `${name.length > 1 ? "--" : "-"}${name} '${value}'`)
.join(" ");
const o= s.run(`echo ${text(input_file)} | ${p_home}piper --model ${model} ${pass}`);
$.exit(o.code);
})
.parse();
function text(input_file){
const candidate= input_file ? s.cat(input_file).stdout : $.stdin.text();
if(typeof candidate!=="string")
$.error("Missing input text file or piped text. Use `--help` for more information.");
return "'"+candidate.trim().replaceAll("\n", "\t — \t").replaceAll("'", "\"")+"'";
}
function getModel(identifier){
const candidate= typeof identifier==="number" ?
models()[identifier] :
models().find(l=> l.includes(identifier));
if(!candidate)
$.error([
`Model identifier '${identifier}' seems not to matching any existing model.`,
"Try `--models` to see all available models."
].join("\n"));
return candidate.slice(candidate.indexOf(" ")+1);
}
function models(){
const out= s.ls(`${p_models}*.onnx`)
.map((l, n)=> `${n}: ${l}`);
if(!out.length)
$.error(helpTextModels("No available models."));
return out;
}
function helpTextModels(...text_initial){
return [
...text_initial,
"Visits https://github.com/rhasspy/piper",
"and download/extract model(s) into "+p_models
].join("\n");
}

10
bin/pocket-sh-add.sh Executable file
View File

@ -0,0 +1,10 @@
[ ! -r ~/.config/pocketshaddrc ] && echo "\`~/.config/pocketshaddrc\` not found" && exit 1
. ~/.config/pocketshaddrc
curl -sS -X POST \
-F "url=$1" \
-F "title=$2" \
-F "consumer_key=$CONSUMER_KEY" \
-F "access_token=$ACCESS_TOKEN" \
https://getpocket.com/v3/add \
> /dev/null

80
bin/socky.mjs Executable file
View File

@ -0,0 +1,80 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
$.api("")
.version("2023-08-11")
.command("textInImage [file]", "Generovat obrázek s textem pro sdílení na sociálních sítích.")
.alias("tii")
.option("--pointsize", "Text size", "62")
.option("--gravity", "`convert -list gravity`", "Center")
.option("--text", "Text")
.action(textInImage)
.command("textSplit <file>", "Rozdělit text dle zadaného limitu.")
.alias("ts")
.option("--limit", "Počet znaků kde text rozdělovat.", 500)
.option("--counter", "Prepend counter, set no. of chars of counter.", 5)
.option("--text", "Text")
.action(textSplit)
.parse();
function textSplit(file, { limit, counter, text }){
limit= parseInt(limit) - parseInt(counter);
const words= getText(file, text).split(" ");
let buffer= [], i= -1, chars= limit;
for(const word of words){
const { length }= word;
if((chars+length) < limit){
chars+= ( is_empty ? 0 : 1 ) + length;
buffer[i].end= i;
continue;
}
chars= 0;
i+= 1;
buffer[i]= { start: i };
}
const { length }= buffer;
echo(buffer.map(function({ start, end }, i){
return (i+1)+"/"+length+" "+words.slice(start, end).join(" ");
}).join("\n============\n"));
$.exit(0);
}
function textInImage(file, { text, gravity, pointsize }){
text= getText(file, text);
const output= !file ? "textInImage.png" : (({ pathname: p })=> p.slice(1, p.lastIndexOf(".")+1)+"png")(new URL(file, "file://"));
const round_corners= [ //https://9to5answer.com/rounding-corners-of-pictures-with-imagemagick
"\\(",
"+clone -alpha extract",
"-draw 'fill black polygon 0,0 0,15 15,0 fill white circle 15,15 15,0'",
"\\( +clone -flip \\) -compose Multiply -composite",
"\\( +clone -flop \\) -compose Multiply -composite",
"\\) -alpha off -compose CopyOpacity -composite",
].join(" ");
s.run([
"convert",
"\\(",
"-size ::size::",
"-pointsize ::pointsize::",
"-fill ::fill:: -background ::background::",
"-gravity ::gravity::",
"-family ::family:: -weight Bold",
"-bordercolor ::background:: -border 5%",
"caption:::text::",
"\\)",
round_corners,
"::output::"
].join(" "), {
fill: "whitesmoke",
background: "#2b2b2b",
size: "1024x512",
family: "Ubuntu Mono",
output, text, gravity, pointsize
});
$.exit(0);
}
function getText(file, text){
if(file && s.test("-f", file))
return s.cat(file).toString();
if(!text)
return $.stdin.text().trim();
return text;
}

119
bin/uu Executable file
View File

@ -0,0 +1,119 @@
#!/usr/bin/env bash
set -eo pipefail
this="${0##*/}"
version="2022-07-27"
config_file="$HOME/.config/${this}rc"
[ ! -t 0 ] && exo-open --launch TerminalEmulator -- -e "$this $*" && exit 0
arg=${1:---help}
if [[ "$arg" = "--completion-bash" ]]; then
echo "_${this}_completion(){
local cur=\"\${COMP_WORDS[COMP_CWORD]}\"
local com_basic=\"--alias --help --version --tips\"
local com_urls=\"cht.sh wttr.in rate.sx qrenco.de ifconfig.co\"
if [[ \$COMP_CWORD != 1 ]]; then
local com_web=\"\"
case \"\${COMP_WORDS[1]}\" in
--raw)
COMPREPLY=( \$(compgen -W \"\$com_urls \$com_basic\" -- \"\$cur\" ) )
return 0;
;;
--alias)
COMPREPLY=( \$(compgen -W \"? + -\" -- \"\$cur\" ) )
return 0;
;;
wttr.in)
local com_web+=\"moon m u M 0 1 2 A F n q Q T\"
;;
ifconfig.co)
local com_web+=\"json\"
;;
cht.sh)
local com_web+=\"\$(compgen -c)\"
;;
esac
COMPREPLY=( \$(compgen -W \":help \$com_web\" -- \"\$cur\" ) )
return 0;
fi
COMPREPLY=( \$(compgen -W \"--raw \$com_urls \$com_basic\" -- \"\$cur\" ) )
}
complete -o bashdefault -o default -F _${this}_completion ${this}
"
exit 0;
fi
_echo(){ [[ $is_raw == 1 ]] && echo -e "$1" || echo "$1" | less -R -S; }
[[ "$arg" = "--raw" ]] && is_raw=1 && command shift && arg=${1:---help} || is_raw=0
if [[ "$arg" = "--help" ]]; then
_echo "\
$this@v$version URL UTILS
This is helper around \`curl\` to run web-based commands such as 'wttr.in', 'cht.sh', …
Usage:
$this [--raw] BASE_URL [PARAMS]
$this [--raw] --[help|version|tips]
$this [--raw] --alias NAME +|-|? FULL_URL
Options:
--raw no output using \`less\`
--alias you can also use vars '\\\$1', …
PARAMS parameters to be concatenated with BASE_URL with '/' or '?'/'&' when parameter starts with '?' … se below
Examples:
$this cht.sh/less $this cht.sh less
$this wttr.in/prague?M&n $this wttr.in prague ?M ?n
$this --raw ident.me/json | jq
Config file:
$config_file
"
exit 0
fi
[[ "$arg" = "--version" ]] && echo "$version" && exit 0
if [[ "$arg" = "--tips" ]]; then
_echo "\
cht.sh The only cheat sheet you need Unified access to the best community driven documentation repositories of the world.
wttr.in Weather report
rate.sx show exchange rates for cryptocurrencies
qrenco.de
ifconfig.co
https://github.com/chubin/awesome-console-services/tree/65e8e897c9c5a2ec013747dd9f1acc03c8573fe7
"
exit 0
fi
command shift
if [[ "$arg" == "--alias" ]]; then
alias_name="${1:-[^_]*}"
[[ $alias_name == *.* ]] && echo "Alias should not contains '.'" && exit 1
case "$2" in
"+")
out=$(grep -v -e "uu_${alias_name}_alias=" -e "uu_${alias_name}_is_raw=" $config_file)
out="$out\nuu_${alias_name}_alias=\"$3\""
out="$out\nuu_${alias_name}_is_raw=$is_raw"
echo -e "$out" > $config_file
exit 0
;;
"-") echo -e "$(grep -v -e "uu_${alias_name}_alias=" -e "uu_${alias_name}_is_raw=" $config_file)" > $config_file || echo "No aliases yet"; exit 0;;
*) grep -e "uu_${alias_name}_alias=" -e "uu_${alias_name}_is_raw=" $config_file || echo "No aliases yet"; exit 0;;
esac
exit 1
fi
args=""
if [[ $arg != *.* ]]; then
. $config_file
is_raw_name=uu_${arg}_is_raw
is_raw=${!is_raw_name}
[[ -z $is_raw ]] && echo "No alias '$arg' found." && exit 1
arg_name=uu_${arg}_alias
arg=${!arg_name}
else
for p in "$@"; do
[[ $p == "?"* ]] \
&& args+=" --data-urlencode ${p:1} " \
|| arg+="/$p"
done
fi
out=$(curl -fGsS -H 'Accept-Language: cs' $args --compressed $arg || echo 'Curl error, see terminal error output.')
_echo "$out"

159
bin/§ai-commit.mjs Executable file
View File

@ -0,0 +1,159 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
$.is_fatal= true;
const token_file= "~/.config/openai.token";
let token;
const gitmoji_list= {
build: "building_construction",
chore: "bricks",
ci: "construction_worker",
docs: "memo",
feat: "sparkles",
fix: "bug",
perf: "zap",
refactor: "recycle",
revert: "rewind",
style: "art",
test: "white_check_mark"
};
const git3moji_list= {
build: "tv",
chore: "tv",
ci: "tv",
docs: "abc",
feat: "zap",
fix: "bug",
perf: "zap",
refactor: "cop",
style: "zap",
test: "cop"
};
const conventional_desc= [
"Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)",
"Other changes that don't modify src or test files",
"Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)",
"Documentation only changes",
"A new feature",
"A bug Fix",
"A code change that improves performance",
"A code change that neither fixes a bug nor adds a feature",
"Reverts a previous commit",
"Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)",
"Adding missing tests or correcting existing tests"
];
$.api("", true)
.version("2024-02-19")
.describe([
"Utility to use ChatGPT to generate a commit message from COMMIT_EDITMSG file.",
`Don't forget to set the token in ${token_file} file.`,
"",
"Conventional/gitmoji commits should follow https://www.conventionalcommits.org/en/v1.0.0/ (https://github.com/pvdlg/conventional-commit-types).",
"For the gitmoji the <type> uses emojis as follows:",
...Object.entries(gitmoji_list).map(( v, i )=> echo.format("%c"+v[0]+` (${v[1]}): ${conventional_desc[i]}`, "display: list-item")),
...Object.entries(git3moji_list).map(( v, i )=> echo.format("%c"+v[0]+` (${v[1]}): ${conventional_desc[i]}`, "display: list-item"))
])
.option("--format, -f", [ "Use one of the following formats to generate the commit message: [regular (default), conventional, gitmoji, git3moji]",
"For gitmoji see: https://gitmoji.dev/"
])
.action(async function({ format= "regular" }= {}){
const question= questionChatGPT(format);
const response= (await pipe(
()=> s.cat("./.git/COMMIT_EDITMSG"),
s=> s.slice(s.indexOf("diff --git")),
diffToChunks(3900-545), //the worst scenario of ★ +new lines
ch=> ch.map(pipe( question, requestCommitMessage )),
ch=> Promise.all(ch)
)())
.map(pipe(
j=> j.choices[0].text.trim(),
t=> t.match(/\[[^\]]*\]/) ?? convertToJSONArray(t),
JSON.parse,
format==="regular" ? i=> i : i=> gitmoji(i, format==="git3moji"),
a=> a.join("\n")
))
.join("\n\n");
echo(response);
$.exit(0);
})
.parse();
function diffToChunks(max_tokens){ return function(input){
if(input.length < max_tokens)
return [ input ];
return input.split(/(?=diff --git)/g)
.flatMap(function(input){
if(input.length < max_tokens)
return [ input ];
const [ file, ...diffs ]= input.split(/\n(?=@@)/g);
if(file.includes("new file"))
return [ file ];
return diffs
.filter(chunk=> chunk.length < max_tokens)
.reduce(function(chunks, chunk){
const i= chunks.length-1;
if(chunks[i].length + chunk.length < max_tokens-1)
chunks[i]+= "\n"+chunk;
else
chunks.push(file+"\n"+chunk);
return chunks;
}, [ file ])
.filter(chunk=> chunk.length < max_tokens);
});
}; }
function convertToJSONArray(text){
const arr= text.split("\n")
.filter(line=> line.trim().match(/^[1-3]\. /))
.map(line=> line.slice(3))
.map(line => line.startsWith('"') ? line : "\"" + ( "'`".slice("").includes(line[0]) ? line.slice(1, -1) : line ) + "\"");
return `[${arr.join(", ")}]`;
}
function questionChatGPT(format){ return function(diff){
const msg= [
[
"I would like to ask you to act like a git commit message writer.",
"I will enter a git diff, and your job is to convert it into a useful commit message and make 3 options as JSON array.",
"Do not preface the commit with anything, use a concise, precise, present-tense, complete sentence.",
"The length should be fewer than 50 characters if possible.",
].join(" ") //340chars★
];
if(format!=="regular")
msg.push(
[
"It should follow the conventional commits.",
"The format is <type in lowercase>: <description>.",
"A type can be one of the following: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, or test.",
].join(" ") //203chars★
);
msg.push("", diff);
return msg.join("\n");
}; }
function gitmoji(candidates, is_three= false){
return candidates.map(message=> message.trim().replace(/^[^:]*:/, toGitmoji));
function toGitmoji(name){
const candidate= ( is_three ? git3moji_list : gitmoji_list )[name.slice(0, -1)];
return !candidate ? name : `:${candidate}:`;
}
}
function requestCommitMessage(prompt){
if(!token) token= s.cat(token_file).stdout.trim();
const model= "text-davinci-003";
return fetch(`https://api.openai.com/v1/engines/${model}/completions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer "+token
},
body: JSON.stringify({
max_tokens: 1000,
temperature: 0.1,
prompt
}),
signal: AbortSignal.timeout(10000)
}).then(r=> r.json());
}
// vim: set tabstop=4 shiftwidth=4 textwidth=250 noexpandtab :
// vim>60: set foldmethod=indent foldlevel=1 foldnestmax=2:

26
bin/§awk Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
this="${0##*/}"
USAGE="\
usage: $this [<awk_args>] <field_no>
Ex: getent passwd | grep andy | $this -F: 5
Ex: echo \"A B\" | $this 2
"
err(){ echo -e "$USAGE" >&2; exit 1; }
[[ $# -eq 0 ]] && err
# bail if the *last* argument isn't a number (source:
# http://stackoverflow.com/a/808740)
last=${@:(-1)}
if ! [ $last -eq $last ] &>/dev/null; then
echo "_awk! Last argument (awk field) must be numeric." >&2
err
fi
if [ $# -gt 1 ]; then
# Source:
# http://www.cyberciti.biz/faq/linux-unix-bsd-apple-osx-bash-get-last-argument/
rest=${@:1:$(( $# - 1 ))}
else
rest='' # just to be sure
fi
awk $rest "{ print \$$last }"

34
bin/§battery Executable file
View File

@ -0,0 +1,34 @@
#!/bin/bash
version="2022-01-25"
this="${0##*/}"
USAGE="\
$this@v$version
Wrapper around 'upower' to show battery info(s).
Usage: $this --[help|all|oneline|notify]
'oneline' [Default] prints 'status | time | percentage'
'notify' sends 'oneline' to 'notify-send'
'all' prints 'upower -i'
'help' prints this text
"
arg=${1:---oneline}
if [[ "$arg" = "--help" ]]; then
echo -e "$USAGE"
exit 0
fi
batt_name=`upower -e | grep 'BAT'`
batt_info=`upower -i $batt_name`
if [[ "$arg" = "--all" ]]; then
echo -e "$batt_info"
exit 0
fi
batt_oneline=`echo "$batt_info" | grep -E "state|percentage|to\ full|to\ empty" | §awk -F: 2 | sed 's/^ *//g' | tr -s '\n' '|' | sed 's/|$/\n/' | sed 's/|/ | /g'`
if [[ "$arg" = "--oneline" ]]; then
echo -e "$batt_oneline"
exit 0
fi
icon=`echo "$batt_info" | grep "icon-name" | §awk -F\' 2`
notify-send --icon=$icon "Battery" "$batt_oneline"

6
bin/§calc Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
function _echo(){
[ -t 0 ] && echo "$1" && exit
notify-send -i kcalc "$1" && exit
}
_echo `python3 -c 'import sys; print(eval(" ".join(sys.argv[1:])))' "$*"`

60
bin/§cordova-release.mjs Executable file
View File

@ -0,0 +1,60 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, style, fetch, cyclicLoop */
import { join as pathJoin } from "path";
import { platform } from "process";
const config_path= $.xdg.data`package_global.json`;
$.is_fatal= true;
$.api("[name]", true)
.version("2022-10-06")
.describe([
"Release cordova app with saved data in: "+config_path+".",
"This should be JSON file with `cordova_keys_store` key/object:",
`{"cordova_keys_store": { "NAME": { "path": "", "password": "", "alias": "" } }}`,
"You can lists all saved options (NAME), when you run without arguments." ])
.option("--noclear", "Skipping cleaning existing apk files.")
.action(function main(name, { noclear }){
if(!name){
echo("Available options:");
pipe(getConfig, Object.keys, a=> "- "+a.join("\n- "), echo)();
$.exit(0);
}
const /* runtime arguments and cwd */
{ path, password, alias }= getConfigFor(name),
cwd= process.cwd(),
platform_android= toDirPath( cwd, "platforms", "android" ),
platform_build= !s.test("-e", toDirPath(platform_android, "app")) ? toDirPath(platform_android, "build") : toDirPath(platform_android, "app", "build"),
apk_dir= toDirPath(platform_build, "outputs", "apk"),
key_path= pathJoin(cwd, "keystore.jks"),
process_clear= !noclear && !s.test("-e", toDirPath(platform_android, "app"));
$.configAssign({ verbose: true, fatal: true });
if(process_clear) s.rm("-Rf", apk_dir+"*");
s.cp(path, key_path);
s.run("cordova" + ( platform==="win32" ? ".cmd" : "" ) + " ::args::",
{ args: [ "build", "--release", "android", "--",'--keystore=keystore.jks', "--storePassword="+password, "--password="+password, "--alias="+alias ] });
s.rm(key_path);// cordova si to uklada a uz potom bez nej nelze buildit vubec
s.rm(platform_android+"release-signing.properties");
$.exit(0);
})
.parse(process.argv);
function toDirPath(...path){ return pathJoin(...path)+"/"; }
function getConfigFor(name){
const config= getConfig();
if(Object.hasOwn(config, name))
return config[name];
$.error(`Name '${name}' not found, use one of: `+Object.keys(config));
}
function getConfig(){
if(!s.test("-f", config_path))
$.error("No config file found! Tested file path: "+config_path);
try{
const config= s.cat(config_path).xargs(JSON.parse).cordova_keys_store;
if(!Object.keys(config).length) throw new Error();
return config;
} catch(e){
$.error("Unsupported config file: "+config_path+"! Use `--help` for more information.");
}
}

61
bin/§extract Executable file
View File

@ -0,0 +1,61 @@
#!/bin/bash
this="${0##*/}"
this_version="2021-03-14"
while read; do printf '%s\n' "$REPLY"
done <<-EOF
$this ($this_version) <andrle.jan@centrum.cz>
Utility for extracting archives into folder with the same name.
EOF
err() {
printf >&2 "Error: $*\n"
exit 1
}
ARC="$1"
[[ ! -z "$ARC" ]] || ARC="--help"
if [[ "$ARC" = "--help" ]]; then
while read; do printf '%s\n' "$REPLY"
done <<-EOF
Usage: $this [file|--help|]
[--help|] - show this text
[file] - path to file for extracting
Supported formats (used utilities):
EOF
sed -n 42,52p $0 | sed -e 's/^/ /'
exit 0
fi
[[ -f $ARC ]] || err $"'$ARC' does not exist"
ARC_name_ext="${ARC##*/}"
ARC="$(readlink -f "$ARC")"
ARC_name="${ARC_name_ext%.*}"
mkdir "$ARC_name" || err $"Directory '$ARC_name' can not be created"
[[ -d $ARC_name ]] || err $"Directory '$ARC_name' does not exist"
[[ -w $ARC_name ]] || err $"Permission denied: '$ARC_name' is not writable"
cd "$ARC_name"
case "$ARC" in
*.tar.bz2) tar xjf "$ARC" ;;
*.tar.gz) tar xzf "$ARC" ;;
*.bz2) bunzip2 "$ARC" ;;
*.rar) unrar e "$ARC" ;;
*.gz) gunzip "$ARC" ;;
*.tar) tar xf "$ARC" ;;
*.tbz2) tar xjf "$ARC" ;;
*.tgz) tar xzf "$ARC" ;;
*.zip) unzip "$ARC" ;;
*.epub) unzip "$ARC" ;;
*.docx) unzip "$ARC" ;;
*.xmlx) unzip "$ARC" ;;
*.pptx) unzip "$ARC" ;;
*.Z) uncompress "$ARC" ;;
*.7z) 7z x "$ARC" ;;
*.eml) munpack -t "$ARC" ;;
*) err $"'$ARC' cannot be extracted by $this" ;;
esac
# sudo apt install mpack

94
bin/§mail.mjs Executable file
View File

@ -0,0 +1,94 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, style, fetch, cyclicLoop, xdg, $ */
import { basename } from "path";
const app= {
name: basename(process.argv[1]),
version: "2022-09-28",
cmd: $.xdg.home`.local/bin/himalaya`,
configs: $.xdg.config`himalaya/`,
modificator: "§"
};
const chars= { "&AOE-": "á", "&AWE-": "š", "&ARs-": "ě" };
let argv_arr= argvArr();
if("help"===argv_arr.toString().replaceAll("-", "")){ //#region
echo([ `${app.name}@${app.version}`,
`This is small wrapper around 'himalaya' fixing coding errors and provide better 'read'. (Use § for calling himalaya directly)`,
"" ].join("\n"));
s.run(app.cmd+" --help");
$.exit(0); //#endregion
}
if("version"===argv_arr.toString().replaceAll("-", "")){//#region
echo(`${app.name} ${app.version}`);
s.run(app.cmd+" --version");
$.exit(0);//#endregion
}
if("completion,bash"===argv_arr.toString()){//#region
const completion= s.run(app.cmd+" ::argv_arr::", { argv_arr });
echo(completion.toString().replace("himalaya)", `himalaya|${app.name})`));
echo(`alias ${app.name}-inbox="§mail § | less -R -S"`);
echo(`complete -F _himalaya -o bashdefault -o default ${app.name}`);
$.exit(0);//#endregion
}
(async function main(){
if(argv_arr.indexOf(app.modificator)!==-1) await runH(argv_arr.filter(l=> l!==app.modificator));
argv_arr= argv_arr.filter(str=> str!==app.modificator);
if(argv_arr.indexOf("list")!==-1){
argv_arr.push("-w", process.stdout.columns);
await runH(argv_arr);
}
if(argv_arr.indexOf("read")!==-1){
const email= $.xdg.temp`/himalaya-read.eml`;
argv_arr.push("-h", "From", "-h", "Subject");
await s.$().runA(app.cmd+" ::argv_arr::", { argv_arr }).pipe(s=> s.to(email));
await s.runA`vim ${email}`.pipe(process.stdout);
$.exit(0);
}
if(argv_arr[0] && argv_arr[0]!=="--rofi") await runH(argv_arr);
const template_path= app.configs+"template-inbox.json";
if(!s.test("-f", template_path)) await runH([]);
const out= await pipe(
f=> s.cat(f).xargs(JSON.parse),
argv_arr.indexOf('--rofi')===-1 ? templateRead : templateRofi,
a=> Promise.all(a)
)(template_path);
for(const l of out){
if(typeof l=="string"&&!l.indexOf("%c===\n")) echo(l, "unset:all", "color:magenta");
else echo(l);
}
$.exit(0);
})();
function templateRofi(lines){
return lines.filter(line=> line.type!=="text")
.map(line=>
s.$().runA(app.cmd+" ::value:: -w 120", line)
.then(data=> data.toString().split("\n")
.filter(l=> l)
.map(line=> line.replaceAll("✷ ", "* "))
.map(line_result=> line_result+" │ "+line.label)
.join("\n"))
);
}
function templateRead(lines){
argv_arr.push("-w", process.stdout.columns);
return lines.map(line=> line.type==="text" ?
Promise.resolve("%c===\n%c"+line.value) :
s.$().runA(app.cmd+" ::value:: ::argv_arr::", { value: line.value, argv_arr })
);
}
function argvArr(){
const _chars= Object.entries(chars).reduce((acc, [ key, val ])=> Reflect.set(acc, val, key) && acc, {});
return process.argv.slice(2).map(str=> str.replace(new RegExp(`(${Object.keys(_chars).join("|")})`, "g"), l=> _chars[l]));
}
async function runH(args){
const result= await s.runA(app.cmd+" ::args::", { args }).pipe(process.stdout);
$.exit(result.exitCode);
}
// vim: set tabstop=4 shiftwidth=4 textwidth=250 noexpandtab ft=javascript :
// vim>60: set foldmethod=marker foldmarker=#region,#endregion :

71
bin/§software Executable file
View File

@ -0,0 +1,71 @@
#!/bin/bash
this="${0##*/}"
this_version="2021-12-01"
err() { printf >&2 "Error: $*\n"; exit 1; }
log() { printf ":: $* ::\n"; }
if [ ! -t 0 ]; then
exo-open --launch TerminalEmulator -- -e "$this $*"
exit
fi
action="${1:---help}"
if [[ "$action" = "--help" ]]; then
while read; do printf '%s\n' "$REPLY"
done <<-EOF
$this ($this_version) <andrle.jan@centrum.cz>
Utility for updating my packages managers such as \`npm\`, \`apt\`, \`github-releases\` …
Usage: $this --[help|check|update] [basic|dangerous]
[default] --help: show this text
--check: check updates
--update: update all
--list: list of packages managers
modificators:
[default] basic: checks/updates regular packages managers
dangerous: ↘+also \`pip\`
EOF
exit 0
fi
if [[ "$action" = "--list" ]]; then
echo npm
echo github-releases.js
echo _vim_plugins updates only
echo snap updates only
echo flatpak updates only
echo pip [dangerous]
exit 0
fi
modificator="${2:-basic}"
if [[ "$action" = "--check" ]]; then
log "npm outdated --global (for update use: \`npm update --global\`)"
npm outdated --global
[[ "${?}" = "0" ]] && echo "> all up-to.date"
log "github-releases.js --check (for update use for example: \`github-releases.js --update all\`)"
github-releases.js --check
if [[ ! "$modificator" = "basic" ]]; then
log "pip list --outdated"
pip list --outdated
fi
exit 0
fi
if [[ "$action" = "--update" ]]; then
log "npm update --global"
npm update --global
log "github-releases.js --update all"
github-releases.js --update all
log "_vim_plugins --update"
_vim_plugins --update
log "snap refresh --list"
snap refresh --list
log "flatpak update"
flatpak update
if [[ ! "$modificator" = "basic" ]]; then
log "pip list --outdated --format=freeze | grep -v '^\\-e' | cut -d = -f 1 | xargs -n1 pip install -U --user"
pip list --outdated --format=freeze | grep -v '^\-e' | cut -d = -f 1 | xargs -n1 pip install -U --user
fi
exit 0
fi
err "Wrong arguments (use \`--help\`)"

22
bin/§trans.mjs Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 8,-W097, -W040, node: true, expr: true, undef: true *//* global echo, $, pipe, s, style, fetch, cyclicLoop */
$.is_fatal= true;
$.api("<lang>")
.version("2022-09-23")
.describe("This is just wrapper around 'trans' cli utility.")
.option("-b", "Turn off '-b' parametrer for 'trans' which is give by this script.")
.option("-c", "Redirect into clipboard ('| xclip -selection clipboard')")
.example("2en pes → 'trans cs:en -b pes'")
.example("en -b dog → 'trans en:cs dog'")
.action(function main(lang, { _: query, c: to_clipboard, b: to_long }){
query= [ `"${query.join(" ").replaceAll('"', '\\"')}"` ];
if(!to_long) query.unshift('-b');
query.unshift(lang[0]==="2" ? lang.replace("2", "cs:") : ( lang.indexOf(":")!==-1 ? lang : lang+":cs" ));
query.push('-no-ansi');
const result= s.$().run("trans "+query.join(" "));
if(!to_clipboard) return echo(result.toString());
result.runA("xclip -selection clipboard 2> /dev/null 1> /dev/null")
.then($.exit.bind(null, 0)).catch($.exit.bind(null, 1));
})
.parse(process.argv);

58
bin/§ubuntu-info Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
this="${0##*/}"
this_version="2021-01-22"
USAGE="\
$this@v$this_version
Wrapper around '/etc/os-release' to show Ubuntu (like) os info.
Usage: $this --[help|all|raw]
'all' [default] prints all infos
'pick' prints only given key (default key is 'DESCRIPTION')
'raw' cats '/etc/os-release'
'help' prints this text
Examples:
$this --all
$this --pick NAME
$this --pick VERSION_NAME
$this --all | grep NAME
"
arg=${1:---all}
if [[ "$arg" = "--help" ]]; then
echo -e "$USAGE"
exit 0
fi
if [[ "$arg" = "--raw" ]]; then
cat /etc/os-release
exit 0
fi
. /etc/os-release
out="ID=$ID"
out="${out}\nDESCRIPTION=$PRETTY_NAME"
case "$ID" in
neon) out="${out}\nNAME=$NAME (${VARIANT:-User Edition})"
out="${out}\nVERSION=$VERSION ($VERSION_ID)"
;;
*) out="${out}\nNAME=$NAME"
out="${out}\nVERSION=$VERSION"
;;
esac
out="${out}\nVERSION_NICK=$UBUNTU_CODENAME"
codename=`grep $(lsb_release -rs) /usr/share/python-apt/templates/Ubuntu.info | grep -m 1 "Description: Ubuntu " | cut -d "'" -f2`
out="${out}\nVERSION_NAME=$codename"
# http://www.releases.ubuntu.com/jammy/
out="${out}\nLIKE=$ID_LIKE"
out="${out}\n`grep URL /etc/os-release | sed 's/^\([A-Z_]*\)_URL/URL_\1/'`"
if [[ "$arg" = "--all" ]]; then
echo -e "$out"
exit 0
fi
if [[ "$arg" = "--pick" ]]; then
echo -e `echo -e "$out" | grep "\b${2:-DESCRIPTION}\b" | cut -d = -f 2-`
exit 0
fi
echo "Wrong argument, see '--help'."
exit 1

3
bin/§vim_cache_clean Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
find ~/.vim/undodir/ -maxdepth 1 -mindepth 1 -type f -mtime +180 -delete
find ~/.vim/view/ -maxdepth 1 -mindepth 1 -type f -mtime +180 -delete

281
bin/§vim_plugins.mjs Executable file
View File

@ -0,0 +1,281 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 11,-W097, -W040, module: true, node: true, expr: true, undef: true *//* global echo, $, pipe, s, fetch, cyclicLoop */
//TODO: save options!?
$.is_fatal= true;
const dirs= { vim_root: $.xdg.home`.vim` };
Object.assign(dirs, {
pack: dirs.vim_root+"/pack/",
bundle: dirs.vim_root+"/bundle/",
one_files: dirs.vim_root+"/bundle/__one_files/plugin/" });
const file_one_file= dirs.bundle+"one_file.json";
const runResToArr= pipe( s.$().run, ({ stderr, stdout })=> stdout+stderr, o=> o.split("\n"));
const css= echo.css`
.code{ color: yellow; }
.code::before, .code::after{ content: "\`"; }
.url{ color: lightblue; }
.bold{ color: magenta; }
`;
$.api()
.version("2023-04-18")
.describe([
"Utility for managing vim plugins “native” way. It uses two types:",
`- “old” way (${f("bundle", css.code)}): inspiration from ${f("https://shapeshed.com/vim-packages/", css.url)}`,
`- vim8 native package managing (${f("pack", css.code)}): see for example ${f("https://shapeshed.com/vim-packages/", css.url)}`
])
.command("path <type>", [ "Prints paths for given package type",
"Use (S is alias for this script name):",
"- "+f("cd `S path bundle`", css.code),
"- "+f("cd `S path one_files`", css.code),
"- "+f("cd `S path pack`", css.code)
])
.action(function(type){ echo(dirs[type]); $.exit(0); })
.command("clone <type> <url>", [ "Add/install new package.",
`Use ${f("bundle", css.code )}/${f("pack", css.code)} to specify the package ${f("type", css.code)}.`,
`The ${f("url", css.url)} should be a URL to the script itself or url of the git repository or github repository in the form of ${f("username/reponame", css.url)}.`
])
.alias("C")
.option("--target, -t [target]", `In case of ${f("pack", css.code)} type, specify the target sub-directory (typically/defaults ${f("start", css.code)}).`)
.option("--branch, -b [branch]", `In case of ${f("git", css.bold)} repository, specify the branch if it is different from default one.`)
.action(function(type, url, options){
switch(type){
case "bundle": return addBundle(url);
case "pack": return addPack(url, options);
}
echo("Nothing todo, check given arguments (compare to `--help`):", { type, url, options });
$.exit(1);
})
.command("remove <type> <path>", [ "Remove/uninstall package.",
`As ${f("type", css.bold)}/${f("path", css.bold)} use output printed by ${f("list", css.code)} command.`
])
.alias("R").alias("rm")
.action(function(type, path){
switch(type){
case "bundle": return removeBundle(path);
case "pack": return removePack(path);
}
echo("Nothing todo, check given arguments (compare to `--help`):", { type, path });
$.exit(1);
})
.command("list", "List all plugins paths/url/… (based on option).")
.alias("L").alias("ls")
.option("--type, -t [type]", `Defaults to list of paths (${f("paths", css.code)}). Use ${f("repos", css.code)} to show plugins origin.`)
.example("list")
.example("list --type paths")
.example("list --type repos")
.action(actionList)
.command("export", "List all plugins in the form that can be imported by this Utility.")
.action(actionList.bind(null, { type: "json" }))
.command("status", "Loops through all installed plugins and shows overall status.")
.alias("S")
.action(actionStatus)
.command("pull", "Loops through all installed plugins and updates them.")
.alias("P").alias("update")
.action(actionUpdate)
.parse();
function removePack(path){
s.cd(dirs.pack);
s.$("-V").rm("-rf", path);
const root= dirs.pack+path.split("/")[0];
const { code, stdout }= s.$().find(root+"/*/*");
if(!code) echo(stdout);
else if(s.test("-d", root))
s.$("-V").rm("-rf", root);
$.exit(0);
}
function removeBundle(path){
const is_onefile= dirs.one_files.endsWith(path.split("/").slice(0, 2).join("/")+"/");
const name= path.slice(path.lastIndexOf("/")+1);
s.cd(dirs.bundle);
if(is_onefile){
s.rm("-f", path);
pipe( s.cat, JSON.parse, f=> f.filter(u=> !u.endsWith(name)), JSON.stringify, s.echo )
(file_one_file).to(file_one_file);
} else {
s.run`git submodule deinit -f ${path}`;
s.run`git rm ${path}`;
s.rm("-rf", ".git/modules/"+path);
}
const type= is_onefile ? "file" : "repo";
s.run`git commit --all -m "Remove ${type}: ${name}"`;
$.exit(0);
}
async function addBundle(url){
const is_onefile= url.endsWith(".vim");
if(!is_onefile)
url= gitUrl(url);
const name= url.split(/[\/\.]/g).at(is_onefile ? -2 : -1);
s.cd(dirs.bundle);
if(is_onefile){
const file= await fetch(url).then(r=> r.text());
s.echo(file).to(dirs.one_files+name+".vim");
const log= new Set(s.cat(file_one_file).xargs(JSON.parse));
log.add(url);
s.echo(JSON.stringify([ ...log ])).to(file_one_file);
} else {
s.run`git submodule init`;
s.run`git submodule add ${url}`;
}
s.run`git add .`;
const type= is_onefile ? "file" : "repo";
s.run`git commit -m "Added ${type}: ${name}"`;
$.exit(0);
}
/** @param {string} url @param {{ target: "start", branch?: string }} options */
function addPack(url, { target= "start", branch }){
url= gitUrl(url);
const author= url.split(/[:\/]/).at(-2);
const path= dirs.pack+author+"/"+target;
s.mkdir("-p", path);
s.cd(path);
branch= !branch ? "" : `-b ${branch}`;
s.run`git clone ${branch} --single-branch ${url} --depth 1`;
$.exit(0);
}
function gitUrl(url_candidate){
if(url_candidate.endsWith(".git"))
return url_candidate;
return "git@github.com:"+url_candidate;
}
async function actionUpdate(){
const css= echo.css`
.success{ color: lightgreen; }
.success::before{ content: "✓ "; }
`;
updateRepo(dirs.bundle, getBundle());
const todo= getOneFilesUrls();
const progress= echoProgress(todo.length, "Downloaded one-file plugins");
await Promise.all(todo.map(function(url, i){
return fetch(url).then(r=> {
progress.update(i, url);
return r.text();
}).then(f=> s.echo(f).to(dirs.one_files+fileName(url)));
}));
echo("One-file plugin(s) updated.");
s.cd(dirs.bundle).$().run`git commit -m "Update"`;
updateRepo(dirs.pack, getPack());
$.exit(0);
function updateRepo(dir, paths){
echo(dir);
const progress= echoProgress(paths.length, "Pulling");
const todo= paths.map(function(p, i){
progress.update(i, p);
return pull(p);
}).filter(isUpToDate);
if(!todo.length)
return echo("%cAll up-to-date!", css.success);
todo.forEach(([ p, result ])=> echo("%c"+p+"\n", css.success, result.join("\n")));
}
function pull(p){
s.cd(p);
return [ p, runResToArr("git pull") ];
}
function isUpToDate([ _, result ]){
return result[0]===" Already up-to-date.";
}
}
function actionList({ type= "paths" }){
if("paths"===type){
echo("%cbundle", css.bold, dirs.bundle);
getOneFiles().forEach(echoPath);
getBundle().forEach(echoPath);
echo("%cpack", css.bold, dirs.pack);
getPack().forEach(echoPath);
$.exit(0);
}
const progress= echoProgress(3, "Collecting plugins urls");
progress.update(0, dirs.bundle);
const urls_bundle= getBundle().map(getRepo);
progress.update(1, dirs.bundle);
const urls_onefiles= getOneFilesUrls();
progress.update(2, dirs.pack);
const urls_pack= getPack().map(getRepo);
if("repos"===type){
const echoUrl= pipe(
u=> u.replace("git@github.com:", "https://github.com/"),
u=> f(u, css.url),
echo
)
echo("%cbundle", css.bold, dirs.bundle);
urls_bundle.forEach(echoUrl);
echo("%cbundle", css.bold, dirs.one_files);
urls_onefiles.forEach(echoUrl);
echo("%cpack", css.bold, dirs.pack);
urls_pack.forEach(echoUrl);
}
if("json"===type){ //TODO: save options!?
const o= {};
o.bundle= urls_bundle;
o.one_files= urls_onefiles;
o.pack= urls_pack;
echo(JSON.stringify(o));
}
$.exit(0);
function getRepo(p){ s.cd(p); return runResToArr("git remote -v")[0].split(/\s+/g)[1]; }
}
function actionStatus(){
const css= echo.css`
.success { color: lightgreen; }
.success::before { content: "✓ "; }
`;
check(dirs.bundle, getBundle());
echo("Onefiles plugins are not supported yet");
check(dirs.pack, getPack());
$.exit(0);
function check(root, repos){
echo(root);
const progress= echoProgress(repos.length);
const results= repos.flatMap(function(p, i){
progress.update(i, p);
s.cd(p);
const result= runResToArr("git fetch --dry-run --verbose")
.filter(l=> !l ? false : l.startsWith("From") || (!l.startsWith(" = [up-to-date]") && !l.startsWith("POST") ));
if(result.length===1) return [];
return [ [ p, result.join("\n") ] ];
});
if(!results.length)
return echo("%cup-to-date", css.success);
results.forEach(([ p, l ])=> {
echoPath(p);
echo(l);
});
}
}
import { relative } from 'node:path';
function echoPath(path){ return echo(formatPath(path)); }
function formatPath(path){
const type= path.startsWith(dirs.bundle) ? "bundle" : "pack";
return echo.format("%c"+relative(dirs[type], path), "color:lightblue");
}
import { stdout } from 'node:process';
function echoProgress(length, message_start= "Working"){
if(typeof stdout.isTTY === "undefined") return { update(){} };
const css= echo.css`
.progress { color: lightblue; }
`;
echo.use("-R", `${message_start} (%c0/${length}%c)`, css.progress);
return {
update(i, status){
const s= status ? `: ${status}` : "";
return echo.use("-R", `${message_start} (%c${i+1}/${length}%c)${s}`, css.progress);
}
};
}
function getPack(){ return s.ls(dirs.pack).flatMap(f=> s.find(dirs.pack+f+"/start/*/")[0]).filter(Boolean); }
function getBundle(){ return s.cd(dirs.bundle).grep("path", ".gitmodules").split("\n").filter(Boolean).map(l=> dirs.bundle+l.split(" = ")[1]); }
function getOneFiles(){ return s.find(dirs.one_files+"*"); }
function getOneFilesUrls(){ return s.cat(file_one_file).xargs(JSON.parse); }
function fileName(url){ return url.split("/").pop(); }
/** Quick formating of one piece of text. */
function f(text, css){ return echo.format("%c"+text, css); }

44
bin/§wallpaper_BIOTD.mjs Executable file
View File

@ -0,0 +1,44 @@
#!/usr/bin/env node
/* jshint esversion: 6,-W097, -W040, browser: true, expr: true, undef: true */
import { readFileSync, writeFileSync, createWriteStream } from "fs";
import { get } from "https";
import { homedir } from "os";
const url_main= "https://www.bing.com";
const folder= homedir()+"/Obrázky/Bing Image Of The Day/";
get_(url_main+"/HPImageArchive.aspx?format=js&idx=0&n=2&mkt=cs-CZ")
.then(res=> {
let body= "";
res.on("data", chunk=> body+= chunk);
res.on("end", ()=> pipe(data, update)(body));
})
.catch(e=> console.error(String(e)));
function update(data){
if(data===null) return false;
Promise.allSettled(data.map(({ url, copyright }, id)=> getImage_(url, id ? "prev" : "now", copyright)))
.then(res=> {
let template= readFileSync(folder+"index_template.html").toString();
writeFileSync(folder+"index.html", res.reduce((out, { status, value })=> status==="rejected" ? out : out.replace(`::${value.name}::`, value.description), template));
})
.catch(e=> console.error(String(e)));
}
function getImage_(url, name, desc){
const description= desc.replace("(©", "<br>(©");
return get_(url_main+url)
.then(res=> {
const fs= createWriteStream(folder+name+'.jpg');
res.pipe(fs);
return new Promise(res=> fs.on("finish", ()=> {
fs.close();
res({ name, description });
}));
});
}
function data(body){
try { return JSON.parse(body).images; }
catch (_) { return null; }
}
function get_(url){ return new Promise(function(res,rej){ get(url, res).on("error", rej); }); }
function pipe(...f){ return Array.prototype.reduce.bind(f, (acc, f)=> f(acc)); }

20
bin/§wolframalpha.mjs Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env nodejsscript
/* jshint esversion: 8,-W097, -W040, node: true, expr: true, undef: true *//* global echo, $, pipe, s, style, fetch, cyclicLoop */
$.is_fatal= true;
$.api("<...query_array>", true)
.version("v2022-09-23")
.describe([
"This is just redirection to [WolframAlpha](https://www.wolframalpha.com/) site.",
"Use the same expressions as on web page."
])
.example("linear fit {1.3, 2.2},{2.1, 5.8},{3.7, 10.2},{4.2, 11.8}")
.example("polynomial fit {1,2},{2,3.1},{3,3.9}")
.example("Fit[{{1,2},{2,3.1},{3,3.9}}, {1,x}, x]")
.action(function main(first, { _: query_array= [] }){
query_array.unshift(first);
echo("Opening:");
echo("https://www.wolframalpha.com/input/?i="+encodeURI(query_array.join(" ")).replace(/\+/g, '%2B'))
.xargs(s.runA, "exo-open --launch WebBrowser {}")
.then($.exit.bind(null, 0)).catch($.exit.bind(null, 1));
})
.parse();