You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
resolveGuildEmoji(client, emojiId): looks up a custom emoji across every cached guild. With the async cache this needs an emoji index keyed by ID, or a scan of each guild's emoji cache.
A computed, sorted position (discord.js GuildChannel#position / Role#position), with the API value exposed as rawPosition. Channel and role position currently return the raw API value, because a synchronous sorted getter would have to read every sibling from a possibly async cache. We could expose it as fetchPosition(), or resolve it in _hydrate.
Sort the cached channels and roles of a guild without refetching them. fetchSorted and RoleManager#setPosition call the API because the cache cannot list entities by guild.
messages (resolved through client.messages and the channel), and attachments (built as Attachment), like discord.js.
Decide whether MessageMentions should resolve the raw mentioned users and members from the payload (discord.js _adds them, so parsedUsers includes them even when they were not cached). It currently only reads them by ID from the cache.
I checked issue #159's checklist against the current state of main (HEAD 53aee8f, which is #158). The issue was filed slightly out of date: most of the "Already ported" caveat items and several checklist boxes are now done. Concretely, packages/plugin-gateway/src/util/Util.ts already exports cleanContent (handles /command and :emoji:, not just @/#/role mentions), cleanCodeBlockContent, parseWebhookURL, verifyString, flatten, discordSort, getSortableGroupTypes, moveElementInArray, makeError/makePlainError, basename/findName, and resolveSKUId. GuildChannelManager#setPositions and RoleManager#setPosition(s) exist. fetchRecommendedShardCount already lives in plugin-sharder/src/util/gateway.ts, as the issue itself suggested. MessageMentions already resolves users/members/roles/channels via transformResolved-backed relations.
What's still genuinely open, verified by reading the current source:
resolveGuildEmoji — no matches anywhere in src/. Not started.
MessagePayload's nameOf() (structures/messages/MessagePayload.ts:688-694) still imports basename from node:path and reimplements URL/query-string handling locally, instead of calling Util.ts's basename/findName. This is exactly the duplication the issue's private-helpers section flags.
flatten and resolveSKUId are implemented but have no callers — flatten because structures already expose raw data (per the issue's own note), resolveSKUId because no SKU structure exists yet to consume it. These need an explicit keep/remove decision, not blind implementation.
Position follow-ups, all still open:
Role#position (structures/guilds/Role.ts:106) and the channel equivalent (ChannelPermissionMixin.ts:47) return the raw API value, not a computed Discord-order position with rawPosition split out.
No fetchSorted-equivalent exists — the cache can't list a guild's channels/roles to sort without a REST refetch.
GuildChannelManager#setPositions/RoleManager#setPositions (managers/GuildChannelManager.ts:92, managers/RoleManager.ts:158) take only absolute position, no discord.js-style relative entries.
GuildMemberRoleManager (managers/GuildMemberRoleManager.ts) hand-sorts via comparePositionTo in fetch()/fetchHighest()/fetchHoist()/etc. instead of reusing discordSort.
transformResolved/MessageMentions follow-ups, both open in structures/messages/MessageMentions.ts:
No messages getter (resolved via client.messages) or attachments getter (built as Attachment).
parsedUsers/members only read from cache relations; discord.js also seeds uncached mentioned users/members from the raw payload — needs a decision, not just an implementation, since it changes what "resolved" means.
Proposed plan
Given the size, and that #157/#158 already sliced this tracking issue into digestible PRs, I'd split the remaining work the same way rather than one large PR:
Add resolveGuildEmoji(client, emojiId) to Util.ts, scanning cached guild emoji stores (or an ID index if one exists in plugin-cache) — needs a quick look at how plugin-cache exposes per-guild emoji listing today.
Replace MessagePayload's local nameOf() with Util.ts's basename/findName, verifying query-string stripping matches discord.js's behavior (check tests/serializers.test.ts for the existing contract before changing it).
PR 2 — Decide and resolve flatten/resolveSKUId status (item 3, needs a decision first)
Confirm with the maintainer whether to keep these as public API surface with no internal callers (fine if they're meant for consumers), or drop them until something needs them. Recommend asking rather than guessing.
PR 3 — Position follow-ups (item 4, the biggest chunk)
Computed sorted position (discord.js parity) with the raw value as rawPosition, resolved once in _hydrate per the pattern used elsewhere in the codebase (kRelations/kPatchRelations conventions).
A cache-side way to list a guild's channels/roles for sorting without a REST call — depends on whether plugin-cache already supports listing-by-guild-ID for these stores.
relative entries on both setPositions methods.
Refactor GuildMemberRoleManager to sort via discordSort instead of comparePositionTo.
#249 resolves the following from this issue (verified against current main; resolveGuildEmoji was already done in #236):
transformResolved now resolves messages (IDs via the new channelId option, raw data via client.messages) and attachments (built as Attachment). The Pullfrog plan put these on MessageMentions, but discord.js's MessageMentions has neither, so they live on transformResolved.
Raw mentioned users from the payload: decided and done. MessageMentions#parsedUsers now includes the users the payload mentions even when uncached (as discord.js does by caching them first), without writing the cache. Content-only mentions stay cache-only, as in discord.js.
MessagePayload's nameOf() already reuses basename; the URL handling stays local on purpose (findName would keep fragments and mis-name https://host/ and ?x=/a/b), now documented.
GuildMemberRoleManager keeps comparePositionTo: discordSort would not simplify it (extra copy plus reversing, and it tells roles from channels by a type heuristic).
flatten / resolveSKUId are kept as public util API.
Still open:
A computed, sorted position for GuildChannel / Role, with the API value as rawPosition.
Sort the cached channels and roles of a guild without refetching them (fetchSorted, RoleManager#setPosition).
Activity
pullfrog commentedon Sep 28, 2026
Findings
I checked issue #159's checklist against the current state of
main(HEAD53aee8f, which is #158). The issue was filed slightly out of date: most of the "Already ported" caveat items and several checklist boxes are now done. Concretely,packages/plugin-gateway/src/util/Util.tsalready exportscleanContent(handles/commandand:emoji:, not just@/#/role mentions),cleanCodeBlockContent,parseWebhookURL,verifyString,flatten,discordSort,getSortableGroupTypes,moveElementInArray,makeError/makePlainError,basename/findName, andresolveSKUId.GuildChannelManager#setPositionsandRoleManager#setPosition(s)exist.fetchRecommendedShardCountalready lives inplugin-sharder/src/util/gateway.ts, as the issue itself suggested.MessageMentionsalready resolvesusers/members/roles/channelsviatransformResolved-backed relations.What's still genuinely open, verified by reading the current source:
resolveGuildEmoji— no matches anywhere insrc/. Not started.MessagePayload'snameOf()(structures/messages/MessagePayload.ts:688-694) still importsbasenamefromnode:pathand reimplements URL/query-string handling locally, instead of callingUtil.ts'sbasename/findName. This is exactly the duplication the issue's private-helpers section flags.flattenandresolveSKUIdare implemented but have no callers —flattenbecause structures already expose raw data (per the issue's own note),resolveSKUIdbecause noSKUstructure exists yet to consume it. These need an explicit keep/remove decision, not blind implementation.Role#position(structures/guilds/Role.ts:106) and the channel equivalent (ChannelPermissionMixin.ts:47) return the raw API value, not a computed Discord-order position withrawPositionsplit out.fetchSorted-equivalent exists — the cache can't list a guild's channels/roles to sort without a REST refetch.GuildChannelManager#setPositions/RoleManager#setPositions(managers/GuildChannelManager.ts:92,managers/RoleManager.ts:158) take only absoluteposition, no discord.js-stylerelativeentries.GuildMemberRoleManager(managers/GuildMemberRoleManager.ts) hand-sorts viacomparePositionToinfetch()/fetchHighest()/fetchHoist()/etc. instead of reusingdiscordSort.transformResolved/MessageMentionsfollow-ups, both open instructures/messages/MessageMentions.ts:messagesgetter (resolved viaclient.messages) orattachmentsgetter (built asAttachment).parsedUsers/membersonly read from cache relations; discord.js also seeds uncached mentioned users/members from the raw payload — needs a decision, not just an implementation, since it changes what "resolved" means.Proposed plan
Given the size, and that #157/#158 already sliced this tracking issue into digestible PRs, I'd split the remaining work the same way rather than one large PR:
PR 1 —
resolveGuildEmoji+ file-name helper consolidation (items 1-2, small, low-risk)resolveGuildEmoji(client, emojiId)toUtil.ts, scanning cached guild emoji stores (or an ID index if one exists inplugin-cache) — needs a quick look at howplugin-cacheexposes per-guild emoji listing today.MessagePayload's localnameOf()withUtil.ts'sbasename/findName, verifying query-string stripping matches discord.js's behavior (checktests/serializers.test.tsfor the existing contract before changing it).PR 2 — Decide and resolve
flatten/resolveSKUIdstatus (item 3, needs a decision first)PR 3 — Position follow-ups (item 4, the biggest chunk)
position(discord.js parity) with the raw value asrawPosition, resolved once in_hydrateper the pattern used elsewhere in the codebase (kRelations/kPatchRelationsconventions).plugin-cachealready supports listing-by-guild-ID for these stores.relativeentries on bothsetPositionsmethods.GuildMemberRoleManagerto sort viadiscordSortinstead ofcomparePositionTo.PR 4 —
transformResolved/MessageMentionsfollow-ups (item 5)messagesandattachmentsgetters toMessageMentions.parsedUsers/members.I did not start implementation. Let me know if you'd like me to proceed with PR 1 (the smallest, least ambiguous slice) first.
Claude Sonnet| 𝕏RedStar071 commentedon Oct 17, 2026
#249 resolves the following from this issue (verified against current
main;resolveGuildEmojiwas already done in #236):transformResolvednow resolvesmessages(IDs via the newchannelIdoption, raw data viaclient.messages) andattachments(built asAttachment). The Pullfrog plan put these onMessageMentions, but discord.js'sMessageMentionshas neither, so they live ontransformResolved.MessageMentions#parsedUsersnow includes the users the payload mentions even when uncached (as discord.js does by caching them first), without writing the cache. Content-only mentions stay cache-only, as in discord.js.MessagePayload'snameOf()already reusesbasename; the URL handling stays local on purpose (findNamewould keep fragments and mis-namehttps://host/and?x=/a/b), now documented.GuildMemberRoleManagerkeepscomparePositionTo:discordSortwould not simplify it (extra copy plus reversing, and it tells roles from channels by atypeheuristic).flatten/resolveSKUIdare kept as public util API.Still open:
positionforGuildChannel/Role, with the API value asrawPosition.fetchSorted,RoleManager#setPosition).GuildChannelManager#setPositions/RoleManager#setPositionsacceptingrelativeentries.