#!/bin/sh -


readonly _command="${0##*/}"
readonly _version='0.3.2'

#give you some color see see
RED=$(printf '\033[31m')
GREEN=$(printf '\033[32m')
YELLOW=$(printf '\033[33m')
BLUE=$(printf '\033[34m')
BOLD=$(printf '\033[1m')
DIM=$(printf '\033[2m')
UNDER=$(printf '\033[4m')
RESET=$(printf '\033[m')


if [ -d '/proc/' ]; then
	#openwrt not have ps -p
	_now_sh="$(readlink "/proc/$$/exe")"
	_now_sh="${_now_sh##*/}"
else
	#ios and macos not have "/proc/"
	_now_sh="$(basename "$(echo "$(ps -p $$ -o command=)" | awk '{print $1}')")"
fi
if [ ! -t 0 ]; then
	case "${_now_sh}" in
		dash)
			while IFS= read -r line || [ -n "$line" ]; do
				_incoming="$_incoming$line"'\n'
			done
			_incoming="$(printf '%b\n' "${_incoming}")"
		;;
		sh)
			if readlink "$(which sh)" | grep -q 'dash$'; then
				while IFS= read -r line || [ -n "$line" ]; do
					_incoming="$_incoming$line"'\n'
				done
				_incoming="$(printf '%b\n' "${_incoming}")"
			else
				read -d $'\0' -r _incoming
			fi
		;;
		*)
			read -d $'\0' -r _incoming
		;;
	esac
fi

#I only wrote the APT method to get the version number, but I didn't use other system.
#emmmmmmmm
_get_package_version(){
	if [ "${1}" = 'sh' ]; then
		_command2="$(readlink "$(which sh)")"
		_command2="${_command2##*/}"
	else
		_command2="${1}"
	fi
	#If it is not Debian, return the unknown version directly
	if which dpkg 2>>/dev/null 1>>/dev/null; then
		if dpkg -s "${_command2}" 2>>/dev/null 1>>/dev/null; then
			echo "${_command2}/$(dpkg -s "${_command2}" | grep '^Version: ' | sed 's#^Version: ##')"
		else
			_command_p="$(which "${_command2}")"
			if ! dpkg -S "${_command_p}" 2>>/dev/null 1>>/dev/null; then
				echo "${_command2}/unknown"
			else
				_command_p="$(dpkg -S "${_command_p}")"
				_command_p="${_command_p##*/}"
				echo "${_command2}/$(dpkg -s "${_command_p}" | grep '^Version: ' | sed 's#^Version: ##')"
			fi
		fi
	else
		echo "${_command2}/unknown"
	fi
	unset _command2 _command_p
}

_user_agent="$(_get_package_version 'curl') $(uname -s)/$(uname -r) $(_get_package_version "${_now_sh}") ${_command}/${_version}(shell script)"
#need to install jo to build json
_use_request='POST'
if ! which jo 2>>/dev/null 1>>/dev/null; then
	_not_use_json='1'
fi
#The default encryption in Bark App is like this, copy it first
_server='api.day.app'
_encryption_algorithm='aes-128'
_encryption_mode='cbc'
_encryption_padding='pkcs7'


_print_help(){
	echo "Usage: ${_command} [OPTIONS...]
	-h   --help						Print this help
	-X   --request <GET/POST/encryption>			Specify request method to use
	-k   --key <bark_key>					Specify the device key to push to
	-1   --title <title>					Specify the title to be push
	-2   --body <body>					Specify the content to be push
	-l   --level <active/timeSensitive/passive/critical>	Specify the interruption level of the push
	-lv  --level-volume <1/10>				Specify the volume of the sound used to push critical notifications
	-b   --badge <number>					Specify the app badge (will overwrite the existing one)
	-a   --autocopy <0/1>					Specify whether to enable auto-copy
	-c   --copy <string>					Specify the content to be copied to the clipboard
	-c2  --call <0/1>					Specify continuous ringing when sending push notifications
	-s   --sound <string>					Specify the custom ringtone
	-i   --icon <url>					Specify the URL of the custom icon
	-g   --group <string>					Specify the notification group
	-i2  --isarchive <0/1>					Specify whether to save to the app
	-u   --url <url>					Specify the URL to jump to when clicked
	-s2  --server <domain_name>				Specify the server used to forward messages
	-ea  --encryption-algorithm <AES128/AES192/AES256>	Specify the algorithm used during encryption <AES128/AES192/AES256>
	-em  --encryption-mode <CBC/ECB>			Specify the mode used during the encryption process <CBC/ECB>
	-ep  --encryption-padding <pkcs7>			Specify the padding used during encryption process <pkcs7>
	-ek  --encryption-key <key>				Specify the key used for encryption
	-ei  --encryption-iv <iv>				Specify the iv used for encryption
	--not-use-json						Specifies not use json when use POST
Example:
	${_command} -k key -1 test_title -2 test_body
	echo 'test_body' | ${_command} -k key -1 test_title
	${_command} -X GET -k key -1 test_title -2 test_body
	echo 'test_body' | ${_command} -X POST -k key -1 test_title"
}

