jekyll-webmention_io 3.3.7 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 92d94de0add9cc4571ce7b0f312cce727c3b387e645101f8c190e025c58be261
4
- data.tar.gz: 8dbd9f8960c7c5c427c861fc08bc368a901e04b51267219fc1030b81b33b25e7
3
+ metadata.gz: '09dd03eeea574cdb024da147de7b2be165209a645a600aa709a650975b10d1b0'
4
+ data.tar.gz: 8493598855950605e57eb6142fa4e4a9e3f9b5b09323b100ed6823192ef3ece1
5
5
  SHA512:
6
- metadata.gz: 676aa2fd229c06d44f077f3eb7fb7f52deec8bad2f082b101fffb6379b66f35e90a1fcfe31d06539f4baeb77f6a5f7712aa5319d09c463fe5d2cf2c02e34974d
7
- data.tar.gz: f73660974098440e3b658183dd9a9a10817d8fc010db197766e80678d43e4e295341adef6e057eee33c5acc5b06b1f3ce38f1bd1cbbad776199c64a95129147f
6
+ metadata.gz: de0de587f76d245329a31d2800b7e9d4e53bfc7bbdf318e3adeaef0ade095c2ae865e670510e18d0ca77a32398f9eb7060b9a6acbf2afb78a4d62708576ab015
7
+ data.tar.gz: d3113c40d8d9674f5b842ce2f2ce777d33cfe3abab46ee1dc97474f2eb0788ea1cd7f8c60699dc0dcf10563cec3ac8963f3fc6715f64f0bfddabdba8b7455a53
@@ -26,18 +26,17 @@ module Jekyll
26
26
  WebmentionIO.log "msg", "Getting ready to send webmentions (this may take a while)."
27
27
 
28
28
  count = 0
29
+ max_attempts = WebmentionIO.max_attempts()
29
30
  cached_outgoing = WebmentionIO.get_cache_file_path "outgoing"
30
31
  if File.exist?(cached_outgoing)
31
32
  outgoing = WebmentionIO.load_yaml(cached_outgoing)
32
33
  outgoing.each do |source, targets|
33
34
  targets.each do |target, response|
34
35
  # skip ones we’ve handled
35
- next unless response == false
36
+ next unless response == false or response.instance_of? Integer
36
37
 
37
- # convert protocol-less links
38
- if target.index("//").zero?
39
- target = "http:#{target}"
40
- end
38
+ # skip protocol-less links, we'll need to revisit this again later
39
+ next if target.index("//").zero?
41
40
 
42
41
  # produce an escaped version of the target (in case of special
43
42
  # characters, etc).
@@ -46,6 +45,17 @@ module Jekyll
46
45
  # skip bad URLs
47
46
  next unless WebmentionIO.uri_ok?(escaped)
48
47
 
48
+ # give up if we've attempted this too many times
49
+ response = (response || 0) + 1
50
+
51
+ if ! max_attempts.nil? and response > max_attempts
52
+ outgoing[source][target] = ""
53
+ WebmentionIO.log "msg", "Giving up sending from #{source} to #{target}."
54
+ next
55
+ else
56
+ outgoing[source][target] = response
57
+ end
58
+
49
59
  # get the endpoint
50
60
  endpoint = WebmentionIO.get_webmention_endpoint(escaped)
51
61
  next unless endpoint
@@ -64,9 +74,7 @@ module Jekyll
64
74
  count += 1
65
75
  end
66
76
  end
67
- if count.positive?
68
- WebmentionIO.dump_yaml(cached_outgoing, outgoing)
69
- end
77
+ WebmentionIO.dump_yaml(cached_outgoing, outgoing)
70
78
  WebmentionIO.log "msg", "#{count} webmentions sent."
71
79
  end # file exists (outgoing)
72
80
  end # def process
@@ -18,7 +18,6 @@ module Jekyll
18
18
  end
19
19
  end
20
20
 
21
- using StringInflection
22
21
  class CompileJS < Generator
23
22
  safe true
24
23
  priority :low
@@ -61,7 +60,7 @@ module Jekyll
61
60
  def add_webmention_types
62
61
  js_types = []
