Rocket.Chat

CommunicationOpen SourceSelf-HostedBot DevelopmentAPIWebhookDocker

Communication Tool

Rocket.Chat

Overview

Rocket.Chat is an open-source enterprise team collaboration platform. With a modern Slack-like UI while being completely operable in self-hosted environments, it's ideal for enterprises prioritizing data sovereignty. It offers rich API, Webhook integration features, custom app development through Apps-Engine, and Docker support for advanced customization.

Details

Rocket.Chat is an open-source team collaboration platform released in 2015. Developed in JavaScript (Meteor framework), it has gained adoption in enterprises and development teams as a self-hostable Slack alternative. It's particularly suitable for organizations prioritizing security and privacy.

In 2024-2025, major upgrades were made including migration to Node.js 20.x and Meteor 3.0, MongoDB 7.0 support addition, E2EE (End-to-End Encryption) enhancement, and introduction of a new bot and app development framework through Apps-Engine. Traditional Bots integration features are deprecated, and Apps-Engine-based development is now recommended.

Rocket.Chat API provides REST API, Realtime API, and Livechat API, enabling advanced integration with external systems through comprehensive Webhook functionality and custom script execution. With Docker & Docker Compose easy deployment, rich plugin ecosystem, and MongoDB integration, you can build scalable and extensible chat platforms.

Pros and Cons

Pros

  • Completely Open Source: Source code availability, free customization
  • Self-Host Support: Complete own-environment operation, data sovereignty assurance
  • Rich APIs: REST API, Realtime API, Livechat API
  • Apps-Engine: Powerful app and bot development framework
  • E2EE Support: End-to-end encryption for security
  • Webhook Features: Comprehensive webhook integration and custom scripts
  • Docker Support: Easy deployment and containerization
  • Cost Efficiency: Unlimited usage in self-hosted environments

Cons

  • Operational Costs: Need for server construction and maintenance management
  • Technical Requirements: Technical demands for setup and customization
  • UI/UX: Usability challenges compared to Slack
  • Plugin Ecosystem: Limited third-party integration scope
  • Mobile Experience: Native app feature limitations
  • Learning Curve: Learning costs for Apps-Engine development

Key Links

Code Examples

Rocket.Chat Apps-Engine App Development

