Skip to content

feat(pokemon): personal shiny annotation and filtering via Golbat v3 … - #1267

Open
lokixx wants to merge 10 commits into
WatWowMap:developfrom
lokixx:feat/personal-shiny-filter
Open

lokixx wants to merge 10 commits into
WatWowMap:developfrom
lokixx:feat/personal-shiny-filter

Conversation

@lokixx

@lokixx lokixx commented Oct 12, 2026

Copy link
Copy Markdown

Summary

Adds personal shiny support using Golbat's v3 pokemon scan (annotate_for / shiny_for).

  • Users enter one or more player IDs in Player IDs for Personal Shinies (Pokémon options → Markers)
  • Pokémon shiny for those IDs get a gold glow, the shiny sprite, a ✨ marker label and a "✨ Shiny for " popup line
  • Only Show Personal Shinies (Pokémon options → Filters) adds shiny_for to every Golbat filter clause, so filtering happens before Golbat's result limit and works at any zoom
  • New personalShiny permission (authentication.perms.personalShiny) gates the feature per role; map.misc.enablePersonalShiny toggles it map-wide
  • ReactMap checks Golbat's /api/status (cached 5 min per endpoint) and falls back to the v2 scan when annotate_for isn't advertised

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-12T18:45:37.000951Z 85935d3 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85935d38a3

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

const { gender, ...rest } = filter
if (!gender) return rest
const genders = []
for (let g = gender.min; g <= gender.max; g += 1) genders.push(g)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bound the v3 gender expansion

When personal-shiny IDs force the v3 path while the user has legacy filtering enabled, gender.min/max can come directly from an unconstrained G... expression in dnfifyIvFilter; for example, G0-1000000000 makes this synchronous loop allocate a billion entries and can block or exhaust the Node process before the request reaches Golbat. Clamp or reject bounds outside Golbat's supported gender enum rather than expanding an arbitrary range.

Useful? React with 👍 / 👎.

