mirror of https://github.com/openclaw/openclaw.git
564 lines
20 KiB
TypeScript
564 lines
20 KiB
TypeScript
import { resolveDefaultModelForAgent } from "openclaw/plugin-sdk/agent-runtime";
|
|
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
import { resetInboundDedupe } from "openclaw/plugin-sdk/reply-runtime";
|
|
import type { MsgContext } from "openclaw/plugin-sdk/reply-runtime";
|
|
import type { GetReplyOptions, ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
|
|
import { createReplyDispatcher } from "openclaw/plugin-sdk/reply-runtime";
|
|
import type { MockFn } from "openclaw/plugin-sdk/testing";
|
|
import { beforeEach, vi } from "vitest";
|
|
import type { TelegramBotDeps } from "./bot-deps.js";
|
|
|
|
type AnyMock = ReturnType<typeof vi.fn>;
|
|
type AnyAsyncMock = ReturnType<typeof vi.fn>;
|
|
type LoadConfigFn = typeof import("openclaw/plugin-sdk/config-runtime").loadConfig;
|
|
type ResolveStorePathFn = typeof import("openclaw/plugin-sdk/config-runtime").resolveStorePath;
|
|
type TelegramBotRuntimeForTest = NonNullable<
|
|
Parameters<typeof import("./bot.js").setTelegramBotRuntimeForTest>[0]
|
|
>;
|
|
type DispatchReplyWithBufferedBlockDispatcherFn =
|
|
typeof import("openclaw/plugin-sdk/reply-runtime").dispatchReplyWithBufferedBlockDispatcher;
|
|
type DispatchReplyWithBufferedBlockDispatcherResult = Awaited<
|
|
ReturnType<DispatchReplyWithBufferedBlockDispatcherFn>
|
|
>;
|
|
type DispatchReplyHarnessParams = Parameters<DispatchReplyWithBufferedBlockDispatcherFn>[0];
|
|
|
|
const EMPTY_REPLY_COUNTS: DispatchReplyWithBufferedBlockDispatcherResult["counts"] = {
|
|
block: 0,
|
|
final: 0,
|
|
tool: 0,
|
|
};
|
|
|
|
const { sessionStorePath } = vi.hoisted(() => ({
|
|
sessionStorePath: `/tmp/openclaw-telegram-${process.pid}-${process.env.VITEST_POOL_ID ?? "0"}.json`,
|
|
}));
|
|
|
|
const { loadWebMedia } = vi.hoisted((): { loadWebMedia: AnyMock } => ({
|
|
loadWebMedia: vi.fn(),
|
|
}));
|
|
|
|
export function getLoadWebMediaMock(): AnyMock {
|
|
return loadWebMedia;
|
|
}
|
|
|
|
vi.mock("openclaw/plugin-sdk/web-media", () => ({
|
|
loadWebMedia,
|
|
}));
|
|
vi.mock("openclaw/plugin-sdk/web-media.js", () => ({
|
|
loadWebMedia,
|
|
}));
|
|
|
|
const { loadConfig, resolveStorePathMock } = vi.hoisted(
|
|
(): {
|
|
loadConfig: MockFn<LoadConfigFn>;
|
|
resolveStorePathMock: MockFn<ResolveStorePathFn>;
|
|
} => ({
|
|
loadConfig: vi.fn<LoadConfigFn>(() => ({})),
|
|
resolveStorePathMock: vi.fn<ResolveStorePathFn>(
|
|
(storePath?: string) => storePath ?? sessionStorePath,
|
|
),
|
|
}),
|
|
);
|
|
|
|
export function getLoadConfigMock(): AnyMock {
|
|
return loadConfig;
|
|
}
|
|
vi.doMock("openclaw/plugin-sdk/config-runtime", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/config-runtime")>();
|
|
return {
|
|
...actual,
|
|
loadConfig,
|
|
resolveStorePath: resolveStorePathMock,
|
|
};
|
|
});
|
|
|
|
const { readChannelAllowFromStore, upsertChannelPairingRequest } = vi.hoisted(
|
|
(): {
|
|
readChannelAllowFromStore: MockFn<TelegramBotDeps["readChannelAllowFromStore"]>;
|
|
upsertChannelPairingRequest: AnyAsyncMock;
|
|
} => ({
|
|
readChannelAllowFromStore: vi.fn(async () => [] as string[]),
|
|
upsertChannelPairingRequest: vi.fn(async () => ({
|
|
code: "PAIRCODE",
|
|
created: true,
|
|
})),
|
|
}),
|
|
);
|
|
|
|
export function getReadChannelAllowFromStoreMock(): AnyAsyncMock {
|
|
return readChannelAllowFromStore;
|
|
}
|
|
|
|
export function getUpsertChannelPairingRequestMock(): AnyAsyncMock {
|
|
return upsertChannelPairingRequest;
|
|
}
|
|
|
|
vi.doMock("openclaw/plugin-sdk/conversation-runtime", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/conversation-runtime")>();
|
|
return {
|
|
...actual,
|
|
readChannelAllowFromStore,
|
|
upsertChannelPairingRequest,
|
|
};
|
|
});
|
|
vi.doMock("openclaw/plugin-sdk/conversation-runtime.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/conversation-runtime")>();
|
|
return {
|
|
...actual,
|
|
readChannelAllowFromStore,
|
|
upsertChannelPairingRequest,
|
|
};
|
|
});
|
|
|
|
const skillCommandListHoisted = vi.hoisted(() => ({
|
|
listSkillCommandsForAgents: vi.fn(() => []),
|
|
}));
|
|
const modelProviderDataHoisted = vi.hoisted(() => ({
|
|
buildModelsProviderData: vi.fn(),
|
|
}));
|
|
const replySpyHoisted = vi.hoisted(() => ({
|
|
replySpy: vi.fn(async (_ctx: MsgContext, opts?: GetReplyOptions) => {
|
|
await opts?.onReplyStart?.();
|
|
return undefined;
|
|
}) as MockFn<
|
|
(
|
|
ctx: MsgContext,
|
|
opts?: GetReplyOptions,
|
|
configOverride?: OpenClawConfig,
|
|
) => Promise<ReplyPayload | ReplyPayload[] | undefined>
|
|
>,
|
|
}));
|
|
|
|
async function dispatchHarnessReplies(
|
|
params: DispatchReplyHarnessParams,
|
|
runReply: (
|
|
params: DispatchReplyHarnessParams,
|
|
) => Promise<ReplyPayload | ReplyPayload[] | undefined>,
|
|
): Promise<DispatchReplyWithBufferedBlockDispatcherResult> {
|
|
await params.dispatcherOptions.typingCallbacks?.onReplyStart?.();
|
|
const reply = await runReply(params);
|
|
const payloads: ReplyPayload[] =
|
|
reply === undefined ? [] : Array.isArray(reply) ? reply : [reply];
|
|
const dispatcher = createReplyDispatcher({
|
|
deliver: async (payload, info) => {
|
|
await params.dispatcherOptions.deliver?.(payload, info);
|
|
},
|
|
responsePrefix: params.dispatcherOptions.responsePrefix,
|
|
enableSlackInteractiveReplies: params.dispatcherOptions.enableSlackInteractiveReplies,
|
|
responsePrefixContextProvider: params.dispatcherOptions.responsePrefixContextProvider,
|
|
responsePrefixContext: params.dispatcherOptions.responsePrefixContext,
|
|
onHeartbeatStrip: params.dispatcherOptions.onHeartbeatStrip,
|
|
onSkip: (payload, info) => {
|
|
params.dispatcherOptions.onSkip?.(payload, info);
|
|
},
|
|
onError: (err, info) => {
|
|
params.dispatcherOptions.onError?.(err, info);
|
|
},
|
|
});
|
|
let finalCount = 0;
|
|
for (const payload of payloads) {
|
|
if (dispatcher.sendFinalReply(payload)) {
|
|
finalCount += 1;
|
|
}
|
|
}
|
|
dispatcher.markComplete();
|
|
await dispatcher.waitForIdle();
|
|
return {
|
|
queuedFinal: finalCount > 0,
|
|
counts: {
|
|
block: 0,
|
|
final: finalCount,
|
|
tool: 0,
|
|
},
|
|
};
|
|
}
|
|
|
|
const dispatchReplyHoisted = vi.hoisted(() => ({
|
|
dispatchReplyWithBufferedBlockDispatcher: vi.fn<DispatchReplyWithBufferedBlockDispatcherFn>(
|
|
async (params: DispatchReplyHarnessParams) =>
|
|
await dispatchHarnessReplies(params, async (dispatchParams) => {
|
|
return await replySpyHoisted.replySpy(dispatchParams.ctx, dispatchParams.replyOptions);
|
|
}),
|
|
),
|
|
}));
|
|
export const listSkillCommandsForAgents = skillCommandListHoisted.listSkillCommandsForAgents;
|
|
const buildModelsProviderData = modelProviderDataHoisted.buildModelsProviderData;
|
|
export const replySpy = replySpyHoisted.replySpy;
|
|
export const dispatchReplyWithBufferedBlockDispatcher =
|
|
dispatchReplyHoisted.dispatchReplyWithBufferedBlockDispatcher;
|
|
|
|
function parseModelRef(raw: string): { provider?: string; model: string } {
|
|
const trimmed = raw.trim();
|
|
if (!trimmed) {
|
|
return { model: "" };
|
|
}
|
|
const slashIndex = trimmed.indexOf("/");
|
|
if (slashIndex > 0 && slashIndex < trimmed.length - 1) {
|
|
return {
|
|
provider: trimmed.slice(0, slashIndex),
|
|
model: trimmed.slice(slashIndex + 1),
|
|
};
|
|
}
|
|
return { model: trimmed };
|
|
}
|
|
|
|
function createModelsProviderDataFromConfig(cfg: OpenClawConfig): {
|
|
byProvider: Map<string, Set<string>>;
|
|
providers: string[];
|
|
resolvedDefault: { provider: string; model: string };
|
|
} {
|
|
const byProvider = new Map<string, Set<string>>();
|
|
const add = (providerRaw: string | undefined, modelRaw: string | undefined) => {
|
|
const provider = providerRaw?.trim().toLowerCase();
|
|
const model = modelRaw?.trim();
|
|
if (!provider || !model) {
|
|
return;
|
|
}
|
|
const existing = byProvider.get(provider) ?? new Set<string>();
|
|
existing.add(model);
|
|
byProvider.set(provider, existing);
|
|
};
|
|
|
|
const resolvedDefault = resolveDefaultModelForAgent({ cfg });
|
|
add(resolvedDefault.provider, resolvedDefault.model);
|
|
|
|
for (const raw of Object.keys(cfg.agents?.defaults?.models ?? {})) {
|
|
const parsed = parseModelRef(raw);
|
|
add(parsed.provider ?? resolvedDefault.provider, parsed.model);
|
|
}
|
|
|
|
const providers = [...byProvider.keys()].toSorted();
|
|
return { byProvider, providers, resolvedDefault };
|
|
}
|
|
|
|
vi.doMock("openclaw/plugin-sdk/command-auth", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/command-auth")>();
|
|
return {
|
|
...actual,
|
|
listSkillCommandsForAgents: skillCommandListHoisted.listSkillCommandsForAgents,
|
|
buildModelsProviderData,
|
|
};
|
|
});
|
|
vi.doMock("openclaw/plugin-sdk/command-auth.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/command-auth")>();
|
|
return {
|
|
...actual,
|
|
listSkillCommandsForAgents: skillCommandListHoisted.listSkillCommandsForAgents,
|
|
buildModelsProviderData,
|
|
};
|
|
});
|
|
vi.doMock("openclaw/plugin-sdk/reply-runtime", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/reply-runtime")>();
|
|
return {
|
|
...actual,
|
|
getReplyFromConfig: replySpyHoisted.replySpy,
|
|
__replySpy: replySpyHoisted.replySpy,
|
|
dispatchReplyWithBufferedBlockDispatcher:
|
|
dispatchReplyHoisted.dispatchReplyWithBufferedBlockDispatcher,
|
|
};
|
|
});
|
|
vi.doMock("openclaw/plugin-sdk/reply-runtime.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/reply-runtime")>();
|
|
return {
|
|
...actual,
|
|
getReplyFromConfig: replySpyHoisted.replySpy,
|
|
__replySpy: replySpyHoisted.replySpy,
|
|
dispatchReplyWithBufferedBlockDispatcher:
|
|
dispatchReplyHoisted.dispatchReplyWithBufferedBlockDispatcher,
|
|
};
|
|
});
|
|
|
|
const systemEventsHoisted = vi.hoisted(() => ({
|
|
enqueueSystemEventSpy: vi.fn<TelegramBotDeps["enqueueSystemEvent"]>(() => false),
|
|
}));
|
|
export const enqueueSystemEventSpy: MockFn<TelegramBotDeps["enqueueSystemEvent"]> =
|
|
systemEventsHoisted.enqueueSystemEventSpy;
|
|
|
|
vi.doMock("openclaw/plugin-sdk/infra-runtime", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("openclaw/plugin-sdk/infra-runtime")>();
|
|
return {
|
|
...actual,
|
|
enqueueSystemEvent: systemEventsHoisted.enqueueSystemEventSpy,
|
|
};
|
|
});
|
|
|
|
const sentMessageCacheHoisted = vi.hoisted(() => ({
|
|
wasSentByBot: vi.fn(() => false),
|
|
}));
|
|
export const wasSentByBot = sentMessageCacheHoisted.wasSentByBot;
|
|
|
|
vi.doMock("./sent-message-cache.js", () => ({
|
|
wasSentByBot: sentMessageCacheHoisted.wasSentByBot,
|
|
recordSentMessage: vi.fn(),
|
|
clearSentMessageCache: vi.fn(),
|
|
}));
|
|
|
|
// All spy variables used inside vi.mock("grammy", ...) must be created via
|
|
// vi.hoisted() so they are available when the hoisted factory runs, regardless
|
|
// of module evaluation order across different test files.
|
|
const grammySpies = vi.hoisted(() => ({
|
|
useSpy: vi.fn() as MockFn<(arg: unknown) => void>,
|
|
middlewareUseSpy: vi.fn() as AnyMock,
|
|
onSpy: vi.fn() as AnyMock,
|
|
stopSpy: vi.fn() as AnyMock,
|
|
commandSpy: vi.fn() as AnyMock,
|
|
botCtorSpy: vi.fn((_: string, __?: { client?: { fetch?: typeof fetch } }) => undefined),
|
|
answerCallbackQuerySpy: vi.fn(async () => undefined) as AnyAsyncMock,
|
|
sendChatActionSpy: vi.fn() as AnyMock,
|
|
editMessageTextSpy: vi.fn(async () => ({ message_id: 88 })) as AnyAsyncMock,
|
|
editMessageReplyMarkupSpy: vi.fn(async () => ({ message_id: 88 })) as AnyAsyncMock,
|
|
sendMessageDraftSpy: vi.fn(async () => true) as AnyAsyncMock,
|
|
setMessageReactionSpy: vi.fn(async () => undefined) as AnyAsyncMock,
|
|
setMyCommandsSpy: vi.fn(async () => undefined) as AnyAsyncMock,
|
|
getMeSpy: vi.fn(async () => ({
|
|
username: "openclaw_bot",
|
|
has_topics_enabled: true,
|
|
})) as AnyAsyncMock,
|
|
sendMessageSpy: vi.fn(async () => ({ message_id: 77 })) as AnyAsyncMock,
|
|
sendAnimationSpy: vi.fn(async () => ({ message_id: 78 })) as AnyAsyncMock,
|
|
sendPhotoSpy: vi.fn(async () => ({ message_id: 79 })) as AnyAsyncMock,
|
|
getFileSpy: vi.fn(async () => ({ file_path: "media/file.jpg" })) as AnyAsyncMock,
|
|
}));
|
|
|
|
export const useSpy: MockFn<(arg: unknown) => void> = grammySpies.useSpy;
|
|
export const middlewareUseSpy: AnyMock = grammySpies.middlewareUseSpy;
|
|
export const onSpy: AnyMock = grammySpies.onSpy;
|
|
export const stopSpy: AnyMock = grammySpies.stopSpy;
|
|
export const commandSpy: AnyMock = grammySpies.commandSpy;
|
|
export const botCtorSpy: MockFn<
|
|
(token: string, options?: { client?: { fetch?: typeof fetch } }) => void
|
|
> = grammySpies.botCtorSpy;
|
|
export const answerCallbackQuerySpy: AnyAsyncMock = grammySpies.answerCallbackQuerySpy;
|
|
export const sendChatActionSpy: AnyMock = grammySpies.sendChatActionSpy;
|
|
export const editMessageTextSpy: AnyAsyncMock = grammySpies.editMessageTextSpy;
|
|
export const editMessageReplyMarkupSpy: AnyAsyncMock = grammySpies.editMessageReplyMarkupSpy;
|
|
export const sendMessageDraftSpy: AnyAsyncMock = grammySpies.sendMessageDraftSpy;
|
|
export const setMessageReactionSpy: AnyAsyncMock = grammySpies.setMessageReactionSpy;
|
|
export const setMyCommandsSpy: AnyAsyncMock = grammySpies.setMyCommandsSpy;
|
|
export const getMeSpy: AnyAsyncMock = grammySpies.getMeSpy;
|
|
export const sendMessageSpy: AnyAsyncMock = grammySpies.sendMessageSpy;
|
|
export const sendAnimationSpy: AnyAsyncMock = grammySpies.sendAnimationSpy;
|
|
export const sendPhotoSpy: AnyAsyncMock = grammySpies.sendPhotoSpy;
|
|
export const getFileSpy: AnyAsyncMock = grammySpies.getFileSpy;
|
|
|
|
const runnerHoisted = vi.hoisted(() => ({
|
|
sequentializeMiddleware: vi.fn(async (_ctx: unknown, next?: () => Promise<void>) => {
|
|
if (typeof next === "function") {
|
|
await next();
|
|
}
|
|
}),
|
|
sequentializeSpy: vi.fn(() => runnerHoisted.sequentializeMiddleware),
|
|
throttlerSpy: vi.fn(() => "throttler"),
|
|
}));
|
|
export const sequentializeSpy: AnyMock = runnerHoisted.sequentializeSpy;
|
|
export let sequentializeKey: ((ctx: unknown) => string) | undefined;
|
|
export const throttlerSpy: AnyMock = runnerHoisted.throttlerSpy;
|
|
export const telegramBotRuntimeForTest: TelegramBotRuntimeForTest = {
|
|
Bot: class {
|
|
api = {
|
|
config: { use: grammySpies.useSpy },
|
|
answerCallbackQuery: grammySpies.answerCallbackQuerySpy,
|
|
sendChatAction: grammySpies.sendChatActionSpy,
|
|
editMessageText: grammySpies.editMessageTextSpy,
|
|
editMessageReplyMarkup: grammySpies.editMessageReplyMarkupSpy,
|
|
sendMessageDraft: grammySpies.sendMessageDraftSpy,
|
|
setMessageReaction: grammySpies.setMessageReactionSpy,
|
|
setMyCommands: grammySpies.setMyCommandsSpy,
|
|
getMe: grammySpies.getMeSpy,
|
|
sendMessage: grammySpies.sendMessageSpy,
|
|
sendAnimation: grammySpies.sendAnimationSpy,
|
|
sendPhoto: grammySpies.sendPhotoSpy,
|
|
getFile: grammySpies.getFileSpy,
|
|
};
|
|
use = grammySpies.middlewareUseSpy;
|
|
on = grammySpies.onSpy;
|
|
stop = grammySpies.stopSpy;
|
|
command = grammySpies.commandSpy;
|
|
catch = vi.fn();
|
|
constructor(
|
|
public token: string,
|
|
public options?: { client?: { fetch?: typeof fetch } },
|
|
) {
|
|
(grammySpies.botCtorSpy as unknown as (token: string, options?: unknown) => void)(
|
|
token,
|
|
options,
|
|
);
|
|
}
|
|
} as unknown as TelegramBotRuntimeForTest["Bot"],
|
|
sequentialize: ((keyFn: (ctx: unknown) => string) => {
|
|
sequentializeKey = keyFn;
|
|
return (
|
|
runnerHoisted.sequentializeSpy as unknown as () => ReturnType<
|
|
TelegramBotRuntimeForTest["sequentialize"]
|
|
>
|
|
)();
|
|
}) as unknown as TelegramBotRuntimeForTest["sequentialize"],
|
|
apiThrottler: (() =>
|
|
(
|
|
runnerHoisted.throttlerSpy as unknown as () => unknown
|
|
)()) as unknown as TelegramBotRuntimeForTest["apiThrottler"],
|
|
};
|
|
export const telegramBotDepsForTest: TelegramBotDeps = {
|
|
loadConfig,
|
|
resolveStorePath: resolveStorePathMock,
|
|
readChannelAllowFromStore:
|
|
readChannelAllowFromStore as TelegramBotDeps["readChannelAllowFromStore"],
|
|
upsertChannelPairingRequest:
|
|
upsertChannelPairingRequest as TelegramBotDeps["upsertChannelPairingRequest"],
|
|
enqueueSystemEvent: enqueueSystemEventSpy as TelegramBotDeps["enqueueSystemEvent"],
|
|
dispatchReplyWithBufferedBlockDispatcher,
|
|
loadWebMedia: loadWebMedia as TelegramBotDeps["loadWebMedia"],
|
|
buildModelsProviderData: buildModelsProviderData as TelegramBotDeps["buildModelsProviderData"],
|
|
listSkillCommandsForAgents:
|
|
listSkillCommandsForAgents as TelegramBotDeps["listSkillCommandsForAgents"],
|
|
wasSentByBot: wasSentByBot as TelegramBotDeps["wasSentByBot"],
|
|
};
|
|
|
|
vi.doMock("./bot.runtime.js", () => telegramBotRuntimeForTest);
|
|
|
|
export const getOnHandler = (event: string) => {
|
|
const handler = onSpy.mock.calls.find((call) => call[0] === event)?.[1];
|
|
if (!handler) {
|
|
throw new Error(`Missing handler for event: ${event}`);
|
|
}
|
|
return handler as (ctx: Record<string, unknown>) => Promise<void>;
|
|
};
|
|
|
|
const DEFAULT_TELEGRAM_TEST_CONFIG: OpenClawConfig = {
|
|
agents: {
|
|
defaults: {
|
|
envelopeTimezone: "utc",
|
|
},
|
|
},
|
|
channels: {
|
|
telegram: { dmPolicy: "open", allowFrom: ["*"] },
|
|
},
|
|
};
|
|
|
|
export function makeTelegramMessageCtx(params: {
|
|
chat: {
|
|
id: number;
|
|
type: string;
|
|
title?: string;
|
|
is_forum?: boolean;
|
|
};
|
|
from: { id: number; username?: string };
|
|
text: string;
|
|
date?: number;
|
|
messageId?: number;
|
|
messageThreadId?: number;
|
|
}) {
|
|
return {
|
|
message: {
|
|
chat: params.chat,
|
|
from: params.from,
|
|
text: params.text,
|
|
date: params.date ?? 1736380800,
|
|
message_id: params.messageId ?? 42,
|
|
...(params.messageThreadId === undefined
|
|
? {}
|
|
: { message_thread_id: params.messageThreadId }),
|
|
},
|
|
me: { username: "openclaw_bot" },
|
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
|
};
|
|
}
|
|
|
|
export function makeForumGroupMessageCtx(params?: {
|
|
chatId?: number;
|
|
threadId?: number;
|
|
text?: string;
|
|
fromId?: number;
|
|
username?: string;
|
|
title?: string;
|
|
}) {
|
|
return makeTelegramMessageCtx({
|
|
chat: {
|
|
id: params?.chatId ?? -1001234567890,
|
|
type: "supergroup",
|
|
title: params?.title ?? "Forum Group",
|
|
is_forum: true,
|
|
},
|
|
from: { id: params?.fromId ?? 12345, username: params?.username ?? "testuser" },
|
|
text: params?.text ?? "hello",
|
|
messageThreadId: params?.threadId,
|
|
});
|
|
}
|
|
|
|
beforeEach(() => {
|
|
resetInboundDedupe();
|
|
loadConfig.mockReset();
|
|
loadConfig.mockReturnValue(DEFAULT_TELEGRAM_TEST_CONFIG);
|
|
resolveStorePathMock.mockReset();
|
|
resolveStorePathMock.mockImplementation((storePath?: string) => storePath ?? sessionStorePath);
|
|
loadWebMedia.mockReset();
|
|
readChannelAllowFromStore.mockReset();
|
|
readChannelAllowFromStore.mockResolvedValue([]);
|
|
upsertChannelPairingRequest.mockReset();
|
|
upsertChannelPairingRequest.mockResolvedValue({ code: "PAIRCODE", created: true } as const);
|
|
onSpy.mockReset();
|
|
commandSpy.mockReset();
|
|
stopSpy.mockReset();
|
|
useSpy.mockReset();
|
|
replySpy.mockReset();
|
|
replySpy.mockImplementation(async (_ctx: MsgContext, opts?: GetReplyOptions) => {
|
|
await opts?.onReplyStart?.();
|
|
return undefined;
|
|
});
|
|
dispatchReplyWithBufferedBlockDispatcher.mockReset();
|
|
dispatchReplyWithBufferedBlockDispatcher.mockImplementation(
|
|
async (params: DispatchReplyHarnessParams) =>
|
|
await dispatchHarnessReplies(params, async (dispatchParams) => {
|
|
return await replySpy(dispatchParams.ctx, dispatchParams.replyOptions);
|
|
}),
|
|
);
|
|
|
|
sendAnimationSpy.mockReset();
|
|
sendAnimationSpy.mockResolvedValue({ message_id: 78 });
|
|
sendPhotoSpy.mockReset();
|
|
sendPhotoSpy.mockResolvedValue({ message_id: 79 });
|
|
sendMessageSpy.mockReset();
|
|
sendMessageSpy.mockResolvedValue({ message_id: 77 });
|
|
getFileSpy.mockReset();
|
|
getFileSpy.mockResolvedValue({ file_path: "media/file.jpg" });
|
|
|
|
setMessageReactionSpy.mockReset();
|
|
setMessageReactionSpy.mockResolvedValue(undefined);
|
|
answerCallbackQuerySpy.mockReset();
|
|
answerCallbackQuerySpy.mockResolvedValue(undefined);
|
|
sendChatActionSpy.mockReset();
|
|
sendChatActionSpy.mockResolvedValue(undefined);
|
|
setMyCommandsSpy.mockReset();
|
|
setMyCommandsSpy.mockResolvedValue(undefined);
|
|
getMeSpy.mockReset();
|
|
getMeSpy.mockResolvedValue({
|
|
username: "openclaw_bot",
|
|
has_topics_enabled: true,
|
|
});
|
|
editMessageTextSpy.mockReset();
|
|
editMessageTextSpy.mockResolvedValue({ message_id: 88 });
|
|
editMessageReplyMarkupSpy.mockReset();
|
|
editMessageReplyMarkupSpy.mockResolvedValue({ message_id: 88 });
|
|
sendMessageDraftSpy.mockReset();
|
|
sendMessageDraftSpy.mockResolvedValue(true);
|
|
enqueueSystemEventSpy.mockReset();
|
|
wasSentByBot.mockReset();
|
|
wasSentByBot.mockReturnValue(false);
|
|
listSkillCommandsForAgents.mockReset();
|
|
listSkillCommandsForAgents.mockReturnValue([]);
|
|
buildModelsProviderData.mockReset();
|
|
buildModelsProviderData.mockImplementation(async (cfg: OpenClawConfig) => {
|
|
return createModelsProviderDataFromConfig(cfg);
|
|
});
|
|
middlewareUseSpy.mockReset();
|
|
runnerHoisted.sequentializeMiddleware.mockReset();
|
|
runnerHoisted.sequentializeMiddleware.mockImplementation(async (_ctx, next) => {
|
|
if (typeof next === "function") {
|
|
await next();
|
|
}
|
|
});
|
|
sequentializeSpy.mockReset();
|
|
sequentializeSpy.mockImplementation(() => runnerHoisted.sequentializeMiddleware);
|
|
botCtorSpy.mockReset();
|
|
sequentializeKey = undefined;
|
|
});
|