Packages@robojs/xp
events
Variable: events
const events: {
off: <T>(event, listener) => void;
on: <T>(event, listener) => void;
once: <T>(event, listener) => void;
onLevelDown: (handler) => void;
onLevelUp: (handler) => void;
onXPChange: (handler) => void;
};Event system for XP changes and level progression
Type-safe event system for subscribing to XP-related events. All events are emitted after Flashcore persistence, guaranteeing consistency.
Available Events:
'levelUp': Emitted when a user gains a level (typed asLevelUpEvent)'levelDown': Emitted when a user loses a level (typed asLevelDownEvent)'xpChange': Emitted on any XP change (typed asXPChangeEvent)
All events include storeId field:
- Identifies which data store triggered the event
- Allows event listeners to filter events by store
- Role rewards only process default store events
- Leaderboard cache invalidation uses this for per-store invalidation
Convenience Methods (delegates to on()):
onLevelUp(handler): Shorthand foron('levelUp', handler)onLevelDown(handler): Shorthand foron('levelDown', handler)onXPChange(handler): Shorthand foron('xpChange', handler)
Features:
- Strongly typed event payloads via discriminated unions
- Events emitted synchronously after persistence
- Automatic role reconciliation via internal event listeners
- One-time listeners via
once() - Listener removal via
off() - Convenience methods for common event types
Type declaration
Examples
Type-Safe Level-Up Listener (with convenience method)
import { events } from '@robojs/xp'
import type { LevelUpEvent } from '@robojs/xp'
// Using convenience method
events.onLevelUp((event: LevelUpEvent) => {
console.log(`User ${event.userId} leveled up to level ${event.newLevel}!`)
})
// Or using generic on() method
events.on('levelUp', (event: LevelUpEvent) => {
console.log(`User ${event.userId} leveled up to level ${event.newLevel}!`)
})Level-Down Listener (XP Removal)
import { events } from '@robojs/xp'
import type { LevelDownEvent } from '@robojs/xp'
// Using convenience method
events.onLevelDown((event: LevelDownEvent) => {
console.log(`User ${event.userId} dropped to level ${event.newLevel}`)
})
// Or using generic on() method
events.on('levelDown', (event: LevelDownEvent) => {
console.log(`User ${event.userId} dropped to level ${event.newLevel}`)
// Role rewards removed automatically if removeRewardsOnLoss is true
})Track All XP Changes (with convenience method)
import { events } from '@robojs/xp'
import type { XPChangeEvent } from '@robojs/xp'
// Using convenience method
events.onXPChange((event: XPChangeEvent) => {
console.log(`Store ${event.storeId}: User ${event.userId} XP changed by ${event.delta}`)
})
// Or using generic on() method
events.on('xpChange', (event: XPChangeEvent) => {
console.log(`User ${event.userId} XP changed by ${event.delta}`)
console.log(`Store: ${event.storeId}`)
console.log(`Reason: ${event.reason || 'message'}`)
console.log(`Old XP: ${event.oldXp}, New XP: ${event.newXp}`)
})Level-Down Listener (XP Removal)
import { events } from '@robojs/xp'
import type { LevelDownEvent } from '@robojs/xp'
// Handle level-down events (e.g., from moderation)
events.on('levelDown', (event: LevelDownEvent) => {
console.log(`User ${event.userId} dropped to level ${event.newLevel}`)
console.log(`Lost ${event.oldLevel - event.newLevel} levels`)
// Role rewards removed automatically if removeRewardsOnLoss is true
})Track All XP Changes
import { events } from '@robojs/xp'
import type { XPChangeEvent } from '@robojs/xp'
// Listen for any XP change (level-up, level-down, or same level)
events.on('xpChange', (event: XPChangeEvent) => {
console.log(`User ${event.userId} XP changed by ${event.delta}`)
console.log(`Reason: ${event.reason || 'message'}`)
console.log(`Old XP: ${event.oldXp}, New XP: ${event.newXp}`)
console.log(`Old Level: ${event.oldLevel}, New Level: ${event.newLevel}`)
})One-Time Listener
import { events } from '@robojs/xp'
// Listen for first level-up only
events.once('levelUp', (event) => {
console.log('First level up detected!')
console.log(`User ${event.userId} reached level ${event.newLevel}`)
// Listener automatically removed after first trigger
})Remove Listener
import { events } from '@robojs/xp'
import type { XPChangeEvent } from '@robojs/xp'
// Create a named listener function
const trackXpChanges = (event: XPChangeEvent) => {
console.log(`XP changed: ${event.delta}`)
}
// Register listener
events.on('xpChange', trackXpChanges)
// Later, remove listener
events.off('xpChange', trackXpChanges)Build Level-Up Announcement System
import { events } from '@robojs/xp'
import { getClient } from '@robojs/discordjs'
events.on('levelUp', async (event) => {
// Get guild and user
const client = getClient()
const guild = await client.guilds.fetch(event.guildId)
const member = await guild.members.fetch(event.userId)
// Send announcement to system channel
const channel = guild.systemChannel
if (channel) {
await channel.send({
content: `Congratulations <@${event.userId}>! You reached level ${event.newLevel}!`,
allowedMentions: { users: [event.userId] }
})
}
})Track XP for Analytics (with multi-store support)
import { events } from '@robojs/xp'
events.on('xpChange', (event) => {
// Send to analytics service
analytics.track('xp_change', {
guildId: event.guildId,
userId: event.userId,
storeId: event.storeId,
delta: event.delta,
reason: event.reason,
timestamp: Date.now()
})
})
// Filter by store for specific tracking
events.on('levelUp', (event) => {
if (event.storeId === 'reputation') {
analytics.track('reputation_level_up', {
guildId: event.guildId,
userId: event.userId,
newLevel: event.newLevel
})
}
})Remarks
- Events are emitted after successful Flashcore persistence
- Role rewards reconcile automatically via internal event listeners
- Event names are string literals:
'levelUp','levelDown','xpChange' - TypeScript provides type safety for event payloads via discriminated unions
- Events are emitted synchronously - listeners run immediately
- Internal listeners for role rewards are registered in
runtime/rewards.ts