// apps/sample-app/SampleApp.ts - Apps-Engine app main
import {
    IConfigurationExtend,
    IEnvironmentRead,
    ILogger,
    IRead,
    IModify,
    IPersistence,
    IHttp,
} from '@rocket.chat/apps-engine/definition/accessors';
import { App } from '@rocket.chat/apps-engine/definition/App';
import { IAppInfo } from '@rocket.chat/apps-engine/definition/metadata';
import {
    IMessage,
    IPreMessageSentPrevent,
    IPostMessageSent,
} from '@rocket.chat/apps-engine/definition/messages';
import { ISlashCommand, SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands';

export class SampleApp extends App implements IPreMessageSentPrevent, IPostMessageSent {
    constructor(info: IAppInfo, logger: ILogger) {
        super(info, logger);
    }

    // App initialization configuration
    public async initialize(configurationExtend: IConfigurationExtend, environmentRead: IEnvironmentRead): Promise<void> {
        // Register slash command
        await configurationExtend.slashCommands.provideSlashCommand(new SampleSlashCommand());
        
        // Register API endpoint
        await configurationExtend.api.provideApi({
            visibility: ApiVisibility.PUBLIC,
            security: ApiSecurity.UNSECURE,
            endpoints: [new SampleEndpoint(this)],
        });
    }

    // Pre-message processing
    public async executePreMessageSentPrevent(
        message: IMessage,
        read: IRead,
        http: IHttp,
        persistence: IPersistence,
    ): Promise<boolean> {
        // Block specific keywords
        if (message.text && message.text.includes('blocked-word')) {
            return true; // Block message
        }
        return false; // Allow message
    }

    // Post-message processing
    public async executePostMessageSent(
        message: IMessage,
        read: IRead,
        http: IHttp,
        persistence: IPersistence,
        modify: IModify,
    ): Promise<void> {
        // Auto-response implementation
        if (message.text && message.text.includes('help')) {
            const messageBuilder = modify.getCreator().startMessage()
                .setSender(await read.getUserReader().getById('rocket.cat'))
                .setRoom(message.room)
                .setText('🚀 Help information sent!');
            
            await modify.getCreator().finish(messageBuilder);
        }

        // External API integration
        if (message.text && message.text.includes('weather')) {
            await this.sendWeatherInfo(message, modify, http, read);
        }
    }

    private async sendWeatherInfo(
        message: IMessage,
        modify: IModify,
        http: IHttp,
        read: IRead,
    ): Promise<void> {
        try {
            // External weather API call
            const response = await http.get('https://api.openweathermap.org/data/2.5/weather', {
                params: {
                    q: 'Tokyo',
                    appid: 'your-api-key',
                    units: 'metric',
                },
            });

            if (response.statusCode === 200 && response.data) {
                const weather = response.data;
                const messageBuilder = modify.getCreator().startMessage()
                    .setSender(await read.getUserReader().getById('rocket.cat'))
                    .setRoom(message.room)
                    .setText(`🌤️ Tokyo Weather: ${weather.weather[0].description}, Temperature: ${weather.main.temp}°C`);

                await modify.getCreator().finish(messageBuilder);
            }
        } catch (error) {
            console.error('Weather API error:', error);
        }
    }
}

// Slash command implementation
class SampleSlashCommand implements ISlashCommand {
    public command = 'sample';
    public i18nParamsExample = 'sample_params';
    public i18nDescription = 'sample_description';
    public providesPreview = false;

    public async executor(context: SlashCommandContext, read: IRead, modify: IModify): Promise<void> {
        const messageBuilder = modify.getCreator().startMessage()
            .setSender(context.getSender())
            .setRoom(context.getRoom())
            .setText('🚀 Sample command executed!');

        await modify.getCreator().finish(messageBuilder);
    }
}

Webhook Integration Script

// Incoming Webhook script - GitHub integration example
class Script {
    process_incoming_request({ request }) {
        console.log('GitHub Webhook received:', request.content);
        
        const eventType = request.headers['x-github-event'];
        
        switch (eventType) {
            case 'push':
                return this.handlePushEvent(request.content);
            case 'pull_request':
                return this.handlePullRequestEvent(request.content);
            case 'issues':
                return this.handleIssueEvent(request.content);
            default:
                return this.handleUnknownEvent(eventType);
        }
    }

    handlePushEvent(payload) {
        const commits = payload.commits;
        const repository = payload.repository;
        const pusher = payload.pusher;

        let commitMessages = commits.map(commit => {
            const shortId = commit.id.substring(0, 7);
            return `• [${shortId}](${commit.url}) ${commit.message}`;
        }).join('\n');

        return {
            content: {
                username: 'GitHub',
                icon_url: 'https://github.com/fluidicon.png',
                text: `🚀 **Push to ${repository.full_name}**`,
                attachments: [{
                    color: '#28a745',
                    title: `${commits.length} commits pushed by ${pusher.name}`,
                    text: commitMessages,
                    title_link: payload.compare,
                    fields: [
                        {
                            title: 'Repository',
                            value: `[${repository.full_name}](${repository.html_url})`,
                            short: true
                        },
                        {
                            title: 'Branch',
                            value: payload.ref.split('/').pop(),
                            short: true
                        }
                    ]
                }]
            }
        };
    }

    handlePullRequestEvent(payload) {
        const pr = payload.pull_request;
        const action = payload.action;
        
        const colors = {
            opened: '#28a745',
            closed: pr.merged ? '#6f42c1' : '#d73a49',
            reopened: '#28a745'
        };

        const emoji = {
            opened: '📝',
            closed: pr.merged ? '🎉' : '❌',
            reopened: '🔄'
        };

        return {
            content: {
                username: 'GitHub',
                icon_url: payload.sender.avatar_url,
                text: `${emoji[action]} **Pull Request ${action}**`,
                attachments: [{
                    color: colors[action] || '#0366d6',
                    title: `#${pr.number} - ${pr.title}`,
                    title_link: pr.html_url,
                    text: pr.body,
                    author_name: pr.user.login,
                    author_link: pr.user.html_url,
                    author_icon: pr.user.avatar_url,
                    fields: [
                        {
                            title: 'Repository',
                            value: `[${payload.repository.full_name}](${payload.repository.html_url})`,
                            short: true
                        },
                        {
                            title: 'Branch',
                            value: `${pr.head.ref}${pr.base.ref}`,
                            short: true
                        }
                    ]
                }]
            }
        };
    }

    handleIssueEvent(payload) {
        const issue = payload.issue;
        const action = payload.action;

        return {
            content: {
                username: 'GitHub',
                icon_url: payload.sender.avatar_url,
                text: `🐛 **Issue ${action}**`,
                attachments: [{
                    color: action === 'closed' ? '#28a745' : '#d73a49',
                    title: `#${issue.number} - ${issue.title}`,
                    title_link: issue.html_url,
                    text: issue.body,
                    author_name: issue.user.login,
                    author_link: issue.user.html_url,
                    author_icon: issue.user.avatar_url
                }]
            }
        };
    }

    handleUnknownEvent(eventType) {
        return {
            content: {
                text: `⚠️ Unknown GitHub event: ${eventType}`
            }
        };
    }
}

Outgoing Webhook Script

// Outgoing Webhook script - External service integration
class Script {
    prepare_outgoing_request({ request }) {
        const message = request.data.text;
        const command = message.split(' ')[0];

        switch (command) {
            case '/weather':
                return this.prepareWeatherRequest(request);
            case '/translate':
                return this.prepareTranslateRequest(request);
            case '/jira':
                return this.prepareJiraRequest(request);
            case '/deploy':
                return this.prepareDeployRequest(request);
            default:
                return this.prepareHelpResponse();
        }
    }

    prepareWeatherRequest(request) {
        const city = request.data.text.split(' ')[1] || 'Tokyo';
        
        return {
            url: 'https://api.openweathermap.org/data/2.5/weather',
            method: 'GET',
            params: {
                q: city,
                appid: 'your-weather-api-key',
                units: 'metric'
            }
        };
    }

    prepareTranslateRequest(request) {
        const text = request.data.text.replace('/translate', '').trim();
        
        return {
            url: 'https://api.mymemory.translated.net/get',
            method: 'GET',
            params: {
                q: text,
                langpair: 'ja|en'
            }
        };
    }

    prepareJiraRequest(request) {
        const issueKey = request.data.text.split(' ')[1];
        
        return {
            url: `https://your-domain.atlassian.net/rest/api/2/issue/${issueKey}`,
            method: 'GET',
            headers: {
                'Authorization': `Bearer ${process.env.JIRA_TOKEN}`,
                'Content-Type': 'application/json'
            }
        };
    }

    prepareDeployRequest(request) {
        const environment = request.data.text.split(' ')[1] || 'staging';
        
        return {
            url: 'https://api.your-deploy-service.com/deploy',
            method: 'POST',
            headers: {
                'Authorization': `Bearer ${process.env.DEPLOY_TOKEN}`,
                'Content-Type': 'application/json'
            },
            data: {
                environment: environment,
                branch: 'main',
                user: request.data.user_name
            }
        };
    }

    process_outgoing_response({ request, response }) {
        const originalMessage = request.data.text;
        const command = originalMessage.split(' ')[0];

        switch (command) {
            case '/weather':
                return this.processWeatherResponse(response);
            case '/translate':
                return this.processTranslateResponse(response);
            case '/jira':
                return this.processJiraResponse(response);
            case '/deploy':
                return this.processDeployResponse(response);
            default:
                return { content: { text: 'Unknown command response' } };
        }
    }

    processWeatherResponse(response) {
        if (response.status_code === 200) {
            const weather = response.content;
            return {
                content: {
                    text: `🌤️ **Weather in ${weather.name}**`,
                    attachments: [{
                        color: '#87CEEB',
                        fields: [
                            {
                                title: 'Weather',
                                value: weather.weather[0].description,
                                short: true
                            },
                            {
                                title: 'Temperature',
                                value: `${weather.main.temp}°C`,
                                short: true
                            },
                            {
                                title: 'Humidity',
                                value: `${weather.main.humidity}%`,
                                short: true
                            },
                            {
                                title: 'Wind Speed',
                                value: `${weather.wind.speed} m/s`,
                                short: true
                            }
                        ]
                    }]
                }
            };
        } else {
            return {
                content: {
                    text: '❌ Failed to fetch weather information'
                }
            };
        }
    }

    processTranslateResponse(response) {
        if (response.status_code === 200) {
            const translation = response.content.responseData.translatedText;
            return {
                content: {
                    text: `🔤 **Translation Result**: ${translation}`
                }
            };
        } else {
            return {
                content: {
                    text: '❌ Translation failed'
                }
            };
        }
    }

    processJiraResponse(response) {
        if (response.status_code === 200) {
            const issue = response.content;
            return {
                content: {
                    text: `🎫 **JIRA Issue: ${issue.key}**`,
                    attachments: [{
                        color: '#0052CC',
                        title: issue.fields.summary,
                        title_link: `https://your-domain.atlassian.net/browse/${issue.key}`,
                        text: issue.fields.description,
                        fields: [
                            {
                                title: 'Status',
                                value: issue.fields.status.name,
                                short: true
                            },
                            {
                                title: 'Assignee',
                                value: issue.fields.assignee?.displayName || 'Unassigned',
                                short: true
                            }
                        ]
                    }]
                }
            };
        } else {
            return {
                content: {
                    text: '❌ Failed to fetch JIRA issue'
                }
            };
        }
    }

    processDeployResponse(response) {
        if (response.status_code === 200) {
            const deploy = response.content;
            return {
                content: {
                    text: `🚀 **Deployment Started**`,
                    attachments: [{
                        color: '#28a745',
                        fields: [
                            {
                                title: 'Environment',
                                value: deploy.environment,
                                short: true
                            },
                            {
                                title: 'Deploy ID',
                                value: deploy.id,
                                short: true
                            },
                            {
                                title: 'Status',
                                value: 'In Progress',
                                short: true
                            }
                        ]
                    }]
                }
            };
        } else {
            return {
                content: {
                    text: '❌ Failed to start deployment'
                }
            };
        }
    }
}

REST API Bot Development

// Node.js Rocket.Chat Bot implementation
const axios = require('axios');

class RocketChatBot {
    constructor(serverUrl, username, password) {
        this.serverUrl = serverUrl;
        this.username = username;
        this.password = password;
        this.userId = null;
        this.authToken = null;
    }

    // Login authentication
    async login() {
        try {
            const response = await axios.post(`${this.serverUrl}/api/v1/login`, {
                username: this.username,
                password: this.password
            });

            this.userId = response.data.data.userId;
            this.authToken = response.data.data.authToken;

            console.log('✅ Bot login successful');
            return true;
        } catch (error) {
            console.error('❌ Login failed:', error.response?.data || error.message);
            return false;
        }
    }

    // Send message
    async sendMessage(roomId, message, attachments = []) {
        try {
            const response = await axios.post(
                `${this.serverUrl}/api/v1/chat.postMessage`,
                {
                    roomId: roomId,
                    text: message,
                    attachments: attachments
                },
                {
                    headers: {
                        'X-Auth-Token': this.authToken,
                        'X-User-Id': this.userId
                    }
                }
            );

            return response.data;
        } catch (error) {
            console.error('Message send error:', error.response?.data || error.message);
            throw error;
        }
    }

    // Get channels list
    async getChannels() {
        try {
            const response = await axios.get(
                `${this.serverUrl}/api/v1/channels.list`,
                {
                    headers: {
                        'X-Auth-Token': this.authToken,
                        'X-User-Id': this.userId
                    }
                }
            );

            return response.data.channels;
        } catch (error) {
            console.error('Get channels error:', error.response?.data || error.message);
            throw error;
        }
    }

    // Get message history
    async getMessages(roomId, count = 50) {
        try {
            const response = await axios.get(
                `${this.serverUrl}/api/v1/channels.history`,
                {
                    params: {
                        roomId: roomId,
                        count: count
                    },
                    headers: {
                        'X-Auth-Token': this.authToken,
                        'X-User-Id': this.userId
                    }
                }
            );

            return response.data.messages;
        } catch (error) {
            console.error('Get messages error:', error.response?.data || error.message);
            throw error;
        }
    }

    // File upload
    async uploadFile(roomId, filePath, description = '') {
        const FormData = require('form-data');
        const fs = require('fs');
        
        const form = new FormData();
        form.append('file', fs.createReadStream(filePath));
        form.append('description', description);

        try {
            const response = await axios.post(
                `${this.serverUrl}/api/v1/rooms.upload/${roomId}`,
                form,
                {
                    headers: {
                        ...form.getHeaders(),
                        'X-Auth-Token': this.authToken,
                        'X-User-Id': this.userId
                    }
                }
            );

            return response.data;
        } catch (error) {
            console.error('File upload error:', error.response?.data || error.message);
            throw error;
        }
    }

    // Real-time message monitoring (WebSocket)
    startMessageListener() {
        const WebSocket = require('ws');
        const ws = new WebSocket(`${this.serverUrl.replace('http', 'ws')}/websocket`);

        ws.on('open', () => {
            console.log('WebSocket connected');
            
            // Authentication
            ws.send(JSON.stringify({
                msg: 'connect',
                version: '1',
                support: ['1']
            }));

            ws.send(JSON.stringify({
                msg: 'method',
                method: 'login',
                params: [{
                    resume: this.authToken
                }],
                id: '1'
            }));

            // Subscribe to message stream
            ws.send(JSON.stringify({
                msg: 'sub',
                id: 'messages',
                name: 'stream-notify-user',
                params: [`${this.userId}/message`, false]
            }));
        });

        ws.on('message', (data) => {
            try {
                const message = JSON.parse(data);
                if (message.msg === 'changed' && message.collection === 'stream-notify-user') {
                    this.handleIncomingMessage(message.fields.args[1]);
                }
            } catch (error) {
                console.error('WebSocket message parse error:', error);
            }
        });

        return ws;
    }

    async handleIncomingMessage(messageData) {
        // Ignore bot's own messages
        if (messageData.u._id === this.userId) return;

        const message = messageData.msg;
        const roomId = messageData.rid;
        const user = messageData.u;

        console.log(`📥 Message from ${user.username}: ${message}`);

        // Command processing
        if (message.startsWith('!')) {
            await this.processCommand(message, roomId, user);
        }

        // Keyword monitoring
        if (message.toLowerCase().includes('help')) {
            await this.sendHelpMessage(roomId);
        }
    }

    async processCommand(message, roomId, user) {
        const [command, ...args] = message.slice(1).split(' ');

        switch (command.toLowerCase()) {
            case 'status':
                await this.sendSystemStatus(roomId);
                break;
            case 'weather':
                await this.sendWeatherInfo(roomId, args[0] || 'Tokyo');
                break;
            case 'joke':
                await this.sendRandomJoke(roomId);
                break;
            default:
                await this.sendMessage(roomId, `❓ Unknown command: ${command}`);
        }
    }

    async sendSystemStatus(roomId) {
        const os = require('os');
        
        const statusMessage = {
            roomId: roomId,
            text: '📊 **System Status**',
            attachments: [{
                color: '#28a745',
                fields: [
                    {
                        title: 'Uptime',
                        value: `${Math.floor(os.uptime() / 3600)}h ${Math.floor((os.uptime() % 3600) / 60)}m`,
                        short: true
                    },
                    {
                        title: 'Memory Usage',
                        value: `${Math.round((os.totalmem() - os.freemem()) / os.totalmem() * 100)}%`,
                        short: true
                    },
                    {
                        title: 'Platform',
                        value: `${os.platform()} ${os.arch()}`,
                        short: true
                    },
                    {
                        title: 'Node Version',
                        value: process.version,
                        short: true
                    }
                ]
            }]
        };

        await this.sendMessage(statusMessage.roomId, statusMessage.text, statusMessage.attachments);
    }

    async sendHelpMessage(roomId) {
        const helpMessage = `
🤖 **Bot Commands**

\`!status\` - Display system status
\`!weather [city]\` - Get weather information
\`!joke\` - Display random joke
\`help\` - Show this help

**Other features:**
• Auto-response
• File monitoring
• External API integration
        `;

        await this.sendMessage(roomId, helpMessage);
    }
}

// Bot usage example
async function startBot() {
    const bot = new RocketChatBot(
        'https://your-rocketchat.com',
        'bot-username',
        'bot-password'
    );

    if (await bot.login()) {
        // Start WebSocket connection
        bot.startMessageListener();

        // Periodic task execution
        setInterval(async () => {
            const channels = await bot.getChannels();
            const generalChannel = channels.find(c => c.name === 'general');
            
            if (generalChannel) {
                await bot.sendMessage(
                    generalChannel._id,
                    '⏰ Periodic report: System is operating normally'
                );
            }
        }, 3600000); // Every hour
    }
}

startBot().catch(console.error);

Docker Compose Configuration

# docker-compose.yml
version: '3.8'

services:
  rocketchat:
    image: registry.rocket.chat/rocketchat/rocket.chat:${RELEASE:-latest}
    container_name: rocketchat
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.rocketchat.rule=Host(`${DOMAIN}`)"
      - "traefik.http.routers.rocketchat.tls=true"
      - "traefik.http.routers.rocketchat.entrypoints=https"
      - "traefik.http.routers.rocketchat.tls.certresolver=le"
    environment:
      MONGO_URL: "mongodb://mongodb:27017/rocketchat"
      MONGO_OPLOG_URL: "mongodb://mongodb:27017/local"
      ROOT_URL: "https://${DOMAIN}"
      PORT: 3000
      DEPLOY_METHOD: docker
      DEPLOY_PLATFORM: ${DEPLOY_PLATFORM:-}
      REG_TOKEN: ${REG_TOKEN:-}
    depends_on:
      - mongodb
    expose:
      - 3000
    volumes:
      - rocketchat_uploads:/app/uploads

  mongodb:
    image: docker.io/bitnami/mongodb:${MONGODB_VERSION:-5.0}
    container_name: mongodb
    restart: unless-stopped
    volumes:
      - mongodb_data:/bitnami/mongodb
    environment:
      MONGODB_REPLICA_SET_MODE: primary
      MONGODB_REPLICA_SET_NAME: rs0
      MONGODB_PORT_NUMBER: 27017
      MONGODB_INITIAL_PRIMARY_HOST: mongodb
      MONGODB_INITIAL_PRIMARY_PORT_NUMBER: 27017
      MONGODB_ADVERTISED_HOSTNAME: mongodb
      MONGODB_ENABLE_JOURNAL: true
      ALLOW_EMPTY_PASSWORD: yes

  # Traefik reverse proxy
  traefik:
    image: traefik:latest
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.https.address=:443"
      - "--certificatesresolvers.le.acme.tlschallenge=true"
      - "--certificatesresolvers.le.acme.email=${LETSENCRYPT_EMAIL}"
      - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "traefik_letsencrypt:/letsencrypt"

volumes:
  mongodb_data:
  rocketchat_uploads:
  traefik_letsencrypt:

networks:
  default:
    name: rocketchat_network

Environment Variables Configuration

# .env file
# Basic settings
DOMAIN=your-domain.com
[email protected]
RELEASE=6.0.0
MONGODB_VERSION=5.0

# Rocket.Chat settings
ROOT_URL=https://your-domain.com
PORT=3000
MONGO_URL=mongodb://mongodb:27017/rocketchat
MONGO_OPLOG_URL=mongodb://mongodb:27017/local

# Optional deployment settings
DEPLOY_METHOD=docker
DEPLOY_PLATFORM=
REG_TOKEN=

# API settings
ROCKETCHAT_URL=https://your-domain.com
ROCKETCHAT_USERNAME=admin
ROCKETCHAT_PASSWORD=your-admin-password

# Bot settings
BOT_USERNAME=automation-bot
BOT_PASSWORD=bot-password

# External API keys
WEATHER_API_KEY=your-weather-api-key
JIRA_TOKEN=your-jira-token
GITHUB_TOKEN=your-github-token

# Webhook URLs
SLACK_WEBHOOK_URL=your-slack-webhook-url
TEAMS_WEBHOOK_URL=your-teams-webhook-url

System Metrics Monitoring

// monitoring.js - System monitoring script
const { RocketChatBot } = require('./rocketchat-bot');
const os = require('os');

class SystemMonitor {
    constructor(bot, alertChannelId) {
        this.bot = bot;
        this.alertChannelId = alertChannelId;
        this.thresholds = {
            cpu: 80,
            memory: 85,
            disk: 90
        };
    }

    async startMonitoring() {
        setInterval(async () => {
            await this.checkSystemHealth();
        }, 300000); // Every 5 minutes

        console.log('🔍 System monitoring started');
    }

    async checkSystemHealth() {
        const metrics = await this.getSystemMetrics();
        
        if (this.shouldAlert(metrics)) {
            await this.sendAlert(metrics);
        }

        // Daily report (once a day)
        const now = new Date();
        if (now.getHours() === 9 && now.getMinutes() < 5) {
            await this.sendDailyReport(metrics);
        }
    }

    async getSystemMetrics() {
        const cpuUsage = await this.getCpuUsage();
        const memoryUsage = this.getMemoryUsage();
        const diskUsage = await this.getDiskUsage();

        return {
            cpu: cpuUsage,
            memory: memoryUsage,
            disk: diskUsage,
            uptime: os.uptime(),
            timestamp: new Date()
        };
    }

    async getCpuUsage() {
        const cpus = os.cpus();
        let totalIdle = 0;
        let totalTick = 0;

        cpus.forEach(cpu => {
            for (let type in cpu.times) {
                totalTick += cpu.times[type];
            }
            totalIdle += cpu.times.idle;
        });

        return Math.round(100 - (totalIdle / totalTick) * 100);
    }

    getMemoryUsage() {
        const total = os.totalmem();
        const free = os.freemem();
        return Math.round(((total - free) / total) * 100);
    }

    async getDiskUsage() {
        const { exec } = require('child_process');
        return new Promise((resolve) => {
            exec("df -h / | awk 'NR==2{print $5}' | sed 's/%//'", (error, stdout) => {
                resolve(error ? 0 : parseInt(stdout.trim()));
            });
        });
    }

    shouldAlert(metrics) {
        return metrics.cpu > this.thresholds.cpu ||
               metrics.memory > this.thresholds.memory ||
               metrics.disk > this.thresholds.disk;
    }

    async sendAlert(metrics) {
        const alertMessage = {
            text: '🚨 **System Alert**',
            attachments: [{
                color: '#d73a49',
                title: 'High resource usage detected',
                fields: [
                    {
                        title: 'CPU Usage',
                        value: `${metrics.cpu}%`,
                        short: true
                    },
                    {
                        title: 'Memory Usage',
                        value: `${metrics.memory}%`,
                        short: true
                    },
                    {
                        title: 'Disk Usage',
                        value: `${metrics.disk}%`,
                        short: true
                    },
                    {
                        title: 'Uptime',
                        value: `${Math.floor(metrics.uptime / 3600)}h ${Math.floor((metrics.uptime % 3600) / 60)}m`,
                        short: true
                    }
                ],
                timestamp: metrics.timestamp.toISOString()
            }]
        };

        await this.bot.sendMessage(
            this.alertChannelId,
            alertMessage.text,
            alertMessage.attachments
        );
    }
}

// Usage example
async function startMonitoring() {
    const bot = new RocketChatBot(
        process.env.ROCKETCHAT_URL,
        process.env.BOT_USERNAME,
        process.env.BOT_PASSWORD
    );

    if (await bot.login()) {
        const channels = await bot.getChannels();
        const alertChannel = channels.find(c => c.name === 'alerts');
        
        if (alertChannel) {
            const monitor = new SystemMonitor(bot, alertChannel._id);
            await monitor.startMonitoring();
        }
    }
}

startMonitoring().catch(console.error);