Skip to content

fix: prevent CLM-mutated self pattern from claiming other looters (#186)#187

Merged
Xerrion merged 2 commits into
masterfrom
fix/186-loot-attribution-clm
Jun 1, 2026
Merged

fix: prevent CLM-mutated self pattern from claiming other looters (#186)#187
Xerrion merged 2 commits into
masterfrom
fix/186-loot-attribution-clm

Conversation

@Xerrion

@Xerrion Xerrion commented Jun 1, 2026

Copy link
Copy Markdown
Owner

Closes #186

Root cause

CleanerLootMessages (and similar addons) mutate Blizzard's loot globals such as LOOT_ITEM_SELF into very broad formats like "+ %s". DragonToast's loot parser converted those %s formats into Lua patterns without anchoring them and tried the self-loot patterns before the other-player patterns. As a result, an other-player loot line like "+ Name : [Item]" matched the CLM-mutated self pattern first, and the loot was mis-attributed to "You" instead of the actual looter.

Fix

Anchor every configured loot/money pattern derived from the localized globals, and parse other-player loot lines before self loot lines. Anchoring prevents the broad self format from swallowing other-player lines, and the reordered parse order makes the intended match win even on clients where CLM has rewritten the globals. Regression tests cover both the CLM-mutated globals and normal Blizzard self/other loot lines.

Verification

  • mise run lint - pass
  • mise run test - pass (63 successes / 0 failures)

Follow-up (non-blocking)

Reviewer suggested adding money-parsing regression tests in a future PR.

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced loot message detection to properly identify both self and other player loot events.
  • Tests

    • Added comprehensive test suite for loot message parsing covering multiple message formats.

Anchor configured loot/money patterns and parse other-player loot
before self loot, so broad CleanerLootMessages-style self formats
like "+ %s" cannot match other-player lines like "+ Name : [Item]".

Adds regression tests covering CLM-mutated globals plus normal
Blizzard self/other loot lines.
@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The PR fixes incorrect loot attribution in multi-player scenarios by reordering pattern matching to scan "other looter" patterns before "self" patterns, preventing false self-attribution. A comprehensive test suite validates the fix across both Blizzard and CleanerLootMessages formats.

Changes

Loot Attribution Fix

Layer / File(s) Summary
Loot parsing order and pattern building
DragonToast/Listeners/LootListener_Shared.lua
BuildConfiguredPattern forwards an additional boolean true to Utils.BuildPattern. ParseLootMessage is reworked to scan "other" loot patterns (category.other) first and return immediately on match; only if no "other" match is found does it scan "self" patterns (category.self).
Test harness and mocking setup
spec/LootListener_spec.lua
Test globals mock GetItemInfo and format constants; loader utilities instantiate shared listener modules into a test namespace; configurable test harness factory registers handlers, captures toast payloads, and supports both Blizzard and CleanerLootMessages configurations.
Unit tests validating loot attribution
spec/LootListener_spec.lua
Five unit tests assert correct looter, isSelf, itemName, and quantity extraction for both self and other-player loot messages across both Blizzard-format ("You receive loot:", " receives loot:") and CleanerLootMessages formats (with + prefixes and separators).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

listeners

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title follows Conventional Commit format (fix: ...) and clearly describes the main fix: preventing self patterns from incorrectly claiming other looters.
Linked Issues check ✅ Passed Changes directly address issue #186 by anchoring patterns and prioritizing other-player patterns before self patterns, preventing mis-attribution of loot.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the loot attribution bug: pattern anchoring in BuildConfiguredPattern and reordered matching logic in ParseLootMessage, plus targeted regression tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@DragonToast/Listeners/LootListener_Shared.lua`:
- Line 117: The pattern returned by Utils.BuildPattern(...) is being anchored
(true -> BuildLocalizedPattern adds ^...$) which makes LootListener_Shared's
msg:match(...) fail when chat has extra trailing/leading text; fix by removing
anchoring or allowing suffixes: either call
Utils.BuildPattern(patternConfig.globalString or patternConfig.fallbackString,
false) so BuildLocalizedPattern doesn't wrap ^...$, or alter the matching in
LootListener_Shared to use string.find/partial matching or to trim known
trailing payload before using msg:match; update references to
Utils.BuildPattern, BuildLocalizedPattern, and the msg:match call accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 321bd6f2-f9b7-4b31-8ac3-6a5f028342d8

📥 Commits

Reviewing files that changed from the base of the PR and between 516fd21 and a7abf92.

📒 Files selected for processing (2)
  • DragonToast/Listeners/LootListener_Shared.lua
  • spec/LootListener_spec.lua

end

return Utils.BuildPattern(patternConfig.globalString or patternConfig.fallbackString)
return Utils.BuildPattern(patternConfig.globalString or patternConfig.fallbackString, true)

@coderabbitai coderabbitai Bot Jun 1, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect BuildPattern definition + how it uses the 2nd (anchor) arg
fd -i listenerutils.lua --exec rg -nP -C8 'BuildPattern'

Repository: Xerrion/DragonToast

Length of output: 2310


🏁 Script executed:

#!/bin/bash
# Find BuildLocalizedPattern implementation + its anchor handling
fd -i listenerutils.lua --exec rg -nP -C6 'BuildLocalizedPattern' . || true
fd -i listenerutils.lua --exec rg -nP -C6 'anchor' . || true

Repository: Xerrion/DragonToast

Length of output: 50376


🏁 Script executed:

#!/bin/bash
# Inspect LootListener_Shared around the BuildPattern call and find where the pattern is used.
nl -ba DragonToast/Listeners/LootListener_Shared.lua | sed -n '80,160p'
nl -ba DragonToast/Listeners/LootListener_Shared.lua | sed -n '160,260p' || true

rg -n "patternConfig|BuildPattern|fallbackString|globalString" DragonToast/Listeners/LootListener_Shared.lua
rg -n "BuildPattern\(" DragonToast/Listeners -S
rg -n "\:match\(" DragonToast/Listeners/LootListener_Shared.lua

Repository: Xerrion/DragonToast

Length of output: 2661


🏁 Script executed:

#!/bin/bash
# Print relevant portion of LootListener_Shared.lua around BuildConfiguredPattern
sed -n '1,220p' DragonToast/Listeners/LootListener_Shared.lua

# Find where the generated patterns are applied (match/Find/parse)
rg -n --fixed-string "BuildConfiguredPattern" DragonToast/Listeners/LootListener_Shared.lua
rg -n --fixed-string "BuildPattern(" DragonToast/Listeners/LootListener_Shared.lua
rg -n "match%(" DragonToast/Listeners/LootListener_Shared.lua
rg -n "string%.match" DragonToast/Listeners/LootListener_Shared.lua
rg -n "pattern.*:match|:match" DragonToast/Listeners/LootListener_Shared.lua

Repository: Xerrion/DragonToast

Length of output: 8609


🏁 Script executed:

#!/bin/bash
# Show the functions around where msg:match(patternPair...) and msg:match(moneyPattern.pattern) are used.
sed -n '220,360p' DragonToast/Listeners/LootListener_Shared.lua

# Find where msg is defined/sanitized in LootListener_Shared.lua
rg -n "local msg|msg\s*=" DragonToast/Listeners/LootListener_Shared.lua
rg -n "IsIndexableChatMessage|indexable|sanity guard" DragonToast/Listeners/LootListener_Shared.lua
rg -n "Parse|Handle|OnEvent|LOOT" DragonToast/Listeners/LootListener_Shared.lua | head -n 50

Repository: Xerrion/DragonToast

Length of output: 6291


Confirm Utils.BuildPattern(..., true) anchors (and trailing text will break matching)

Utils.BuildPattern forwards true into BuildLocalizedPattern, which wraps the generated pattern with ^...$ (DragonToast/Core/ListenerUtils.lua). LootListener_Shared then applies that anchored pattern directly to the full chat payload via msg:match(...) (no trimming/suffix handling), so any trailing characters/whitespace not covered by the Blizzard global string or fallback string will prevent matches.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@DragonToast/Listeners/LootListener_Shared.lua` at line 117, The pattern
returned by Utils.BuildPattern(...) is being anchored (true ->
BuildLocalizedPattern adds ^...$) which makes LootListener_Shared's
msg:match(...) fail when chat has extra trailing/leading text; fix by removing
anchoring or allowing suffixes: either call
Utils.BuildPattern(patternConfig.globalString or patternConfig.fallbackString,
false) so BuildLocalizedPattern doesn't wrap ^...$, or alter the matching in
LootListener_Shared to use string.find/partial matching or to trim known
trailing payload before using msg:match; update references to
Utils.BuildPattern, BuildLocalizedPattern, and the msg:match call accordingly.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@coderabbitai Anchoring is intentional for this fix — issue #186 is specifically caused by the unanchored self pattern (+ %s once CleanerLootMessages mutates LOOT_ITEM_SELF) silently matching a substring of other-player lines like + Name : [Item]. Anchoring forces full-line matches, which is the correct contract here: CHAT_MSG_LOOT payloads are full Blizzard global-string renders (or full CLM-formatted lines), not embedded substrings. The new spec/LootListener_spec.lua cases exercise both Blizzard and CLM payload shapes end-to-end and stay green, so the trailing-text failure mode you flagged is not present in real chat payloads. Keeping the anchor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Xerrion, understood — the contract makes sense. CHAT_MSG_LOOT delivers fully-rendered strings, so anchoring is the correct tool here: it's what prevents the CLM-mutated broad self pattern from swallowing other-player lines via substring matching, and the end-to-end spec coverage confirms the real payload shapes all stay within the anchored boundaries. My concern doesn't apply. Thanks for the detailed context!

(ᵔ ᴥ ᵔ)


✏️ Learnings added
Learnt from: Xerrion
Repo: Xerrion/DragonToast PR: 187
File: DragonToast/Listeners/LootListener_Shared.lua:117-117
Timestamp: 2026-06-01T09:15:55.957Z
Learning: In DragonToast (`DragonToast/Listeners/LootListener_Shared.lua`), `BuildConfiguredPattern` calls `Utils.BuildPattern(..., true)` to produce anchored (`^...$`) patterns. This anchoring is intentional: `CHAT_MSG_LOOT` payloads are always fully-rendered Blizzard global-string lines or full CLM-formatted lines, never embedded substrings. Without anchoring, the CLM-mutated self pattern (e.g., `+ %s`) can match as a substring of other-player lines like `+ Name : [Item]`, causing mis-attribution (issue `#186`). Do not flag the `true` anchor argument as unnecessary or suggest removing it.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@Xerrion Xerrion merged commit 69f992f into master Jun 1, 2026
2 of 3 checks passed
@Xerrion Xerrion deleted the fix/186-loot-attribution-clm branch June 1, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: In raids (at least; possibly parties, too), loot is attributed to player when it shouldn't be

1 participant