find_missing_follows.rb
Example: fetch the list of accounts followed by a given user and check which of them have been deleted / deactivated.
rb
require 'didkit'
require 'minisky'
$handle = ARGV[0]
if $handle.nil?
puts "Usage: #{$PROGRAM_NAME} <handle | did>"
exit 1
end
$did = DID.resolve_handle($handle)
if $did.nil?
puts "Couldn't resolve handle: #{$handle}"
exit 1
end
# first, we'll get the list of all follow records from the user's PDS
# (and to do that, we need to look up the PDS hostname):
pds_host = $did.document.pds_host
pds = Minisky.new(pds_host, nil, progress: '.')
print "Fetching all follows of #{$handle} from #{pds_host}: "
follows = pds.fetch_all('com.atproto.repo.listRecords',
{ repo: $did, collection: 'app.bsky.graph.follow', limit: 100 },
field: 'records')
puts
puts "Found #{follows.length} follows."
# next, we'll check which of those are visible on the AppView:
appview = Minisky.new('api.bsky.app', nil)
profiles = []
i = 0
puts
print "Fetching profiles of all followed accounts: "
# getProfiles lets us load multiple profiles in one request, but only up to 25,
# so we slice them into batches of 25 each
#
# (alternatively, we could also use #fetch_all and the getFollowers endpoint
# here to achieve more or less the same thing)
while i < follows.length
batch = follows[i...i+25]
dids = batch.map { |x| x['value']['subject'] }
print '.'
result = appview.get_request('app.bsky.actor.getProfiles', { actors: dids })
profiles += result['profiles']
i += 25
end
# now, here are the DIDs that are on the follows list,
# but aren't being returned from getProfiles:
missing = follows.map { |x| x['value']['subject'] } - profiles.map { |x| x['did'] }
puts
puts "#{missing.length} followed accounts are missing:"
puts
# for each of those, try to find out what is the reason:
missing.each do |did|
begin
did = DID.new(did)
document = did.document
handle = document.handles.first
rescue StandardError
puts "#{did} (?) => DID not found"
next
end
begin
status = did.account_status || 'deleted'
puts "#{did} (@#{handle}) => #{status}"
rescue StandardError => e
puts "#{did} (@#{handle}) => Error: #{e}"
end
end