ofiere-openclaw-plugin 4.23.0 → 4.24.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools.ts +80 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.23.0",
3
+ "version": "4.24.0",
4
4
  "type": "module",
5
5
  "description": "OpenClaw plugin for Ofiere PM - 13 meta-tools covering tasks, agents, projects, scheduling, knowledge, workflows, notifications, memory, prompts, constellation, space file management, execution plan builder, and SOP management",
6
6
  "keywords": ["openclaw", "ofiere", "project-management", "agents", "plugin"],
package/src/tools.ts CHANGED
@@ -2447,10 +2447,6 @@ async function dispatchReportDirect(
2447
2447
  const sent: string[] = [];
2448
2448
  const failed: string[] = [];
2449
2449
 
2450
- // Resolve dashboard URL + auth for sending
2451
- const dashboardUrl = process.env.OFIERE_DASHBOARD_URL || "https://ofiere.com";
2452
- const serviceRoleKey = (supabase as any).supabaseKey || process.env.OFIERE_SERVICE_ROLE_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY || "";
2453
-
2454
2450
  for (const b of bindings) {
2455
2451
  const limit = LIMITS[b.channel_type] || 4000;
2456
2452
  let msg = report;
@@ -2458,32 +2454,89 @@ async function dispatchReportDirect(
2458
2454
  msg = msg.substring(0, limit - 80) + "\n\n... [Full report on dashboard]";
2459
2455
  }
2460
2456
 
2461
- // accountId must match the key used during gateway sync (gatewaySync.ts:435),
2462
- // which defaults to agentId when channel_account_id is null.
2463
2457
  const effectiveAccountId = b.channel_account_id || b.agent_id || agentId || "default";
2464
2458
 
2465
- try {
2466
- const res = await fetch(`${dashboardUrl}/api/channels/gateway-send`, {
2467
- method: "POST",
2468
- headers: {
2469
- "Content-Type": "application/json",
2470
- "Authorization": `Bearer ${serviceRoleKey}`,
2471
- },
2472
- body: JSON.stringify({
2473
- userId,
2474
- channelType: b.channel_type,
2475
- accountId: effectiveAccountId,
2476
- message: msg,
2477
- ...(b.thread_id ? { threadId: b.thread_id } : {}),
2478
- }),
2479
- });
2480
- if (res.ok) sent.push(b.channel_type);
2481
- else {
2482
- const errText = await res.text().catch(() => "");
2483
- failed.push(`${b.channel_type}: HTTP ${res.status} ${errText.slice(0, 100)}`);
2459
+ if (b.channel_type === "telegram") {
2460
+ // Direct Telegram Bot API — bypass gateway entirely
2461
+ const chatId = b.thread_id;
2462
+ if (!chatId) {
2463
+ failed.push(`telegram: no chat_id (thread_id) for ${effectiveAccountId}`);
2464
+ continue;
2465
+ }
2466
+
2467
+ const config = typeof b.channel_config === "string" ? JSON.parse(b.channel_config) : b.channel_config;
2468
+ if (!config?.encryptedToken) {
2469
+ failed.push(`telegram: no encryptedToken for ${effectiveAccountId}`);
2470
+ continue;
2471
+ }
2472
+
2473
+ // Decrypt bot token (mirrors dashboard/lib/encryption.ts)
2474
+ let botToken: string | null = null;
2475
+ try {
2476
+ const crypto = await import("crypto");
2477
+ const keyHex = process.env.OFIERE_ENCRYPTION_KEY ||
2478
+ crypto.createHash("sha256").update("nerv-os-default-key-change-me").digest("hex");
2479
+ const keyBuf = Buffer.from(keyHex, "hex");
2480
+ const data = Buffer.from(config.encryptedToken, "hex");
2481
+ const iv = data.subarray(0, 12);
2482
+ const authTag = data.subarray(12, 28);
2483
+ const ciphertext = data.subarray(28);
2484
+ const decipher = crypto.createDecipheriv("aes-256-gcm", keyBuf, iv);
2485
+ decipher.setAuthTag(authTag);
2486
+ const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
2487
+ botToken = decrypted.toString("utf8");
2488
+ } catch (e: any) {
2489
+ failed.push(`telegram: decrypt failed for ${effectiveAccountId}: ${e.message}`);
2490
+ continue;
2491
+ }
2492
+
2493
+ if (!botToken) {
2494
+ failed.push(`telegram: null token for ${effectiveAccountId}`);
2495
+ continue;
2496
+ }
2497
+
2498
+ try {
2499
+ const res = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
2500
+ method: "POST",
2501
+ headers: { "Content-Type": "application/json" },
2502
+ body: JSON.stringify({ chat_id: chatId, text: msg }),
2503
+ });
2504
+ if (res.ok) {
2505
+ sent.push(b.channel_type);
2506
+ } else {
2507
+ const errText = await res.text().catch(() => "");
2508
+ failed.push(`telegram: Bot API ${res.status} ${errText.slice(0, 100)}`);
2509
+ }
2510
+ } catch (e: any) {
2511
+ failed.push(`telegram: ${e.message || "fetch error"}`);
2512
+ }
2513
+ } else {
2514
+ // Non-telegram: route through dashboard gateway-send (best-effort)
2515
+ const dashboardUrl = process.env.OFIERE_DASHBOARD_URL || "https://ofiere.com";
2516
+ const serviceRoleKey = (supabase as any).supabaseKey || process.env.OFIERE_SERVICE_ROLE_KEY || process.env.SUPABASE_SERVICE_ROLE_KEY || "";
2517
+ try {
2518
+ const res = await fetch(`${dashboardUrl}/api/channels/gateway-send`, {
2519
+ method: "POST",
2520
+ headers: {
2521
+ "Content-Type": "application/json",
2522
+ "Authorization": `Bearer ${serviceRoleKey}`,
2523
+ },
2524
+ body: JSON.stringify({
2525
+ userId,
2526
+ channelType: b.channel_type,
2527
+ accountId: effectiveAccountId,
2528
+ message: msg,
2529
+ ...(b.thread_id ? { threadId: b.thread_id } : {}),
2530
+ }),
2531
+ });
2532
+ if (res.ok) sent.push(b.channel_type);
2533
+ else {
2534
+ const errText = await res.text().catch(() => "");
2535
+ failed.push(`${b.channel_type}: HTTP ${res.status} ${errText.slice(0, 100)}`);
2536
+ }
2537
+ } catch (e: any) {
2538
+ failed.push(`${b.channel_type}: ${e.message || "fetch error"}`);
2484
2539
  }
2485
- } catch (e: any) {
2486
- failed.push(`${b.channel_type}: ${e.message || "fetch error"}`);
2487
2540
  }
2488
2541
  }
2489
2542