63
62
  WebmentionIO.types.each do |type|
64
- js_types.push "'#{type}': '#{type.to_singular}'"
63
+ js_types.push "'#{type}': '#{ActiveSupport::Inflector.singularize(type)}'"
65
64
  end
66
65
  types_js = <<-EOF
67
66
  ;(function(window,JekyllWebmentionIO){
@@ -8,6 +8,8 @@
8
8
  # This generator caches sites you mention so they can be mentioned
9
9
  #
10
10
 
11
+ require "jsonpath"
12
+
11
13
  module Jekyll
12
14
  module WebmentionIO
13
15
  class QueueWebmentions < Generator
@@ -17,6 +19,7 @@ module Jekyll
17
19
  def generate(site)
18
20
  @site = site
19
21
  @site_url = site.config["url"].to_s
22
+ @syndication = site.config.dig("webmentions", "syndication")
20
23
 
21
24
  if @site.config['serving']
22
25
  Jekyll::WebmentionIO.log "msg", "Webmentions lookups are not run when running `jekyll serve`."
@@ -31,52 +34,180 @@ module Jekyll
31
34
  return
32
35
  end
33
36
 
34
- if @site.config.dig("webmentions", "pause_lookups")
35
- WebmentionIO.log "info", "Webmention lookups are currently paused."
36
- return
37
- end
37
+ compile_jsonpath_expressions() if ! @syndication.nil?
38
38
 
39
- WebmentionIO.log "msg", "Beginning to gather webmentions you’ve made. This may take a while."
39
+ WebmentionIO.log "msg", "Collecting webmentions you’ve made. This may take a while."
40
40
 
41
41
  upgrade_outgoing_webmention_cache
42
42
 
43
- posts = WebmentionIO.gather_documents(@site)
44
-
43
+ posts = WebmentionIO.gather_documents(@site).select { |p| ! p.data["draft"] }
45
44
  gather_webmentions(posts)
46
45
  end
47
46
 
48
47
  private
49
48
 
49
+ def compile_jsonpath_expressions()
50
+ @syndication.each do | target, config |
51
+ next if ! config.key? "response_mapping"
52
+
53
+ mapping = config["response_mapping"]
54
+
55
+ mapping.clone.each do | key, pattern |
56
+ begin
57
+ mapping[key] = JsonPath.new(pattern)
58
+ rescue StandardError => e
59
+ WebmentionIO.log "error", "Ignoring invalid JsonPath expression #{pattern}: #{e}"
60
+
61
+ mapping.delete(key)
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def combine_values(a, b)
68
+ return case [ a.instance_of?(Array), b.instance_of?(Array) ]
69
+ when [ false, false ]
70
+ [ a, b ]
71
+ when [ false, true ]
72
+ [ a ] + b
73
+ when [ true, false ]
74
+ a << b
75
+ when [ true, true ]
76
+ a + b
77
+ end
78
+ end
79
+
80
+ def process_syndication(post, target, response)
81
+ # If this is a syndication target, and we have a response,
82
+ # and the syndication entry contains a response mapping, then
83
+ # go through that map and store the selected values into
84
+ # the page front matter.
85
+
86
+ response = JSON.generate(response)
87
+
88
+ target["response_mapping"].each do |key, pattern|
89
+ result = pattern.on(response)
90
+
91
+ if ! result
92
+ WebmentionIO.log "msg", "The path #{skey} doesn't exist in the response from #{target['endpoint']} for #{uri}"
93
+ next
94
+ elsif result.length == 1
95
+ result = result.first
96
+ end
97
+
98
+ if post.data[key].nil?
99
+ post.data[key] = result
100
+ else
101
+ post.data[key] = combine_values(post.data[key], result)
102
+ end
103
+ end
104
+ end
105
+
106
+ def get_collection_for_post(post)
107
+ @site.collections.each do |name, collection|
108
+ next if name == "posts"
109
+
110
+ return collection if collection.docs.include? post
111
+ end
112
+
113
+ return nil
114
+ end
115
+
116
+ def get_syndication_target(uri)
117
+ return nil if @syndication.nil?
118
+
119
+ @syndication.values.detect { |t| t["endpoint"] == uri }
120
+ end
121
+
50
122
  def gather_webmentions(posts)
51
123
  webmentions = WebmentionIO.read_cached_webmentions "outgoing"
52
124
 
53
125
  posts.each do |post|
54
- uri = File.join(@site_url, post.url)
126
+ # Collect potential outgoing webmentions in this post.
55
127
  mentions = get_mentioned_uris(post)
56
- if webmentions.key? uri
57
- mentions.each do |mentioned_uri, response|
58
- unless webmentions[uri].key? mentioned_uri
59
- webmentions[uri][mentioned_uri] = response
128
+
129
+ mentions.each do |mentioned_uri, response|
130
+ # If this webmention was a product of a syndication instruction,
131
+ # this goes back into the configuration and pulls that syndication
132
+ # target config out.
133
+ #
134
+ # If this is just a normal webmention, this will return nil.
135
+ target = get_syndication_target(mentioned_uri)
136
+
137
+ fulluri = File.join(@site_url, post.url)
138
+ shorturi = post.data["shorturl"] || fulluri
139
+
140
+ # Old cached responses might use either the full or short URIs so
141
+ # we need to check for both.
142
+ cached_response =
143
+ webmentions.dig(shorturi, mentioned_uri) ||
144
+ webmentions.dig(fulluri, mentioned_uri)
145
+
146
+ if cached_response.nil?
147
+ if ! target.nil?
148
+ uri = target["shorturl"] ? shorturi : fulluri
149
+
150
+ if target.key? "fragment"
151
+ uri += "#" + target["fragment"]
152
+ end
153
+ else
154
+ uri = fulluri
60
155
  end
156
+
157
+ webmentions[uri] ||= {}
158
+ webmentions[uri][mentioned_uri] = response
159
+ elsif ! target.nil? and target.key? "response_mapping"
160
+ process_syndication(post, target, cached_response)
61
161
  end
62
- else
63
- webmentions[uri] = mentions
64
162
  end
65
163
  end
66
164
 
67
- WebmentionIO.cache_webmentions "outgoing", webmentions
165
+ # This check is moved down here because we still need the steps
166
+ # above to populate frontmatter during the site build, even
167
+ # if we're not going to modify the webmention cache.
168
+
169
+ if @site.config.dig("webmentions", "pause_lookups")
170
+ WebmentionIO.log "info", "Webmention lookups are currently paused."
171
+ return
172
+ else
173
+ WebmentionIO.cache_webmentions "outgoing", webmentions
174
+ end
68
175
  end
69
176
 
70
177
  def get_mentioned_uris(post)
178
+ collection = get_collection_for_post(post)
179
+
71
180
  uris = {}
181
+
182
+ syndication_targets = []
183
+ syndication_targets += post.data["syndicate_to"] || []
184
+
185
+ if ! collection.nil?
186
+ syndication_targets += collection.metadata["syndicate_to"] || []
187
+ end
188
+
189
+ syndication_targets.each do |endpoint|
190
+ if @syndication.key? endpoint
191
+ uris[@syndication[endpoint]["endpoint"]] = false
192
+ else
193
+ WebmentionIO.log "msg", "Found reference to syndication endpoint \"#{endpoint}\" without matching entry in configuration."
194
+ end
195
+ end
196
+
72
197
  if post.data["in_reply_to"]
73
198
  uris[post.data["in_reply_to"]] = false
74
199
  end
200
+
201
+ if post.data["bookmark_of"]
202
+ uris[post.data["bookmark_of"]] = false
203
+ end
204
+
75
205
  post.content.scan(/(?:https?:)?\/\/[^\s)#\[\]{}<>%|\^"']+/) do |match|
76
206
  unless uris.key? match
77
207
  uris[match] = false
78
208
  end
79
209
  end
210
+
80
211
  return uris
81
212
  end
82
213
 
@@ -11,7 +11,6 @@ require "htmlbeautifier"
11
11
 
12
12
  module Jekyll
13
13
  module WebmentionIO
14
- using StringInflection
15
14
  class WebmentionTag < Liquid::Tag
16
15
  def initialize(tag_name, text, tokens)
17
16
  super
@@ -50,7 +49,7 @@ module Jekyll
50
49
  if !WebmentionIO.types.include? type
51
50
  WebmentionIO.log "warn", "#{type} are not extractable"
52
51
  else
53
- type = type.to_singular
52
+ type = ActiveSupport::Inflector.singularize(type)
54
53
  WebmentionIO.log "info", "Searching #{webmentions.length} webmentions for type==#{type}"
55
54
  if webmentions.is_a? Hash
56
55
  webmentions = webmentions.values
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module WebmentionIO
5
- VERSION = "3.3.7"
5
+ VERSION = "4.0.0"
6
6
  end
7
7
  end
@@ -50,20 +50,6 @@ module Jekyll
50
50
  @content = determine_content
51
51
  end
52
52
 
53
- def markdownify(string)
54
- @converter ||= @site.find_converter_instance(Jekyll::Converters::Markdown)
55
-
56
- if string
57
- string = @converter.convert(string.to_s)
58
- unless string.start_with?("<p")
59
- string = string.sub(/^<[^>]+>/, "<p>").sub(/<\/[^>]+>$/, "</p>")
60
- end
61
- string.strip
62
- else
63
- string
64
- end
65
- end
66
-
67
53
  def determine_uri
68
54
  @raw["data"]["url"] || @raw["source"]
69
55
  end
@@ -164,13 +150,11 @@ module Jekyll
164
150
  end
165
151
 
166
152
  def determine_content
167
- content = if %w(post reply link).include? @type
168
- @raw.dig("data", "content")
169
- else
170
- @raw.dig("activity", "sentence_html")
171
- end
172
-
173
- markdownify(content)
153
+ if %w(post reply link).include? @type
154
+ @raw.dig("data", "content")
155
+ else
156
+ @raw.dig("activity", "sentence_html")
157
+ end
174
158
  end
175
159
  end
176
160
  end
@@ -15,16 +15,30 @@ require "json"
15
15
  require "net/http"
16
16
  require "uri"
17
17
  require "openssl"
18
- require "string_inflection"
18
+ require "active_support"
19
19
  require "indieweb/endpoints"
20
20
  require "webmention"
21
21
 
22
22
  module Jekyll
23
23
  module WebmentionIO
24
+ module UriState
25
+ UNSUPPORTED = "unsupported"
26
+ ERROR = "error"
27
+ FAILURE = "failure"
28
+ SUCCESS = "success"
29
+ end
30
+
31
+ module UriPolicy
32
+ BAN = "ban"
33
+ IGNORE = "ignore"
34
+ RETRY = "retry"
35
+ end
36
+
24
37
  class << self
25
38
  # define simple getters and setters
26
39
  attr_reader :config, :jekyll_config, :cache_files, :cache_folder,
27
- :file_prefix, :types, :supported_templates, :js_handler
40
+ :file_prefix, :types, :supported_templates, :js_handler,
41
+ :uri_whitelist, :uri_blacklist
28
42
  attr_writer :api_suffix
29
43
  end
30
44
 
@@ -69,6 +83,18 @@ module Jekyll
69
83
  end
70
84
 
71
85
  @js_handler = WebmentionIO::JSHandler.new(site)
86
+
87
+ @uri_whitelist = @config
88
+ .fetch("bad_uri_policy", {})
89
+ .fetch("whitelist", [])
90
+ .clone
91
+ .insert(-1, "^https?://webmention.io/")
92
+ .map { |expr| Regexp.new(expr) }
93
+
94
+ @uri_blacklist = @config
95
+ .fetch("bad_uri_policy", {})
96
+ .fetch("blacklist", [])
97
+ .map { |expr| Regexp.new(expr) }
72
98
  end
73
99
 
74
100
  # Setter
@@ -81,6 +107,10 @@ module Jekyll
81
107
  Jekyll.sanitized_path(@cache_folder, "#{@file_prefix}#{filename}")
82
108
  end
83
109
 
110
+ def self.max_attempts()
111
+ @config.dig("max_attempts")
112
+ end
113
+
84
114
  def self.get_cache_file_path(key)
85
115
  @cache_files[key] || false
86
116
  end
@@ -213,11 +243,11 @@ module Jekyll
213
243
  endpoint = IndieWeb::Endpoints.get(uri)[:webmention]
214
244
  unless endpoint
215
245
  log("info", "Could not find a webmention endpoint at #{uri}")
216
- uri_is_not_ok(uri)
246
+ update_uri_cache(uri, UriState::UNSUPPORTED)
217
247
  end
218
248
  rescue StandardError => e
219
249
  log "info", "Endpoint lookup failed for #{uri}: #{e.message}"
220
- uri_is_not_ok(uri)
250
+ update_uri_cache(uri, UriState::FAILURE)
221
251
  endpoint = false
222
252
  end
223
253
  endpoint
@@ -231,10 +261,24 @@ module Jekyll
231
261
  case response.code
232
262
  when 200, 201, 202
233
263
  log "info", "Webmention successful!"
264
+ update_uri_cache(target, UriState::SUCCESS)
234
265
  response.body
235
266
  else
236
267
  log "info", response.inspect
237
268
  log "info", "Webmention failed, but will remain queued for next time"
269
+
270
+ if response.body
271
+ begin
272
+ body = JSON.parse(response.body)
273
+
274
+ if body.key? "error"
275
+ log "msg", "Endpoint returned error: #{body['error']}"
276
+ end
277
+ rescue
278
+ end
279
+ end
280
+
281
+ update_uri_cache(target, UriState::ERROR)
238
282
  false
239
283
  end
240
284
  end
@@ -291,12 +335,12 @@ module Jekyll
291
335
  redirect_to = redirect_to.relative? ? "#{original_uri.scheme}://#{original_uri.host}" + redirect_to.to_s : redirect_to.to_s
292
336
  return get_uri_source(redirect_to, redirect_limit - 1, original_uri)
293
337
  else
294
- uri_is_not_ok(uri)
338
+ update_uri_cache(uri, UriState::FAILURE)
295
339
  return false
296
340
  end
297
341
  else
298
342
  log("warn", "too many redirects for #{original_uri}") if original_uri
299
- uri_is_not_ok(uri)
343
+ update_uri_cache(uri, UriState::FAILURE)
300
344
  return false
301
345
  end
302
346
  end
@@ -347,37 +391,176 @@ module Jekyll
347
391
  return response
348
392
  rescue *EXCEPTIONS => e
349
393
  log "warn", "Got an error checking #{uri}: #{e}"
350
- uri_is_not_ok(uri)
394
+ update_uri_cache(uri, UriState::FAILURE)
351
395
  return false
352
396
  end
353
397
  end
354
398
 
355
- # Cache bad URLs for a bit
356
- def self.uri_is_not_ok(uri)
399
+ # Given the provided state value (see UriState), retrieve the policy
400
+ # entry. If no entry exists, return a new default entry that
401
+ # indicates unlimited retries.
402
+ def self.get_bad_uri_policy_entry(state)
403
+ settings = @config.fetch("bad_uri_policy", {})
404
+
405
+ default_policy = { "policy" => UriPolicy::RETRY }
406
+ policy_entry = nil
407
+
408
+ # Retrieve the policy entry, the default entry, or the canned default
409
+ policy_entry = settings.fetch(state) {
410
+ settings.fetch("default", default_policy)
411
+ }
412
+
413
+ # Convert shorthand entry to full policy record
414
+ if policy_entry.instance_of? String
415
+ policy_entry = { "policy" => policy_entry }
416
+ end
417
+
418
+ if policy_entry["policy"] == UriPolicy::RETRY and ! policy_entry.key? "retry_delay"
419
+ # If this is a retry policy and no delay is set, set up the default
420
+ # delay policy. This inherits from the legacy cache_bad_uris_for
421
+ # setting to enable backward compatibility with older configurations.
422
+ #
423
+ # We do this here to make the rule enforcement logic a little tidier.
424
+
425
+ policy_entry["retry_delay"] = [ @config.fetch("cache_bad_uris_for", 1) * 24 ]
426
+ end
427
+
428
+ return policy_entry
429
+ end
430
+
431
+ # Retrieve the bad_uris cache entry for the given URI. This method
432
+ # takes the cache and a URI instance (i.e. parsing must already be done).
433
+ #
434
+ # If the URI has no entry in the cache, returns nil and *not* a default
435
+ # entry.
436
+ def self.get_bad_uri_cache_entry(bad_uris, uri)
437
+ return nil if ! bad_uris.key? uri.host
438
+
439
+ entry = bad_uris[uri.host].clone
440
+
441
+ if entry.instance_of? String
442
+ # Older version of the bad URL cache, convert to new format with some
443
+ # "sensible" defaults.
444
+
445
+ entry = {
446
+ "state" => UriState::UNSUPPORTED,
447
+ "last_checked" => DateTime.parse(entry).to_time,
448
+ "attempts" => 1
449
+ }
450
+ else
451
+ # Otherwise, parse the check time into a real Time object before
452
+ # returning the entry.
453
+ #
454
+ # We convert to a Time object so we can do arithmetic on it later.
455
+
456
+ entry["last_checked"] = DateTime.parse(entry["last_checked"]).to_time
457
+ end
458
+
459
+ return entry
460
+ end
461
+
462
+ # Update the URI cache for this entry.
463
+ #
464
+ # If the state is UriState.SUCCESS or the URI is whitelisted or
465
+ # blacklisted, we delete any existing entries since no policy will
466
+ # apply. This ensures we reset the policy state when a webmention
467
+ # succeeds.
468
+ #
469
+ # Otherwise, we either create or update an entry for the URI, recording
470
+ # the state and the current attempt counter.
471
+ def self.update_uri_cache(uri, state)
357
472
  uri = URI::Parser.new.parse(uri.to_s)
358
- # Never cache webmention.io in here
359
- return if uri.host == "webmention.io"
473
+ uri_str = uri.to_s
360
474
 
361
475
  cache_file = @cache_files["bad_uris"]
362
476
  bad_uris = load_yaml(cache_file)
363
- bad_uris[uri.host] = Time.now.to_s
477
+
478
+ if state == UriState::SUCCESS or
479
+ @uri_whitelist.any? { |expr| expr.match uri_str } or
480
+ @uri_blacklist.any? { |expr| expr.match uri_str }
481
+
482
+ return if bad_uris.delete(uri.host).nil?
483
+ else
484
+ old_entry = get_bad_uri_cache_entry(bad_uris, uri) || {}
485
+
486
+ bad_uris[uri.host] = {
487
+ "state" => state,
488
+ "attempts" => old_entry.fetch("attempts", 0) + 1,
489
+ "last_checked" => Time.now.to_s
490
+ }
491
+ end
492
+
364
493
  dump_yaml(cache_file, bad_uris)
365
494
  end
366
495
 
496
+ # Check if we should attempt to send a webmention to the given URI based
497
+ # on the error handling policy and the last attempt.
367
498
  def self.uri_ok?(uri)
368
499
  uri = URI::Parser.new.parse(uri.to_s)
369
500
  now = Time.now.to_s
501
+ uri_str = uri.to_s
502
+
503
+ # If the URI is whitelisted, it's always ok!
504
+ return true if @uri_whitelist.any? { |expr| expr.match uri_str }
505
+
506
+ # If the URI is blacklisted, it's never ok!
507
+ return false if @uri_blacklist.any? { |expr| expr.match uri_str }
508
+
370
509
  bad_uris = load_yaml(@cache_files["bad_uris"])
371
- if bad_uris.key? uri.host
372
- last_checked = DateTime.parse(bad_uris[uri.host])
373
- cache_bad_uris_for = @config["cache_bad_uris_for"] || 1 # in days
374
- recheck_at = last_checked.next_day(cache_bad_uris_for).to_s
375
- return false if recheck_at > now
510
+ entry = get_bad_uri_cache_entry(bad_uris, uri)
511
+
512
+ # If the entry isn't in our cache yet, then it's ok.
513
+ return true if entry.nil?
514
+
515
+ # Okay, the last time we tried to send a webmention to this URI it
516
+ # failed, so depending on what happened and the policy, we need to
517
+ # decide what to do.
518
+ #
519
+ # First pull the retry policy given the type of the last error for the URI
520
+ policy_entry = get_bad_uri_policy_entry(entry["state"])
521
+ policy = policy_entry["policy"]
522
+
523
+ if policy == UriPolicy::BAN
524
+ return false
525
+ elsif policy == UriPolicy::IGNORE
526
+ return true
527
+ elsif policy == UriPolicy::RETRY
528
+ now = Time.now
529
+
530
+ attempts = entry["attempts"]
531
+ max_attempts = policy_entry["max_attempts"]
532
+
533
+ if ! max_attempts.nil? and attempts >= max_attempts
534
+ # If there's a retry limit and we've hit it, URI is not ok.
535
+ log "msg", "Skipping #{uri}, attempted #{attempts} times and max is #{max_attempts}"
536
+
537
+ return false
538
+ end
539
+
540
+ retry_delay = policy_entry["retry_delay"]
541
+
542
+ # Sneaky trick. By clamping to the array length, the last entry in
543
+ # the retry_delay list is used for all remaining retries.
544
+ delay = retry_delay[(attempts - 1).clamp(0, retry_delay.length - 1)]
545
+
546
+ recheck_at = (entry["last_checked"] + delay * 3600)
547
+
548
+ if recheck_at.to_r > now.to_r
549
+ log "msg", "Skipping #{uri}, next attempt will happen after #{recheck_at}"
550
+
551
+ return false
552
+ end
553
+ else
554
+ log "error", "Invalid bad URI policy type: #{policy}"
376
555
  end
556
+
377
557
  return true
378
558
  end
379
559
 
380
- private_class_method :get_http_response, :uri_is_not_ok
560
+ private_class_method :get_http_response,
561
+ :get_bad_uri_policy_entry,
562
+ :get_bad_uri_cache_entry,
563
+ :update_uri_cache
381
564
  end
382
565
  end
383
566
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-webmention_io
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.7
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aaron Gustafson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-02-19 00:00:00.000000000 Z
11
+ date: 2023-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -65,19 +65,25 @@ dependencies:
65
65
  - !ruby/object:Gem::Version
66
66
  version: '4.0'
67
67
  - !ruby/object:Gem::Dependency
68
- name: string_inflection
68
+ name: activesupport
69
69
  requirement: !ruby/object:Gem::Requirement
70
70
  requirements:
71
71
  - - "~>"
72
72
  - !ruby/object:Gem::Version
73
- version: '0.1'
73
+ version: '7.0'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 7.0.4.3
74
77
  type: :runtime
75
78
  prerelease: false
76
79
  version_requirements: !ruby/object:Gem::Requirement
77
80
  requirements:
78
81
  - - "~>"
79
82
  - !ruby/object:Gem::Version
80
- version: '0.1'
83
+ version: '7.0'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 7.0.4.3
81
87
  - !ruby/object:Gem::Dependency
82
88
  name: htmlbeautifier
83
89
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +126,20 @@ dependencies:
120
126
  - - "~>"
121
127
  - !ruby/object:Gem::Version
122
128
  version: '7.0'
129
+ - !ruby/object:Gem::Dependency
130
+ name: jsonpath
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: 1.0.1
136
+ type: :runtime
137
+ prerelease: false
138
+ version_requirements: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - "~>"
141
+ - !ruby/object:Gem::Version
142
+ version: 1.0.1
123
143
  - !ruby/object:Gem::Dependency
124
144
  name: bundler
125
145
  requirement: !ruby/object:Gem::Requirement
@@ -154,14 +174,14 @@ dependencies:
154
174
  requirements:
155
175
  - - "~>"
156
176
  - !ruby/object:Gem::Version
157
- version: '12.0'
177
+ version: '13.0'
158
178
  type: :development
159
179
  prerelease: false
160
180
  version_requirements: !ruby/object:Gem::Requirement
161
181
  requirements:
162
182
  - - "~>"
163
183
  - !ruby/object:Gem::Version
164
- version: '12.0'
184
+ version: '13.0'
165
185
  - !ruby/object:Gem::Dependency
166
186
  name: rspec
167
187
  requirement: !ruby/object:Gem::Requirement