Files

243 lines
8.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# discord colors are standard 6-digit hex, but converted to a base-10 integer
# colors taken from https://xkcd.com/color/rgb/
# and built into a palette based on https://sashamaps.net/docs/resources/20-colors/
# into my color palette https://color.digitaladapt.com/
#
# DARK: maroon(#650021), brown(#653700), olive(#6e750e), teal(#029386), navy(#01153e), black(#000000),
# BRIGHT: red(#e50000), orange(#f97306), yellow(#ffff14), lime(#aaff32), green(#15b01a),
# cyan(#00ffff), blue(#0343df), purple(#7e1e9c), magenta(#c20078), grey(#929591),
# LIGHT: pink(#ff81c0), apricot(#ffb16d), beige(#e6daa6), mint(#9ffeb0), lavender(#c79fef), white(#ffffff).
scriptRoot=$(dirname "$0")
alert=false
quiet=false
skipMsg=false
declare -A colors
colors=(
["maroon"]=6619169 ["brown"]=6633216 ["olive"]=7238926 ["teal"]=168838 ["navy"]=70974 ["black"]=0
["red"]=15007744 ["orange"]=16347910 ["yellow"]=16776980 ["lime"]=11206450 ["green"]=1421338
["cyan"]=65535 ["blue"]=213983 ["purple"]=8265372 ["magenta"]=12714104 ["grey"]=9606545
["pink"]=16744896 ["apricot"]=16757101 ["beige"]=15129254 ["mint"]=10485424 ["lavender"]=13082607 ["white"]=16777215
)
declare -A ansiColors
ansiColors=(
["maroon"]="\e[37m\e[48;2;101;0;33m" ["brown"]="\e[37m\e[48;2;101;55;0m" ["olive"]="\e[37m\e[48;2;110;117;14m" ["teal"]="\e[37m\e[48;2;2;147;134m" ["navy"]="\e[37m\e[48;2;1;21;62m" ["black"]="\e[37m\e[48;2;0;0;0m"
["red"]="\e[30m\e[48;2;229;0;0m" ["orange"]="\e[30m\e[48;2;249;115;6m" ["yellow"]="\e[30m\e[48;2;255;255;20m" ["lime"]="\e[30m\e[48;2;170;255;50m" ["green"]="\e[30m\e[48;2;21;176;26m"
["cyan"]="\e[30m\e[48;2;0;255;255m" ["blue"]="\e[30m\e[48;2;3;67;223m" ["purple"]="\e[30m\e[48;2;126;30;156m" ["magenta"]="\e[30m\e[48;2;194;0;120m" ["grey"]="\e[30m\e[48;2;146;149;145m"
["pink"]="\e[30m\e[48;2;255;129;192m" ["apricot"]="\e[30m\e[48;2;255;177;109m" ["beige"]="\e[30m\e[48;2;230;218;166m" ["mint"]="\e[30m\e[48;2;159;254;176m" ["lavender"]="\e[30m\e[48;2;199;159;239m" ["white"]="\e[30m\e[48;2;255;255;255m"
)
print_usage() {
echo "Usage: $0 [[-h hook-url] | -a] [-c color] [-d [distinct-name]] [-t title] [-q] (message... | -z | < file.msg)"
}
# handle all arguments provided
while getopts ':h:c:d:t:zaq' flag; do
case "$flag" in
h)
if [[ "$OPTARG" == "http"*"?wait=true" ]]; then
hookUrl="$OPTARG"
else
echo "Invalid hook-url provided, should be something like:"
echo "https://discord.com/api/webhooks/***?wait=true"
print_usage
exit 1
fi
;;
c)
if [[ -v "colors[$OPTARG]" ]]; then
color="$OPTARG"
else
echo "Invalid color provided"
"${scriptRoot}/colors-discord.sh"
print_usage
exit 1
fi
;;
d)
# whatever the distinct key, we clean it up, since it will be used as a filename
# replace "/" and "|" with "_"
distinct=$(echo "$OPTARG" | sed 's/[\/\|]/_/g')
;;
:)
# '-d' without value is allowed, but generates a warning
# in which case we'll use name of parent command with arguments
# whatever the distinct key, we clean it up, since it will be used as a filename
# replace "/" and "|" with "_"
if [[ "$OPTARG" = "d" ]]; then
distinct=$(ps -o args= $PPID | sed 's/[\/\|]/_/g')
echo "Warning: doing distinct by parent, with key '$distinct'" >&2
else
echo "option '$OPTARG' provided with value"
print_usage
exit 1
fi
;;
t)
# title, for supplying a title, without activing the "distinct" feature
title="$OPTARG"
;;
z)
skipMsg=true
;;
q)
quiet=true
;;
a)
alert=true
;;
*)
echo "unknown option provided '$flag'"
print_usage
exit 1
;;
esac
done
shift "$((OPTIND-1))"
# load in defaults from config
configFile="$scriptRoot/config.sh"
if [[ -f "$configFile" ]]; then
source "$configFile"
botName="$DISCORD_SERVER_NAME"
if [[ -z "$hookUrl" ]]; then
hookUrl="$DISCORD_GENERAL_HOOK"
if [[ "$alert" = true ]]; then
hookUrl="$DISCORD_ALERT_HOOK"
fi
fi
if [[ -n "$title" ]]; then
title="$title $DISCORD_TITLE_SUFFIX"
fi
fi
# stop if we have no hook to send message to
if [[ -z "$hookUrl" ]]; then
echo "Discord Hook URL must either be in config file, or supplied via '-h' argument."
exit 1
fi
# get full message, read from arguments
message=$(echo "$@" | sed 's/^ *//' | sed 's/ *$//')
# if we have no message, read from stdin instead
if [[ -z "$message" ]] && [[ "$skipMsg" = false ]]; then
message=$(cat - | sed 's/^ *//' | sed 's/ *$//')
fi
# sed removes blank lines from the end of the message
message=$(echo "$message" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}')
fullMsg="$message"
# output the given message, unless instructed to be quiet
if [[ "$quiet" = false ]]; then
if [[ -n "$distinct" ]] || [[ -n "$title" ]] || [[ -n "$color" ]]; then
if [[ -n "$color" ]]; then
echo -e "${ansiColors[$color]} ${title-$distinct} \e[m"
else
# no color specified, make title bold and underlined
echo -e "\e[1m\e[4m ${title-$distinct} \e[m"
fi
fi
if [[ -n "$fullMsg" ]] && [[ "$skipMsg" = false ]]; then
echo "$fullMsg"
fi
fi
# if message is too long, split it, and after curl, recursively call with remainder
# 4k max, but we must account for escaping text in json, so limit to 90% capacity
extra="${message:3600}"
if [ -n "$extra" ]; then
message="${message:0:3600}"
# as an added elegance, when spliting into pieces, find the last \n newline,
# and break there instead, but only if the new smaller chunk is of sufficient size
chunk1=$(echo "$message" | head -n -1)
chunks=$(echo "$message" | tail -n 1)
if (( "${#chunk1}" >= 1000 )); then
message="$chunk1"
extra="$chunks$extra"
fi
fi
# sed: rewrite json string, because "\u001b[m" needs to "\u001b[0m" instead
# linux is fine with the missing zero, but discord needs it
message=$(echo "$message" | jq -aRs . | sed 's/\\u001b\[m/\\u001b[0m/g')
if [[ "$message" = '"\n"' ]]; then
if [[ "$quiet" = true ]]; then
# quiet was specified, and there was no message, so stop now
exit 0
else
# message empty, so denote as such
message='"\ud83d\udea9 MESSAGE EMPTY \ud83d\udea9"'
fi
else
if [[ "$message" = *"\\u001b"* ]]; then
prefix="ansi\n"
fi
# wrap message into "ansi" block, for text color support
message="\"\`\`\`$prefix${message:1:-1}\`\`\`\""
fi
# handle distinct stuff
oldMsg=""
if [[ -n "$distinct" ]]; then
if [[ -f "$scriptRoot/unique/$distinct.msg" ]]; then
oldMsg=$(cat "$scriptRoot/unique/$distinct.msg")
fi
if [[ "$fullMsg" = "$oldMsg" ]]; then
echo "duplicate message"
exit 0
fi
echo "$fullMsg" > "$scriptRoot/unique/$distinct.msg"
fi
# build content json
content="{"
if [[ -n "$botName" ]]; then
content="$content\"username\": $(echo "$botName" | jq -aRs .), "
fi
content="$content\"embeds\":[{\"description\": "
if [[ "$skipMsg" = false ]]; then
content="$content$message"
else
content="$content\"\""
fi
if [[ -n "$color" ]]; then
content="$content, \"color\": ${colors[$color]}"
fi
if [[ -n "$distinct" ]] || [[ -n "$title" ]]; then
content="$content, \"title\": $(echo "${title-$distinct}" | jq -aRs .)"
fi
content="$content}]}"
# actually send the message to discord
response=$(curl -s -H "Content-Type: application/json" -X POST -d "$content" "$hookUrl" | jq -r '.message')
# response message is null unless there was an issue
if [[ "$response" != "null" ]]; then
echo "$response" >&2
fi
# if the message was too long, send along the rest as a separate message
if [ -n "$extra" ]; then
script=$(basename "$0")
args=("-h" "$hookUrl")
# when we call again, we convert distinct into a title
if [[ -n "$distinct" ]] || [[ -n "$title" ]]; then
args+=("-t" "${title-$distinct}")
fi
if [[ -n "$color" ]]; then
args+=("-c" "$color")
fi
"$scriptRoot/$script" ${args[@]} "$extra"
fi