Skip to content

block_tracker.rb

Example: monitor the firehose for people blocking your account or adding you to lists.

rb
require 'didkit'
require 'minisky'
require 'skyfall'

$handle = ARGV[0]

if $handle.nil?
  puts "Usage: #{$PROGRAM_NAME} <monitored_did | handle>"
  exit 1
end

$monitored_did = DID.resolve_handle(ARGV[0]).to_s

if $monitored_did.empty?
  puts "Couldn't resolve handle: #{ARGV[0]}"
  exit 1
end

sky = Skyfall::Firehose.new('bsky.network', :subscribe_repos)

sky.on_connect { log "Connected, monitoring #{$monitored_did}" }
sky.on_disconnect { log "Disconnected" }
sky.on_reconnect { log "Reconnecting..." }
sky.on_error { |e| log "ERROR: #{e}" }

sky.on_message do |msg|
  # we're only interested in repo commit messages
  next if msg.type != :commit

  msg.operations.each do |op|
    next if op.action != :create

    begin
      case op.type
      when :bsky_block
        process_block(msg, op)
      when :bsky_listitem
        process_list_item(msg, op)
      end
    rescue StandardError => e
      log "Error: #{e}"
    end
  end
end

def log(msg)
  puts "[#{Time.now}] #{msg}"
end

def process_block(msg, op)
  if op.raw_record['subject'] == $monitored_did
    owner_handle = get_user_handle(op.repo)
    log "@#{owner_handle} has blocked you!"
  end
end

def process_list_item(msg, op)
  if op.raw_record['subject'] == $monitored_did
    owner_handle = get_user_handle(op.repo)

    list_uri = op.raw_record['list']
    list_name = get_list_name(list_uri)

    log "@#{owner_handle} has added you to list \"#{list_name}\""
  end
end

def get_user_handle(did)
  DID.new(did).get_verified_handle
end

def get_list_name(list_uri)
  # the listitem record only has an URI of the list,
  # so we need to look up the list record separately
  repo, collection, rkey = list_uri.gsub('at://', '').split('/')

  # get DID document to find out what their PDS is
  pds = DID.new(repo).document.pds_host

  # load the list record from their PDS
  sky = Minisky.new(pds, nil)
  record = sky.get_request('com.atproto.repo.getRecord', {
    repo: repo,
    collection: collection,
    rkey: rkey
  })

  record['value']['name']
end

# close the connection cleanly on Ctrl+C
trap("SIGINT") { sky.disconnect }

sky.connect