Robot memory system with user management, persistent data storage, and automatic serialization for maintaining state across restarts.
The Brain serves as the robot's memory system, providing data persistence and user management functionality.
/**
* Robot memory and user management system
* @param robot - Robot instance that owns this brain
*/
class Brain {
constructor(robot: Robot);
// Properties
data: {
users: Record<string, User>;
_private: Record<string, any>;
};
autoSave: boolean;
// Data storage methods
set(key: string, value: any): Brain;
get(key: string): any;
remove(key: string): Brain;
// Persistence methods
save(): void;
close(): void;
// User management methods
users(): Record<string, User>;
userForId(id: string, options?: UserOptions): User;
userForName(name: string): User | null;
usersForRawFuzzyName(fuzzyName: string): User[];
usersForFuzzyName(fuzzyName: string): User[];
// Configuration methods
setAutoSave(enabled: boolean): void;
resetSaveInterval(seconds: number): void;
mergeData(data: any): void;
}Usage Examples:
import { Robot } from "hubot";
const robot = new Robot("Shell");
// Store and retrieve data (chainable)
robot.brain.set('server-stats', { uptime: Date.now(), requests: 0 })
.set('app-version', '1.0.0');
const stats = robot.brain.get('server-stats');
// Remove data (chainable)
robot.brain.remove('old-data').remove('another-key');
// Configure auto-save (enabled by default)
robot.brain.setAutoSave(true);
robot.brain.resetSaveInterval(30); // Save every 30 seconds
// Access all users
const allUsers = robot.brain.users();
console.log(`Total users: ${Object.keys(allUsers).length}`);
// Get or create user by ID
const user = robot.brain.userForId('user123', { name: 'Alice' });
// Find user by name
const alice = robot.brain.userForName('Alice');
// Fuzzy name search
const matches = robot.brain.usersForFuzzyName('al'); // Finds "Alice", "Albert", etc.
// Manual save trigger
robot.brain.save();
// Cleanup on shutdown
robot.brain.close();Represents individual chat users with persistent data storage capabilities.
/**
* User representation with persistent data storage
* @param id - Unique user identifier
* @param options - Hash of user properties
*/
class User {
constructor(id: string, options?: UserOptions);
// Properties
id: string;
name: string; // Defaults to id if not provided
room?: string;
// Data storage methods
async set(key: string, value: any): Promise<void>;
async get(key: string): Promise<any>;
}
interface UserOptions {
name?: string;
room?: string;
[key: string]: any; // Additional user properties
}Usage Examples:
// Create user with basic info
const user = new User('user123', {
name: 'Alice Smith',
room: 'general',
email: 'alice@example.com'
});
// Store user-specific data (async)
await user.set('preferences', { theme: 'dark', notifications: true });
await user.set('score', 100);
await user.set('last-seen', new Date().toISOString());
// Retrieve user data (async)
const prefs = await user.get('preferences');
const score = await user.get('score');
// Use in listeners (async)
robot.hear(/my score/i, async (res) => {
const score = await res.message.user.get('score') || 0;
res.reply(`Your score is ${score} points!`);
});
robot.hear(/set theme (.+)/i, async (res) => {
const theme = res.match[1];
await res.message.user.set('theme', theme);
res.reply(`Theme set to ${theme}`);
});Brain automatically handles data persistence with configurable auto-save functionality.
// Brain emits events for persistence
robot.brain.on('save', (data) => {
// Save data to your preferred storage (file, database, etc.)
console.log('Saving brain data:', data);
});
robot.brain.on('loaded', (data) => {
console.log('Brain data loaded');
});
// Manual data merging (for loading from storage)
const savedData = loadFromStorage(); // Your storage implementation
robot.brain.mergeData(savedData);Advanced user lookup capabilities with fuzzy matching.
/**
* Get all users as object with user IDs as keys
*/
users(): Record<string, User>;
/**
* Get or create user by ID
* @param id - User ID
* @param options - User properties if creating new user
*/
userForId(id: string, options?: UserOptions): User;
/**
* Find user by exact name match
* @param name - User's display name
*/
userForName(name: string): User | null;
/**
* Find users by fuzzy name matching (raw search)
* @param fuzzyName - Partial or fuzzy name to match
*/
usersForRawFuzzyName(fuzzyName: string): User[];
/**
* Find users by fuzzy name with exact match preference
* @param fuzzyName - Partial or fuzzy name to match
*/
usersForFuzzyName(fuzzyName: string): User[];Usage Examples:
// Find users for @mentions
robot.hear(/@(\w+)/i, (res) => {
const username = res.match[1];
const users = robot.brain.usersForFuzzyName(username);
if (users.length === 1) {
res.send(`You mentioned ${users[0].name}`);
} else if (users.length > 1) {
const names = users.map(u => u.name).join(', ');
res.send(`Multiple users found: ${names}`);
} else {
res.send(`User '${username}' not found`);
}
});
// User statistics
robot.respond(/user stats/i, (res) => {
const users = robot.brain.users();
const userCount = Object.keys(users).length;
const activeUsers = Object.values(users).filter(u =>
u.get('last-seen') &&
new Date(u.get('last-seen')) > new Date(Date.now() - 24 * 60 * 60 * 1000)
).length;
res.send(`Total users: ${userCount}, Active today: ${activeUsers}`);
});Brain can integrate with custom DataStore implementations for different persistence backends.
/**
* Abstract DataStore interface for persistent storage
* @param robot - Robot instance
*/
class DataStore {
constructor(robot: Robot);
robot: Robot;
// Global data methods
async set(key: string, value: any): Promise<void>;
async get(key: string): Promise<any>;
async setObject(key: string, objectKey: string, value: any): Promise<void>;
async getObject(key: string, objectKey: string): Promise<any>;
async setArray(key: string, value: any): Promise<void>;
}
/**
* Error class for DataStore unavailable conditions
* @param message - Error message
*/
class DataStoreUnavailable extends Error {
constructor(message: string);
}interface BrainData {
users: Record<string, User>;
_private: Record<string, any>;
}
interface UserData {
id: string;
name: string;
room?: string;
[key: string]: any;
}
interface BrainEvents {
'save': (data: BrainData) => void;
'loaded': () => void;
'close': () => void;
}