highrise-core
v1.3.3
Published
Enterprise WebSocket infrastructure for Highrise featuring spatial intelligence systems, memory-optimized architecture, and production-grade reliability for scalable application development
Maintainers
Readme
Highrise Bot SDK
A feature-rich WebSocket SDK for creating bots in Highrise. Designed for performance, reliability, and ease of use.
🚀 Features
Core Capabilities
- Real-time WebSocket Connection - Stable connection with auto-reconnect
- Complete Bot API Coverage - All official Highrise bot endpoints implemented
- Advanced Caching System - Efficient user position tracking and spatial queries and more upcoming
- Comprehensive Event Handling - All major Highrise events supported
- Web API Integration - Access to user, room, item, post, grab data
Performance Highlights
- 95%+ Memory Reduction in position tracking using binary encoding
- Sub-millisecond spatial queries for user location
- Automatic resource cleanup and connection management
- Object pooling for reduced garbage collection
- 95% Memory leak free
📦 Installation
npm install highrise-core🎯 Quick Start
Basic Bot Setup
const { Highrise } = require('highrise-core');
// Create bot instance
const bot = new Highrise(
['ChatEvent', 'UserJoinedEvent', 'UserMovedEvent'], // Events to listen for
{
LoggerOptions: {
showTimestamp: true,
showMethodName: true,
colors: true
},
autoReconnect: true,
reconnectDelay: 5000
}
);
// Event handlers
bot.on('Ready', (metadata) => {
console.log(`Bot connected to room: ${metadata.room.room_name}`);
});
bot.on('Chat', async (user, message) => {
if (message === '!hello') {
await bot.message.send(`Hello ${user.username}! 👋`);
}
});
bot.on('UserJoined', async (user) => {
await bot.message.send(`Welcome to the room, ${user.username}! 🎉`);
});
// Connect to Highrise
bot.login('your_64_character_bot_token', 'your_24_character_room_id');🔧 Core Features
Movement & Positioning
// Make the bot walk to coordinates
await bot.player.walk(10, 0, 5, 'FrontRight');
// Teleport users
await bot.player.teleport('user_id', 15, 0, 10, 'BackLeft');
// Make the bot sit on furniture
await bot.player.sit('entity_id', 0);Advanced Spatial Queries
// Get players in a 8x8 square area
const playersInArea = bot.cache.position.getPlayersInSquare(
{ x: 10, y: 0, z: 10 },
8
);
// Find closest player
const closestPlayer = bot.cache.position.getClosestPlayer({ x: 0, z: 0 });
// Get players within 5 units radius (sorted by distance)
const nearbyPlayers = bot.cache.position.getPlayersInCircle(
{ x: 10, z: 10 },
5
);
// Check if specific user is in area
const isInVIP = bot.cache.position.isPlayerInRectangle('user_id',
{
c1: { x: 20, z: 20 }, // Top-right
c2: { x: 25, z: 20 }, // Top-left
c3: { x: 25, z: 25 }, // bottom-right
c4: { x: 20, z: 25 } // bottom-left
}
);Communication
// Room messages
await bot.message.send("Hello everyone!");
// Private whispers
await bot.whisper.send("user_id", "This is a private message!");
// Direct messages
await bot.direct.send("conversation_id", "Direct message content");
// Send room invites via DM
await bot.direct.invite("conversation_id", { room_id: "target_room_id" });
// Send world invites via DM
await bot.direct.invite("conversation_id", { world_id: "target_world_id" });Moderation
// Basic moderation
await bot.player.moderation.kick("user_id");
await bot.player.moderation.mute("user_id", 5 * 60 * 1000); // 5 minutes (default)
await bot.player.moderation.ban("user_id", 60 * 60 * 1000); // 1 hour (default)
// Room privilege management
await bot.room.moderator.add("user_id");
await bot.room.designer.add("user_id");Economy & Items
// Check wallet
const wallet = await bot.inventory.wallet.get();
console.log(`Gold: ${wallet.gold}, Boosts: ${wallet.boost_tokens}`);
// Send tips
await bot.player.tip("user_id", 100); // 100 gold bars (default: 1)
// Buy bot items
await bot.inventory.item.buy("item_id");
// Set bot outfit
await bot.inventory.set([
{
type: 'clothing',
amount: 1,
id: 'shirt-n_cooltshirt',
account_bound: false,
active_palette: null
}
]);
// set outfit back to default
await bot.inventory.set();🌐 Web API Integration
// User information
const user = await bot.webapi.user.get("username_or_id");
console.log(`${user.username} - ${user.followers} followers`);
// Room information
const room = await bot.webapi.room.get("room_id");
console.log(`${room.name} - ${room.connectedUsers} users online`);
// Item catalog
const items = await bot.webapi.item.search({ query: "dragon", limit: 10 });
items.items.forEach(item => {
console.log(`${item.name} - ${item.pricing.gems} gems`);
});
// Posts and social
const posts = await bot.webapi.post.getByAuthor("user_id");📊 Performance Metrics
The SDK is optimized for high-performance scenarios:
- Movement Cache: Tracks 1000+ users with ~5KB memory usage
- Spatial Queries: Process 10,000+ positions in <1ms
- WebSocket: Handles 100+ events per second efficiently
- Memory: 80-95% reduction in position storage through binary encoding
Monitoring
// Display real-time metrics
bot.showMetrics();
// Typical terminal output:
Room: Unfairly's room
Uptime: 2h 15m
Performance: 1500 msg, 300 evt, 2 err
Cache: 45 users (12 active)
Memory: 5.2 KB used🔄 Await System
The await system allows your bot to wait for specific events with powerful filtering capabilities. Perfect for creating interactive experiences, games, and complex bot logic.
🎯 Basic Usage
Wait for a Single Chat Message
// Wait for any user to type !start
const results = await bot.await.chat(
(user, message) => message === '!start',
30000, // 30 second timeout
1 // Collect 1 match
);
if (results.length > 0) {
const [[user, message]] = results;
await bot.message.send(`Game started by ${user.username}! 🎮`);
}Wait for a Whisper
// Wait for help request via whisper
const helpRequests = await bot.await.whisper(
(user, message) => message.includes('help'),
15000,
1
);
if (helpRequests.length > 0) {
const [[user, message]] = helpRequests;
await bot.whisper.send(user.id, "I'm here to help! What do you need?");
}Wait for a Direct Message
// Wait for confirmation in DM
const confirmations = await bot.await.direct(
(user, message, conversation) => message === '!confirm',
60000,
1
);
if (confirmations.length > 0) {
const [[user, message, conversation]] = confirmations;
await bot.direct.send(conversation.id, "Order confirmed! ✅");
}Wait for a Tip
// Wait for any tip to the bot
const tips = await bot.await.tip(
(sender, receiver, currency) => receiver.id === bot.info.user.id,
120000,
1
);
if (tips.length > 0) {
const [[sender, receiver, currency]] = tips;
await bot.message.send(`Thank you ${sender.username} for the ${currency.amount} gold tip! 💰`);
}Wait for Player Movement
// Wait for player to enter specific area
const entrants = await bot.await.movement(
(user, position, anchor) =>
position.x >= 10 && position.x <= 15 &&
position.z >= 10 && position.z <= 15,
60000,
1
);
if (entrants.length > 0) {
const [[user, position, anchor]] = entrants;
await bot.message.send(`Welcome to the dance floor, ${user.username}! 💃`);
}🚀 Advanced Usage
Chat - Voting System
// Collect votes from multiple users
await bot.message.send("Vote for your favorite: !pizza, !burger, or !sushi");
const votes = await bot.await.chat(
(user, message) => ['!pizza', '!burger', '!sushi'].includes(message),
45000, // 45 seconds
20 // Collect up to 20 votes
);
// Process results
const voteCount = {
pizza: votes.filter(([user, message]) => message === '!pizza').length,
burger: votes.filter(([user, message]) => message === '!burger').length,
sushi: votes.filter(([user, message]) => message === '!sushi').length
};
const winner = Object.keys(voteCount).reduce((a, b) => voteCount[a] > voteCount[b] ? a : b);
await bot.message.send(`The winner is ${winner} with ${voteCount[winner]} votes! 🏆`);Whisper - Secret Command System
// Private command system
await bot.message.send("Whisper me '!mod' to apply for moderator");
const modApplications = await bot.await.whisper(
(user, message) => message === '!mod',
120000, // 2 minutes
10 // Collect up to 10 applications
);
// Process each application
for (const [user, message] of modApplications) {
const isEligible = await checkUserEligibility(user.id);
if (isEligible) {
await bot.whisper.send(user.id, "You've been approved as moderator! 🛡️");
await bot.room.moderator.add(user.id);
} else {
await bot.whisper.send(user.id, "Sorry, you don't meet the requirements yet.");
}
}Direct - Support Ticket System
// Handle multiple support requests
const supportTickets = await bot.await.direct(
(user, message, conversation) =>
message.includes('help') || message.includes('support') || message.includes('issue'),
300000, // 5 minutes
5 // Handle 5 tickets at once
);
// Create tickets for each request
for (const [user, message, conversation] of supportTickets) {
const ticketId = createSupportTicket(user.id, message);
await bot.direct.send(conversation.id,
`Support ticket #${ticketId} created! We'll assist you shortly. ⏱️`
);
// Assign to available support agent
assignTicketToAgent(ticketId, conversation.id);
}Tip - Fundraiser Tracker
// Track large donations during fundraiser
await bot.message.send("🎗️ Fundraiser started! Every donation helps!");
const donations = await bot.await.tip(
(sender, receiver, currency) =>
receiver.id === bot.info.user.id && currency.amount >= 50,
600000, // 10 minutes
50 // Track up to 50 large donations
);
// Calculate and announce totals
let totalRaised = 0;
let donorCount = 0;
donations.forEach(([sender, receiver, currency]) => {
totalRaised += currency.amount;
donorCount++;
// Acknowledge large donors
if (currency.amount >= 500) {
bot.message.send(`🎉 HUGE thanks to ${sender.username} for ${currency.amount} gold!`);
}
});
await bot.message.send(
`🏁 Fundraiser complete! Raised ${totalRaised} gold from ${donorCount} donors! Thank you all!`
);Movement - Interactive Game
// Treasure hunt game - wait for players to find hidden spots
const treasureSpots = [
{ x: 5, z: 5, name: "Ancient Ruins" },
{ x: 15, z: 20, name: "Crystal Cave" },
{ x: 25, z: 10, name: "Dragon's Lair" }
];
await bot.message.send("🔍 Treasure hunt started! Find the hidden locations!");
const discoveries = await bot.await.movement(
(user, position, anchor) => {
return treasureSpots.some(spot =>
Math.abs(position.x - spot.x) < 2 &&
Math.abs(position.z - spot.z) < 2
);
},
300000, // 5 minutes
10 // Up to 10 discoveries
);
// Reward discoverers
for (const [user, position, anchor] of discoveries) {
const treasure = treasureSpots.find(spot =>
Math.abs(position.x - spot.x) < 2 &&
Math.abs(position.z - spot.z) < 2
);
await bot.message.send(
`🏆 ${user.username} discovered ${treasure.name}! +100 gold reward!`
);
await bot.player.tip(user.id, 100);
// Mark as discovered
treasureSpots.splice(treasureSpots.indexOf(treasure), 1);
}🎮 Complex Scenarios
Multi-Stage Registration
// Complete user registration flow
await bot.message.send("Type !register to start the signup process");
const starters = await bot.await.chat(
(user, message) => message === '!register',
30000,
1
);
if (starters.length > 0) {
const [[user, message]] = starters;
// Stage 1: Get username preference
await bot.whisper.send(user.id, "What username should we use for your account?");
const usernameResponse = await bot.await.whisper(
(u, msg) => u.id === user.id && msg.length >= 3,
60000,
1
);
if (usernameResponse.length > 0) {
const [[u, preferredUsername]] = usernameResponse;
// Stage 2: Get email
await bot.whisper.send(user.id, "What's your email address?");
const emailResponse = await bot.await.whisper(
(u, msg) => u.id === user.id && msg.includes('@'),
60000,
1
);
if (emailResponse.length > 0) {
const [[u, email]] = emailResponse;
// Complete registration
await completeRegistration(user.id, preferredUsername, email);
await bot.whisper.send(user.id, "Registration complete! Welcome! 🎉");
}
}
}Real-time Event Coordination
// Coordinate multiple users for an event
await bot.message.send("🎪 Event starting! Stand in the red circle and type !ready");
const [redCircleUsers, readyUsers] = await Promise.all([
// Wait for users to enter red circle
bot.await.movement(
(user, position, anchor) =>
position.x >= 8 && position.x <= 12 &&
position.z >= 8 && position.z <= 12,
60000,
10
),
// Wait for users to type !ready
bot.await.chat(
(user, message) => message === '!ready',
60000,
10
)
]);
// Find users who did both
const participatingUsers = redCircleUsers.filter(([user1]) =>
readyUsers.some(([user2]) => user2.id === user1.id)
);
await bot.message.send(
`${participatingUsers.length} players are ready! Starting event... 🚀`
);Advanced Movement Tracking
// Track complex movement patterns
const movementPatterns = await bot.await.movement(
(user, position, anchor) => {
const previousData = bot.cache.position.get(user.id);
if (!previousData) return false;
const prevPos = previousData.position;
const distance = Math.sqrt(
Math.pow(position.x - prevPos.x, 2) +
Math.pow(position.z - prevPos.z, 2)
);
// Detect rapid movement (possible teleport)
const isTeleport = distance > 20;
// Detect sitting/standing transitions
const wasSitting = previousData.anchor !== null;
const isSitting = anchor !== null;
const postureChange = wasSitting !== isSitting;
return isTeleport || postureChange;
},
120000,
20
);
// Analyze movement patterns
movementPatterns.forEach(([user, position, anchor]) => {
if (anchor) {
bot.utils.logger.info('Movement', `${user.username} sat down`);
} else {
bot.utils.logger.info('Movement', `${user.username} stood up`);
}
});💡 Pro Tips
1. Always Check Array Length
const results = await bot.await.chat(filter, timeout, max);
if (results.length > 0) {
// Process results
} else {
// Handle timeout
}2. Use Destructuring for Single Results
const results = await bot.await.whisper(filter, timeout, 1);
if (results.length > 0) {
const [[user, message]] = results; // Destructure the single result
// Use user and message
}3. Combine Multiple Awaits
// Wait for either chat OR whisper
const [chatResult, whisperResult] = await Promise.all([
bot.await.chat(filter1, timeout, 1),
bot.await.whisper(filter2, timeout, 1)
]);4. Use Appropriate Timeouts
// Quick responses: 15-30 seconds
// User interactions: 1-2 minutes
// Complex processes: 5-10 minutes
// Events/games: 10-30 minutes5. Handle Timeouts Gracefully
try {
const results = await bot.await.direct(filter, 30000, 1);
if (results.length === 0) {
await bot.message.send("Time's up! ⏰");
return;
}
// Process results
} catch (error) {
bot.utils.logger.error('Await', 'Await operation failed', error);
}The await system transforms your bot from reactive to proactive, enabling complex interactive experiences that respond to user actions in real-time!
🔌 Event System
Available Events
bot.on('Ready', (metadata) => {
// Bot connected and ready
});
bot.on('Chat', (user, message) => {
// Room message received
});
bot.on('Whisper', (user, message) => {
// Private whisper received
});
bot.on('Movement', (user, position, anchor) => {
// User moved
});
bot.on('UserJoined', (user, position) => {
// User entered the room
});
bot.on('UserLeft', (user) => {
// User left the room
});
bot.on('Direct', (user, message, conversation) => {
// Direct message received
});
bot.on('Tip', (sender, receiver, currency) => {
// Tip received or sent
});
bot.on('Moderation', (moderator, target, action) => {
// Moderation action occurred
});⚙️ Configuration
Bot Options
const bot = new Highrise(events, {
LoggerOptions: {
showTimestamp: true, // Show timestamps in logs
showMethodName: true, // Show method names in logs
colors: true // Color-coded log output
},
autoReconnect: true, // Auto-reconnect on disconnect
reconnectDelay: 5000 // Delay between reconnect attempts (ms)
});Sender Configuration
// Configure request timeouts and retries
bot.configureSenders({
defaultTimeout: 10000, // 10 second timeout
maxRetries: 3, // Maximum retry attempts
retryDelay: 100 // Delay between retries (ms)
});🛠️ Advanced Usage
Custom Event Handlers
// Register custom event processing
bot.on('Movement', async (user, position) => {
const isInVIP = bot.cache.position.isPlayerInRectangle(user.id,
{
c1: { x: 20, z: 20 }, // Top-right
c2: { x: 25, z: 20 }, // Top-left
c3: { x: 25, z: 25 }, // bottom-right
c4: { x: 20, z: 25 } // bottom-left
}
);
// Custom movement logic
if (isInVIP) {
await bot.whisper.send(user.id, "You're in the VIP area! 🎉");
}
});Rate Limiting
// Implement custom rate limiting
class RateLimitedBot {
constructor(bot) {
this.bot = bot;
this.lastMessage = 0;
}
async sendMessage(message) {
const now = Date.now();
if (now - this.lastMessage < 1000) {
await bot.utils.sleep(1000 - (now - this.lastMessage));
}
this.lastMessage = Date.now();
return this.bot.message.send(message);
}
}📈 Real-World Example
Welcome Bot
const { Highrise } = require('highrise-core');
const bot = new Highrise(['ChatEvent', 'UserJoinedEvent', 'UserMovedEvent']);
// Track new users
const newUsers = new Set();
bot.on('Ready', () => {
console.log('Welcome bot is online!');
});
bot.on('UserJoined', async (user) => {
newUsers.add(user.id);
if (newUsers.has(user.id)) {
await bot.message.send(`Welcome ${user.username}! Enjoy your stay! 🏠`);
newUsers.delete(user.id);
}
});
bot.on('UserLeft', (user) => {
newUsers.delete(user.id);
});
bot.on('Chat', async (user, message) => {
// Remove from new users if they chat
newUsers.delete(user.id);
// Simple commands
if (message === '!help') {
await bot.message.send('Available commands: !help, !info, !position');
}
if (message === '!position') {
const position = await bot.room.users.position(user.id);
await bot.whisper.send(user.id, `Your position: ${position.x}, ${position.y}, ${position.z}`);
}
});
bot.login('your_bot_token', 'your_room_id');🔍 Key Benefits
- Reliable: Automatic reconnection and error recovery
- Fast: Optimized caching and efficient data structures and event emition
- Comprehensive: Full Highrise API coverage
- Developer-Friendly: Intuitive API with full TypeScript support
- Production-Ready: Built-in monitoring and performance tracking
📚 Documentation
For complete API documentation, check the TypeScript definitions in index.d.ts or visit our documentation site (will be available soon).
🐛 Issues and Support
Found a bug or need help? Please send a message to me in discord @oqs0_ with:
- sdk version
- Error logs
- Steps to reproduce
📄 License
MIT License - Copyright (c) 2025 Yahya Ahmed
Ready to build? Start with the Quick Start section and check the examples for common use cases!
