How Does AAA Resolve Item Levels? A Technical Deep Dive into Azeroth Auction Assassin
Azeroth Auction Assassin (AAA) resolves item levels using either static data from ilvl_items.json for Classic content or a dynamic bonus ID resolver for modern Retail WoW, controlled by the USE_POST_MIDNIGHT_ILVL configuration flag.
Azeroth Auction Assassin (AAA) is an open-source auction house sniper for World of Warcraft that requires precise item level calculation to filter deals accurately. Understanding how AAA resolves item levels is crucial for configuring the tool correctly across different WoW expansions and data formats. The system employs two distinct resolution paths depending on whether you are scanning Classic-era content or modern Retail auctions.
The Two Modes of Item Level Resolution
AAA determines which resolution strategy to use based on the USE_POST_MIDNIGHT_ILVL boolean flag stored in mega_data.json or passed as an environment variable【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/mega_data_setup.py#L49-L51】.
Pre-Midnight Static Mode (Classic Data)
When USE_POST_MIDNIGHT_ILVL is set to false (the default for Classic data), AAA uses a static lookup approach. The application downloads ilvl_items.json during startup and stores the values in MegaData.base_ilvls. During auction scanning, the item level is retrieved directly from DESIRED_ILVL_ITEMS["base_ilvls"][item_id]【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/mega_alerts.py#L418-L425】.
This mode optionally applies bonus-specific additive item levels (ilvl_addition) extracted from the static bonuses.json dataset (see get_bonus_id_sets).
Post-Midnight Dynamic Mode (Retail Data)
When USE_POST_MIDNIGHT_ILVL is true (required for modern Retail WoW after the "midnight" patch), AAA executes a full-featured resolver that interprets bonus IDs, drop-level curves, and era-specific scaling using live Raidbots data. The core algorithm lives in utils/ilvl_resolver.py within the resolve_post_midnight_ilvl function【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L77-L89】.
How the Post-Midnight Resolver Works
The dynamic resolver processes item levels through several sophisticated stages to handle Retail WoW's complex scaling systems.
Initialization and Data Loading
When USE_POST_MIDNIGHT_ILVL is enabled, the MegaData class in utils/mega_data_setup.py fetches and caches four critical datasets from Raidbots on initialization:
self.bonuses_by_id = get_bonus_ids()["bonuses_by_id"] # bonus definitions
self.equippable_items = get_raidbots_equippable_items() # base item-level per item
self.item_curves = get_raidbots_item_curves() # level-curve tables
self.item_squish_era = get_raidbots_item_squish_era() # era definitions
These objects are stored in memory and passed to the resolver for each auction evaluation.
The Resolution Algorithm
The resolve_post_midnight_ilvl function executes a multi-stage algorithm:
-
Base Level Retrieval: Fetches the base item level from
equippable_itemsvia_get_base_item_level, handling both string and integer keys. If missing, the base defaults to0【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L10-L31】. -
Bonus ID Parsing: Iterates over
bonus_liststo extract four operation types frombonuses_by_id:itemLevel→ set-level operation (priority-based)levelOffset→ additive offsetlevelOffsetSecondary→ extra additive offset applied after all erasdropLevelCurve→ curve-based offset using the optionaldrop_levelargument (the required player level)
The collected operations are grouped by squish era to allow era-specific handling【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L100-L132】.
-
Legacy Offset Handling: Sums any plain
levelfields in bonuses intolegacy_level_offsetas a fallback when no other operations exist【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L134-L137】. -
Early Exit: If no operations were collected, returns the base level plus any legacy offset【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L144-L148】.
Era-Based Processing
For modern Retail items, the resolver walks through the list of eras supplied by item_squish_era (parsed from item-squish-era.json). For each era it:
-
Applies Global Curves: Optionally applies a global curve (
curveId) via_apply_curveusing linear interpolation between points【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L34-L74】. -
Selects Best Set-Level: Chooses the best set-level operation for that era (lowest priority) and overwrites
item_level【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L68-L72】. -
Applies Drop-Level Curve: If a
drop_levelis present, scales the required level through the curve and adds any static offset【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L73-L80】. -
Aggregates Level Offsets: Sums all
levelOffsetvalues belonging to the era【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L81-L85】. -
Applies Secondary Offsets: After era processing, sums every
levelOffsetSecondaryonto the finalitem_level【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L86-L88】.
The function returns the integer item_level, or None if the base could not be determined and no operations existed【/cache/repos/github.com/ff14-advanced-market-search/azerothauctionassassin/main/utils/ilvl_resolver.py#L89-L90】.
Integration with the Alert Pipeline
In mega_alerts.py, the application selects the appropriate resolution path for each auction:
if mega_data.USE_POST_MIDNIGHT_ILVL:
ilvl = resolve_post_midnight_ilvl(
auction["item"]["id"],
auction["item"]["bonus_lists"],
required_lvl,
mega_data.bonuses_by_id,
mega_data.equippable_items,
mega_data.item_curves,
mega_data.item_squish_era,
)
else:
# static path (base_ilvl + additive bonuses)
…
If the resolver returns None—which occurs when the base item level cannot be determined from equippable_items and no valid bonus operations exist—the auction is discarded by the alert pipeline. This prevents false positives from malformed auction data or unknown items that cannot be accurately priced.
Code Examples
Direct Resolution Using the API
You can resolve item levels programmatically using the post-midnight resolver:
from utils.ilvl_resolver import resolve_post_midnight_ilvl
from utils.mega_data_setup import MegaData
# Initialise MegaData (loads static & Raidbots data)
mega = MegaData()
item_id = 19019 # Example: "Vicious Gladiator's Greatsword"
bonus_lists = [5275, 6228] # Bonus IDs from the auction
required_lvl = 60 # Player level required to use the item
ilvl = resolve_post_midnight_ilvl(
item_id,
bonus_lists,
required_lvl,
mega.bonuses_by_id,
mega.equippable_items,
mega.item_curves,
mega.item_squish_era,
)
print(f"Resolved ilvl: {ilvl}")
Using the Built-in Alert Filter
For standard operation, allow the main application to handle resolution automatically:
from main import AzerothAuctionAssassin # entry point for the whole app
# Run the scanner; the engine will automatically pick the correct ilvl mode
# based on the `USE_POST_MIDNIGHT_ILVL` flag in mega_data.json.
AzerothAuctionAssassin().run()
Key Files and Their Roles
| File | Role | Link |
|---|---|---|
utils/ilvl_resolver.py |
Core algorithm that interprets bonus IDs, curves, and eras to compute a post-midnight ilvl | utils/ilvl_resolver.py |
utils/mega_data_setup.py |
Loads configuration, decides whether to use the dynamic resolver, and fetches the Raidbots static datasets | utils/mega_data_setup.py |
mega_alerts.py |
Where the ilvl is actually requested for each auction (chooses static vs dynamic path) | mega_alerts.py |
utils/api_requests.py |
Provides the static ilvl_items.json data for the pre-midnight path |
utils/api_requests.py |
StaticData/ilvl_items.json |
Cached static ilvl values for Classic and early-retail items (used when USE_POST_MIDNIGHT_ILVL is false) |
StaticData/ilvl_items.json |
README.md |
High-level description of the project's purpose and configuration options (including USE_POST_MIDNIGHT_ILVL) |
README.md |
Summary
- AAA uses two distinct resolution strategies: static lookup from
ilvl_items.jsonfor Classic data, and dynamic calculation using bonus IDs and curves for modern Retail WoW. - The
USE_POST_MIDNIGHT_ILVLflag controls which mode is active, with the dynamic resolver fetching live data from Raidbots on initialization. - The post-midnight resolver in
utils/ilvl_resolver.pyprocesses bonus lists through era-specific operations including set-level overrides, drop-level curves, and additive offsets to compute final item levels. - Failed resolutions return
None, causing the alert pipeline inmega_alerts.pyto discard auctions for unknown items or malformed data.
Frequently Asked Questions
What is the difference between pre-midnight and post-midnight item level resolution?
Pre-midnight resolution uses static data from ilvl_items.json downloaded during startup, suitable for Classic WoW where item levels are fixed values stored in MegaData.base_ilvls. Post-midnight resolution uses a dynamic algorithm in utils/ilvl_resolver.py that interprets bonus IDs, drop-level curves, and era-specific scaling from live Raidbots data, which is required for modern Retail WoW after the "midnight" patch introduced complex item scaling systems.
How does AAA handle bonus IDs when calculating item levels?
The post-midnight resolver iterates over each bonus ID in the auction's bonus_lists and extracts four operation types from the bonuses_by_id dataset: itemLevel (set-level operations with priority handling), levelOffset (additive bonuses), levelOffsetSecondary (post-era additive bonuses), and dropLevelCurve (scaling curves based on required player level). These operations are grouped by squish era and applied sequentially through era-based processing to determine the final item level.
What happens if the item level resolver returns None?
If resolve_post_midnight_ilvl returns None—which occurs when the base item level cannot be determined from equippable_items and no valid bonus operations exist—the alert pipeline in mega_alerts.py discards the auction. This prevents false positives from malformed auction data or unknown items that cannot be accurately priced, ensuring only verifiable items trigger alerts.
Where does AAA get its data for the dynamic resolver?
When USE_POST_MIDNIGHT_ILVL is enabled, the MegaData class fetches four critical datasets from Raidbots during initialization: bonuses_by_id (bonus definitions), equippable_items (base item levels per item), item_curves (level-curve tables), and item_squish_era (era definitions). These are cached in the MegaData instance and passed to resolve_post_midnight_ilvl for real-time calculation of item levels based on current game data.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →