Compare commits

...

14 Commits

Author SHA1 Message Date
c6ef55cb0c Add a separator before the 'Posted from' message if showClient is set to true. 2024-05-12 20:57:57 -04:00
05f236df25 A few more changes, added the ability to show the client in posts. 2024-05-12 17:55:17 -04:00
a6849cceea Added the ability to give a post a title. 2024-05-12 17:37:05 -04:00
5eff83b3ca Updates to the music function so that it now uses the post_status function to do the actual work of posting. Fixed problems of visibility not respecting the setting. 2024-05-12 17:33:49 -04:00
220d5c3d30 Music post sound now works. 2024-05-11 20:52:52 -04:00
2e8bb5961a Lots of updates, removed the timeline display code because it's buggy and the web client does it better anyway. Repurposed this program to do posting from the cli, so for music, or from other programs, or quick posts without having the leave the cli. 2024-05-11 18:16:00 -04:00
cc1dab3fdb Experimental new code using getopt, it seems to work better, but who knows lol. 2023-02-04 00:05:31 -05:00
da6a960bcd I think I fixed the weird posting bug. Posts from within the main loop should work as expected now. 2023-02-02 20:43:30 -05:00
cabe5058ff Updated displayed text for timelines. Trying to get only new stuff to show up when it's refreshed, but that's not quite working yet. 2023-02-02 03:19:46 -05:00
d7aa704cf1 Not much luck improving the jq stuff yet, but I did fix ' showing up as '. 2023-02-01 22:51:26 -05:00
36513b8db6 I think there's now decent error detection for posting music. 2023-02-01 17:55:28 -05:00
870cf0e4fd Added player name into music post. 2023-02-01 13:20:21 -05:00
fb97aab81f Improved handling for posts, now they can be piped through ratatoskr.sh -p. Also, with no pipe and no arguments, type your message and hit control+d when done to post. 2023-02-01 02:02:18 -05:00
a6da379c5f A bit of code reorganization. Added /refresh command. Actually displaying stuff and grabbing the latest only still needs some work. 2023-01-31 18:18:39 -05:00
2 changed files with 97 additions and 133 deletions

View File

@ -1,3 +1,11 @@
# ratatoskr # ratatoskr
A command line client for Pleroma, written in bash. A command line client for Pleroma, written in bash. Originally it was to be a client with viewing as well as posting features, but there are plenty of clients that do decent formatting of statuses, and the web client also does a great job, especially with things like threading. So This project is mainly for posting to your instance.
It can be used to post things you may want to send like playing music, or it can be used to post from piped in information as in:
echo "Hello world" | ./ratatoskr -p
It's still very much a work in progress, but have fun with it anyway. :)
One thing not in help, if you want to display the client in your posts, add showClient="true" to your ~/.config/ratatoskr/default.conf file.

View File

