LogoRobo.js

API reference

Complete TypeScript API reference for the XP plugin.

All exports are available from the @robojs/xp package. The plugin exports XP (also aliased as xp), config, events, leaderboard, math, rewards, constants, formatXP, and getXpLabel.

XP manipulation

The XP object (also exported as xp) provides methods for reading and modifying user XP.

XP.addXP

Add XP to a user. Creates the user record if it doesn't exist.

import { XP } from '@robojs/xp'

const result = await XP.addXP(guildId, userId, 100, { reason: 'contest_winner' })
import { XP } from '@robojs/xp'

const result = await XP.addXP(guildId, userId, 100, { reason: 'contest_winner' })

Parameters:

ParameterTypeDescription
guildIdstringGuild snowflake
userIdstringUser snowflake
amountnumberXP to add (must be >= 0)
options.reasonstring?Reason included in events
options.storeIdstring?Store to use (default: 'default')

Returns: XPChangeResult

interface XPChangeResult {
	oldXp: number
	newXp: number
	oldLevel: number
	newLevel: number
	leveledUp: boolean
}

XP is capped at the curve's maxLevel threshold if one is defined. Also available as XP.add().

XP.removeXP

Remove XP from a user. XP is clamped at 0.

const result = await XP.removeXP(guildId, userId, 50, { reason: 'moderation' })
const result = await XP.removeXP(guildId, userId, 50, { reason: 'moderation' })

Parameters: Same as addXP.

Returns: XPRemoveResult

interface XPRemoveResult {
	oldXp: number
	newXp: number
	oldLevel: number
	newLevel: number
	leveledDown: boolean
}

Also available as XP.remove().

XP.setXP

Set a user's total XP to an exact value.

const result = await XP.setXP(guildId, userId, 5000, { reason: 'admin_adjustment' })
const result = await XP.setXP(guildId, userId, 5000, { reason: 'admin_adjustment' })

Parameters: Same as addXP, but amount is the absolute XP value.

Returns: XPSetResult

interface XPSetResult {
	oldXp: number
	newXp: number
	oldLevel: number
	newLevel: number
}

Also available as XP.set().

XP.recalc

Recompute a user's level from their stored total XP and reconcile role rewards. Idempotent.

const result = await XP.recalc(guildId, userId)
const result = await XP.recalc(guildId, userId)

Parameters:

ParameterTypeDescription
guildIdstringGuild snowflake
userIdstringUser snowflake
options.storeIdstring?Store to use (default: 'default')

Returns: RecalcResult

interface RecalcResult {
	oldLevel: number
	newLevel: number
	totalXp: number
	reconciled: boolean // true if level was corrected
}

XP.getXP

Get a user's total XP. Returns 0 if the user has no record.

const xp = await XP.getXP(guildId, userId)
const repXp = await XP.getXP(guildId, userId, { storeId: 'reputation' })
const xp = await XP.getXP(guildId, userId)
const repXp = await XP.getXP(guildId, userId, { storeId: 'reputation' })

Also available as XP.get().

XP.getLevel

Get a user's current level. Returns 0 if the user has no record.

const level = await XP.getLevel(guildId, userId)
const level = await XP.getLevel(guildId, userId)

XP.getUser

Get the full user record. Returns null if the user has no record.

const user = await XP.getUser(guildId, userId)
const user = await XP.getUser(guildId, userId)

Returns: UserXP | null

interface UserXP {
	xp: number          // Total XP accumulated
	level: number       // Current level
	lastAwardedAt: number // Unix timestamp (ms) of last XP award
	messages: number    // Total messages in guild text channels
	xpMessages: number  // Messages that actually awarded XP
}

The messages counter increments for all valid guild messages. The xpMessages counter only increments when XP is awarded (after cooldown, no-XP zone, and multiplier checks pass).

Configuration

See Configuration for full details on each config field.

config.get

Get the merged config for a guild. Never returns null.

