drive 0.4.0 → 0.5.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: cf5ec0cbf839ae0e64fabcecc4f66f40a1e4654252053c57837c8895b290366f
4
- data.tar.gz: e5819d0ad8774657ad71eb4695971399ac22215fd7c37d96cf7cc8db3c64f543
3
+ metadata.gz: 99a4adcee91a1d02137f10f2ffff028ee3bcabd96896d550c71e81c443ad47b8
4
+ data.tar.gz: 2a5e5b329808e3e315464d70e1e8fe1357a5736632d96dde4077f3f36a57518e
5
5
  SHA512:
6
- metadata.gz: c2589ac5dc34686550476cf266d06ef8424c0e3d8307761cb4861d15bc3cd7b00884407ac273132f4efa70b4ec1cc671cec67b0bbec70e426aa53d65dbd3bfd3
7
- data.tar.gz: e4610a657c780d8458a236bc7512b8dc984e75337e4a17624cff92cc53458799375761027fa5361432c924f0fdfaa088ac5dcde2eddf012039d746ba7e4945f9
6
+ metadata.gz: 57b24c0022b7476eb3db58ab1108ee86283f5397a49704c39626f751c8158baad8d35404ceebafedaf53bbefc912d5a99e65ddbe6cd6cfea546b8417b20320d8
7
+ data.tar.gz: eeaa76fd2453f73da3332ded46d662d4165b458ea9db7359582ec6c05b980db96446eaccf47ed880893d1f4298fa37876c9fb573a7b9d399953d43daa98a29a4
data/CHANGELOG.md CHANGED
@@ -6,6 +6,15 @@ For more information about changelogs, check [Keep a Changelog](http://keepachan
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 0.5.0 - 2026-10-07
10
+
11
+ * [FEATURE] `RateLimited` no longer limits Claude's, ChatGPT's or Perplexity's search crawler
12
+
13
+ `Crawler.verified?(request)` believes a user agent naming Claude-SearchBot, OAI-SearchBot or
14
+ PerplexityBot, as well as Google's, Bing's or Apple's crawler, only where its address is in the
15
+ ranges its search engine publishes as JSON, fetched once a day. None of the three new ones
16
+ resolves to a domain of its own, so no crawler is checked by DNS any more.
17
+
9
18
  ## 0.4.0 - 2026-10-06
10
19
 
11
20
  * [BREAKING] No more log of changes, and no feed
data/README.md CHANGED
@@ -11,8 +11,8 @@ files, a monthly database — changing only the rows the source did, and countin
11
11
  - **`AccessFile`**: stages a table of a Microsoft Access file, read with `mdb-export`.
12
12
  - **`RateLimited`**: 20 requests a minute and 200 a day per address, for public pages. A search
13
13
  engine's own crawler has no limit.
14
- - **`Crawler`**: whether a request comes from Google's, Bing's or Apple's crawler: its address
15
- resolves to their domain and back, checked once a day per address.
14
+ - **`Crawler`**: whether a request comes from the crawler of Google, Bing, Apple, Claude, ChatGPT
15
+ or Perplexity: its address is in the ranges they publish, fetched once a day.
16
16
 
17
17
  It needs PostgreSQL.
18
18
 
@@ -24,10 +24,10 @@ gem install drive
24
24
 
25
25
  ```ruby
26
26
  # Gemfile
27
- gem 'drive', '~> 0.4.0'
27
+ gem 'drive', '~> 0.5.0'
28
28
  ```
29
29
 
30
- `~> 0.4.0` takes every 0.4.x and stops short of 0.5, which may break what 0.4 does.
30
+ `~> 0.5.0` takes every 0.5.x and stops short of 0.6, which may break what 0.5 does.
31
31
 
32
32
  Then copy its migrations, of runs, and run them:
33
33
 
@@ -1,33 +1,34 @@
1
- require 'resolv'
1
+ require 'ipaddr'
2
+ require 'net/http'
2
3
 
3
4
  # A search engine's own crawler, told apart from a client that only names one in its user agent:
4
- # its address resolves to the search engine's domain, and that host back to the address, which is
5
- # how Google and Bing say to check theirs.
5
+ # its address is in the ranges the search engine publishes for it.
6
6
  module Crawler
7
- # The user agents of the crawlers checked.
8
- AGENTS = /Googlebot|Google-InspectionTool|Storebot-Google|GoogleOther|bingbot|Applebot/i
9
-
10
- # The domains their addresses resolve to.
11
- DOMAINS = %w[.googlebot.com .google.com .search.msn.com .applebot.apple.com].freeze
7
+ # The ranges each search engine publishes, by the user agents of its crawlers.
8
+ RANGES = {
9
+ /Googlebot|Google-InspectionTool|Storebot-Google|GoogleOther/i =>
10
+ 'https://developers.google.com/static/crawling/ipranges/common-crawlers.json',
11
+ /bingbot/i => 'https://www.bing.com/toolbox/bingbot.json',
12
+ /Applebot/i => 'https://search.developer.apple.com/applebot.json',
13
+ /Claude-SearchBot/i => 'https://claude.com/crawling/bots.json',
14
+ /OAI-SearchBot/i => 'https://openai.com/searchbot.json',
15
+ /PerplexityBot/i => 'https://www.perplexity.ai/perplexitybot.json',
16
+ }.freeze
12
17
 
13
18
  # @param request [ActionDispatch::Request] request a page was asked with.
14
- # @return [Boolean] whether it came from a crawler, checked once a day for each address.
19
+ # @return [Boolean] whether it came from a crawler, its ranges fetched once a day.
15
20
  def self.verified?(request)
16
- ip = request.remote_ip
17
- request.user_agent.to_s.match?(AGENTS) &&
18
- Rails.cache.fetch(['crawler', ip], expires_in: 1.day) { resolves? ip }
21
+ url = RANGES.find { |agent, _| request.user_agent.to_s.match? agent }&.last
22
+ url.present? && ranges(url).any? { it.include? request.remote_ip }
19
23
  end
20
24
 
21
- # @param ip [String] address a request came from.
22
- # @return [Boolean] whether it names a crawler's host, and that host names it back.
23
- def self.resolves?(ip)
24
- dns = Resolv::DNS.new.tap { it.timeouts = 2 }
25
- host = dns.getname(ip).to_s
26
- DOMAINS.any? { host.end_with? it } && dns.getaddresses(host).map(&:to_s).include?(ip)
27
- rescue Resolv::ResolvError
28
- false
29
- ensure
30
- dns&.close
25
+ private_class_method def self.ranges(url)
26
+ Rails.cache.fetch(['crawler', url], expires_in: 1.day) do
27
+ uri = URI url
28
+ body = Net::HTTP.start(uri.host, use_ssl: true, open_timeout: 2, read_timeout: 2) do |http|
29
+ http.get(uri.path).body
30
+ end
31
+ JSON.parse(body)['prefixes'].map { IPAddr.new it.values.first }
32
+ end
31
33
  end
32
- private_class_method :resolves?
33
34
  end
data/lib/drive/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Drive
2
2
  # Version of the gem, read by the gemspec.
3
- VERSION = '0.4.0'.freeze
3
+ VERSION = '0.5.0'.freeze
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: drive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - claudiob