Comment thread server/src/models/Pokemon.js Outdated
// Golbat applies shiny_for before its result limit, so zoomed out views
// aren't truncated by non shiny Pokémon
const scanFilters = useV3
? filters.map((filter) =>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add a shiny-only clause when no DNF filters exist

When Only Show Personal Shinies is enabled but the current IV/PvP/species settings produce no Golbat DNF clauses (for example, all Pokémon with unconstrained global IV settings and hundo disabled), filters is empty, so this map adds no shiny_for clause. Golbat then applies the result limit to all Pokémon, and the later JavaScript filter can miss every shiny in a dense or zoomed-out viewport; add a shiny-only clause when the DNF list is empty so filtering still occurs before the limit.

Useful? React with 👍 / 👎.

color="secondary"
style={{ width: 50, minHeight: 38 }}
style={{
width: staticSetting.type === 'text' ? 160 : 50,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the unapproved input-width change

This conditional expands the user-option control from 50px to 160px, but the review request contains no explicit permission to adjust spacing; the root AGENTS.md forbids introducing spacing-related style changes without that permission. Retain the existing width or obtain explicit permission before including this layout change.

AGENTS.md reference: AGENTS.md:L7-L7

Useful? React with 👍 / 👎.

// v3 wraps the list with scan stats
results = /** @type {any} */ (results)?.pokemon
}
if (onlyPersonalShiny) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply personal-shiny filtering to manual-ID fallbacks

When a deep link or manual ID is absent from the v3 scan—or was removed here because it is not shiny—the later /api/pokemon/id/... fallback appends an unannotated object after this filter. PkmnBackend.valid does not test shiny_for, so that Pokémon can still be returned while Only Show Personal Shinies is active, and a true shiny loaded only by ID cannot be highlighted; annotate or filter the fallback result before appending it.

Useful? React with 👍 / 👎.

Comment thread server/src/models/Pokemon.js Outdated
Comment on lines +272 to +273
const onlyPersonalShiny =
annotateFor.length > 0 && !!args.filters.onlyPersonalShiny

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor the shiny-only toggle with empty player IDs

On a Golbat-backed map, if the user enables Only Show Personal Shinies before entering IDs, clears the field, or supplies only invalid IDs, parsePlayerIds returns an empty array and this expression silently turns the requested filter off. The server then performs a regular v2 scan and returns all otherwise-selected Pokémon while the UI still says the shiny-only filter is enabled; preserve the toggle intent and return no matches or disable/validate the toggle until valid IDs exist.

Useful? React with 👍 / 👎.

@jfberry

jfberry commented Oct 12, 2026

Copy link
Copy Markdown
Contributor

IMO Whichever route for shiny PR is selected (assuming this sticks around) should merge after #1258 and get the capability using this

lokixx and others added 3 commits October 12, 2026 21:22
Shundos are personal shinies with 100% IV. The onlyShundo toggle narrows
Golbat v3 scan clauses to 100% IV alongside shiny_for, and shundo markers
glow purple to stand apart from other personal shinies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Personal shiny ids, only personal shinies and only shundos options were
sent to the client disabled when the user lacked the personalShiny perm.
Omit them from the client menus entirely instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Shiny IV Range slider under IV Range filters personal shinies
separately. Shiny filters replace every global and per-Pokemon IV range
with the shiny range and are sent as extra shiny_for clauses in the same
Golbat v3 scan, so the regular IV Range no longer affects shinies.
Leagues, zero/hundo, size and stat filters still apply to shinies.

The slider is hidden without the personalShiny perm and defaults to
0-100. Regenerated env config also picks up the existing personal shiny
client options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jfberry and others added 2 commits October 19, 2026 21:25
…Map#1258)

* feat(golbat): discover instance capabilities from /api/status

Add a GolbatCapabilities service that reads GET /api/status from every
configured Golbat endpoint and keeps { features, limits, filters } per
instance, keyed by the endpoint base url. Discovery runs from
DbManager.getDbContext (startup and config reload), refreshes every five
minutes, and rechecks an instance immediately, debounced to once per
30 s, when a scanner call answers 400/422 — the first sign of a
downgrade. A 404 or non-JSON body means an older Golbat with no
capabilities; any other failure keeps the last good result.

Pokestop.getAvailable now decides showcase focus support from
supportsFilter(mem, 'showcase_focus') when the status route advertises
filters, and only falls back to the deprecated per-response
showcase_focus_filter flag for a build that does not. The hard error on
an unsupported build is unchanged; only the source of truth moves. The
header builder shared by the evaluator and the service is extracted
into scannerHeaders.js.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016611GTrQ2W8WcznKrLQnMv

* fix(pokestop): re-read Golbat status without the recheck debounce

The unsupported showcase-focus verdict in Pokestop.getAvailable called
the debounced recheck(), which shares its 30 s floor with the periodic
status fetch. A Golbat upgrade landing inside that window left the
availability pass throwing until the next one. Use refresh() there: it
is still single-flight, and the path is already throttled by the
availability refresh window. The debounce stays on the hot-path recheck
triggered by 4xx scanner responses.

The replaced test now drives the real registry with a fake transport so
it exercises the debounce instead of a mocked method.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016611GTrQ2W8WcznKrLQnMv

* fix(golbat): harden capability discovery per review

- Keep every distinct credential set configured for a shared endpoint url
  and fall through to the next on 401/403, remembering the set that works.
- Read the status body outside the JSON parse guard so a stalled or reset
  body keeps the last good capabilities instead of recording a legacy result.
- Force at most one status refresh per availability payload when showcase
  focus is unsupported, so failing session inits no longer re-read the
  status on every page load.
- Resolve rechecks against the registered endpoint url instead of splitting
  at the first /api/, so endpoints configured with a path still recheck.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
# [1.54.0](WatWowMap/ReactMap@v1.53.2...v1.54.0) (2026-10-20)

### Features

* **golbat:** discover instance capabilities from /api/status ([WatWowMap#1258](WatWowMap#1258)) ([c83f93f](WatWowMap@c83f93f))
lokixx and others added 4 commits October 22, 2026 23:27
Personal shinies at 70-99% IV get a ground ripple with twinkling stars,
and shundos get a crowned effect: breathing aura, spinning spectrum
halo, iridescent sheen across the sprite and twinkling stars. Shinies
under 70% keep the static gold glow.

Effects are CSS layers in the fancy Pokemon marker, animating only
transform, opacity and background-position. Stars stay clear of the
weather boost icon, markers are offset so they don't pulse in unison,
and motion stops under prefers-reduced-motion. A new shinySparkles
user setting (default on, personalShiny perm) turns them off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pokemon ids sent to clients are now the encounter id encrypted as a
single AES-128 block, with zeroed padding authenticating the token on
the way back in. The key is generated into server/.cache and created
atomically so PM2 cluster workers share it; purging it only invalidates
tokens issued before the next restart.

pokemonSingle still accepts raw encounter ids so Poracle links keep
working, and returns the token for the client to use. Controlled by
api.hideEncounterIds, on by default.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants