Class: Abqari::Syndication::Dispatcher

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

Overview

Build-time orchestrator. Walks recent posts, dispatches to each configured network, accumulates a per-post log.

Instance Method Summary collapse

Constructor Details

#initialize(site) ⇒ Dispatcher

Returns a new instance of Dispatcher.



83
84
85
86
# File 'lib/abqari/syndication.rb', line 83

def initialize(site)
  @site = site
  @cfg  = Syndication.config_for(site)
end

Instance Method Details

#enabled?Boolean

Returns:

  • (Boolean)


88
89
90
# File 'lib/abqari/syndication.rb', line 88

def enabled?
  Syndication.enabled?(@site)
end

#log_pathObject



92
93
94
# File 'lib/abqari/syndication.rb', line 92

def log_path
  File.join(@site.site_root, 'vendor', 'syndication', 'sent.yml')
end

#networksObject



100
101
102
103
104
105
106
107
# File 'lib/abqari/syndication.rb', line 100

def networks
  @networks ||= begin
    out = []
    out << Networks::Mastodon.new(@site) if network_enabled?('mastodon')
    out << Networks::Bluesky.new(@site)  if network_enabled?('bluesky')
    out
  end
end

#runObject



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/abqari/syndication.rb', line 109

def run
  return unless enabled?
  # Hard production gate. POSSE is an irreversible outbound side
  # effect (public posts to Mastodon/Bluesky). `Site#build` runs
  # in every environment — including `bin/serve`'s dev rebuilds,
  # which include drafts and future-dated posts and resolve URLs
  # against the dev `url` (e.g. http://localhost:4000). Broadcasting
  # from anywhere but production would leak unpublished content and
  # ship localhost links. No env override — this must not fire by
  # accident.
  return unless @site.respond_to?(:env) && @site.env == :production
  return if networks.empty?

  FileUtils.mkdir_p(File.dirname(log_path))
  log = load_log
  cutoff = Date.today - window_days
  syndicated = 0

  recent_posts(cutoff).each do |post|
    url = absolute_url_for(post)
    next if url.nil?

    networks.each do |net|
      next unless net.configured?
      key = "#{net.name}|#{url}"
      # Skip only when previously SENT successfully. Failures
      # are recorded for visibility but get retried on the next
      # build — a transient API outage shouldn't permanently
      # skip syndication for a post.
      prev = log['sent'][key]
      next if prev && prev['status'] == 'sent'

      text = Formatter.snippet_for(post, url, limit: LIMITS[net.name])
      result = net.post(text: text, url: url)
      if result
        log['sent'][key] = {
          'status' => 'sent',
          'at' => Time.now.utc.iso8601,
          'syndicated_url' => result
        }
        syndicated += 1
        Log.info "Syndicated to #{net.name}: #{url}#{result}"
      else
        log['sent'][key] = {
          'status' => 'failed',
          'at' => Time.now.utc.iso8601
        }
      end
    end
  end

  save_log(log) if syndicated.positive? || log_dirty?(log)
  Log.info "Syndication: posted #{syndicated} new entry/entries" if syndicated.positive?
end

#window_daysObject



96
97
98
# File 'lib/abqari/syndication.rb', line 96

def window_days
  @cfg.fetch('window_days', 14).to_i
end