mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-09 05:19:32 +08:00
IRC: fix DM target + onboarding allowFrom consistency
This commit is contained in:
43
extensions/irc/src/monitor.test.ts
Normal file
43
extensions/irc/src/monitor.test.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { resolveIrcInboundTarget } from "./monitor.js";
|
||||
|
||||
describe("irc monitor inbound target", () => {
|
||||
it("keeps channel target for group messages", () => {
|
||||
expect(
|
||||
resolveIrcInboundTarget({
|
||||
target: "#openclaw",
|
||||
senderNick: "alice",
|
||||
}),
|
||||
).toEqual({
|
||||
isGroup: true,
|
||||
target: "#openclaw",
|
||||
rawTarget: "#openclaw",
|
||||
});
|
||||
});
|
||||
|
||||
it("maps DM target to sender nick and preserves raw target", () => {
|
||||
expect(
|
||||
resolveIrcInboundTarget({
|
||||
target: "openclaw-bot",
|
||||
senderNick: "alice",
|
||||
}),
|
||||
).toEqual({
|
||||
isGroup: false,
|
||||
target: "alice",
|
||||
rawTarget: "openclaw-bot",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to raw target when sender nick is empty", () => {
|
||||
expect(
|
||||
resolveIrcInboundTarget({
|
||||
target: "openclaw-bot",
|
||||
senderNick: " ",
|
||||
}),
|
||||
).toEqual({
|
||||
isGroup: false,
|
||||
target: "openclaw-bot",
|
||||
rawTarget: "openclaw-bot",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,6 +16,20 @@ export type IrcMonitorOptions = {
|
||||
onMessage?: (message: IrcInboundMessage, client: IrcClient) => void | Promise<void>;
|
||||
};
|
||||
|
||||
export function resolveIrcInboundTarget(params: { target: string; senderNick: string }): {
|
||||
isGroup: boolean;
|
||||
target: string;
|
||||
rawTarget: string;
|
||||
} {
|
||||
const rawTarget = params.target;
|
||||
const isGroup = isChannelTarget(rawTarget);
|
||||
if (isGroup) {
|
||||
return { isGroup: true, target: rawTarget, rawTarget };
|
||||
}
|
||||
const senderNick = params.senderNick.trim();
|
||||
return { isGroup: false, target: senderNick || rawTarget, rawTarget };
|
||||
}
|
||||
|
||||
export async function monitorIrcProvider(opts: IrcMonitorOptions): Promise<{ stop: () => void }> {
|
||||
const core = getIrcRuntime();
|
||||
const cfg = opts.config ?? (core.config.loadConfig() as CoreConfig);
|
||||
@@ -83,16 +97,20 @@ export async function monitorIrcProvider(opts: IrcMonitorOptions): Promise<{ sto
|
||||
return;
|
||||
}
|
||||
|
||||
const isGroup = isChannelTarget(event.target);
|
||||
const inboundTarget = resolveIrcInboundTarget({
|
||||
target: event.target,
|
||||
senderNick: event.senderNick,
|
||||
});
|
||||
const message: IrcInboundMessage = {
|
||||
messageId: makeIrcMessageId(),
|
||||
target: event.target,
|
||||
target: inboundTarget.target,
|
||||
rawTarget: inboundTarget.rawTarget,
|
||||
senderNick: event.senderNick,
|
||||
senderUser: event.senderUser,
|
||||
senderHost: event.senderHost,
|
||||
text: event.text,
|
||||
timestamp: Date.now(),
|
||||
isGroup,
|
||||
isGroup: inboundTarget.isGroup,
|
||||
};
|
||||
|
||||
core.channel.activity.record({
|
||||
|
||||
@@ -72,4 +72,47 @@ describe("irc onboarding", () => {
|
||||
expect(result.cfg.channels?.irc?.groupPolicy).toBe("allowlist");
|
||||
expect(Object.keys(result.cfg.channels?.irc?.groups ?? {})).toEqual(["#openclaw", "#ops"]);
|
||||
});
|
||||
|
||||
it("writes DM allowFrom to top-level config for non-default account prompts", async () => {
|
||||
const prompter: WizardPrompter = {
|
||||
intro: vi.fn(async () => {}),
|
||||
outro: vi.fn(async () => {}),
|
||||
note: vi.fn(async () => {}),
|
||||
select: vi.fn(async () => "allowlist"),
|
||||
multiselect: vi.fn(async () => []),
|
||||
text: vi.fn(async ({ message }: { message: string }) => {
|
||||
if (message === "IRC allowFrom (nick or nick!user@host)") {
|
||||
return "Alice, Bob!ident@example.org";
|
||||
}
|
||||
throw new Error(`Unexpected prompt: ${message}`);
|
||||
}) as WizardPrompter["text"],
|
||||
confirm: vi.fn(async () => false),
|
||||
progress: vi.fn(() => ({ update: vi.fn(), stop: vi.fn() })),
|
||||
};
|
||||
|
||||
const promptAllowFrom = ircOnboardingAdapter.dmPolicy?.promptAllowFrom;
|
||||
expect(promptAllowFrom).toBeTypeOf("function");
|
||||
|
||||
const cfg: CoreConfig = {
|
||||
channels: {
|
||||
irc: {
|
||||
accounts: {
|
||||
work: {
|
||||
host: "irc.libera.chat",
|
||||
nick: "openclaw-work",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updated = (await promptAllowFrom?.({
|
||||
cfg,
|
||||
prompter,
|
||||
accountId: "work",
|
||||
})) as CoreConfig;
|
||||
|
||||
expect(updated.channels?.irc?.allowFrom).toEqual(["alice", "bob!ident@example.org"]);
|
||||
expect(updated.channels?.irc?.accounts?.work?.allowFrom).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -105,8 +105,17 @@ function setIrcDmPolicy(cfg: CoreConfig, dmPolicy: DmPolicy): CoreConfig {
|
||||
};
|
||||
}
|
||||
|
||||
function setIrcAllowFrom(cfg: CoreConfig, accountId: string, allowFrom: string[]): CoreConfig {
|
||||
return updateIrcAccountConfig(cfg, accountId, { allowFrom });
|
||||
function setIrcAllowFrom(cfg: CoreConfig, allowFrom: string[]): CoreConfig {
|
||||
return {
|
||||
...cfg,
|
||||
channels: {
|
||||
...cfg.channels,
|
||||
irc: {
|
||||
...cfg.channels?.irc,
|
||||
allowFrom,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function setIrcNickServ(
|
||||
@@ -157,9 +166,7 @@ async function promptIrcAllowFrom(params: {
|
||||
prompter: WizardPrompter;
|
||||
accountId?: string;
|
||||
}): Promise<CoreConfig> {
|
||||
const accountId = params.accountId?.trim() || resolveDefaultIrcAccountId(params.cfg);
|
||||
const resolved = resolveIrcAccount({ cfg: params.cfg, accountId });
|
||||
const existing = resolved.config.allowFrom ?? [];
|
||||
const existing = params.cfg.channels?.irc?.allowFrom ?? [];
|
||||
|
||||
await params.prompter.note(
|
||||
[
|
||||
@@ -188,7 +195,7 @@ async function promptIrcAllowFrom(params: {
|
||||
.filter(Boolean),
|
||||
),
|
||||
];
|
||||
return setIrcAllowFrom(params.cfg, accountId, normalized);
|
||||
return setIrcAllowFrom(params.cfg, normalized);
|
||||
}
|
||||
|
||||
async function promptIrcNickServConfig(params: {
|
||||
|
||||
@@ -77,7 +77,10 @@ export type CoreConfig = OpenClawConfig & {
|
||||
|
||||
export type IrcInboundMessage = {
|
||||
messageId: string;
|
||||
/** Conversation peer id: channel name for groups, sender nick for DMs. */
|
||||
target: string;
|
||||
/** Raw IRC PRIVMSG target (bot nick for DMs, channel for groups). */
|
||||
rawTarget?: string;
|
||||
senderNick: string;
|
||||
senderUser?: string;
|
||||
senderHost?: string;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { GroupPolicy } from "./types.base.js";
|
||||
import type { DiscordConfig } from "./types.discord.js";
|
||||
import type { IrcConfig } from "./types.irc.js";
|
||||
import type { GoogleChatConfig } from "./types.googlechat.js";
|
||||
import type { IMessageConfig } from "./types.imessage.js";
|
||||
import type { IrcConfig } from "./types.irc.js";
|
||||
import type { MSTeamsConfig } from "./types.msteams.js";
|
||||
import type { SignalConfig } from "./types.signal.js";
|
||||
import type { SlackConfig } from "./types.slack.js";
|
||||
|
||||
Reference in New Issue
Block a user