Repository navigation
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
d10cc15 to
db4a15f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d10cc156ad
ℹ️ 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".
| 'stateTraceLog', | ||
| ]) | ||
|
|
||
| export const SETTINGS_SYNC_KEY = 'settings-sync' |
There was a problem hiding this comment.
Scope sync metadata to the current account
When two sync-enabled users sign into the same browser, this global local-storage key retains the first user's revision; if the second user's server revision is lower, check() treats that server copy as stale and pushes the persisted local state, silently overwriting the second user's synced settings. Include a stable account identifier in the key (or reset the metadata when identity changes).
Useful? React with 👍 / 👎.
| typeof dbUser.data === 'string' | ||
| ? JSON.parse(dbUser.data) | ||
| : dbUser.data || {} | ||
| data.personalShiny = result.players |
There was a problem hiding this comment.
Preserve edits made during player-ID lookup
When a user edits or removes players while the external friend-code lookup is still running, result.players was derived from the old list, and this assignment overwrites the newly re-read user's current personalShiny value with that stale list. This can resurrect deleted players and then sync the wrong accounts to Poracle; compare against the current list and merge only still-pending matching friend codes.
Useful? React with 👍 / 👎.
| if (found.length < GOLBAT_SPLIT_AT || depth >= MAX_SPLIT_DEPTH) { | ||
| return found |
There was a problem hiding this comment.
Keep splitting capped selective Golbat scans
On the Golbat v2 fallback in a dense viewport with selective clauses (for example, one species marked personal-shiny-only), Golbat caps raw candidates before applying the filters, so the filtered response can contain far fewer than 1,500 rows even though it was capped. Treating that short result as complete misses later candidates and therefore real personal shinies; cap detection must not depend only on the filtered result length.
Useful? React with 👍 / 👎.
| container | ||
| alignItems="flex-start" | ||
| justifyContent="center" | ||
| spacing={1} |
There was a problem hiding this comment.
Remove the unapproved grid spacing
This new spacing={1} prop introduces a grid gap, but repository policy forbids adding spacing-related styles without explicit written user permission; no such permission accompanies this review, so remove it or obtain that permission.
AGENTS.md reference: AGENTS.md:L7-L7
Useful? React with 👍 / 👎.
| const webhook = | ||
| user?.selectedWebhook && | ||
| user.perms?.webhooks?.includes(user.selectedWebhook) && | ||
| Event?.webhookObj?.[user.selectedWebhook] |
There was a problem hiding this comment.
Re-link player accounts when the webhook changes
When a user switches from Poracle webhook A to B, this function can act only on the currently selected webhook, while webhookChange in server/src/graphql/resolvers.js:841-855 merely updates the selection. A therefore remains linked and can keep sending alerts, while the user-only lastSynced timestamp can prevent B from being linked for up to an hour; migrate the links on webhook change and track sync per webhook.
Useful? React with 👍 / 👎.
| webhooks: [], | ||
| scanner: [], | ||
| scannerCooldownBypass: [], | ||
| personalShinyLimits: personalShinyLimits(['local']), |
There was a problem hiding this comment.
Derive limits from the local user's status
For a local account whose stored status is used as a role (for example, a donor status with a larger personal-shiny limit), limits are computed only for the literal local role even though getPerms() later evaluates user.status. The configured status-specific limit is therefore ignored and the user receives the fallback maximum (possibly zero); recompute this array with the loaded status as part of the permission plumbing.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
|
|
||
| // Pokémon set to only shinies come from the personal shiny scan; here they | ||
| // only show when the 0% / 100% quick selects match them | ||
| const shinyAllowed = !!perms.personalShiny && !args.filters.onlyLegacy |
There was a problem hiding this comment.
Ignore shiny-only state when no players are active
After a user pauses/removes every active player, or their maximum is reduced to zero, the UI hides the personal-shiny controls but this flag remains true solely from permission state. The resolver's no-player path still calls getAll(), where persisted personal_shiny_only filters suppress those species, leaving the user with missing Pokémon and no visible way to disable the filter; gate this behavior on active personalShinyPlayers as well.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
| if (result.status === 'failed' || result.status === 'limited') { | ||
| return { ...rest, lookup: result.status } |
There was a problem hiding this comment.
Persist terminal lookup failures
When a previously pending ticket later returns failed or limited during a background retry, these branches return a changed player row without setting resolved; resolvePending() then exits on !result.resolved and never saves the terminal status. The profile remains stuck on “looking up” and the server keeps retrying, so terminal status changes need to signal that the result should be persisted.
Useful? React with 👍 / 👎.
| shinyFor ? ` – ${shinyFor}` : '' | ||
| }`, | ||
| 'pokemon', | ||
| shinyFor ? 'personalShiny' : 'pokemon', |
There was a problem hiding this comment.
Key notification deduplication by category
When a spawn is first rendered as an ordinary Pokémon and later becomes marked personal-shiny (for example after its friend-code lookup completes), the first notification caches only pkmn.id; this effect reruns with the new category, but sendNotification() drops it as a duplicate. Users with both notification types enabled miss the important shiny alert, so qualify the deduplication key by category while retaining the encounter ID for navigation.
Useful? React with 👍 / 👎.
db4a15f to
0cdbe16
Compare
|
@codex review |
0cdbe16 to
2bd5a6a
Compare
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
9eacbea to
e677349
Compare
|
Found a client crash with this branch in production (running f34dea0; still present on e677349). Symptom: Profile → Permissions throws Cause: Fix we deployed: hide it from the Permissions page, since it's an internal value rather than a user-facing permission: -export const ALWAYS_EXCLUDED = new Set(['donor', 'blockedGuildNames', 'admin'])
+export const ALWAYS_EXCLUDED = new Set([
+ 'donor',
+ 'blockedGuildNames',
+ 'admin',
+ 'personalShinyLimits',
+])( 🤖 Generated with Claude Code |
e677349 to
20dcf4c
Compare
|
Thanks for the report and the fix! Applied the same change in the latest push: |
|
@codex review |
|
Codex Review: Something went wrong. Try again later by commenting “@codex review”. ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
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". |
20dcf4c to
4863e1c
Compare
|
Thanks! That was ReactMap's own rates without a rates service: a species never seen shiny was assumed shiny-capable at 1/512 until enough checks ruled it out. Fixed in the latest push: without |
|
yeah I was on a little bit older pull, so pulled just again and will test again if it happens. |
|
I had hit the limit of 150 lookups for https://playerid.mew.is/ earlier today. It got reset now, but I still see "Too many lookups today, it is tried again later" in reactmap. What happens to the accounts in this state? Does the retry happen automatically? If so, when? |
|
The feature works well, especially the multi-account and multi-profile support. Thanks! I found one config quirk: authentication.perms.personalShiny.roles accepts role aliases, but authentication.perms.personalShiny.limits[].roles only matches raw role IDs and does not resolve aliases from authentication.aliases. For example: "personalShiny": {
"enabled": true,
"trialPeriodEligible": false,
"roles": ["Shiny1", "Shiny2", "Shiny3"],
"limits": [
{ "roles": ["2222222222333333333"], "max": 2 },
{ "roles": ["3333333333444444444"], "max": 3 }
],
"defaultMax": 1,
"golbat": true,
"changeLimit": { "changes": 2, "hours": 720 }
}The Shiny IV slider already works well. The issue is that enabling Only Personal Shinies currently seems to remove or override the other active filters. Perhaps Only Personal Shinies be combined with the active filters using AND? The intended behavior is: This should preserve the active filters in Main, Extra, and Select while limiting the results to personal shinies. |
|
@rpotpogo Yes, it's retried automatically. When the lookup service answers "too many", ReactMap pauses all lookups for an hour. After that, a player still waiting (the "Too many lookups today" note) is looked up again on that user's next map request, and the note goes away once the player ID is found. Saving the profile again also asks again right away. The pause only lives in memory, so restarting ReactMap clears it as well. With the next push the profile also refreshes itself every 30 s while a lookup is pending, so you don't need to reload to see the ID. @ChristianATh Thanks, glad it works well! Aliases in personalShiny.limits[].roles: good catch, fixed in the next push. They're now resolved like the other role lists. Only Personal Shinies is meant to be exactly that: your personal shinies AND every condition your filters set (IV, Shiny IV, level, CP, gender, size, PvP; PvP leagues are OR'd among themselves and XXS/XXL work like on the map). With nothing set it shows all of them. The Pokémon selected in "Select" only limit it while "link global and advanced" is on; otherwise every species is included. Could you give an example of a filter that seems ignored (the filter and a Pokémon that showed although it shouldn't)? Then I can reproduce it. |
4863e1c to
8ce72c0
Compare
|
in regards to my previous post Since I changed the secret of react map as a test everybody needed to re-auth but people then get as it seems personalShiny":false and it won't look in discord if you got the correct role. i had to change personalShiny":false to personalShiny":true in the DB of that person to fix it note: this is for as far as I can see only the case with "normal" users I as admin am NOT affected by this "yet" |
8ce72c0 to
acef5ee
Compare
|
@ChristianATh Thanks for the detailed examples! That's by design: with Only Personal Shinies the regular IV range is switched off and the Shiny IV range takes its place (otherwise a 70-100 IV filter would hide almost every shiny). All other filters still apply, with two things to know:
@Pogodenhelder Thanks, added "Personal Shiny" to the login log (next push), so you can see what the user got. Perms (personalShiny like iv, pvp, …) are only worked out at login, from the user's Discord roles at that moment, and stored in the session; that's ReactMap's normal role check, the same for every perm. So a re-login giving false means ReactMap didn't see a matching role then. Could you check:
|
Personal shiny - users save one or more named players in their profile, by player id or, with a PlayerID lookup service (api.playerIdLookup), by friend code; Pokémon that will be shiny for any of them are marked (sprite, glow, label, popup) - prediction uses the encounter id + player id roll; rates come from a rates service (api.shinyRates.url) when set (with its fallback for unlisted variants and its overrides for the map's timezone, api.shinyRates.timezone), else they are estimated from pokemon_shiny_stats; known Dittos are shiny on the third draw at 1/64 - special backgrounds: the rates service's background rates (or Golbat's background_for) mark Pokémon with a background for the user, shown with the background picture; a Background quick select shows all of them while there are backgrounds to predict - Shiny and Background search categories list the user's own shinies and backgrounds nearby, by name or all of them (with shiny sprites) - a Golbat that rolls personal shinies itself (annotate_for in /api/status) marks and filters them through the v3 scan, with the v2 scan and own checks as fallback; authentication.perms.personalShiny.golbat turns it off - Shiny IV is a filter range like the others: shinies within it that match the filter's other conditions are shown next to the regular matches (0-100 sets no condition, like the other ranges); Only Shiny shows just the shinies of a Pokémon - Only Personal Shinies shows all of the user's shinies, narrowed down by every condition the filters set (shiny IV, PvP, gender, size, stats) - per-role limits (authentication.perms.personalShiny.limits/defaultMax, a limit's roles can also name single users by their Discord / Telegram id), a configurable limit on swapping players (changeLimit), players can be paused without deleting them - a Personal Shiny switch in the Poracle pokemon dialog when Poracle reports per-player shiny tracking in /health - players are linked in Poracle when a webhook is selected; zoomed out scans are split so every Pokémon in view is checked despite Golbat's result cap - notification + sound for personal shinies, IV in map notifications Settings sync - logged in users' settings are synced between browsers through a hidden backup row; only filters that differ from the defaults are sent, gzipped - saves are conditional on the revision they are based on (per tab), resets and loaded backups are synced instead of overwritten Misc - persistent hide, per-category alert sounds - IV percentages are whole numbers everywhere (popup, search) - Poracle rules made with a command (empty template) open with their own values in the dialog instead of the defaults, and the command preview leaves out their empty template and any-size value Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
acef5ee to
6cd76e4
Compare
|
@skelgaard I checked the discord role id and its the same in discord as in the config. how to reproduce: have a working user, change in react map the secret and then you get logged out and then it happens. |
|
@Pogodenhelder I can't find anything in the branch that sets personalShiny to false: perms are only worked out at login, by ReactMap's own role check (unchanged by this PR), and nothing changes them during a session. My guess: what your users still get comes from alwaysEnabledPerms, which only needs them to be in the server, while personalShiny is the one that needs a role. If the bot can't read the members' roles, that's exactly the result, and the old sessions from before the secret change still had the perms from when it could. You're not affected because allowedUsers gets every perm without looking at roles. To check:
I ran the Discord login code with a test user: with the role, personalShiny comes out true; when the bot can't read the user's roles, personalShiny is false while everything from alwaysEnabledPerms stays true, which is exactly what you see. |
|
Is there a perm that only a role gives (not in alwaysEnabledPerms), e.g. give iv only to that role? Is it false too after a fresh login? no the only thing that is false is the personal shiny part. Any errors in ReactMap's log at login about fetching roles/members? In the Discord developer portal, is "Server Members Intent" on for the bot, and is the bot still in the server? true and all my users with that role doesn't have it anymore. so I tried the following, I changed the session token in config so that ALL sessions are invalid again, and boom everybody looses it again. |
|
to be specific on your first question Is there a perm that only a role gives (not in alwaysEnabledPerms), e.g. give iv only to that role? Is it false too after a fresh login? I added the show devices to that role, people login and see that in admin panel. edit: found out that moving the block in the config to another place and reconfigure it did work. |
|
@Pogodenhelder Glad you found it, thanks for letting me know! |
|
Thank you for adding this functionality to ReactMap! The server I use deployed this PR over the weekend and I ran into some user experience issues that you may want to consider resolving before merging:
|
…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))
- "Personal Shinies" in the drawer replaces "Only Personal Shinies":
- Off: no shinies are added; the Shiny IV ranges and Only Shiny can't
be set and are ignored (Pokémon the filters show are still marked)
- Include: the map as usual plus the user's shinies within the Shiny IV
range (the Pokémon's own, or else the global one; 0-100 = all)
- Only: only the user's shinies, as before; the IV range is disabled,
as the Shiny IV range takes over
- Options to always show the IV of personal shinies, the player names and
the sparkle, each on by default
- Half / Half Glow: with two glows, both colours split in half instead of
the "Multiple" colour (hidden while it is on)
- The legacy filter knows a "shiny" term
- Tests against Golbat WatWowMap#423's test vectors (draws, Ditto, disguises,
the seen-shiny rule, the legacy shiny term)
- The Hundo / Nundo glows are labelled "100% IV" / "0% IV"
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@roynuj Thanks a lot for testing it this thoroughly, that's really helpful! The latest commits address most of it: Shinies as a separate set (1a–c): keeping them part of the regular filters is by design. A shiny is the same Pokémon with the same filter, plus a shiny condition, and that keeps the filters and the UI simple. The cases you describe are now covered by a new Personal Shinies mode in the drawer (Off / Include / Only), which replaces "Only Personal Shinies":
IV on shinies / Minimum Circle IV: there's now an Always Show IV of Personal Shinies setting. Turn it off and shinies follow your normal IV settings. Trainer names: Player Names on Personal Shinies can be turned off. You then only get the ✨, and the popup still says who the shiny is for. Two shiny icons: Sparkle on Personal Shinies turns off the sparkle on the sprite. Shundo / shnundo glows: I'd like to keep half/half as the default, since it shows both things at once. There's now a Half / Half Glow setting. Turn it off and Pokémon with more than one glow use the "Multiple" colour, which you can set to e.g. purple. Friend code text overlapping the input: fixed. Updating: the branch now gets regular commits instead of force-pushes, so |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…port The own /api/status fetch every 5 minutes is replaced by the capability registry of WatWowMap#1258 (`supportsFilter(mem, 'annotate_for')`). A Golbat that refuses personal scans (e.g. its rates aren't loaded) is still skipped for a minute, and its status is now re-read right away in case it was downgraded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With all PvP ranks shown the built PvP list isn't narrowed down to the filter's ranks, so in only mode every personal shiny with any PvP rank passed a filter set to e.g. great league 1-50. Check the parsed ranks against the filter's own range instead, with the same mega and level cap conditions as the map. The shiny filter helpers move to filters/pokemon/personalShiny.js so they can be tested without a database. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
















Summary
Personal shiny prediction for ReactMap: users save their in-game player(s) once, and wild Pokémon that will be shiny for them are marked on the map. It works with any Golbat. When Golbat can roll personal shinies itself (UnownHash/Golbat#423), ReactMap uses that instead.
The branch also carries settings sync between browsers, Poracle dialog improvements and a few alert improvements. They are listed separately below and can be split into their own PRs if preferred.
Try it (Docker)
Next to your
docker-compose.yml(ReactMap service namedreactmap); run it again to update. It builds from its own clean clone (rm-shiny): building in a folder with your configs in it copies them into the image, and an emptylocal.jsonthere breaks the build.Back to the official image:
rm docker-compose.override.yml && docker compose up -d reactmap.Already cloned the branch yourself? New changes come as regular commits, so
git pullworks. Cloned it before 20 October (when it was still force-pushed)? Then once:Personal shiny needs a login (Discord, Telegram or local), as the players are saved per user in their profile. Enable the perm in
local.json, see Config, and log out and in again to get it.Personal shiny
How it works
java.util.Randomseeded fromsha1(encounterId as uint64 LE + playerId). The firstnextFloat()is compared against the species/form shiny rate. ReactMap reproduces this bit for bit.Profile
Filters
shinyterm, e.g.shiny & 90-100.Rates
pokemon_shiny_statsand snapped to the known in-game rates (1/512, 1/256, 1/128, 1/64, 1/25, 1/20, 1/10). Without a rates service, only species/forms that have been seen shiny there are predicted, so unreleased shinies are never shown.api.shinyRates.urlpoints to a rates service using the same JSON schema as Golbat#423's[shiny_rates]. Its rates are used first and the own estimates fill the gaps. If the service is unreachable, ReactMap falls back to its own estimates.fallbackis used for variants it doesn't list, its per-timezoneoverrides(local events such as Community Day) for the map's timezone (api.shinyRates.timezone, the server's by default), and a known Ditto is shiny when the third draw is below 1/64. The implementation is checked against Custom Attribution Prefix #423's test vectors.Golbat support
Recommended: a Golbat with #423. Golbat then checks the Pokémon for the user's players itself, in one scan. Without it, ReactMap fetches the candidates and checks every one of them for every player itself, which gets heavy with many players or in dense areas.
filters.annotate_forin/api/status(checked every 5 minutes), the main scan uses/api/pokemon/v3/scanwithannotate_for. Personal shiny filtering usesshiny_forclauses. Golbat then rolls the shinies with its own rates, including timezone overrides.authentication.perms.personalShiny.golbat: falsealways uses ReactMap's own check.Config
Backgrounds
background_for), Pokémon with a special background for the user are marked: the background picture is shown behind them, like research rewards, and the popup says who it is for. The background draw follows the shiny draw, as in Golbat#423.Search
Poracle
capabilities.personalShinyin/health(PoracleNG). It sets the shiny bit of the rule'spersonalfield, and personal shiny rules get a ✨ in the rule list. Other Poracle versions get neither the switch nor the field.uidas well, and the command preview leaves out their empty template and any-size value.Settings sync
Misc
Testing
server/test/personalShiny.test.jschecks the draws against Golbat#423's test vectors, the shiny/background draw order, known Dittos, nearby Pokémon and disguises, the seen-shiny rule and the legacyshinyterm.Running in production (Golbat, MariaDB, PoracleNG) with both kinds of Golbat:
annotate_for. Itsshiny_formatches ReactMap's own check for the same players, and the personal shiny scan of a whole region drops from 29 calls (~2 s, 38 MB) to one (~7 ms, 14 KB).Predictions were verified in-game: every Pokémon predicted shiny for a player was shiny when encountered. Friend code lookup, the change limit, Poracle linking (including the hourly re-sync), the Poracle Personal Shiny switch and editing command-created rules were tested with real accounts.
🤖 Generated with [Claude Code]