Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
216 commits
Select commit Hold shift + click to select a range
0a6deb7
build(deps): bump github.com/go-telegram/bot
dependabot[bot] May 29, 2026
370d2d6
docs: huma API migration design (tracking, humans, profiles)
jfberry May 30, 2026
c0bbddd
docs: huma API migration implementation plan
jfberry May 30, 2026
1ab35f6
build: add huma v2 dependency
jfberry May 30, 2026
f6c8697
feat(api): legacy {status,message} error model for huma
jfberry May 30, 2026
4dd5f9d
feat(api): huma API constructor + public openapi.json/docs
jfberry May 30, 2026
b141b26
feat(api): flex SchemaProvider + lenient body wrapper (leniency spike)
jfberry May 30, 2026
023f6df
fix(api): copy schema in lenient wrapper to avoid registry contamination
jfberry May 30, 2026
0b53923
fix(api): stop huma $schema leak + fold validation detail into legacy…
jfberry May 30, 2026
e60f6be
feat(api): migrate GET /tracking/pokemon/{id} to huma + wire huma int…
jfberry May 30, 2026
b0912b9
feat(api): migrate POST /tracking/pokemon/{id} with clean/edit/summar…
jfberry May 30, 2026
365bf6a
refactor(api): polish tracking huma template — optional params, docum…
jfberry May 30, 2026
74bd0d6
refactor(api): model profile_no as optional int and silent/suppressMe…
jfberry May 31, 2026
3823f15
docs: correct rsvp_changes (enum, not boolean) in spec+plan; ground a…
jfberry May 31, 2026
5c89657
docs: per-field canonical-type audit for tracking huma migration
jfberry May 31, 2026
5ca9adc
docs: sign-off decisions on tracking field audit (enum/bool model, 3 …
jfberry May 31, 2026
f2ebdb7
feat(api): migrate monster delete + bulk-delete to huma
jfberry May 31, 2026
e21aef9
feat(api): lenient string-enum infrastructure + apply to pokemon gend…
jfberry May 31, 2026
bf8da0c
docs: v2 API design (clean v2, freeze v1) with implementor-review RFC…
jfberry Jun 1, 2026
be6da35
docs: settle v2 internal decisions (PUT full-replace, strict, v1 depr…
jfberry Jun 2, 2026
1418ebb
docs: ready-to-post v2 RFC GitHub issue draft
jfberry Jun 2, 2026
9a7a6da
docs(v2): reframe v1->v2 migration messaging; drop defunct min/max_we…
jfberry Jun 3, 2026
0d76a7a
docs(v2): add prospective pvp_ranking_evolution (mega) field from pvp…
jfberry Jun 3, 2026
5630e6e
docs(v2): single silent query param on mutating endpoints (consolidat…
jfberry Jun 3, 2026
b89a976
docs: implementation plan for in-place huma migration of easy-win /ap…
jfberry Jun 3, 2026
97bc510
docs: huma full-API master plan (in-place + v2, problem+json, one ins…
jfberry Jun 3, 2026
a00c3fc
docs(v2): define active_hours schema, blocked_alerts enum, locations …
jfberry Jun 3, 2026
d1a438e
docs: complete API surface inventory (huma in-place + v2, gin permane…
jfberry Jun 3, 2026
0d8f80b
docs(v2): add humans/profiles + schedules section to RFC issue body
jfberry Jun 3, 2026
c569fb8
docs(v2): human-scoped tracking paths + (human,uid) ownership guard +…
jfberry Jun 3, 2026
a264d69
docs(v2): always return rowtext on tracking mutations (per-rule descr…
jfberry Jun 3, 2026
69b45b8
docs(v2): mutations return assembled message only (no per-rule descri…
jfberry Jun 3, 2026
4355c33
docs(v2): unify ?include_descriptions across reads+mutations; drop th…
jfberry Jun 3, 2026
21b073c
docs: lift #138 gate (build P0-P5 now); defer mega field to post-merg…
jfberry Jun 3, 2026
89377dc
refactor(api): problem+json error model for the huma surface
jfberry Jun 3, 2026
ad5480e
refactor(api): revert in-place pokemon huma migration (v1 frozen)
jfberry Jun 3, 2026
9b365f5
test(api): confirm single huma instance serves in-place and v2 paths
jfberry Jun 3, 2026
a5068fe
feat(api): huma in-place for reload endpoints
jfberry Jun 3, 2026
4983a83
feat(api): huma in-place for weather, stats, and geocode reads
jfberry Jun 3, 2026
191cb3b
feat(api): huma in-place for geofence reads, config schema, masterdat…
jfberry Jun 3, 2026
c945d05
feat(api): huma in-place for geofence tile-URL endpoints
jfberry Jun 3, 2026
7e466ea
feat(api): huma in-place for DTS editor read endpoints
jfberry Jun 3, 2026
a69061e
feat(api): huma in-place for summaries reads and command endpoint
jfberry Jun 3, 2026
2217215
feat(api): huma in-place for autocreate run, schema, and delete endpo…
jfberry Jun 3, 2026
8a1a3b7
feat(api): huma in-place for DTS render/enrich/sendtest/save (open bo…
jfberry Jun 3, 2026
0711f3f
feat(api): huma in-place for config values/validate/poracleWeb (open …
jfberry Jun 3, 2026
6c689cd
feat(api): huma in-place for test, deliverMessages/postMessage, resol…
jfberry Jun 3, 2026
676dbb3
feat(api): huma in-place for summary schedule upsert (lenient active_…
jfberry Jun 3, 2026
b7f4cb4
feat(api): huma in-place for autocreate templates get/save/validate (…
jfberry Jun 3, 2026
bfdccd9
feat(api): v2 strict tracking building blocks + pokemon endpoints
jfberry Jun 3, 2026
9ea2b2e
feat(api): v2 strict tracking for nest
jfberry Jun 3, 2026
a0ea290
feat(api): v2 strict tracking for lure
jfberry Jun 3, 2026
eb61f03
feat(api): v2 strict tracking for maxbattle
jfberry Jun 3, 2026
71e1bce
feat(api): v2 strict tracking for gym
jfberry Jun 3, 2026
28652cd
feat(api): v2 strict tracking for fort
jfberry Jun 3, 2026
3fc3a2b
feat(api): v2 strict tracking for raid
jfberry Jun 3, 2026
021e0e6
feat(api): v2 strict tracking for egg
jfberry Jun 3, 2026
1cd04a2
feat(api): v2 strict tracking for quest
jfberry Jun 3, 2026
eae87d9
feat(api): add read-filter to v2 tracking generic layer
jfberry Jun 3, 2026
25acd56
feat(api): v2 strict tracking for invasion
jfberry Jun 3, 2026
355d8dc
feat(api): v2 strict tracking for incident
jfberry Jun 3, 2026
5ae6e33
feat(api): v2 full tracking snapshot endpoint
jfberry Jun 3, 2026
1d83bbf
feat(api): v2 humans core/status/location/areas endpoints
jfberry Jun 3, 2026
8d34c52
feat(api): v2 locations CRUD (with new PUT) and roles endpoints
jfberry Jun 3, 2026
5324fdb
feat(api): v2 profiles endpoints with strict active_hours schema
jfberry Jun 3, 2026
aeeabed
test(api): golden OpenAPI spec test over the full huma surface
jfberry Jun 3, 2026
4f60091
refactor(api): remove orphaned gin handlers left by the in-place huma…
jfberry Jun 3, 2026
88bc88c
docs: document huma /api + /api/v2 surface; CHANGELOG; reconcile v2-d…
jfberry Jun 3, 2026
0d612bf
feat(api): document v2 wildcard/sentinel semantics + verify omit-to-w…
jfberry Jun 3, 2026
da731c7
feat(api): hide v2 wildcard values as null in tracking responses
jfberry Jun 3, 2026
f2188fb
fix(api): derive v2 raid/maxbattle level sentinel from pokemon_id
jfberry Jun 3, 2026
7799ce4
feat(api): typed response schemas for in-place huma endpoints
jfberry Jun 3, 2026
a522676
feat(api): richer huma response schemas + reload descriptions + v2 pr…
jfberry Jun 3, 2026
4b650b5
refactor(api): unify {status:ok} success responses into one shared sc…
jfberry Jun 3, 2026
6054d02
feat(api): expose pvp_ranking_evolution on v2 pokemon tracking
jfberry Jun 4, 2026
6ddb23b
feat(api): request schema for deliverMessages + document postMessage …
jfberry Jun 4, 2026
e258f1d
feat(api): define schemas for stats, resolve/test requests, poracleWe…
jfberry Jun 4, 2026
8126fe7
feat(api): named outer schemas for config values/validate + DTS rende…
jfberry Jun 6, 2026
3036503
feat(api): fill remaining OpenAPI description gaps
jfberry Jun 11, 2026
4ae48d7
docs: v1->v2 migration guide + reconcile RFC/plan status notes
jfberry Jun 11, 2026
16e4d6a
Merge branch 'huma-api-migration' — OpenAPI-documented /api + strict …
jfberry Jun 11, 2026
7639e44
Merge pull request #135 from jfberry/dependabot/go_modules/processor/…
jfberry Jun 11, 2026
032cef9
deps: general minor/patch update of Go dependencies
jfberry Jun 11, 2026
a1341bc
version: bump to 5.2.0 for the new development cycle
jfberry Jun 11, 2026
2f1fd48
docs: mute API (v2) design spec
jfberry Jun 11, 2026
d5aeefa
docs(spec): mute API — conform DELETE shape to v2 tracking ({deleted:…
jfberry Jun 11, 2026
64fc5e4
feat(api): v2 mutes endpoints + snapshot inclusion
jfberry Jun 11, 2026
f3e9d81
Merge pull request #146 from jfberry/mute-api
jfberry Jun 11, 2026
9596e9a
build(deps): bump actions/checkout from 5 to 6
dependabot[bot] Jun 12, 2026
c109e95
pvp: include mega forms in cross-species evolution-direct tracking
jfberry Jun 15, 2026
388b5c3
Merge pull request #149 from jfberry/fix-pvp-mega-evolution-direct
jfberry Jun 16, 2026
b29886d
Merge pull request #147 from jfberry/dependabot/github_actions/action…
jfberry Jun 18, 2026
a25e593
quest: support `all pokemon` / `all items` wildcards and fix reward=0…
jfberry Jun 21, 2026
7802f47
discordbot: actually clean up threads under a dead master channel
jfberry Jun 23, 2026
838b7b1
Merge pull request #153 from jfberry/fix-thread-keepalive-dead-master…
jfberry Jun 23, 2026
c24eb67
geocoding: add cached GeoNames intersection lookups ({{intersection}})
jfberry Jun 23, 2026
457dc74
enrichment: funnel geocoding + intersection through addLocationFields
jfberry Jun 23, 2026
ab1e29c
geocoding: address code-review findings on intersection feature
jfberry Jun 23, 2026
1f8c69c
intersection: use secure.geonames.org for HTTPS
jfberry Jun 24, 2026
eef7fc6
Merge pull request #154 from jfberry/feature/geonames-intersection
jfberry Jun 24, 2026
3ce9ef8
breaker: add shared circuit-breaker wrapper; convert geocoder/staticm…
jfberry Jun 23, 2026
bc87c80
breaker: guard AccuWeather and Shlink with circuit breakers
jfberry Jun 23, 2026
8ac4559
breaker: address code-review findings
jfberry Jun 24, 2026
d665adb
Merge pull request #152 from jfberry/fix-quest-all-pokemon-wildcard
jfberry Jun 24, 2026
f18df31
quest v2: clarify reward=0 "all items / all pokemon" wildcard
jfberry Jun 24, 2026
8165af6
Merge branch 'quest-v2-wildcard-clarity' into develop
jfberry Jun 24, 2026
443cb06
Merge pull request #155 from jfberry/refactor/shared-circuit-breaker
jfberry Jul 7, 2026
0897239
fix(bot): apply form filter on !raid, reject form on remove
jfberry Jul 8, 2026
5642557
fix(geocoding): log forward-geocode failures so they can be diagnosed
jfberry Jul 14, 2026
247c49f
Merge pull request #158 from jfberry/fix/geocoding-forward-logging
jfberry Jul 14, 2026
c80eb65
Merge pull request #157 from jfberry/fix/raid-form-filter
jfberry Jul 14, 2026
91b27e1
fix(logging): normalize level names, stop editor shadow-key accumulation
jfberry Jul 14, 2026
01399c0
Merge pull request #159 from jfberry/fix/logging-level-normalization
jfberry Jul 14, 2026
4450a21
fix(lure/invasion): only alert on genuine, active lures and incidents
jfberry Jul 14, 2026
0bbb317
Merge pull request #160 from jfberry/fix/lure-showcase-stale-gate
jfberry Jul 14, 2026
fbeaebb
feat(showcase): Phase 1 — ingest & alert on pokestop-webhook showcases
jfberry Jul 14, 2026
8829cb8
feat(showcase): Phase 2 — translated "what's featured" focus enrichment
jfberry Jul 14, 2026
1d5d3b8
feat(showcase): dedicated 'showcase' template type + focus in default…
jfberry Jul 15, 2026
7744ad4
fix(showcase): resolve pokestop name in the title (empty-title bug)
jfberry Jul 15, 2026
e68d445
fix(showcase): editor + DTS.md — don't advertise non-resolving incide…
jfberry Jul 15, 2026
b931b11
Merge pull request #161 from jfberry/feature/showcase-support
jfberry Jul 15, 2026
6490cb9
docs: costume tracking design spec
jfberry Jul 15, 2026
d72096d
docs: costume spec — v2 create/update/null semantics, tracked line, R…
jfberry Jul 15, 2026
cbeb270
docs: costume spec — v1 API costume with absent→9000 default
jfberry Jul 15, 2026
27f65ae
docs: costume tracking implementation plan (12 tasks, TDD)
jfberry Jul 15, 2026
4af4101
feat(gamedata): load costumes.json + CostumeTranslationKey
jfberry Jul 15, 2026
8a95c49
fix(gamedata): log costumes.json load failures + convention doc + neg…
jfberry Jul 15, 2026
038301e
feat(db): monsters.costume column + API absent->9000 default
jfberry Jul 15, 2026
748a4d0
fix(tracking): default costume to 9000 at all build sites — no dup rows
jfberry Jul 15, 2026
cc8b5a4
style: gofmt costume build-site struct alignment
jfberry Jul 15, 2026
53bb096
feat(matching): filter pokemon rules by costume (9000=any)
jfberry Jul 15, 2026
f85134e
feat(enrichment): weave costume into fullName + costumeName
jfberry Jul 15, 2026
2ca2b18
feat(tracker): RecordCostume/RecentCostumes on shared RecentActivity
jfberry Jul 15, 2026
03e4338
feat(bot): costume: arg name resolution
jfberry Jul 15, 2026
8a2499e
feat(bot): !track/!untrack costume + costume in !tracked
jfberry Jul 15, 2026
2e0722b
feat(bot): !info costumes + per-species recently-seen costumes
jfberry Jul 15, 2026
3c60ca4
feat(slash): /track costume option with name autocomplete
jfberry Jul 15, 2026
b286e01
feat(api): v2 pokemon Costume field (9000/0/null semantics)
jfberry Jul 15, 2026
1e2cdf1
docs(dts): costumeName field + fullName-includes-costume note
jfberry Jul 15, 2026
6c46b63
test: costumed-pokemon testdata + costume tracking e2e verified
jfberry Jul 15, 2026
ac3780d
test(costume): drop duplicate hasFieldDef helper after rebase onto sh…
jfberry Jul 15, 2026
91f3309
review: clarify costume UnmarshalJSON guard, rowtext name fallback, p…
jfberry Jul 15, 2026
0a1a070
docs: recency-aware form & costume autocomplete + !info design spec
jfberry Jul 15, 2026
f23309e
docs: recency-aware form & costume autocomplete implementation plan (…
jfberry Jul 15, 2026
c8cb2da
feat(tracker): per-species RecordForm/RecentForms recency bucket
jfberry Jul 15, 2026
d74c05c
feat(processor): record spawn form into RecentActivity
jfberry Jul 15, 2026
9ca1b11
feat(slash): PrependRecentCostumes/PrependRecentForms boost helpers
jfberry Jul 15, 2026
01d8b45
feat(slash): boost recent costumes/forms to top of /track pickers
jfberry Jul 15, 2026
4b84dcc
feat(info): recently-seen forms section in !info
jfberry Jul 15, 2026
9c1d155
fix(info): render recency sections before the full form list
jfberry Jul 15, 2026
a5d4e95
fix(info): bold the recently-seen forms header to match recently-seen…
jfberry Jul 15, 2026
4e59a3f
feat(info): show recent forms as copy-pasteable form:<name> tokens
jfberry Jul 15, 2026
76b685d
fix(enrichment): raid & maxbattle boss names now include costume (and…
jfberry Jul 15, 2026
bc6397c
docs: raid costume tracking design spec
jfberry Jul 15, 2026
8122a58
docs: raid costume tracking implementation plan (8 tasks, TDD)
jfberry Jul 15, 2026
df59b9f
feat(db): raid.costume column + v1 API absent->9000 default
jfberry Jul 15, 2026
ae6fb83
feat(matching): filter raid rules by costume (9000=any)
jfberry Jul 15, 2026
9152d8b
feat(rowtext): show costume in raid !tracked line
jfberry Jul 15, 2026
30b1c24
feat(bot): !raid costume filter (add + remove)
jfberry Jul 15, 2026
f5ddbe3
feat(tracker): RecordRaidCostume/RecentRaidCostumes bucket + producer
jfberry Jul 15, 2026
0f8de8a
feat(info): recently-seen raid costumes section
jfberry Jul 15, 2026
e3e8bd2
feat(slash): /raid costume option + recent-raid-costume autocomplete …
jfberry Jul 15, 2026
094ea48
test(slash): make /raid costume dispatcher test discriminate the raid…
jfberry Jul 15, 2026
7af7522
feat(api): v2 raid Costume field (9000/0/null semantics)
jfberry Jul 15, 2026
720e7e8
fix(raid): copy Costume in the two RaidTrackingAPI->RaidTracking conv…
jfberry Jul 15, 2026
307acc1
docs: !info forms & costumes consistency design spec
jfberry Jul 16, 2026
b2ef283
docs: info spec — !info X forms shows recent forms too + add /raid fo…
jfberry Jul 16, 2026
a9ef3ad
docs: !info forms & costumes consistency implementation plan (4 tasks…
jfberry Jul 16, 2026
b28c207
feat(tracker): RecordRaidForm/RecentRaidForms bucket + producer
jfberry Jul 16, 2026
f975881
feat(info): copy-pasteable costume sections + recently-seen raid forms
jfberry Jul 16, 2026
5019707
feat(info): truncate forms roster + !info <pokemon> forms/costumes su…
jfberry Jul 16, 2026
fe254d5
test(info): costumes-subroute test proves spawn+raid combine; strengt…
jfberry Jul 16, 2026
72a3148
feat(slash): /raid form option + recent-raid-form autocomplete boost
jfberry Jul 16, 2026
fe6c5e5
test(info): forms-subroute test proves recent forms shown; drop redun…
jfberry Jul 16, 2026
3efab02
Merge pull request #163 from jfberry/feature/raid-costume
jfberry Jul 16, 2026
a7efcd0
Merge pull request #162 from jfberry/feature/costume-tracking
jfberry Jul 16, 2026
0237962
fix(tileserver): guard nearbyStops with isset() in rampardos fort-upd…
jfberry Jul 17, 2026
d94fd93
docs(config): fix command_security example — move [discord] sub-table…
jfberry Jul 18, 2026
eb44b16
docs: derived DTS test-data design spec
jfberry Jul 18, 2026
1eb9444
docs: pin editor handoff doc location to PoracleNG repo
jfberry Jul 18, 2026
a3e38b1
docs: derived DTS test-data implementation plan (10 tasks, TDD)
jfberry Jul 18, 2026
ff7099d
refactor(test): unify poracle-test enrichment onto the shared enrich*…
jfberry Jul 18, 2026
b1579d4
feat(test): canonical DTS<->source alias table + EnrichWebhook name r…
jfberry Jul 18, 2026
c539744
fix(test): alias TemplateType is authoritative in enrichForType (mons…
jfberry Jul 18, 2026
3e83162
feat(test): incident DTS test type (move pokestop-event samples, rend…
jfberry Jul 18, 2026
966c362
feat(test): weatherchange derived DTS test type (+ affected-pokemon l…
jfberry Jul 18, 2026
8b87837
fix(test): weatherchange test uses AlertType 'weatherchange' (matches…
jfberry Jul 18, 2026
b08a254
feat(test): questSummary derived DTS test type (shared group render b…
jfberry Jul 18, 2026
638de70
feat(test): monsterChanged derived DTS test type (old+new, OriginalView)
jfberry Jul 18, 2026
0bceab7
fix(test): monsterChanged preview sets changeType/changeTypeText (mat…
jfberry Jul 18, 2026
ef6b11e
feat(test): rsvpChanges derived DTS test type (raid + RSVP, OverrideC…
jfberry Jul 18, 2026
9443202
feat(api): DTS-type-aware /api/dts/testdata (server filter + tags + map)
jfberry Jul 18, 2026
db35d1c
fix(api): fort-update dtsType resolves + smoke-test dtsType filtering…
jfberry Jul 18, 2026
0a68ee2
feat(test): !poracle-test accepts DTS type names (via dtsmap)
jfberry Jul 18, 2026
5e0c2b9
docs: editor handoff — derived DTS types & DTS-name-addressed test data
jfberry Jul 18, 2026
37c9221
fix(api): POST /api/test resolves DTS type names; monsterChanged prev…
jfberry Jul 18, 2026
e72451c
Merge pull request #166 from jfberry/feature/derived-dts-test-data
jfberry Jul 22, 2026
72dfb9a
feat(quest): add pokecoins reward tracking (type 8)
lenisko Jul 21, 2026
f42c5a5
fix(quest): accept pokecoins (type 8) in the v1/v2 tracking API
jfberry Jul 22, 2026
eba9e6f
i18n(quest): drop redundant embedded pokecoin override
jfberry Jul 22, 2026
0934014
fix(dts): restore legacy PoracleJS quest field aliases
jfberry Jul 22, 2026
ceda46d
Merge pull request #167 from jfberry/feat/quest-pokecoins-rewards
jfberry Jul 22, 2026
164ec32
Merge pull request #168 from jfberry/fix/quest-legacy-dts-aliases
jfberry Jul 22, 2026
657af0d
fix(rowtext): show explicitly-tracked Normal form in !tracked
jfberry Jul 22, 2026
ab85fb6
test(poracletest): de-flake DTSName payload comparison
jfberry Jul 22, 2026
2f87c6e
Merge pull request #169 from jfberry/fix/tracked-normal-form-display
jfberry Jul 22, 2026
d7807aa
fix(test): weatherchange enrich reads show_altered_pokemon_static_map…
jfberry Jul 19, 2026
7278eb1
fix(test): carry weatherchange per-user tile through enrichWeatherChange
jfberry Jul 19, 2026
cf3cb35
fix(dts): enrichForType supports showcase — align /api/dts/enrich wit…
jfberry Jul 19, 2026
aad67e1
Merge pull request #170 from jfberry/fix/flaky-poracletest-disappear-…
jfberry Jul 23, 2026
e145bba
Merge pull request #173 from jfberry/fix/derived-dts-enrich-followups
jfberry Jul 23, 2026
11a4ab0
feat(health): add derivedDtsTypes capability flag
jfberry Jul 24, 2026
e80d838
Merge pull request #174 from jfberry/feat/health-derived-dts-capability
jfberry Jul 24, 2026
f6af08a
fix(dts): allow saving platform-agnostic (help) overrides so they sha…
jfberry Jul 27, 2026
210f40f
docs: editor handoff for agnostic-help override + readonly visibility
jfberry Jul 27, 2026
a8ed29f
Merge pull request #176 from jfberry/fix/dts-agnostic-platform-save
jfberry Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
run:
working-directory: processor
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
Expand Down
10 changes: 7 additions & 3 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All API endpoints are available through the processor (default port 3030). The processor handles all endpoints directly.

> **Live OpenAPI docs.** This surface is now also documented live via an OpenAPI 3.1 spec at `GET /openapi.json` with interactive docs at `GET /docs` (both public, no secret). The `/api/*` read/feature endpoints are served by [huma](https://github.com/danielgtaylor/huma), and errors on the huma surface are returned as RFC 9457 `application/problem+json` (`status`, `title`, `detail`, `errors[]`) rather than the legacy `{status:"error",message}` shape shown under [Response Format](#response-format).
>
> **New strict `/api/v2` API.** A clean, strict, typed `/api/v2` surface (human-scoped tracking — including the new `incident` type — plus discrete humans/profiles action endpoints) is the **recommended API for new clients**. The v1 endpoints documented below are **frozen and deprecated-but-supported** (no sunset date yet); migrate to `/api/v2` to access new tracking types and the cleaner contract. See the [v1 → v2 migration guide](docs/v1-to-v2-migration-guide.md) for the endpoint/field mapping, and [`docs/v2-api-design.md`](docs/v2-api-design.md) and `/docs` for v2 details.

## Contents

- [Authentication](#authentication)
Expand Down Expand Up @@ -329,8 +333,8 @@ Use `level: 90` for all levels.

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `reward_type` | int | required | Reward type (2=item, 3=stardust, 4=candy, 7=pokemon, 12=mega energy) |
| `reward` | int | 0 | Reward ID (pokemon ID, item ID, or stardust amount) |
| `reward_type` | int | required | Reward type (2=item, 3=stardust, 4=candy, 7=pokemon, 8=pokecoins, 12=mega energy) |
| `reward` | int | 0 | Reward ID (pokemon ID, item ID, or stardust/pokecoin amount) |
| `form` | int | 0 | Form ID (for pokemon rewards) |
| `shiny` | bool | false | Shiny only |
| `amount` | int | 0 | Minimum reward amount |
Expand Down Expand Up @@ -1041,7 +1045,7 @@ Returns test webhook scenarios from `testdata.json`. The editor can use these as
}
```

Available test scenarios: boring, hundo, great-rank1, great-rank9, ultra1, unencountered, boosted, shiny (pokemon); egg1, level1, egg5, level5, egg6, level3 (raid); invasion, lure, giovanni, kecleon, goldstop, goldlure, showcase, pokemoncontest (pokestop); teamchange (gym); level1, level3 (max_battle); quest-item, quest-stardust, quest-pokemon, quest-energy (quest); edit, new, remove, etc. (fort_update).
Available test scenarios: boring, hundo, great-rank1, great-rank9, ultra1, unencountered, boosted, shiny (pokemon); egg1, level1, egg5, level5, egg6, level3 (raid); invasion, lure, giovanni, kecleon, goldstop, goldlure, showcase, pokemoncontest (pokestop); teamchange (gym); level1, level3 (max_battle); quest-item, quest-stardust, quest-pokecoins, quest-pokemon, quest-energy (quest); edit, new, remove, etc. (fort_update).

### GET/POST /api/dts/reload

Expand Down
62 changes: 62 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Changelog

All notable changes to PoracleNG are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [Unreleased]

### Added

- **v2 mutes API.** `GET/POST /api/v2/humans/{id}/mutes` and
`DELETE /api/v2/humans/{id}/mutes[?scope=&value=]` expose the in-memory alert
mutes (the `!mute` / alert-button feature) over HTTP, and the v2 full
snapshot now carries a `mutes` array. Mutes remain volatile — they are
cleared by a processor restart.
- **OpenAPI 3.1 spec + interactive docs.** The processor now serves a single
OpenAPI 3.1 document at `GET /openapi.json` with interactive documentation at
`GET /docs` (both public, no `X-Poracle-Secret` required). The spec covers the
entire `/api/*` and `/api/v2/*` surface, generated from the code via
[huma](https://github.com/danielgtaylor/huma) mounted on gin.
- **New strict `/api/v2` API** for tracking and humans/profiles — typed bodies,
`additionalProperties: false`, enforced required fields, and no lenient type
coercion. A malformed request gets a clear `422` instead of a silent guess.
- **Tracking** is human-scoped: `GET|POST /api/v2/humans/{id}/tracking/{type}`,
`GET|PUT|DELETE /api/v2/humans/{id}/tracking/{type}/{uid}`, bulk
`DELETE …/{type}?uid=1,2,3`, and a full snapshot at
`GET /api/v2/humans/{id}/tracking` (human + all-type rules + profiles +
locations + summaries). Supports `?profile=`, `?include_descriptions=`,
`?silent=`, and `?all_profiles=`.
- **humans/profiles** are exposed as discrete, typed action endpoints under
`/api/v2/humans/{id}/…` (enable/disable, admin-disable, language, location,
areas, check-location, locations CRUD, roles, profiles CRUD with a strict
typed `active_hours` schema, and profile switch).
- **New `incident` tracking type** (game `PokestopEvent`, e.g. Showcase) —
available only via the `/api/v2` tracking surface, bringing the v2 tracking
type count to 11 (pokemon, raid, egg, quest, invasion, incident, lure, nest,
gym, fort, maxbattle).
- **New `PUT /api/v2/humans/{id}/locations/{label}`** — update a saved
location's coordinates in place, completing saved-location CRUD (v1 required
delete + re-add to move a location).

### Changed

- **Error bodies on the huma `/api` surface are now RFC 9457
`application/problem+json`** (`status`, `title`, `detail`, `errors[]`),
replacing the old ad-hoc `{ "status": "error", "message": … }` /
`{ "error": … }` bodies. **This is a behavior change for clients that parse
error response bodies.** HTTP status codes are unchanged, though a few
input-validation failures that were manual `400`s now surface as huma
validation `422`s.
- **`include_empty` on fort tracking now defaults to `true`** when omitted,
honoring the `forts` DB column default. The previous gin handler defaulted it
to `false`; API clients that omit `include_empty` now get `true`.

### Deprecated

- **v1 tracking/humans/profiles endpoints are frozen and
deprecated-but-supported.** `/api/tracking/*`, `/api/humans/*`,
`/api/profiles/*` (and `/api/tracking/pokemon/refresh`) remain on gin,
unchanged and fully functional — clients migrate to `/api/v2` on their own
schedule. Migration is encouraged to access new tracking types (e.g.
`incident`) and the cleaner strict contract. No sunset date is set yet.
18 changes: 16 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@ Role-loss handling is left to Discord: private-thread visibility inherits View C
- `GET /health` — health check
- `GET /metrics` — Prometheus metrics
- `POST /` — webhook receiver from Golbat (no auth, Golbat doesn't authenticate)
- `GET /openapi.json` — OpenAPI 3.1 spec
- `GET /docs` — interactive API docs

**huma surface (`/api/*` reads + features, and `/api/v2/*`)**: The `/api/*` read/reload/feature endpoints listed below and the entire `/api/v2/*` surface are served by [huma](https://github.com/danielgtaylor/huma) (mounted on gin via humagin, one instance), and appear in the OpenAPI 3.1 spec at `/openapi.json` / `/docs`. Error bodies on this surface are RFC 9457 `application/problem+json` (`status`, `title`, `detail`, `errors[]`) — replacing the old `{status:"error",message}` / `gin.H{"error":...}` bodies (status codes unchanged; a few manual-400s became validation-422s). `/health`, `/metrics`, pprof, and the webhook receiver `POST /` stay on plain gin by design.

## Command System

Expand Down Expand Up @@ -543,11 +547,20 @@ All API endpoints are accessed via the processor (port 3030). The processor hand
| DELETE | `/api/profiles/{id}/byProfileNo/{profile_no}` | Delete profile |
| GET | `/api/snapshots/{messageID}?target={id}` | Inspect a delivered-message snapshot (admin diagnostics; 503 if `[snapshots] enabled = false`) |
| GET | `/api/dts/actions` | List registered button actions + their scopes/params (drives the config editor's button UI) |
| GET | `/health` | Health check; returns `{status, version, capabilities}` where `capabilities` is a static feature map (`buttons`, `snapshots`, `autocreate`, `tomlDts`, `buttonResponseObject`) so clients can do explicit feature detection without probing endpoints |
| GET | `/health` | Health check; returns `{status, version, capabilities}` where `capabilities` is a static feature map (`buttons`, `snapshots`, `autocreate`, `tomlDts`, `buttonResponseObject`, `derivedDtsTypes`) so clients can do explicit feature detection without probing endpoints |
| GET | `/metrics` | Prometheus metrics |
| GET | `/openapi.json` | OpenAPI 3.1 spec (whole `/api` + `/api/v2` surface; public) |
| GET | `/docs` | Interactive API docs (public) |

Tracking types: pokemon, raid, egg, quest, invasion, lure, nest, gym, fort, maxbattle.

**`/api/v2/*` (strict v2 surface)**: Alongside the in-place `/api/*` endpoints above sits a clean, strict, documented v2 API for tracking + humans/profiles. It is served by the same huma instance (typed bodies, `additionalProperties:false`, no lenient coercion, problem+json errors).
- **Tracking** is human-scoped: `GET|POST /api/v2/humans/{id}/tracking/{type}`, `GET|PUT|DELETE /api/v2/humans/{id}/tracking/{type}/{uid}`, bulk `DELETE …/{type}?uid=1,2,3`, and a full snapshot `GET /api/v2/humans/{id}/tracking` (human + all-type rules + profiles + locations + summaries). Query params: `?profile=`, `?include_descriptions=`, `?silent=`, `?all_profiles=`. v2 adds an **11th tracking type, `incident`** (game `PokestopEvent`, e.g. Showcase), not present in v1.
- **humans/profiles** are discrete typed action endpoints under `/api/v2/humans/{id}/…` (enable/disable, admin-disable, language, location, areas, check-location, locations CRUD incl. a **new `PUT …/locations/{label}`** to update coords, roles, profiles CRUD with a strict typed `active_hours` schema, profile switch). Pure action endpoints return a minimal `{"status":"ok"}` ack; resource endpoints return the typed body directly.
- Design doc: `docs/v2-api-design.md`.

**v1 is frozen**: the v1 `/api/tracking/*`, `/api/humans/*`, `/api/profiles/*` (and `/api/tracking/pokemon/refresh`) endpoints remain on plain gin, unchanged and fully supported (still lenient via the coercion below), but deprecated — clients migrate to `/api/v2` on their own schedule. No sunset date yet.

**Flexible JSON type coercion**: All tracking CRUD POST endpoints accept flexible JSON types for numeric and boolean fields. Third-party clients like ReactMap may send `"clean": false` (boolean) instead of `"clean": 0` (number). The `flexBool` and `flexInt` custom JSON types in `processor/internal/api/tracking.go` handle this coercion transparently:
- `flexBool`: accepts `true`/`false`, `0`/`1`, `"0"`/`"1"` — coerces to int (0 or 1)
- `flexInt`: accepts numbers, booleans, quoted strings — coerces to int
Expand Down Expand Up @@ -702,8 +715,9 @@ The store is a separate pogreb instance from the geocoder cache — different wo
- `!<type> mute ...` aliases (e.g. `!raid mute id:X`) route through `RouteToMuteFromType`.
- `!tracked` shows the active mutes alongside tracking rules.
- Mute buttons on alerts (see "Button actions" below) write the same `mute.Entry` shape via `buttonactions.HandleMute`.
- **v2 mutes API** (`internal/api/v2_mutes.go`): `GET|POST /api/v2/humans/{id}/mutes`, `DELETE …/mutes?scope=&value=` (single) and `DELETE …/mutes` (all). Strict schemas, problem+json; mute identity is `(scope, value)` so deletes address via query params; DELETE returns `{deleted:[…]}` like v2 tracking. Area values validate against `AreaLogic` when present, else live `StateMgr` fences (production path). The v2 full snapshot includes a `mutes` array. The API documents the in-memory volatility — entries vanish on restart. Design: `docs/superpowers/specs/2026-06-11-mute-api-design.md`.

Tracking-UID mutes (`!mute id:N` / `!mute raid id:N`) are documented in the design but rejected by the v1 parser — they need `MatchedUser.RuleUID` plumbing in the matcher first.
Tracking-UID mutes are fully wired: the matcher compares `mute.Event.MatchedRuleUID` (populated per matched user in `cmd/processor/helpers.go`) against `tracking`-scope entries, created via `!mute id:N`, alert buttons, or the v2 mutes API.

## Button Actions

Expand Down
25 changes: 23 additions & 2 deletions DTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ These fields are available in every template:
| `neighbourhood` | string | Neighbourhood name |
| `suburb` | string | Suburb name |
| `flag` | string | Country flag emoji |
| `intersection` | string | Nearest street intersection (`Street1 & Street2`) from GeoNames; empty when disabled (`[geocoding] intersection_users`) or none nearby |
| `staticMap` | string | Static map tile image URL |
| `staticmap` | string | *Deprecated* — alias for `staticMap` |
| `imgUrl` | string | Primary icon URL |
Expand Down Expand Up @@ -420,9 +421,12 @@ Time-remaining fields (`tthd`, `tthh`, `tthm`, `tths`) are in the Common Fields
| `megaEvolutions` | array | Mega evolution entries |
| `hasMegaEvolutions` | bool | Has mega evolutions |
| `pokestopName` | string | Nearby pokestop name (if applicable) |
| `costumeName` | string | Translated costume name (empty when no costume, i.e. `costume == 0` or the `costume_N` translation key is absent). |

`distance`, `bearing`, `bearingEmoji`, `userDistanceTrack`, `userTrackDistance` are documented in Common Fields.

**Note:** `fullName` already includes the costume, parenthesised, when the pokemon is wearing one — e.g. `"Pikachu (Holiday 2016)"`. `costumeName` is provided separately for templates that want to style or place the costume text independently of `fullName`.

### seenType values

`{{seenType}}` is normalised from Golbat's raw `seen_type` (see Golbat's
Expand Down Expand Up @@ -719,13 +723,17 @@ See `examples/dts/rsvpChanges/rsvp-update.json` for an installable starting poin
| `pokestopUrl` | string | Pokestop image URL (alias for `pokestop_url`) |
| `questString` | string | Translated quest objective |
| `questStringEng` | string | English quest objective |
| `quest_task` | string | Legacy PoracleJS alias for `questString` (translated objective) |
| `rewardString` | string | All rewards as text (translated) |
| `rewardStringEng` | string | English rewards text |
| `quest_reward` | string | Legacy PoracleJS alias for `rewardString` |
| `conditionString` | string | Comma-joined completion conditions, translated, e.g. "Excellent Throw, Curve Ball" |
| `conditionStringEng` | string | English copy of `conditionString` |
| `quest_conditions` | string | Legacy PoracleJS alias for `conditionString` |
| `conditionList` | array | Per-condition objects: `{type, name, formatted}` where `name` is the bare label ("Throw Type") and `formatted` includes the payload ("Excellent Throw"). Falls back to bare name when the webhook payload doesn't carry the data needed for the formatted variant. |
| `conditionListEng` | array | English copy of `conditionList` |
| `dustAmount` | int | Stardust reward amount |
| `pokecoinAmount` | int | Pokecoin reward amount |
| `itemAmount` | int | Item reward amount |
| `energyAmount` | int | Mega energy amount (first reward) |
| `candyAmount` | int | Candy amount (first reward) |
Expand Down Expand Up @@ -757,6 +765,8 @@ These are flat top-level strings, not nested under a `rewardData` object:
| `itemNamesEng` | string | English item names |
| `dustText` | string | Translated stardust text (e.g. "500 Stardust") |
| `dustTextEng` | string | English stardust text |
| `pokecoinText` | string | Translated pokecoin text (e.g. "10 Pokécoins") |
| `pokecoinTextEng` | string | English pokecoin text |
| `energyMonstersNames` | string | Mega energy reward text (translated) |
| `energyMonstersNamesEng` | string | English energy reward text |
| `candyMonstersNames` | string | Candy reward text (translated) |
Expand All @@ -777,7 +787,7 @@ The view passed to `questSummary` is shaped differently from a regular `quest` t

| Field | Type | Description |
|-------|------|-------------|
| `rewardType` | int | Reward type ID (2=item, 3=stardust, 4=candy, 7=pokemon, 12=mega energy) |
| `rewardType` | int | Reward type ID (2=item, 3=stardust, 4=candy, 7=pokemon, 8=pokecoins, 12=mega energy) |
| `reward` | int | Reward ID (item ID for type 2, dust amount for type 3, pokemon ID for types 4/7/12) |
| `rewardForm` | int | Pokemon form ID for `rewardType == 7` (so e.g. two different Spinda forms group separately). `0` for all other reward types. |
| `rewardName` | string | Translated reward name for the group header. Formatted to match the per-row reward strings from regular `quest` enrichment, **with amounts stripped** for types 2/4/12 because amounts vary across stops within a group. Examples: `"Spinda 01"` (type 7 + form, matches per-row `fullName`), `"Lapras Candy"` (type 4), `"Charizard Mega Energy"` (type 12), `"Razz Berry"` (type 2), `"1500 Stardust"` (type 3 — amount is included because it's part of the group key). |
Expand Down Expand Up @@ -862,7 +872,11 @@ These aliases are added on top of the pokestop / location / time / weather field

### Showcase fields

These fields are only populated for **Showcase** incidents (`displayType == 9`). Always guard showcase blocks with `{{#if showcasePresent}}`.
**Showcases render via their own `type: "showcase"` template** — a specialised display model (focus + leaderboard), distinct from the plain `incident` card used by Gold-Stop / Kecleon. A bundled default `showcase` template ships in `fallbacks/dts.json`; operators can override it like any other type. (Showcases are still *tracked* as incidents — a `grunt_type="showcase"` incident rule — only the rendered template differs.)

The `showcase` type resolves the pokestop-identity / time / `displayType` fields (shared with `incident`) **plus** the showcase fields below. It does **not** carry the incident-only aliases `incidentTypeName`, `incidentEmoji`, or `color` — the bundled template hardcodes its title/emoji/colour, so those render empty if used in a showcase template.

Always guard the leaderboard with `{{#if showcasePresent}}` and the focus line with `{{#if showcaseFocusPresent}}`.

#### Top-level showcase fields

Expand All @@ -874,6 +888,13 @@ These fields are only populated for **Showcase** incidents (`displayType == 9`).
| `showcaseLastUpdateFormatted` | string | `showcaseLastUpdate` formatted using the operator's configured time layout. |
| `showcase` | array | Up to 3 enriched contestant entries (see per-entry fields below). Empty array when no data. |
| `showcaseFirst` | object | Convenience alias for `showcase[0]` (the winner). `nil` when no contestants. |
| `showcaseFocusPresent` | bool | `true` when the contest's featured focus was decoded. Guard focus blocks with `{{#if showcaseFocusPresent}}`. |
| `showcaseFocusType` | string | Raw focus class: `pokemon`, `type`, `alignment`, `class`, `family`, `buddy`, `generation`, `hatched`, `mega`, `shiny`. |
| `showcaseFocusCategory` | string | Translated focus category label, e.g. `Type`, `Buddy`. |
| `showcaseFocusName` | string | Translated featured value, e.g. `Steel` (type focus) or `3+` (buddy focus). Empty for flag focuses (`hatched`/`shiny`/`mega`) — the category conveys it. |
| `showcaseFocusEmoji` | string | Optional emoji key for the focus category (from `util.json` `showcaseFocus`). |

The focus tells you *what the contest is featuring* (e.g. "Type: Steel"), separate from the `showcase` leaderboard of *who is winning*. Example: `{{#if showcaseFocusPresent}}Featuring {{showcaseFocusCategory}}: {{showcaseFocusName}}{{/if}}`.

#### Per-entry fields (each item in `{{#each showcase}}`)

Expand Down
Loading