mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-07 22:44:16 +00:00
148 lines
4.5 KiB
TypeScript
148 lines
4.5 KiB
TypeScript
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
import { resolveIrcAccount } from "./accounts.js";
|
|
import { connectIrcClient, type IrcClient } from "./client.js";
|
|
import { buildIrcConnectOptions } from "./connect-options.js";
|
|
import { handleIrcInbound } from "./inbound.js";
|
|
import { isChannelTarget } from "./normalize.js";
|
|
import { makeIrcMessageId } from "./protocol.js";
|
|
import { getIrcRuntime } from "./runtime.js";
|
|
import type { CoreConfig, IrcInboundMessage } from "./types.js";
|
|
|
|
export type IrcMonitorOptions = {
|
|
accountId?: string;
|
|
config?: CoreConfig;
|
|
runtime?: RuntimeEnv;
|
|
abortSignal?: AbortSignal;
|
|
statusSink?: (patch: { lastInboundAt?: number; lastOutboundAt?: number }) => void;
|
|
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);
|
|
const account = resolveIrcAccount({
|
|
cfg,
|
|
accountId: opts.accountId,
|
|
});
|
|
|
|
const runtime: RuntimeEnv = opts.runtime ?? {
|
|
log: (...args: unknown[]) => core.logging.getChildLogger().info(args.map(String).join(" ")),
|
|
error: (...args: unknown[]) => core.logging.getChildLogger().error(args.map(String).join(" ")),
|
|
exit: () => {
|
|
throw new Error("Runtime exit not available");
|
|
},
|
|
};
|
|
|
|
if (!account.configured) {
|
|
throw new Error(
|
|
`IRC is not configured for account "${account.accountId}" (need host and nick in channels.irc).`,
|
|
);
|
|
}
|
|
|
|
const logger = core.logging.getChildLogger({
|
|
channel: "irc",
|
|
accountId: account.accountId,
|
|
});
|
|
|
|
let client: IrcClient | null = null;
|
|
|
|
client = await connectIrcClient(
|
|
buildIrcConnectOptions(account, {
|
|
channels: account.config.channels,
|
|
abortSignal: opts.abortSignal,
|
|
onLine: (line) => {
|
|
if (core.logging.shouldLogVerbose()) {
|
|
logger.debug?.(`[${account.accountId}] << ${line}`);
|
|
}
|
|
},
|
|
onNotice: (text, target) => {
|
|
if (core.logging.shouldLogVerbose()) {
|
|
logger.debug?.(`[${account.accountId}] notice ${target ?? ""}: ${text}`);
|
|
}
|
|
},
|
|
onError: (error) => {
|
|
logger.error(`[${account.accountId}] IRC error: ${error.message}`);
|
|
},
|
|
onPrivmsg: async (event) => {
|
|
if (!client) {
|
|
return;
|
|
}
|
|
if (event.senderNick.toLowerCase() === client.nick.toLowerCase()) {
|
|
return;
|
|
}
|
|
|
|
const inboundTarget = resolveIrcInboundTarget({
|
|
target: event.target,
|
|
senderNick: event.senderNick,
|
|
});
|
|
const message: IrcInboundMessage = {
|
|
messageId: makeIrcMessageId(),
|
|
target: inboundTarget.target,
|
|
rawTarget: inboundTarget.rawTarget,
|
|
senderNick: event.senderNick,
|
|
senderUser: event.senderUser,
|
|
senderHost: event.senderHost,
|
|
text: event.text,
|
|
timestamp: Date.now(),
|
|
isGroup: inboundTarget.isGroup,
|
|
};
|
|
|
|
core.channel.activity.record({
|
|
channel: "irc",
|
|
accountId: account.accountId,
|
|
direction: "inbound",
|
|
at: message.timestamp,
|
|
});
|
|
|
|
if (opts.onMessage) {
|
|
await opts.onMessage(message, client);
|
|
return;
|
|
}
|
|
|
|
await handleIrcInbound({
|
|
message,
|
|
account,
|
|
config: cfg,
|
|
runtime,
|
|
connectedNick: client.nick,
|
|
sendReply: async (target, text) => {
|
|
client?.sendPrivmsg(target, text);
|
|
opts.statusSink?.({ lastOutboundAt: Date.now() });
|
|
core.channel.activity.record({
|
|
channel: "irc",
|
|
accountId: account.accountId,
|
|
direction: "outbound",
|
|
});
|
|
},
|
|
statusSink: opts.statusSink,
|
|
});
|
|
},
|
|
}),
|
|
);
|
|
|
|
logger.info(
|
|
`[${account.accountId}] connected to ${account.host}:${account.port}${account.tls ? " (tls)" : ""} as ${client.nick}`,
|
|
);
|
|
|
|
return {
|
|
stop: () => {
|
|
client?.quit("shutdown");
|
|
client = null;
|
|
},
|
|
};
|
|
}
|