Skip to content

feat(ahbot): whisper restock orders via separate clerk bot - #164

Open
yujiabei wants to merge 1 commit into
azerothcore:masterfrom
yujiabei:feature/ahbot-whisper-orders
Open

feat(ahbot): whisper restock orders via separate clerk bot#164
yujiabei wants to merge 1 commit into
azerothcore:masterfrom
yujiabei:feature/ahbot-whisper-orders

Conversation

@yujiabei

@yujiabei yujiabei commented Aug 9, 2026

Copy link
Copy Markdown

Summary

  • Add whisper order parse helpers and PlayerScript clerk path
  • Keep AH seller GUID offline so random restock/bidding never pauses
  • Config: WhisperOrders, WhisperOrdersAccount, WhisperOrdersReceiverGUID
  • Fix Alliance/Horde auction house selection via character-cache race

Test plan

  • Whisper clerk with Shift-click item link + quantity
  • Listing appears on correct faction AH
  • Seller character remains offline; Update cycles continue

Summary by CodeRabbit

  • New Features
    • Added whisper-based auction ordering for authorized players.
    • Players can request item quantities and receive confirmation, partial-success, or error messages.
    • Added automatic auction listing with faction-appropriate pricing, item validation, and stack handling.
    • Added configuration for enabling whisper orders and specifying the authorized account and receiver character.
    • Added automatic startup handling for the auction-house clerk character.

Allow a configured player account to whisper item-link + quantity to an
online clerk character; listings still use the offline AH seller GUID so
random restock/bidding keep running. Fix Alliance/Horde house selection
using character-cache race.
@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Whisper orders can now be configured, routed to an online clerk, parsed from private messages, and fulfilled through auction listings. Seller-player lifecycle handling is centralized for both whisper orders and scheduled auction-bot updates.

Changes

Whisper Order Restocking

Layer / File(s) Summary
Configuration and clerk startup
conf/mod_ahbot.conf.dist, src/AuctionHouseBotCommon.*, src/AuctionHouseBotWorldScript.*
Adds whisper-order settings and global state. Loads the settings and logs in the configured clerk character after startup checks.
Order contract and item selling
src/AHBotWhisperOrderParse.h, src/AuctionHouseBot.h, src/AuctionHouseBot.cpp
Adds strict whisper parsing and copper formatting. Adds OrderResult, temporary seller-player lifecycle helpers, and item listing with validation, randomized pricing, deposits, and partial-failure reporting.
Auction cycle player lifecycle
src/AuctionHouseBot.cpp
Uses one acquired seller player and session across faction selling and buying operations. Centralizes cleanup after each update cycle.
Whisper order routing
src/AuctionHouseBotPlayerScript.cpp, src/AuctionHouseBotScript.cpp
Registers private-whisper handling. Authorizes senders, submits valid orders, reports listing results, and suppresses handled messages.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WhisperSender
  participant AHBot_PlayerScript
  participant AuctionHouseBot
  participant TemporarySellerPlayer
  participant AuctionHouse
  WhisperSender->>AHBot_PlayerScript: Send private item and quantity whisper
  AHBot_PlayerScript->>AHBot_PlayerScript: Validate receiver, account, and order syntax
  AHBot_PlayerScript->>AuctionHouseBot: Call SellOrderedItem(itemId, quantity)
  AuctionHouseBot->>TemporarySellerPlayer: Acquire seller player
  AuctionHouseBot->>AuctionHouse: Create and persist auction listings
  AuctionHouse-->>AuctionHouseBot: Return listing totals and errors
  AuctionHouseBot-->>AHBot_PlayerScript: Return OrderResult
  AHBot_PlayerScript-->>WhisperSender: Send localized result message
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% 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
Title check ✅ Passed The title clearly identifies the main change: whisper-based restock orders through a separate clerk bot.
Description check ✅ Passed The description explains the main changes and lists tests, but it omits the template headings and detailed step-by-step test instructions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🧹 Nitpick comments (2)
conf/mod_ahbot.conf.dist (1)

123-125: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider defaulting WhisperOrders to 0.

Every other AHBot toggle in this file defaults to 0. WhisperOrders enables an item-creation path, so an opt-in default matches the module convention and the safer posture. The in-code global also initializes to false in src/AuctionHouseBotCommon.cpp, while sConfigMgr->GetOption uses true as fallback; aligning all three removes the inconsistency.

♻️ Proposed change
-AuctionHouseBot.WhisperOrders = 1
+AuctionHouseBot.WhisperOrders = 0

Also update the doc block at Line 41 and the fallback in AHBot_WorldScript::OnBeforeConfigLoad.

🤖 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 `@conf/mod_ahbot.conf.dist` around lines 123 - 125, Default
AuctionHouseBot.WhisperOrders to 0 in the distributed configuration and update
its documentation entry to match. In AHBot_WorldScript::OnBeforeConfigLoad,
change the GetOption fallback for WhisperOrders from true to false so it aligns
with the false-initialized global and opt-in module defaults.
src/AuctionHouseBot.cpp (1)

1136-1193: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Reduce duplication with Sell.

Lines 1141-1191 repeat the item-creation, pricing, deposit, and auction-persistence sequence of Sell at Lines 801-929. The two copies already differ in small ways, for example the price fallback. Extract a shared private helper that takes config, prototype, stackCount, and AHBplayer, and returns the created AuctionEntry. Both call sites then stay consistent.

🤖 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 `@src/AuctionHouseBot.cpp` around lines 1136 - 1193, Extract the duplicated
item creation, pricing, deposit, and auction persistence flow from Sell and the
shown listing loop into a shared private helper that accepts config, prototype,
stackCount, and AHBplayer and returns the created AuctionEntry. Update both Sell
and the loop to use this helper, preserving each operation’s transaction and
result-accounting behavior while centralizing price fallback logic.
🤖 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 `@src/AHBotWhisperOrderParse.h`:
- Around line 18-53: Add uint32_t overflow guards to the digit-accumulation
loops for itemId and quantity in the whisper parsing logic, rejecting the input
before multiplication and addition would wrap. Preserve the existing zero-value
and invalid-input checks, and apply the same validation consistently to both
loops.

In `@src/AuctionHouseBot.cpp`:
- Around line 1044-1134: The order flow in AuctionHouseBot.cpp currently stores
user-facing Chinese text directly in OrderResult::error; replace each
result.error literal in the shown validation and setup paths with a stable
OrderResult error enum. In AuctionHouseBotPlayerScript.cpp lines 62-89, add a
single catalog that maps those enum values and success cases to localized text
selected by GetSessionDbLocaleIndex(), and use it for all whisper responses
across both affected files.
- Around line 1062-1066: Update the auction price resolution near the logic
selecting BuyPrice or SellPrice based on config->UseBuyPriceForSeller: if the
selected field is zero, fall back to the other price field, then reject the
order when the resolved buyoutPrice remains zero. Ensure bidPrice is derived
only after this validation so auctions cannot be created with both startbid and
buyout set to zero.

In `@src/AuctionHouseBotPlayerScript.cpp`:
- Around line 47-57: Update the order flow around SellOrderedItem to select the
seller bot deterministically instead of using gBots.begin(), whose pointer
ordering is arbitrary. Resolve the bot by its configured GUID, or pass the
requested faction into the order so SellOrderedItem always uses the intended
auction house.
- Line 43: Update the non-matching clerk-whisper logging in
AuctionHouseBotPlayerScript to stop including the full message body. Keep the
log at INFO level, but report only the sender GUID and the parse-failure
context.

In `@src/AuctionHouseBotWorldScript.cpp`:
- Around line 165-193: Update the missing-character LOG_ERROR in the whisper
clerk login flow to state that gWhisperOrdersReceiver does not correspond to an
existing character GUID. Rework the one-shot _whisperLoginAttempted guard so the
clerk login check can run again after AddPlayerBot fails or the clerk later
disconnects, preserving the requirement that the clerk remains online.
- Around line 5-10: Guard the mod-playerbots-specific include and the
sRandomPlayerbotMgr.AddPlayerBot(...) call in AuctionHouseBotWorldScript using
the project’s build macro, and update README.md and module metadata to declare
mod-playerbots as a dependency so builds without it remain supported.

---

Nitpick comments:
In `@conf/mod_ahbot.conf.dist`:
- Around line 123-125: Default AuctionHouseBot.WhisperOrders to 0 in the
distributed configuration and update its documentation entry to match. In
AHBot_WorldScript::OnBeforeConfigLoad, change the GetOption fallback for
WhisperOrders from true to false so it aligns with the false-initialized global
and opt-in module defaults.

In `@src/AuctionHouseBot.cpp`:
- Around line 1136-1193: Extract the duplicated item creation, pricing, deposit,
and auction persistence flow from Sell and the shown listing loop into a shared
private helper that accepts config, prototype, stackCount, and AHBplayer and
returns the created AuctionEntry. Update both Sell and the loop to use this
helper, preserving each operation’s transaction and result-accounting behavior
while centralizing price fallback logic.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fb92444c-4f45-4567-a4bb-3e4e8ebbe8cf

📥 Commits

Reviewing files that changed from the base of the PR and between a680cc1 and e0bc157.

📒 Files selected for processing (10)
  • conf/mod_ahbot.conf.dist
  • src/AHBotWhisperOrderParse.h
  • src/AuctionHouseBot.cpp
  • src/AuctionHouseBot.h
  • src/AuctionHouseBotCommon.cpp
  • src/AuctionHouseBotCommon.h
  • src/AuctionHouseBotPlayerScript.cpp
  • src/AuctionHouseBotScript.cpp
  • src/AuctionHouseBotWorldScript.cpp
  • src/AuctionHouseBotWorldScript.h

Comment on lines +18 to +53
uint32_t itemId = 0;
while (pos < msg.size() && std::isdigit(static_cast<unsigned char>(msg[pos])))
{
itemId = itemId * 10u + static_cast<uint32_t>(msg[pos] - '0');
++pos;
}
if (itemId == 0)
return false;

// Link form: |Hitem:...|h[Name]|h
std::size_t firstH = msg.find("|h", pos);
if (firstH == std::string::npos)
return false;
std::size_t secondH = msg.find("|h", firstH + 2);
if (secondH == std::string::npos)
return false;

pos = secondH + 2;
// Client links often end with |r before the quantity.
if (pos + 1 < msg.size() && msg[pos] == '|' && msg[pos + 1] == 'r')
pos += 2;

while (pos < msg.size() && std::isspace(static_cast<unsigned char>(msg[pos])))
++pos;

if (pos >= msg.size() || !std::isdigit(static_cast<unsigned char>(msg[pos])))
return false;

uint32_t quantity = 0;
while (pos < msg.size() && std::isdigit(static_cast<unsigned char>(msg[pos])))
{
quantity = quantity * 10u + static_cast<uint32_t>(msg[pos] - '0');
++pos;
}
if (quantity == 0)
return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard the digit accumulation against unsigned wraparound.

Both loops accumulate into uint32_t without a bound. A whisper that contains a long digit run wraps the value silently. Example: a quantity of 4294967297 becomes 1, so the caller lists a different amount than the sender requested. The same wrap applies to the item id.

Add an overflow check in each loop.

🛡️ Proposed fix
     uint32_t itemId = 0;
     while (pos < msg.size() && std::isdigit(static_cast<unsigned char>(msg[pos])))
     {
+        if (itemId > (UINT32_MAX - static_cast<uint32_t>(msg[pos] - '0')) / 10u)
+            return false;
         itemId = itemId * 10u + static_cast<uint32_t>(msg[pos] - '0');
         ++pos;
     }

Apply the same guard to the quantity loop. Include <cstdint> is already present.

🤖 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 `@src/AHBotWhisperOrderParse.h` around lines 18 - 53, Add uint32_t overflow
guards to the digit-accumulation loops for itemId and quantity in the whisper
parsing logic, rejecting the input before multiplication and addition would
wrap. Preserve the existing zero-value and invalid-input checks, and apply the
same validation consistently to both loops.

Comment thread src/AuctionHouseBot.cpp
Comment on lines +1044 to +1134
ItemTemplate const* prototype = sObjectMgr->GetItemTemplate(itemId);
if (!prototype)
{
result.error = "物品不存在";
return result;
}
result.itemName = prototype->Name1;

if (prototype->Bonding == BIND_WHEN_PICKED_UP)
{
result.error = "拾取绑定物品不能上架";
return result;
}
if (prototype->Bonding == BIND_QUEST_ITEM)
{
result.error = "任务物品不能上架";
return result;
}
if (prototype->BuyPrice == 0 && prototype->SellPrice == 0)
{
result.error = "物品没有价格,无法上架";
return result;
}
if (prototype->Quality > AHB_MAX_QUALITY)
{
result.error = "物品品质不受支持";
return result;
}

uint32 maxStack = prototype->GetMaxStackSize();
if (maxStack == 0)
maxStack = 1;

if (quantity == 0)
{
result.error = "数量无效";
return result;
}
if (quantity > maxStack * 20u)
{
result.error = Acore::StringFormat("数量过大(上限 {})", maxStack * 20u);
return result;
}

// Pick AH config: neutral-only when two-side AH is on; otherwise seller race.
// Do NOT use a temp Player::GetTeamId() after Initialize-only — that does not
// load race and was listing Alliance sellers onto the Horde AH (house 6).
AHBConfig* config = gNeutralConfig;
if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
{
TeamId team = TEAM_NEUTRAL;
if (CharacterCacheEntry const* info = sCharacterCache->GetCharacterCacheByGuid(
ObjectGuid::Create<HighGuid::Player>(_id)))
team = Player::TeamIdForRace(info->Race);

if (team == TEAM_ALLIANCE)
config = gAllianceConfig;
else if (team == TEAM_HORDE)
config = gHordeConfig;
else
{
result.error = "无法确定拍卖行阵营";
return result;
}
}

if (!config)
{
result.error = "拍卖行未就绪";
return result;
}

AuctionHouseEntry const* ahEntry = sAuctionMgr->GetAuctionHouseEntryFromFactionTemplate(config->GetAHFID());
AuctionHouseObject* auctionHouse = sAuctionMgr->GetAuctionsMap(config->GetAHFID());
if (!ahEntry || !auctionHouse)
{
result.error = "拍卖行未就绪";
return result;
}

std::string accountName = "AuctionHouseBot" + std::to_string(_account);
WorldSession session(_account, std::move(accountName), 0, nullptr, SEC_PLAYER,
sWorld->getIntConfig(CONFIG_EXPANSION), 0, LOCALE_enUS, 0, false, false, 0);
Player tempPlayer(&session);
bool addedToAccessor = false;
Player* AHBplayer = AcquireAHBplayer(tempPlayer, addedToAccessor);
if (!AHBplayer)
{
result.error = "拍卖机器人卖家在线冲突,无法上架";
return result;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Centralize the user-facing whisper-order messages. Both files embed Chinese response text directly in code, while the module configuration and logs are English. The shared root cause is the absence of a message catalog and a locale mapping layer.

  • src/AuctionHouseBot.cpp#L1044-L1134: replace the result.error string literals with a stable error enum on OrderResult.
  • src/AuctionHouseBotPlayerScript.cpp#L62-L89: map the error enum and the success cases to text through a single catalog, keyed by GetSessionDbLocaleIndex().
📍 Affects 2 files
  • src/AuctionHouseBot.cpp#L1044-L1134 (this comment)
  • src/AuctionHouseBotPlayerScript.cpp#L62-L89
🤖 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 `@src/AuctionHouseBot.cpp` around lines 1044 - 1134, The order flow in
AuctionHouseBot.cpp currently stores user-facing Chinese text directly in
OrderResult::error; replace each result.error literal in the shown validation
and setup paths with a stable OrderResult error enum. In
AuctionHouseBotPlayerScript.cpp lines 62-89, add a single catalog that maps
those enum values and success cases to localized text selected by
GetSessionDbLocaleIndex(), and use it for all whisper responses across both
affected files.

Comment thread src/AuctionHouseBot.cpp
Comment on lines +1062 to +1066
if (prototype->BuyPrice == 0 && prototype->SellPrice == 0)
{
result.error = "物品没有价格,无法上架";
return result;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Prevent auctions with a zero start bid and zero buyout.

The validation at Lines 1062-1066 passes when only one of BuyPrice and SellPrice is non-zero. At Line 1156 the code selects a single field based on config->UseBuyPriceForSeller. If that selected field is 0 and the other one is non-zero, buyoutPrice stays 0. bidPrice then also becomes 0, and the auction is listed with startbid = 0 and buyout = 0. A player can take the stack for free.

Add a fallback to the other price field, and reject the order when the resolved price is 0.

🐛 Proposed fix
         uint64 buyoutPrice = config->GetItemPrice(itemId);
         if (buyoutPrice == 0)
             buyoutPrice = config->UseBuyPriceForSeller ? prototype->BuyPrice : prototype->SellPrice;
+        if (buyoutPrice == 0)
+            buyoutPrice = config->UseBuyPriceForSeller ? prototype->SellPrice : prototype->BuyPrice;
 
         buyoutPrice = buyoutPrice * urand(config->GetMinPrice(prototype->Quality), config->GetMaxPrice(prototype->Quality));
         buyoutPrice = buyoutPrice / 100;
         uint64 bidPrice = buyoutPrice * urand(config->GetMinBidPrice(prototype->Quality), config->GetMaxBidPrice(prototype->Quality));
         bidPrice = bidPrice / 100;
+        if (bidPrice == 0)
+            bidPrice = 1;

Also applies to: 1154-1161

🤖 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 `@src/AuctionHouseBot.cpp` around lines 1062 - 1066, Update the auction price
resolution near the logic selecting BuyPrice or SellPrice based on
config->UseBuyPriceForSeller: if the selected field is zero, fall back to the
other price field, then reject the order when the resolved buyoutPrice remains
zero. Ensure bidPrice is derived only after this validation so auctions cannot
be created with both startbid and buyout set to zero.

uint32_t quantity = 0;
if (!AHBotParseWhisperOrder(msg, itemId, quantity))
{
LOG_INFO("module", "AHBot: whisper to clerk not an order (need item-link + quantity). msg='{}'", msg);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Do not log the whisper body.

This line writes the full private message text to the server log at INFO level for every non-matching whisper sent to the clerk. Private chat content is user data. Log only the sender GUID and the parse failure.

🔒 Proposed fix
-            LOG_INFO("module", "AHBot: whisper to clerk not an order (need item-link + quantity). msg='{}'", msg);
+            LOG_DEBUG("module", "AHBot: whisper to clerk from account {} is not an order (need item-link + quantity)",
+                player->GetSession()->GetAccountId());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LOG_INFO("module", "AHBot: whisper to clerk not an order (need item-link + quantity). msg='{}'", msg);
LOG_DEBUG("module", "AHBot: whisper to clerk from account {} is not an order (need item-link + quantity)",
player->GetSession()->GetAccountId());
🤖 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 `@src/AuctionHouseBotPlayerScript.cpp` at line 43, Update the non-matching
clerk-whisper logging in AuctionHouseBotPlayerScript to stop including the full
message body. Keep the log at INFO level, but report only the sender GUID and
the parse-failure context.

Comment on lines +47 to +57
if (gBots.empty())
{
ChatHandler handler(player->GetSession());
handler.PSendSysMessage("上架失败:拍卖机器人未就绪");
return false;
}

LOG_INFO("module", "AHBot: whisper order from {} item {} qty {}", player->GetName(), itemId, quantity);

// Listing is always performed by the AH seller bot(s), never by the clerk.
AuctionHouseBot::OrderResult result = (*gBots.begin())->SellOrderedItem(itemId, quantity);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Select the seller bot deterministically.

gBots is a std::set<AuctionHouseBot*>, so it is ordered by pointer value. *gBots.begin() therefore returns an arbitrary bot. SellOrderedItem derives the auction house faction from that bot's own character race. With more than one configured bot, the same order lists on the Alliance house in one run and on the Horde house in another. This contradicts the stated faction-selection fix.

Select the bot by a configured GUID, or accept a faction argument in the order.

🤖 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 `@src/AuctionHouseBotPlayerScript.cpp` around lines 47 - 57, Update the order
flow around SellOrderedItem to select the seller bot deterministically instead
of using gBots.begin(), whose pointer ordering is arbitrary. Resolve the bot by
its configured GUID, or pass the requested faction into the order so
SellOrderedItem always uses the intended auction house.

Comment on lines +5 to +10
#include "CharacterCache.h"
#include "Config.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "ObjectGuid.h"
#include "RandomPlayerbotMgr.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check whether the repository declares a playerbots dependency and how it is guarded.
fd -H -t f 'CMakeLists.txt|README.md|module.json|\.gitmodules' | while IFS= read -r f; do
  echo "=== $f ==="; cat -n "$f"
done
rg -n 'RandomPlayerbotMgr|MOD_PLAYERBOTS|playerbot' -g '!*.md' .

Repository: azerothcore/mod-ah-bot

Length of output: 3854


🏁 Script executed:

#!/bin/bash
set -u

echo "=== tracked module metadata/files ==="
git ls-files | rg '(^|/)(CMakeLists\.txt|README\.md|module\.json|\.gitmodules|.*\.(cmake|dist)$|.*\.h$|.*\.cpp$)' | sed -n '1,200p'

echo
echo "=== source outline ==="
ast-grep outline src/AuctionHouseBotWorldScript.cpp --view expanded || true

echo
echo "=== relevant source slices ==="
cat -n src/AuctionHouseBotWorldScript.cpp | sed -n '1,35p;140,210p'

echo
echo "=== build config references ==="
for f in $(git ls-files | rg '(^|/)(CMakeLists\.txt|.*\.cmake)$'); do
  echo "--- $f ---"
  rg -n 'playerbot|Playerbot|PLAYERBOT|modules|MOD_|add_library|add_subdirectory|OptionalModule|mod_ahbot|mod-ahbot' "$f" || true
done

Repository: azerothcore/mod-ah-bot

Length of output: 4915


🌐 Web query:

RandomPlayerbotMgr.h sRandomPlayerbotMgr AddPlayerBot ObjectGuid uint32 mod-playerbots

💡 Result:

In the mod-playerbots project, AddPlayerBot is a method inherited from the PlayerbotHolder class, which serves as the base for both PlayerbotMgr and RandomPlayerbotMgr [1][2]. The signature for the method is [1]: void AddPlayerBot(ObjectGuid guid, uint32 masterAccountId); Usage and Functionality: 1. Scope: AddPlayerBot is defined in PlayerbotHolder.h and implemented in PlayerbotMgr.cpp [1][3]. It is used to initiate the login process for a bot [2]. 2. Parameters: - ObjectGuid guid: The unique identifier for the character to be added as a bot [1][3]. - uint32 masterAccountId: The account ID of the master player controlling the bot [1][3]. If this is 0, the system treats it as a "Random Bot" (Type 1), managed by the server [2]. 3. Security Checks: Before a bot is added, the method performs several validations to ensure the master player is permitted to control the specific character [3][2]. These checks include verifying guild membership, account linking, or whether the character is configured as a permitted bot class [3][2]. 4. RandomPlayerbotMgr Implementation: In RandomPlayerbotMgr.cpp, this method is called within the ProcessBot function to log in random bots that are not currently in the world [4][5]. The macro sRandomPlayerbotMgr provides global access to the RandomPlayerbotMgr instance, which manages these server-controlled random bots [6][2].

Citations:


Document and guard the mod-playerbots dependency.

include "RandomPlayerbotMgr.h" and sRandomPlayerbotMgr.AddPlayerBot(...) require mod-playerbots, but README.md and the module metadata do not declare it. Add the dependency to the installation/config, or surround the include and call with a build macro so mod-ahbot does not fail on builds without RandomPlayerbotMgr.h.

🤖 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 `@src/AuctionHouseBotWorldScript.cpp` around lines 5 - 10, Guard the
mod-playerbots-specific include and the sRandomPlayerbotMgr.AddPlayerBot(...)
call in AuctionHouseBotWorldScript using the project’s build macro, and update
README.md and module metadata to declare mod-playerbots as a dependency so
builds without it remain supported.

Comment on lines +165 to +193
_whisperLoginAttempted = true;

// Never log in AuctionHouseBot.GUID (seller). That character must stay offline
// so classic temp-Player Update()/Sell()/Buy() keep running. Login a separate
// clerk character that only receives whisper orders.
if (!gWhisperOrdersReceiver)
{
LOG_ERROR("module", "AHBot: WhisperOrders enabled but WhisperOrdersReceiverGUID is 0");
return;
}

if (gBotsId.find(gWhisperOrdersReceiver) != gBotsId.end())
{
LOG_ERROR("module", "AHBot: WhisperOrdersReceiverGUID {} must not be an AH seller GUID", gWhisperOrdersReceiver);
return;
}

ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(gWhisperOrdersReceiver);
if (ObjectAccessor::FindConnectedPlayer(guid))
return;

if (!sCharacterCache->GetCharacterAccountIdByGuid(guid))
{
LOG_ERROR("module", "AHBot: failed to keep clerk character {} online for WhisperOrders; whispers will not work until it is logged in", gWhisperOrdersReceiver);
return;
}

LOG_INFO("module", "AHBot: WhisperOrders login clerk character {}", gWhisperOrdersReceiver);
sRandomPlayerbotMgr.AddPlayerBot(guid, 0);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Correct the error text and reconsider the one-shot login attempt.

Two points in this block:

  1. Line 186 checks character existence through GetCharacterAccountIdByGuid. The message at Line 188 states that the module "failed to keep clerk character online". The real cause is a missing character for that GUID. Change the text to state that the GUID does not exist.
  2. _whisperLoginAttempted is set at Line 165 before the login call. If AddPlayerBot fails, or if the clerk logs out later, the module never retries. The comment at Lines 167-169 promises a clerk that stays online. If a periodic re-check is intended, reset the flag or re-run the check on an interval instead of once.
🔧 Proposed change for the message
     if (!sCharacterCache->GetCharacterAccountIdByGuid(guid))
     {
-        LOG_ERROR("module", "AHBot: failed to keep clerk character {} online for WhisperOrders; whispers will not work until it is logged in", gWhisperOrdersReceiver);
+        LOG_ERROR("module", "AHBot: WhisperOrdersReceiverGUID {} does not exist; whisper orders are disabled", gWhisperOrdersReceiver);
         return;
     }
🤖 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 `@src/AuctionHouseBotWorldScript.cpp` around lines 165 - 193, Update the
missing-character LOG_ERROR in the whisper clerk login flow to state that
gWhisperOrdersReceiver does not correspond to an existing character GUID. Rework
the one-shot _whisperLoginAttempted guard so the clerk login check can run again
after AddPlayerBot fails or the clerk later disconnects, preserving the
requirement that the clerk remains online.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant