refactor: generalize channel type handling across various components
This commit is contained in:
4
index.ts
4
index.ts
@@ -38,10 +38,6 @@ const plugin = {
|
|||||||
description: "Plugin-level project execution: parallel (each project independent) or sequential (only one project active at a time)",
|
description: "Plugin-level project execution: parallel (each project independent) or sequential (only one project active at a time)",
|
||||||
default: "parallel",
|
default: "parallel",
|
||||||
},
|
},
|
||||||
orchestratorDm: {
|
|
||||||
type: "string",
|
|
||||||
description: "Telegram/WhatsApp chat ID for orchestrator DM notifications (heartbeat summaries)",
|
|
||||||
},
|
|
||||||
notifications: {
|
notifications: {
|
||||||
type: "object",
|
type: "object",
|
||||||
description: "Notification settings for worker lifecycle events",
|
description: "Notification settings for worker lifecycle events",
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
export type ChannelType = "telegram" | "whatsapp";
|
export type ChannelType = string;
|
||||||
|
|
||||||
export interface BindingAnalysis {
|
export interface BindingAnalysis {
|
||||||
channelEnabled: boolean;
|
channelEnabled: boolean;
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ import path from "node:path";
|
|||||||
|
|
||||||
export type InteractionContext =
|
export type InteractionContext =
|
||||||
| { type: "via-agent"; agentId: string; agentName?: string }
|
| { type: "via-agent"; agentId: string; agentName?: string }
|
||||||
| { type: "direct"; channel?: "telegram" | "whatsapp" | "cli" }
|
| { type: "direct"; channel?: string; chatId?: string }
|
||||||
| {
|
| {
|
||||||
type: "group";
|
type: "group";
|
||||||
channel: "telegram" | "whatsapp";
|
channel: string;
|
||||||
groupId: string;
|
groupId: string;
|
||||||
projectName?: string;
|
projectName?: string;
|
||||||
};
|
};
|
||||||
@@ -64,7 +64,7 @@ export async function detectContext(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
type: "group",
|
type: "group",
|
||||||
channel: messageChannel as "telegram" | "whatsapp",
|
channel: messageChannel,
|
||||||
groupId: actualGroupId,
|
groupId: actualGroupId,
|
||||||
projectName,
|
projectName,
|
||||||
};
|
};
|
||||||
@@ -72,11 +72,12 @@ export async function detectContext(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Direct (DM or CLI) ---
|
// --- Direct (DM or CLI) ---
|
||||||
|
// Extract chat ID from sessionKey (e.g. "agent:devclaw:telegram:direct:657120585")
|
||||||
|
const chatId = sessionKey ? sessionKey.split(":").pop() : undefined;
|
||||||
return {
|
return {
|
||||||
type: "direct",
|
type: "direct",
|
||||||
channel: messageChannel
|
channel: messageChannel ?? "cli",
|
||||||
? (messageChannel as "telegram" | "whatsapp")
|
chatId,
|
||||||
: "cli",
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,35 +121,31 @@ function buildMessage(event: NotifyEvent): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a notification message to a Telegram/WhatsApp target.
|
* Send a notification message via the native OpenClaw messaging CLI.
|
||||||
*
|
*
|
||||||
* Uses the OpenClaw CLI to invoke the message tool.
|
* Uses `openclaw message send` which handles target resolution, chunking,
|
||||||
|
* retries, and error reporting for all supported channels.
|
||||||
* Fails silently (logs error but doesn't throw) to avoid breaking the main flow.
|
* Fails silently (logs error but doesn't throw) to avoid breaking the main flow.
|
||||||
*/
|
*/
|
||||||
async function sendMessage(
|
async function sendMessage(
|
||||||
target: string,
|
target: string,
|
||||||
message: string,
|
message: string,
|
||||||
channel: "telegram" | "whatsapp",
|
channel: string,
|
||||||
workspaceDir: string,
|
workspaceDir: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
// Use openclaw agent to send via message tool
|
|
||||||
// The message tool requires action=send, target, message
|
|
||||||
await execFileAsync(
|
await execFileAsync(
|
||||||
"openclaw",
|
"openclaw",
|
||||||
[
|
[
|
||||||
"gateway",
|
"message",
|
||||||
"call",
|
"send",
|
||||||
"tools.invoke",
|
"--channel",
|
||||||
"--params",
|
channel,
|
||||||
JSON.stringify({
|
"--target",
|
||||||
tool: "message",
|
target,
|
||||||
params: {
|
"--message",
|
||||||
action: "send",
|
message,
|
||||||
target,
|
"--json",
|
||||||
message,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
{ timeout: 30_000 },
|
{ timeout: 30_000 },
|
||||||
);
|
);
|
||||||
@@ -178,8 +174,8 @@ export async function notify(
|
|||||||
config?: NotificationConfig;
|
config?: NotificationConfig;
|
||||||
/** Target for project-scoped notifications (groupId) */
|
/** Target for project-scoped notifications (groupId) */
|
||||||
groupId?: string;
|
groupId?: string;
|
||||||
/** Channel type for routing */
|
/** Channel type for routing (e.g. "telegram", "whatsapp", "discord", "slack") */
|
||||||
channel?: "telegram" | "whatsapp";
|
channel?: string;
|
||||||
/** Target for DM notifications (orchestrator) */
|
/** Target for DM notifications (orchestrator) */
|
||||||
orchestratorDm?: string;
|
orchestratorDm?: string;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ export type Project = {
|
|||||||
baseBranch: string;
|
baseBranch: string;
|
||||||
deployBranch: string;
|
deployBranch: string;
|
||||||
autoChain: boolean;
|
autoChain: boolean;
|
||||||
|
/** Messaging channel for this project's group (e.g. "telegram", "whatsapp", "discord", "slack"). Stored at registration time. */
|
||||||
|
channel?: string;
|
||||||
/** Project-level role execution: parallel (DEV+QA can run simultaneously) or sequential (only one role at a time). Default: parallel */
|
/** Project-level role execution: parallel (DEV+QA can run simultaneously) or sequential (only one role at a time). Default: parallel */
|
||||||
roleExecution?: "parallel" | "sequential";
|
roleExecution?: "parallel" | "sequential";
|
||||||
maxDevWorkers?: number;
|
maxDevWorkers?: number;
|
||||||
@@ -133,6 +135,9 @@ export async function readProjects(workspaceDir: string): Promise<ProjectsData>
|
|||||||
if (project.autoChain === undefined) {
|
if (project.autoChain === undefined) {
|
||||||
project.autoChain = false;
|
project.autoChain = false;
|
||||||
}
|
}
|
||||||
|
if (!project.channel) {
|
||||||
|
project.channel = "telegram";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -519,9 +519,10 @@ export function createHeartbeatTickTool(api: OpenClawPluginApi) {
|
|||||||
skipped: result.skipped.length,
|
skipped: result.skipped.length,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Send heartbeat notification to orchestrator DM
|
// Send heartbeat notification back to whoever triggered it
|
||||||
|
// Both channel and target are derived from context (DM sessionKey)
|
||||||
const notifyConfig = getNotificationConfig(pluginConfig);
|
const notifyConfig = getNotificationConfig(pluginConfig);
|
||||||
const orchestratorDm = pluginConfig?.orchestratorDm as string | undefined;
|
const orchestratorDm = context.type === "direct" ? context.chatId : undefined;
|
||||||
|
|
||||||
await notify(
|
await notify(
|
||||||
{
|
{
|
||||||
@@ -541,7 +542,7 @@ export function createHeartbeatTickTool(api: OpenClawPluginApi) {
|
|||||||
workspaceDir,
|
workspaceDir,
|
||||||
config: notifyConfig,
|
config: notifyConfig,
|
||||||
orchestratorDm,
|
orchestratorDm,
|
||||||
channel: "telegram",
|
channel: "channel" in context ? context.channel : undefined,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ export function createProjectRegisterTool(api: OpenClawPluginApi) {
|
|||||||
// Provide helpful note if project is already registered
|
// Provide helpful note if project is already registered
|
||||||
const contextInfo = context.projectName
|
const contextInfo = context.projectName
|
||||||
? `Note: This group is already registered as "${context.projectName}". You may be re-registering it.`
|
? `Note: This group is already registered as "${context.projectName}". You may be re-registering it.`
|
||||||
: `Registering project for this ${context.channel === "whatsapp" ? "WhatsApp" : "Telegram"} group (ID: ${actualGroupId.substring(0, 20)}...).`;
|
: `Registering project for this ${context.channel} group (ID: ${actualGroupId.substring(0, 20)}...).`;
|
||||||
|
|
||||||
// 1. Check project not already registered (allow re-register if incomplete)
|
// 1. Check project not already registered (allow re-register if incomplete)
|
||||||
const data = await readProjects(workspaceDir);
|
const data = await readProjects(workspaceDir);
|
||||||
@@ -205,6 +205,7 @@ export function createProjectRegisterTool(api: OpenClawPluginApi) {
|
|||||||
baseBranch,
|
baseBranch,
|
||||||
deployBranch,
|
deployBranch,
|
||||||
autoChain: false,
|
autoChain: false,
|
||||||
|
channel: context.channel,
|
||||||
roleExecution,
|
roleExecution,
|
||||||
dev: emptyWorkerState([...DEV_TIERS]),
|
dev: emptyWorkerState([...DEV_TIERS]),
|
||||||
qa: emptyWorkerState([...QA_TIERS]),
|
qa: emptyWorkerState([...QA_TIERS]),
|
||||||
|
|||||||
@@ -298,8 +298,7 @@ export function createTaskCompleteTool(api: OpenClawPluginApi) {
|
|||||||
workspaceDir,
|
workspaceDir,
|
||||||
config: notifyConfig,
|
config: notifyConfig,
|
||||||
groupId,
|
groupId,
|
||||||
// Channel detection: default to telegram if not available
|
channel: project.channel ?? "telegram",
|
||||||
channel: "telegram",
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user