Sélectionner une révision Git
stpkg 35,67 Kio
#!/bin/bash
# TODO:
# Scale up, not only down??
# Fail on remove empty sticker pack dir
# Test trusted source
# Preview
# Fix add/del
# Test other output formats
# Thumbnails: https://github.com/matrix-org/synapse/issues/13039 (manually upload small version of the sticker (32x32?) and uses this as thumbnail_url)
shopt -s nocasematch # Case insensitive
STPKG_CONF_FILE=~/.config/stpkg.sh
BASE_DIR="$PWD"
declare -A animatedFiletype=(gif apng)
# Detect install
if [ "x$1" = "x-install" ]; then
INSTALL="yes"
fi
if [ "x$STPKG_COLORS" = "x" ]; then
STPKG_COLORS="yes"
error() { __red "$*\n" >/dev/stderr; }
warn() { __yellow "$*\n" >/dev/stderr; }
info() { __green "$*\n" >/dev/stderr; }
__red() { echo -ne '\e[31m'$*'\e[0m'; }
__green() { echo -ne '\e[32m'$*'\e[0m'; }
__yellow() { echo -ne '\e[33m'$*'\e[0m'; }
__bold() { echo -ne '\033[1m'$*'\e[0m'; }
__italic() { echo -ne '\033[3m'$*'\e[0m'; }
__underline() { echo -ne '\033[4m'$*'\e[0m'; }
else
STPKG_COLORS="no"
error() { echo 'ERROR:' $* >/dev/stderr; }
warn() { echo 'WARN:' $* >/dev/stderr; }
info() { echo 'INFO:' $* >/dev/stderr; }
alias __red=echo
alias __green=echo
alias __yellow=echo
alias __bold=echo
alias __italic=echo
alias __underline=echo
fi
die() {
error $*
exit 1
}
# Be paranoid
REQUIREMENTS=""
require() {
for CMD in $*; do
local __cmd=`which $CMD`
[ -z $__cmd ] && die "Failed to find the '$CMD' command"
REQUIREMENTS="$CMD $REQUIREMENTS"
done
}
require \
md5sum mktemp column uuidgen convert montage identify python3 curl \
sed tr git sponge ls find jq chmod cat expr iconv bc
__grep=`which egrep`
if [ -z $__grep ]; then
__grep=`which grep`
[ -z $__grep ] && die "Failed to find 'grep' or 'egrep'"
warn "Cound not find 'egrep', will use 'grep' instead." \
"Please, note that some functionalities may not work with regular expressions."
fi
SELF=`readlink -f "$0"`
MD5=`md5sum $SELF`
if [ "x$STPKG_NO_MSG" = "xyes" -a ! "x$1" = "x-install" -a ! "x$1" = "xupdate" ]; then
# Do not silence errors
warn() { return 0; }
info() { return 0; }
fi
########################
# Some basic functions #
########################
__exit() { cd "$BASE_DIR"; exit $1; }
__EXIT() { cd "$BASE_DIR"; }
trap __exit EXIT
alias exit=__exit
usage() {
local BASE_NAME=$(basename $0)
echo `__bold "$BASE_NAME usage:"`
local BASE_NAME=`__green "$BASE_NAME"`
cat << EOF
$BASE_NAME -install [`__yellow "-sshfs 'username@server' -local mnt_point -pickerrepo 'foo@git.fr:stickerpicker.git'"`] [`__yellow "install_path"`]
$BASE_NAME show
$BASE_NAME help
$BASE_NAME update
$BASE_NAME list [`__yellow "-p -e -np"`] [`__yellow "bash_regex"`]
$BASE_NAME add|del <`__yellow "pack"`>
$BASE_NAME pack [`__yellow "-t token"`] [`__yellow "name"`] <`__yellow "pack_folder"`>
$BASE_NAME edit [`__yellow "-t token"`] <`__yellow "pack_name"`> add|del <`__yellow "sticker"`> [`__yellow "file"`]
$BASE_NAME display [`__yellow "-dl folder"`] <`__yellow "pack_name"`> [`__yellow "sticker_regex"`]
`__bold Flags:`
`__yellow "-e"` / `__yellow "-ne"` The pack is enabled / disabled
`__yellow "-p"` / `__yellow "-np"` The pack is present / not present in the sticker repo
`__yellow "-t"` Matrix user token, let empty to fill it on stdin
`__yellow "-dl"` Specify a folder to dl, may exists or not
`__bold Configuration variables from $(echo '~/.config/stpkg.sh'):`
`__green STPKG_INSTALL` [`__italic 'path'`] The cloned stickerpicker project location. [$(__yellow `__italic '~/html/stickerpicker'`)]
`__green STPKG_BASE` [`__italic 'path'`] Where the sticker project is located.
`__green STPKG_COLORS` [`__italic "yes|no"`] Use colors for output. [$(__yellow `__italic "yes"`)]
`__green STPKG_NO_MSG` [`__italic "yes|no"`] Disable messages, no 'info' or 'warn'. [$(__yellow `__italic 'no'`)]
`__green STPKG_TOKEN` [`__italic 'token'`] The Matrix access token. $(__red `__bold \! Sensitive data \!`)
`__green STPKG_HOMESERV` [`__italic 'homeserv'`] The Matrix home server. [$(__yellow `__italic 'iiens.net'`)]
`__bold Notes:`
The `__italic "$(__yellow 'stpkg -install [path]')"` must be called only once. The default install
path is `__italic "$(__yellow '~/html/stickerpicker')"`. This command will give you a `__italic $(__yellow STPKG_INSTALL)`
and a `__italic $(__yellow STPKG_BASE)` env that you must put in your bashrc for the rest of the
stpkg commands to work.
`__bold Requirements:`
The stpkg command requires `__italic $(__yellow 'egrep')`. If `__italic $(__yellow 'egrep')` is not found, `__italic $(__yellow 'grep')` will be
used instead. Here is a list of all the requirements (that should be checked automatically):
$REQUIREMENTS
EOF
exit 0
}
slugify() {
echo "$1" | \
iconv -c -t ascii//TRANSLIT | \
sed -e 's/[~^]+//g' -e 's/[^a-zA-Z0-9]+/-/g' -e 's/^-+|-+$//g' | \
tr A-Z a-z
}
default_index() {
jq -n "{ \"homeserver_url\": \"https://matrix.iiens.net\", \"packs\": [ \"scalar-privacy_pam.json\" ] }"
}
validate_token_internal() {
curl -s -X POST "https://$STPKG_HOMESERV/_matrix/media/r0/upload?access_token=$STPKG_TOKEN" | \
python3 -c "import sys, json; print(json.load(sys.stdin)['errcode'])" # TODO: Use jq here
}
validate_token() {
local RES=`validate_token_internal`
[ "x$RES" = "xM_UNKNOWN_TOKEN" ] && die "The token is incorrect or doesn't exist on '$STPKG_HOMESERV'"
[ ! "x$RES" = "xM_UNKNOWN" ] && die "Got an 'yet implemented' error in Token Validation"
info "Token is valid on homeserver '$STPKG_HOMESERV'"
}
validate_homeserv() {
curl "https://$STPKG_HOMESERV" >/dev/null 2>&1
[ $? -ne 0 ] && die "Homeserver '$STPKG_HOMESERV' doesn't exist or is inaccessible"
}
file_get_width() {
local WIDTH=$(identify -format "%w" "$1[0]") >/dev/null
echo "$WIDTH"
}
file_get_height() {
local HEIGHT=$(identify -format "%h" "$1[0]") >/dev/null
echo "$HEIGHT"
}
__get_dest_dimensions() {
local INIT_WIDTH=$1
local INIT_HEIGHT=$2
local MAX_WIDTH=$3
local MAX_HEIGHT=$3
local WIDTH_RATIO="$(echo "scale=10; $INIT_WIDTH/$STPKG_MAX_WIDTH" | bc)"
local HEIGHT_RATIO="$(echo "scale=10; $INIT_HEIGHT/$STPKG_MAX_HEIGHT" | bc)"
if [ $(echo "$HEIGHT_RATIO >= $WIDTH_RATIO" | bc) = "1" ]; then
if [ $(echo "$HEIGHT_RATIO > 1" | bc) = "1" ]; then
local DEST_HEIGHT=$STPKG_MAX_HEIGHT
local DEST_WIDTH=""
else
local DEST_HEIGHT=$INIT_HEIGHT
local DEST_WIDTH=$INIT_WIDTH
fi
elif [ $(echo "$WIDTH_RATIO > 1" | bc) = "1" ]; then
local DEST_HEIGHT=""
local DEST_WIDTH=$STPKG_MAX_WIDTH
else
local DEST_HEIGHT=$INIT_HEIGHT
local DEST_WIDTH=$INIT_WIDTH
fi
echo "${DEST_WIDTH}x${DEST_HEIGHT}"
}
file_get_type() { xdg-mime query filetype "$1"; }
file_get_ext() { xdg-mime query filetype "$1" | sed 's+^.*/++'; }
file_get_ext_from_type() { echo "$1" | sed 's+^.*/++'; }
file_get_name() { echo -n "$1" | cut -f1 -d'.'; }
__upload_file() {
local TYPE=$1 # The type of file (png/gif)
local FILE=$2 # The file
local NAME=$3 # The pretty name for the sticker
# TODO: Use jq here
curl -s -X POST -H \
"Content-Type: $TYPE" --data-binary "@$FILE" "https://$STPKG_HOMESERV/_matrix/media/r0/upload?access_token=$STPKG_TOKEN" | \
tee /tmp/toto | python -c "import sys, json; print(json.load(sys.stdin)['content_uri'])"
[ $? -ne 0 ] && die "Failed to upload sticker $NAME for pack $PACK_NAME to $STPKG_HOMESERV"
}
create_sticker_json() {
local PACK_NAME=$1 # Pack name
local NAME=$2 # Pretty name for the sticker
local WIDTH=$3 # The WIDTH!
local HEIGHT=$4 # The HEIGHT!
local TYPE=$5 # The type of the picture (png/gif)
local MXC=$6 # The MXC URL
echo -n "{\"body\":\"$NAME\",\"info\":{\"mimetype\":\"$TYPE\",\"h\":$HEIGHT," \
"\"w\":$WIDTH,\"thumbnail_url\":\"$MXC\"},\"msgtype\":\"m.sticker\",\"url\":\"$MXC\"" \
",\"id\":\"$PACK_NAME-$NAME\"}"
}
mxc_to_https() {
# The transformation is the following
# `mxc://<serv>/<id>` => `https://<serv>/_matrix/media/r0/download/<serv>/<id>`
# It seems that every requests should be made to matrix.org...
# `mxc://<serv>/<id>` => `https://matrix.org/_matrix/media/r0/download/<serv>/<id>`
local SERV=`echo "$1" | awk -F/ '{print $3}'`
local ID=`echo "$1" | awk -F/ '{print $4}'`
echo "https://matrix.org/_matrix/media/r0/download/$SERV/$ID"
}
################################
# The special install function #
################################
__install() {
# Parse options
local PARSE_FLAGS=1
while [ $PARSE_FLAGS -eq 1 ]; do
case "$1" in
-pickerrepo)
STPKG_STICKER_REPO="$2"
shift 2
;;
-sshfs)
local SSHFS="$2"
shift 2
;;
-local)
local MNT="$2"
shift 2
;;
*)
local PARSE_FLAGS=0
;;
esac
done
STPKG_STICKER_REPO=${STPKG_STICKER_REPO:="https://github.com/maunium/stickerpicker.git"}
# Create the ~/.config/stpkg.sh
[ ! -d ~/.config ] && mkdir ~/.config
cat > $STPKG_CONF_FILE << EOF
if [ ! "x\$INSTALL" = "xyes" ]; then
STPKG_BASE="$BASE_DIR"
STPKG_HOMESERV='matrix.org'
STPKG_TOKEN=''
STPKG_COLORS='yes'
STPKG_NO_MSG='no'
STPKG_MAX_HEIGHT=128
STPKG_MAX_WIDTH=256
STPKG_GENERATE_PREVIEW="no"
STPKG_IMAGE_TARGET_FILETYPE="image/png"
STPKG_ANIMATED_TARGET_FILETYPE="image/gif"
STPKG_REUPLOAD_SAME_FILE="no"
__do_mount() { :; }
EOF
chmod 00600 $STPKG_CONF_FILE
# Set install dir. Oh boi, tricky things going around here because we
# handle the sshfs ourself.
if [ ! "x$SSHFS" = "x" ]; then
info "Detected a sshfs install"
[ "x$MNT" = "x" ] && die "You must specify a mount point with '-local /mount/pount' when using the '-sshfs' option"
local STPKG_INSTALL=$1
local STPKG_INSTALL=$MNT${STPKG_INSTALL:="/html/stickerpicker/"}
# The sshfs hook
cat >> $STPKG_CONF_FILE << EOF
STPKG_SSHFS='yes'
STPKG_INSTALL="$STPKG_INSTALL"
SSHFS_USER="$SSHFS"
SSHFS_MNT="`echo "$MNT" | sed "s+$HOME+~+g"`"
fi
__do_mount() {
mountpoint $MNT >/dev/null 2>&1
[ \$? -ne 0 -a ! "x\$INSTALL" = "xyes" ] && {
[ ! -d "\$STPKG_INSTALL" ] && {
sshfs $SSHFS: $MNT \
|| die "Failed to mount $SSHFS:~/ to $MNT";
}
[ ! -d "\$STPKG_INSTALL" ] \
&& die 'Failed to mount $SSHFS:~/ to $MNT, sshfs problems?'
}
}
EOF
# Do the sshfs hook
[ -f "$MNT" ] && die "The destination mount pount already exists"
mkdir "$MNT"
mountpoint $MNT >/dev/null 2>&1
[ $? -eq 0 ] && die "The '$MNT' folder is already a mountpoint"
sshfs $SSHFS: $MNT || die "Failed to mount $SSHFS:~/ to $MNT"
else
local STPKG_INSTALL=$1
local STPKG_INSTALL=${STPKG_INSTALL:="~/html/stickerpicker/"}
echo "STPKG_INSTALL=$STPKG_INSTALL" >> $STPKG_CONF_FILE
fi
# Check install dir
[ -d "$STPKG_INSTALL" ] \
&& die "Folder '$STPKG_INSTALL' already exists." \
"If you already installed the sticker picker you can use '$0 update'"
# Clone and copy packs
info "Will install sticker picker in folder: $STPKG_INSTALL"
git clone "$STPKG_STICKER_REPO" "$STPKG_INSTALL" \
|| die "Failed to git clone the stickerpicker project. Your config file ($STPKG_CONF_FILE) is probably corrupted, you should clean it by hand"
cd $STPKG_INSTALL/ || die "Failed to cd to '$STPKG_INSTALL/'"
cp packs/* web/packs || die "Failed to copy default packs to '$STPKG_INSTALL/web/packs/'"
default_index > $STPKG_INSTALL/web/packs/index.json || die "Failed to create default index.json file"
echo "fi" >> $STPKG_CONF_FILE
info "You may edit the $STPKG_CONF_FILE to setup your home server and token (variable STPKG_TOKEN)"
}
if [ "x$1" = "x-install" ]; then
[ "x$STPKG_NO_MSG" = "xyes" ] \
&& echo "!!! STPKG_NO_MSG is turn on, you won't see what's going on during the install"
shift
REPO_DIR="$(dirname $SELF)"
[ "$REPO_DIR" = "$BASE_DIR" ] || die \
"You must run this script from the root of the sticker repo." \
"You are in '$BASE_DIR' and should be in '$REPO_DIR'"
__install $*
exit 0
fi
#########################
# Cmd handler functions #
#########################
__show() {
local COLOR_STCOLOR=`[ "x$STPKG_COLORS" = "xyes" ] && echo '__green' || echo '__yellow'`
local COLOR_STNOMSG=`[ "x$STPKG_NO_MSG" = "xyes" ] && echo '__green' || echo '__yellow'`
local COMMITS=`git -C $STPKG_BASE rev-list --count HEAD 2>/dev/null`
local REVISION=`git -C $STPKG_BASE rev-list --count master 2>/dev/null`
local REVISION=`expr $COMMITS - $REVISION`
local BRANCH=`git -C $STPKG_BASE branch 2>&1 | grep --color=auto "\*" | sed -e "s/* //" -e "s/$/ /"`
local SAFE=0
local UNSAFE=0
local UNSAFE_CMD=""
for CMD in $REQUIREMENTS; do
[[ "`which $CMD`" =~ ^(/usr/bin|/bin|/sbin|/usr/sbin) ]] \
&& local SAFE=`expr $SAFE + 1` \
|| {
local UNSAFE=`expr $UNSAFE + 1`
local UNSAFE_CMD="$CMD $UNSAFE_CMD"
}
done
[ $UNSAFE -gt 0 ] && local UNSAFE=`__yellow $UNSAFE` || local UNSAFE=`__green $UNSAFE`
if [ "x$STPKG_TOKEN" = "x" ]; then
local TOKEN=`__yellow 'absent' `
else
local TOKEN=`validate_token_internal`
case "$TOKEN" in
M_UNKNOWN_TOKEN) local TOKEN=`__red '[invalid token]'`;;
M_UNKNOWN) local TOKEN=`__green '[valid]'`;;
*) local TOKEN=`__yellow '[unimplemented]'`;;
esac
local TOKEN="$TOKEN `__red "Sensitive data, I won't show it!"`"
fi
## SSHFS stuff
if [ "x$STPKG_SSHFS" = "xyes" ]; then
cat << EOF
`__bold stpkg with sshfs install:`
sshfs `[ -d $STPKG_INSTALL ] && __green 'mounted' || { __yellow 'umounted'; echo " (should not be the case at this point)"; }`
user $SSHFS_USER
mountpoint $SSHFS_MNT
EOF
fi
## Version stuff
cat << EOF
`__bold stpkg version:`
branch $BRANCH
commits $COMMITS
revision $REVISION
`__bold stpkg options and variables:`
`__green STPKG_INSTALL` `__italic $STPKG_INSTALL`
`__green STPKG_BASE` `__italic $STPKG_BASE`
`__green STPKG_COLORS` `__italic $($COLOR_STCOLOR $STPKG_COLORS)`
`__green STPKG_NO_MSG` `__italic $($COLOR_STNOMSG $STPKG_NO_MSG)`
`__green STPKG_HOMESERV` `__italic $STPKG_HOMESERV`
`__green STPKG_TOKEN` $TOKEN
`__bold required commands due to the '"require"' function:`
safe (system) `__green $SAFE`
unsafe (user) $UNSAFE
EOF
[ ! -z "$UNSAFE_CMD" ] && {
echo -ne ' '`__yellow unsafe commands`' '
for CMD in $UNSAFE_CMD; do echo -n "$CMD "; done
echo ''
}
}
__update() {
info "Updating packs..."
info "... update the sticker repo"
local LOCATION_OPT="--git-dir=$STPKG_BASE/.git --work-tree=$STPKG_BASE/"
git $LOCATION_OPT fetch >/dev/null 2>&1 || die "Failed to fetch from sticker repo"
git $LOCATION_OPT rebase >/dev/null 2>&1 || die "Failed to rebase... what did you do to your master branch?"
info "... update the sticker picker repo"
local LOCATION_OPT="--git-dir=$STPKG_INSTALL/.git --work-tree=$STPKG_INSTALL/"
git $LOCATION_OPT fetch >/dev/null 2>&1 || die "Failed to fetch from stickerpicker repo"
git $LOCATION_OPT rebase >/dev/null 2>&1 || die "Failed to rebase... what did you do to the master branch of the sticker picker?"
info "... copy the Json pack files in the sticker picker folder"
cp $STPKG_BASE/packs/*/*.json $STPKG_INSTALL/web/packs/ || die "Failed to copy packs Json files"
local NEW_MD5=`md5sum $(readlink -f "$STPKG_BASE/stpkg")`
if [ ! "$NEW_MD5" = "$MD5" ]; then
info "... creating update script"
local UPDATE=`mktemp --suffix=.sh`
cat > $UPDATE << EOF
`declare -f __yellow`
`declare -f __green`
`declare -f __red`
`declare -f die`
__yellow "... Update stpkg ... "
cp "$STPKG_BASE/stpkg" "$SELF" && __green "success!" || __red "failed!"
rm "$UPDATE" || die "Failed to delete the update script"
echo ""
EOF
exec bash "$UPDATE" \
|| die "Failed to run the update script." \
"You will need to copy manually '$STPKG_BASE/stpkg' to '$SELF' manually."
fi
info "Update finished!"
exit 0
}
__display() {
if [ "x$1" = "x-dl" ]; then
local DL_SWITCH="yes"
local DL_FOLDER="$2"
if [ ! -d "$DL_FOLDER" ]; then
info "Need to create folder '$DL_FOLDER'";
mkdir "$DL_FOLDER" || die "Failed to create folder '$DL_FOLDER'";
else
warn "Folder '$DL_FOLDER' already exists"
fi
info "Will use the '$DL_FOLDER' to dl found stickers"
shift 2
fi
local FILE="${STPKG_INSTALL}web/packs/$1.json"
[ ! -r "$FILE" ] && die "Pack '$1' is not available (check with 'pack list'). The corresponding file should be '$FILE'"
echo "Display the sticker pack $(__green `jq '.title' < "$FILE"`):"
local FIRST="yes"
jq '.stickers[] | "\(.body) \(.info.thumbnail_url) \(.info.mimetype)"' < "$FILE" | while IFS= read LINE; do
local NAME=`echo "$LINE" | awk -F '"| ' '{print $2}'`
local URL=` echo "$LINE" | awk -F '"| ' '{print $3}'`
local TYPE=`echo "$LINE" | awk -F '"| ' '{print $4}' | awk -F '/' '{print $2}'`
[ ! "x$2" = "x" ] && { [[ "$NAME" =~ $2 ]] || continue; }
local URL=`mxc_to_https "$URL"`
# Header if first
if [ "x$FIRST" = "xyes" ]; then
local FIRST="no"
echo "Name Download_URL Type"
fi
echo -ne "$NAME $URL $TYPE"
if [ "x$DL_SWITCH" = "xyes" ]; then
curl "$URL" --create-dirs --output "$DL_FOLDER/$NAME.$TYPE" >/dev/null 2>&1 \
&& echo " `__green DL`" \
|| echo " `__red Failed`"
else
echo ""
fi
done | column -t
}
__list() {
# Handle -e and -p flags
local PARSE_FLAGS=1
while [ $PARSE_FLAGS -eq 1 ]; do
case "$1" in
-e)
local FILTER_ENABLED="yes"
shift
;;
-ne)
local FILTER_DISABLED="yes"
shift
;;
-np)
local FILTER_NOT_PRESENT="yes"
shift
;;
-p)
local FILTER_PRESENT="yes"
shift
;;
*)
local PARSE_FLAGS=0
;;
esac
done
[ "x$FILTER_PRESENT" = "xyes" -a "x$FILTER_NOT_PRESENT" = "xyes" ] \
&& die "The -p and -np flags are incompatible"
[ "x$FILTER_ENABLED" = "xyes" -a "x$FILTER_DISABLED" = "xyes" ] \
&& die "The -e and -ne flags are incompatible"
# Get the regex
local REG="$*"
# A sticker pack can be:
# - enabled and present in the picker folder
# - disabled and present in the picker folder
# - orphan when the Json file is present in the picker folder but not in the pack repo
# Note that the Json files from the sticker repo are copied at update time
# in the sticker repo picker folder.
# A pack can be 'orphan' and 'enabled', or 'orphan' and 'disabled'. But
# 'enabled' and 'disabled' are exclusive tags.
ls -d1 $STPKG_INSTALL/web/packs/*.json | grep -v index.json | while IFS= read -r LINE; do
local LINE=`basename $LINE .json`
[ ! "x$REG" = "x" ] && ! [[ "$LINE" =~ $REG ]] && continue
local PRESENT=`find $STPKG_BASE/packs/ -type f -name "$LINE.json"`
[ "x$FILTER_PRESENT" = "xyes" -a "x$PRESENT" = "x" ] && continue
[ "x$FILTER_NOT_PRESENT" = "xyes" -a ! "x$PRESENT" = "x" ] && continue
local PRESENT=`[ ! "x$PRESENT" = "x" ] && echo 'p' || echo 'a'`
local ENABLED=`jq -r ".packs | index(\"$LINE.json\") | ." < $STPKG_INSTALL/web/packs/index.json`
[ "x$FILTER_ENABLED" = "xyes" -a "x$ENABLED" = "xnull" ] && continue
[ "x$FILTER_DISABLED" = "xyes" -a ! "x$ENABLED" = "xnull" ] && continue
local ENABLED=`[ "x$ENABLED" = "xnull" ] && echo 'd' || echo 'e'`
echo -ne "$PRESENT$ENABLED $LINE\n"
done
}
# Usage: process_sticker_file <filename> <&JSON>
process_sticker_file() {
FILE="$1"
local -n JSON_REF="$2"
# Get the names and extensions
local INIT_TYPE=$(file_get_type "$FILE")
local INIT_EXT=$(file_get_ext_from_type "$INIT_TYPE")
local NAME=$(file_get_name "$FILE")
local TEMP=`mktemp --suffix=.stpkg`
if [ "x$TYPE" != "xgif" ] && [ "x$TYPE" != "xpng" ]; then
local DSTTYPE="png"
else
local DSTTYPE="$TYPE"
fi
local DEST="$DEST_FOLDER/$NAME.$DSTTYPE"
if [ "x${STPKG_NOCONVERT_FILE}" = "xyes" ]; then
#
# Don't convert image, upload it as it is
#
local DEST_TYPE=$INIT_TYPE
cp ${FILE} ${TEMP}
else
if [[ ${animatedFiletype[$INIT_EXT]} ]]; then
#
# Convert animated image
#
local DEST_TYPE=$STPKG_ANIMATED_TARGET_FILETYPE
local DEST_EXT=$(file_get_ext_from_type "$DEST_TYPE")
local backgroundColor="`convert "${FILE}[0]" -format "%[pixel:u.p{0,0}]" info:`"
convert "${FILE}" -dispose previous -background "${backgroundColor}" -trim -layers TrimBounds -coalesce "$DEST_EXT:${TEMP}" 1>&2 \
|| die "$NAME failed trimming the sticker"
# Get sizes
local INIT_WIDTH=$(file_get_width "${TEMP}")
local INIT_HEIGHT=$(file_get_height "${TEMP}")
local DEST_DIMENSIONS=$(__get_dest_dimensions $INIT_WIDTH $INIT_HEIGHT $STPKG_MAX_WIDTH $STPKG_MAX_HEIGHT)
convert "${TEMP}" -coalesce -dispose previous -resize ${DEST_DIMENSIONS} "$DEST_EXT:${TEMP}" 1>&2 \
|| die "$NAME failed resizing the sticker"
# Can't optimize the gif because discord doesn't know how to display them properly (for bridged channels)
#convert "${TEMP}" -coalesce -layers Optimize "$DEST_EXT:${TEMP}" 1>&2 \
else
#
# Convert fixed image
#
local DEST_TYPE=$STPKG_IMAGE_TARGET_FILETYPE
local DEST_EXT=$(file_get_ext_from_type "$DEST_TYPE")
convert "$FILE" -trim "${INIT_EXT}:${TEMP}"
# Get sizes
local INIT_WIDTH=$(file_get_width "$TEMP")
local INIT_HEIGHT=$(file_get_height "$TEMP")
local DEST_DIMENSIONS=$(__get_dest_dimensions $INIT_WIDTH $INIT_HEIGHT $STPKG_MAX_WIDTH $STPKG_MAX_HEIGHT)
convert "$TEMP" -bordercolor none -border 1 -background none -gravity center -trim +repage -resize ${DEST_DIMENSIONS} "${DEST_EXT}:${TEMP}" 1>&2 \
|| die "$NAME failed converting the sticker"
fi
fi
# Get real final dimensions for json
DEST_WIDTH=$(file_get_width "$TEMP")
DEST_HEIGHT=$(file_get_height "$TEMP")
# Upload the transformed file
local MXC=$(__upload_file "$DEST_TYPE" "$TEMP" "$NAME")
rm "$TEMP"
JSON_REF=$(create_sticker_json "$PACK_NAME" "$NAME" "$DEST_WIDTH" "$DEST_HEIGHT" "$DEST_TYPE" "$MXC")
}
__edit() {
# <pack name> <add|del> <sticker> [image file to use]
local PACK_NAME="$1"
local ACTION="$2"
local STICKER_NAME="$3"
[ -z "$PACK_NAME" -o -z "$ACTION" -o -z "$STICKER_NAME" ] && die "Command argument are invalid, check the usage"
shift 3
local JSON_FILE="${STPKG_INSTALL}web/packs/$PACK_NAME.json"
[ ! -r "$JSON_FILE" ] && die "Can't read file '$JSON_FILE' associated to the '$PACK_NAME' pack"
local TEMP=`mktemp --suffix=.stpkg`
cp $JSON_FILE $TEMP
case "$ACTION" in
add)
[ $# -eq 0 ] && die "You must specify an image file for the add action"
local FILE="$1"
[ ! -r "$FILE" ] && die "Failed to find file '$FILE'"
local ST_JSON=""
process_sticker_file "$FILE" ST_JSON
jq "del(.stickers[] | select(.body == \"$STICKER_NAME\"))" < "$TEMP" | sponge "$TEMP"
jq ".stickers += [$ST_JSON]" < $TEMP | sponge $TEMP
;;
del)
[ $# -ne 0 ] && die "Extra argument are present: $*"
jq "del(.stickers[] | select(.body == \"$STICKER_NAME\"))" < "$TEMP" | sponge "$TEMP"
;;
*)
die "Unknown action '$ACTION', should be 'add' or 'del'"
;;
esac
# TODO [OPTIONAL] Update the preview
# TODO Add a dry mode, to only preview the change
# TODO If the pack was already added to the picker, re-add it to take into
# account the modifications
#jq < $TEMP ### to test/review the change
cp $TEMP $JSON_FILE
}
__add() {
[ "x$1" = "x" ] && die "You must specify a pack for the 'add' command"
info "Enable pack '$1'"
local PRESENT=`find $STPKG_INSTALL/web/packs/ -type f -name "$1.json" | grep -v index.json`
[ "x$PRESENT" = "x" ] && die "Pack '$1' is not present"
# Use a temp file because of sponge
local TEMP=`mktemp --suffix=.stpkg`
cp $STPKG_INSTALL/web/packs/index.json $TEMP
jq ".packs += [\"$1.json\"]" < $TEMP | sponge $TEMP
cp $TEMP $STPKG_INSTALL/web/packs/index.json
}
__del() {
[ "x$1" = "x" ] && die "You must specify a pack 'del' command"
info "Disable pack '$1'"
local TEMP=`mktemp --suffix=.stpkg`
cp $STPKG_INSTALL/web/packs/index.json $TEMP
jq "{ \"homeserver_url\": .homeserver_url, \"packs\": .packs | map(select(. != \"$1.json\")) }" \
< $TEMP | sponge $TEMP
cp $TEMP $STPKG_INSTALL/web/packs/index.json
}
__default() {
info "Reset the index.json in the sticker picker install directory"
default_index > $STPKG_INSTALL/web/packs/index.json || die "Failed to create default index.json file"
}
__pack() {
# The pack source folder and the pack name
if [ $# -eq 1 ]; then
# The pack name is not specified
local PACK_FOLDER="$1"
local PACK_NAME=${PACK_FOLDER##*/}
else if [ $# -eq 2 ]; then
# The pack name is specified
local PACK_NAME="$1"
local PACK_FOLDER="$2"
else
die "Invalid number of arguments for the 'pack' command"
fi fi
local PACK_NAME=`slugify $PACK_NAME`
info "Will pack the folder '$PACK_FOLDER' into '$PACK_NAME'"
[ ! -d "$PACK_FOLDER" ] && die "Source folder '$PACK_FOLDER' doesn't exist"
# Check packs
local DEST_FOLDER="$STPKG_BASE/packs/$PACK_NAME"
[ -d "$DEST_FOLDER" ] \
&& { warn "Old folder for pack $PACK_NAME exists, using checksums to reupload necessary stickers"; } \
|| { { info "Create folder for pack $PACK_NAME" ; mkdir "$DEST_FOLDER"; } \
|| die "Failed to create destination folder for pack '$PACK_NAME'"; }
#[ -d "$DEST_FOLDER" ] \
# && { { warn "Delete old folder content for pack $PACK_NAME" ; rm "$DEST_FOLDER"/*; } \
# || die "Failed to remove old folder pack content"; } \
# || { { info "Create folder for pack $PACK_NAME" ; mkdir "$DEST_FOLDER"; } \
# || die "Failed to create destination folder for pack '$PACK_NAME'"; }
local DEST_INDEX="$DEST_FOLDER/$PACK_NAME.json"
touch $DEST_INDEX
[ ! -f $DEST_INDEX ] && die "Failed to create the index file '$DEST_INDEX'"
# Populate all stickers
local TEMP_JSON=`mktemp --suffix=.stpkg`
echo -n "{\"title\":\"$PACK_NAME\",\"id\":\"`uuidgen`\",\"stickers\":[" > $TEMP_JSON
cd $PACK_FOLDER
local TOTAL_FILES=`find . -maxdepth 1 -type f | wc -l`
local CURRENT_FILE=1
local FIRST_IN_ARRAY=""
# Check checksums
local SUM_FILE="$DEST_FOLDER/.$PACK_NAME.checksums"
if [ ! -f "$SUM_FILE" ]; then
touch $SUM_FILE
fi
local -A hashes unchangedFiles updatedFiles deletedFiles
local TEMP_CHECK=`mktemp --suffix=.stpkg`
md5sum -c $SUM_FILE &>/dev/null >$TEMP_CHECK
while read -r _hash _file; do
hashes["$_file"]="$_hash"
done < $SUM_FILE
for i in `cat $TEMP_CHECK | grep ': OK$' | sed 's/\(.*\): OK/\1/'`; do
if [ "x$i" = "x" ]; then continue; fi
if [ "x$STPKG_REUPLOAD_SAME_FILE" = "xyes" ]; then
updatedFiles["$i"]="${hashes["$i"]}"
else
unchangedFiles["$i"]="${hashes["$i"]}"
fi
done
for i in `cat $TEMP_CHECK | grep ': FAILED$' | sed 's/\(.*\): FAILED/\1/'`; do
if [ "x$i" = "x" ]; then continue; fi
updatedFiles["$i"]="${hashes["$i"]}"
done
for i in `cat $TEMP_CHECK | grep ': FAILED open or read$' | sed 's/\(.*\): FAILED open or read/\1/'`; do
if [ "x$i" == "x" ]; then continue; fi
deletedFiles["$i"]="${hashes["$i"]}"
echo "add $i to deleted"
done
local ST_JSON=""
echo "----- Pack -----"
{
for FILE in *; do
# Add a ',' only if it's not the first in the array
echo -n "$FIRST_IN_ARRAY" >> $TEMP_JSON
local STICKER_STATUS=""
local STICKER_NAME=$(file_get_name "$FILE")
if [[ -v unchangedFiles[$FILE] ]]; then
STICKER_STATUS="Unchanged"
ST_JSON=`jq ".stickers[] | select(.body == \"$STICKER_NAME\")" < "$DEST_INDEX"`
else
if [[ -v updatedFiles[$FILE] ]]; then
STICKER_STATUS="`__yellow Updated`"
sed -i "/[0-9a-f]\{32\} $FILE/d" $SUM_FILE
else
STICKER_STATUS="`__green New`"
if [[ "`md5sum $FILE | sed -e 's/\([0-9a-f]\{32\}\) .*$/\1/'`" = "$(cat $SUM_FILE | grep "$FILE" | sed -e 's/\([0-9a-f]\{32\}\) .*$/\1/')" ]]; then
die "New file is replacing existing file, that should not be happening"
fi
fi
process_sticker_file $FILE ST_JSON
md5sum $FILE >> $SUM_FILE
fi
echo "$ST_JSON" >> "$TEMP_JSON"
# For the report
[ "x$FIRST_IN_ARRAY" = "x" ] && echo -e "StickerName MXC Type Width Height Status"
FIRST_IN_ARRAY=","
local report="`echo -ne "$ST_JSON" | jq -r '[.body, .url, .info.mimetype, .info.w, .info.h]|join(" ")'` $STICKER_STATUS"
echo "$report"
done
for i in "${!deletedFiles[@]}"; do
local STICKER_NAME=$(file_get_name "$i")
ST_JSON=`jq ".stickers[] | select(.body == \"$STICKER_NAME\")" < "$DEST_INDEX"`
sed -i "/[0-9a-f]\{32\} $i/d" $SUM_FILE
local report="`echo -ne "$ST_JSON" | jq -r '[.body, .url, .info.mimetype, .info.w, .info.h]|join(" ")'` `__red Deleted`"
echo "$report"
done
} | column -t
echo "----------------"
echo -ne "# $PACK_NAME\n(no preview)" > "$DEST_FOLDER/README.md"
find $DEST_FOLDER -type f \( ! -name "preview.png" -and ! -name "$PACK_NAME.json" -and ! -name '.*.checksums' \) -exec rm {} \;
echo -n "]}" >> $TEMP_JSON
cat $TEMP_JSON | jq > $DEST_INDEX
# Sort checksums to get better diffs
cat $SUM_FILE | sort | sponge $SUM_FILE
info "Pack created, you can now commit it and create a MR to share it with other users"
}
__do_command() {
case "$1" in
show|update|list|add|del|default|pack|display|edit)
# Source conf file, may have the sshfs hook
if [ -f $STPKG_CONF_FILE ]; then
source $STPKG_CONF_FILE
else
error "Conf file \"$STPKG_CONF_FILE\" doesn't exist. Did you use the install command?"
error "Help:"
usage
fi
__do_mount
# Get some exec / paths #
STPKG_INSTALL=${STPKG_INSTALL:="~/html/stickerpicker/"}
if [ ! -d "$STPKG_INSTALL" ]; then
error "STPKG_INSTALL folder ($STPKG_INSTALL) doesn't exist. Did you use the install script?"
usage
fi
info "Using STPKG_INSTALL: $STPKG_INSTALL`[ "x$STPKG_SSHFS" = "xyes" ] && echo ' (this is an sshfs install)'`"
[ "x$STPKG_BASE" = "x" ] && die \
"The STPKG_BASE env var is mandatory, it must point to the sticker repo. It should have been given by the install script"
;;&
edit|pack)
local CURRCOMMAND="$1"
shift
# Home server
STPKG_HOMESERV=${STPKG_HOMESERV:="matrix.org"}
STPKG_HOMESERV=`echo "$STPKG_HOMESERV" | iconv -c -t ascii//TRANSLIT | sed -e 's/^http:\/\/|^https:\/\///g' -e 's/^-+|-+$//g'`
validate_homeserv # Check if homeserv exists
# Get the token
if [ "x$1" = "x-t" ]; then
STPKG_TOKEN="$2"
shift 2
fi
if [ "x$STPKG_TOKEN" = "x" ]; then
read -sp "`__yellow 'Enter your access token:'`" STPKG_TOKEN
echo ''
fi
validate_token # Validate token, will exit if invalid
local PARSE_FLAGS=1
while [ $PARSE_FLAGS -eq 1 ]; do
case "$1" in
# Overwrite max width
-w|-width)
STPKG_MAX_WIDTH="$2"
shift 2
;;
# Overwrite max height
-h|-height)
STPKG_MAX_HEIGHT="$2"
shift 2
;;
# Do we need to resize/convert the files we are given?
-noconvert)
STPKG_NOCONVERT_FILE="yes"
shift
;;
# What filetype do we want our image stickers to be?
-ti)
STPKG_IMAGE_TARGET_FILETYPE="$2"
shift 2
;;
# What filetype do we want our animated stickers to be?
-ta)
STPKG_ANIMATED_TARGET_FILETYPE="$2"
shift 2
;;
# Generate preview of the sticker pack
-preview)
STPKG_GENERATE_PREVIEW="yes"
die "Asking for a preview generation, while this feature has been dropped for the moment"
shift
;;
# Do not reupload unchanged stickers
-reupload)
STPKG_REUPLOAD_SAME_FILE="yes"
shift
;;
*)
local PARSE_FLAGS=0
;;
esac
done
# Default values
STPKG_MAX_WIDTH=${STPKG_MAX_WIDTH:="256"}
STPKG_MAX_HEIGHT=${STPKG_MAX_HEIGHT:="128"}
STPKG_NOCONVERT_FILE=${STPKG_NOCONVERT_FILE:="no"}
STPKG_GENERATE_PREVIEW=${STPKG_GENERATE_PREVIEW:="no"}
STPKG_IMAGE_TARGET_FILETYPE=${STPKG_IMAGE_TARGET_FILETYPE:="image/png"}
STPKG_ANIMATED_TARGET_FILETYPE=${STPKG_ANIMATED_TARGET_FILETYPE:="image/gif"}
STPKG_REUPLOAD_SAME_FILE=${STPKG_REUPLOAD_SAME_FILE:="no"}
__$CURRCOMMAND $*
exit
;;&
*)
;;
esac
__$*
}
######################
# Parse command line #
######################
[ "x$1" = "x" ] && usage
case "$1" in
show) __do_command show ;;
update) __do_command update ;;
list) shift; __do_command list $* ;;
add) shift; __do_command add $* ;;
del) shift; __do_command del $* ;;
default) __do_command default ;;
pack) shift; __do_command pack $* ;;
display) shift; __do_command display $*;;
edit) shift; __do_command edit $* ;;
*) __do_command usage ;;
esac