mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 09:01:22 +00:00
Slack: add some fixes and connect it all up
This commit is contained in:
@@ -473,6 +473,7 @@ export async function agentCommand(
|
||||
const whatsappTarget = opts.to ? normalizeE164(opts.to) : allowFrom[0];
|
||||
const telegramTarget = opts.to?.trim() || undefined;
|
||||
const discordTarget = opts.to?.trim() || undefined;
|
||||
const slackTarget = opts.to?.trim() || undefined;
|
||||
const signalTarget = opts.to?.trim() || undefined;
|
||||
const imessageTarget = opts.to?.trim() || undefined;
|
||||
|
||||
@@ -484,11 +485,13 @@ export async function agentCommand(
|
||||
? whatsappTarget
|
||||
: deliveryProvider === "discord"
|
||||
? discordTarget
|
||||
: deliveryProvider === "signal"
|
||||
? signalTarget
|
||||
: deliveryProvider === "imessage"
|
||||
? imessageTarget
|
||||
: undefined;
|
||||
: deliveryProvider === "slack"
|
||||
? slackTarget
|
||||
: deliveryProvider === "signal"
|
||||
? signalTarget
|
||||
: deliveryProvider === "imessage"
|
||||
? imessageTarget
|
||||
: undefined;
|
||||
const message = `Delivery failed (${deliveryProvider}${deliveryTarget ? ` to ${deliveryTarget}` : ""}): ${String(err)}`;
|
||||
runtime.error?.(message);
|
||||
if (!runtime.error) runtime.log(message);
|
||||
@@ -514,6 +517,13 @@ export async function agentCommand(
|
||||
if (!bestEffortDeliver) throw err;
|
||||
logDeliveryError(err);
|
||||
}
|
||||
if (deliveryProvider === "slack" && !slackTarget) {
|
||||
const err = new Error(
|
||||
"Delivering to Slack requires --to <channelId|user:ID|channel:ID>",
|
||||
);
|
||||
if (!bestEffortDeliver) throw err;
|
||||
logDeliveryError(err);
|
||||
}
|
||||
if (deliveryProvider === "signal" && !signalTarget) {
|
||||
const err = new Error(
|
||||
"Delivering to Signal requires --to <E.164|group:ID|signal:group:ID|signal:+E.164>",
|
||||
@@ -539,6 +549,7 @@ export async function agentCommand(
|
||||
deliveryProvider !== "whatsapp" &&
|
||||
deliveryProvider !== "telegram" &&
|
||||
deliveryProvider !== "discord" &&
|
||||
deliveryProvider !== "slack" &&
|
||||
deliveryProvider !== "signal" &&
|
||||
deliveryProvider !== "imessage" &&
|
||||
deliveryProvider !== "webchat"
|
||||
@@ -574,6 +585,7 @@ export async function agentCommand(
|
||||
deliveryProvider === "whatsapp" ||
|
||||
deliveryProvider === "telegram" ||
|
||||
deliveryProvider === "discord" ||
|
||||
deliveryProvider === "slack" ||
|
||||
deliveryProvider === "signal" ||
|
||||
deliveryProvider === "imessage"
|
||||
? resolveTextChunkLimit(cfg, deliveryProvider)
|
||||
@@ -666,6 +678,26 @@ export async function agentCommand(
|
||||
}
|
||||
}
|
||||
|
||||
if (deliveryProvider === "slack" && slackTarget) {
|
||||
try {
|
||||
if (media.length === 0) {
|
||||
await deps.sendMessageSlack(slackTarget, text);
|
||||
} else {
|
||||
let first = true;
|
||||
for (const url of media) {
|
||||
const caption = first ? text : "";
|
||||
first = false;
|
||||
await deps.sendMessageSlack(slackTarget, caption, {
|
||||
mediaUrl: url,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (!bestEffortDeliver) throw err;
|
||||
logDeliveryError(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (deliveryProvider === "signal" && signalTarget) {
|
||||
try {
|
||||
if (media.length === 0) {
|
||||
|
||||
@@ -31,6 +31,7 @@ async function noteProviderPrimer(prompter: WizardPrompter): Promise<void> {
|
||||
"WhatsApp: dedicated second number recommended; primary number OK (self-chat).",
|
||||
"Telegram: Bot API (token from @BotFather), replies via your bot.",
|
||||
"Discord: Bot token from Discord Developer Portal; invite bot to your server.",
|
||||
"Slack: Socket Mode app token + bot token, DMs via App Home Messages tab.",
|
||||
"Signal: signal-cli as a linked device; separate number recommended.",
|
||||
"iMessage: local imsg CLI; separate Apple ID recommended only on a separate Mac.",
|
||||
].join("\n"),
|
||||
@@ -74,6 +75,10 @@ function buildSlackManifest(botName: string) {
|
||||
display_name: safeName,
|
||||
always_online: false,
|
||||
},
|
||||
app_home: {
|
||||
messages_tab_enabled: true,
|
||||
messages_tab_read_only_enabled: false,
|
||||
},
|
||||
slash_commands: [
|
||||
{
|
||||
command: "/clawd",
|
||||
@@ -94,6 +99,7 @@ function buildSlackManifest(botName: string) {
|
||||
"users:read",
|
||||
"app_mentions:read",
|
||||
"reactions:read",
|
||||
"reactions:write",
|
||||
"pins:read",
|
||||
"pins:write",
|
||||
"emoji:read",
|
||||
@@ -137,6 +143,7 @@ async function noteSlackTokenHelp(
|
||||
"2) Add Socket Mode + enable it to get the app-level token (xapp-...)",
|
||||
"3) OAuth & Permissions → install app to workspace (xoxb- bot token)",
|
||||
"4) Enable Event Subscriptions (socket) for message events",
|
||||
"5) App Home → enable the Messages tab for DMs",
|
||||
"Tip: set SLACK_BOT_TOKEN + SLACK_APP_TOKEN in your env.",
|
||||
"",
|
||||
"Manifest (JSON):",
|
||||
@@ -237,10 +244,16 @@ export async function setupProviders(
|
||||
const whatsappLinked = await detectWhatsAppLinked();
|
||||
const telegramEnv = Boolean(process.env.TELEGRAM_BOT_TOKEN?.trim());
|
||||
const discordEnv = Boolean(process.env.DISCORD_BOT_TOKEN?.trim());
|
||||
const slackBotEnv = Boolean(process.env.SLACK_BOT_TOKEN?.trim());
|
||||
const slackAppEnv = Boolean(process.env.SLACK_APP_TOKEN?.trim());
|
||||
const telegramConfigured = Boolean(
|
||||
telegramEnv || cfg.telegram?.botToken || cfg.telegram?.tokenFile,
|
||||
);
|
||||
const discordConfigured = Boolean(discordEnv || cfg.discord?.token);
|
||||
const slackConfigured = Boolean(
|
||||
(slackBotEnv && slackAppEnv) ||
|
||||
(cfg.slack?.botToken && cfg.slack?.appToken),
|
||||
);
|
||||
const signalConfigured = Boolean(
|
||||
cfg.signal?.account || cfg.signal?.httpUrl || cfg.signal?.httpPort,
|
||||
);
|
||||
@@ -257,6 +270,7 @@ export async function setupProviders(
|
||||
`WhatsApp: ${whatsappLinked ? "linked" : "not linked"}`,
|
||||
`Telegram: ${telegramConfigured ? "configured" : "needs token"}`,
|
||||
`Discord: ${discordConfigured ? "configured" : "needs token"}`,
|
||||
`Slack: ${slackConfigured ? "configured" : "needs tokens"}`,
|
||||
`Signal: ${signalConfigured ? "configured" : "needs setup"}`,
|
||||
`iMessage: ${imessageConfigured ? "configured" : "needs setup"}`,
|
||||
`signal-cli: ${signalCliDetected ? "found" : "missing"} (${signalCliPath})`,
|
||||
@@ -291,6 +305,11 @@ export async function setupProviders(
|
||||
label: "Discord (Bot API)",
|
||||
hint: discordConfigured ? "configured" : "needs token",
|
||||
},
|
||||
{
|
||||
value: "slack",
|
||||
label: "Slack (Socket Mode)",
|
||||
hint: slackConfigured ? "configured" : "needs tokens",
|
||||
},
|
||||
{
|
||||
value: "signal",
|
||||
label: "Signal (signal-cli)",
|
||||
@@ -695,6 +714,19 @@ export async function setupProviders(
|
||||
}
|
||||
}
|
||||
|
||||
if (!selection.includes("slack") && slackConfigured) {
|
||||
const disable = await prompter.confirm({
|
||||
message: "Disable Slack provider?",
|
||||
initialValue: false,
|
||||
});
|
||||
if (disable) {
|
||||
next = {
|
||||
...next,
|
||||
slack: { ...next.slack, enabled: false },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (!selection.includes("signal") && signalConfigured) {
|
||||
const disable = await prompter.confirm({
|
||||
message: "Disable Signal provider?",
|
||||
@@ -724,4 +756,3 @@ export async function setupProviders(
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export type ProviderChoice =
|
||||
| "whatsapp"
|
||||
| "telegram"
|
||||
| "discord"
|
||||
| "slack"
|
||||
| "signal"
|
||||
| "imessage";
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ const makeDeps = (overrides: Partial<CliDeps> = {}): CliDeps => ({
|
||||
sendMessageWhatsApp: vi.fn(),
|
||||
sendMessageTelegram: vi.fn(),
|
||||
sendMessageDiscord: vi.fn(),
|
||||
sendMessageSlack: vi.fn(),
|
||||
sendMessageSignal: vi.fn(),
|
||||
sendMessageIMessage: vi.fn(),
|
||||
...overrides,
|
||||
@@ -173,6 +174,25 @@ describe("sendCommand", () => {
|
||||
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes to slack provider", async () => {
|
||||
const deps = makeDeps({
|
||||
sendMessageSlack: vi
|
||||
.fn()
|
||||
.mockResolvedValue({ messageId: "s1", channelId: "C123" }),
|
||||
});
|
||||
await sendCommand(
|
||||
{ to: "channel:C123", message: "hi", provider: "slack" },
|
||||
deps,
|
||||
runtime,
|
||||
);
|
||||
expect(deps.sendMessageSlack).toHaveBeenCalledWith(
|
||||
"channel:C123",
|
||||
"hi",
|
||||
expect.objectContaining({ mediaUrl: undefined }),
|
||||
);
|
||||
expect(deps.sendMessageWhatsApp).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("routes to imessage provider", async () => {
|
||||
const deps = makeDeps({
|
||||
sendMessageIMessage: vi.fn().mockResolvedValue({ messageId: "i1" }),
|
||||
|
||||
@@ -86,6 +86,34 @@ export async function sendCommand(
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider === "slack") {
|
||||
const result = await deps.sendMessageSlack(opts.to, opts.message, {
|
||||
mediaUrl: opts.media,
|
||||
});
|
||||
runtime.log(
|
||||
success(
|
||||
`✅ Sent via slack. Message ID: ${result.messageId} (channel ${result.channelId})`,
|
||||
),
|
||||
);
|
||||
if (opts.json) {
|
||||
runtime.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
provider: "slack",
|
||||
via: "direct",
|
||||
to: opts.to,
|
||||
channelId: result.channelId,
|
||||
messageId: result.messageId,
|
||||
mediaUrl: opts.media ?? null,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (provider === "signal") {
|
||||
const result = await deps.sendMessageSignal(opts.to, opts.message, {
|
||||
mediaUrl: opts.media,
|
||||
|
||||
Reference in New Issue
Block a user