From 28b9fd1aee37233b5fe6faca91e68adc036e7e05 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 21:28:18 +0000 Subject: [PATCH] Filter several service accounts, not just Playerbots The auction-house bot account (ahouse) owns 9 mule characters that were showing up in the armory alongside real players. One LIKE pattern could not cover both it and RNDBOT%. Replaces botAccountPattern with botAccountPatterns, a comma-separated list of LIKE patterns, and builds the clause with Utils.botAccountFilter. An empty list now disables the filter outright rather than emitting a clause that matches nothing, so misconfiguration fails open instead of hiding everyone. The stack default becomes "RNDBOT%,ahouse"; config.default.json keeps the generic "RNDBOT%" so the fork stays upstreamable. Verified against the live realm: 29 listed before, 20 after. The 9 excluded are the ahouse mules. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NW2ooBP2KPqQVzdZZDMvZd --- config.default.json | 2 +- docker-compose.mythica.yml | 5 ++++- docs/mythica-deploy.md | 8 +++++--- src/armory/Config.ts | 2 +- src/armory/Utils.ts | 20 +++++++++++++++++++ src/armory/controllers/CharacterController.ts | 10 +++++++--- src/armory/controllers/GuildController.ts | 7 +++++-- src/armory/controllers/IndexController.ts | 7 +++++-- 8 files changed, 48 insertions(+), 13 deletions(-) diff --git a/config.default.json b/config.default.json index 2860cad..70f9fcb 100644 --- a/config.default.json +++ b/config.default.json @@ -10,7 +10,7 @@ "loadDbcs": true, "hideGameMasters": true, "hideBotAccounts": true, - "botAccountPattern": "RNDBOT%", + "botAccountPatterns": "RNDBOT%", "transmogModule": false, "useZamCdn": false, "realms": [ diff --git a/docker-compose.mythica.yml b/docker-compose.mythica.yml index c1cbb8c..4153f7e 100644 --- a/docker-compose.mythica.yml +++ b/docker-compose.mythica.yml @@ -48,8 +48,11 @@ services: # Playerbots are ordinary accounts, not game masters, so the GM # filter does not touch them. Without this the armory lists ~1350 # bots against ~29 real characters. + # Comma-separated LIKE patterns. RNDBOT% covers the Playerbots + # accounts; ahouse is the auction-house bot, which owns 9 mule + # characters. Add further service accounts here as they appear. ACORE_ARMORY_HIDE_BOT_ACCOUNTS: ${ARMORY_HIDE_BOTS:-1} - ACORE_ARMORY_BOT_ACCOUNT_PATTERN: ${ARMORY_BOT_PATTERN:-RNDBOT%} + ACORE_ARMORY_BOT_ACCOUNT_PATTERNS: ${ARMORY_BOT_PATTERNS:-RNDBOT%,ahouse} ACORE_ARMORY_TRANSMOG_MODULE: ${ARMORY_TRANSMOG:-0} # 0 = serve 3D model assets from the local data directory. diff --git a/docs/mythica-deploy.md b/docs/mythica-deploy.md index d146f1a..2b67642 100644 --- a/docs/mythica-deploy.md +++ b/docs/mythica-deploy.md @@ -133,6 +133,8 @@ FLUSH PRIVILEGES; this realm belong to real players — `Spinnaker` among them — so hiding them would hide people who should be listed. Set `ARMORY_HIDE_GMS=1` to restore upstream behaviour. -- **Playerbots are hidden** via `hideBotAccounts` / `botAccountPattern` - (`RNDBOT%`). They are ordinary accounts rather than game masters, so the GM - filter never touched them. `ARMORY_HIDE_BOTS=0` shows them again. +- **Service accounts are hidden** via `hideBotAccounts` / `botAccountPatterns`, + a comma-separated list of LIKE patterns defaulting to `RNDBOT%,ahouse` — the + Playerbots accounts and the auction-house bot, which owns 9 mule characters. + None of them are game masters, so the GM filter never touched them. Add more + with `ARMORY_BOT_PATTERNS`, or set `ARMORY_HIDE_BOTS=0` to show them all. diff --git a/src/armory/Config.ts b/src/armory/Config.ts index 4bbafe2..fc0bc80 100644 --- a/src/armory/Config.ts +++ b/src/armory/Config.ts @@ -32,7 +32,7 @@ export class Config { public loadDbcs: boolean; public hideGameMasters: boolean; public hideBotAccounts: boolean; - public botAccountPattern: string; + public botAccountPatterns: string; public transmogModule: boolean; public useZamCdn: boolean; public realms: IRealmConfig[]; diff --git a/src/armory/Utils.ts b/src/armory/Utils.ts index 67f92dd..6e0a062 100644 --- a/src/armory/Utils.ts +++ b/src/armory/Utils.ts @@ -73,4 +73,24 @@ export class Utils { public static quoteSqlString(value: string): string { return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`; } + + /** + * Build the LIKE test that identifies non-player accounts. + * + * `patterns` is a comma-separated list, so a realm can name several service + * accounts at once — Playerbots plus an auction-house bot, say. Returns an + * empty string when nothing is configured, which callers treat as "do not + * filter" rather than emitting an always-false clause. + */ + public static botAccountFilter(column: string, patterns: string): string { + const list = String(patterns ?? "") + .split(",") + .map((p) => p.trim()) + .filter((p) => p.length > 0); + + if (list.length === 0) { + return ""; + } + return `(${list.map((p) => `${column} LIKE ${Utils.quoteSqlString(p)}`).join(" OR ")})`; + } } diff --git a/src/armory/controllers/CharacterController.ts b/src/armory/controllers/CharacterController.ts index 4ff44eb..1623502 100644 --- a/src/armory/controllers/CharacterController.ts +++ b/src/armory/controllers/CharacterController.ts @@ -347,10 +347,14 @@ export class CharacterController { // rows we do not want, then require the join to have missed. Keeping the // pattern inline rather than bound avoids disturbing the positional // values below. - const botJoin = this.armory.config.hideBotAccounts - ? `LEFT JOIN \`${realm.authDatabase}\`.\`account\` AS \`bot_account\` ON \`bot_account\`.\`id\` = \`characters\`.\`account\` AND \`bot_account\`.\`username\` LIKE ${Utils.quoteSqlString(this.armory.config.botAccountPattern)}` + const botFilter = this.armory.config.hideBotAccounts + ? Utils.botAccountFilter("`bot_account`.`username`", this.armory.config.botAccountPatterns) : ""; - const botWhere = this.armory.config.hideBotAccounts ? "AND `bot_account`.`id` IS NULL" : ""; + const botJoin = + botFilter === "" + ? "" + : `LEFT JOIN \`${realm.authDatabase}\`.\`account\` AS \`bot_account\` ON \`bot_account\`.\`id\` = \`characters\`.\`account\` AND ${botFilter}`; + const botWhere = botFilter === "" ? "" : "AND `bot_account`.`id` IS NULL"; const [rows] = await this.armory.getCharactersDb(realm.name).query({ sql: ` diff --git a/src/armory/controllers/GuildController.ts b/src/armory/controllers/GuildController.ts index 441cc6e..e7ca050 100644 --- a/src/armory/controllers/GuildController.ts +++ b/src/armory/controllers/GuildController.ts @@ -93,7 +93,10 @@ export class GuildController { ssp = ssp.where("`account_access`.`id` IS NULL"); } - if (this.armory.config.hideBotAccounts) { + const botFilter = this.armory.config.hideBotAccounts + ? Utils.botAccountFilter(`\`${realm.authDatabase}\`.\`account\`.\`username\``, this.armory.config.botAccountPatterns) + : ""; + if (botFilter !== "") { ssp.joins.push({ table1: "characters", column1: "account", @@ -101,7 +104,7 @@ export class GuildController { column2: "id", database2: realm.authDatabase, kind: "LEFT", - where: `AND \`${realm.authDatabase}\`.\`account\`.\`username\` LIKE ${Utils.quoteSqlString(this.armory.config.botAccountPattern)}`, + where: `AND ${botFilter}`, }); ssp = ssp.where(`\`${realm.authDatabase}\`.\`account\`.\`id\` IS NULL`); } diff --git a/src/armory/controllers/IndexController.ts b/src/armory/controllers/IndexController.ts index de776af..16c9052 100644 --- a/src/armory/controllers/IndexController.ts +++ b/src/armory/controllers/IndexController.ts @@ -55,7 +55,10 @@ export class IndexController { ssp = ssp.where("`account_access`.`id` IS NULL"); } - if (this.armory.config.hideBotAccounts) { + const botFilter = this.armory.config.hideBotAccounts + ? Utils.botAccountFilter(`\`${realm.authDatabase}\`.\`account\`.\`username\``, this.armory.config.botAccountPatterns) + : ""; + if (botFilter !== "") { ssp.joins.push({ table1: "characters", column1: "account", @@ -63,7 +66,7 @@ export class IndexController { column2: "id", database2: realm.authDatabase, kind: "LEFT", - where: `AND \`${realm.authDatabase}\`.\`account\`.\`username\` LIKE ${Utils.quoteSqlString(this.armory.config.botAccountPattern)}`, + where: `AND ${botFilter}`, }); ssp = ssp.where(`\`${realm.authDatabase}\`.\`account\`.\`id\` IS NULL`); } -- 2.49.1