Skip to content

tangled_issue_tracker.rb

Example: show 10 most recent issues in a Tangled repo.

Tangled is a git hosting site (GitHub alternative) built on the AT Protocol.

rb
require 'didkit'
require 'minisky'

$url = ARGV[0]

if $url.nil?
  puts "Usage: #{$PROGRAM_NAME} https://tangled.org/user.handle/repo-name"
  exit 1
elsif !$url.start_with?('https://tangled.org/')
  puts "Invalid Tangled URL: #{$url.inspect}"
  exit 1
end

# try e.g.:
# https://tangled.org/microcosm.blue/microcosm-rs
# https://tangled.org/nonbinary.computer/jacquard
# https://tangled.org/tangled.org/core

# first, let's convert the URL to an at:// URI

handle, repo_name = $url.split('/')[3..4]
did = DID.resolve_handle(handle)

if did.nil?
  puts "User not found: #{handle}"
  exit 1
end

# let's search the user's PDS repo for "Tangled repo" records

pds = did.document.pds_host
all_repos = Minisky.new(pds, nil).fetch_all('com.atproto.repo.listRecords',
  { repo: did, collection: 'sh.tangled.repo', limit: 100 },
  field: 'records')

selected_repo = all_repos.detect { |x| x['value']['name'] == repo_name }

if selected_repo.nil?
  puts "Repo not found: #{handle}/#{repo_name}"
  exit 1
end

repo_uri = selected_repo['uri']

# Now, we want to list all issues reported on this Tangled repo.
# 
# The problem is, an issue record is saved on the PDS of the reporting person, so
# the issues connected to a given Tangled repo can be spread across many different
# PDSes and ATProto repos - kind of like the follow records of your Bluesky
# followers are spread across many ATProto repos.
# 
# But unlike follow records, we can't use the Bluesky AppView to look up those,
# because the Bluesky AppView doesn't track non-Bluesky data like Tangled or
# Leaflet records!
#
# Instead, we'll use a service called Constellation, part of an independent
# "Microcosm" project. It collects "backlinks" for all records on the network,
# i.e. all links to a given URI from other records, and lets you search for them.

constellation = Minisky.new('constellation.microcosm.blue', nil)

# look up all backlinks to the repo's at-URI from 'sh.tangled.repo.issue' records:

issues = constellation.get_request('blue.microcosm.links.getBacklinks', {
  subject: repo_uri,
  source: 'sh.tangled.repo.issue:repo',
  limit: 10
})['records']

# the backlinks are returned as did + collection + rkey
# now, for each issue, take those and look up the record on the relevant PDS:

issues.each do |i|
  repo, collection, rkey = i.values_at('did', 'collection', 'rkey')

  did = DID.new(repo)
  author_pds = did.document.pds_host

  record = Minisky.new(author_pds, nil).get_request('com.atproto.repo.getRecord', {
    repo: repo,
    collection: collection,
    rkey: rkey
  })

  handle = did.document.handles.first
  time = Time.parse(record['value']['createdAt'])

  puts "#{time} (@#{handle})"
  p record['value']['title']
  puts

  body = record['value']['body']
  lines = body.lines

  if lines.length > 5
    body = lines[0...5].join + "(...)"
  end

  puts body

  puts
  puts '-' * 60
  puts
end