VMangos AHBot: The Lost Config

If you’re running a private World of Warcraft server using a MaNGOS‐based core, you might already know about the “AHBot” feature that automatically adds items to your auction house. However, the default AHBot has limited flexibility — you can’t easily control which items appear, their prices, or their quantities and the AHBot.conf doesn’t seem to work. I decided to use my Monk Mode time this week to fix that. This article shows you how to configure AHBot, customize it with a brand‐new Python script, and keep your auction house feeling natural and dynamic.

Whether you’re a seasoned VMangos administrator or just diving into private servers, this definitive guide will give you everything you need to transform your Auction House into a player‐friendly marketplace, complete with script details, SQL insert files, and clear instructions. Read on to unlock the full potential of your AHBot and create the most compelling WoW experience possible.

TLDR: Download the Script or Insert Files (or Both!)

If you just want the file without the explanation, get them here (but bookmark this page for later, you might need it.)

First Things First: Configure mangosd.conf

Before diving into the AHBot script itself, you may want to edit a few settings in your MaNGOS config files. Typically, these files are located in vmangos/etc/ on a Linux setup, or possibly in the same directory as your mangosd binary if you’re running a Windows server (I don’t run Windows so not sure, sorry.) Open mangosd.conf and look for the following options (They’re not all together so use “find in file”):

Progression.UnlinkedAuctionHouses = 1
AllowTwoSide.Interaction.Auction = 1

# AUCTION HOUSE BOT

# AHBot.Enable
#   Enable AH bot.
#   Default: 0 - off
#            1 - on

# AHBot.ah.fid
#   Faction template ID for the AH bot.
#   120 = Neutral (Booty Bay, Gadgetzan, Everlook)
#   55  = Alliance (Ironforge, Stormwind, Teldrassil)
#   29  = Horde (Orgrimmar, Thunderbluff, Undercity)

# AHBot.itemcount
#   Max number of auction listings for the AH bot, including any player listings.
#   Increase or decrease this to match the size of your generated data file.

AHBot.Enable = 1
AHBot.ah.fid = 55
AHBot.itemcount = 24304

With AHBot enabled and a suitable faction ID (e.g., 55 for Alliance), the server is ready to accept your own custom item pool. Now let’s see how we generate that pool.

The Default AHBot Is Limited

By default, AHBot uses data from the auctionhousebot table in your database to generate listings. But if you’ve tried customizing it, you’ve likely found:

  • You can’t easily filter out low‐quality items or quest items.
  • Bonding rules aren’t always consistent.
  • Prices don’t scale well for large stacks.
  • You might accidentally get hundreds of identical items.

These issues can lead to an unrealistic or stagnant auction house. Or worse: an auction house stuffed with junk no one wants.

Introducing the Lost AHBot Config Script

To solve these problems, I created a Python script that lets you:

  • Filter items by quality, item level, or classes.
  • Avoid “Deprecated” or “Test” items.
  • Respect bonding rules (no bind on pickup, etc.).
  • Adjust prices with multipliers, random variance, and stack‐size scaling.
  • Manually include or exclude specific item entries.
  • Generate a text file of SQL INSERT statements you can review before updating your database.

Can I Use This Script With My Setup?

There are lots of different setups out the like TrinityCore, AzerothCore, Mangos, CMangos VMangos etc. If you want to know if this script will work with your setup you just need to check if the auctionhousebot table is setup the same. If it is, you’re good to go because ultimatley we’re just inserting lines into that table.

Log into your sql shell with: (yes there’s no space between the -p and your password)

mysql -u YourUserName -pYourPassword --database=mangos

The, we just need to look at the auctionhousebot table:

DESCRIBE auctionhousebot;

If it has 4 fields like this, you can use this config script.

+--------+---------------------+------+-----+---------+-------+
| Field    | Type                       | Null | Key | Default | Extra |
+--------+---------------------+------+-----+---------+-------+
| item     | int(11) unsigned     | NO   |     | NULL      |       
| stack    | tinyint(3) unsigned | NO   |     | 1       |       |
| bid       | int(11) unsigned     | NO   |     | 1       |       |
| buyout | int(11) unsigned     | NO   |     | 1       |       |
+--------+---------------------+------+-----+---------+-------+

