Last active
January 26, 2022 04:18
-
-
Save Shados/19c34d5e92218f444f98b51a7cf5446d to your computer and use it in GitHub Desktop.
Script-directory watcher/pusher for the Steam version of Bitburner
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env nix-shell | |
#!nix-shell -i bash -p bash gnused jq coreutils curl inotify-tools | |
# vim: set ft=sh : | |
set -eEo pipefail | |
shopt -s inherit_errexit nullglob | |
set -u | |
SCRIPTNAME=$(basename "$0") | |
# Set the DRY_RUN env var when running the command if you just want it to print | |
# what it will do, instead of actually doing anything | |
if [[ -v DRY_RUN ]]; then | |
DRY_RUN_CMD="dry_run" | |
else | |
DRY_RUN_CMD="" | |
fi | |
BB_PORT=9990 | |
BB_URL=localhost | |
# Oddly, .txt files aren't allowed, even though you can create them in game. | |
ALLOWED_FILETYPES=("js" "ns" "script") | |
ALLOWED_FILETYPES_PATTERN="" | |
declare -A ALLOWED_FILETYPES_HASH | |
for ft in "${ALLOWED_FILETYPES[@]}"; do | |
ALLOWED_FILETYPES_HASH["$ft"]=1 | |
ALLOWED_FILETYPES_PATTERN="${ALLOWED_FILETYPES_PATTERN:+${ALLOWED_FILETYPES_PATTERN}|}$ft" | |
done | |
ALLOWED_FILETYPES_PATTERN=".*?\.($ALLOWED_FILETYPES_PATTERN)\$" | |
function main { | |
check_parameters "$@" | |
local script_dir="$(realpath "$2")" | |
# Do an initial sync of all files in the script directory | |
push_script_dir "$script_dir" | |
# Watch the script directory, and push files on writes | |
watch_script_dir "$script_dir" | |
} | |
function check_parameters { | |
if [ $# -ne 2 ]; then | |
usage | |
exit 1 | |
fi | |
AUTH_TOKEN=$(cat "$1") | |
if [[ "${#AUTH_TOKEN}" -ne 64 ]]; then | |
usage | |
printf "\n\nAUTH_TOKEN_FILE does not appear to be contain the token (does not contain a 64-character string)!\n" | |
exit 1 | |
fi | |
} | |
function usage { | |
printf "Usage: %s AUTH_TOKEN_FILE SCRIPT_DIRECTORY\n" "$SCRIPTNAME" | |
printf "\tAUTH_TOKEN_FILE is the path containing your bitburner API Server auth token\n" | |
printf "\tSCRIPT_DIRECTORY is the directory of files you want to watch and push to bitburner\n" | |
} | |
function push_script_dir { | |
local script_dir="$1" | |
log "Doing initial push of %q\n" "$script_dir..." | |
find "$script_dir" -type f -regextype egrep -regex "$ALLOWED_FILETYPES_PATTERN" -print0 | while IFS= read -r -d $'\0' file_path; do | |
push_file "$script_dir" "$file_path" | |
done | |
log "Initial push complete\n" | |
} | |
function watch_script_dir { | |
log "Starting to watch %q for changes...\n" "$script_dir" | |
local script_dir="$1" | |
inotifywait --monitor --event close_write --format '%w%f%0' --no-newline -r "$script_dir" | while IFS= read -r -d $'\0' file_path; do | |
if file_is_allowed "$file_path"; then | |
push_file "$script_dir" "$file_path" | |
fi | |
done | |
} | |
function file_is_allowed { | |
local file_path="$1" | |
local ext="${file_path##*.}" | |
return $([[ -v ALLOWED_FILETYPES_HASH["$ext"] ]]) | |
} | |
function push_file { | |
local script_dir="$1" | |
local file_path="$2" | |
# Strip the base directory from the file path | |
local filename="${file_path#"$script_dir/"}" | |
log "Pushing file %q\n" "$filename" | |
# Prepare the payload | |
if [[ $(dirname "$filename") != "." ]]; then | |
# If the file is going to be in a subdirectory, it NEEDS the leading `/`, | |
# i.e. `/my-dir/file.js`. | |
# If the file is standalone, it CAN NOT HAVE a leading slash, i.e. | |
# `file.js`. | |
filename="/$filename" | |
fi | |
# base64 encode the file and chuck it into a temporary file, as a JSON string | |
local code_tmpfile="$SCRIPT_TMPDIR/code_b64.json" | |
printf '"' >"$code_tmpfile" | |
base64 -w 0 "$file_path" >>"$code_tmpfile" | |
printf '"\n' >>"$code_tmpfile" | |
# Prepare the JSON payload | |
local payload_tmpfile="$SCRIPT_TMPDIR/payload.json" | |
jq -c --null-input \ | |
--exit-status \ | |
--arg filename "$(sanitise_path "$filename")" \ | |
--argfile code "$code_tmpfile" \ | |
'{ filename: $filename, code: $code }' >"$payload_tmpfile" | |
# Push the file | |
$DRY_RUN_CMD curl \ | |
--request POST \ | |
--header "Content-Type: application/json" \ | |
--header "Authorization: Bearer $AUTH_TOKEN" \ | |
--data "@$payload_tmpfile" \ | |
--verbose \ | |
"http://$BB_URL:$BB_PORT/" >/dev/null 2>/dev/null | |
# TODO handle HTTP return codes | |
rm "$code_tmpfile" "$payload_tmpfile" | |
} | |
function sanitise_path { | |
# NOTE: You can upload files with spaces in the path, but you can't actually | |
# `vim` or `rm` them in-game, as there's no way to escape or quote the ' ' | |
# character in the game's very limited terminal. So instead we replace spaces | |
# with -. | |
printf "%s" "$1" | sed -e 's| |-|g' | |
} | |
function dry_run { | |
printf "%s" "$1" | |
shift | |
for arg in "$@"; do | |
printf " %q" "$arg" | |
done | |
printf "\n" | |
} | |
function log { | |
printf "[%s] " "$(date -Ins)" | |
printf "$@" | |
} | |
function cleanup { | |
rm -rf "$1" | |
} | |
SCRIPT_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/$SCRIPTNAME.XXXXXXXX") | |
trap "cleanup $SCRIPT_TMPDIR" SIGINT SIGTERM ERR EXIT | |
main "$@" |
A word to anyone using this: bitburner's file-upload API is pretty glitchy. Under some circumstances it will just kinda crap out and valid attempts to push files over it will just hang, until you restart bitburner outright.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
If you don't have Nix, you'll need to replace the shebang lines with
#!/usr/bin/env bash
and then ensure all packages listed after the-p
in the second shebang line are available on$PATH
. Most likely this will just mean installing thejq
andinotify-tools
packages for your distro.Licensed under either of Apache License, Version 2.0 or MIT license at your option.