├── .env.example ├── .gitignore ├── README.md ├── bot.js ├── callback_handlers └── approveAccess.js ├── commands ├── cancel_shutdown.js ├── control.js ├── getip.js ├── index.js ├── killprocess.js ├── request_access.js ├── screenshot.js ├── secret.js ├── shutdown.js ├── stats.js └── upload.js ├── middleware ├── access.js └── firstUserAdmin.js ├── package-lock.json ├── package.json └── utils ├── accessControl.js └── screenshot.js /.env.example: -------------------------------------------------------------------------------- 1 | TELEGRAM_TOKEN=1234567890:ABC-YourTelegramBotTokenHere 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node modules 2 | node_modules/ 3 | 4 | # Environment variables 5 | .env 6 | 7 | # Logs 8 | *.log 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | 13 | # OS generated files 14 | .DS_Store 15 | Thumbs.db 16 | 17 | # IDE/editor directories 18 | .idea/ 19 | .vscode/ 20 | .history/ 21 | 22 | # User data files 23 | users.txt 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PC Robot Telegram Bot 2 | 3 | [![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) 4 | 5 | A Telegram bot that can control your PC through various commands such as playing/pausing media, taking screenshots, scheduling shutdowns, requesting and approving user access, fetching system stats, uploading files from the server, and more. 6 | 7 | **GitHub Repo:** [https://github.com/monokaijs/pc-robot-telegram-bot](https://github.com/monokaijs/pc-robot-telegram-bot) 8 | 9 | **Author:** [MonokaiJs](https://delimister.com) ([Telegram](https://t.me/delimister) | [Facebook](https://www.facebook.com/delimister)) 10 | 11 | ## Features 12 | 13 | - **Access Control:** 14 | The first user to interact with the bot becomes the admin. Other users must send `/request_access` to request permission, which the admin can approve. 15 | 16 | - **Media Control:** 17 | Control media playback (play/pause, next, previous, volume up/down, mute) remotely. 18 | 19 | - **Screenshots:** 20 | Capture screenshots of any monitor connected to the system and receive the images directly in Telegram. 21 | 22 | - **File Upload:** 23 | Upload files from the server's filesystem to the user via the bot. 24 | 25 | - **System Management:** 26 | Schedule or cancel system shutdowns, view system stats (CPU, RAM, top processes), and retrieve the server’s public IP. 27 | 28 | - **Command Hints:** 29 | When typing `/` in Telegram, the bot shows a list of available commands as hints. 30 | 31 | ## Requirements 32 | 33 | - **Node.js:** v14 or higher recommended. 34 | [Download Node.js](https://nodejs.org/en/download/) 35 | 36 | - **NPM:** v6 or higher. 37 | 38 | - **Telegram Bot Token:** From [BotFather](https://t.me/BotFather). 39 | 40 | - **Platform-Specific Tools:** 41 | - *Windows:* `DisplaySwitch.exe` for display switching, `taskkill` for ending processes. 42 | - *Linux/macOS:* `xrandr`, `ps`, `shutdown`, `pkill` commands if needed. 43 | 44 | ## Setup Instructions 45 | 46 | 1. **Clone the Repo:** 47 | ```bash 48 | git clone https://github.com/monokaijs/pc-robot-telegram-bot.git 49 | cd pc-robot-telegram-bot 50 | ``` 51 | 52 | 2. **Install Dependencies:** 53 | ```bash 54 | npm install 55 | ``` 56 | 57 | 3. **Create `.env` File:** 58 | ```bash 59 | cp .env.example .env 60 | ``` 61 | Edit `.env` and put your Telegram bot token: 62 | ``` 63 | TELEGRAM_TOKEN=1234567890:ABC-YourTelegramBotTokenHere 64 | ``` 65 | 66 | 4. **Run the Bot:** 67 | ```bash 68 | node bot.js 69 | ``` 70 | 71 | 5. **First Interaction:** 72 | The first user to send a message to the bot will become the admin. The admin can then approve other users who request access. 73 | 74 | ## File Structure 75 | 76 | - **bot.js:** Main entry point that initializes the bot, sets commands, loads middleware, and starts the bot. 77 | - **commands/**: Contains individual command files (e.g., `request_access.js`, `upload.js`, `stats.js`, etc.). 78 | - **middleware/**: Contains middleware like access checks and first-user-becomes-admin logic. 79 | - **callback_handlers/**: Contains handlers for inline callback queries (e.g., approving access requests). 80 | - **utils/**: Utility files for reading/writing user data and managing access control. 81 | 82 | ## Example Commands 83 | 84 | - `/request_access`: Request access if you’re not an admin or allowed user. 85 | - `/control`: Show media controls and screenshot options. 86 | - `/screenshot`: Capture and receive a screenshot. 87 | - `/upload `: Send a file located on the server. 88 | - `/cancel_shutdown`: Cancel any scheduled shutdown. 89 | - `/stats`: Display CPU load, memory usage, and top processes. 90 | - `/getip`: Get the server’s public IP. 91 | - `/secret`: A protected command only available to allowed users and admin. 92 | 93 | ## Running the Bot Automatically with Forever 94 | 95 | To ensure your Telegram bot runs continuously and starts automatically when your system boots, you can use `forever` (for Unix-based systems) or `forever-win` (for Windows). 96 | 97 | ### 1. Install Forever 98 | 99 | - **For Unix-based Systems (Linux/macOS):** 100 | ```bash 101 | npm install -g forever 102 | ``` 103 | 104 | - **For Windows:** 105 | ```bash 106 | npm install -g forever-win 107 | ``` 108 | 109 | ### 2. Start the Bot with Forever 110 | 111 | - **For Unix-based Systems:** 112 | ```bash 113 | forever start bot.js 114 | ``` 115 | 116 | - **For Windows:** 117 | ```bash 118 | forever-win start bot.js 119 | ``` 120 | 121 | ### 3. Set Up Automatic Startup 122 | 123 | - **For Unix-based Systems (Linux/macOS):** 124 | - **Using systemd:** 125 | 1. **Create a service file:** `/etc/systemd/system/pc-robot.service` 126 | 2. **Add the following content:** 127 | ```ini 128 | [Unit] 129 | Description=PC Robot Telegram Bot 130 | After=network.target 131 | 132 | [Service] 133 | ExecStart=/usr/local/bin/forever start /path/to/pc-robot-telegram-bot/bot.js 134 | Restart=always 135 | User=your_username 136 | Group=your_group 137 | Environment=PATH=/usr/bin:/usr/local/bin 138 | Environment=NODE_ENV=production 139 | WorkingDirectory=/path/to/pc-robot-telegram-bot 140 | 141 | [Install] 142 | WantedBy=multi-user.target 143 | ``` 144 | 3. **Reload systemd and enable the service:** 145 | ```bash 146 | sudo systemctl daemon-reload 147 | sudo systemctl enable pc-robot.service 148 | sudo systemctl start pc-robot.service 149 | ``` 150 | 151 | - **For Windows:** 152 | - **Using Task Scheduler:** 153 | 1. **Open Task Scheduler.** 154 | 2. **Create a new task** with the following settings: 155 | - **Trigger:** At system startup 156 | - **Action:** Start a program 157 | - **Program/script:** `forever-win` 158 | - **Add arguments:** `start bot.js` 159 | - **Start in:** `C:\path\to\pc-robot-telegram-bot` 160 | 3. **Save the task.** The bot will now start automatically when Windows boots. 161 | 162 | ### 4. Manage Forever Processes 163 | 164 | - **List running processes:** 165 | ```bash 166 | forever list 167 | ``` 168 | 169 | - **Stop the bot:** 170 | - **Unix-based Systems:** 171 | ```bash 172 | forever stop bot.js 173 | ``` 174 | - **Windows:** 175 | ```bash 176 | forever-win stop bot.js 177 | ``` 178 | 179 | - **Restart the bot:** 180 | - **Unix-based Systems:** 181 | ```bash 182 | forever restart bot.js 183 | ``` 184 | - **Windows:** 185 | ```bash 186 | forever-win restart bot.js 187 | ``` 188 | 189 | **Note:** 190 | - Forever ensures that your bot restarts automatically if it crashes or the system reboots. 191 | - Regularly check the logs to monitor the bot's performance and troubleshoot any issues. 192 | 193 | ## Error Handling & Stability 194 | 195 | - The bot uses `bot.catch()` to log errors and continue running, preventing crashes from unexpected issues. 196 | - Ensure proper permissions and paths exist for files and commands to avoid runtime errors. 197 | 198 | ```javascript 199 | bot.catch((err, ctx) => { 200 | console.error(`Bot Error: ${err}`, ctx.updateType); 201 | }); 202 | ``` 203 | 204 | ## Contributing 205 | 206 | 1. **Fork the repository.** 207 | 2. **Create your feature branch:** 208 | ```bash 209 | git checkout -b feature/new-feature 210 | ``` 211 | 3. **Commit your changes:** 212 | ```bash 213 | git commit -m 'Add new feature' 214 | ``` 215 | 4. **Push to the branch:** 216 | ```bash 217 | git push origin feature/new-feature 218 | ``` 219 | 5. **Open a Pull Request.** 220 | 221 | ## License 222 | 223 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 224 | -------------------------------------------------------------------------------- /bot.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const {Telegraf} = require('telegraf'); 3 | const {initUsers} = require('./utils/accessControl'); 4 | const approveAccessHandler = require('./callback_handlers/approveAccess'); 5 | 6 | const BOT_TOKEN = process.env.TELEGRAM_TOKEN; 7 | if (!BOT_TOKEN) { 8 | console.error('Error: TELEGRAM_TOKEN is not defined in .env file'); 9 | process.exit(1); 10 | } 11 | 12 | const bot = new Telegraf(BOT_TOKEN); 13 | 14 | initUsers(); 15 | 16 | const firstUserAdminMiddleware = require('./middleware/firstUserAdmin'); 17 | const accessMiddleware = require('./middleware/access'); 18 | 19 | bot.use(firstUserAdminMiddleware); 20 | bot.use(accessMiddleware); 21 | 22 | require('./commands')(bot); 23 | 24 | bot.on('callback_query', async (ctx) => approveAccessHandler(ctx, bot)); 25 | bot.catch((err, ctx) => { 26 | console.error(`Bot Error: ${err}`, ctx.updateType); 27 | }); 28 | 29 | bot.launch().then(async () => { 30 | await bot.telegram.setMyCommands([ 31 | {command: 'request_access', description: 'Request access to the bot'}, 32 | {command: 'secret', description: 'Access secret command (if allowed)'}, 33 | {command: 'upload', description: 'Send file from server: /upload [path]'}, 34 | {command: 'control', description: 'Control media playback and take screenshots'}, 35 | {command: 'screenshot', description: 'Take a screenshot of a screen'}, 36 | {command: 'cancel_shutdown', description: 'Cancel a scheduled shutdown'}, 37 | {command: 'stats', description: 'Show system stats and top processes'}, 38 | {command: 'getip', description: 'Show public IP of the server'}, 39 | ]); 40 | 41 | console.log('Bot startup successfully.'); 42 | }); 43 | -------------------------------------------------------------------------------- /callback_handlers/approveAccess.js: -------------------------------------------------------------------------------- 1 | const { isAdmin, addAllowedUser } = require('../utils/accessControl'); 2 | 3 | module.exports = async (ctx, bot) => { 4 | const callbackData = ctx.callbackQuery.data; 5 | const fromId = ctx.from.id.toString(); 6 | 7 | // Only admin can approve 8 | if (!isAdmin(fromId)) { 9 | await ctx.answerCbQuery('You are not authorized to approve.', { show_alert: true }); 10 | return; 11 | } 12 | 13 | if (callbackData.startsWith('approve_access_')) { 14 | const userIdToApprove = callbackData.replace('approve_access_', ''); 15 | addAllowedUser(userIdToApprove); 16 | 17 | await ctx.answerCbQuery('User approved!', { show_alert: false }); 18 | await ctx.editMessageText(`User ID ${userIdToApprove} has been granted access!`); 19 | await bot.telegram.sendMessage(userIdToApprove, 'Your access request has been approved!'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /commands/cancel_shutdown.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | module.exports = (bot) => { 4 | bot.command('cancel_shutdown', async (ctx) => { 5 | const platform = process.platform; // 'win32', 'darwin', 'linux' 6 | 7 | let command; 8 | if (platform === 'win32') { 9 | command = 'shutdown /a'; 10 | } else if (platform === 'darwin' || platform === 'linux') { 11 | command = 'sudo shutdown -c'; 12 | } else { 13 | await ctx.reply('Unsupported platform for canceling shutdown.'); 14 | return; 15 | } 16 | 17 | exec(command, (error) => { 18 | if (error) { 19 | console.error('Error canceling shutdown:', error); 20 | ctx.reply(`Failed to cancel shutdown: ${error.message}`); 21 | return; 22 | } 23 | ctx.reply('Successfully canceled the scheduled shutdown.'); 24 | }); 25 | }); 26 | }; 27 | -------------------------------------------------------------------------------- /commands/control.js: -------------------------------------------------------------------------------- 1 | const { Markup } = require('telegraf'); 2 | 3 | module.exports = (bot) => { 4 | bot.command('control', (ctx) => { 5 | ctx.reply( 6 | 'Use the buttons below to control your media playback or capture a screenshot.', 7 | Markup.inlineKeyboard([ 8 | [ 9 | Markup.button.callback('⏯️ Play/Pause', 'play_pause'), 10 | Markup.button.callback('⏭️ Next', 'next'), 11 | Markup.button.callback('⏮️ Previous', 'previous'), 12 | ], 13 | [ 14 | Markup.button.callback('🔊 Volume Up', 'volume_up'), 15 | Markup.button.callback('🔉 Volume Down', 'volume_down'), 16 | Markup.button.callback('🔇 Mute', 'mute'), 17 | ], 18 | [ 19 | Markup.button.callback('🖼️ Screenshot', 'select_screen'), 20 | ], 21 | ]) 22 | ); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /commands/getip.js: -------------------------------------------------------------------------------- 1 | const https = require('https'); 2 | 3 | function fetchPublicIP() { 4 | return new Promise((resolve, reject) => { 5 | https.get('https://api.ipify.org?format=json', (res) => { 6 | let data = ''; 7 | 8 | res.on('data', chunk => { 9 | data += chunk; 10 | }); 11 | 12 | res.on('end', () => { 13 | try { 14 | const json = JSON.parse(data); 15 | if (json.ip) { 16 | resolve(json.ip); 17 | } else { 18 | reject(new Error('No IP found in response')); 19 | } 20 | } catch (err) { 21 | reject(err); 22 | } 23 | }); 24 | }).on('error', (err) => { 25 | reject(err); 26 | }); 27 | }); 28 | } 29 | 30 | module.exports = (bot) => { 31 | bot.command('getip', async (ctx) => { 32 | try { 33 | const publicIP = await fetchPublicIP(); 34 | await ctx.reply(`Your server's public IP is: ${publicIP}`); 35 | } catch (error) { 36 | console.error('Error fetching public IP:', error); 37 | await ctx.reply('Failed to retrieve public IP.'); 38 | } 39 | }); 40 | }; 41 | -------------------------------------------------------------------------------- /commands/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (bot) => { 5 | const commandsPath = __dirname; 6 | const commandFiles = fs.readdirSync(commandsPath).filter(file => file.endsWith('.js') && file !== 'index.js'); 7 | 8 | for (const file of commandFiles) { 9 | const commandPath = path.join(commandsPath, file); 10 | const command = require(commandPath); 11 | command(bot); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /commands/killprocess.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | module.exports = (bot) => { 4 | bot.command('killprocess', async (ctx) => { 5 | const messageText = ctx.message.text; 6 | const parts = messageText.split(' '); 7 | if (parts.length < 2) { 8 | return ctx.reply('Please provide a process name. Usage: /killprocess '); 9 | } 10 | 11 | const processName = parts.slice(1).join(' '); 12 | const platform = process.platform; 13 | let command; 14 | 15 | if (platform === 'win32') { 16 | // On Windows, use taskkill 17 | // If the process name includes ".exe", keep it. Otherwise, append it if you know the exact process. 18 | // For generic usage, assume the user provides the full name: 19 | // Example: /killprocess notepad.exe 20 | command = `taskkill /IM "${processName}" /F`; 21 | } else if (platform === 'linux' || platform === 'darwin') { 22 | // On Linux/macOS, use pkill 23 | command = `pkill -f "${processName}"`; 24 | } else { 25 | return ctx.reply('Unsupported platform for killing processes.'); 26 | } 27 | 28 | exec(command, (error, stdout, stderr) => { 29 | if (error) { 30 | console.error('Error killing process:', error); 31 | return ctx.reply(`Failed to kill process: ${error.message}`); 32 | } 33 | 34 | // Check stdout/stderr for output that might indicate success or failure 35 | // If nothing is returned, just assume success. 36 | ctx.reply(`Attempted to kill process: ${processName}`); 37 | }); 38 | }); 39 | }; 40 | -------------------------------------------------------------------------------- /commands/request_access.js: -------------------------------------------------------------------------------- 1 | const { Markup } = require('telegraf'); 2 | const { isAdmin, isAllowedUser, getAdmin } = require('../utils/accessControl'); 3 | 4 | module.exports = (bot) => { 5 | bot.command('request_access', async (ctx) => { 6 | const fromId = ctx.from.id.toString(); 7 | if (isAllowedUser(fromId) || isAdmin(fromId)) { 8 | return ctx.reply('You already have access.'); 9 | } 10 | 11 | const adminId = getAdmin(); 12 | if (!adminId) { 13 | return ctx.reply('No admin is set yet. Please try again later.'); 14 | } 15 | 16 | const requestText = `User ID ${fromId} is requesting access. Approve?`; 17 | await ctx.telegram.sendMessage( 18 | adminId, 19 | requestText, 20 | Markup.inlineKeyboard([ 21 | [Markup.button.callback('Approve', `approve_access_${fromId}`)] 22 | ]) 23 | ); 24 | 25 | ctx.reply('Your request has been sent to the admin. Please wait for approval.'); 26 | }); 27 | }; 28 | -------------------------------------------------------------------------------- /commands/screenshot.js: -------------------------------------------------------------------------------- 1 | const { Markup } = require('telegraf'); 2 | const { getScreens, takeScreenshot, cleanupFile } = require('../utils/screenshot'); 3 | 4 | module.exports = (bot) => { 5 | // /screenshot command 6 | bot.command('screenshot', async (ctx) => { 7 | const screens = await getScreens(); 8 | 9 | if (screens.length === 1) { 10 | // Directly take a screenshot if there's only one screen 11 | try { 12 | const filePath = await takeScreenshot(screens[0].id); 13 | await ctx.replyWithPhoto({ source: filePath }); 14 | cleanupFile(filePath); 15 | } catch (error) { 16 | await ctx.reply('Failed to take a screenshot.'); 17 | } 18 | } else { 19 | // Show an inline keyboard with the list of screens, indexed from 1 20 | const buttons = screens.map((screen, index) => 21 | Markup.button.callback(`Screen ${index + 1}`, `screenshot_${screen.id}`) 22 | ); 23 | await ctx.reply('Select a screen to capture:', Markup.inlineKeyboard(buttons)); 24 | } 25 | }); 26 | 27 | // Handle callback queries related to screenshots 28 | bot.action('select_screen', async (ctx) => { 29 | const screens = await getScreens(); 30 | if (screens.length === 1) { 31 | try { 32 | const filePath = await takeScreenshot(screens[0].id); 33 | await ctx.replyWithPhoto({ source: filePath }); 34 | cleanupFile(filePath); 35 | } catch (error) { 36 | await ctx.reply('Failed to take a screenshot.'); 37 | } 38 | } else { 39 | const buttons = screens.map((screen, index) => 40 | Markup.button.callback(`Screen ${index + 1}`, `screenshot_${screen.id}`) 41 | ); 42 | await ctx.editMessageText('Select a screen to capture:', Markup.inlineKeyboard(buttons)); 43 | } 44 | 45 | await ctx.answerCbQuery(); 46 | }); 47 | 48 | bot.action(/^screenshot_(.*)$/, async (ctx) => { 49 | const screenId = ctx.match[1]; // captured group from the regex 50 | try { 51 | const filePath = await takeScreenshot(screenId); 52 | await ctx.replyWithPhoto({ source: filePath }); 53 | cleanupFile(filePath); 54 | } catch (error) { 55 | await ctx.reply('Failed to take a screenshot.'); 56 | } 57 | await ctx.answerCbQuery(); 58 | }); 59 | }; 60 | -------------------------------------------------------------------------------- /commands/secret.js: -------------------------------------------------------------------------------- 1 | module.exports = (bot) => { 2 | bot.command('secret', (ctx) => { 3 | ctx.reply('You have access to the secret command!'); 4 | }); 5 | }; 6 | -------------------------------------------------------------------------------- /commands/shutdown.js: -------------------------------------------------------------------------------- 1 | const { exec } = require('child_process'); 2 | 3 | async function scheduleShutdown(minutes, force = true) { 4 | const platform = process.platform; 5 | const time = parseInt(minutes, 10); 6 | 7 | if (isNaN(time) || time < 0) { 8 | throw new Error('Invalid time specified.'); 9 | } 10 | 11 | if (platform === 'win32') { 12 | const forceFlag = force ? ' /f' : ''; 13 | return new Promise((resolve, reject) => { 14 | exec(`shutdown /s /t ${time * 60}${forceFlag}`, (error) => { 15 | if (error) return reject(error); 16 | resolve(`System will shut down in ${time} minute(s).${force ? ' (Forced)' : ''}`); 17 | }); 18 | }); 19 | } else if (platform === 'darwin' || platform === 'linux') { 20 | return new Promise((resolve, reject) => { 21 | exec(`sudo shutdown -h +${time}`, (error) => { 22 | if (error) return reject(error); 23 | resolve(`System will shut down in ${time} minute(s).`); 24 | }); 25 | }); 26 | } else { 27 | throw new Error('Unsupported platform for shutdown command.'); 28 | } 29 | } 30 | 31 | module.exports = (bot) => { 32 | bot.command('shutdown', async (ctx) => { 33 | const messageText = ctx.message.text.trim(); 34 | const parts = messageText.split(' '); 35 | 36 | if (parts.length < 2) { 37 | return ctx.reply('Please specify the time in minutes. Usage: /shutdown '); 38 | } 39 | 40 | const minutes = parts[1]; 41 | 42 | try { 43 | const resultMessage = await scheduleShutdown(minutes, true); 44 | await ctx.answerCbQuery(resultMessage, { show_alert: true }); 45 | } catch (error) { 46 | console.error('Error scheduling shutdown:', error); 47 | await ctx.answerCbQuery(`Failed to schedule shutdown: ${error.message}`, { show_alert: true }); 48 | } 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /commands/stats.js: -------------------------------------------------------------------------------- 1 | const os = require('os'); 2 | const { exec } = require('child_process'); 3 | 4 | module.exports = (bot) => { 5 | bot.command('stats', async (ctx) => { 6 | try { 7 | // Gather CPU and memory stats 8 | const totalMem = os.totalmem(); 9 | const freeMem = os.freemem(); 10 | const usedMem = totalMem - freeMem; 11 | const memUsagePercent = ((usedMem / totalMem) * 100).toFixed(2); 12 | 13 | // CPU load average (On Windows this will be 0,0,0) 14 | const loadAvg = os.loadavg().map(v => v.toFixed(2)).join(', '); 15 | 16 | const platform = process.platform; 17 | let command; 18 | 19 | if (platform === 'win32') { 20 | command = `powershell -Command "Get-WmiObject Win32_PerfFormattedData_PerfProc_Process | Where-Object { $_.Name -notmatch '_Total|Idle|System' } | Sort-Object PercentProcessorTime -Descending | Select-Object -First 10 Name, PercentProcessorTime, WorkingSet"`; 21 | } else { 22 | // On Linux/macOS: top 10 CPU-consuming processes using ps 23 | // head -n 11: 1 line of header + 10 lines of processes 24 | command = 'ps -eo pid,comm,pcpu,pmem --sort=-pcpu | head -n 11'; 25 | } 26 | 27 | exec(command, (error, stdout, stderr) => { 28 | if (error) { 29 | console.error('Error fetching processes:', error); 30 | ctx.reply('Failed to retrieve process list.'); 31 | return; 32 | } 33 | 34 | let response = `*System Stats*\n`; 35 | response += `*CPU Load Avg:* ${loadAvg}\n`; 36 | response += `*Memory Usage:* ${memUsagePercent}% (Used ${(usedMem / (1024 * 1024)).toFixed(2)} MB / Total ${(totalMem / (1024 * 1024)).toFixed(2)} MB)\n\n`; 37 | response += `*Top 10 Processes:*\n`; 38 | 39 | if (platform === 'win32') { 40 | 41 | const lines = stdout.trim().split('\n'); 42 | const header = lines.shift(); 43 | response += '```\n' + header + '\n'; 44 | 45 | for (const line of lines) { 46 | const parts = line.trim().split(/\s+/); 47 | // Expecting something like: [Name, PercentProcessorTime, WorkingSet] 48 | if (parts.length >= 3) { 49 | const name = parts[0]; 50 | const cpu = parts[1]; 51 | const workingSetBytes = parseInt(parts[2], 10); 52 | const workingSetMB = (workingSetBytes / (1024 * 1024)).toFixed(2); 53 | response += `${name.padEnd(20)} ${cpu.padEnd(10)} ${workingSetMB}MB\n`; 54 | } else { 55 | // If we can't parse properly, just print the line as is. 56 | response += line + '\n'; 57 | } 58 | } 59 | 60 | response += '```\n'; 61 | } else { 62 | // On Linux/macOS, we trust ps output to be already formatted nicely 63 | response += '```\n' + stdout.trim() + '\n```\n'; 64 | } 65 | 66 | ctx.replyWithMarkdown(response); 67 | }); 68 | } catch (err) { 69 | console.error('Error retrieving stats:', err); 70 | ctx.reply('An error occurred while retrieving system stats.'); 71 | } 72 | }); 73 | }; 74 | -------------------------------------------------------------------------------- /commands/upload.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | 4 | module.exports = (bot) => { 5 | bot.command('upload', async (ctx) => { 6 | const messageText = ctx.message.text.trim(); 7 | const parts = messageText.split(' '); 8 | 9 | if (parts.length < 2) { 10 | return ctx.reply('Please specify a file path. Usage: /upload '); 11 | } 12 | 13 | const filePath = parts.slice(1).join(' '); 14 | const fullPath = path.resolve(filePath); // Resolve to absolute path if needed 15 | 16 | try { 17 | const stat = fs.statSync(fullPath); 18 | 19 | // Telegram maximum file size currently: 2GB = 2 * 1024 * 1024 * 1024 bytes 20 | const MAX_FILE_SIZE = 2 * 1024 * 1024 * 1024; 21 | 22 | if (stat.size > MAX_FILE_SIZE) { 23 | return ctx.reply('File is too large to send via Telegram (limit is 2GB).'); 24 | } 25 | 26 | // Send the file as a document 27 | await ctx.replyWithDocument({ source: fullPath }); 28 | } catch (error) { 29 | console.error('Error uploading file:', error); 30 | if (error.code === 'ENOENT') { 31 | ctx.reply('File not found. Please provide a valid file path.'); 32 | } else { 33 | ctx.reply('An error occurred while uploading the file.'); 34 | } 35 | } 36 | }); 37 | }; 38 | -------------------------------------------------------------------------------- /middleware/access.js: -------------------------------------------------------------------------------- 1 | const { isAdmin, isAllowedUser } = require('../utils/accessControl'); 2 | 3 | module.exports = (ctx, next) => { 4 | const fromId = ctx.from?.id; 5 | const messageText = ctx.message?.text || ''; 6 | const command = messageText.split(' ')[0]; 7 | 8 | // Always allow admin 9 | if (fromId && isAdmin(fromId.toString())) { 10 | return next(); 11 | } 12 | 13 | // Allow `/request_access` to non-allowed users 14 | if (command === '/request_access') { 15 | return next(); 16 | } 17 | 18 | // If user is not allowed and not admin 19 | if (fromId && !isAllowedUser(fromId.toString())) { 20 | return ctx.reply('Access denied. Please use /request_access to request permission.'); 21 | } 22 | 23 | return next(); 24 | }; 25 | -------------------------------------------------------------------------------- /middleware/firstUserAdmin.js: -------------------------------------------------------------------------------- 1 | const { getAdmin, setAdmin } = require('../utils/accessControl'); 2 | 3 | module.exports = (ctx, next) => { 4 | const currentAdmin = getAdmin(); 5 | if (!currentAdmin && ctx.from && ctx.from.id) { 6 | const userId = ctx.from.id.toString(); 7 | setAdmin(userId); 8 | ctx.reply('You are now the admin!'); 9 | } 10 | return next(); 11 | }; 12 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pc-remote-bot", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "pc-remote-bot", 9 | "version": "1.0.0", 10 | "dependencies": { 11 | "dotenv": "^16.4.7", 12 | "robotjs": "^0.6.0", 13 | "screenshot-desktop": "^1.15.0", 14 | "telegraf": "^4.16.3" 15 | } 16 | }, 17 | "node_modules/@telegraf/types": { 18 | "version": "7.1.0", 19 | "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz", 20 | "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==", 21 | "license": "MIT" 22 | }, 23 | "node_modules/abort-controller": { 24 | "version": "3.0.0", 25 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", 26 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", 27 | "license": "MIT", 28 | "dependencies": { 29 | "event-target-shim": "^5.0.0" 30 | }, 31 | "engines": { 32 | "node": ">=6.5" 33 | } 34 | }, 35 | "node_modules/ansi-regex": { 36 | "version": "2.1.1", 37 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", 38 | "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", 39 | "license": "MIT", 40 | "engines": { 41 | "node": ">=0.10.0" 42 | } 43 | }, 44 | "node_modules/aproba": { 45 | "version": "1.2.0", 46 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", 47 | "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", 48 | "license": "ISC" 49 | }, 50 | "node_modules/are-we-there-yet": { 51 | "version": "1.1.7", 52 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", 53 | "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", 54 | "deprecated": "This package is no longer supported.", 55 | "license": "ISC", 56 | "dependencies": { 57 | "delegates": "^1.0.0", 58 | "readable-stream": "^2.0.6" 59 | } 60 | }, 61 | "node_modules/balanced-match": { 62 | "version": "1.0.2", 63 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 64 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 65 | "license": "MIT" 66 | }, 67 | "node_modules/base64-js": { 68 | "version": "1.5.1", 69 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 70 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 71 | "funding": [ 72 | { 73 | "type": "github", 74 | "url": "https://github.com/sponsors/feross" 75 | }, 76 | { 77 | "type": "patreon", 78 | "url": "https://www.patreon.com/feross" 79 | }, 80 | { 81 | "type": "consulting", 82 | "url": "https://feross.org/support" 83 | } 84 | ], 85 | "license": "MIT" 86 | }, 87 | "node_modules/bl": { 88 | "version": "4.1.0", 89 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", 90 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", 91 | "license": "MIT", 92 | "dependencies": { 93 | "buffer": "^5.5.0", 94 | "inherits": "^2.0.4", 95 | "readable-stream": "^3.4.0" 96 | } 97 | }, 98 | "node_modules/bl/node_modules/readable-stream": { 99 | "version": "3.6.2", 100 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 101 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 102 | "license": "MIT", 103 | "dependencies": { 104 | "inherits": "^2.0.3", 105 | "string_decoder": "^1.1.1", 106 | "util-deprecate": "^1.0.1" 107 | }, 108 | "engines": { 109 | "node": ">= 6" 110 | } 111 | }, 112 | "node_modules/brace-expansion": { 113 | "version": "1.1.11", 114 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 115 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 116 | "license": "MIT", 117 | "dependencies": { 118 | "balanced-match": "^1.0.0", 119 | "concat-map": "0.0.1" 120 | } 121 | }, 122 | "node_modules/buffer": { 123 | "version": "5.7.1", 124 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", 125 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", 126 | "funding": [ 127 | { 128 | "type": "github", 129 | "url": "https://github.com/sponsors/feross" 130 | }, 131 | { 132 | "type": "patreon", 133 | "url": "https://www.patreon.com/feross" 134 | }, 135 | { 136 | "type": "consulting", 137 | "url": "https://feross.org/support" 138 | } 139 | ], 140 | "license": "MIT", 141 | "dependencies": { 142 | "base64-js": "^1.3.1", 143 | "ieee754": "^1.1.13" 144 | } 145 | }, 146 | "node_modules/buffer-alloc": { 147 | "version": "1.2.0", 148 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", 149 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", 150 | "license": "MIT", 151 | "dependencies": { 152 | "buffer-alloc-unsafe": "^1.1.0", 153 | "buffer-fill": "^1.0.0" 154 | } 155 | }, 156 | "node_modules/buffer-alloc-unsafe": { 157 | "version": "1.1.0", 158 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", 159 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", 160 | "license": "MIT" 161 | }, 162 | "node_modules/buffer-fill": { 163 | "version": "1.0.0", 164 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", 165 | "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", 166 | "license": "MIT" 167 | }, 168 | "node_modules/chownr": { 169 | "version": "1.1.4", 170 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", 171 | "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", 172 | "license": "ISC" 173 | }, 174 | "node_modules/code-point-at": { 175 | "version": "1.1.0", 176 | "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", 177 | "integrity": "sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", 178 | "license": "MIT", 179 | "engines": { 180 | "node": ">=0.10.0" 181 | } 182 | }, 183 | "node_modules/concat-map": { 184 | "version": "0.0.1", 185 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 186 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 187 | "license": "MIT" 188 | }, 189 | "node_modules/console-control-strings": { 190 | "version": "1.1.0", 191 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", 192 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", 193 | "license": "ISC" 194 | }, 195 | "node_modules/core-util-is": { 196 | "version": "1.0.3", 197 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", 198 | "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", 199 | "license": "MIT" 200 | }, 201 | "node_modules/debug": { 202 | "version": "4.4.0", 203 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 204 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 205 | "license": "MIT", 206 | "dependencies": { 207 | "ms": "^2.1.3" 208 | }, 209 | "engines": { 210 | "node": ">=6.0" 211 | }, 212 | "peerDependenciesMeta": { 213 | "supports-color": { 214 | "optional": true 215 | } 216 | } 217 | }, 218 | "node_modules/decompress-response": { 219 | "version": "4.2.1", 220 | "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", 221 | "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", 222 | "license": "MIT", 223 | "dependencies": { 224 | "mimic-response": "^2.0.0" 225 | }, 226 | "engines": { 227 | "node": ">=8" 228 | } 229 | }, 230 | "node_modules/deep-extend": { 231 | "version": "0.6.0", 232 | "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", 233 | "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", 234 | "license": "MIT", 235 | "engines": { 236 | "node": ">=4.0.0" 237 | } 238 | }, 239 | "node_modules/delegates": { 240 | "version": "1.0.0", 241 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 242 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", 243 | "license": "MIT" 244 | }, 245 | "node_modules/detect-libc": { 246 | "version": "1.0.3", 247 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", 248 | "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", 249 | "license": "Apache-2.0", 250 | "bin": { 251 | "detect-libc": "bin/detect-libc.js" 252 | }, 253 | "engines": { 254 | "node": ">=0.10" 255 | } 256 | }, 257 | "node_modules/dotenv": { 258 | "version": "16.4.7", 259 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", 260 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", 261 | "license": "BSD-2-Clause", 262 | "engines": { 263 | "node": ">=12" 264 | }, 265 | "funding": { 266 | "url": "https://dotenvx.com" 267 | } 268 | }, 269 | "node_modules/end-of-stream": { 270 | "version": "1.4.4", 271 | "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", 272 | "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", 273 | "license": "MIT", 274 | "dependencies": { 275 | "once": "^1.4.0" 276 | } 277 | }, 278 | "node_modules/event-target-shim": { 279 | "version": "5.0.1", 280 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", 281 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", 282 | "license": "MIT", 283 | "engines": { 284 | "node": ">=6" 285 | } 286 | }, 287 | "node_modules/expand-template": { 288 | "version": "2.0.3", 289 | "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", 290 | "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", 291 | "license": "(MIT OR WTFPL)", 292 | "engines": { 293 | "node": ">=6" 294 | } 295 | }, 296 | "node_modules/fs-constants": { 297 | "version": "1.0.0", 298 | "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", 299 | "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", 300 | "license": "MIT" 301 | }, 302 | "node_modules/fs.realpath": { 303 | "version": "1.0.0", 304 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 305 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 306 | "license": "ISC" 307 | }, 308 | "node_modules/gauge": { 309 | "version": "2.7.4", 310 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", 311 | "integrity": "sha512-14x4kjc6lkD3ltw589k0NrPD6cCNTD6CWoVUNpB85+DrtONoZn+Rug6xZU5RvSC4+TZPxA5AnBibQYAvZn41Hg==", 312 | "deprecated": "This package is no longer supported.", 313 | "license": "ISC", 314 | "dependencies": { 315 | "aproba": "^1.0.3", 316 | "console-control-strings": "^1.0.0", 317 | "has-unicode": "^2.0.0", 318 | "object-assign": "^4.1.0", 319 | "signal-exit": "^3.0.0", 320 | "string-width": "^1.0.1", 321 | "strip-ansi": "^3.0.1", 322 | "wide-align": "^1.1.0" 323 | } 324 | }, 325 | "node_modules/github-from-package": { 326 | "version": "0.0.0", 327 | "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", 328 | "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", 329 | "license": "MIT" 330 | }, 331 | "node_modules/glob": { 332 | "version": "7.2.3", 333 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 334 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 335 | "deprecated": "Glob versions prior to v9 are no longer supported", 336 | "license": "ISC", 337 | "dependencies": { 338 | "fs.realpath": "^1.0.0", 339 | "inflight": "^1.0.4", 340 | "inherits": "2", 341 | "minimatch": "^3.1.1", 342 | "once": "^1.3.0", 343 | "path-is-absolute": "^1.0.0" 344 | }, 345 | "engines": { 346 | "node": "*" 347 | }, 348 | "funding": { 349 | "url": "https://github.com/sponsors/isaacs" 350 | } 351 | }, 352 | "node_modules/has-unicode": { 353 | "version": "2.0.1", 354 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", 355 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", 356 | "license": "ISC" 357 | }, 358 | "node_modules/ieee754": { 359 | "version": "1.2.1", 360 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 361 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 362 | "funding": [ 363 | { 364 | "type": "github", 365 | "url": "https://github.com/sponsors/feross" 366 | }, 367 | { 368 | "type": "patreon", 369 | "url": "https://www.patreon.com/feross" 370 | }, 371 | { 372 | "type": "consulting", 373 | "url": "https://feross.org/support" 374 | } 375 | ], 376 | "license": "BSD-3-Clause" 377 | }, 378 | "node_modules/inflight": { 379 | "version": "1.0.6", 380 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 381 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 382 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 383 | "license": "ISC", 384 | "dependencies": { 385 | "once": "^1.3.0", 386 | "wrappy": "1" 387 | } 388 | }, 389 | "node_modules/inherits": { 390 | "version": "2.0.4", 391 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 392 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 393 | "license": "ISC" 394 | }, 395 | "node_modules/ini": { 396 | "version": "1.3.8", 397 | "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", 398 | "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", 399 | "license": "ISC" 400 | }, 401 | "node_modules/is-fullwidth-code-point": { 402 | "version": "1.0.0", 403 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", 404 | "integrity": "sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", 405 | "license": "MIT", 406 | "dependencies": { 407 | "number-is-nan": "^1.0.0" 408 | }, 409 | "engines": { 410 | "node": ">=0.10.0" 411 | } 412 | }, 413 | "node_modules/isarray": { 414 | "version": "1.0.0", 415 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 416 | "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", 417 | "license": "MIT" 418 | }, 419 | "node_modules/mimic-response": { 420 | "version": "2.1.0", 421 | "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", 422 | "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", 423 | "license": "MIT", 424 | "engines": { 425 | "node": ">=8" 426 | }, 427 | "funding": { 428 | "url": "https://github.com/sponsors/sindresorhus" 429 | } 430 | }, 431 | "node_modules/minimatch": { 432 | "version": "3.1.2", 433 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 434 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 435 | "license": "ISC", 436 | "dependencies": { 437 | "brace-expansion": "^1.1.7" 438 | }, 439 | "engines": { 440 | "node": "*" 441 | } 442 | }, 443 | "node_modules/minimist": { 444 | "version": "1.2.8", 445 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 446 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 447 | "license": "MIT", 448 | "funding": { 449 | "url": "https://github.com/sponsors/ljharb" 450 | } 451 | }, 452 | "node_modules/mkdirp": { 453 | "version": "0.5.6", 454 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", 455 | "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", 456 | "license": "MIT", 457 | "dependencies": { 458 | "minimist": "^1.2.6" 459 | }, 460 | "bin": { 461 | "mkdirp": "bin/cmd.js" 462 | } 463 | }, 464 | "node_modules/mkdirp-classic": { 465 | "version": "0.5.3", 466 | "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", 467 | "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", 468 | "license": "MIT" 469 | }, 470 | "node_modules/mri": { 471 | "version": "1.2.0", 472 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", 473 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", 474 | "license": "MIT", 475 | "engines": { 476 | "node": ">=4" 477 | } 478 | }, 479 | "node_modules/ms": { 480 | "version": "2.1.3", 481 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 482 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 483 | "license": "MIT" 484 | }, 485 | "node_modules/nan": { 486 | "version": "2.22.0", 487 | "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", 488 | "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", 489 | "license": "MIT" 490 | }, 491 | "node_modules/napi-build-utils": { 492 | "version": "1.0.2", 493 | "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", 494 | "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", 495 | "license": "MIT" 496 | }, 497 | "node_modules/node-abi": { 498 | "version": "2.30.1", 499 | "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-2.30.1.tgz", 500 | "integrity": "sha512-/2D0wOQPgaUWzVSVgRMx+trKJRC2UG4SUc4oCJoXx9Uxjtp0Vy3/kt7zcbxHF8+Z/pK3UloLWzBISg72brfy1w==", 501 | "license": "MIT", 502 | "dependencies": { 503 | "semver": "^5.4.1" 504 | } 505 | }, 506 | "node_modules/node-fetch": { 507 | "version": "2.7.0", 508 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 509 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 510 | "license": "MIT", 511 | "dependencies": { 512 | "whatwg-url": "^5.0.0" 513 | }, 514 | "engines": { 515 | "node": "4.x || >=6.0.0" 516 | }, 517 | "peerDependencies": { 518 | "encoding": "^0.1.0" 519 | }, 520 | "peerDependenciesMeta": { 521 | "encoding": { 522 | "optional": true 523 | } 524 | } 525 | }, 526 | "node_modules/noop-logger": { 527 | "version": "0.1.1", 528 | "resolved": "https://registry.npmjs.org/noop-logger/-/noop-logger-0.1.1.tgz", 529 | "integrity": "sha512-6kM8CLXvuW5crTxsAtva2YLrRrDaiTIkIePWs9moLHqbFWT94WpNFjwS/5dfLfECg5i/lkmw3aoqVidxt23TEQ==", 530 | "license": "MIT" 531 | }, 532 | "node_modules/npmlog": { 533 | "version": "4.1.2", 534 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", 535 | "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", 536 | "deprecated": "This package is no longer supported.", 537 | "license": "ISC", 538 | "dependencies": { 539 | "are-we-there-yet": "~1.1.2", 540 | "console-control-strings": "~1.1.0", 541 | "gauge": "~2.7.3", 542 | "set-blocking": "~2.0.0" 543 | } 544 | }, 545 | "node_modules/number-is-nan": { 546 | "version": "1.0.1", 547 | "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", 548 | "integrity": "sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", 549 | "license": "MIT", 550 | "engines": { 551 | "node": ">=0.10.0" 552 | } 553 | }, 554 | "node_modules/object-assign": { 555 | "version": "4.1.1", 556 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 557 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 558 | "license": "MIT", 559 | "engines": { 560 | "node": ">=0.10.0" 561 | } 562 | }, 563 | "node_modules/once": { 564 | "version": "1.4.0", 565 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 566 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 567 | "license": "ISC", 568 | "dependencies": { 569 | "wrappy": "1" 570 | } 571 | }, 572 | "node_modules/p-timeout": { 573 | "version": "4.1.0", 574 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz", 575 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==", 576 | "license": "MIT", 577 | "engines": { 578 | "node": ">=10" 579 | } 580 | }, 581 | "node_modules/path-is-absolute": { 582 | "version": "1.0.1", 583 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 584 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 585 | "license": "MIT", 586 | "engines": { 587 | "node": ">=0.10.0" 588 | } 589 | }, 590 | "node_modules/prebuild-install": { 591 | "version": "5.3.6", 592 | "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-5.3.6.tgz", 593 | "integrity": "sha512-s8Aai8++QQGi4sSbs/M1Qku62PFK49Jm1CbgXklGz4nmHveDq0wzJkg7Na5QbnO1uNH8K7iqx2EQ/mV0MZEmOg==", 594 | "license": "MIT", 595 | "dependencies": { 596 | "detect-libc": "^1.0.3", 597 | "expand-template": "^2.0.3", 598 | "github-from-package": "0.0.0", 599 | "minimist": "^1.2.3", 600 | "mkdirp-classic": "^0.5.3", 601 | "napi-build-utils": "^1.0.1", 602 | "node-abi": "^2.7.0", 603 | "noop-logger": "^0.1.1", 604 | "npmlog": "^4.0.1", 605 | "pump": "^3.0.0", 606 | "rc": "^1.2.7", 607 | "simple-get": "^3.0.3", 608 | "tar-fs": "^2.0.0", 609 | "tunnel-agent": "^0.6.0", 610 | "which-pm-runs": "^1.0.0" 611 | }, 612 | "bin": { 613 | "prebuild-install": "bin.js" 614 | }, 615 | "engines": { 616 | "node": ">=6" 617 | } 618 | }, 619 | "node_modules/process-nextick-args": { 620 | "version": "2.0.1", 621 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 622 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 623 | "license": "MIT" 624 | }, 625 | "node_modules/pump": { 626 | "version": "3.0.2", 627 | "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", 628 | "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", 629 | "license": "MIT", 630 | "dependencies": { 631 | "end-of-stream": "^1.1.0", 632 | "once": "^1.3.1" 633 | } 634 | }, 635 | "node_modules/rc": { 636 | "version": "1.2.8", 637 | "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", 638 | "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", 639 | "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 640 | "dependencies": { 641 | "deep-extend": "^0.6.0", 642 | "ini": "~1.3.0", 643 | "minimist": "^1.2.0", 644 | "strip-json-comments": "~2.0.1" 645 | }, 646 | "bin": { 647 | "rc": "cli.js" 648 | } 649 | }, 650 | "node_modules/readable-stream": { 651 | "version": "2.3.8", 652 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", 653 | "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", 654 | "license": "MIT", 655 | "dependencies": { 656 | "core-util-is": "~1.0.0", 657 | "inherits": "~2.0.3", 658 | "isarray": "~1.0.0", 659 | "process-nextick-args": "~2.0.0", 660 | "safe-buffer": "~5.1.1", 661 | "string_decoder": "~1.1.1", 662 | "util-deprecate": "~1.0.1" 663 | } 664 | }, 665 | "node_modules/rimraf": { 666 | "version": "2.6.3", 667 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", 668 | "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", 669 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 670 | "license": "ISC", 671 | "dependencies": { 672 | "glob": "^7.1.3" 673 | }, 674 | "bin": { 675 | "rimraf": "bin.js" 676 | } 677 | }, 678 | "node_modules/robotjs": { 679 | "version": "0.6.0", 680 | "resolved": "https://registry.npmjs.org/robotjs/-/robotjs-0.6.0.tgz", 681 | "integrity": "sha512-6pRWI3d+CBZqCXT/rsJfabbZoELua+jTeXilG27F8Jvix/J2BYZ0O7Tly2WCmXyqw5xYdCvOwvCeLRHEtXkt4w==", 682 | "hasInstallScript": true, 683 | "license": "MIT", 684 | "dependencies": { 685 | "nan": "^2.14.0", 686 | "node-abi": "^2.13.0", 687 | "prebuild-install": "^5.3.3" 688 | } 689 | }, 690 | "node_modules/safe-buffer": { 691 | "version": "5.1.2", 692 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 693 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 694 | "license": "MIT" 695 | }, 696 | "node_modules/safe-compare": { 697 | "version": "1.1.4", 698 | "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz", 699 | "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==", 700 | "license": "MIT", 701 | "dependencies": { 702 | "buffer-alloc": "^1.2.0" 703 | } 704 | }, 705 | "node_modules/sandwich-stream": { 706 | "version": "2.0.2", 707 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz", 708 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==", 709 | "license": "Apache-2.0", 710 | "engines": { 711 | "node": ">= 0.10" 712 | } 713 | }, 714 | "node_modules/screenshot-desktop": { 715 | "version": "1.15.0", 716 | "resolved": "https://registry.npmjs.org/screenshot-desktop/-/screenshot-desktop-1.15.0.tgz", 717 | "integrity": "sha512-CLaZNBDEXU+KJ6BGsO8jSbKI7Zck7gQmFJHjzluBdwrVP0jOemP2avpD3ufWu81yqzwB92u2AMv+K9IlaslRsg==", 718 | "funding": [ 719 | { 720 | "type": "github", 721 | "url": "https://github.com/sponsors/bencevans" 722 | } 723 | ], 724 | "license": "MIT", 725 | "dependencies": { 726 | "temp": "^0.9.4" 727 | } 728 | }, 729 | "node_modules/semver": { 730 | "version": "5.7.2", 731 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", 732 | "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", 733 | "license": "ISC", 734 | "bin": { 735 | "semver": "bin/semver" 736 | } 737 | }, 738 | "node_modules/set-blocking": { 739 | "version": "2.0.0", 740 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", 741 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", 742 | "license": "ISC" 743 | }, 744 | "node_modules/signal-exit": { 745 | "version": "3.0.7", 746 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", 747 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", 748 | "license": "ISC" 749 | }, 750 | "node_modules/simple-concat": { 751 | "version": "1.0.1", 752 | "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", 753 | "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", 754 | "funding": [ 755 | { 756 | "type": "github", 757 | "url": "https://github.com/sponsors/feross" 758 | }, 759 | { 760 | "type": "patreon", 761 | "url": "https://www.patreon.com/feross" 762 | }, 763 | { 764 | "type": "consulting", 765 | "url": "https://feross.org/support" 766 | } 767 | ], 768 | "license": "MIT" 769 | }, 770 | "node_modules/simple-get": { 771 | "version": "3.1.1", 772 | "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", 773 | "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", 774 | "license": "MIT", 775 | "dependencies": { 776 | "decompress-response": "^4.2.0", 777 | "once": "^1.3.1", 778 | "simple-concat": "^1.0.0" 779 | } 780 | }, 781 | "node_modules/string_decoder": { 782 | "version": "1.1.1", 783 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 784 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 785 | "license": "MIT", 786 | "dependencies": { 787 | "safe-buffer": "~5.1.0" 788 | } 789 | }, 790 | "node_modules/string-width": { 791 | "version": "1.0.2", 792 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", 793 | "integrity": "sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", 794 | "license": "MIT", 795 | "dependencies": { 796 | "code-point-at": "^1.0.0", 797 | "is-fullwidth-code-point": "^1.0.0", 798 | "strip-ansi": "^3.0.0" 799 | }, 800 | "engines": { 801 | "node": ">=0.10.0" 802 | } 803 | }, 804 | "node_modules/strip-ansi": { 805 | "version": "3.0.1", 806 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", 807 | "integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", 808 | "license": "MIT", 809 | "dependencies": { 810 | "ansi-regex": "^2.0.0" 811 | }, 812 | "engines": { 813 | "node": ">=0.10.0" 814 | } 815 | }, 816 | "node_modules/strip-json-comments": { 817 | "version": "2.0.1", 818 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", 819 | "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", 820 | "license": "MIT", 821 | "engines": { 822 | "node": ">=0.10.0" 823 | } 824 | }, 825 | "node_modules/tar-fs": { 826 | "version": "2.1.1", 827 | "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", 828 | "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", 829 | "license": "MIT", 830 | "dependencies": { 831 | "chownr": "^1.1.1", 832 | "mkdirp-classic": "^0.5.2", 833 | "pump": "^3.0.0", 834 | "tar-stream": "^2.1.4" 835 | } 836 | }, 837 | "node_modules/tar-stream": { 838 | "version": "2.2.0", 839 | "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", 840 | "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", 841 | "license": "MIT", 842 | "dependencies": { 843 | "bl": "^4.0.3", 844 | "end-of-stream": "^1.4.1", 845 | "fs-constants": "^1.0.0", 846 | "inherits": "^2.0.3", 847 | "readable-stream": "^3.1.1" 848 | }, 849 | "engines": { 850 | "node": ">=6" 851 | } 852 | }, 853 | "node_modules/tar-stream/node_modules/readable-stream": { 854 | "version": "3.6.2", 855 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", 856 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", 857 | "license": "MIT", 858 | "dependencies": { 859 | "inherits": "^2.0.3", 860 | "string_decoder": "^1.1.1", 861 | "util-deprecate": "^1.0.1" 862 | }, 863 | "engines": { 864 | "node": ">= 6" 865 | } 866 | }, 867 | "node_modules/telegraf": { 868 | "version": "4.16.3", 869 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz", 870 | "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==", 871 | "license": "MIT", 872 | "dependencies": { 873 | "@telegraf/types": "^7.1.0", 874 | "abort-controller": "^3.0.0", 875 | "debug": "^4.3.4", 876 | "mri": "^1.2.0", 877 | "node-fetch": "^2.7.0", 878 | "p-timeout": "^4.1.0", 879 | "safe-compare": "^1.1.4", 880 | "sandwich-stream": "^2.0.2" 881 | }, 882 | "bin": { 883 | "telegraf": "lib/cli.mjs" 884 | }, 885 | "engines": { 886 | "node": "^12.20.0 || >=14.13.1" 887 | } 888 | }, 889 | "node_modules/temp": { 890 | "version": "0.9.4", 891 | "resolved": "https://registry.npmjs.org/temp/-/temp-0.9.4.tgz", 892 | "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", 893 | "license": "MIT", 894 | "dependencies": { 895 | "mkdirp": "^0.5.1", 896 | "rimraf": "~2.6.2" 897 | }, 898 | "engines": { 899 | "node": ">=6.0.0" 900 | } 901 | }, 902 | "node_modules/tr46": { 903 | "version": "0.0.3", 904 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 905 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 906 | "license": "MIT" 907 | }, 908 | "node_modules/tunnel-agent": { 909 | "version": "0.6.0", 910 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 911 | "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", 912 | "license": "Apache-2.0", 913 | "dependencies": { 914 | "safe-buffer": "^5.0.1" 915 | }, 916 | "engines": { 917 | "node": "*" 918 | } 919 | }, 920 | "node_modules/util-deprecate": { 921 | "version": "1.0.2", 922 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 923 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", 924 | "license": "MIT" 925 | }, 926 | "node_modules/webidl-conversions": { 927 | "version": "3.0.1", 928 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 929 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 930 | "license": "BSD-2-Clause" 931 | }, 932 | "node_modules/whatwg-url": { 933 | "version": "5.0.0", 934 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 935 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 936 | "license": "MIT", 937 | "dependencies": { 938 | "tr46": "~0.0.3", 939 | "webidl-conversions": "^3.0.0" 940 | } 941 | }, 942 | "node_modules/which-pm-runs": { 943 | "version": "1.1.0", 944 | "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", 945 | "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", 946 | "license": "MIT", 947 | "engines": { 948 | "node": ">=4" 949 | } 950 | }, 951 | "node_modules/wide-align": { 952 | "version": "1.1.5", 953 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", 954 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", 955 | "license": "ISC", 956 | "dependencies": { 957 | "string-width": "^1.0.2 || 2 || 3 || 4" 958 | } 959 | }, 960 | "node_modules/wrappy": { 961 | "version": "1.0.2", 962 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 963 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 964 | "license": "ISC" 965 | } 966 | } 967 | } 968 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pc-remote-bot", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "bot.js", 6 | "scripts": { 7 | "start": "node bot.js" 8 | }, 9 | "private": true, 10 | "dependencies": { 11 | "dotenv": "^16.4.7", 12 | "robotjs": "^0.6.0", 13 | "screenshot-desktop": "^1.15.0", 14 | "telegraf": "^4.16.3" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /utils/accessControl.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | const USERS_FILE = 'users.txt'; 4 | 5 | let adminId = null; 6 | let allowedUsers = new Set(); 7 | 8 | function initUsers() { 9 | if (!fs.existsSync(USERS_FILE)) { 10 | fs.writeFileSync(USERS_FILE, '', 'utf8'); 11 | } 12 | const data = fs.readFileSync(USERS_FILE, 'utf8'); 13 | const lines = data.split('\n').map(l => l.trim()); 14 | if (lines.length > 0 && lines[0] !== '') { 15 | adminId = lines[0]; 16 | } 17 | 18 | const userLines = lines.slice(1).filter(l => l !== ''); 19 | allowedUsers = new Set(userLines); 20 | } 21 | 22 | function saveUsers() { 23 | const adminLine = adminId ? adminId : ''; 24 | const userLines = Array.from(allowedUsers); 25 | const fileContent = [adminLine, ...userLines].join('\n'); 26 | fs.writeFileSync(USERS_FILE, fileContent, 'utf8'); 27 | } 28 | 29 | function getAdmin() { 30 | return adminId; 31 | } 32 | 33 | function setAdmin(userId) { 34 | adminId = userId; 35 | saveUsers(); 36 | console.log(`Set admin to user: ${userId}`); 37 | } 38 | 39 | function isAdmin(userId) { 40 | return adminId && adminId === userId; 41 | } 42 | 43 | function isAllowedUser(userId) { 44 | // The admin should inherently have access, but to keep logic consistent: 45 | if (isAdmin(userId)) return true; 46 | return allowedUsers.has(userId); 47 | } 48 | 49 | function addAllowedUser(userId) { 50 | // Do not add admin as a duplicate line if they're already admin 51 | if (isAdmin(userId) || allowedUsers.has(userId)) return; 52 | allowedUsers.add(userId); 53 | saveUsers(); 54 | } 55 | 56 | module.exports = { 57 | initUsers, 58 | getAdmin, 59 | setAdmin, 60 | isAdmin, 61 | isAllowedUser, 62 | addAllowedUser 63 | }; 64 | -------------------------------------------------------------------------------- /utils/screenshot.js: -------------------------------------------------------------------------------- 1 | const screenshot = require('screenshot-desktop'); 2 | const fs = require('fs'); 3 | 4 | async function getScreens() { 5 | try { 6 | return await screenshot.listDisplays(); 7 | } catch (error) { 8 | console.error('Error fetching screens:', error); 9 | return []; 10 | } 11 | } 12 | 13 | async function takeScreenshot(screenId) { 14 | const tempFilePath = `screenshot_${Date.now()}.png`; 15 | try { 16 | await screenshot({ screen: screenId, filename: tempFilePath }); 17 | return tempFilePath; 18 | } catch (error) { 19 | console.error('Error taking screenshot:', error); 20 | throw error; 21 | } 22 | } 23 | 24 | function cleanupFile(filePath) { 25 | try { 26 | fs.unlinkSync(filePath); 27 | } catch (error) { 28 | console.error('Error removing file:', filePath, error); 29 | } 30 | } 31 | 32 | module.exports = { getScreens, takeScreenshot, cleanupFile }; 33 | --------------------------------------------------------------------------------