ofiere-openclaw-plugin 4.22.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 +81 -27
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ofiere-openclaw-plugin",
3
- "version": "4.22.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
@@ -2428,7 +2428,7 @@ async function dispatchReportDirect(
2428
2428
  channelTypes: string[],
2429
2429
  ): Promise<{ sent: string[]; failed: string[] }> {
2430
2430
  // Query agent's active channel bindings
2431
- let query = supabase.from("channel_bindings").select("channel_type, channel_account_id, agent_id, channel_config")
2431
+ let query = supabase.from("channel_bindings").select("channel_type, channel_account_id, agent_id, channel_config, thread_id")
2432
2432
  .eq("user_id", userId)
2433
2433
  .eq("agent_id", agentId)
2434
2434
  .eq("notifications_enabled", true)
@@ -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,31 +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
- }),
2478
- });
2479
- if (res.ok) sent.push(b.channel_type);
2480
- else {
2481
- const errText = await res.text().catch(() => "");
2482
- 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"}`);
2483
2539
  }
2484
- } catch (e: any) {
2485
- failed.push(`${b.channel_type}: ${e.message || "fetch error"}`);
2486
2540
  }
2487
2541
  }
2488
2542