Repository navigation
Addon authors
This page is for people writing their own addons. If you just play, you can skip it.
Goblinomics has a public API, so your addon can read wealth, bookings, crafting profit and farm sessions, react to events, or plug in its own price source. There's exactly one global, Goblinomics, and the API hangs off it:
local API = Goblinomics and Goblinomics.API and Goblinomics.API.v1On clients older than 12.1 Goblinomics switches itself off and the global doesn't exist, so always check for nil.
Everything on this page is API.v1, and v1 stays compatible for the whole 1.x line. Names, arguments and payload fields won't be removed or change meaning. New functions and new payload fields can appear. If I ever need to break something, it goes into a v2 next to v1. Anything not documented here (the addons' private namespaces, SavedVariables layout) is internal and can change in any release. Please don't read GoblinomicsLedgerDB directly; I will rename things in there.
Add ## OptionalDeps: Goblinomics to your TOC so Goblinomics loads before you.
API.On(event, handler, owner) -- handler(event, payload)
API.Off(event, owner)owner is required, a string like your addon's name. There's one handler per owner and event, so a second On with the same owner replaces the first. Treat payloads as read-only; they're shared with every other listener.
| Event | Payload | From |
|---|---|---|
MONEY_DELTA |
{ scope = "player" | "warband", amount, total, time, context, restricted } |
Core |
ITEMS_DELTA |
{ changes, time, context, restricted } |
Core |
LOOT_RECEIVED |
{ itemKey, link, quantity, source, time, context, restricted } |
Core |
CONTEXT_CHANGED |
{ key, active } |
Core |
RESTRICTED_ENTER / RESTRICTED_LEAVE
|
{ kind, time, ... } |
Core |
PRICES_CHANGED |
{}, debounced |
Core |
MODULE_STATE_CHANGED |
{ id, enabled } |
Core |
NETWORTH_UPDATED |
the wealth result: wealth, gold, auctions, items, speculative, chars, warband, remote
|
Vault |
VAULT_SPECULATIVE_UPDATED, VAULT_GOALS
|
Vault | |
LEDGER_TRANSACTION |
a booking (see below), plus merged or imported
|
Ledger |
LEDGER_TRANSACTION_ITEM |
a booking that got its item attached later | Ledger |
LEDGER_CHANGED |
{}, debounced |
Ledger |
LEDGER_AH_EVENT |
{ kind, itemKey, quantity, amount, net, unitPrice } |
Ledger |
GATHERER_SESSION |
{ action, session } |
Gatherer |
GATHERER_FARMS |
Gatherer | |
WORKSHOP_CRAFT, WORKSHOP_MATCH, WORKSHOP_ORDER
|
Workshop | |
IMPORT_DONE |
{ id, source, time, added, events, duplicates } |
Import |
Amounts are always copper. LEDGER_AH_EVENT.kind is one of post, sale, purchase, expired, cancelled; for sales net is what actually reached your pocket.
Core events are not sent during boss encounters, Mythic+ and rated PvP. What happened in there is replayed afterwards, with restricted set.
A booking has time, char ("Name-Realm"), category (AH, Vendor, Repair, Quest, Loot, Crafting, Mail, Other, Transfer), sub, amount (signed), and optionally itemKey, quantity, tag, note.
Module APIs only exist while that module is loaded, so check before calling.
| API | Functions |
|---|---|
API.Vault |
Current(), History(), Snapshot(label), SetRemote(snap), RemoveRemote(id), Remotes(), ItemCount(itemKey)
|
API.Ledger |
Query(filter), Aggregates(fromKey), Import(batch, dryRun), RemoveImport(source), AuctionStats(itemKey, days)
|
API.Workshop |
Breakdown(filter), Daily(days, filter)
|
API.Gatherer |
Summaries(from) |
Call these with a colon: API.Ledger:Query({ from = t, category = "AH" }). The same goes for API.Price and API.Value. API.Ledger:Query takes from (epoch seconds), char, category, tag and search, and returns bookings newest first. API.Vault:History() is keyed by "YYYY-MM-DD" with wealth, speculative, gold, chars and warband per day. API.Vault:ItemCount(itemKey) is how many of an item you own across all characters, bank and warband bank, bound copies left out. API.Ledger:AuctionStats(itemKey, days) sums up your own auctions of an item (posted, sold, expired, cancelled, revenue, depositLost, saleRate, averagePrice, lastSale), or returns nil if there are none; leave out days for all time.
A few helpers are useful on their own. API.KnownCharacters() lists every character of the account Goblinomics has seen. API.Money.Format(copper) formats copper the way Goblinomics does. API.ItemKey.FromLink(link) turns an item link into the key Goblinomics uses everywhere. Keys follow TSM's item string convention (i:2589, bonus IDs sorted after that, p: for caged pets), so you can pass them straight to TSM_API. API.Price:Get(itemKey, role) returns a price, API.Price:HasRole(role) tells you whether any source provides a role at all (only TSM has saleRate), API.Price:Query(itemKey, name) asks a source for a named price such as TSM's DBRecent (nil if no source knows the name), and API.Value:Evaluate(itemKey, { bound = false, quantity = 1 }) returns the valuation the Vault uses (unit, total, tier, rule).
-- MyAddon.toc: ## OptionalDeps: Goblinomics
local API = Goblinomics and Goblinomics.API and Goblinomics.API.v1
if not API then return end
local OWNER = "MyAddon"
API.On("NETWORTH_UPDATED", function(_, result)
print("Wealth: " .. API.Money.Format(result.wealth)
.. " (speculative: " .. API.Money.Format(result.speculative) .. ")")
end, OWNER)
API.On("LEDGER_AH_EVENT", function(_, e)
if e.kind == "sale" then
print(("Sold %d x %s, net %s"):format(e.quantity or 1, e.itemKey or "?", API.Money.Format(e.net or 0)))
end
end, OWNER)
-- /myah: auction house income of the last 7 days
SLASH_MYADDON_AH1 = "/myah"
SlashCmdList.MYADDON_AH = function()
if not API.Ledger then
print("The Goblinomics Ledger is switched off.")
return
end
local income = 0
for _, b in ipairs(API.Ledger:Query({ from = time() - 7 * 86400, category = "AH" })) do
if b.amount > 0 then income = income + b.amount end
end
print("AH income, 7 days: " .. API.Money.Format(income))
endGoblinomics asks price sources per role: market, destroy, saleRate (a fraction, 0.05 means 5 %) and vendor. For each role it walks through the sources and takes the first positive value. For market and destroy the source the player picked as preferred (TSM or Auctionator) comes first, then everything else by priority, lower first. TSM is 10, Auctionator 20, the built-in vendor source 100.
local API = Goblinomics and Goblinomics.API and Goblinomics.API.v1
if not API then return end
API.Price:RegisterSource({
id = "myprices",
name = "My Prices",
priority = 50,
roles = { market = true },
Get = function(self, itemKey, role)
local itemID = API.ItemKey.ToItemID(itemKey)
return itemID and MyPriceTable[itemID] or nil -- copper, or nil
end,
Status = function(self) -- optional
return { available = MyPriceTable ~= nil, note = nil }
end,
})Get is called as source:Get(itemKey, role). Return copper, or nil. If you're waiting for item data, return nil, "pending" and Goblinomics will say so instead of treating the item as priceless. Results are cached, so when your data changes, call API.Price:Invalidate() (or API.Price:Invalidate(itemKey) for one item). That also triggers PRICES_CHANGED, and the Vault recalculates. Errors inside Get are caught and reported, but please don't rely on that.
The source shows up in Settings > Prices under "Price sources", with the note from Status if you return one.
Goblinomics is under the Mozilla Public License 2.0. Your own addon that talks to Goblinomics through this API is your code, in your files, and you can release it under whatever licence you want. The MPL only applies to the files of Goblinomics itself: if you ship a modified copy of one of them, that file stays MPL and its source has to be available. Please don't call your addon "Goblinomics" though, something like "Goblinomics: Mount Tracker" or "MyAddon for Goblinomics" is fine.
If something here is unclear or you need a hook that doesn't exist, open an issue at https://github.com/xLN1995/goblinomics/issues. I'd rather add a proper API function than have addons poking into private tables.