_print_info(){
	echo "${_command} - Helps you build notifications to your bark app using curl

Version: ${_version}
User-Agent: ${_user_agent}
Default use request method: ${_use_request}"
}

_transform_url_encoded(){
	printf %s "${1}" | jq -s -R -r @uri
}

_randnum(){
	if [ -z "${2}" ]; then
		tr -dc 'A-Za-z0-9' < /dev/urandom | head -c "${1}"
	else
		tr -dc "${1}" < /dev/urandom | head -c "${2}"
	fi
}

_GET(){
	if [ -n "${_title}" ]; then
		_title="$(_transform_url_encoded "${_title}")/"
	fi
	_body="$(_transform_url_encoded "${_body}")"
	curl -s -L -X GET "https://${_server}/${1}/${_title}${_body}$(echo "${_parameter}" | sed 's#^&#?#')" -H "User-Agent: ${_user_agent}"
}

_POST(){
	if [ -z "${_not_use_json}" ]; then
		_content="$(jo -n -- -s "title=${_title}" -s "body=${_body}" -s "level=${_level}" -n "volume=${_level_volume}" -n "badge=${_badge}" -n "autoCopy=${_autocopy}" -s "copy=${_copy}" -n "call=${_call}" -s "sound=${_sound}" -s "icon=${_icon}" -s "group=${_group}" -n "isArchive=${_isarchive}" -s "url=${_url}")"
		if [ "${_not_use_custom_batch}" = '1' ]; then
			_content="$(printf '%s\n' "${_content}" | jq -c ".device_keys=${1}")"
		else
			_content="$(printf '%s\n' "${_content}" | jq -c --arg _key "${1}" '.device_key=$_key')"
		fi
		curl -s -L -X POST "https://${_server}/push" -H "User-Agent: ${_user_agent}" -H 'Content-Type: application/json; charset=utf-8' -d "${_content}"
	else
		if [ -n "${_title}" ]; then
			_parameter="title=$(_transform_url_encoded "${_title}")&body=$(_transform_url_encoded "${_body}")${_parameter}"
		else
			_parameter="body=$(_transform_url_encoded "${_body}")${_parameter}"
		fi
		curl -s -L -X POST "https://${_server}/${1}" -d "${_parameter}" -H "User-Agent: ${_user_agent}"
	fi
}

_encryption(){
	_content="$(jo -n -- -s "title=${_title}" -s "body=${_body}" -s "level=${_level}" -n "volume=${_level_volume}" -n "badge=${_badge}" -n "autoCopy=${_autocopy}" -s "copy=${_copy}" -n "call=${_call}" -s "sound=${_sound}" -s "icon=${_icon}" -s "group=${_group}" -n "isArchive=${_isarchive}" -s "url=${_url}")"
	if [ -z "${_encryption_iv}" ]; then
		_encryption_iv="$(_randnum '16')"
		curl -s -L -X POST "https://${_server}/${1}" -H "User-Agent: ${_user_agent}" --data-urlencode "ciphertext=$(printf '%s\n' "${_content}" | openssl enc "-${_encryption_algorithm}-${_encryption_mode}" -K "$(printf "${_encryption_key}" | xxd -ps -c 200)" -iv "$(printf "${_encryption_iv}" | xxd -ps -c 200)" | base64 | tr -d '\n')" --data-urlencode "iv=${_encryption_iv}"
	else
		curl -s -L -X POST "https://${_server}/${1}" -H "User-Agent: ${_user_agent}" --data-urlencode "ciphertext=$(printf '%s\n' "${_content}" | openssl enc "-${_encryption_algorithm}-${_encryption_mode}" -K "$(printf "${_encryption_key}" | xxd -ps -c 200)" -iv "$(printf "${_encryption_iv}" | xxd -ps -c 200)" | base64 | tr -d '\n')"
	fi
}

if [ "${#}" = '0' ]; then
	_print_help
	exit 1
