mirror of https://github.com/DanielFGray/tekup
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
6.7 KiB
269 lines
6.7 KiB
#!/usr/bin/env bash |
|
|
|
declare verbose=0 |
|
declare expire_length |
|
declare request_deletion_key |
|
declare clipboard |
|
declare -r conf_dir="${XDG_CONFIG_DIR:-$HOME/.config}/tekup" |
|
declare -r log_file="${conf_dir}/log" |
|
declare -r config_file="${conf_dir}/conf" |
|
declare -A colors |
|
colors[red]=$(tput setaf 1) |
|
colors[reset]=$(tput sgr0) |
|
|
|
err() { |
|
printf "${colors[red]}%s${colors[reset]}\n" "$*" >&2 |
|
} |
|
|
|
die() { |
|
[[ -n "$1" ]] && err "$1" |
|
exit 1 |
|
} |
|
|
|
usage() { |
|
LESS=-FEXR less <<-HELP |
|
Usage: tekup [OPTIONS] [FILES...] |
|
Upload files to https://teknik.io |
|
|
|
OPTIONS: |
|
-d request a deletion key for images |
|
-e <STRING> expiration time. only has an effect on text pastes. |
|
must be in the form of 'N UNIT' where N is a number |
|
and UNIT is one of the following: |
|
view minute hour day month year |
|
-N disables expiring pastes (if enabled in config file, for example) |
|
-c save the url to the clipboard |
|
-h print this help |
|
-v print verbose output. can be stacked. there are three levels of |
|
verbosity: |
|
first will show progress from curl |
|
second will print the json response |
|
third will show verbose output from curl |
|
|
|
CONFIGURATION: |
|
A configuration file can be defined at ${config_file/$HOME/\~} |
|
If a line begins with '#' it is treated as a comment and ignored. |
|
A configuration file has the following options: |
|
|
|
api_key teknik.io api key |
|
expire_length same as -e flag above |
|
do_not_track boolean, asks teknik.io not to log the upload |
|
verbose_level must be a number |
|
clipboard boolean |
|
|
|
If ${log_file/$HOME/\~} is a writeable file tekup will always ask for a |
|
deletion key and log it in that file. |
|
HELP |
|
} |
|
|
|
has() { |
|
local _verbose |
|
if [[ $1 = '-v' ]]; then |
|
_verbose=1 |
|
shift |
|
fi |
|
for c; do c="${c%% *}" |
|
if ! command -v "$c" &> /dev/null; then |
|
(( _verbose > 0 )) && err "$c not found" |
|
return 1 |
|
fi |
|
done |
|
} |
|
|
|
select_from() { |
|
local cmd='command -v' |
|
for a; do |
|
case "$a" in |
|
-c) cmd="$2"; shift 2 ;; |
|
esac |
|
done |
|
for c; do |
|
if $cmd "${c%% *}" &> /dev/null; then |
|
echo "$c" |
|
return 0 |
|
fi |
|
done |
|
return 1 |
|
} |
|
|
|
parse_config_file() { |
|
local line key val nr=0 |
|
while IFS= read -r line; do |
|
(( ++nr )) |
|
[[ -z "$line" || "$line" = '#'* ]] && continue |
|
read -r key <<< "${line%% *}" |
|
read -r val <<< "${line#* }" |
|
if [[ -z "$val" ]]; then |
|
config_err+=( "missing value for \"$key\" in config file on line $nr" ) |
|
continue |
|
fi |
|
case "$key" in |
|
expire_length) expire_length="$val" ;; |
|
api_key) api_key="$val" ;; |
|
do_not_track) do_not_track="$val" ;; |
|
verbose_level) verbose="$val" ;; |
|
clipboard) [[ $val = 'true' ]] && clipboard=1 ;; |
|
*) config_err+=( "unknown key \"$key\" in config file on line $nr" ) |
|
esac |
|
done < "$config_file" |
|
if (( ${#config_err[@]} > 0 )); then |
|
err 'there were errors parsing config file:' |
|
for e in "${config_err[@]}"; do |
|
err " $e" |
|
done |
|
fi |
|
} |
|
|
|
upload_text() { |
|
local file curl_opts expire_unit |
|
file="$1" |
|
shift |
|
curl_opts=() |
|
case "$verbose" in |
|
0) curl_opts+=( -s ) ;; |
|
1|2) curl_opts+=( -# ) ;; |
|
*) curl_opts+=( -v ) ;; |
|
esac |
|
|
|
[[ -n "$api_key" ]] && curl_opts+=( -H "Authorization: AuthToken ${api_key}" ) |
|
|
|
if [[ -n "$expire_length" ]]; then |
|
printf -v expire_l '%d' "${expire_length% *}" |
|
expire_unit="${expire_length#* }" |
|
curl_opts+=( --data "expireLength=${expire_l}&expireUnit=${expire_unit/%s}") |
|
fi |
|
|
|
curl_opts+=( ${do_not_track:+ --data "doNotTrack=${do_not_track}"} ) |
|
|
|
curl_opts+=( |
|
--data "title=${file##*/}" |
|
--data-urlencode "code=$(< "$file")" |
|
https://api.teknik.io/v1/Paste ) |
|
curl "${curl_opts[@]}" | parse_response |
|
} |
|
|
|
upload_file() { |
|
local file mime curl_opts |
|
file="$1" |
|
mime="$2" |
|
shift 2 |
|
curl_opts=() |
|
case "$verbose" in |
|
0) curl_opts+=( -s ) ;; |
|
1|2) curl_opts+=( -# ) ;; |
|
*) curl_opts+=( -v ) ;; |
|
esac |
|
|
|
[[ -n "$api_key" ]] && curl_opts+=( -H "Authorization: AuthToken ${api_key}" ) |
|
|
|
curl_opts+=( ${do_not_track:+ -F "doNotTrack=${do_not_track}"} ) |
|
|
|
[[ -w "$log_file" || -n "$request_deletion_key" ]] && |
|
curl_opts+=( -F 'genDeletionKey=true' ) |
|
|
|
curl_opts+=( -F "contentType=${mime%%;*}" |
|
-F "file=@\"${file}\"" |
|
https://api.teknik.io/v1/Upload ) |
|
curl "${curl_opts[@]}" | parse_response |
|
} |
|
|
|
upload_files() { |
|
local f mime |
|
|
|
for f; do |
|
if [[ "$f" = '-' ]]; then |
|
echo 'reading from stdin' |
|
tee /tmp/tekup_paste &> /dev/null |
|
f=/tmp/tekup_paste |
|
fi |
|
|
|
if [[ ! -e "$f" ]]; then |
|
err "$f does not exist" |
|
continue |
|
fi |
|
|
|
mime=$(file -Lib "$f") |
|
if [[ "${mime%%/*}" = 'text' ]]; then |
|
upload_text "$f" |
|
else |
|
upload_file "$f" "$mime" |
|
fi |
|
done |
|
} |
|
|
|
parse_response() { |
|
local url deletion_key expire_date log_msg |
|
read -r response |
|
|
|
(( verbose > 1 )) && jq <<< "$response" |
|
|
|
if [[ "$response" != *'http'* || -z "$response" || "$response" = *'error'* ]]; then |
|
err "error uploading $file" |
|
die "$(jq -r '.error.message' <<< "$response")" |
|
fi |
|
|
|
url=$(jq -r '.result.url' <<< "$response") |
|
|
|
url="${url/paste.teknik.io/p.teknik.io\/Raw}" |
|
url="${url/upload/u}" |
|
|
|
file="${file/\/tmp\/tekup_paste/stdin}" |
|
printf '%s: %s\n' "$file" "$url" |
|
|
|
if [[ -n "$clipboard" ]]; then |
|
prg=$(select_from 'xclip -r ' 'xsel -b') |
|
[[ -z "$prg" ]] && err 'xclip or xsel required for writing to clipboard' |
|
$prg <<< "$url" |
|
fi |
|
|
|
if [[ -w "$log_file" || -n "$request_deletion_key" ]]; then |
|
deletion_key=$(jq -r '.result.deletionKey' <<< "$response") |
|
[[ "$deletion_key" = null ]] && deletion_key='' |
|
[[ -n "$deletion_key" ]] && printf 'deletion key: %s/%s\n' "$url" "$deletion_key" |
|
fi |
|
|
|
if [[ -n "$expire_length" ]]; then |
|
expire_date=$(jq -r '.result.expiration | (match("[0-9]+").string)? // .result.expiration' <<< "$response") |
|
[[ "$expire_date" = null ]] && expire_date='' |
|
[[ -n "$expire_date" ]] && |
|
printf 'expires at: %s\n' "$(date -d "@$expire_date")" |
|
fi |
|
|
|
if [[ -w "$log_file" ]]; then |
|
log_msg="$(date +%s) | file: $file | url: $url" |
|
[[ -n "$expire_date" ]] && |
|
log_msg+=" | expires: $expire_date" |
|
[[ -n "$deletion_key" ]] && |
|
log_msg+=" | delete: $url/$deletion_key" |
|
echo "$log_msg" >> "$log_file" |
|
fi |
|
} |
|
|
|
finish() { |
|
[[ -e /tmp/tekup_paste ]] && rm /tmp/tekup_paste |
|
} |
|
|
|
trap finish SIGHUP SIGINT SIGTERM |
|
|
|
has -v curl jq || die |
|
|
|
[[ -s "$config_file" ]] && parse_config_file |
|
|
|
OPTERR=0 |
|
while getopts 'hve:dNc' opt; do |
|
case "$opt" in |
|
h) usage; exit 0 ;; |
|
v) (( ++verbose )) ;; |
|
d) request_deletion_key=1 ;; |
|
e) expire_length="$OPTARG" ;; |
|
N) expire_length='' ;; |
|
c) clipboard=1 ;; |
|
esac |
|
done |
|
shift $(( OPTIND - 1 )) |
|
unset opt OPTARG OPTIND |
|
|
|
(( $# > 0 )) || die 'No files to upload.' |
|
|
|
upload_files "$@" |
|
finish
|
|
|