rostatstrack
v1.0.2
Published
A Node.js package that tracks and logs ROBLOX game and group statistics over time
Maintainers
Readme
🎮 rostatstrack
A powerful Node.js package for tracking and logging ROBLOX game and group statistics over time.
rostatstrack automates the collection of critical ROBLOX metrics, storing them in organized CSV files with detailed logging. Perfect for game developers, analytics enthusiasts, and community managers who want to track their ROBLOX presence over time.
📊 What It Tracks
| Metric | Description | Use Case | |--------|-------------|----------| | Game Visits | Total number of visits to your game | Track overall popularity and growth | | Favorites | Number of users who favorited your game | Measure player engagement | | Active Players | Current concurrent players | Monitor real-time activity | | Group Members | Total members in your group | Track community growth | | Upvotes | Positive votes on your game | Gauge player satisfaction | | Downvotes | Negative votes on your game | Identify improvement areas |
✨ Features
🎯 Core Functionality
- Automated Data Collection - Set it and forget it! Collects stats at configurable intervals
- Dual Usage Modes - Use as a CLI tool or integrate into your Node.js applications
- Smart CSV Organization - Daily CSV files with headers, easy to analyze
- Comprehensive Logging - Detailed logs with timestamps and error tracking
- Robust Error Handling - Automatic retries on API failures with exponential backoff
🛡️ Reliability Features
- Rate Limit Respect - Built-in delays to comply with ROBLOX API limits
- Graceful Shutdown - Handles SIGINT/SIGTERM for clean exits
- Log Rotation - Automatic archiving of old log files
- Config Validation - Validates settings before starting
- Network Resilience - Retries on 429, 500, 502, 503, and 504 errors
📦 Developer Experience
- TypeScript-Ready - Works seamlessly in TypeScript projects
- Zero Configuration - Works out of the box with sensible defaults
- Full API Access - Programmatic control over all functionality
- Easy Integration - Simple, intuitive API design
📦 Installation
Prerequisites
- Node.js >= 14.0.0
- npm or yarn
- A ROBLOX game universe ID and group ID
Quick Install
As a Global CLI Tool
Install globally to use the rostatstrack command anywhere on your system:
npm install -g rostatstrackAs a Project Dependency
Install locally in your project for programmatic use:
npm install rostatstrackOr with Yarn:
yarn add rostatstrackFrom Source (Development)
Clone the repository for development or customization:
git clone https://github.com/yourusername/roblox-stats-tracker.git
cd roblox-stats-tracker
npm install🚀 Quick Start
1. Find Your IDs
Universe ID
- Go to ROBLOX Creator Dashboard
- Select your game
- The universe ID is in the URL:
https://create.roblox.com/dashboard/creations/experiences/[universeId]/...
Group ID
- Navigate to your group page on ROBLOX
- The group ID is in the URL:
https://www.roblox.com/groups/[groupId]/...
2. Create Configuration
Create a config.js file in your project directory:
const path = require("path");
module.exports = {
// REQUIRED: Your ROBLOX game's universe ID
universeId: 1931573465,
// REQUIRED: Your ROBLOX group ID
groupId: 2880815,
// OPTIONAL: Interval between data collections (milliseconds)
// Default: 300000 (5 minutes)
// Minimum: 60000 (1 minute)
saveInterval: 5 * 60 * 1000,
// OPTIONAL: Directory to save CSV files
// Default: './saves'
savesDir: path.join(__dirname, "saves"),
// OPTIONAL: Directory to save log files
// Default: './logs'
logDir: path.join(__dirname, "logs"),
// OPTIONAL: Name of the current log file
// Default: 'latest.log'
logFileName: "latest.log"
};3. Run the Tracker
rostatstrackThat's it! Your stats are now being tracked every 5 minutes.
💻 Usage
CLI Usage
Basic Usage
After creating your config.js file:
# If installed globally
rostatstrack
# If installed locally
npx rostatstrack
# Or using npm script
npm startRunning in Background (Linux/macOS)
For production environments, run the tracker in the background:
Using screen:
# Install screen (if not installed)
# Ubuntu/Debian:
sudo apt-get install screen
# CentOS/RHEL:
sudo yum install screen
# Start tracker in a screen session
screen -S rostatstrack
rostatstrack
# Detach from screen: Press Ctrl+A, then D
# Reattach to screen
screen -r rostatstrack
# List all screens
screen -lsUsing PM2 (Recommended):
# Install PM2 globally
npm install -g pm2
# Start tracker
pm2 start bin/cli.js --name "rostatstrack"
# View logs
pm2 logs rostatstrack
# Stop tracker
pm2 stop rostatstrack
# Restart tracker
pm2 restart rostatstrack
# Configure auto-restart on system reboot
pm2 startup
pm2 saveRunning in Background (Windows)
Using Windows Service:
Install PM2 and create a Windows service:
npm install -g pm2
npm install -g pm2-windows-service
# Install as service
pm2-service-install
# Start service
pm2 start bin/cli.js --name "rostatstrack"
pm2 saveLibrary Usage
Basic Example
const RobloxStatsTracker = require('rostatstrack');
// Create tracker instance
const tracker = new RobloxStatsTracker({
universeId: 1931573465,
groupId: 2880815,
saveInterval: 5 * 60 * 1000 // 5 minutes
});
// Start tracking
tracker.start()
.then(() => {
console.log('Tracker started successfully');
})
.catch(error => {
console.error('Failed to start tracker:', error);
process.exit(1);
});
// Handle graceful shutdown
process.on('SIGINT', () => {
console.log('Shutting down gracefully...');
tracker.stop();
});Advanced Example with Error Handling
const RobloxStatsTracker = require('rostatstrack');
const path = require('path');
// Configure tracker
const tracker = new RobloxStatsTracker({
universeId: 1931573465,
groupId: 2880815,
saveInterval: 5 * 60 * 1000,
savesDir: path.join(__dirname, 'data', 'stats'),
logDir: path.join(__dirname, 'data', 'logs'),
logFileName: 'tracker.log'
});
// Custom error handler
async function startTracker() {
try {
await tracker.start();
console.log('✅ Tracker started successfully');
} catch (error) {
console.error('❌ Critical error:', error.message);
// Send alert (email, Slack, etc.)
await sendAlert(`Tracker failed: ${error.message}`);
process.exit(1);
}
}
// Graceful shutdown with cleanup
async function shutdown() {
console.log('Received shutdown signal');
tracker.stop();
// Wait for current operation to complete
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('Tracker stopped. Goodbye!');
process.exit(0);
}
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
// Start the tracker
startTracker();One-Time Stats Fetch
Fetch current stats without starting the continuous tracker:
const RobloxStatsTracker = require('rostatstrack');
const tracker = new RobloxStatsTracker({
universeId: 1931573465,
groupId: 2880815
});
async function getCurrentStats() {
try {
const stats = await tracker.fetchRobloxStats();
console.log('Current Statistics:');
console.log('─────────────────────────────────');
console.log(`📅 Timestamp: ${stats.timestamp}`);
console.log(`👁️ Visits: ${stats.visits?.toLocaleString() || 'N/A'}`);
console.log(`⭐ Favorites: ${stats.favorites?.toLocaleString() || 'N/A'}`);
console.log(`🎮 Active Players: ${stats.players?.toLocaleString() || 'N/A'}`);
console.log(`👥 Group Members: ${stats.members?.toLocaleString() || 'N/A'}`);
console.log(`👍 Upvotes: ${stats.upVotes?.toLocaleString() || 'N/A'}`);
console.log(`👎 Downvotes: ${stats.downVotes?.toLocaleString() || 'N/A'}`);
return stats;
} catch (error) {
console.error('Failed to fetch stats:', error.message);
throw error;
}
}
getCurrentStats();Integration with Express.js API
const express = require('express');
const RobloxStatsTracker = require('rostatstrack');
const app = express();
const tracker = new RobloxStatsTracker({
universeId: 1931573465,
groupId: 2880815
});
// Endpoint to get current stats
app.get('/api/stats/current', async (req, res) => {
try {
const stats = await tracker.fetchRobloxStats();
res.json({
success: true,
data: stats
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Endpoint to start tracker
app.post('/api/tracker/start', async (req, res) => {
try {
if (tracker.isRunning) {
return res.status(400).json({
success: false,
error: 'Tracker is already running'
});
}
tracker.start();
res.json({
success: true,
message: 'Tracker started successfully'
});
} catch (error) {
res.status(500).json({
success: false,
error: error.message
});
}
});
// Endpoint to stop tracker
app.post('/api/tracker/stop', (req, res) => {
tracker.stop();
res.json({
success: true,
message: 'Tracker stopped'
});
});
app.listen(3000, () => {
console.log('API running on http://localhost:3000');
});📖 API Reference
Class: RobloxStatsTracker
Main class for tracking ROBLOX statistics.
Constructor
new RobloxStatsTracker(config)Parameters:
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| config | Object | Yes | - | Configuration object |
| config.universeId | Number | Yes | - | ROBLOX game universe ID |
| config.groupId | Number | Yes | - | ROBLOX group ID |
| config.saveInterval | Number | No | 300000 | Interval between saves (ms), min: 60000 |
| config.savesDir | String | No | './saves' | Directory for CSV files |
| config.logDir | String | No | './logs' | Directory for log files |
| config.logFileName | String | No | 'latest.log' | Name of current log file |
Throws:
Error- If configuration validation fails
Example:
const tracker = new RobloxStatsTracker({
universeId: 1931573465,
groupId: 2880815,
saveInterval: 10 * 60 * 1000 // 10 minutes
});Method: start()
Starts the continuous stats tracking loop.
tracker.start() → Promise<void>Returns: Promise<void> - Resolves when tracker stops
Throws:
Error- If tracker is already runningError- On critical failures (network, filesystem, etc.)
Behavior:
- Creates necessary directories
- Initializes log file (archives existing one)
- Fetches initial stats immediately
- Waits until next interval mark
- Continues fetching at configured intervals
Example:
try {
await tracker.start();
} catch (error) {
console.error('Tracker failed:', error);
}Method: stop()
Requests the tracker to stop gracefully.
tracker.stop() → voidReturns: void
Behavior:
- Sets stop flag
- Allows current iteration to complete
- Exits loop cleanly
Example:
process.on('SIGINT', () => {
console.log('Stopping tracker...');
tracker.stop();
});Method: fetchRobloxStats()
Fetches current statistics from ROBLOX APIs.
tracker.fetchRobloxStats() → Promise<Stats>Returns: Promise<Stats> - Statistics object
Stats Object:
{
timestamp: string; // ISO 8601 timestamp
visits: number | null; // Total game visits
favorites: number | null; // Total favorites
players: number | null; // Current active players
members: number | null; // Group member count
upVotes: number | null; // Total upvotes
downVotes: number | null; // Total downvotes
}Note: Values are null if the API call fails
Example:
const stats = await tracker.fetchRobloxStats();
console.log(`Current players: ${stats.players}`);Method: saveDataToCSV(data)
Saves statistics to a CSV file.
tracker.saveDataToCSV(data) → Promise<void>Parameters:
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| data | Stats | Yes | Statistics object to save |
Returns: Promise<void>
Behavior:
- Creates daily CSV files (format:
YYYY-MM-DD.csv) - Adds headers if file is new
- Appends data as new row
Example:
const stats = await tracker.fetchRobloxStats();
await tracker.saveDataToCSV(stats);Property: isRunning
Indicates if the tracker is currently running.
tracker.isRunning → booleanType: boolean
Read-only: Yes
Example:
if (tracker.isRunning) {
console.log('Tracker is active');
}📁 Data Format
CSV Structure
Daily CSV files are created in the saves directory with this structure:
Timestamp,Visits,Favorites,Players,Members,UpVotes,DownVotes
2024-03-20T12:00:00.000Z,1000000,50000,150,5000,75000,2500
2024-03-20T12:05:00.000Z,1000025,50001,148,5001,75005,2501
2024-03-20T12:10:00.000Z,1000051,50003,152,5001,75012,2502File Organization
project/
├── saves/
│ ├── 2024-03-20.csv
│ ├── 2024-03-21.csv
│ └── 2024-03-22.csv
└── logs/
├── latest.log
├── 2024-03-19T14-23-45-123Z.log
└── 2024-03-20T08-15-30-456Z.logLog Format
2024-03-20T12:00:00.123Z - ROBLOX Stats Tracker started
2024-03-20T12:00:00.456Z - game API call successful. Status: 200
2024-03-20T12:00:00.789Z - group API call successful. Status: 200
2024-03-20T12:00:01.012Z - votes API call successful. Status: 200
2024-03-20T12:00:01.234Z - Data saved successfully to 2024-03-20.csv🔍 Data Analysis
Using CSViewer (Recommended)
CSViewer is a fast, lightweight CSV viewer perfect for analyzing your data:
- Download from csviewer.com
- Open your daily CSV files
- Use filtering and sorting features
- Export charts and visualizations
Why CSViewer?
- ✅ Free for commercial use
- ✅ Handles large files efficiently
- ✅ Built-in charting capabilities
- ✅ No installation required (portable)
Using Excel/Google Sheets
Import CSV files to create custom charts and pivot tables:
Excel:
- Open Excel
- File → Import → CSV file
- Select your CSV file
- Create charts from the Insert tab
Google Sheets:
- Open Google Sheets
- File → Import → Upload
- Select your CSV file
- Use built-in charting tools
Using Python/Pandas
import pandas as pd
import matplotlib.pyplot as plt
# Load data
df = pd.read_csv('saves/2024-03-20.csv')
df['Timestamp'] = pd.to_datetime(df['Timestamp'])
# Plot visits over time
plt.figure(figsize=(12, 6))
plt.plot(df['Timestamp'], df['Visits'])
plt.title('Game Visits Over Time')
plt.xlabel('Time')
plt.ylabel('Total Visits')
plt.xticks(rotation=45)
plt.tight_layout()
plt.show()Using R
library(ggplot2)
library(readr)
# Load data
data <- read_csv('saves/2024-03-20.csv')
data$Timestamp <- as.POSIXct(data$Timestamp)
# Plot active players
ggplot(data, aes(x=Timestamp, y=Players)) +
geom_line(color='blue') +
labs(title='Active Players Over Time',
x='Time', y='Players') +
theme_minimal()⚙️ Configuration Guide
Minimal Configuration
module.exports = {
universeId: 1931573465,
groupId: 2880815
};Production Configuration
const path = require("path");
const os = require("os");
module.exports = {
// ROBLOX IDs
universeId: parseInt(process.env.ROBLOX_UNIVERSE_ID) || 1931573465,
groupId: parseInt(process.env.ROBLOX_GROUP_ID) || 2880815,
// Collection interval: 10 minutes
saveInterval: 10 * 60 * 1000,
// Use dedicated data directory
savesDir: path.join(__dirname, 'data', 'csv'),
logDir: path.join(__dirname, 'data', 'logs'),
// Custom log file name
logFileName: `tracker-${os.hostname()}.log`
};Environment Variables
Create a .env file:
ROBLOX_UNIVERSE_ID=1931573465
ROBLOX_GROUP_ID=2880815
SAVE_INTERVAL=300000
DATA_DIR=/var/data/rostatstrackLoad in config.js:
require('dotenv').config();
const path = require('path');
const dataDir = process.env.DATA_DIR || __dirname;
module.exports = {
universeId: parseInt(process.env.ROBLOX_UNIVERSE_ID),
groupId: parseInt(process.env.ROBLOX_GROUP_ID),
saveInterval: parseInt(process.env.SAVE_INTERVAL) || 300000,
savesDir: path.join(dataDir, 'saves'),
logDir: path.join(dataDir, 'logs'),
logFileName: 'latest.log'
};Interval Guidelines
| Interval | Use Case | Daily Data Points | |----------|----------|-------------------| | 1 minute | High-frequency monitoring | 1,440 | | 5 minutes | Standard tracking (default) | 288 | | 10 minutes | Low-frequency tracking | 144 | | 15 minutes | Minimal resource usage | 96 | | 30 minutes | Very light tracking | 48 | | 1 hour | Historical trends only | 24 |
Recommendation: 5-10 minutes balances detail with API respect
🐛 Troubleshooting
Common Issues
"Invalid configuration" Error
Problem: Config validation failed
Solution:
// Ensure universeId and groupId are positive integers
module.exports = {
universeId: 1931573465, // ✅ Number, not string
groupId: 2880815, // ✅ Number, not string
saveInterval: 300000 // ✅ At least 60000 (1 minute)
};"Tracker is already running" Error
Problem: Called start() twice
Solution:
if (!tracker.isRunning) {
await tracker.start();
}API Call Failures
Problem: ROBLOX API returning errors
Symptoms in logs:
Error in game API call: Request failed with status code 429Solutions:
- Rate Limited (429): Increase
saveIntervalto 10+ minutes - Server Errors (500-504): Tracker auto-retries, wait for recovery
- Invalid IDs (400): Verify your universe ID and group ID are correct
No Data Being Saved
Problem: CSV files not created or empty
Checks:
- Check log file for errors:
cat logs/latest.log - Verify write permissions:
ls -la saves/ - Check disk space:
df -h - Confirm API calls succeed in logs
Process Crashes
Problem: Tracker stops unexpectedly
Solutions:
- Use PM2 for auto-restart:
pm2 start bin/cli.js --name rostatstrack --max-restarts 10- Check system resources:
free -m # Check memory
df -h # Check disk space- Review logs:
tail -f logs/latest.logDebug Mode
Enable verbose logging:
const tracker = new RobloxStatsTracker({ /* config */ });
// Add custom logging
const originalLog = tracker.log.bind(tracker);
tracker.log = async function(message) {
console.log('[DEBUG]', message);
await originalLog(message);
};
await tracker.start();❓ FAQ
General Questions
Q: Is this allowed by ROBLOX Terms of Service?
A: Yes! This uses public ROBLOX APIs that are documented and intended for developer use. We respect rate limits and follow best practices.
Q: How much data will this generate?
A: At 5-minute intervals, approximately:
- ~1 KB per data point
- ~300 KB per day
- ~9 MB per month
- ~110 MB per year
Q: Can I track multiple games?
A: Yes! Create separate tracker instances:
const game1 = new RobloxStatsTracker({
universeId: 111111,
groupId: 222222,
savesDir: './saves/game1'
});
const game2 = new RobloxStatsTracker({
universeId: 333333,
groupId: 444444,
savesDir: './saves/game2'
});
await Promise.all([
game1.start(),
game2.start()
]);Q: Does this work with private/group-only games?
A: Yes! The ROBLOX APIs used are public and work for all game types.
Technical Questions
Q: Does this use webhooks or polling?
A: Polling. It fetches data at regular intervals from ROBLOX's public APIs.
Q: What happens if my server restarts?
A: The tracker will need to be restarted. Use PM2 or systemd for auto-restart.
Q: Can I change the CSV format?
A: Yes! Extend the class and override saveDataToCSV():
class CustomTracker extends RobloxStatsTracker {
async saveDataToCSV(data) {
// Your custom CSV logic
}
}Q: Is there a way to send alerts for certain thresholds?
A: Yes! Implement custom logic:
const stats = await tracker.fetchRobloxStats();
if (stats.players > 1000) {
await sendSlackAlert('🎉 Over 1000 concurrent players!');
}Q: Can I export to a database instead of CSV?
A: Yes! Override or extend the save method:
class DatabaseTracker extends RobloxStatsTracker {
async saveDataToCSV(data) {
await super.saveDataToCSV(data); // Keep CSV
await this.saveToDatabase(data); // Also save to DB
}
async saveToDatabase(data) {
await db.query(
'INSERT INTO stats (timestamp, visits, favorites, ...) VALUES (?, ?, ?, ...)',
[data.timestamp, data.visits, data.favorites, ...]
);
}
}🛠️ Development
Building from Source
git clone https://github.com/yourusername/roblox-stats-tracker.git
cd roblox-stats-tracker
npm installProject Structure
rostatstrack/
├── bin/
│ └── cli.js # CLI entry point
├── utils/
│ ├── apiHandler.js # API retry logic
│ └── validation.js # Config validation
├── index.js # Main library export
├── main.js # Legacy standalone script
├── config.js # Example configuration
├── package.json # Package metadata
└── README.md # This fileRunning Tests
npm testContributing
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Quick Contribution Guide:
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
TL;DR: You can use this package for any purpose, including commercial projects, as long as you include the license notice.
🙏 Acknowledgments
- ROBLOX for providing public APIs
- axios team for the excellent HTTP client
- All contributors and users of this package
📬 Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: [email protected]
🔗 Links
Made with ❤️ for the ROBLOX developer community
⭐ Star us on GitHub if this project helped you!
