Bash + Neocities = Pastebin clone

Created: Sun Feb 17 12:17:38 CET 2019

Last mod­i­fied: Sun Feb 17 12:17:38 CET 2019


Assuming you have an ac­count at Neocities.

First, go get your API key.

Where to get your API key

Expected be­hav­iour of our com­mand line tool:

echo hello | ./paste 2>/dev/null
# https://baseurl/jUt5gp8z.txt

The tool de­pends on:

… and re­lies on /dev/urandom for gen­er­at­ing ran­dom file­names. Note that the first two el­e­ments on the list might need to be in­stalled first; whereas the last seven are al­most al­ways al­ready there.

I made the fol­low­ing func­tion for check­ing if a pro­gram ex­ist be­fore the script starts.

program_must_exist()
{
  command -v ${1} >/dev/null # >&2
  if [ ${?} -ne 0 ]
  then
    debug "Missing a dependency: ${1}"
    exit 1
  fi
}

Also a func­tion for check­ing the num­ber of ar­gu­ment passed to a func­tion or to the whole script via com­mand-line fa­cil­ity.

number_of_arguments()
{
  if [[ ${1} -ne ${2} ]]
  then
    debug "You provided ${1} arguments whereas I expected ${2} of them"
    exit 1
  fi
}

We start with the usual header; with­out -o pipefail be­cause it must be set af­ter new_u­uid() is called.

#!/usr/bin/env bash
set -eu

A helper func­tion.

function debug() {
  echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')]: ${@}" >&2
}

Useful con­stants.

readonly API_KEY='YOUR_API_KEY!!!'
readonly BASE_URL='https://baseurl/'
readonly UUID_LENGTH=8

Runtime con­stants.

function new_uuid () {
# from gist - fails when set -o pipefail
  tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w ${UUID_LENGTH} | head -n 1
}

A func­tion that re­turns the HTTP sta­tus of a re­quest to a given URL.

function get_status() {
  local url="${1}"

  timelimit -t 3 -s 9 \
    curl -I "${url}" | grep 'HTTP' | awk '{ print $2 }'
}

Two func­tions to restart or abort de­pend­ing on HTTP sta­tus. The first checks if a file al­ready ex­ists on the re­mote server.

function restart_if_file_exists_upstream () {
  local relative_url="$1"

  local http_status
  http_status=$(get_status "${BASE_URL}${relative_url}")

  if [[ "${http_status}" == "200" ]]; then
    debug 'File already exist !'
    "${0}"
    exit 0 # $?
  fi
}

er­roroutif_404() is pretty self-de­scrib­ing.

function error_out_if_404 () {
  local relative_url="${1}"

  local http_status
  http_status=$(get_status "${BASE_URL}${relative_url}")

  if [[ "${http_status}" == "404" ]]; then
    debug 'File was not created on remote server !'
    exit 0 # $?
  fi
}

The func­tion that up­loads what comes from stan­dard in­put to Neocities.

function upload_from_stdin () {
  local JSON
  JSON=$(timelimit -t 3 -s 9 \
           curl -H "Authorization: Bearer $API_KEY" \
                -F "$1=@-" "https://neocities.org/api/upload")

  if [[ $(echo "${JSON}" | grep 'error') != "" ]]; then
    debug "Neocities API returned an error"
    debug "${JSON}"
    exit 1
  fi
}

And the last part, get­ting the pieces to­gether.

function main () {
  local target_file

  target_file="/$(new_uuid).txt"

  # set -o pipefail

  debug "Checking if ${target_file} exists already, restart if it does"
  restart_if_file_exists_upstream "${target_file}"

  debug "Uploading to ${BASE_URL}${target_file} from stdin"
  upload_from_stdin "${target_file}"

  debug "Double-checking by looking for status 404"
  error_out_if_404 "${target_file}"

  # debug 'Done'
  echo "${BASE_URL}${target_file}"
}

main # "$@"

I’m still fairly new to writ­ing bash scripts, do not hes­i­tate to com­ment advices.

Thanks for read­ing ;)

source code