import { config } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const repConfig = await config.get(guildId, { storeId: 'reputation' })
import { config } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const repConfig = await config.get(guildId, { storeId: 'reputation' })

config.set

Update guild config with partial values. Validates before persisting.

const updated = await config.set(guildId, {
	cooldownSeconds: 45,
	xpRate: 1.5
})
const updated = await config.set(guildId, {
	cooldownSeconds: 45,
	xpRate: 1.5
})

Returns the full merged config after the update.

config.validate

Validate a partial config object without persisting.

const { valid, errors } = config.validate(partialConfig)
const { valid, errors } = config.validate(partialConfig)

Returns { valid: boolean, errors: string[] }.

config.getGlobal / config.setGlobal

Manage global defaults that apply to all guilds.

const globalConfig = await config.getGlobal()
await config.setGlobal({ xpRate: 1.5 })
const globalConfig = await config.getGlobal()
await config.setGlobal({ xpRate: 1.5 })

config.getDefault

Returns system defaults before any global or guild overrides.

const defaults = config.getDefault()
const defaults = config.getDefault()

Leaderboard

leaderboard.get

Get paginated leaderboard entries.

import { leaderboard } from '@robojs/xp'

const result = await leaderboard.get(guildId, 0, 10)
// { entries: LeaderboardEntry[], total: number }
import { leaderboard } from '@robojs/xp'

const result = await leaderboard.get(guildId, 0, 10)
// { entries: LeaderboardEntry[], total: number }

Parameters:

ParameterTypeDefaultDescription
guildIdstring--Guild snowflake
offsetnumber0Starting position
limitnumber10Max entries to return
options.storeIdstring?'default'Store to query

Returns: { entries: LeaderboardEntry[], total: number }

interface LeaderboardEntry {
	userId: string
	xp: number
	level: number
	rank: number // 1-indexed
}

Results for offset + limit of 100 or less come from the cache (sub-10ms). Deeper pagination triggers a full dataset sort.

leaderboard.getRank

Get a specific user's rank.

const rankInfo = await leaderboard.getRank(guildId, userId)
// { rank: 1, total: 150 } or null
const rankInfo = await leaderboard.getRank(guildId, userId)
// { rank: 1, total: 150 } or null

Returns null if the user has no XP or XP is 0.

leaderboard.invalidateCache

Manually invalidate the leaderboard cache.

leaderboard.invalidateCache(guildId)
leaderboard.invalidateCache(guildId, { storeId: 'reputation' })
leaderboard.invalidateCache(guildId, { all: true }) // All stores
leaderboard.invalidateCache(guildId)
leaderboard.invalidateCache(guildId, { storeId: 'reputation' })
leaderboard.invalidateCache(guildId, { all: true }) // All stores

Events

See Events for full documentation on the event system.

import { events } from '@robojs/xp'

events.onLevelUp(handler)
events.onLevelDown(handler)
events.onXPChange(handler)
events.on('levelUp', handler)
events.once('xpChange', handler)
events.off('levelUp', handler)
import { events } from '@robojs/xp'

events.onLevelUp(handler)
events.onLevelDown(handler)
events.onXPChange(handler)
events.on('levelUp', handler)
events.once('xpChange', handler)
events.off('levelUp', handler)

Math utilities

Pure math functions for level curve calculations. All functions accept an optional LevelCurve parameter to use a custom curve instead of the default quadratic.

math.xpNeededForLevel

XP delta required to gain a specific level (from the previous level).

import { math } from '@robojs/xp'

math.xpNeededForLevel(1)  // 155
math.xpNeededForLevel(10) // 1100
import { math } from '@robojs/xp'

math.xpNeededForLevel(1)  // 155
math.xpNeededForLevel(10) // 1100

math.totalXpForLevel

Cumulative XP required to reach the start of a level.

math.totalXpForLevel(10) // 5675
math.totalXpForLevel(10) // 5675

math.computeLevelFromTotalXp

Compute level and progress from total XP.

const progress = math.computeLevelFromTotalXp(5500)
// { level: 9, inLevel: 925, toNext: 175 }
const progress = math.computeLevelFromTotalXp(5500)
// { level: 9, inLevel: 925, toNext: 175 }

Returns: LevelProgress

interface LevelProgress {
	level: number   // Current level
	inLevel: number // XP accumulated within the current level
	toNext: number  // XP needed for the next level (0 at maxLevel)
}

math.progressInLevel

Get progress percentage within the current level.

const { current, needed, percentage } = math.progressInLevel(5500)
// { current: 925, needed: 1100, percentage: 84.09 }
const { current, needed, percentage } = math.progressInLevel(5500)
// { current: 925, needed: 1100, percentage: 84.09 }

math.xpDeltaForLevelRange

XP difference between two levels. Can be negative if toLevel < fromLevel.

const delta = math.xpDeltaForLevelRange(5, 10) // XP needed from level 5 to 10
const delta = math.xpDeltaForLevelRange(5, 10) // XP needed from level 5 to 10

math.isValidLevel / math.isValidXp

Validate level and XP values.

math.isValidLevel(50)  // true
math.isValidLevel(-1)  // false
math.isValidXp(10000)  // true
math.isValidXp(-5)     // false
math.isValidLevel(50)  // true
math.isValidLevel(-1)  // false
math.isValidXp(10000)  // true
math.isValidXp(-5)     // false

Rewards

rewards.reconcile

Manually reconcile a user's roles based on their level and the guild config.

import { rewards, config, XP } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const userLevel = await XP.getLevel(guildId, userId)

await rewards.reconcile(guildId, userId, userLevel, guildConfig)
import { rewards, config, XP } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const userLevel = await XP.getLevel(guildId, userId)

await rewards.reconcile(guildId, userId, userLevel, guildConfig)

Parameters:

ParameterTypeDescription
guildIdstringGuild snowflake
userIdstringUser snowflake
newLevelnumberUser's current level
guildConfigGuildConfigFull guild configuration

Also available as rewards.reconcileRewards() and as the top-level export reconcileRewards().

Constants

import { constants } from '@robojs/xp'

constants.DEFAULT_COOLDOWN          // 60
constants.DEFAULT_XP_RATE           // 1.0
constants.DEFAULT_REWARDS_MODE      // 'stack'
constants.DEFAULT_REMOVE_ON_LOSS    // false
constants.DEFAULT_LEADERBOARD_PUBLIC // false
constants.DEFAULT_CURVE_A           // 5
constants.DEFAULT_CURVE_B           // 50
constants.DEFAULT_CURVE_C           // 100
import { constants } from '@robojs/xp'

constants.DEFAULT_COOLDOWN          // 60
constants.DEFAULT_XP_RATE           // 1.0
constants.DEFAULT_REWARDS_MODE      // 'stack'
constants.DEFAULT_REMOVE_ON_LOSS    // false
constants.DEFAULT_LEADERBOARD_PUBLIC // false
constants.DEFAULT_CURVE_A           // 5
constants.DEFAULT_CURVE_B           // 50
constants.DEFAULT_CURVE_C           // 100

The curve constants (DEFAULT_CURVE_A, DEFAULT_CURVE_B, DEFAULT_CURVE_C) are also available as individual named exports.

Utility functions

formatXP

Format an XP value with thousands separators and an optional label.

import { formatXP } from '@robojs/xp'

formatXP(1500)                // '1,500 XP'
formatXP(1500, 'Reputation')  // '1,500 Reputation'
import { formatXP } from '@robojs/xp'

formatXP(1500)                // '1,500 XP'
formatXP(1500, 'Reputation')  // '1,500 Reputation'

getXpLabel

Extract the custom XP label from a guild config.

import { getXpLabel, config } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const label = getXpLabel(guildConfig) // 'XP' or custom label
import { getXpLabel, config } from '@robojs/xp'

const guildConfig = await config.get(guildId)
const label = getXpLabel(guildConfig) // 'XP' or custom label

Next steps

On this page