Skip to content

Instantly share code, notes, and snippets.

@lgrn
Last active March 26, 2024 12:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lgrn/0fe824401dec432b985691c89028c447 to your computer and use it in GitHub Desktop.
Save lgrn/0fe824401dec432b985691c89028c447 to your computer and use it in GitHub Desktop.
#!/bin/bash
while getopts u: flag
do
case "${flag}" in
u) user=${OPTARG};;
*) echo "Bailing out."; exit 1
esac
done
if [ -z "$user" ]; then
echo "The -u (user) flag is required, formatted as username@domain.tld"; exit 1
fi
domain=$(echo $user | cut -d '@' -f 2)
user_id=$(curl --max-time 10 --connect-timeout 5 -s "https://${domain}/api/v2/search?q=${user}" | jq -r '.accounts[0].id')
if [ $? -ne 0 ] || [ -z "$user_id" ] || [ "$user_id" == "null" ]; then
echo "User ID not found for $user."; exit 1
fi
followings=""
api_endpoint="https://$domain/api/v1/accounts/$user_id/following?limit=80"
tmp_headers="headers-$(date +%s).txt"
while [ ! -z "$api_endpoint" ]; do
response=$(curl --max-time 10 --connect-timeout 5 -s -D "$tmp_headers" "$api_endpoint")
followings_iter=$(echo "$response" | jq -r '.[] | .acct') # this iteration
followings=$(echo -e "${followings}\n${followings_iter}") # append to total
next_link=$(grep -oP '(?<=<).*(?=>; rel="next")' "${tmp_headers}")
if [ ! -z "$next_link" ]; then
api_endpoint="$next_link" # move on to the next page
else
api_endpoint="" # no next link, we're done
rm headers-*.txt
fi
done
if [ "$followings" != "" ]; then
follow_count=$(echo "$followings" | grep -c -v '^$')
echo "$user is following ${follow_count} accounts."
fi
echo "Now querying instances for last post dates, please wait."
output=""
failed_accounts=""
failed_count=0
current_time=$(date -u +%s)
for acct in $followings
do
full_acct="$acct" # might be needed to allow override, dunno
if [[ $full_acct != *"@"* ]]; then # local name is missing @instance
acct_instance=$domain
full_acct="${acct}@${domain}"
else
acct_instance=$(echo $acct | cut -d '@' -f 2)
fi
remote_user_id=$(curl --max-time 10 --connect-timeout 5 -s "https://$acct_instance/api/v2/search?q=$full_acct" | jq -r '.accounts[0].id' 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$remote_user_id" ] || [ "$remote_user_id" == "null" ]; then
failed_accounts+="${full_acct}\n"
((failed_count++)); continue
fi
last_post=$(curl --max-time 10 --connect-timeout 5 -s "https://$acct_instance/api/v1/accounts/$remote_user_id/statuses" | jq -r '.[0].created_at' 2>/dev/null)
if [ $? -ne 0 ] || [ -z "$last_post" ] || [ "$last_post" == "null" ]; then
failed_accounts+="$full_acct\n"
((failed_count++)); continue
fi
last_post_epoch="$(date -u -d "$last_post" +%s)"
last_post_diff=$((current_time - last_post_epoch))
days_since=$((last_post_diff / 86400))
output+="$last_post $full_acct (${days_since}d ago)\n"
done
echo "************************************************************"
echo "* last post for accounts (oldest first) *"
echo "************************************************************"
echo -e "$output" | sort
echo -e "\n************************************************************"
echo -e "\n${failed_count} accounts failed.\n"
echo "Possible reasons: the account is private, the instance"
echo "does not support the required Mastodon API endpoints,"
echo "the account has no posts or connection timed out."
for i in $failed_accounts; do echo -e "\n${i}"; done
echo -e "************************************************************"
@lgrn
Copy link
Author

lgrn commented Mar 26, 2024

TODO: Just realized this doesn't work as expected since the mastodon API is paged when fetching all followers.
fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment