ResourcesLib v2¶
Async economy engine — balances, inventories, transfers, passive growth, and multi-resource crafting.
File: Libs/ResourcesLibv2.js · Access: Libs.ResourcesLibv2.* · Async — always await · v1.0.0
What problem does it solve?¶
Telegram bots commonly track:
- User gold, gems, energy
- Chat-wide points or pool balances
- Global bot treasury
- Passive income (mining, interest, regeneration)
ResourcesLibv2 wraps all of this with atomic db.bot.incr / decr, batch mget, and optional growth timers that accrue value over time.
Storage: db.bot with keys ResourcesLib_{scope}_{id}_{name} — same format as v1, so existing balances can stay if you migrate from Bot.getProperty.
Resource scopes¶
| Factory | Scope | Example key |
|---|---|---|
userRes(name) | Current user | ResourcesLib_user_12345_gold |
chatRes(name) | Current chat | ResourcesLib_chat_67890_points |
globalRes(name) | Whole bot | ResourcesLib_global_global_pool |
anotherUserRes(name, telegramId) | Specific user | ResourcesLib_user_99999_gold |
anotherChatRes(name, chatId) | Specific chat | ResourcesLib_chat_11111_points |
let gold = Libs.ResourcesLibv2.userRes("gold")
let energy = Libs.ResourcesLibv2.userRes("energy")
let pool = Libs.ResourcesLibv2.globalRes("jackpot")
User ID comes from user.telegramid; chat ID from chat.chatid.
Reading values¶
| Method | Writes db? | Description |
|---|---|---|
value() | Yes* | Current amount including applied growth |
peek() | No | Raw stored number — fast |
preview() | No | What value would be after growth — no write |
stats() | No | Dashboard bundle (base, current, pending, growth info) |
* value() applies pending growth ticks and may write updated balance + growth state.
let gold = Libs.ResourcesLibv2.userRes("gold")
await gold.add(100)
Bot.sendMessage("Balance: " + await gold.value())
// Shop UI — show pending mining income without committing
let pending = (await gold.stats()).pending
if (pending > 0) {
Bot.sendMessage("+" + pending + " gold ready to collect (open /wallet)")
}
Changing values¶
| Method | Throws? | Description |
|---|---|---|
add(amount) | On bad type | Add to balance |
set(amount) | On bad type | Set balance (updates growth base if enabled) |
remove(amount) | If insufficient | Subtract — throws 'ResLib: not enough resources' |
removeAnyway(amount) | No | Subtract even below zero |
have(amount) | No | true if balance ≥ amount |
tryRemove(amount) | No | { ok, removed, balance } |
spend(amount) | No | Shorthand — returns true if deducted |
reset() | No | Set to 0 |
ensureAtLeast(min) | No | Raise to min if below |
fillTo(target) | No | Add only until target reached |
setClamped(amount, min, max) | No | Set with bounds |
let gold = Libs.ResourcesLibv2.userRes("gold")
// Safe shop purchase
let buy = await gold.tryRemove(50)
if (!buy.ok) {
return Bot.sendMessage("Need 50 gold, you have " + buy.balance)
}
Bot.sendMessage("Purchased! " + buy.balance + " gold left.")
// Or one-liner
if (!(await gold.spend(50))) {
return Bot.sendMessage("Not enough gold.")
}
Transfers and exchanges¶
| Method | Description |
|---|---|
transferTo(other, amount) | Move same resource type — checks balance |
transferToAnyway(other, amount) | Move without balance check |
takeFromAnother(other, amount) | Pull from another into this |
exchangeTo(other, { remove_amount, add_amount }) | Trade rates (e.g. 10 gold → 1 gem) |
let gold = Libs.ResourcesLibv2.userRes("gold")
let gems = Libs.ResourcesLibv2.userRes("gems")
// 100 gold → 1 gem
await gold.exchangeTo(gems, { remove_amount: 100, add_amount: 1 })
Transfers between resources require same resource name on different scopes, or use exchangeTo for different names.
Display¶
await gold.format() // "1250.5"
await gold.format({ suffix: " gold" }) // "1250.5 gold"
await gold.format({ compact: true }) // "1.3K"
Growth (passive income)¶
Access via Libs.ResourcesLibv2.growthFor(resource) or resource.growth.
Growth types¶
| Method | Type | Formula |
|---|---|---|
growth.add({ value, interval, min, max }) | simple | +value every interval seconds |
growth.addPercent({ percent, interval }) | percent | +percent% of base per interval |
growth.addCompoundInterest({ percent, interval }) | compound_interest | Compound % per interval |
let gold = Libs.ResourcesLibv2.userRes("gold")
let g = Libs.ResourcesLibv2.growthFor(gold)
// Mine 1 gold every 60 seconds
await g.add({ value: 1, interval: 60 })
// Or 5% of base every hour
await g.addPercent({ percent: 5, interval: 3600 })
Growth control¶
| Method | Description |
|---|---|
growth.stop() | Pause accrual |
growth.resume() | Re-enable |
growth.restart() | Reset timer, keep config |
growth.progress() | % to next tick |
growth.nextTickIn() | Seconds until next tick |
growth.previewGain() | How much would accrue now (no write) |
let stats = await gold.stats()
if (stats.growth) {
Bot.sendMessage( "Mining: " + Math.round(stats.growth.progress) + "% — next tick in " +
Math.round(stats.growth.nextTickIn) + "s"
)
}
Module helpers¶
loadAll(resources, { withGrowth })¶
Batch-read multiple resources in one mget:
let gold = Libs.ResourcesLibv2.userRes("gold")
let gems = Libs.ResourcesLibv2.userRes("gems")
let energy = Libs.ResourcesLibv2.userRes("energy")
let balances = await Libs.ResourcesLibv2.loadAll([gold, gems, energy], { withGrowth: true })
// { gold: 150, gems: 3, energy: 80 }
spendAll(requirements)¶
Atomic multi-resource check — verifies all first, then deducts in parallel:
let gold = Libs.ResourcesLibv2.userRes("gold")
let wood = Libs.ResourcesLibv2.userRes("wood")
let iron = Libs.ResourcesLibv2.userRes("iron")
let craft = await Libs.ResourcesLibv2.spendAll([
{ res: gold, amount: 50 },
{ res: wood, amount: 10 },
{ res: iron, amount: 5 }
])
if (!craft.ok) {
return Bot.sendMessage( "Need more " + craft.missing + " (have " + craft.have + ", need " + craft.need + ")"
)
}
Bot.sendMessage("Item crafted!")
clearCache()¶
Clears per-command memory cache — call if you need fresh reads after external changes in the same command.
Full example — RPG wallet¶
// /wallet
let gold = Libs.ResourcesLibv2.userRes("gold")
let energy = Libs.ResourcesLibv2.userRes("energy")
let data = await Libs.ResourcesLibv2.loadAll([gold, energy], { withGrowth: true })
let goldStats = await gold.stats()
let msg = [
"Gold: " + await gold.format({ suffix: " G", compact: true }),
"Energy: " + data.energy + " / 100"
]
if (goldStats.pending > 0) {
msg.push("(+" + goldStats.pending + " gold from mining)")
}
Bot.sendMessage(msg.join("\n"))
Full example — setup passive mining once¶
// /enable_mining (one-time setup per user)
let gold = Libs.ResourcesLibv2.userRes("gold")
let g = Libs.ResourcesLibv2.growthFor(gold)
if (await g.have()) {
return Bot.sendMessage("Mining already enabled.")
}
await g.add({ value: 1, interval: 120, max: 10000 })
Bot.sendMessage("Mining enabled! +1 gold every 2 minutes (cap 10,000).")
Migration from v1 (Libs.ResourcesLib)¶
| v1 (sync, deprecated) | v2 (async) |
|---|---|
Libs.ResourcesLib.userRes("gold") | Libs.ResourcesLibv2.userRes("gold") |
gold.add(10) | await gold.add(10) |
gold.value() | await gold.value() |
Bot.getProperty / Bot.setProperty | db.bot |
Keys stay ResourcesLib_* — balances may already exist in db.bot if migrated.
See ResourcesLib.md for legacy sync API.
Common mistakes¶
// Wrong — forget await (gets Promise, not number)
Bot.sendMessage("Gold: " + gold.value())
// Correct
Bot.sendMessage("Gold: " + await gold.value())
// Wrong — remove() without try/catch (throws on insufficient)
await gold.remove(999)
// Correct — use tryRemove or spend
let r = await gold.tryRemove(999)
if (!r.ok) Bot.sendMessage("Not enough.")
Notes¶
- All amounts must be numbers — non-numbers throw with a clear
ResLib:message. - Growth state stored at
{propName}_growthas object ondb.bot. - Uses in-memory cache per command —
clearCache()if needed. incr/decrthrow on storage errors;setchecks{ ok }.