How the Script Works

  1. Connects to your WoW database (the same one where item_template and auctionhousebot live).
  2. Queries the item_template table based on filters you set (e.g., item quality, name keywords, etc.).
  3. Applies randomization for variations and price. Weapons and armor, for instance, get bigger multipliers. Consumables might have varied stack sizes.
  4. Outputs a file of INSERT INTO auctionhousebot... lines.
  5. Manual Review—you can open and check these lines before importing, ensuring you only add what you want.

Script: The Complete Code

Below is the final version of the script. Copy it into a file (e.g., ahbot_generator.py). Or download it from GitHub. You can customize any of the variables at the top: Ensure you add your SQL username and password!

#!/usr/bin/env python3
import mysql.connector
import random

# =========================
# Configuration Variables
# =========================

# Database connection settings
DB_HOST = 'localhost'
DB_USER = 'YourUsername'
DB_PASSWORD = 'YourPassword'
DB_DATABASE = 'mangos'

# General multipliers for bid and buyout prices (applied to the sell_price field)
bid_multiplier = 0.5    # Base multiplier for bid price (for non-weapon/armor items)
buyout_multiplier = 2.5  # Base multiplier for buyout price (for non-weapon/armor items)

# Additional multipliers for weapons (class 2) and armor (class 4)
weapon_armor_bid_multiplier = 4    # Bid multiplier for weapons and armor
weapon_armor_buyout_multiplier = 10  # Buyout multiplier for weapons and armor

# Randomness ranges for price adjustments (e.g., ±25% variation)
bid_variance = (0.75, 1.25)
buyout_variance = (0.75, 1.25)

# =========================
# Variation Range Settings
# =========================
# For weapons (class 2) and armor (class 4), generate a random number of variations between these values.
min_variations_weapon_armor = 1  # Minimum variations for weapons/armor
max_variations_weapon_armor = 3  # Maximum variations for weapons/armor

# For other allowed classes, generate a random number of variations between these values.
min_variations_other = 2         # Minimum variations for other items
max_variations_other = 6         # Maximum variations for other items

# =========================
# Optional Filter Settings
# =========================

# Quality filter: Define allowed quality levels.
# Quality levels:
#   0 = Grey (Poor), 1 = White (Common), 2 = Green (Uncommon),
#   3 = Blue (Rare), 4 = Purple (Epic), 5 = Orange (Legendary),
#   6 = Red (Artifact), 7 = Gold (Bind to Account)
use_filter_quality = True
allowed_qualities = [1, 2, 3, 4, 5]  # Adjust as desired

use_filter_required_honor = True      # Only include items with required_honor_rank = 0
use_filter_required_reputation = True # Only include items with required_reputation_faction = 0 AND required_reputation_rank = 0
use_filter_item_level = False         # Optionally filter by item_level range
min_item_level = 0                    # Minimum item level (inclusive)
max_item_level = 100                  # Maximum item level (inclusive)

# Exclude Certain classes. See below for classes
# 0 Consumable, 1 Container, 2 Weapon, 3 Gem, 4 Armor, 5 Reagent,
# 6 Projectile, 7 Trade Goods, 8 Generic(OBSOLETE), 9 Recipe, 
# 10 Money(OBSOLETE), 11 Quiver, 12 Quest, 13 Key, 14 Permanent(OBSOLETE),
# 15 Miscellaneous, 16 Glyph
exclude_classes = [8, 10, 12, 13, 14]

# Exclude any item with "Deprecated" in its name
exclude_deprecated = True

# Exclude Test Items
exclude_test = True

# === Manual Item Code Overrides ===
include_items = []  # e.g., [12345, 67890]
exclude_items = []  # e.g., [18948, 23456]

# =========================
# Bonding Rules (unchanged)
# =========================
# 0 No bounds, 1 Bind on Pickup, 2 BoE, 3 Bind on Use, 4 Quest Item, 5 Quest Item1
def bonding_allowed(item_class, bonding):
    return bonding in (0, 2, 3)