@ -1,18 +1,11 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Hopefully one day this will be a full featured Pleroma client.
# Let's see how far we can get. :)
# Handle subprocesses that may not close with the main program.
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
# Display usage information. # Display usage information.
help() { help() {
echo "${0##*/}" echo "${0##*/}"
echo "Released under the terms of the WTFPL License" echo "Released under the terms of the WTFPL License"
echo -e "Usage:\n" echo -e "Usage:\n"
echo "With no arguments, open the interactive client."
for i in "${!command[@]}" ; do for i in "${!command[@]}" ; do
echo "-${i/:/ <parameter>}: ${command[${i}]}" echo "-${i/:/ <parameter>}: ${command[${i}]}"
done | sort done | sort
@ -66,9 +59,14 @@ play_sound() {
if [[ "${enable_sound}" == "false" ]]; then if [[ "${enable_sound}" == "false" ]]; then
return return
fi fi
local soundFile="${configPath}/soundpacks/${sound_pack:-default}/${1}.opus" soundName="$*"
if [[ -e "${soundFile}" ]]; then declare -A sounds=(
sox -qV0 "${soundFile}" -d &> /dev/null & [error]="|sox -n -p synth saw E2 fade 0 0.25 0.05 repeat norm -7"
[music]="|sox -np synth pl E2 pl B2 overdrive 100 remix - overdrive 100 fade h .25 1 .25 norm -11"
[status]='|sox -np synth .05 tri C2:C7'
)
if [[ -n "${sounds[${soundName}]}" ]]; then
sox -qV0 "${sounds[${soundName}]}" -d gain -9 &> /dev/null &
fi fi
} }
@ -102,60 +100,63 @@ scrobble_music() {
# Post music with -M flag requires playerctl. # Post music with -M flag requires playerctl.
post_music() { post_music() {
local text="$(playerctl metadata -f 'Now playing "{{title}}" by "{{artist}}" from "{{album}}"')" local text="$(playerctl metadata -f 'Now playing "{{title}}" by "{{artist}}" from "{{album}}" via "{{playerName}}"')"
text="${text//Now playing \"\"/Now Playing}"
text="${text// by \"\"/}"
text="${text// from \"\"/}"
if [[ "${text}" =~ ^"Now playing via" ]]; then
echo "Error, no music was detected. Maybe it is not properly tagged?"
play_sound error
exit 1
fi
local link="$(playerctl metadata -f '{{xesam:url}}')" local link="$(playerctl metadata -f '{{xesam:url}}')"
if [[ "${link}" =~ ^file:// ]]; then if [[ "${link}" =~ ^file:// ]]; then
link="https://www.youtube.com/results?search_query=$(playerctl metadata -f '{{artist}} {{title}}' | urlencode -b)" link="https://www.youtube.com/results?search_query=$(playerctl metadata -f '{{artist}} {{title}}' | urlencode -b)"
fi fi
local json=$(jq -n --arg status "[${text}](${link})" --arg spoiler_text "Music" --arg content_type "text/markdown" '{status: $status, spoiler_text: $spoiler_text, content_type: $content_type}') statusSpoiler_text="Music"
local result postType="music"
result="$(curl -sS --oauth2-bearer "${oauth_token}" -H "Content-Type: application/json" \ post_status "[${text}](${link})"
-d "$json" \
"${instanceURL}/api/v1/statuses")"
# Check for errors
if [[ $? -ne 0 ]]; then
echo "there was a problem contacting the server"
play_sound error
exit 1
fi
local error="$(echo "$result" | jq -r '.error')"
if [[ "$error" != "null" ]]; then
echo "Error: $error"
play_sound error
exit 1
fi
echo "Music posted!"
play_sound post_music
} }
# Post status with -p flag, command line. # Post status with -p flag, command line.
post_status() { post_status() {
local text="$@" if [[ "${*}" == "" ]]; then
visibility="${visibility:-public}" return
local content_type="${content_type:-text/markdown}" fi
local json="$(jq -n --arg status "$text" \ postType="${postType:-status}"
--arg spoiler_text "$spoiler_text" \ local statusText="$*"
--arg visibility "$visibility" \ statusVisibility="${statusVisibility:-public}"
--arg content_type "$content_type" \ statusContent_type="${statusContent_type:-text/markdown}"
if [[ "${showClient}" == "true" ]]; then
if [[ "${statusContent_type}" == "text/markdown" ]]; then
statusText+=$'\n'$'\n''----'$'\n'"[Posted from Ratatoskr](https://git.stormux.org/storm/ratatoskr)"
else
statusText+=$'\n'$'\n'"Posted from Ratatoskr: https://git.stormux.org/storm/ratatoskr"
fi
fi
local statusJson="$(jq -n --arg status "$statusText" \
--arg spoiler_text "$statusSpoiler_text" \
--arg visibility "$statusVisibility" \
--arg content_type "$statusContent_type" \
'{ status: $status, spoiler_text: $spoiler_text, visibility: $visibility, content_type: $content_type }')" '{ status: $status, spoiler_text: $spoiler_text, visibility: $visibility, content_type: $content_type }')"
local result local statusResult
result="$(curl -sS --oauth2-bearer "${oauth_token}" -H "Content-Type: application/json" \ statusResult="$(curl -sS --oauth2-bearer "${oauth_token}" -H "Content-Type: application/json" \
-d "$(echo "$json" | jq 'if .spoiler_text == "" then del(.spoiler_text) else . end | if .visibility == "" then del(.visibility) else . end')" \ -d "$(echo "$statusJson" | jq 'if .spoiler_text == "" then del(.spoiler_text) else . end | if .visibility == "" then del(.visibility) else . end')" \
"${instanceURL}/api/v1/statuses")" "${instanceURL}/api/v1/statuses")"
if [[ $? -ne 0 ]]; then if [[ $? -ne 0 ]]; then
echo "there was a problem contacting the server" echo "there was a problem contacting the server"
play_sound error play_sound error
exit 1 exit 1
fi fi
local error="$(echo "$result" | jq -r '.error')" local error="$(echo "$statusResult" | jq -r '.error')"
if [[ "$error" != "null" ]]; then if [[ "$error" != "null" ]]; then
echo "Error: $error" echo "Error: $error"
play_sound error play_sound error
exit 1 exit 1
fi fi
echo "Status posted!" echo "${postType^} posted!"
play_sound post play_sound "${postType}"
} }
@ -185,17 +186,6 @@ done
# make sure the configuration and soundpack paths exist: # make sure the configuration and soundpack paths exist:
mkdir -p "${configPath}/soundpacks" mkdir -p "${configPath}/soundpacks"
# Keep track of the backgrounded loop
bgLoop=1
# Associative array of command line parameters and short description of what they do.
declare -A command=(
[C]="Recreate default configuration file. Acquire new oauth token."
[h]="Help, show usage information for ${0##*/}."
[M]="Post the currently playing music track, requires playerctl."
[p:]="Post from the command line, e.g. ${0##*/} -p \"hello world\""
[S]="Scrobble the currently playing music track, requires playerctl."
)
# if the default file doesn't exist, create it # if the default file doesn't exist, create it
if [[ ! -e "${configPath}/${configFile}" ]]; then if [[ ! -e "${configPath}/${configFile}" ]]; then
@ -205,91 +195,57 @@ else
source "${configPath}/${configFile}" source "${configPath}/${configFile}"
fi fi
# Associative array of command line parameters and short description of what they do.
declare -A command=(
[C]="Recreate default configuration file. Acquire new oauth token."
[h]="Help, show usage information for ${0##*/}."
[M]="Post the currently playing music track, requires playerctl."
[p::]="Post from the command line, e.g. ${0##*/} -p \"hello world\" or echo \"Hello world\" | ${0}"
[t:]="Title of your post."
[v:]="Post visibility, [direct | followers | public | unlisted], default is public."
[S]="Scrobble the currently playing music track, requires playerctl."
)
# Handle command line parameters # Handle command line parameters
# Convert the keys of the associative array to a format usable by getopts # Convert the keys of the associative array to a format usable by getopt
args="${!command[*]}" shortOptions="${!command[*]}"
args="${args//[[:space:]]/}" shortOptions="${shortOptions//[[:space:]]/}"
while getopts "${args}" i ; do
case "$i" in options="$(getopt -o "$shortOptions" -- "$@")"
C) get_oauth_token;; eval set -- "$options"
h) help;;
M) while [[ $# -gt 0 ]]; do
case $1 in
-C)
get_oauth_token
shift;;
-t)
statusSpoiler_text="${2}"
shift 2;;
-v)
statusVisibility="${2}"
shift 2;;
-h)
help
exit 0;;
-M)
post_music post_music
exit 0;; exit 0;;
p) -p)
post_status "${OPTARG}" if read -rt 0 ; then
post_status "$(cat)"
else
shift 3
post_status "$@"
fi
exit 0;; exit 0;;
S) -S)
scrobble_music scrobble_music
exit 0;; exit 0;;
esac
done
# Main loops
# Display timelines and requested information.
# Important, set bgLoop to 0 or this loop will not close with the program
bgLoop=0
while : ; do
if [[ -n "${since_id}" ]]; then
result="$(curl -sS --oauth2-bearer "${oauth_token}" "${instanceURL}/api/v1/timelines/${timeline:-home}" -d "since_id=${since_id}")"
else
result="$(curl -sS --oauth2-bearer "${oauth_token}" "${instanceURL}/api/v1/timelines/${timeline:-home}")"
fi
# Error checking
# Check if the result is a valid JSON
if ! echo "$result" | jq '.' >/dev/null 2>&1; then
echo "Error: The response from the server was invalid."
play_sound error
sleep "${interval:-300}"
continue
fi
error=$(echo "$result" | jq -r '.error // "null"' 2> /dev/null)
if [[ "${error:-null}" != "null" ]]; then
echo "Error fetching ${timeline:-home} timeline: $error"
play_sound error
sleep "${interval:-300}"
continue
fi
# process the response to get the latest event id
latest_id="$(jq -r '.[].id' <<< "$result")"
if [[ "${since_id}" != "${latest_id}" ]]; then
# handle new events
events="$(jq -r '.[].content' <<< "$result")"
stripped_events="$(echo "$events" | sed -E 's/<[^>]+>//g')"
usernames="$(jq -r '.[].account.username' <<< "$result")"
echo -e "$usernames" | while read -r username; do
echo "$username: $(echo "$stripped_events" | head -n 1)"
stripped_events="$(echo "$stripped_events" | tail -n +2)"
echo
done
play_sound new_${timeline}
since_id="$latest_id"
fi
sleep "${interval:-300}"
done &
# Handle commands
while : ; do
# Command prompt:
read -er command
if [[ ! "${command}" =~ ^/ ]]; then
post_status "${command}"
continue
fi
case "${command}" in
"/exit"|"/quit")
exit 0
;;
*) *)
echo "Error: '${command}' is not a valid command." # unexpected option
play_sound error help
;; exit 1;;
esac esac
done done