merefield
(Robert)
Janvier 31, 2026, 2:30
1211
Ravi dâannoncer que @ç„ćäŒ et @nathank se sont portĂ©s volontaires pour sponsoriser le travail de dĂ©finition de la localisation de lâutilisateur via lâIP
Ce travail sera expérimental pendant un certain temps, mais je suis également ravi de vous informer que la Pull Request (PR) est déjà disponible ici :
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
Ce nâest pas une simple recherche, nous faisons plus que cela - cela vous permettra de dĂ©finir une granularitĂ© spĂ©cifique (par exemple, la ville) pour toutes les localisations dĂ©terminĂ©es automatiquement afin de protĂ©ger la vie privĂ©e des utilisateurs tout en augmentant la confiance de la communautĂ©.
(Oui, il y aura quelques problĂšmes avec les VPN et je crains que nous ne puissions pas facilement les surmonter).
Une fois que je serai satisfait que cela fonctionne suffisamment bien, je le fusionnerai.
Il est de toute façon derriÚre un indicateur de fonctionnalité (feature flag ).
Je vous tiendrai au courant, mais ce sont des moments passionnants, câest lâun des meilleurs ajouts Ă Locations que nous ayons eus depuis un moment.
Merci encore Ă @ @ç„ćäŒ et @nathank !!
Et oui, il y aura aussi une tĂąche rake pour faire cela pour tous les utilisateurs existantsâŠ
9 « J'aime »