merefield
(Robert)
31. Januar 2026 um 14:30
1211
Ich freue mich, bekannt zu geben, dass sowohl @ç„ćäŒ als auch @nathank sich bereit erklĂ€rt haben, die Arbeit zur Festlegung des Benutzerstandorts per IP zu sponsern
Diese Arbeit wird eine Weile experimentell sein, aber ich freue mich auch, Ihnen mitteilen zu können, dass der PR bereits hier verfĂŒgbar ist:
main â automated_ip_based_user_location_determination
opened 02:16PM - 31 Jan 26 UTC
## IPâbased User Location (MaxMind â GeoNames)
### What this adds
- Auto⊠matically determines a userâs location from their IP after post creation.
- Uses **MaxMind (DiscourseIpInfo)** for identity + GeoNames for canonical lat/lon.
- Stores exactly **one granularity level** per user (`country` / `province` / `city`) in `UserCustomField`
`geo_location`.
- Ensures lat/lon match the chosen granularity (privacyâcorrect coordinates).
- Caches GeoNames lookups by `geoname_id` (no extra gems; uses `FinalDestination::HTTP`).
---
## Prerequisites (official accounts)
You must have valid accounts and credentials from:
- **MaxMind GeoLite2**
Sign up and obtain an Account ID + License Key:
https://www.maxmind.com/en/geolite2/signup
- **GeoNames**
Create a username for the GeoNames API and **enable Web Services** in your GeoNames account settings:
https://www.geonames.org/login
---
## Required Configuration
**Env vars (MaxMind)**
- `DISCOURSE_MAXMIND_ACCOUNT_ID`
- `DISCOURSE_MAXMIND_LICENSE_KEY`
**Site settings**
- `location_geonames_username` (GeoNames username)
**Dev note (MaxMind DBs)**
- In development, ensure the MaxMind DBs are downloaded:
DISCOURSE_MAXMIND_ACCOUNT_ID=... DISCOURSE_MAXMIND_LICENSE_KEY=... bundle exec rake maxminddb:get
Restart web/sidekiq afterward so `DiscourseIpInfo` reloads the DBs.
---
## Settings
| Setting | Default | Description |
|---|---:|---|
| `location_ip_granularity` | `city` | Controls stored level: `country`, `province` (admin1), or `city`. |
| `location_geonames_username` | `""` | GeoNames username for `getJSON` requests. Acts as a feature flag
(blank => skip). |
| `location_ip_lookup_cooldown_days` | `1` | Perâuser cooldown between IP lookups (0 disables cooldown). |
| `locations_skip_ip_based_location_update_if_existing` | `true` | Skip IP lookup when existing
`geo_location` already has **lat + lon**. |
| `location_ip_auto_lookup_enabled` | `false` | Autoâenqueue IP lookups on post creation. Rake task still
works when off. |
---
## Flow
- `post_created` â enqueue `Jobs::Locations::IpLocationLookup` with user ID + IP.
- Job:
- Guards: plugin enabled, users map enabled, GeoNames username present, MaxMind env vars present, cooldown
OK, and optional âexisting locationâ check.
- Uses `DiscourseIpInfo.get(ip)` to get `geoname_ids`.
- Resolves GeoNames IDs, picks feature by granularity, builds `geo_location`.
- Saves `geo_location` to user custom field + updates `locations_user` table.
---
## Job + Hook
**Hook**
- `post_created` now enqueues `Jobs::Locations::IpLocationLookup`
- In development, IP is overridden to a fixed test IP (`2.139.231.7`) to ensure resolvable data.
**Job**
- `Jobs::Locations::IpLocationLookup` (new, in `app/jobs/regular/locations/ip_location_lookup.rb`)
- Logs key steps, skips when prerequisites are missing.
- Writes `geo_location` only when successfully built.
---
## GeoNames Helpers
New modules in `lib/locations/`:
- `GeoNamesClient`
- Calls `https://secure.geonames.org/getJSON` via `FinalDestination::HTTP`.
- Normalizes `geoname_id`, `fcl`, `fcode`, `lat`, `lon`, etc.
- Caches only successful lookups (avoids caching nil).
- `GeoNamesGranularityPicker`
- Selects best `PCLI` / `ADM1` / `PPL*` by desired granularity.
- `GeoLocationBuilder`
- Builds `geo_location` hash with correct `lat/lon`, `state`/`city` based on granularity.
---
## Rake Task
New rake task in `lib/tasks/locations.rake`:
**Task**
rake locations:enqueue_user_ip_location_lookups[username_pattern,pattern_type,delay]
**Behavior**
- Iterates users and enqueues the IP lookup job using `user.ip_address`.
- Progress is based on **matched population** (not total users).
- Skips enqueue when IP is blank.
**Options**
- `username_pattern` (optional): filter users by username.
- `pattern_type`: `string` (default substring match) or `regex`.
- `delay`: seconds between enqueues (float, default `0`).
**Help**
rake locations:enqueue_user_ip_location_lookups:help
Dies ist keine einfache Nachschlageaktion, wir machen mehr als das â dies wird es Ihnen ermöglichen, eine bestimmte GranularitĂ€t (z. B. Stadt) fĂŒr alle automatisch ermittelten Standorte festzulegen, um die PrivatsphĂ€re der Benutzer zu schĂŒtzen und gleichzeitig das Vertrauen der Community zu stĂ€rken.
(Ja, es wird einige Probleme mit VPNs geben, und ich fĂŒrchte, das können wir nicht einfach ĂŒberwinden).
Sobald ich zufrieden bin, dass dies ausreichend gut funktioniert, werde ich es zusammenfĂŒhren.
Es ist ohnehin hinter einem Feature-Flag verborgen.
Ich werde Sie auf dem Laufenden halten, aber aufregende Zeiten, dies ist eine der besten ErgÀnzungen zu Locations, die wir seit einiger Zeit hatten.
Nochmals vielen Dank an @ @ç„ćäŒ und @nathank !!
Und ja, es wird auch eine Rake-Aufgabe geben, um dies fĂŒr alle bestehenden Benutzer zu tun âŠ
9 âGefĂ€llt mirâ