Paste it raw, nothing silently dropped
Handles the header row, CRLF, quoted or unquoted cells and blank lines. Bad rows are reported, not swallowed.
The game log is the source of truth, so officers never type an amount — the only exception is an opening balance during migration. Everything is sorted ascending by timestamp before anything else happens. Rows that cannot be parsed are counted as "Rejected" in the preview rather than quietly disappearing, and any row whose reason is not Deposit or Withdrawal is reported but not imported — so an old bot’s Manual transfer bookkeeping, pasted by mistake, can never inflate someone’s personal balance. One paste is capped at 50,000 rows, comfortably above a real 4-week export.
Wrong-guild and gap warnings, before you import
A paste from the wrong guild, or one that starts after your last import, is flagged on the preview.
If a paste holds at least 5 players and more than 70% of them are unknown, the preview asks "is this the right guild’s log?". If its oldest row is newer than the watermark, it says so and explains that the window in between has aged out of the game and cannot be recovered by anyone. The gap is recorded on the import row itself, so the audit trail keeps it. Neither warning blocks the import — they inform a human who can see something the bot cannot.
Big roster? The pickers page and search
Both officer pickers page 25 at a time with ◀ ▶ and a 🔍 name filter that is safe to act on.
A Discord dropdown holds 25 options and has no type-to-filter, so a 120-player guild needs both. The 🔗 Players list and the reclassify picker each page 25 rows and carry a 🔍 Search modal filtering on in-game name; the button turns blue while a filter is on, and an empty submit clears it. The filter is part of the panel’s identity, not decoration: the reclassify handler re-reads the exact same filtered page to decide which rows flip back to personal, because an unfiltered re-read would silently reclassify rows the officer never saw. That case has its own named unit test. A typed % or _ stays a literal character.
Batch undo for a bad paste
🗑 Remove last import deletes that batch’s rows and recomputes the watermark. Re-paste to restore.
The entries table is a rebuildable replica of an external ledger, which is the reason deleting a whole import batch is allowed here and nowhere else in the bot. Removing the last import deletes only the rows belonging to that batch, recomputes the watermark from what remains, and redraws the console — and the confirmation tells you how many rows are going. Opening adjustments written by a migration are not part of any import batch and are never touched. Because the game log itself is unchanged, the fix for a mistaken removal is simply to paste the log again.
Period totals, and honest territory income
Start a period and "Guild spent this period" counts from it; territory income is worked out, not guessed.
Personal balances never reset — a debt does not expire at a season boundary — but the guild-flow figures are framed by a period started with Start new period and an optional label such as "Season 27". Guild spend is the sum of guild-scope withdrawals since that start. Territory income cannot come from the log, because passive generation never appears there, so it is computed: an officer records the guild’s total stored energy with 📊 Set current total (plus an optional separate bank figure), and between two readings the bot reports the change in total minus the net logged flows. With fewer than two readings in the period it prints "— (log the total twice)" rather than inventing a number.
Guild accounts, and the people who left
Mark a whole account 🏦 Guild account and its entire history leaves personal balances. 🙈 hides a leaver.
Some characters exist to move guild energy, so tagging their withdrawals one at a time is busywork. Marking a player 🏦 Guild account flips their default scope and re-scopes their whole history in one transaction, which also takes them off the console entirely — unmarking restores everything. Separately, 🙈 Hide (left guild) removes someone from the board, the full list and the DM list while keeping their history in the period figures. Both are on the player’s card in 🔗 Players, next to 🏷 Transactions, which opens the reclassify picker scoped to just that person.
One Discord, all your alts
A Discord account can own more than one in-game name, and ⚡ My balance sums across all of them.
Alts are normal in Albion, and the log speaks in character names. The registry lets one Discord account own several, so a member with two characters sees one number rather than doing the arithmetic themselves — and the card lists the names it added up. The console still lists each character on its own row, because the log does, and because accountability is per character in the game.
It never touches silver — except once, on purpose
Separate table, separate balance, separate board. One deliberate crossing, in the other direction.
Nothing in this feature reads or writes the silver ledger, the settlement math, seasons or regear. That was a constraint from the start: settlement is the most protected surface in the bot, and dragging a second currency into it was the rejected alternative. The single crossing point runs the other way — if energy tracking is on, a member who owes energy is refused a silver transfer to another member, so a tracked obligation cannot be emptied out into an untracked one.