fix(bot): update embeds after flag edits - #94
Conversation
|
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
embedly-docs | e075458 | Commit Preview URL Branch Preview URL |
Sep 05 2026, 03:59 AM |
Greptile SummaryThis PR teaches the bot to update its existing embed replies in-place when a user edits only the flag modifiers (
Confidence Score: 5/5
|
| Filename | Overview |
|---|---|
| apps/bot/src/lib/utils.ts | Adds trailing ` |
| apps/bot/src/lib/handleUrls.ts | Promotes parseMessageURLs from messageCreate.ts into the shared module, threads requestIndex through the match pipeline, introduces HandleUrlsOptions with updateTargets for in-place edits, and gates cache saves on the create-only path. Logic is correct; reactToFailure double-fire is safely guarded. |
| apps/bot/src/lib/messageCache.ts | Adds botMessageIndexes as an optional field with full runtime validation, exposes getBotMessages and clearBotMessageIndexes, and correctly cleans up indexes in removeBotMessage. Legacy cache entries (no botMessageIndexes) are handled gracefully via ??. |
| apps/bot/src/listeners/messageUpdate.ts | Replaces the old native-embed-suppression-only handler with full URL flag change detection. Partial-old-message fallback (content === null) correctly clears indexes to avoid stale mapping. Update targets are built only when URLs are identical in value and order. |
| apps/bot/src/listeners/messageCreate.ts | Imports the now-shared parseMessageURLs instead of defining it locally; no behavioral change to the create path. |
| apps/bot/src/commands/embed.ts | Adapts two handleUrls call sites from a bare source string to the new { source } options object; mechanical change with no logic impact. |
Sequence Diagram
sequenceDiagram
participant Discord
participant MessageUpdateListener
participant MessageCache
participant handleUrls
participant DiscordAPI
Discord->>MessageUpdateListener: messageUpdate(oldMsg, newMsg)
MessageUpdateListener->>MessageUpdateListener: fetch full message if partial
MessageUpdateListener->>MessageCache: getBotMessages(message.id)
MessageCache-->>MessageUpdateListener: "[{id, requestIndex}, ...]"
alt botMessages is empty
MessageUpdateListener-->>Discord: return (no-op)
else "oldMessage.content === null (partial)"
MessageUpdateListener->>MessageCache: clearBotMessageIndexes(message.id)
else content changed
MessageUpdateListener->>MessageUpdateListener: parseMessageURLs(old) + parseMessageURLs(new)
alt URLs differ (added/removed/reordered)
MessageUpdateListener->>MessageCache: clearBotMessageIndexes(message.id)
else same URLs, check flags
loop each botMessage with requestIndex
MessageUpdateListener->>MessageUpdateListener: hasSameOptions(oldReq, newReq)?
alt flags changed
MessageUpdateListener->>MessageUpdateListener: updateTargets.set(requestIndex, botMsgId)
end
end
alt "updateTargets.size > 0"
MessageUpdateListener->>handleUrls: "handleUrls(newUrls, message, {updateTargets})"
handleUrls->>handleUrls: filter to target requestIndexes only
handleUrls->>DiscordAPI: matchURL(url) per target
handleUrls->>DiscordAPI: fetch botMessage + message.edit(embed)
alt any target failed
handleUrls->>Discord: react ❌ on source message
end
end
end
end
alt oldMsg had no embeds AND new message has embeds AND not SuppressEmbeds
MessageUpdateListener->>DiscordAPI: "message.edit({flags: SuppressEmbeds})"
end
Reviews (2): Last reviewed commit: "fix(bot): invalidate stale embed edit in..." | Re-trigger Greptile
| function hasSameOptions(left: EmbedURLRequest, right: EmbedURLRequest) { | ||
| return ( | ||
| left.force === right.force && | ||
| left.flags?.MediaOnly === right.flags?.MediaOnly && | ||
| left.flags?.SourceOnly === right.flags?.SourceOnly && | ||
| left.flags?.Spoiler === right.flags?.Spoiler | ||
| ); | ||
| } |
There was a problem hiding this comment.
hasSameOptions uses === on optional force?: boolean, so undefined and false are treated as different values. Since parseMessageURLs always writes force = false (never undefined), this won't trigger today, but it's a latent trap if another caller ever omits force. Normalizing with ?? makes the comparison explicit and safe.
| function hasSameOptions(left: EmbedURLRequest, right: EmbedURLRequest) { | |
| return ( | |
| left.force === right.force && | |
| left.flags?.MediaOnly === right.flags?.MediaOnly && | |
| left.flags?.SourceOnly === right.flags?.SourceOnly && | |
| left.flags?.Spoiler === right.flags?.Spoiler | |
| ); | |
| } | |
| function hasSameOptions(left: EmbedURLRequest, right: EmbedURLRequest) { | |
| return ( | |
| (left.force ?? false) === (right.force ?? false) && | |
| left.flags?.MediaOnly === right.flags?.MediaOnly && | |
| left.flags?.SourceOnly === right.flags?.SourceOnly && | |
| left.flags?.Spoiler === right.flags?.Spoiler | |
| ); | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/bot/src/listeners/messageUpdate.ts
Line: 6-13
Comment:
`hasSameOptions` uses `===` on optional `force?: boolean`, so `undefined` and `false` are treated as different values. Since `parseMessageURLs` always writes `force = false` (never `undefined`), this won't trigger today, but it's a latent trap if another caller ever omits `force`. Normalizing with `??` makes the comparison explicit and safe.
```suggestion
function hasSameOptions(left: EmbedURLRequest, right: EmbedURLRequest) {
return (
(left.force ?? false) === (right.force ?? false) &&
left.flags?.MediaOnly === right.flags?.MediaOnly &&
left.flags?.SourceOnly === right.flags?.SourceOnly &&
left.flags?.Spoiler === right.flags?.Spoiler
);
}
```
How can I resolve this? If you propose a fix, please make it concise.fea3c67 to
e075458
Compare
Summary
Behavior
Edits update in place only when URL values and order stay unchanged. URL additions, removals, replacements, and reordering are ignored. Failed refreshes keep the existing Embedly message and react to the source message with ❌.
Validation
apps/bot/node_modules/.bin/oxlint apps/bot/srcapps/bot/node_modules/.bin/oxfmt --checkon changed bot filestsdownbot build@,!,?@,?!, and spoiler modifiersgit diff --checkRaw bot
tscremains noisy from pre-existing stale workspace declaration outputs.