A deliberately small User-Agent classifier: browser, OS, device class, and "does this look automated".
Why not a UA-parsing library
Full parsers ship a regex database of thousands of entries and refresh it on
a release cadence. This module recognises the families that actually show up
in aggregate reports and labels everything else "Other" — which is the
honest answer for a long tail that would otherwise be a hundred one-row
entries in the browsers table. No dependency, no database to keep current,
and the parse is a handful of regex matches on a string we already have.
What it deliberately does not do
This is classification, not fingerprinting. Nothing here is combined with
anything else to make a durable identifier: visitor_id is built from a
daily-rotating salted hash (see PhoenixKitWebAnalytics.Visitor), and the
raw User-Agent string is never stored.
iex> PhoenixKitWebAnalytics.UserAgent.parse("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36").browser
"Chrome"
iex> PhoenixKitWebAnalytics.UserAgent.parse("Googlebot/2.1 (+http://www.google.com/bot.html)").bot?
true
Summary
Types
Functions
Whether the User-Agent looks automated.
Accepts the raw header; matching is case-insensitive.
Parses a User-Agent header.
An empty or missing header yields all-"Unknown" with bot?: false — a
client that sends no UA is unusual but not necessarily automated, and
guessing "bot" there would silently drop real traffic.