376 lines
13 KiB
Bash
Executable file
376 lines
13 KiB
Bash
Executable file
#!/bin/bash
|
|
# iron
|
|
# Simple rautafarmi (json) textboard client.
|
|
# Licensed under GNU GPLv3.
|
|
# See https://codeberg.org/speedie/iron or https://speedie.gq/iron for more information.
|
|
|
|
# wrapper funcs
|
|
printn() { printf "%s$*"; }
|
|
printstr() { printn "%s$*\n"; }
|
|
die() { printstr "%s$*" && exitclient 1; }
|
|
definstance="${definstance:-https://example.com}"
|
|
|
|
cleancli() {
|
|
rm -f "$working_dir/iron-editor-message" \
|
|
"$working_dir/iron-messagedata" \
|
|
"$working_dir/iron-message" \
|
|
"$working_dir/iron-api" \
|
|
"$working_dir/iron-api-orig"
|
|
}
|
|
|
|
updateborder() {
|
|
sp="$(seq "$(tput cols)" | xargs -Iz printf "${color_01}$s${color_01}")"
|
|
return
|
|
}
|
|
|
|
exitclient() {
|
|
[ -z "$1" ] && exitcode="0" || exitcode="$1"
|
|
endmsg="iron terminated. (exit code $exitcode)"
|
|
|
|
# clean working dir
|
|
rm -f "$working_dir/iron-editor-message" \
|
|
"$working_dir/iron-messagedata" \
|
|
"$working_dir/iron-message" \
|
|
"$working_dir/iron-api" \
|
|
"$working_dir/iron-api-orig"
|
|
clear
|
|
printstr "$endmsg"
|
|
exit "$exitcode"
|
|
}
|
|
|
|
end_session() {
|
|
rm -f "$working_dir/iron-session" \
|
|
"$working_dir/iron-editor-message" \
|
|
"$working_dir/iron-messagedata" \
|
|
"$working_dir/iron-api" \
|
|
"$working_dir/iron-api-orig"
|
|
clear
|
|
${0}
|
|
exitclient 0
|
|
}
|
|
|
|
# init
|
|
main() {
|
|
# few basic checks
|
|
[ -z "$(command -v jq)" ] && die "fatal error: jq not found."
|
|
[ -z "$working_dir" ] && working_dir=$HOME/.local/share/iron
|
|
|
|
# check args
|
|
case "$1" in
|
|
"-h") printf "usage: iron [-c]\n" && exit 0 ;;
|
|
"") : ;;
|
|
"-c") rm -rf "$working_dir" && exit 0 ;;
|
|
*) printf "usage: iron [-c]\n" && exit 1 ;;
|
|
esac
|
|
|
|
cleancli
|
|
|
|
mkdir -p "$working_dir"
|
|
|
|
[ -e "$working_dir/iron-instance" ] || printstr "$definstance" > "$working_dir/iron-instance"
|
|
[ -e "$working_dir/iron-ckey" ] && [ -z "$ckey" ] && ckey="$(cat "$working_dir/iron-ckey")"
|
|
|
|
instance="$(cat "$working_dir/iron-instance")"
|
|
[ -z "$instance" ] && instance="example.com" # fallback
|
|
|
|
# hardcoded line count, this is the max lines our messages can take up, otherwise this code sucks
|
|
lc="$(($(tput lines) - 7))"
|
|
|
|
# ansi escape codes, by default we use 7 colors
|
|
[ -z "$NOCOLOR" ] && \
|
|
color_01="$(printf '\033[0m')"
|
|
color_02="$(printf '\033[0;33m')"
|
|
color_03="$(printf '\033[0;35m')"
|
|
color_04="$(printf '\033[0;36m')"
|
|
color_05="$(printf '\033[0;33m')"
|
|
color_06="$(printf '\033[0;31m')"
|
|
color_07="$(printf '\033[0;32m')"
|
|
|
|
name="iron"
|
|
ver="1.3"
|
|
exver="09-04-2022"
|
|
s='─'
|
|
sc="$(tput cols)"
|
|
curmode="-- NORMAL --" # default mode is normal mode
|
|
|
|
updateborder
|
|
|
|
dl_api
|
|
|
|
[ -f "$working_dir/iron-session" ] && \
|
|
printstr "Existing session exists, joining.." || ph
|
|
|
|
EDITOR="${EDITOR:-vim}"
|
|
|
|
clear
|
|
|
|
printheader
|
|
|
|
# die if no instance
|
|
if [ -z "$instance" ]; then
|
|
printn "${color_06}000 - SYSTEM at $(date '+%Y/%m/%d %T:')\nInstance is not set. Please set it!.${color_01}" && sleep 3
|
|
rm "$working_dir/iron-session"
|
|
$0 && exitclient 0
|
|
fi
|
|
|
|
[ "$ckey" != "IRON_NO_CAPTCHA" ] && [ -z "$ckey" ] && printn "${color_06}000 - SYSTEM at $(date '+%Y/%m/%d %T:')\nWarning: No captcha was provided. This may cause messages to not send properly. Set the captcha to IRON_NO_CAPTCHA to hide this warning.${color_01}\n" && sleep 2 && clear && printheader
|
|
[ "$ckey" = "IRON_NO_CAPTCHA" ] && ckey="" # then we don't need a captcha, according to the user who is probably an idiot
|
|
|
|
# check connection
|
|
ping -q "$(dirname "$instance" | sed "s|https://||g")" -c 1 > /dev/null || die "error: could not download api data"
|
|
|
|
parse_msgs
|
|
checkupt
|
|
}
|
|
|
|
dl_api()
|
|
{
|
|
# download everything from the api
|
|
curl -so "$working_dir/iron-api-orig" "$instance/api.php"
|
|
[ -e "$working_dir/iron-api-orig" ] && sed "s|\n|${color_01}\n|g" "$working_dir/iron-api-orig" > "$working_dir/iron-api" || dl_api
|
|
}
|
|
|
|
ph() {
|
|
clear
|
|
|
|
printstrh() {
|
|
printstr "$name $ver"
|
|
printstr "$sp"
|
|
printstr "${color_02}Last login: $(date '+%Y/%m/%d at %T')${color_01}"
|
|
printstr "${color_07}$name was started: '$(date '+%T')'.\n"
|
|
printstr "Welcome to $name $ver. $name is a rautafarmi textboard client with Vim keybinds."
|
|
|
|
[ -z "$instance" ] && pinstance="None set." || pinstance="$instance"
|
|
|
|
# print instance text/url
|
|
printn "\n${color_03}Instance: '$pinstance'\n${color_01}"
|
|
printn "\n${color_02}iron written by speedie & potential contributors and is licensed under the GNU General Public License version 3.0 free software license."
|
|
printn "\n${color_05}See https://speedie.gq/projects/iron.php or https://codeberg.org/speedie/iron for more information.${color_01}\n"
|
|
}
|
|
|
|
printstrh
|
|
|
|
while true; do
|
|
# get user cmd
|
|
read -r -n 1 iscmd
|
|
|
|
# check what to do
|
|
if [ "$iscmd" != ":" ]; then
|
|
case "$iscmd" in
|
|
"Z") read -r -n 1 zbind ;;
|
|
esac
|
|
|
|
clear; printstrh
|
|
else
|
|
read -r bind
|
|
fi
|
|
|
|
# ZZ and ZQ binds
|
|
case "$zbind" in
|
|
"ZZ") $0 && exitclient 0 ;;
|
|
"ZQ") exitclient 0 ;;
|
|
esac
|
|
|
|
# misc binds
|
|
case "$bind" in
|
|
"Q") exitclient 0 ;;
|
|
"Z") $0 && exitclient 0 ;;
|
|
"q") exitclient 0 ;;
|
|
"q!") exitclient 0 ;;
|
|
"quit") exitclient 0 ;;
|
|
"j") touch "$working_dir/iron-session" && main ;;
|
|
"set captcha") set_cap ;;
|
|
"set instance") clear ; printheader ; printn "Enter your instance (Example: https://example.com/ironfarms):\n> " ; read -r instance ; printf "%s" "$instance" > "$working_dir/iron-instance" && clear && printheader && \
|
|
printf "%s000 - SYSTEM at %s\nSet instance to '%s'.%s" "${color_06}" "$(date '+%Y/%m/%d %T:')" "$instance" "${color_01}" && sleep 3 && ${0} && exitclient 0 ;;
|
|
"set instance default") echo "$definstance" > "$working_dir/iron-instance" && clear && printheader && \
|
|
printf "%s000 - SYSTEM at %s\nSet instance to '%s'.%s" "${color_06}" "$(date '+%Y/%m/%d %T:')" "$definstance" "${color_01}" && sleep 3 && ${0} && exitclient 0 ;;
|
|
"reset") rm -rf "$working_dir" && ${0} && exitclient 0 ;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# print basic header and then the separator
|
|
printheader() { printf "%s %s\n%s\n" "$name" "$ver" "$sp"; }
|
|
|
|
# captcha
|
|
set_cap() {
|
|
clear
|
|
printheader
|
|
printn "Enter your captcha answer. This can often be found on the instance website. Note that if this is randomized then iron is not very convenient to use. In this case switch instance or use the official client instead. (Example: 43):\n> "
|
|
read cap
|
|
printstr "$cap" > "$working_dir/iron-ckey" && clear && printheader && printf "%s000 - SYSTEM at %s\nSet captcha to '%s'.%s" "${color_06}" "$(date '+%Y/%m/%d %T:')" "$cap" "${color_01}" && sleep 3 && ${0} && exitclient 0
|
|
}
|
|
|
|
parse_msgs() {
|
|
if [ "$do_not_reparse" != "true" ]; then
|
|
# fail if the json is not valid
|
|
failed_to_parse_json() {
|
|
# originally wanted to fail the client completely if the json isn't valid
|
|
# there is currently some weird bug though so for now we'll restart it to hide the problem
|
|
clear
|
|
rm -f "$working_dir/iron-messagedata" "$working_dir/iron-message"
|
|
$0 && exitclient 0
|
|
}
|
|
|
|
rm -f "$working_dir/iron-editor-message" \
|
|
"$working_dir/iron-messagedata" \
|
|
"$working_dir/iron-message" \
|
|
"$working_dir/iron-api" \
|
|
"$working_dir/iron-api-orig"
|
|
|
|
dl_api
|
|
|
|
# save individual data to combine later
|
|
jq -e ".posts[] .username" "$working_dir/iron-api" > "$working_dir/iron-username" || failed_to_parse_json
|
|
jq -e ".posts[] .message" "$working_dir/iron-api" > "$working_dir/iron-message" || failed_to_parse_json
|
|
jq -e ".posts[] .time" "$working_dir/iron-api" > "$working_dir/iron-messagetime" || failed_to_parse_json
|
|
jq -e ".posts[] .postID" "$working_dir/iron-api" > "$working_dir/iron-messageid" || failed_to_parse_json
|
|
jq -e ".posts[] .imageURL" "$working_dir/iron-api" > "$working_dir/iron-messageimg" || failed_to_parse_json
|
|
|
|
# delete prev message data
|
|
rm -f "$working_dir/iron-messagedata"
|
|
|
|
# format it somewhat
|
|
# we will mostly print this file as it is
|
|
# replace "" in username because it means none was specified.
|
|
# replace "%" with "%%" so that printf will actually print it.
|
|
for i in $(seq $lc); do
|
|
username="$(head -n "$i" "$working_dir/iron-username" | tail -n 1 | sed 's/""/Anonymous/g')"
|
|
message="$(head -n "$i" "$working_dir/iron-message" | tail -n 1 | sed "s/%/%%/g; s/>>/DISREPLY/g; s/>/DISGTEXT/g; s/DISREPLY/${color_04}>>/g; s/DISGTEXT/${color_07}>/g; s/\" \"//g")"
|
|
time="$(head -n "$i" "$working_dir/iron-messagetime" | tail -n 1)"
|
|
id="$(head -n "$i" "$working_dir/iron-messageid" | tail -n 1)"
|
|
imageurl="$(head -n "$i" "$working_dir/iron-messageimg" | tail -n 1 | sed 's/""/iron_no_image/g')"
|
|
|
|
# image url formatting
|
|
imgformattingb="]"
|
|
[ -z "$message" ] && [ "$imageurl" != "iron_no_image" ] && imgformattinga="[URL: "
|
|
[ ! -z "$message" ] && imgformattinga=" [URL: "
|
|
[ "$imageurl" = "iron_no_image" ] && imgformattinga="" && imgformattingb="" && imageurl=""
|
|
|
|
# print it
|
|
printf "%s%s - %s%s%s at %s%s:%s\n%s%s%s%s%s%s%s\n" \
|
|
"${color_04}" "$id" "${color_05}" "$username" "${color_02}" "${color_03}" "$time" "${color_01}" "$message" "${color_01}" "${color_04}" "$imgformattinga" "$imageurl" "$imgformattingb" "${color_01}" | sed 's/"//g' | sed "s/.*/${color_01}&${color_01}/" >> "$working_dir/iron-messagedata"
|
|
done
|
|
fi
|
|
|
|
# get the number of messages we need from the parsed list of stuff
|
|
fold -w${sc} "$working_dir/iron-messagedata" | head -n "$lc"
|
|
|
|
# print footer
|
|
printstr "${color_01}$sp"
|
|
printstr "${color_04}I: ${instance} L: ${lc}/$(wc -l < "$working_dir/iron-message") LL: $(date '+%Y/%m/%d at %T')${color_01}"
|
|
printstr "${sp}"
|
|
|
|
do_not_reparse=false
|
|
}
|
|
|
|
# send the message using curl that we composed
|
|
sendmsg()
|
|
{
|
|
[ -e "$working_dir/iron-editor-message" ] && smsg="$(cat "$working_dir/iron-editor-message")"
|
|
|
|
if [ -z "$ckey" ]; then
|
|
curl -sX POST "$instance/post.php" -F "message=$smsg" > /dev/null && curl_status=sent
|
|
else
|
|
curl -sX POST "$instance/post.php" -F "message=$smsg" -F "captcha=$ckey" > /dev/null && curl_status=sent
|
|
fi
|
|
|
|
# if it was sent properly
|
|
if [ "$curl_status" = "sent" ]; then
|
|
# print system status, if message was sent
|
|
if [ ! -z "$smsg" ]; then
|
|
clear
|
|
printheader
|
|
printn "${color_06}000 - SYSTEM at $(date '+%Y/%m/%d %T:')\nMessage '$smsg' sent. ${color_01}" && sleep 0.5
|
|
else
|
|
clear
|
|
printheader
|
|
printn "${color_06}000 - SYSTEM at $(date '+%Y/%m/%d %T:')\nError: Message cannot be empty. ${color_01}" && sleep 0.5
|
|
fi
|
|
else
|
|
clear
|
|
printheader
|
|
printn "${color_06}000 - SYSTEM at $(date '+%Y/%m/%d %T:')\nError: Server did not accept the message.\nCheck with your instance maintainer or try composing another message. ${color_01}" && sleep 0.5
|
|
fi
|
|
|
|
rm -f "$working_dir/iron-editor-message"
|
|
clear
|
|
${0} && exitclient 0
|
|
}
|
|
|
|
# check for new messages by comparing printed messages and downloaded messages
|
|
checkupt() {
|
|
while true; do
|
|
[ ! -e "$working_dir/iron-messagedata" ] && break
|
|
[ "$sc" != "$(tput cols)" ] && updateborder
|
|
|
|
check_mode
|
|
done
|
|
|
|
dl_api; parse_msgs
|
|
}
|
|
|
|
# compose a message
|
|
insert() {
|
|
clear
|
|
do_not_reparse=true # don't download new messages and parse the api. this saves time as we don't need it now anyway!
|
|
printheader
|
|
parse_msgs
|
|
printn "%s-- INSERT --\n> "
|
|
read smsg && ed=normal
|
|
|
|
[ -z "$smsg" ] && ${0} && exitclient 0
|
|
|
|
sendmsg
|
|
${0} && exitclient 0
|
|
}
|
|
|
|
# run a command and perform its action
|
|
cmd() {
|
|
read -r cmd > /dev/null
|
|
|
|
case "$cmd" in
|
|
"q") exitclient 0 ;;
|
|
"l") end_session ;;
|
|
"r") rm -f "$working_dir/iron-messagedata" && ${0} && exitclient 0 ;;
|
|
"reset") rm -rf "${working_dir:?}/" && ${0} && exitclient 0 ;;
|
|
"e") touch "$working_dir/iron-editor-message" ; $EDITOR "$working_dir/iron-editor-message" && ed=editor && sendmsg ;;
|
|
esac
|
|
|
|
$0 && exitclient 0
|
|
}
|
|
|
|
# ZZ/ZQ actions
|
|
zbind() {
|
|
read -r -n 1 curmode
|
|
|
|
# check mode
|
|
case "$curmode" in
|
|
"") curmode="NORMAL" && checkupt ;; # check for update, no mode change or keybind
|
|
"Q") exitclient 0 ;;
|
|
"Z") end_session ;;
|
|
esac
|
|
|
|
return
|
|
}
|
|
|
|
check_mode() {
|
|
while true; do
|
|
read -r -t 1 -n 1 curmode > /dev/null
|
|
|
|
case "$curmode" in
|
|
"i") curmode="INSERT" && insert ;;
|
|
"") curmode="NORMAL"; do_not_reparse=true; clear; printheader; parse_msgs ;;
|
|
"r") rm -f "$working_dir/iron-messagedata" "$working_dir/iron-message" && $0 && exitclient 0 ;;
|
|
":") cmd ;;
|
|
"e") touch "$working_dir/iron-editor-message" ; $EDITOR "$working_dir/iron-editor-message" && ed=editor && sendmsg ;;
|
|
"Z") zbind ;;
|
|
"l") end_session ;;
|
|
esac
|
|
|
|
checkupt || break
|
|
done
|
|
}
|
|
|
|
main "$@"
|