otori 0.5.0 → 0.6.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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +16 -0
- data/lib/otori/signals.rb +20 -2
- data/lib/otori/version.rb +1 -1
- data/lib/otori.rb +6 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 554c128771ba6ee67560679dc82411c949a7950188d9d89c50419d4f2135b288
|
|
4
|
+
data.tar.gz: 562127711e5aa8d21bdde1d65cea1209e26f228d50c9b16a75530dcea7248a78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 79b5ba2de508acc8324892a7ee500a2e5d1252bb82ecda1d7c9f809211f12a35d92a2b7d6c64d638a2003a58215ed252495e5b311be5794b97be0eadfd5d02aa
|
|
7
|
+
data.tar.gz: 7ec7f1393886c5064bbec6819b1e6637a5a6366b511485c2d6ae66dfe8cceebc1ec92ca15336427cae491aa496ae4f688585223663a58ec3b372433afb911700
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `Otori::Signals#pragmatic_rating` and `Otori.signals_rating(params,
|
|
13
|
+
pragmatic: true)`. The raw `human_rating` is unchanged and stays honest:
|
|
14
|
+
desktop users without a touchscreen can trigger at most four of the five
|
|
15
|
+
signals, capping them at `0.8`. The pragmatic rating treats `0.8` and up
|
|
16
|
+
as fully human without influencing lower ratings: values at or below `0.4`
|
|
17
|
+
are returned untouched, and the `0.4` to `0.8` range is stretched linearly
|
|
18
|
+
to `0.4` to `1.0` (so `0.6` becomes `0.7`). Opt in per call; the default
|
|
19
|
+
`Otori.signals_rating(params)` keeps returning the raw rating.
|
|
20
|
+
|
|
10
21
|
## [0.5.0] - 2026-10-13
|
|
11
22
|
|
|
12
23
|
### Added
|
data/README.md
CHANGED
|
@@ -356,6 +356,22 @@ signals.focus?
|
|
|
356
356
|
> password manager submissions, which often only trigger focus plus mouse or
|
|
357
357
|
> touch.
|
|
358
358
|
|
|
359
|
+
### Pragmatic rating
|
|
360
|
+
|
|
361
|
+
Desktop users without a touchscreen can trigger at most four of the five
|
|
362
|
+
signals, capping their raw rating at `0.8`. The raw rating always stays honest,
|
|
363
|
+
but if you want such submissions to count as fully human, ask for the pragmatic
|
|
364
|
+
rating:
|
|
365
|
+
|
|
366
|
+
```ruby
|
|
367
|
+
Otori.signals_rating(params, pragmatic: true)
|
|
368
|
+
signals.pragmatic_rating
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Ratings at or below `0.4` are returned unchanged, the `0.4` to `0.8` range is
|
|
372
|
+
stretched linearly to `0.4` to `1.0`, and anything `0.8` or above counts as
|
|
373
|
+
fully human.
|
|
374
|
+
|
|
359
375
|
## Security considerations
|
|
360
376
|
|
|
361
377
|
This gem provides basic bot protection, but it should not be your only line
|
data/lib/otori/signals.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Otori
|
|
|
16
16
|
raw = JSON.parse(json.to_s)
|
|
17
17
|
raise JSON::ParserError, "expected an object" unless raw.is_a?(Hash)
|
|
18
18
|
|
|
19
|
-
new(KEYS.transform_values { raw[
|
|
19
|
+
new(KEYS.transform_values { raw[it] == true })
|
|
20
20
|
rescue JSON::ParserError
|
|
21
21
|
new
|
|
22
22
|
end
|
|
@@ -25,8 +25,17 @@ module Otori
|
|
|
25
25
|
from_json(json).human_rating
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def self.pragmatic_rating(json)
|
|
29
|
+
from_json(json).pragmatic_rating
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Touchless devices can trigger at most four signals, capping them at 0.8,
|
|
33
|
+
# so this stretches 0.4-0.8 to 0.4-1.0 to let them rate as fully human.
|
|
34
|
+
PRAGMATIC_ANCHOR = 0.4
|
|
35
|
+
PRAGMATIC_CLEAN = 0.8
|
|
36
|
+
|
|
28
37
|
def initialize(flags = {})
|
|
29
|
-
@flags = KEYS.keys.to_h { [
|
|
38
|
+
@flags = KEYS.keys.to_h { [it, flags.fetch(it, false) == true] }
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
KEYS.each_key do |key|
|
|
@@ -37,6 +46,15 @@ module Otori
|
|
|
37
46
|
@flags.values.count(true) / KEYS.size.to_f
|
|
38
47
|
end
|
|
39
48
|
|
|
49
|
+
def pragmatic_rating
|
|
50
|
+
raw = human_rating
|
|
51
|
+
return raw if raw <= PRAGMATIC_ANCHOR
|
|
52
|
+
return 1.0 if raw >= PRAGMATIC_CLEAN
|
|
53
|
+
|
|
54
|
+
span = (1.0 - PRAGMATIC_ANCHOR) / (PRAGMATIC_CLEAN - PRAGMATIC_ANCHOR)
|
|
55
|
+
(PRAGMATIC_ANCHOR + (raw - PRAGMATIC_ANCHOR) * span).round(2)
|
|
56
|
+
end
|
|
57
|
+
|
|
40
58
|
def to_h = @flags.dup
|
|
41
59
|
end
|
|
42
60
|
end
|
data/lib/otori/version.rb
CHANGED
data/lib/otori.rb
CHANGED
|
@@ -50,11 +50,15 @@ module Otori
|
|
|
50
50
|
caught
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
-
def signals_rating(params)
|
|
53
|
+
def signals_rating(params, pragmatic: false)
|
|
54
54
|
raw = param_value(params, config.signals_input_name)
|
|
55
55
|
return 0.0 if raw.nil? || raw.to_s.empty?
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
if pragmatic
|
|
58
|
+
Signals.pragmatic_rating(raw.to_s)
|
|
59
|
+
else
|
|
60
|
+
Signals.human_rating(raw.to_s)
|
|
61
|
+
end
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
private
|