#!/bin/sh
instance=$(jj -i config.json instance)
alias default_auth_browser=links
instance_point="https://$instance/api/v1"
instance_point_pleroma="https://$instance/api/pleroma"
instance_hist='instance.hist'
enabled_nsfw=$(jj -i config.json enabled_nsfw)
max_statuses=$(jj -i config.json max_statuses)
default_visibility=$(jj -i config.json default_visibility)
default_content_type=$(jj -i config.json default_content_type)
format_time=$(jj -i config.json format_time)
boost_symbol=$(jj -i config.json boost_symbol)
statuses_separator=$(jj -i config.json statuses_separator)
reversed_statuses=$(jj -i config.json reversed_statuses)
quoting_reply=$(jj -i config.json quoting_reply)
#[AUTH SECTION]
mkdir -m 711 -p .app_sessions
touch .auth.json
chmod 600 .auth.json
auth="$(jj -i .auth.json "$(echo $instance | sed 's/\./\\\./g')")"
echo "Instance: $instance"
if [ -n "$auth" ]; then
default_curl_opt()
{
curl -s --compressed -H "Authorization: Bearer $auth" "$@"
}
post_request()
{
curl -s --compressed -X POST -H "Authorization: Bearer $auth" "$@"
}
echo '+Authorized account+'
else
default_curl_opt()
{
curl -s --compressed $1
}
echo 'Please make auth and restart'
fi
auth_api_create_client()
{
if [ ! -e ".app_sessions/$instance" ]; then
curl -s --compressed --url $instance_point/apps \
--data-urlencode 'client_name=pleroma-cli' \
--data-urlencode 'redirect_uris=urn:ietf:wg:oauth:2.0:oob' \
--data-urlencode 'scopes=read write follow' \
--data-urlencode 'website=https://gitea.phreedom.club/localhost_frssoft/pleroma-cli' \
--output .app_sessions/$instance
chmod 600 .app_sessions/$instance
fi
}
auth_api_get_code()
{
auth_api_create_client
client_id=$(jj -i .app_sessions/$instance client_id)
default_auth_browser "https://$instance/oauth/authorize?client_id=$client_id&response_type=code&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=read+write+follow"
echo 'Input token-code:'
read pass
}
auth_api_get_token()
{
auth_api_get_code
clear
client_id=$(jj -i .app_sessions/$instance client_id)
client_secret=$(jj -i .app_sessions/$instance client_secret)
token=$(curl -s --compressed --url https://$instance/oauth/token \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode "client_id=$client_id" \
--data-urlencode "client_secret=$client_secret" \
--data-urlencode "redirect_uri=urn:ietf:wg:oauth:2.0:oob" \
--data-urlencode 'scope=read write follow' \
--data-urlencode "code=$pass" | jj access_token)
jj -p -i .auth.json -v "$token" "$(echo $instance | sed 's/\./\\\./g')" -o .auth.json
}
#[AUTH SECTION END]
#[MIGRATION SECTION]
backup_restore_menu()
{
clear
echo "Backup/Restore menu
Backups will be placed to folder backups_$instance
Hint:
For restore create folder 'restore' and copy 'friends.csv, blocks.csv, mutes.csv' in to folder
Note: check selected instance before backup/restore"
sub_menu_lvl=1
entry1='Backup followings'
entry2='Backup blocks'
entry3='Backup mutes'
entry4='Backup all'
entry5='Restore followings'
entry6='Restore blocks'
entry7='Restore mutes'
entry8='Restore all'
while [ $sub_menu_lvl -eq 1 ]; do
choice=$(echo "Main menu\n$entry1\n$entry2\n$entry3\n$entry4\n$entry5\n$entry6\n$entry7\n$entry8" | fzy)
case $choice in
"Main menu") sub_menu_lvl=0 ;;
"$entry1") follow_api_export ;;
"$entry2") blocks_api_export ;;
"$entry3") mutes_api_export ;;
"$entry4")
echo 'Initialized... Please wait...'
follow_api_export
sleep 3
blocks_api_export
sleep 3
mutes_api_export
;;
"$entry5") follow_api_import ;;
"$entry6") blocks_api_import ;;
"$entry7") mutes_api_import ;;
"$entry8")
echo 'Initialized... Please wait...'
follow_api_import
sleep 5
blocks_api_import
sleep 5
mutes_api_import
;;
esac
done
}
backup_api_create()
{
post_request "$instance_point/pleroma/backups"
}
backup_api_list()
{
default_curl_opt "$instance_point/pleroma/backups"
}
legacy_addr_preprocess()
{
sed -E "/^[a-zA-Z0-9+_.-]+@[a-zA-Z0-9.-]+$/! s/$/@$instance/"
}
follow_api_export()
{
count=40
mkdir -m 711 -p backups_$instance
account_info=$(account_api_me)
acc_id=$(echo $account_info | jj id)
acc_following_count=$(echo $account_info | jj following_count)
while [ $count -gt 0 ]; do
followings=$(followings_api_get $acc_id)
count=$(echo $followings | jj \#)
if [ $count -eq 0 ]; then
echo "$acc_following_count followings exported"
elif [ $count -lt 40 ]; then
countindex=$(expr $count - 1)
echo $followings | jj -l \#.acct | delq | legacy_addr_preprocess >> backups_$instance/friends.csv
offset=$(echo $followings | jj $countindex.id)
echo "+$count follows"
count=0
echo "$acc_following_count followings exported"
elif [ $count -gt 0 ]; then
countindex=$(expr $count - 1)
echo $followings | jj -l \#.acct | delq | legacy_addr_preprocess >> backups_$instance/friends.csv
offset=$(echo $followings | jj $countindex.id)
echo "+$count follows"
fi
sleep 1
done
}
blocks_api_export()
{
count=40
mkdir -m 711 -p backups_$instance
while [ $count -gt 0 ]; do
blocks=$(blocks_api_get)
count=$(echo $blocks | jj \#)
if [ $count -eq 0 ]; then
echo "Blocks exported"
elif [ $count -lt 40 ]; then
countindex=$(expr $count - 1)
echo $blocks | jj -l \#.acct | delq | legacy_addr_preprocess >> backups_$instance/blocks.csv
offset=$(echo $blocks | jj $countindex.id)
echo "+$count blocks"
count=0
echo "Blocks exported"
elif [ $count -gt 0 ]; then
echo $blocks | jj -l \#.acct | delq | legacy_addr_preprocess >> backups_$instance/blocks.csv
offset=$(echo $blocks | jj $countindex.id)
echo "+$count blocks"
fi
sleep 1
done
}
mutes_api_export()
{
count=40
mkdir -m 711 -p backups_$instance
while [ $count -gt 0 ]; do
mutes=$(mutes_api_get)
count=$(echo $mutes | jj \#)
if [ $count -eq 0 ]; then
echo "Mutes exported"
elif [ $count -lt 40 ]; then
countindex=$(expr $count - 1)
echo $mutes | jj -l \#.acct | delq | legacy_addr_preprocess >> backups_$instance/mutes.csv
offset=$(echo $mutes | jj $countindex.id)
echo "+$count mutes"
count=0
echo "Mutes exported"
elif [ $count -gt 0 ]; then
echo $mutes | jj -l \#.acct | delq| legacy_addr_preprocess >> backups_$instance/mutes.csv
offset=$(echo $mutes | jj $countindex.id)
echo "+$count mutes"
fi
sleep 1
done
}
follow_api_import()
{
post_request "$instance_point_pleroma/follow_import" \
--data 'list=@restore/friends.csv'
}
blocks_api_import()
{
post_request "$instance_point_pleroma/blocks_import" \
--data 'list=@restore/blocks.csv'
}
mutes_api_import()
{
post_request "$instance_point_pleroma/mutes_import" \
--data 'list=@restore/mutes.csv'
}
#[MIGRATION SECTION END]
account_api_me()
{
default_curl_opt $instance_point/accounts/verify_credentials
}
followings_api_get()
{
default_curl_opt "$instance_point/accounts/$1/following?limit=40&max_id=$offset"
}
followings_menu()
{
account_info=$(account_api_me)
acc_id=$(echo $account_info | jj id)
acc_following_count=$(echo $account_info | jj following_count)
echo "Followers: $acc_following_count"
sub_menu_lvl=1
while [ $sub_menu_lvl -eq 1 ]; do
followings=$(followings_api_get $acc_id)
accts=$(echo $followings | jj -l \#.acct | nl -s: -v0 -w1)
choice=$(echo "Main menu\n$accts\nNext" | fzy)
case $choice in
"Main menu") sub_menu_lvl=0 ;;
"Next") offset=$(echo $followings | jj $(expr $(echo $followings | jj \#) - 1).id) ;;
*)
index=$(echo $choice | cut -f 1 -d:)
statuses_view_menu "$(echo $followings | jj $index.acct)"
;;
esac
done
}
blocks_api_get()
{
default_curl_opt "$instance_point/blocks?limit=40&max_id=$offset"
}
mutes_api_get()
{
default_curl_opt "$instance_point/mutes?limit=40&max_id=$offset"
}
thread_api_statuses()
{
default_curl_opt "$instance_point/statuses/$1/context"
}
html_to_txt_render()
{
sed -e "s/
]*>/\n/g ; s/
]*>/\n/g ; s/<[^>]*>//g ; s/>*/>/g ; s/<*/ $uri" if [ -n "$reply_to_id" ]; then echo "| Reply to: $reply_to_id" fi if [ -n "$reblog" ]; then echo "| $boost_symbol $reblog" fi spoiler_text=$(echo "$status" | jj spoiler_text) if [ -n "$spoiler_text" ]; then echo "#[$spoiler_text]" fi echo "$status" | jj -r content | delqse | html_to_txt_render attachments=$(echo $status | jj -l media_attachments.#.remote_url | delq) if [ -n "$attachments" ]; then echo "#EXTINF:-1, $uri" >> attachments.m3u8 echo "$attachments" >> attachments.m3u8 fi echo "$statuses_separator" done } statuses_view_menu() { sub_menu_lvl=2 json=$(statuses_api_account $1) while [ $sub_menu_lvl -eq 2 ]; do clear echo "[Statuses $1]" ids_massive=$(echo $json | jj -l \#.id | delq) jsonmassive=$json statuses_render menustatuses=$(echo 'Prev\nNext\nReply\nShare\nFavorite\nThread\nBack' | fzy) case "$menustatuses" in "Back") sub_menu_lvl=1 ;; "Prev") indexator=$(expr $max_statuses - 1) echo '#EXTM3U' > attachments.m3u8 clear offset=$(echo $json | jj $indexator.id) json=$(statuses_api_account $1 $offset) ;; "Next") echo '#EXTM3U' > attachments.m3u8 clear offset=$(echo $json | jj 0.id) json=$(statuses_api_account $1 '' $offset) ;; "Reply") reply_mode ;; "Share") share_mode ;; "Favorite") favourite_mode ;; "Thread") thread_open ;; esac done } thread_open() { echo 'Input id' read status_id thread_menu $status_id } thread_menu() { sub_menu_lvl=2 jsonthread=$(thread_api_statuses $1) ancestors=$(echo $jsonthread | jj ancestors) descendants=$(echo $jsonthread | jj descendants) if [ '[]' != "$ancestors" ] && [ '[]' != "$descendants" ]; then jsonthread=$(echo "$ancestors""$descendants") elif [ '[]' != "$ancestors" ]; then jsonthread="$ancestors" else jsonthread="$descendants" fi while [ $sub_menu_lvl -eq 2 ]; do clear echo '[Thread]' ids_massive=$(echo $jsonthread | jj -l \#.id | delq) jsonmassive=$jsonthread statuses_render menuthread=$(echo 'Back\nReply\nShare\nFavorite' | fzy) case "$menuthread" in "Back") sub_menu_lvl=1 ;; "Reply") reply_mode ;; "Share") share_mode ;; "Favorite") favourite_mode ;; esac done } timeline_api() { default_curl_opt "$instance_point/$timeline?limit=$max_statuses&max_id=$1&min_id=$2" | tee preload } timeline_menu() { json=$(timeline_api) sub_menu_lvl=1 echo '#EXTM3U' > attachments.m3u8 while [ $sub_menu_lvl -eq 1 ]; do ids_massive=$(echo $json | jj -l \#.id | delq) jsonmassive=$json statuses_render menu=$(echo 'Prev\nNext\nReply\nShare\nFavorite\nThread\nMain menu' | fzy) case $menu in "Prev") indexator=$(expr $max_statuses - 1) echo '#EXTM3U' > attachments.m3u8 clear offset=$(jj -i preload $indexator.id) json=$(timeline_api $offset) ;; "Next") echo '#EXTM3U' > attachments.m3u8 clear offset=$(jj -i preload 0.id) json=$(timeline_api '' $offset) ;; "Reply") reply_mode ;; "Share") share_mode ;; "Favorite") favourite_mode ;; "Thread") thread_open ;; "Main menu") sub_menu_lvl=0 ;; esac done } reply_mode() { echo 'Input id' read status_id if [ "$quoting_reply" = 'true' ]; then status_api_one $status_id | jj content | html_to_txt_render | sed 's/^/> /' > tmp_status.md fi write_status_menu $status_id } share_api_status() { post_request -w "%{http_code}" --url $instance_point/statuses/$1/reblog --output /dev/null } share_mode() { echo 'Input id (s - stop)' sharemode=1 while [ $sharemode -eq 1 ]; do read status_id if [ "$status_id" = 's' ]; then sharemode=0 else share_api_status $status_id echo $http_code fi done } favorite_api_status() { post_request -w "%{http_code}" --url $instance_point/statuses/$1/favourite --output /dev/null } favourite_mode() { echo 'Input id (s - stop)' favoritemode=1 while [ $favoritemode -eq 1 ]; do read status_id if [ "$status_id" = 's' ]; then favoritemode=0 else favorite_api_status $status_id echo $http_code fi done } write_api_status() { if [ -n "$mediaattach" ]; then media=$(upload_api_media | jj id) post_request -w "%{http_code}" --url $instance_point/statuses \ --data-urlencode "status=$1" \ --data-urlencode "content_type=$content_type" \ --data-urlencode "visibility=$status_visibility" \ --data-urlencode "in_reply_to_id=$replyto" \ --data-urlencode "media_ids[]=$media" \ --output /dev/null echo $http_code else post_request -w "%{http_code}" --url $instance_point/statuses \ --data-urlencode "status=$1" \ --data-urlencode "content_type=$content_type" \ --data-urlencode "visibility=$status_visibility" \ --data-urlencode "in_reply_to_id=$replyto" \ --output /dev/null echo $http_code fi } upload_api_media() { post_request --url $instance_point/media \ --form "file=$mediaattach" } write_status_menu() { touch tmp_status.md content_type="$default_content_type" status_visibility="$default_visibility" replyto=$1 mediaattach= http_code= if [ -n "$sub_menu_lvl" ]; then sub_menu_lvl=2 level=2 else sub_menu_lvl=1 level=1 fi while [ $sub_menu_lvl -eq $level ]; do clear echo 'Status:' cat tmp_status.md echo '\==========/' echo "Chars: $(cat tmp_status.md | wc -m)" echo "Visiblity: $status_visibility" echo "Content type: $content_type" if [ -n "$replyto" ]; then echo "Reply to: $replyto" fi if [ -n "$mediaattach" ]; then echo "Attachments: $mediaattach" fi if [ -n "$http_code" ]; then echo "Send state: $http_code" fi wrirepostmenu=$(echo "Edit\nSend\nAdd attach\nChange type\nVisiblity\nReset\nBack\nMain menu" | fzy) case $wrirepostmenu in "Edit") $EDITOR tmp_status.md ;; "Send") http_code=$(write_api_status "$(cat tmp_status.md)") ;; "Add attach") echo 'Input path to attach (ex. @image.png or @/full/path/to/attach.png)' read mediaattach ;; "Change type") content_type=$(echo 'text/plain\ntext/markdown\ntext/html' | fzy) ;; "Visiblity") status_visibility=$(echo 'public\nunlisted\nlocal\nprivate\ndirect\nlist' | fzy) ;; "Reset") echo > tmp_status.md ;; "Back") sub_menu_lvl=$(expr $level - 1) ;; "Main menu") sub_menu_lvl=0 ;; esac done } notif_menu() { sub_menu_lvl=1 clrnotif='Clear all notifications' json=$(notif_api_get_all) while [ $sub_menu_lvl -eq 1 ]; do clear echo "Notifications: $(echo $json | jj \#)" for i in $(echo $json | jj -l \#.id); do date_utc=$(echo $json | jj \#[id=$i].created_at) date -d "$date_utc" "$format_time" acct=$(echo $json | jj \#[id=$i].account.acct) typenotif=$(echo $json | jj \#[id=$i].type) echo "$typenotif <- $acct" echo $json | jj \#[id=$i].status.pleroma.content.text/plain echo '___' done menu_choice=$(echo "Main menu\nRefresh\n$clrnotif" | fzy) case "$menu_choice" in "Main menu") sub_menu_lvl=0 ;; "Refresh") json=$(notif_api_get_all) ;; "$clrnotif") notif_api_remove_all && json=$(notif_api_get_all) ;; esac done } notif_api_get_all() { default_curl_opt "$instance_point/notifications" | jj -p } notif_api_remove_all() { post_request --url $instance_point/notifications/clear } menu_write_status='Write status' menu_timeline='Timelines' notif='Notifications' followingsmenu='Followings' authmake='Auth' backup_restore='Backup/Restore' switchinstance='Switch instance' Exit='Exit' while true; do if [ -n "$auth" ]; then main_menu=$(echo "$menu_write_status\n$menu_timeline\n$notif\n$followingsmenu\n$backup_restore\n$switchinstance\n$Exit" | fzy) else main_menu=$(echo "$authmake\n$menu_timeline\n$switchinstance\n$Exit" | fzy) fi case $main_menu in "$menu_write_status") write_status_menu ;; "$menu_timeline") timeline=$(echo 'timelines/home\nfavourites\ntimelines/direct\ntimelines/public' | fzy) timeline_menu ;; "$notif") notif_menu ;; "$followingsmenu") followings_menu ;; "$backup_restore") backup_restore_menu ;; "$switchinstance") empty=0 case $(echo 'Recently used\nChoice from list\nManual input' | fzy) in "Recently used") if [ -s $instance_hist ]; then touch $instance_hist && instance=$(cat $instance_hist | fzy) else echo 'No recently used instances...' empty=1 fi ;; "Choice from list") instance=$(jj -l -i config.json public_list_instances | sed 's/"//g' | fzy) ;; "Manual input") echo "Type instance (ex. $instance):" && read instance ;; esac if [ $empty -eq 0 ]; then echo $instance >> $instance_hist cat $instance_hist | sort | uniq | tee $instance_hist 1>>/dev/null export instance export instance_point="https://$instance/api/v1" conf_instance_state=$(echo 'Permanent\nTemporaly' | fzy) if [ "$conf_instance_state" = 'Permanent' ]; then jj -i config.json instance -v $instance -o config.json else echo '' fi clear auth="$(jj -i .auth.json "$(echo $instance | sed 's/\./\\\./g')")" echo "Instance: $instance" if [ -n "$auth" ]; then default_curl_opt() { curl -s --compressed -H "Authorization: Bearer $auth" "$@" } post_request() { curl -s --compressed -X POST -H "Authorization: Bearer $auth" "$@" } echo '+Authorized account+' else default_curl_opt() { curl -s --compressed $1 } echo 'Please make auth and restart' fi fi ;; "$authmake") auth_api_get_token ;; "$Exit") exit 0 ;; esac done