Class: Abqari::Syndication::Networks::Bluesky

Inherits:
Base
  • Object
show all
Defined in:
lib/abqari/syndication.rb

Overview

Bluesky — uses AT Protocol's createSession (login with handle

  • app password) then createRecord to post. App passwords are generated from the Bluesky UI (Settings → App Passwords) and are distinct from the account password.

Constant Summary collapse

SERVICE_HOST =
'bsky.social'

Instance Method Summary collapse

Methods inherited from Base

#initialize

Constructor Details

This class inherits a constructor from Abqari::Syndication::Networks::Base

Instance Method Details

#configured?Boolean

Returns:

  • (Boolean)


375
376
377
# File 'lib/abqari/syndication.rb', line 375

def configured?
  !handle.to_s.empty? && !app_password.to_s.empty?
end

#nameObject



371
372
373
# File 'lib/abqari/syndication.rb', line 371

def name
  'bluesky'
end

#post(text:, url:) ⇒ Object



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/abqari/syndication.rb', line 379

def post(text:, url:)
  session = create_session
  return nil unless session

  # Bluesky requires facets to be set explicitly for embedded
  # links; without them the URL renders as plain text but
  # remains visible. We pass it as plain text for simplicity —
  # Bluesky's client auto-linkifies bare URLs in display.
  uri = URI("https://#{SERVICE_HOST}/xrpc/com.atproto.repo.createRecord")
  body = JSON.dump(
    repo: session['did'],
    collection: 'app.bsky.feed.post',
    record: {
      '$type'     => 'app.bsky.feed.post',
      text:       text,
      createdAt:  Time.now.utc.iso8601,
      facets:     link_facets(text, url)
    }
  )
  status, payload = http_json(uri,
                              method: :post,
                              headers: {
                                'Authorization' => "Bearer #{session['accessJwt']}",
                                'Content-Type'  => 'application/json'
                              },
                              body: body)
  return nil unless status&.between?(200, 299) && payload

  # createRecord returns `{ uri: 'at://...', cid: '...' }`. Build
  # the web URL from the at-URI: at://<did>/app.bsky.feed.post/<rkey>
  # → https://bsky.app/profile/<handle>/post/<rkey>.
  rkey = payload['uri'].to_s.split('/').last
  rkey.empty? ? payload['uri'] : "https://bsky.app/profile/#{handle}/post/#{rkey}"
end