#!/bin/sh # surplus on wheels (s+ow): a pure shell script to run surplus with mdtest using the termux-api # shellcheck disable=SC2059 LOCATION_FALLBACK="%d%d%d\nSingapore?" # shellcheck disable=SC2269 LOCATION_PRIORITISE_NETWORK="$LOCATION_PRIORITISE_NETWORK" LOCATION_TIMEOUT=${LOCATION_TIMEOUT:-50} # shellcheck disable=SC2269 JID_NOMINAL_TARGET="$JID_NOMINAL_TARGET" # shellcheck disable=SC2269 JID_ERRORED_TARGET="$JID_ERRORED_TARGET" MDTEST_BIN="$HOME/.local/bin/mdtest" MDTEST_DIR="$HOME/.local/share/mdtest" SPOW_CACHE_DIR="$HOME/.cache/s+ow" # per-tool session logs SPOW_NETLC_OUT="$SPOW_CACHE_DIR/location.net.json" SPOW_GPSLC_OUT="$SPOW_CACHE_DIR/location.gps.json" SPOW_LOCTN_OUT="$SPOW_CACHE_DIR/location.json" SPOW_SPLUS_OUT="$SPOW_CACHE_DIR/surplus.out.log" SPOW_SPLUS_ERR="$SPOW_CACHE_DIR/surplus.err.log" # per-session collated logs SPOW_SESH_OUT="$SPOW_CACHE_DIR/out.log" SPOW_SESH_ERR="$SPOW_CACHE_DIR/err.log" # per-week collated logs SPOW_WEEK_PRE="$SPOW_CACHE_DIR/$(date +%Y)W$(date +"%V")" SPOW_WEEK_OUT="$SPOW_WEEK_PRE.out.log" SPOW_WEEK_ERR="$SPOW_WEEK_PRE.err.log" # last successful surplus output SPOW_LAST_OUT="$SPOW_CACHE_DIR/last" # list of fakes # shellcheck disable=SC2034 SPOW_FAKE_OUT="$SPOW_CACHE_DIR/fake" # check for network location priority if [ "$LOCATION_PRIORITISE_NETWORK" = "n" ]; then LOCATION_PRIORITISE_NETWORK="" fi # check for cron status if [ "$SPOW_CRON" = "n" ]; then SPOW_CRON="" fi # ensure commands exist if ! command -v termux-location >/dev/null 2>&1; then printf "s+ow: error: termux-location is not installed.\ninstall it with 'pkg install termux-api' and with installing the termux:api app from the play store or f-droid.\n" exit 1 fi if ! command -v surplus >/dev/null 2>&1; then printf "s+ow: error: surplus is not installed.\ninstall it with 'pip install https://github.com/markjoshwel/surplus/releases/latest/download/surplus-latest-py3-none-any.whl'\n" exit 1 fi if ! command -v ~/.local/bin/mdtest >/dev/null 2>&1; then printf "s+ow: error: mdtest is not installed.\ninstall it by getting a release or building it from source from https://github.com/markjoshwel/whatsmeow-termux\n" exit 1 fi # ensure directories mkdir -p "$SPOW_CACHE_DIR" "$MDTEST_DIR" # create new session logs rm -f "$SPOW_LOCTN_OUT" "$SPOW_SPLUS_OUT" "$SPOW_SPLUS_ERR" \ "$SPOW_SESH_OUT" "$SPOW_SESH_ERR" touch "$SPOW_NETLC_OUT" "$SPOW_GPSLC_OUT" "$SPOW_LOCTN_OUT" \ "$SPOW_SPLUS_OUT" "$SPOW_SPLUS_ERR" \ "$SPOW_SESH_OUT" "$SPOW_SESH_ERR" \ "$SPOW_WEEK_OUT" "$SPOW_WEEK_ERR" status=0 # 0 is nominal # 1 is an termux-location error # 2 is a surplus error # helper functions locate() { # spawn termux-location processes ( termux-location -p "network" >"$SPOW_NETLC_OUT" if [ -s "$SPOW_NETLC_OUT" ]; then printf "net" | tee -a "$SPOW_SESH_ERR" else printf "net?" | tee -a "$SPOW_SESH_ERR" fi cat "$SPOW_NETLC_OUT" >>"$SPOW_SESH_OUT" ) & tl_net_pid="$!" sleep 1 ( termux-location -p "gps" >"$SPOW_GPSLC_OUT" if [ -s "$SPOW_GPSLC_OUT" ]; then printf "gps" | tee -a "$SPOW_SESH_ERR" else printf "gps?" | tee -a "$SPOW_SESH_ERR" fi cat "$SPOW_GPSLC_OUT" >>"$SPOW_SESH_OUT" ) & tl_gps_pid="$!" # wait until timeout or both finished printf "running termux-location" | tee -a "$SPOW_SESH_ERR" while [ "$LOCATION_TIMEOUT" -gt 0 ]; do # get process statuses kill -0 "$tl_net_pid" >/dev/null 2>&1 tl_net_status="$?" kill -0 "$tl_gps_pid" >/dev/null 2>&1 tl_gps_status="$?" # break if both finished if [ "$tl_net_status" -eq 1 ] && [ "$tl_gps_status" -eq 1 ]; then break fi # exception: if network is proritised: just use that if [ "$tl_net_status" -eq 1 ] && [ -n "$LOCATION_PRIORITISE_NETWORK" ]; then # break only if theres an actual response if [ -s "$SPOW_NETLC_OUT" ]; then break fi # else just keep on waiting for gps to finish fi sleep 1 printf "." | tee -a "$SPOW_SESH_ERR" LOCATION_TIMEOUT=$((LOCATION_TIMEOUT - 1)) done if [ "$LOCATION_TIMEOUT" -eq 0 ]; then printf " errored (timeout)\n" | tee -a "$SPOW_SESH_ERR" else printf " nominal\n" | tee -a "$SPOW_SESH_ERR" fi # check outputs printf "determining output: " | tee -a "$SPOW_SESH_ERR" if [ -s "$SPOW_NETLC_OUT" ] && [ -s "$SPOW_GPSLC_OUT" ]; then printf "both succeeded, " acc_net="$(grep "\"accuracy\"" <"$SPOW_NETLC_OUT" | awk -F ': ' '{print $2}' | tr -d ',')" acc_gps="$(grep "\"accuracy\"" <"$SPOW_GPSLC_OUT" | awk -F ': ' '{print $2}' | tr -d ',')" # compare accuracy if awk -v n1="$acc_net" -v n2="$acc_gps" 'BEGIN { if (n1 < n2) exit 0; else exit 1; }'; then printf "choosing network (%s < %s)" "$acc_net" "$acc_gps" | tee -a "$SPOW_SESH_ERR" cat "$SPOW_NETLC_OUT" >"$SPOW_LOCTN_OUT" else printf "choosing gps (%s < %s)" "$acc_gps" "$acc_net" | tee -a "$SPOW_SESH_ERR" cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT" fi cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT" else # one or none succeeded if [ -s "$SPOW_NETLC_OUT" ]; then if [ -n "$LOCATION_PRIORITISE_NETWORK" ]; then printf "using network (prioritised)" | tee -a "$SPOW_SESH_ERR" else printf "using network" | tee -a "$SPOW_SESH_ERR" fi cat "$SPOW_NETLC_OUT" >"$SPOW_LOCTN_OUT" fi if [ -s "$SPOW_GPSLC_OUT" ]; then printf "using gps" | tee -a "$SPOW_SESH_ERR" cat "$SPOW_GPSLC_OUT" >"$SPOW_LOCTN_OUT" fi fi if [ ! -s "$SPOW_LOCTN_OUT" ]; then printf "none (error)" | tee -a "$SPOW_SESH_ERR" fi printf "\n" | tee -a "$SPOW_SESH_ERR" } gensharetext() { surplus -td "$1" >"$SPOW_SPLUS_OUT" 2>"$SPOW_SPLUS_ERR" ret="$?" cat "$SPOW_SPLUS_OUT" >>"$SPOW_SESH_OUT" cat "$SPOW_SPLUS_ERR" >>"$SPOW_SESH_ERR" return "$ret" } send() { (cd "$MDTEST_DIR" && "$MDTEST_BIN" send "$1" "$2") } notify_start() { termux-notification \ --priority "min" \ --ongoing \ --id "s+ow" \ --title "surplus on wheels" \ --content "s+ow has started running." } notify() { # $1 is text # $2 is attempt number (if any) attempt_text="$1" if [ $# -eq 2 ]; then attempt_text="$1 (attempt $2)" fi termux-notification \ --priority "min" \ --ongoing \ --id "s+ow" \ --title "surplus on wheels" \ --content "$attempt_text" } notify_end() { # $1 is done tuple # $2 is sharetext termux-notification \ --priority "min" \ --id "s+ow" \ --title "surplus on wheels" \ --content "$(printf 'Run has finished. %s\n\n%s' "$1" "$2")" } # program functions mdtest() { (cd "$MDTEST_DIR" && "$MDTEST_BIN") } run() { notify_start printf "[run! stdout (%s)]\n" "$(date)" >>"$SPOW_SESH_OUT" printf "[run! stderr (%s)]\n" "$(date)" >>"$SPOW_SESH_ERR" time_run_start="$(date +%s)" # if cron: wait until its the new hour if [ -n "$SPOW_CRON" ]; then notify "Waiting for the 30th second to pass..." printf "waiting for the 30th second to pass...\n" while [ "$(date +'%S')" -lt 30 ]; do printf " $(date)\n" sleep 1 done printf "proceeding\n" fi time_locate_start="$(date +%s)" # termux-location location="" for locate_run in 1 2 3; do # run three times in case :p notify "Running termux-location" "$locate_run" if [ "$locate_run" -gt "1" ]; then LOCATION_TIMEOUT=75 locate else locate fi if [ ! -s "$SPOW_LOCTN_OUT" ]; then # erroneous: is empty echo "s+ow: error: failed to get location" | tee -a "$SPOW_SESH_ERR" status=1 else # nominal: is not empty location="$(cat "$SPOW_LOCTN_OUT")" status=0 break fi done time_locate_end="$(date +%s)" time_locate=$((time_locate_end - time_locate_start)) time_surplus_start="$(date +%s)" # surplus printf "running surplus... " notify "Running surplus -td $location" if [ "$status" -eq 0 ]; then if gensharetext "$location"; then # surplus ran nominally cp "$SPOW_SPLUS_OUT" "$SPOW_LAST_OUT" status=0 printf "nominal\n" else # something happened :^) status=2 printf "errored\n" fi else printf "skipped\n" fi time_surplus_end="$(date +%s)" time_surplus=$((time_surplus_end - time_surplus_start)) # if cron: wait until its the new hour if [ -n "$SPOW_CRON" ]; then notify "Waiting for the new hour..." printf "waiting until the new hour...\n" while [ "$(date +'%M')" -eq 58 ]; do printf " $(date)\n" sleep 1 done printf "proceeding\n" fi time_sendmsg_start="$(date +%s)" # mdtest/send message printf "sending message(s)... " notify "Sending message(s)" sent_type=0 # 0 for freshly made sharetext # 1 for recycling a last location # 2 for using fallback template sharetext="" if [ "$status" -eq 0 ]; then # s+ow has behaved nominally until now, send as per normal sharetext="$(cat "$SPOW_SPLUS_OUT")" printf "\n" send "$JID_NOMINAL_TARGET" "$sharetext" else # something has gone wrong, send an appropriate fallback sharetext="" if [ -s "$SPOW_LAST_OUT" ]; then # use last successful location sharetext="$(cat "$SPOW_LAST_OUT")" sent_type=1 printf "using last...\n" else # no last location, use fallback # shellcheck disable=SC2059 sharetext="$(printf "$LOCATION_FALLBACK" "$status" "$locate_run" "$sent_type")" sent_type=2 printf "using fallback... \n" fi # send done info except printf "(%d, %d, %d) [lc:%ds sp:%ds]\n" "$status" "$locate_run" "$sent_type" "$time_locate" "$time_surplus" >>"$SPOW_SESH_ERR" send "$JID_NOMINAL_TARGET" "$sharetext" send "$JID_ERRORED_TARGET" "$(cat "$SPOW_SESH_ERR")" fi time_sendmsg_end="$(date +%s)" time_sendmsg=$((time_sendmsg_end - time_sendmsg_start)) time_run_end="$(date +%s)" time_run=$((time_run_end - time_run_start)) done_info="$(printf "(%d, %d, %d) [lc:%ds sp:%ds sm:%ds - %ds]\n" "$status" "$locate_run" "$sent_type" "$time_locate" "$time_surplus" "$time_sendmsg" "$time_run")" echo "done $done_info" | tee -a "$SPOW_SESH_ERR" # cleanup notify_end "$done_info" "$sharetext" } # script entry if [ "$1" = "mdtest" ]; then mdtest elif [ -z "$1" ]; then # ensure JID targets are set if [ -z "$JID_NOMINAL_TARGET" ] || [ -z "$JID_ERRORED_TARGET" ]; then echo "s+ow: error: JID_NOMINAL_TARGET and JID_ERRORED_TARGET are not set" exit 1 fi run else echo "usage: $0 [mdtest] surplus on wheels: a pure shell script to run surplus with mdtest using the termux-api choices $0 mdtest run mdtest for testing or authentication $0 run surplus on wheels normally" fi printf "%s\n\n" "$(cat "$SPOW_SESH_OUT")" >>"$SPOW_WEEK_OUT" printf "%s\n\n" "$(cat "$SPOW_SESH_ERR")" >>"$SPOW_WEEK_ERR"