Files
Moltbot/src/plugins/runtime/index.ts
2026-02-17 10:57:31 +09:00

436 lines
15 KiB
TypeScript

import { createRequire } from "node:module";
import { resolveEffectiveMessagesConfig, resolveHumanDelayConfig } from "../../agents/identity.js";
import { createMemoryGetTool, createMemorySearchTool } from "../../agents/tools/memory-tool.js";
import { handleSlackAction } from "../../agents/tools/slack-actions.js";
import {
chunkByNewline,
chunkMarkdownText,
chunkMarkdownTextWithMode,
chunkText,
chunkTextWithMode,
resolveChunkMode,
resolveTextChunkLimit,
} from "../../auto-reply/chunk.js";
import {
hasControlCommand,
isControlCommandMessage,
shouldComputeCommandAuthorized,
} from "../../auto-reply/command-detection.js";
import { shouldHandleTextCommands } from "../../auto-reply/commands-registry.js";
import {
formatAgentEnvelope,
formatInboundEnvelope,
resolveEnvelopeFormatOptions,
} from "../../auto-reply/envelope.js";
import {
createInboundDebouncer,
resolveInboundDebounceMs,
} from "../../auto-reply/inbound-debounce.js";
import { dispatchReplyFromConfig } from "../../auto-reply/reply/dispatch-from-config.js";
import { finalizeInboundContext } from "../../auto-reply/reply/inbound-context.js";
import {
buildMentionRegexes,
matchesMentionPatterns,
matchesMentionWithExplicit,
} from "../../auto-reply/reply/mentions.js";
import { dispatchReplyWithBufferedBlockDispatcher } from "../../auto-reply/reply/provider-dispatcher.js";
import { createReplyDispatcherWithTyping } from "../../auto-reply/reply/reply-dispatcher.js";
import { removeAckReactionAfterReply, shouldAckReaction } from "../../channels/ack-reactions.js";
import { resolveCommandAuthorizedFromAuthorizers } from "../../channels/command-gating.js";
import { discordMessageActions } from "../../channels/plugins/actions/discord.js";
import { signalMessageActions } from "../../channels/plugins/actions/signal.js";
import { telegramMessageActions } from "../../channels/plugins/actions/telegram.js";
import { createWhatsAppLoginTool } from "../../channels/plugins/agent-tools/whatsapp-login.js";
import { recordInboundSession } from "../../channels/session.js";
import { registerMemoryCli } from "../../cli/memory-cli.js";
import { loadConfig, writeConfigFile } from "../../config/config.js";
import {
resolveChannelGroupPolicy,
resolveChannelGroupRequireMention,
} from "../../config/group-policy.js";
import { resolveMarkdownTableMode } from "../../config/markdown-tables.js";
import { resolveStateDir } from "../../config/paths.js";
import {
readSessionUpdatedAt,
recordSessionMetaFromInbound,
resolveStorePath,
updateLastRoute,
} from "../../config/sessions.js";
import { auditDiscordChannelPermissions } from "../../discord/audit.js";
import {
listDiscordDirectoryGroupsLive,
listDiscordDirectoryPeersLive,
} from "../../discord/directory-live.js";
import { monitorDiscordProvider } from "../../discord/monitor.js";
import { probeDiscord } from "../../discord/probe.js";
import { resolveDiscordChannelAllowlist } from "../../discord/resolve-channels.js";
import { resolveDiscordUserAllowlist } from "../../discord/resolve-users.js";
import { sendMessageDiscord, sendPollDiscord } from "../../discord/send.js";
import { shouldLogVerbose } from "../../globals.js";
import { monitorIMessageProvider } from "../../imessage/monitor.js";
import { probeIMessage } from "../../imessage/probe.js";
import { sendMessageIMessage } from "../../imessage/send.js";
import { getChannelActivity, recordChannelActivity } from "../../infra/channel-activity.js";
import { enqueueSystemEvent } from "../../infra/system-events.js";
import {
listLineAccountIds,
normalizeAccountId as normalizeLineAccountId,
resolveDefaultLineAccountId,
resolveLineAccount,
} from "../../line/accounts.js";
import { monitorLineProvider } from "../../line/monitor.js";
import { probeLineBot } from "../../line/probe.js";
import {
createQuickReplyItems,
pushMessageLine,
pushMessagesLine,
pushFlexMessage,
pushTemplateMessage,
pushLocationMessage,
pushTextMessageWithQuickReplies,
sendMessageLine,
} from "../../line/send.js";
import { buildTemplateMessageFromPayload } from "../../line/template-messages.js";
import { getChildLogger } from "../../logging.js";
import { normalizeLogLevel } from "../../logging/levels.js";
import { convertMarkdownTables } from "../../markdown/tables.js";
import { isVoiceCompatibleAudio } from "../../media/audio.js";
import { mediaKindFromMime } from "../../media/constants.js";
import { fetchRemoteMedia } from "../../media/fetch.js";
import { getImageMetadata, resizeToJpeg } from "../../media/image-ops.js";
import { detectMime } from "../../media/mime.js";
import { saveMediaBuffer } from "../../media/store.js";
import { buildPairingReply } from "../../pairing/pairing-messages.js";
import {
readChannelAllowFromStore,
upsertChannelPairingRequest,
} from "../../pairing/pairing-store.js";
import { runCommandWithTimeout } from "../../process/exec.js";
import { resolveAgentRoute } from "../../routing/resolve-route.js";
import { monitorSignalProvider } from "../../signal/index.js";
import { probeSignal } from "../../signal/probe.js";
import { sendMessageSignal } from "../../signal/send.js";
import {
listSlackDirectoryGroupsLive,
listSlackDirectoryPeersLive,
} from "../../slack/directory-live.js";
import { monitorSlackProvider } from "../../slack/index.js";
import { probeSlack } from "../../slack/probe.js";
import { resolveSlackChannelAllowlist } from "../../slack/resolve-channels.js";
import { resolveSlackUserAllowlist } from "../../slack/resolve-users.js";
import { sendMessageSlack } from "../../slack/send.js";
import {
auditTelegramGroupMembership,
collectTelegramUnmentionedGroupIds,
} from "../../telegram/audit.js";
import { monitorTelegramProvider } from "../../telegram/monitor.js";
import { probeTelegram } from "../../telegram/probe.js";
import { sendMessageTelegram, sendPollTelegram } from "../../telegram/send.js";
import { resolveTelegramToken } from "../../telegram/token.js";
import { textToSpeechTelephony } from "../../tts/tts.js";
import { getActiveWebListener } from "../../web/active-listener.js";
import {
getWebAuthAgeMs,
logoutWeb,
logWebSelfId,
readWebSelfId,
webAuthExists,
} from "../../web/auth-store.js";
import { loadWebMedia } from "../../web/media.js";
import { formatNativeDependencyHint } from "./native-deps.js";
import type { PluginRuntime } from "./types.js";
let cachedVersion: string | null = null;
function resolveVersion(): string {
if (cachedVersion) {
return cachedVersion;
}
try {
const require = createRequire(import.meta.url);
const pkg = require("../../../package.json") as { version?: string };
cachedVersion = pkg.version ?? "unknown";
return cachedVersion;
} catch {
cachedVersion = "unknown";
return cachedVersion;
}
}
const sendMessageWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendMessageWhatsApp"] = async (
...args
) => {
const { sendMessageWhatsApp } = await loadWebOutbound();
return sendMessageWhatsApp(...args);
};
const sendPollWhatsAppLazy: PluginRuntime["channel"]["whatsapp"]["sendPollWhatsApp"] = async (
...args
) => {
const { sendPollWhatsApp } = await loadWebOutbound();
return sendPollWhatsApp(...args);
};
const loginWebLazy: PluginRuntime["channel"]["whatsapp"]["loginWeb"] = async (...args) => {
const { loginWeb } = await loadWebLogin();
return loginWeb(...args);
};
const startWebLoginWithQrLazy: PluginRuntime["channel"]["whatsapp"]["startWebLoginWithQr"] = async (
...args
) => {
const { startWebLoginWithQr } = await loadWebLoginQr();
return startWebLoginWithQr(...args);
};
const waitForWebLoginLazy: PluginRuntime["channel"]["whatsapp"]["waitForWebLogin"] = async (
...args
) => {
const { waitForWebLogin } = await loadWebLoginQr();
return waitForWebLogin(...args);
};
const monitorWebChannelLazy: PluginRuntime["channel"]["whatsapp"]["monitorWebChannel"] = async (
...args
) => {
const { monitorWebChannel } = await loadWebChannel();
return monitorWebChannel(...args);
};
const handleWhatsAppActionLazy: PluginRuntime["channel"]["whatsapp"]["handleWhatsAppAction"] =
async (...args) => {
const { handleWhatsAppAction } = await loadWhatsAppActions();
return handleWhatsAppAction(...args);
};
let webOutboundPromise: Promise<typeof import("../../web/outbound.js")> | null = null;
let webLoginPromise: Promise<typeof import("../../web/login.js")> | null = null;
let webLoginQrPromise: Promise<typeof import("../../web/login-qr.js")> | null = null;
let webChannelPromise: Promise<typeof import("../../channels/web/index.js")> | null = null;
let whatsappActionsPromise: Promise<
typeof import("../../agents/tools/whatsapp-actions.js")
> | null = null;
function loadWebOutbound() {
webOutboundPromise ??= import("../../web/outbound.js");
return webOutboundPromise;
}
function loadWebLogin() {
webLoginPromise ??= import("../../web/login.js");
return webLoginPromise;
}
function loadWebLoginQr() {
webLoginQrPromise ??= import("../../web/login-qr.js");
return webLoginQrPromise;
}
function loadWebChannel() {
webChannelPromise ??= import("../../channels/web/index.js");
return webChannelPromise;
}
function loadWhatsAppActions() {
whatsappActionsPromise ??= import("../../agents/tools/whatsapp-actions.js");
return whatsappActionsPromise;
}
export function createPluginRuntime(): PluginRuntime {
return {
version: resolveVersion(),
config: {
loadConfig,
writeConfigFile,
},
system: {
enqueueSystemEvent,
runCommandWithTimeout,
formatNativeDependencyHint,
},
media: {
loadWebMedia,
detectMime,
mediaKindFromMime,
isVoiceCompatibleAudio,
getImageMetadata,
resizeToJpeg,
},
tts: {
textToSpeechTelephony,
},
tools: {
createMemoryGetTool,
createMemorySearchTool,
registerMemoryCli,
},
channel: {
text: {
chunkByNewline,
chunkMarkdownText,
chunkMarkdownTextWithMode,
chunkText,
chunkTextWithMode,
resolveChunkMode,
resolveTextChunkLimit,
hasControlCommand,
resolveMarkdownTableMode,
convertMarkdownTables,
},
reply: {
dispatchReplyWithBufferedBlockDispatcher,
createReplyDispatcherWithTyping,
resolveEffectiveMessagesConfig,
resolveHumanDelayConfig,
dispatchReplyFromConfig,
finalizeInboundContext,
formatAgentEnvelope,
/** @deprecated Prefer `BodyForAgent` + structured user-context blocks (do not build plaintext envelopes for prompts). */
formatInboundEnvelope,
resolveEnvelopeFormatOptions,
},
routing: {
resolveAgentRoute,
},
pairing: {
buildPairingReply,
readAllowFromStore: readChannelAllowFromStore,
upsertPairingRequest: upsertChannelPairingRequest,
},
media: {
fetchRemoteMedia,
saveMediaBuffer,
},
activity: {
record: recordChannelActivity,
get: getChannelActivity,
},
session: {
resolveStorePath,
readSessionUpdatedAt,
recordSessionMetaFromInbound,
recordInboundSession,
updateLastRoute,
},
mentions: {
buildMentionRegexes,
matchesMentionPatterns,
matchesMentionWithExplicit,
},
reactions: {
shouldAckReaction,
removeAckReactionAfterReply,
},
groups: {
resolveGroupPolicy: resolveChannelGroupPolicy,
resolveRequireMention: resolveChannelGroupRequireMention,
},
debounce: {
createInboundDebouncer,
resolveInboundDebounceMs,
},
commands: {
resolveCommandAuthorizedFromAuthorizers,
isControlCommandMessage,
shouldComputeCommandAuthorized,
shouldHandleTextCommands,
},
discord: {
messageActions: discordMessageActions,
auditChannelPermissions: auditDiscordChannelPermissions,
listDirectoryGroupsLive: listDiscordDirectoryGroupsLive,
listDirectoryPeersLive: listDiscordDirectoryPeersLive,
probeDiscord,
resolveChannelAllowlist: resolveDiscordChannelAllowlist,
resolveUserAllowlist: resolveDiscordUserAllowlist,
sendMessageDiscord,
sendPollDiscord,
monitorDiscordProvider,
},
slack: {
listDirectoryGroupsLive: listSlackDirectoryGroupsLive,
listDirectoryPeersLive: listSlackDirectoryPeersLive,
probeSlack,
resolveChannelAllowlist: resolveSlackChannelAllowlist,
resolveUserAllowlist: resolveSlackUserAllowlist,
sendMessageSlack,
monitorSlackProvider,
handleSlackAction,
},
telegram: {
auditGroupMembership: auditTelegramGroupMembership,
collectUnmentionedGroupIds: collectTelegramUnmentionedGroupIds,
probeTelegram,
resolveTelegramToken,
sendMessageTelegram,
sendPollTelegram,
monitorTelegramProvider,
messageActions: telegramMessageActions,
},
signal: {
probeSignal,
sendMessageSignal,
monitorSignalProvider,
messageActions: signalMessageActions,
},
imessage: {
monitorIMessageProvider,
probeIMessage,
sendMessageIMessage,
},
whatsapp: {
getActiveWebListener,
getWebAuthAgeMs,
logoutWeb,
logWebSelfId,
readWebSelfId,
webAuthExists,
sendMessageWhatsApp: sendMessageWhatsAppLazy,
sendPollWhatsApp: sendPollWhatsAppLazy,
loginWeb: loginWebLazy,
startWebLoginWithQr: startWebLoginWithQrLazy,
waitForWebLogin: waitForWebLoginLazy,
monitorWebChannel: monitorWebChannelLazy,
handleWhatsAppAction: handleWhatsAppActionLazy,
createLoginTool: createWhatsAppLoginTool,
},
line: {
listLineAccountIds,
resolveDefaultLineAccountId,
resolveLineAccount,
normalizeAccountId: normalizeLineAccountId,
probeLineBot,
sendMessageLine,
pushMessageLine,
pushMessagesLine,
pushFlexMessage,
pushTemplateMessage,
pushLocationMessage,
pushTextMessageWithQuickReplies,
createQuickReplyItems,
buildTemplateMessageFromPayload,
monitorLineProvider,
},
},
logging: {
shouldLogVerbose,
getChildLogger: (bindings, opts) => {
const logger = getChildLogger(bindings, {
level: opts?.level ? normalizeLogLevel(opts.level) : undefined,
});
return {
debug: (message) => logger.debug?.(message),
info: (message) => logger.info(message),
warn: (message) => logger.warn(message),
error: (message) => logger.error(message),
};
},
},
state: {
resolveStateDir,
},
};
}
export type { PluginRuntime } from "./types.js";