fi
while [ "${#}" -gt '0' ]; do
	case "${1}" in
		-h)
			_print_help
			exit 0
		;;
		-v)
			_print_info
			exit 0
		;;
		-k|--key)
			_keys="${_keys},${2}"
		;;
		-1|--title)
			_title="${2}"
		;;
		-2|--body)
			_body="${2}"
		;;
		-l|--level)
			_level="${2}"
			_parameter="${_parameter}&level=${2}"
		;;
		-lv|--level-volume)
			_level_volume="${2}"
			_parameter="${_parameter}&volume=${2}"
		;;
		-b|--badge)
			_badge="${2}"
			_parameter="${_parameter}&badge=${2}"
		;;
		-a|--autocopy)
			_autocopy="${2}"
			_parameter="${_parameter}&autoCopy=${2}"
		;;
		-c|--copy)
			_copy="${2}"
			_parameter="${_parameter}&copy=${2}"
		;;
		-c2|--call)
			_call="${2}"
			_parameter="${_parameter}&call=${2}"
		;;
		-s|--sound)
			_sound="${2}"
			_parameter="${_parameter}&sound=${2}"
		;;
		-i|--icon)
			_icon="${2}"
			_parameter="${_parameter}&icon=$(_transform_url_encoded "${2}")"
		;;
		-g|--group)
			_group="${2}"
			_parameter="${_parameter}&group=${2}"
		;;
		-i2|--isarchive)
			_isarchive="${2}"
			_parameter="${_parameter}&isArchive=${2}"
		;;
		-u|--url)
			_url="${2}"
			_parameter="${_parameter}&url=${2}"
		;;
		-s2|--server)
			_server="${2}"
		;;
		-ea|--encryption-algorithm)
			case "${2}" in
				AES128|aes128|128)
					_encryption_algorithm='aes-128'
					;;
				AES192|aes192|192)
					_encryption_algorithm='aes-192'
					;;
				AES256|aes256|256)
					_encryption_algorithm='aes-256'
					;;
				*)
					echo "${RED}Error: invalid encryption algorithm${RESET}" 1>&2
					exit 1
					;;
			esac
		;;
		-em|--encryption-mode)
			case "${2}" in
				CBC|cbc)
					_encryption_mode='cbc'
					;;
				ECB|ecb)
					_encryption_mode='ecb'
					;;
				*)
					echo "${RED}Error: invalid encryption mode${RESET}" 1>&2
					exit 1
					;;
			esac
		;;
		-ep|--encryption-padding)
			#But it seems only pkcs7? Generally, this variable can be skipped directly or not, it should be okay
			case "${2}" in
				pkcs7)
					_encryption_padding='pkcs7'
					;;
				*)
					echo "${RED}Error: invalid encryption padding${RESET}" 1>&2
					exit 1
					;;
			esac
		;;
		-ek|--encryption-key)
			_encryption_key="${2}"
		;;
		-ei|--encryption-iv)
			_encryption_iv="${2}"
		;;
		-X)
			case "${2}" in
				GET)
					_use_request='GET'
					;;
				POST)
					_use_request='POST'
					;;
				encryption)
					_use_request='POST_encryption'
					;;
				*)
					echo "${RED}Error: invalid request method${RESET}" 1>&2
					echo 'Please use "GET", "POST" or "encryption"'
					exit 1
					;;
			esac
		;;
		--not-use-json)
			_not_use_json='1'
			_skip='1'
			shift 1
		;;
		--not-use-custom-batch)
			_not_use_custom_batch='1'
			_skip='1'
			shift 1
		;;
		*)
			_skip='1'
			shift 1
		;;
	esac
	if [ "${_skip}" = '1' ]; then
		unset _skip
	else
		shift 2
	fi
done
if [ -n "${_incoming}" ]; then
	_body="${_incoming}"
	unset _incoming
fi
i='1'
i_max="$(echo "${_keys}" | grep -o ',' | wc -l)"
case "${i_max}" in
	0)
		echo "${RED}Error: No key was passed in...${RESET}"
		exit 1
		;;
	1)
		_key="${_keys#\,}"
		case "${_use_request}" in
			GET)
				_GET "${_key}"
				;;
			POST)
				_POST "${_key}"
				;;
			POST_encryption)
				_encryption "${_key}"
				;;
		esac
		;;
	*)
		if [ "${_not_use_custom_batch}" = '1' ]; then
			if [ "${_use_request}" = 'POST' ]; then
				_keys="$(echo "${_keys}" | sed 's#^,##')"
				_keys="$(echo "${_keys}" | sed 's#,#","#g')"
				_keys="[\"${_keys}\"]"
				_POST "$(printf '%s\n' "${_keys}" | jq -c)"
			else
				echo '[x] Error: unsupport'
			fi
		else
			while [ "${i}" -le "${i_max}" ]; do
				_key="$(printf '%s\n' "${_keys}" | awk -v i="$((i+1))" -F ',' '{print $i}')"
				case "${_use_request}" in
					GET)
						echo "$(echo "${_key}" | sed -E 's/^(.{4}).*(.{4})$/\1xxxx\2/'): $(_GET "${_key}")" &
						;;
					POST)
						echo "$(echo "${_key}" | sed -E 's/^(.{4}).*(.{4})$/\1xxxx\2/'): $(_POST "${_key}")" &
						;;
					POST_encryption)
						echo "$(echo "${_key}" | sed -E 's/^(.{4}).*(.{4})$/\1xxxx\2/'): $(_encryption "${_key}")" &
						;;
				esac
				unset _key
				i="$((i+1))"
			done
			wait
		fi
		;;
esac
