nothumanallowed 13.5.182 → 13.5.183

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "13.5.182",
3
+ "version": "13.5.183",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '13.5.182';
8
+ export const VERSION = '13.5.183';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -13,6 +13,10 @@ import { buildSystemPrompt, parseActions, executeTool, TOOL_DEFINITIONS } from '
13
13
  import https from 'https';
14
14
  import http from 'http';
15
15
  import { URL } from 'url';
16
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import os from 'os';
19
+ import { VERSION } from '../constants.mjs';
16
20
 
17
21
  // ── Agent Routing (keyword-based, zero LLM calls) ───────────────────────────
18
22
 
@@ -190,6 +194,71 @@ async function callAgentWithTools(config, agentName, userMessage) {
190
194
 
191
195
  // ── Telegram Bot (Long Polling via native fetch) ─────────────────────────────
192
196
 
197
+ // ── User store for Telegram chat IDs (for broadcast notifications) ──────────
198
+
199
+ const TELEGRAM_USERS_FILE = path.join(os.homedir(), '.nha', 'telegram-users.json');
200
+
201
+ function loadTelegramUsers() {
202
+ try {
203
+ const raw = fs.readFileSync(TELEGRAM_USERS_FILE, 'utf8');
204
+ return JSON.parse(raw);
205
+ } catch {
206
+ return {};
207
+ }
208
+ }
209
+
210
+ function saveTelegramUsers(users) {
211
+ try {
212
+ fs.mkdirSync(path.dirname(TELEGRAM_USERS_FILE), { recursive: true });
213
+ fs.writeFileSync(TELEGRAM_USERS_FILE, JSON.stringify(users, null, 2));
214
+ } catch {}
215
+ }
216
+
217
+ function touchTelegramUser(chatId, username, firstName) {
218
+ const users = loadTelegramUsers();
219
+ const id = String(chatId);
220
+ const now = new Date().toISOString();
221
+ users[id] = {
222
+ chatId: id,
223
+ username: username || null,
224
+ firstName: firstName || null,
225
+ firstSeen: users[id]?.firstSeen || now,
226
+ lastSeen: now,
227
+ };
228
+ saveTelegramUsers(users);
229
+ }
230
+
231
+ function getAllTelegramChatIds() {
232
+ const users = loadTelegramUsers();
233
+ return Object.keys(users);
234
+ }
235
+
236
+ // ── npm update check ─────────────────────────────────────────────────────────
237
+
238
+ function compareSemver(a, b) {
239
+ const pa = a.split('.').map(Number);
240
+ const pb = b.split('.').map(Number);
241
+ for (let i = 0; i < 3; i++) {
242
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
243
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
244
+ }
245
+ return 0;
246
+ }
247
+
248
+ async function checkNpmVersion() {
249
+ const res = await fetch('https://registry.npmjs.org/nothumanallowed/latest', {
250
+ signal: AbortSignal.timeout(8000),
251
+ headers: { 'Accept': 'application/json' },
252
+ });
253
+ const data = await res.json();
254
+ const latest = data.version;
255
+ const current = VERSION;
256
+ const updateAvailable = compareSemver(latest, current) > 0;
257
+ return { current, latest, updateAvailable };
258
+ }
259
+
260
+ // ── Telegram Bot (Long Polling via native fetch) ─────────────────────────────
261
+
193
262
  class TelegramResponder {
194
263
  constructor(config, log, wsBroadcast) {
195
264
  this.config = config;
@@ -203,6 +272,8 @@ class TelegramResponder {
203
272
  this.abortController = null;
204
273
  this.pendingRequests = 0;
205
274
  this.maxConcurrent = 3;
275
+ this._updateCheckTimer = null;
276
+ this._lastNotifiedVersion = null;
206
277
  }
207
278
 
208
279
  get enabled() {
@@ -214,6 +285,8 @@ class TelegramResponder {
214
285
  this.running = true;
215
286
  this.log('[Telegram] Responder started — polling for messages');
216
287
  this._pollLoop();
288
+ // Check for npm updates after 60s, then every 24h
289
+ this._updateCheckTimer = setTimeout(() => this._scheduleUpdateCheck(), 60 * 1000);
217
290
  }
218
291
 
219
292
  stop() {
@@ -222,9 +295,57 @@ class TelegramResponder {
222
295
  this.abortController.abort();
223
296
  this.abortController = null;
224
297
  }
298
+ if (this._updateCheckTimer) {
299
+ clearTimeout(this._updateCheckTimer);
300
+ clearInterval(this._updateCheckTimer);
301
+ this._updateCheckTimer = null;
302
+ }
225
303
  this.log('[Telegram] Responder stopped');
226
304
  }
227
305
 
306
+ async _scheduleUpdateCheck() {
307
+ await this._checkAndNotifyUpdate();
308
+ // Then every 24h
309
+ this._updateCheckTimer = setInterval(() => this._checkAndNotifyUpdate(), 24 * 60 * 60 * 1000);
310
+ }
311
+
312
+ async _checkAndNotifyUpdate() {
313
+ try {
314
+ const { latest, updateAvailable } = await checkNpmVersion();
315
+ if (!updateAvailable) return;
316
+ if (this._lastNotifiedVersion === latest) return; // Already notified for this version
317
+
318
+ this._lastNotifiedVersion = latest;
319
+ const chatIds = getAllTelegramChatIds();
320
+ if (chatIds.length === 0) return;
321
+
322
+ const msg =
323
+ `🆕 *NHA v${latest} disponibile!*\n\n` +
324
+ `Una nuova versione di NotHumanAllowed è stata pubblicata.\n\n` +
325
+ `Aggiorna con:\n` +
326
+ `\`npm install -g nothumanallowed@latest\`\n\n` +
327
+ `Poi riavvia il bot con: \`nha responder restart\``;
328
+
329
+ this.log(`[Telegram] Broadcasting update notification v${latest} to ${chatIds.length} users`);
330
+
331
+ for (const chatId of chatIds) {
332
+ try {
333
+ await this._telegramCall('sendMessage', {
334
+ chat_id: parseInt(chatId, 10),
335
+ text: msg,
336
+ parse_mode: 'Markdown',
337
+ });
338
+ } catch {
339
+ // User blocked bot or chat no longer exists — ignore
340
+ }
341
+ // Small delay to avoid Telegram rate limits
342
+ await this._sleep(300);
343
+ }
344
+ } catch (err) {
345
+ this.log(`[Telegram] Update check failed: ${err.message}`);
346
+ }
347
+ }
348
+
228
349
  async _pollLoop() {
229
350
  while (this.running) {
230
351
  try {
@@ -343,6 +464,9 @@ class TelegramResponder {
343
464
  return;
344
465
  }
345
466
 
467
+ // Track this user for broadcast notifications (update alerts, etc.)
468
+ touchTelegramUser(chatId, message.from?.username, message.from?.first_name);
469
+
346
470
  let rawText = message.text || '';
347
471
  let isVoice = false;
348
472