# Azeroth Auction Assassin Architecture: A Deep Dive into the WoW Auction House Scanner

> Explore the Azeroth Auction Assassin architecture. Learn how this WoW Auction House scanner uses a Python backend, Qt, and Discord alerts to track market data effectively.

- Repository: [FF14 Advanced Market Search/azerothauctionassassin](https://github.com/ff14-advanced-market-search/azerothauctionassassin)
- Tags: architecture
- Published: 2026-03-01

---

**Azeroth Auction Assassin is a Qt-based desktop application that uses a multi-threaded Python backend to scan World of Warcraft Auction House data against JSON configuration files and dispatch Discord alerts.**

The `ff14-advanced-market-search/azerothauctionassassin` repository implements a modular, three-layer architecture designed for real-time auction monitoring. The codebase separates presentation concerns from business logic and infrastructure, enabling both GUI-driven and headless operation modes while maintaining responsive performance through Python's threading capabilities.

## Three-Layer Architecture

The application follows a clean separation of concerns across presentation, business logic, and infrastructure layers.

### Presentation Layer (Qt GUI)

The user interface resides primarily in [`AzerothAuctionAssassin.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/AzerothAuctionAssassin.py), which implements a Qt5 desktop application. This layer handles:

- **Main window initialization**: The `App` class sets up the primary interface, including realm selectors, item/pet configuration panels, and log viewers.
- **Thread management**: Spawns `Item_And_Pet_Statistics` threads to fetch price statistics from the Saddlebag Exchange API without blocking the main window.
- **Log redirection**: Uses a custom `StreamToFile` class to capture `stdout` and `stderr` to timestamped files under `AzerothAuctionAssassinData/logs/`.

An optional **Electron front-end** exists in the `node-ui/` directory, providing an alternative web-based interface that communicates with the same Python backend via IPC.

### Business Logic Layer

The scanning engine lives in [`mega_alerts.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/mega_alerts.py) and [`utils/mega_data_setup.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/mega_data_setup.py). This layer manages:

- **Configuration parsing**: The `MegaData` class loads and validates user-defined snipe lists from JSON files ([`desired_items.json`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/desired_items.json), [`desired_pets.json`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/desired_pets.json), [`desired_ilvl_list.json`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/desired_ilvl_list.json)).
- **Auction scanning**: The `Alerts` class (a `QThread` subclass) orchestrates the continuous scanning loop, utilizing `ThreadPoolExecutor` for parallel realm processing.
- **Alert formatting**: Converts matching auctions into Discord embed payloads via `create_embed` functions.

### Infrastructure Layer

Low-level API interactions and data utilities are encapsulated in `utils/`:

- **API communication**: [`utils/api_requests.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/api_requests.py) and `MegaData` methods like `make_ah_api_request` handle Blizzard OAuth token management (cached via `access_token_creation_unix_time`) and HTTP requests with Tenacity retry logic.
- **Static data**: [`utils/realm_data.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/realm_data.py) provides realm ID mappings, while [`utils/ilvl_resolver.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/ilvl_resolver.py) resolves bonus ID sets for item level calculations.
- **Helper utilities**: [`utils/helpers.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/helpers.py) contains logging utilities, link generators, and Russian-realm specific handling.

## Data Flow Through the Application

The scanning pipeline follows a precise four-stage lifecycle:

### 1. Application Startup

When `python AzerothAuctionAssassin.py` executes, the `App.__init__` method initializes the environment:

```python
log_path = os.path.join(os.getcwd(), "AzerothAuctionAssassinData", "logs")
log_file = os.path.join(
    log_path, f"aaa_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
)
self.stream_handler = StreamToFile(log_file)

```

The system then loads static configuration and spawns background threads for statistics gathering.

### 2. Configuration Loading

The `MegaData` class in [`utils/mega_data_setup.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/mega_data_setup.py) parses user configuration during initialization:

```python
raw_mega_data = json.load(open("AzerothAuctionAssassinData/mega_data.json"))
self.DESIRED_ITEMS = self.__set_desired_items("desired_items", path_to_desired_items)
self.DESIRED_PETS = self.__set_desired_items("desired_pets", path_to_desired_pets)
self.DESIRED_ILVL_LIST = self.__set_desired_ilvl_list(path_to_desired_ilvl_list)

```

Environment variables like `MEGA_WEBHOOK_URL` and `WOW_CLIENT_ID` are resolved through `__set_mega_vars`, and OAuth tokens are validated via `check_access_token`.

### 3. Scanning Execution

When the user clicks **Start Alerts** (or invokes `Alerts.run()` programmatically), the scanning loop begins:

```python
while self.running:
    current_min = int(datetime.now().minute)
    
    matching_realms = [
        realm["dataSetID"]
        for realm in mega_data.get_upload_time_list()
        if is_in_scan_window(
            current_min,
            realm["lastUploadMinute"],
            mega_data.SCAN_TIME_MIN,
            mega_data.SCAN_TIME_MAX,
        )
    ]
    
    pool = ThreadPoolExecutor(max_workers=mega_data.THREADS)
    for connected_id in matching_realms:
        pool.submit(pull_single_realm_data, connected_id)
    pool.shutdown(wait=True)

```

The `pull_single_realm_data` function calls `MegaData.get_listings_single` to fetch auction snapshots, then passes results through the filtering pipeline.

### 4. Filtering and Alert Dispatch

Raw auction data undergoes multi-stage filtering in [`mega_alerts.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/mega_alerts.py):

```python
if "itemID" in auction:
    if auction["itemID"] in mega_data.DESIRED_ITEMS:
        # Price checks and optional ilvl/bonus verification

        pass
else:
    if auction["petID"] in mega_data.DESIRED_PETS:
        # Pet level and breed validation

        pass

```

Advanced item filtering occurs in `check_tertiary_stats_generic`, which validates bonus IDs against RaidBots data. When `USE_POST_MIDNIGHT_ILVL` is enabled, the system calls `utils.ilvl_resolver.resolve_post_midnight_ilvl` for accurate item level calculations.

Matches are formatted as Discord embeds and dispatched to the webhook URL stored in `MEGA_WEBHOOK_URL`.

## Core Components Deep Dive

### UI Initialization ([`AzerothAuctionAssassin.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/AzerothAuctionAssassin.py))

