Class: Abqari::Indieweb::Sender
- Inherits:
-
Object
- Object
- Abqari::Indieweb::Sender
- Defined in:
- lib/abqari/indieweb.rb
Overview
Sender — POSTs webmention notifications to external sites linked
from our posts. The sent-log lives at
vendor/webmentions/sent.yml so re-builds don't double-send.
Endpoint discovery follows the webmention spec:
1. HEAD the target; check `Link: <...>; rel="webmention"`
2. Fall back to GET; check `<link rel="webmention">` in <head>
Only posts published within send_window_days (default 30) are
candidates — historical posts have already received their
notifications in earlier builds, and replaying them on every
CI run wastes outbound HTTP and can look like spam.
Constant Summary collapse
- MAX_TARGET_BYTES =
1 MB cap for endpoint discovery HTML
1 * 1024 * 1024
- LINK_REL_WEBMENTION_RE =
/<(?<url>[^>]+)>\s*;[^,]*?rel\s*=\s*"?webmention"?/i.freeze
- HTML_WEBMENTION_RE =
%r{<link\b[^>]*?rel=["']?webmention["']?[^>]*?href=["']([^"']+)["']|<link\b[^>]*?href=["']([^"']+)["'][^>]*?rel=["']?webmention["']?}i.freeze
Instance Method Summary collapse
- #enabled? ⇒ Boolean
-
#initialize(site) ⇒ Sender
constructor
A new instance of Sender.
- #log_path ⇒ Object
-
#send_all ⇒ Object
Walk recently-published posts, extract outbound external links from their rendered bodies, send a notification per (source, target) pair not already in the sent log.
- #send_window_days ⇒ Object
Constructor Details
#initialize(site) ⇒ Sender
Returns a new instance of Sender.
297 298 299 300 |
# File 'lib/abqari/indieweb.rb', line 297 def initialize(site) @site = site @cfg = Indieweb.config_for(site) end |
Instance Method Details
#enabled? ⇒ Boolean
302 303 304 |
# File 'lib/abqari/indieweb.rb', line 302 def enabled? Indieweb.enabled?(@site) && @cfg['send_webmentions'] == true end |
#log_path ⇒ Object
306 307 308 |
# File 'lib/abqari/indieweb.rb', line 306 def log_path File.join(@site.site_root, 'vendor', 'webmentions', 'sent.yml') end |
#send_all ⇒ Object
Walk recently-published posts, extract outbound external links from their rendered bodies, send a notification per (source, target) pair not already in the sent log.
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
# File 'lib/abqari/indieweb.rb', line 317 def send_all return unless enabled? # Hard production gate — same rationale as the syndication # Dispatcher. Sending webmentions is an outbound POST to every # site a post links to; doing it from a dev build (which includes # drafts / future posts and resolves source URLs against the dev # `url`) would ping targets with unpublished, localhost-scoped # source URLs. Only fire in production. return unless @site.respond_to?(:env) && @site.env == :production FileUtils.mkdir_p(File.dirname(log_path)) log = load_log cutoff = Date.today - send_window_days sent_count = 0 recent_posts(cutoff).each do |post| source_url = absolute_url_for(post) next if source_url.nil? outbound_targets_for(post).each do |target| key = "#{source_url}|#{target}" # Skip only when previously SENT successfully. Prior # `'no-endpoint'` (transient DNS / 5xx) and `'failed'` # (target up but POST rejected) entries are recorded for # visibility but get retried on the next build — a single # bad build must not permanently exclude a (source, target) # pair. Same retry policy as the syndication Dispatcher. prev = log['sent'][key] next if prev && prev['status'] == 'sent' endpoint = discover_endpoint(target) unless endpoint log['sent'][key] = { 'status' => 'no-endpoint', 'at' => Time.now.utc.iso8601 } next end if post_notification(endpoint, source_url, target) log['sent'][key] = { 'status' => 'sent', 'at' => Time.now.utc.iso8601 } sent_count += 1 Log.info "Webmention sent: #{source_url} → #{target}" else log['sent'][key] = { 'status' => 'failed', 'at' => Time.now.utc.iso8601 } end end end save_log(log) if sent_count.positive? || log_dirty?(log) Log.info "Webmentions: sent #{sent_count} new notification(s)" if sent_count.positive? end |
#send_window_days ⇒ Object
310 311 312 |
# File 'lib/abqari/indieweb.rb', line 310 def send_window_days @cfg.fetch('send_window_days', 30).to_i end |