# =========================
# END OF USER CONFIG
# =========================

# =========================
# Class Labels for Comments
# =========================
class_labels = {
    0: 'Consumable',
    1: 'Container',
    2: 'Weapon',
    3: 'Gem',
    4: 'Armor',
    5: 'Reagent',
    6: 'Projectile',
    7: 'Trade Goods',
    9: 'Recipe',
    11: 'Quiver',
    12: 'Quest',
    13: 'Key',
    15: 'Miscellaneous',
    16: 'Glyph'
}

# =========================
# Build the WHERE Clause
# =========================
where_clauses = []

if use_filter_quality:
    allowed_qualities_str = ", ".join(str(q) for q in allowed_qualities)
    where_clauses.append(f"quality IN ({allowed_qualities_str})")
if use_filter_required_honor:
    where_clauses.append("required_honor_rank = 0")
if use_filter_required_reputation:
    where_clauses.append("required_reputation_faction = 0 AND required_reputation_rank = 0")
if use_filter_item_level:
    where_clauses.append(f"item_level BETWEEN {min_item_level} AND {max_item_level}")

# Exclude quest items and keys (classes 12 and 13)
where_clauses.append("class NOT IN (12, 13)")

# Exclude items with "Deprecated" in the name if desired
if exclude_deprecated:
    where_clauses.append("name NOT LIKE '%Deprecated%'")

# Exclude items with "Test" in the name if desired
if exclude_test:
    where_clauses.append("name NOT LIKE '%Test%'")

# Exclude items with a sell_price of 0.
where_clauses.append("sell_price > 0")

if where_clauses:
    base_filter = " AND ".join(where_clauses)
else:
    base_filter = "1"  # No filtering

# If include_items is set, force include those items using OR.
if include_items:
    include_clause = "entry IN (" + ", ".join(str(x) for x in include_items) + ")"
    combined_filter = f"(({base_filter}) OR ({include_clause}))"
else:
    combined_filter = base_filter

# Always exclude items listed in exclude_items.
if exclude_items:
    exclude_clause = "entry NOT IN (" + ", ".join(str(x) for x in exclude_items) + ")"
    final_where_clause = f"({combined_filter}) AND ({exclude_clause})"
else:
    final_where_clause = combined_filter

# =========================
# Connect to the Database
# =========================
cnx = mysql.connector.connect(
    host=DB_HOST,
    user=DB_USER,
    password=DB_PASSWORD,
    database=DB_DATABASE
)
cursor = cnx.cursor(dictionary=True)

# Query the item_template table for eligible items.
# Now we also retrieve the "stackable" field, which is the maximum stack size.
query = f"""
SELECT entry, class, buy_count, sell_price, bonding, name, stackable
FROM item_template
WHERE {final_where_clause}
ORDER BY class, entry;
"""
cursor.execute(query)
rows = cursor.fetchall()

# =========================
# Process Results & Generate Output
# =========================
output_lines = []
current_class = None
total_inserts = 0

for row in rows:
    item_class = row['class']
    bonding = row['bonding']
    # Apply bonding rules based on item class.
    if not bonding_allowed(item_class, bonding):
        continue  # Skip items that don't meet bonding criteria

    # When the item class changes, add a comment header.
    if item_class != current_class:
        current_class = item_class
        class_name = class_labels.get(item_class, f"Class {item_class}")
        output_lines.append(f"-- {class_name}")
    entry = row['entry']
    sell_price = row['sell_price']
    item_name = row['name']
    # Use the "stackable" field for max stack size.
    max_stack = row['stackable']

    # Determine number of variations based on item class.
    if item_class in (2, 4):  # Weapon or Armor
        n_variations_this = random.randint(min_variations_weapon_armor, max_variations_weapon_armor)
    else:
        n_variations_this = random.randint(min_variations_other, max_variations_other)
    
    for i in range(n_variations_this):
        # Determine the stack based on item class:
        # For Consumable (0), Reagent (5), Projectile (6), and Trade Goods (7):
        # 80% of the time, use the max stack; 20% choose a random value between 1 and max_stack.
        # For all other classes, stack should be 1.
        if item_class in (0, 5, 6, 7):
            if random.random() < 0.8:
                stack = max_stack
            else:
                stack = random.randint(1, max_stack)
        else:
            stack = 1

        # Calculate price per unit with multipliers and variance.
        if item_class in (2, 4):
            unit_bid = sell_price * weapon_armor_bid_multiplier * random.uniform(*bid_variance)
            unit_buyout = sell_price * weapon_armor_buyout_multiplier * random.uniform(*buyout_variance)
        else:
            unit_bid = sell_price * bid_multiplier * random.uniform(*bid_variance)
            unit_buyout = sell_price * buyout_multiplier * random.uniform(*buyout_variance)
        
        # Multiply the unit price by the stack amount.
        bid = round(unit_bid * stack)
        buyout = round(unit_buyout * stack)
        
        # Append the item name as a comment after the INSERT statement.
        insert_stmt = (f"INSERT INTO `mangos`.`auctionhousebot` (`item`, `stack`, `bid`, `buyout`) "
                       f"VALUES({entry}, {stack}, {bid}, {buyout}); -- {item_name}")
        output_lines.append(insert_stmt)
        total_inserts += 1

# =========================
# Write Output to a File
# =========================
output_filename = "ahbot_inserts.txt"
with open(output_filename, "w") as f:
    for line in output_lines:
        f.write(line + "\n")

print(f"Total items processed: {len(rows)}")
print(f"Total INSERT statements generated: {total_inserts}")
print(f"Output written to {output_filename}")

cursor.close()
cnx.close()


Installation and Dependencies

If you get an error “Externally Managed Environment” when installing the dependancies, see below to create a virtual environment (it’s the standard way to install dependanceis without it affecting the rest of your system.)

Windows:

  1. Download Python (version 3.7 or higher).
  2. Install MySQL Connector for Python:
pip install mysql-connector-python
  1. Save the script as ahbot_generator.py.
  1. Install Python via Homebrew:
brew install python
  1. Install MySQL Connector for Python:
pip3 install mysql-connector-python
  1. Place the script (e.g., ahbot_generator.py) wherever is convenient.

Linux (Ubuntu/Debian):

  1. Install Python:
sudo apt-get update
sudo apt-get install python3 python3-pip
  1. Install MySQL Connector:
pip3 install mysql-connector-python
  1. Save and run the script in your user directory.

Configuring the Script

All configuration happens at the top of ahbot_generator.py. Here are some key variables you can tweak:

  • bid_multiplier / buyout_multiplier: Adjust for non‐weapon/armor items.
  • weapon_armor_bid_multiplier / weapon_armor_buyout_multiplier: Make weapons and armor more or less expensive.
  • allowed_qualities: Control which item qualities are included.
  • include_items / exclude_items: Always include or exclude specific items by entry ID.

For example, if you want to only include epic and legendary items, change:

allowed_qualities = [4, 5]

All the options are explained within the script.

The ahbot_inserts File

After running python ahbot_generator.py, you’ll see a text file appear called ahbot_inserts.txt, open it and you’ll see something like:

-- Weapon
INSERT INTO `mangos`.`auctionhousebot` (`item`, `stack`, `bid`, `buyout`) VALUES(14543, 1, 3749, 18745); -- Sword of the Perfect Strike
INSERT INTO `mangos`.`auctionhousebot` (`item`, `stack`, `bid`, `buyout`) VALUES(14543, 1, 4232, 21160); -- Sword of the Perfect Strike

-- Trade Goods
INSERT INTO `mangos`.`auctionhousebot` (`item`, `stack`, `bid`, `buyout`) VALUES(8786, 5, 240, 1200); -- Iron Ore
INSERT INTO `mangos`.`auctionhousebot` (`item`, `stack`, `bid`, `buyout`) VALUES(8786, 20, 900, 4500); -- Iron Ore

This file is named ahbot_inserts.txt. Check or change it for duplicates or questionable items before proceeding.

Importing the Data

  1. Truncate the Existing Table:
    In your MySQL/MariaDB shell:
TRUNCATE TABLE auctionhousebot;

This wipes existing data in auctionhousebot.

Load the New Data:

SOURCE /full/path/to/ahbot_inserts.txt;
  1. This applies all the INSERT statements to your auctionhousebot table.
  2. Restart/Reload:
    Restart your MaNGOS world server so it reloads the AHBot with the new items. (Ensure that you already updated the mangosd.conf file with the number of items as mentioned above.)

Benefits of Using an Output File

  • Transparency: You see exactly which items are being inserted and at what prices.
  • Version Control: You could even commit the ahbot_inserts.txt file to a Git repository to track changes.
  • Easy to Rerun or Tweak: If you don’t like the results, adjust the script or remove lines before importing.

Handling Managed Python Environments & Creating a Virtual Environment

In some operating systems—particularly modern Linux distributions and macOS—trying to install Python packages system‐wide with pip may result in a “managed environment” error or warnings about “externally managed environments.” This occurs because the OS strongly encourages you to isolate third‐party Python libraries rather than installing them globally. The best practice (and easiest fix) is to create a virtual environment for Python. This way, you can cleanly install all required dependencies (like mysql-connector-python) without conflicting with system packages or requiring administrator privileges.

Below are quick guidelines for each major platform:

Windows

  1. Open Command Prompt or PowerShell
    Press Windows Key + R, then type cmd (or search for “PowerShell”) and press Enter.
  2. Navigate to Your Script Directory cd path\to\your\script
  3. Create a Virtual Environment python -m venv venv This command makes a folder named venv which holds the isolated environment.
  4. Activate the Environment venv\Scripts\activate Once activated, your prompt changes to indicate you’re inside the virtual environment.
  5. Install Dependencies pip install mysql-connector-python Now these packages go into venv instead of the system environment.
  6. Run Your Script python ahbot_generator.py

macOS

  1. Open Terminal
    Typically found in /Applications/Utilities/Terminal.app.
  2. Install Python 3 (if needed)
    If you haven’t already, install Python via Homebrew or use the system Python if it’s recent enough.
  3. Create a Virtual Environment python3 -m venv venv This creates a venv folder in your current directory.
  4. Activate the Environment source venv/bin/activate You’ll see (venv) at the beginning of your terminal prompt, indicating activation.
  5. Install Dependencies pip install mysql-connector-python Python libraries now go into the isolated venv.
  6. Run the Script python ahbot_generator.py

Linux (Ubuntu, Fedora, Arch, etc.)

  1. Open Terminal
    Use your distro’s default or any shell you prefer.
  2. Install Python 3
    • Ubuntu/Debian: sudo apt-get update sudo apt-get install python3 python3-pip
    • Fedora: sudo dnf install python3 python3-pip
    • Arch Linux (or derivatives): sudo pacman -S python python-pip
  3. Create & Activate the Virtual Environment cd /path/to/your/script python3 -m venv venv source venv/bin/activate If your system is configured with Python in managed mode (PEP 668), this approach avoids “externally managed environment” warnings by installing packages into the local venv.
  4. Install Dependencies pip install mysql-connector-python Your pip commands now install libraries inside venv.
  5. Run the Script python ahbot_generator.py

Final Thoughts

With this setup, you’ll have a lively, diverse auction house that avoids “junk” items, uses realistic prices, and even respects your custom bonding and quality rules. It’s a giant step beyond the default AHBot.

Other Articles & Next Steps:

  • I’m currently working on an update script that will insert automatically and periodically to keep the AH fresh with no user actions needed. Just set and forget.
  • I’ve also made it a goal this year to add local AI into the chat system so that ncp and partybots can actually talk!
  • Have questions? Or have you improved this? Follow me on X and send me a DM

Happy gaming!

Gregory J. Gaynor

Meet Gregory, the writer & brains behind Face Dragons. He's the go-to guy for getting things done.

Gregory's been living the digital nomad life in Asia for as long as anyone can remember, helping clients smash their goals. He writes on topics like software, personal knowledge management (PKM), and personal development. When he's not writing, you'll catch him at the local MMA gym, nose buried in a book, or just chilling with the family.