The main entry point creates the Qt application context and sets up the logging infrastructure. It instantiates the `Item_And_Pet_Statistics` thread to pre-load market data before the user begins scanning.

### Configuration Management (`MegaData`)

Located in [`utils/mega_data_setup.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/mega_data_setup.py), this class serves as the central configuration hub. It handles:

- **Region-aware API construction**: The `construct_api_url` method builds appropriate endpoints (`us.api.blizzard.com` vs `eu.api.blizzard.com`) and adapts to Classic/SoD namespaces.
- **Bonus ID resolution**: Fetches socket, leech, avoidance, and speed bonus sets from RaidBots via `get_bonus_id_sets`.
- **Token refresh**: Automatically renews Blizzard OAuth tokens every ~20 hours using `check_access_token`.

### Scanning Engine (`Alerts` Thread)

The `Alerts` class in [`mega_alerts.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/mega_alerts.py) extends `QThread` to run independently of the UI. It manages:

- **Time-window scanning**: Respects `SCAN_TIME_MIN` and `SCAN_TIME_MAX` to check realms only during their upload windows.
- **Parallel processing**: Uses `ThreadPoolExecutor` with `mega_data.THREADS` workers to concurrent fetch auction data across multiple realms.
- **Graceful shutdown**: Responds to `self.running = False` (set by the UI's **Stop Alerts** button or `Ctrl-C` in CLI mode).

## Practical Usage Examples

### Running the Desktop GUI

```bash

# Install dependencies (Python ≥3.8, Qt5)

pip install -r requirements.txt

# Launch the Qt interface

python AzerothAuctionAssassin.py

```

### Headless CLI Scanning

```python
from mega_alerts import Alerts

alerts = Alerts(
    path_to_data_files="AzerothAuctionAssassinData/mega_data.json",
    path_to_desired_items="AzerothAuctionAssassinData/desired_items.json",
    path_to_desired_pets="AzerothAuctionAssassinData/desired_pets.json",
    path_to_desired_ilvl_items="AzerothAuctionAssassinData/desired_ilvl.json",
    path_to_desired_ilvl_list="AzerothAuctionAssassinData/desired_ilvl_list.json",
)

alerts.run()  # Blocks until alerts.running = False

```

### Programmatic Configuration Updates

```python
import json
import pathlib

data_path = pathlib.Path("AzerothAuctionAssassinData/desired_items.json")
items = json.loads(data_path.read_text()) if data_path.exists() else {}

# Add Thunderfury (itemID 19019) with 10 gold target

items["19019"] = 10.0
data_path.write_text(json.dumps(items, indent=2))

```

## Summary

- **Azeroth Auction Assassin** implements a three-tier architecture separating Qt presentation, Python business logic, and API infrastructure concerns.
- The **scanning engine** uses `QThread` and `ThreadPoolExecutor` to maintain UI responsiveness while concurrently polling multiple realm auction houses.
- **Configuration-as-JSON** allows users to define snipe targets via editable files in `AzerothAuctionAssassinData/` without modifying source code.
- **Blizzard API integration** includes automatic OAuth token management and region-specific endpoint handling through the `MegaData` class.
- **Discord integration** formats matching auctions as rich embeds and dispatches them via configurable webhooks.

## Frequently Asked Questions

### How does Azeroth Auction Assassin authenticate with the Blizzard API?

The application uses OAuth 2.0 client credentials flow. The `MegaData` class in [`utils/mega_data_setup.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/utils/mega_data_setup.py) caches access tokens with their creation timestamp (`access_token_creation_unix_time`) and automatically refreshes them via `check_access_token` after approximately 20 hours.

### What threading model prevents the GUI from freezing during scans?

The UI runs on Qt's main thread while the scanning logic executes in a separate `QThread` subclass named `Alerts` (defined in [`mega_alerts.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/mega_alerts.py)). Inside this thread, a `ThreadPoolExecutor` parallelizes API calls across realms, allowing the interface to remain responsive during heavy network I/O.

### How does the scanner determine which realms to check?

The system calls `MegaData.get_upload_time_list()` to retrieve each realm's last upload minute. It then compares this against the current time using `is_in_scan_window`, respecting the user's `SCAN_TIME_MIN` and `SCAN_TIME_MAX` settings to only scan realms that have recently received fresh auction data.

### Can the application run without the Qt graphical interface?

Yes. While [`AzerothAuctionAssassin.py`](https://github.com/ff14-advanced-market-search/azerothauctionassassin/blob/main/AzerothAuctionAssassin.py) provides the primary Qt interface, the core `Alerts` class can be instantiated and executed programmatically. Additionally, an Electron-based UI exists in the `node-ui/` directory for users preferring a web-based frontend.