Skip to content

Instantly share code, notes, and snippets.

@CosmicToast
Last active January 12, 2020 04:41
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CosmicToast/95c3e65458f5eba6a69603f7295bc87c to your computer and use it in GitHub Desktop.
Save CosmicToast/95c3e65458f5eba6a69603f7295bc87c to your computer and use it in GitHub Desktop.
Fediverse (mastodon-api compatible) image and file status uploader
#!/bin/zsh
# deps: curl, mkdir(1p), jq
# return codes:
# 1: regular error, see stdout
# 2: media upload error
# 3: status post error
local app=${0:t}
usage() {
echo "$app [-a|-A <token>] [-i|-I <instance>] [-n] [-N <spoiler>] [-s <status>] -f <file>..."
echo '\t-a <token>: use oauth token <token>, do not create or read conf'
echo '\t-A <token>: use oauth token <token>, do not read conf, do create conf'
echo '\tneither -a nor -A: read conf, fail if none found'
echo
echo '\t-i <instance>: use instance <instance>, do not create or read conf'
echo '\t-I <instance>: use instance <instance>, do not read conf, do create conf'
echo '\tneither -i not -I: read conf, use default if none found'
echo
echo '\t-n: enable spoiler/nsfw, has a default spoiler'
echo '\t-N <spoiler>: set spoiler/subject to <spoiler>, implies -n'
echo
echo '\t-s <status>: set the status to <status>, otherwise use default'
echo
echo '\t-f <file>: upload file, at least one must be specified, repeatable'
return 0
}
local files=()
local instance='https://toast.cafe'
local post="Posted with $app"
local spoiler="Spoiler generated with $app"
local token=
# ---- auth
# -a <token>: use this token, do not create conf (alternative to -c)
# -A <token>: create a conf file with the token that will be loaded (perms 600)
# -i <instance>: work with the specified instance, do not create conf (alternative to -I)
# -I <instance>: create a conf file with the instance that will be loaded (different from -c)
# ---- post info
# -n: mark post as NSFW
# -N <text>: NSFW spoiler tag (implies -n)
# -s <status>: use a custom text status
# ---- these options are actually required
# -f <file>: file to upload. you can specify this as many times as you'd like
local confflag=0
local instflag=0
local nsfwflag=0
local name=
while getopts a:A:i:I:nN:s:f: name
do
case $name in
a) token=$OPTARG; confflag=0 ;;
c) token=$OPTARG; confflag=1 ;;
i) instance=$OPTARG; instflag=0 ;;
I) instance=$OPTARG; instflag=1 ;;
n) nsfwflag=1 ;;
N) spoiler=$OPTARG; nsfwflag=1 ;;
s) post=$OPTARG ;;
f) files+=( $OPTARG ) ;;
?) usage; return 1 ;;
esac
done
# state for the rest of stuff
: ${XDG_CONFIG_HOME:=~/.config}
: ${FEDIMG_CONFDIR:=$XDG_CONFIG_HOME/fedimg}
FEDIMG_TOKEN=$FEDIMG_CONFDIR/token.zsh
FEDIMG_INST=$FEDIMG_CONFDIR/instance.zsh
[[ -d $FEDIMG_CONFDIR ]] || mkdir -m 700 -p $FEDIMG_CONFDIR # please don't create it yourself
if (( confflag )); then
touch $FEDIMG_TOKEN
chmod 600 $FEDIMG_TOKEN
echo "token=$token" > $FEDIMG_TOKEN # please don't create it yourself
else
[[ -r $FEDIMG_TOKEN ]] && . $FEDIMG_TOKEN
fi
if (( instflag )); then
echo "instance=$instance" > $FEDIMG_INST # feel free to create it yourself (once the dir exists)
else
[[ -r $FEDIMG_INST ]] && . $FEDIMG_INST
fi
# let's check that everything is good!
if [[ -z $token ]]; then
echo TOKEN REQUIRED
return 1
fi
if ! (( #files )); then
echo NEED AT LEAST ONE FILE
return 1
fi
# ---- OK WE CAN START NOW
crl() {
curl -# -H "Authorization: Bearer $token" \
-X POST \
$@
}
local nsfwargs=()
if (( nsfwflag )); then
nsfwargs+=( -F sensitive=true -F "spoiler_text=$spoiler" )
fi
local idargs=()
local id=
for file in $files; do
id=$(crl -F file=@$file $instance/api/v1/media | jq .id) || return 2 # separate id for failure to upload media
idargs+=( -F "media_ids[]=$id" )
done
crl -o /dev/null -F status="$post" $idargs $nsfwargs "$instance/api/v1/statuses" || return 3 # separate id for status posting failure
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment