Compare commits
16 Commits
0ce5af2bc4
...
master
Author | SHA1 | Date | |
---|---|---|---|
c6ef55cb0c | |||
05f236df25 | |||
a6849cceea | |||
5eff83b3ca | |||
220d5c3d30 | |||
2e8bb5961a | |||
cc1dab3fdb | |||
da6a960bcd | |||
cabe5058ff | |||
d7aa704cf1 | |||
36513b8db6 | |||
870cf0e4fd | |||
fb97aab81f | |||
a6da379c5f | |||
f790267a44 | |||
051d6e51e8 |
10
README.md
10
README.md
@ -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.
|
||||||
|
216
ratatoskr.sh
216
ratatoskr.sh
@ -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,87 +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/va/timelines/${timeline:-home}?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 '.data[0].id' <<< "$result")"
|
|
||||||
if [[ "${since_id}" != "${latest_id}" ]]; then
|
|
||||||
# handle new events
|
|
||||||
events="$(jq -r '.data[] | "\(.account.username): \(.content)"' <<< "$result")"
|
|
||||||
echo -e "$events" | while read -r event; do
|
|
||||||
echo "$event"
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
since_id="$latest_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
sleep "${interval:-300}"
|
|
||||||
done &
|
|
||||||
|
|
||||||
|
|
||||||
# Handle commands
|
|
||||||
while : ; do
|
|
||||||
# Command prompt:
|
|
||||||
read -er -p "<${softwareName}> " command
|
|
||||||
if [[ ! "${command}" =~ ^/ ]]; then
|
|
||||||
post_status "${command}"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
case "${command}" in
|
|
||||||
"/exit"|"/quit")
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
*)
|
||||||
systemMessage="Error: '${command}' is not a valid command."
|
# unexpected option
|
||||||
play_sound error
|
help
|
||||||
;;
|
exit 1;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user