mirror of https://github.com/openclaw/openclaw.git
feat: add --force-document to message.send for Telegram (bypass sendPhoto + image optimizer) (#45111)
* feat: add --force-document to message.send for Telegram Adds --force-document CLI flag to bypass sendPhoto and use sendDocument instead, avoiding Telegram image compression for PNG/image files. - TelegramSendOpts: add forceDocument field - send.ts: skip sendPhoto when forceDocument=true (mediaSender pattern) - ChannelOutboundContext: add forceDocument field - telegramOutbound.sendMedia: pass forceDocument to sendMessageTelegram - ChannelHandlerParams / DeliverOutboundPayloadsCoreParams: add forceDocument - createChannelOutboundContextBase: propagate forceDocument - outbound-send-service.ts: add forceDocument to executeSendAction params - message-action-runner.ts: read forceDocument from params - message.ts: add forceDocument to MessageSendParams - register.send.ts: add --force-document CLI option * fix: pass forceDocument through telegram action dispatch path The actual send path goes through dispatchChannelMessageAction -> telegramMessageActions.handleAction -> handleTelegramAction, not deliverOutboundPayloads. forceDocument was not being read in readTelegramSendParams or passed to sendMessageTelegram. * fix: apply forceDocument to GIF branch to avoid sendAnimation * fix: add disable_content_type_detection=true to sendDocument for --force-document * fix: add forceDocument to buildSendSchema for agent discoverability * fix: scope telegram force-document detection * test: fix heartbeat target helper typing * fix: skip image optimization when forceDocument is set * fix: persist forceDocument in WAL queue for crash-recovery replay * test: tighten heartbeat target test entry typing --------- Co-authored-by: thepagent <thepagent@users.noreply.github.com> Co-authored-by: Frank Yang <frank.ekn@gmail.com>
This commit is contained in:
parent
40c81e9cd3
commit
0ee11d3321
|
|
@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
|
||||||
- Docker/timezone override: add `OPENCLAW_TZ` so `docker-setup.sh` can pin gateway and CLI containers to a chosen IANA timezone instead of inheriting the daemon default. (#34119) Thanks @Lanfei.
|
- Docker/timezone override: add `OPENCLAW_TZ` so `docker-setup.sh` can pin gateway and CLI containers to a chosen IANA timezone instead of inheriting the daemon default. (#34119) Thanks @Lanfei.
|
||||||
- Dependencies/pi: bump `@mariozechner/pi-agent-core`, `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, and `@mariozechner/pi-tui` to `0.58.0`.
|
- Dependencies/pi: bump `@mariozechner/pi-agent-core`, `@mariozechner/pi-ai`, `@mariozechner/pi-coding-agent`, and `@mariozechner/pi-tui` to `0.58.0`.
|
||||||
- Cron/sessions: add `sessionTarget: "current"` and `session:<id>` support so cron jobs can bind to the creating session or a persistent named session instead of only `main` or `isolated`. Thanks @kkhomej33-netizen and @ImLukeF.
|
- Cron/sessions: add `sessionTarget: "current"` and `session:<id>` support so cron jobs can bind to the creating session or a persistent named session instead of only `main` or `isolated`. Thanks @kkhomej33-netizen and @ImLukeF.
|
||||||
|
- Telegram/message send: add `--force-document` so Telegram image and GIF sends can upload as documents without compression. (#45111) Thanks @thepagent.
|
||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ function readTelegramSendParams(params: Record<string, unknown>) {
|
||||||
const buttons = params.buttons;
|
const buttons = params.buttons;
|
||||||
const asVoice = readBooleanParam(params, "asVoice");
|
const asVoice = readBooleanParam(params, "asVoice");
|
||||||
const silent = readBooleanParam(params, "silent");
|
const silent = readBooleanParam(params, "silent");
|
||||||
|
const forceDocument = readBooleanParam(params, "forceDocument");
|
||||||
const quoteText = readStringParam(params, "quoteText");
|
const quoteText = readStringParam(params, "quoteText");
|
||||||
return {
|
return {
|
||||||
to,
|
to,
|
||||||
|
|
@ -48,6 +49,7 @@ function readTelegramSendParams(params: Record<string, unknown>) {
|
||||||
buttons,
|
buttons,
|
||||||
asVoice,
|
asVoice,
|
||||||
silent,
|
silent,
|
||||||
|
forceDocument,
|
||||||
quoteText: quoteText ?? undefined,
|
quoteText: quoteText ?? undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||||
deps,
|
deps,
|
||||||
replyToId,
|
replyToId,
|
||||||
threadId,
|
threadId,
|
||||||
|
forceDocument,
|
||||||
}) => {
|
}) => {
|
||||||
const { send, baseOpts } = resolveTelegramSendContext({
|
const { send, baseOpts } = resolveTelegramSendContext({
|
||||||
cfg,
|
cfg,
|
||||||
|
|
@ -127,6 +128,7 @@ export const telegramOutbound: ChannelOutboundAdapter = {
|
||||||
...baseOpts,
|
...baseOpts,
|
||||||
mediaUrl,
|
mediaUrl,
|
||||||
mediaLocalRoots,
|
mediaLocalRoots,
|
||||||
|
forceDocument: forceDocument ?? false,
|
||||||
});
|
});
|
||||||
return { channel: "telegram", ...result };
|
return { channel: "telegram", ...result };
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -877,6 +877,87 @@ describe("sendMessageTelegram", () => {
|
||||||
expect(res.messageId).toBe("9");
|
expect(res.messageId).toBe("9");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it.each([
|
||||||
|
{
|
||||||
|
name: "images",
|
||||||
|
buffer: Buffer.from("fake-image"),
|
||||||
|
contentType: "image/png",
|
||||||
|
fileName: "photo.png",
|
||||||
|
mediaUrl: "https://example.com/photo.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GIFs",
|
||||||
|
buffer: Buffer.from("GIF89a"),
|
||||||
|
contentType: "image/gif",
|
||||||
|
fileName: "fun.gif",
|
||||||
|
mediaUrl: "https://example.com/fun.gif",
|
||||||
|
},
|
||||||
|
])("sends $name as documents when forceDocument is true", async (testCase) => {
|
||||||
|
const chatId = "123";
|
||||||
|
const sendAnimation = vi.fn();
|
||||||
|
const sendDocument = vi.fn().mockResolvedValue({
|
||||||
|
message_id: 10,
|
||||||
|
chat: { id: chatId },
|
||||||
|
});
|
||||||
|
const sendPhoto = vi.fn();
|
||||||
|
const api = { sendAnimation, sendDocument, sendPhoto } as unknown as {
|
||||||
|
sendAnimation: typeof sendAnimation;
|
||||||
|
sendDocument: typeof sendDocument;
|
||||||
|
sendPhoto: typeof sendPhoto;
|
||||||
|
};
|
||||||
|
|
||||||
|
mockLoadedMedia({
|
||||||
|
buffer: testCase.buffer,
|
||||||
|
contentType: testCase.contentType,
|
||||||
|
fileName: testCase.fileName,
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await sendMessageTelegram(chatId, "caption", {
|
||||||
|
token: "tok",
|
||||||
|
api,
|
||||||
|
mediaUrl: testCase.mediaUrl,
|
||||||
|
forceDocument: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendDocument, testCase.name).toHaveBeenCalledWith(chatId, expect.anything(), {
|
||||||
|
caption: "caption",
|
||||||
|
parse_mode: "HTML",
|
||||||
|
disable_content_type_detection: true,
|
||||||
|
});
|
||||||
|
expect(sendPhoto, testCase.name).not.toHaveBeenCalled();
|
||||||
|
expect(sendAnimation, testCase.name).not.toHaveBeenCalled();
|
||||||
|
expect(res.messageId).toBe("10");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("keeps regular document sends on the default Telegram params", async () => {
|
||||||
|
const chatId = "123";
|
||||||
|
const sendDocument = vi.fn().mockResolvedValue({
|
||||||
|
message_id: 11,
|
||||||
|
chat: { id: chatId },
|
||||||
|
});
|
||||||
|
const api = { sendDocument } as unknown as {
|
||||||
|
sendDocument: typeof sendDocument;
|
||||||
|
};
|
||||||
|
|
||||||
|
mockLoadedMedia({
|
||||||
|
buffer: Buffer.from("%PDF-1.7"),
|
||||||
|
contentType: "application/pdf",
|
||||||
|
fileName: "report.pdf",
|
||||||
|
});
|
||||||
|
|
||||||
|
const res = await sendMessageTelegram(chatId, "caption", {
|
||||||
|
token: "tok",
|
||||||
|
api,
|
||||||
|
mediaUrl: "https://example.com/report.pdf",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendDocument).toHaveBeenCalledWith(chatId, expect.anything(), {
|
||||||
|
caption: "caption",
|
||||||
|
parse_mode: "HTML",
|
||||||
|
});
|
||||||
|
expect(res.messageId).toBe("11");
|
||||||
|
});
|
||||||
|
|
||||||
it("routes audio media to sendAudio/sendVoice based on voice compatibility", async () => {
|
it("routes audio media to sendAudio/sendVoice based on voice compatibility", async () => {
|
||||||
const cases: Array<{
|
const cases: Array<{
|
||||||
name: string;
|
name: string;
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,8 @@ type TelegramSendOpts = {
|
||||||
messageThreadId?: number;
|
messageThreadId?: number;
|
||||||
/** Inline keyboard buttons (reply markup). */
|
/** Inline keyboard buttons (reply markup). */
|
||||||
buttons?: TelegramInlineButtons;
|
buttons?: TelegramInlineButtons;
|
||||||
|
/** Send image as document to avoid Telegram compression. Defaults to false. */
|
||||||
|
forceDocument?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type TelegramSendResult = {
|
type TelegramSendResult = {
|
||||||
|
|
@ -763,6 +765,7 @@ export async function sendMessageTelegram(
|
||||||
buildOutboundMediaLoadOptions({
|
buildOutboundMediaLoadOptions({
|
||||||
maxBytes: mediaMaxBytes,
|
maxBytes: mediaMaxBytes,
|
||||||
mediaLocalRoots: opts.mediaLocalRoots,
|
mediaLocalRoots: opts.mediaLocalRoots,
|
||||||
|
optimizeImages: opts.forceDocument ? false : undefined,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
const kind = kindFromMime(media.contentType ?? undefined);
|
const kind = kindFromMime(media.contentType ?? undefined);
|
||||||
|
|
@ -815,7 +818,7 @@ export async function sendMessageTelegram(
|
||||||
);
|
);
|
||||||
|
|
||||||
const mediaSender = (() => {
|
const mediaSender = (() => {
|
||||||
if (isGif) {
|
if (isGif && !opts.forceDocument) {
|
||||||
return {
|
return {
|
||||||
label: "animation",
|
label: "animation",
|
||||||
sender: (effectiveParams: Record<string, unknown> | undefined) =>
|
sender: (effectiveParams: Record<string, unknown> | undefined) =>
|
||||||
|
|
@ -826,7 +829,7 @@ export async function sendMessageTelegram(
|
||||||
) as Promise<TelegramMessageLike>,
|
) as Promise<TelegramMessageLike>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (kind === "image") {
|
if (kind === "image" && !opts.forceDocument) {
|
||||||
return {
|
return {
|
||||||
label: "photo",
|
label: "photo",
|
||||||
sender: (effectiveParams: Record<string, unknown> | undefined) =>
|
sender: (effectiveParams: Record<string, unknown> | undefined) =>
|
||||||
|
|
@ -893,7 +896,11 @@ export async function sendMessageTelegram(
|
||||||
api.sendDocument(
|
api.sendDocument(
|
||||||
chatId,
|
chatId,
|
||||||
file,
|
file,
|
||||||
effectiveParams as Parameters<typeof api.sendDocument>[2],
|
// Only force Telegram to keep the uploaded media type when callers explicitly
|
||||||
|
// opt into document delivery for image/GIF uploads.
|
||||||
|
(opts.forceDocument
|
||||||
|
? { ...effectiveParams, disable_content_type_detection: true }
|
||||||
|
: effectiveParams) as Parameters<typeof api.sendDocument>[2],
|
||||||
) as Promise<TelegramMessageLike>,
|
) as Promise<TelegramMessageLike>,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -201,6 +201,11 @@ function buildSendSchema(options: {
|
||||||
),
|
),
|
||||||
bestEffort: Type.Optional(Type.Boolean()),
|
bestEffort: Type.Optional(Type.Boolean()),
|
||||||
gifPlayback: Type.Optional(Type.Boolean()),
|
gifPlayback: Type.Optional(Type.Boolean()),
|
||||||
|
forceDocument: Type.Optional(
|
||||||
|
Type.Boolean({
|
||||||
|
description: "Send image/GIF as document to avoid Telegram compression (Telegram only).",
|
||||||
|
}),
|
||||||
|
),
|
||||||
buttons: Type.Optional(
|
buttons: Type.Optional(
|
||||||
Type.Array(
|
Type.Array(
|
||||||
Type.Array(
|
Type.Array(
|
||||||
|
|
|
||||||
|
|
@ -252,6 +252,7 @@ export async function handleTelegramAction(
|
||||||
quoteText: quoteText ?? undefined,
|
quoteText: quoteText ?? undefined,
|
||||||
asVoice: readBooleanParam(params, "asVoice"),
|
asVoice: readBooleanParam(params, "asVoice"),
|
||||||
silent: readBooleanParam(params, "silent"),
|
silent: readBooleanParam(params, "silent"),
|
||||||
|
forceDocument: readBooleanParam(params, "forceDocument") ?? false,
|
||||||
});
|
});
|
||||||
return jsonResult({
|
return jsonResult({
|
||||||
ok: true,
|
ok: true,
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,8 @@ export type ChannelOutboundContext = {
|
||||||
mediaUrl?: string;
|
mediaUrl?: string;
|
||||||
mediaLocalRoots?: readonly string[];
|
mediaLocalRoots?: readonly string[];
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
/** Send image as document to avoid Telegram compression. */
|
||||||
|
forceDocument?: boolean;
|
||||||
replyToId?: string | null;
|
replyToId?: string | null;
|
||||||
threadId?: string | number | null;
|
threadId?: string | number | null;
|
||||||
accountId?: string | null;
|
accountId?: string | null;
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,11 @@ export function registerMessageSendCommand(message: Command, helpers: MessageCli
|
||||||
.option("--reply-to <id>", "Reply-to message id")
|
.option("--reply-to <id>", "Reply-to message id")
|
||||||
.option("--thread-id <id>", "Thread id (Telegram forum thread)")
|
.option("--thread-id <id>", "Thread id (Telegram forum thread)")
|
||||||
.option("--gif-playback", "Treat video media as GIF playback (WhatsApp only).", false)
|
.option("--gif-playback", "Treat video media as GIF playback (WhatsApp only).", false)
|
||||||
|
.option(
|
||||||
|
"--force-document",
|
||||||
|
"Send media as document to avoid Telegram compression (Telegram only). Applies to images and GIFs.",
|
||||||
|
false,
|
||||||
|
)
|
||||||
.option(
|
.option(
|
||||||
"--silent",
|
"--silent",
|
||||||
"Send message silently without notification (Telegram + Discord)",
|
"Send message silently without notification (Telegram + Discord)",
|
||||||
|
|
|
||||||
|
|
@ -146,6 +146,7 @@ type ChannelHandlerParams = {
|
||||||
identity?: OutboundIdentity;
|
identity?: OutboundIdentity;
|
||||||
deps?: OutboundSendDeps;
|
deps?: OutboundSendDeps;
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
forceDocument?: boolean;
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
mediaLocalRoots?: readonly string[];
|
mediaLocalRoots?: readonly string[];
|
||||||
};
|
};
|
||||||
|
|
@ -226,6 +227,7 @@ function createChannelOutboundContextBase(
|
||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
identity: params.identity,
|
identity: params.identity,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
silent: params.silent,
|
silent: params.silent,
|
||||||
mediaLocalRoots: params.mediaLocalRoots,
|
mediaLocalRoots: params.mediaLocalRoots,
|
||||||
|
|
@ -245,6 +247,7 @@ type DeliverOutboundPayloadsCoreParams = {
|
||||||
identity?: OutboundIdentity;
|
identity?: OutboundIdentity;
|
||||||
deps?: OutboundSendDeps;
|
deps?: OutboundSendDeps;
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
forceDocument?: boolean;
|
||||||
abortSignal?: AbortSignal;
|
abortSignal?: AbortSignal;
|
||||||
bestEffort?: boolean;
|
bestEffort?: boolean;
|
||||||
onError?: (err: unknown, payload: NormalizedOutboundPayload) => void;
|
onError?: (err: unknown, payload: NormalizedOutboundPayload) => void;
|
||||||
|
|
@ -489,6 +492,7 @@ export async function deliverOutboundPayloads(
|
||||||
replyToId: params.replyToId,
|
replyToId: params.replyToId,
|
||||||
bestEffort: params.bestEffort,
|
bestEffort: params.bestEffort,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
silent: params.silent,
|
silent: params.silent,
|
||||||
mirror: params.mirror,
|
mirror: params.mirror,
|
||||||
}).catch(() => null); // Best-effort — don't block delivery if queue write fails.
|
}).catch(() => null); // Best-effort — don't block delivery if queue write fails.
|
||||||
|
|
@ -557,6 +561,7 @@ async function deliverOutboundPayloadsCore(
|
||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
identity: params.identity,
|
identity: params.identity,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
silent: params.silent,
|
silent: params.silent,
|
||||||
mediaLocalRoots,
|
mediaLocalRoots,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ type QueuedDeliveryPayload = {
|
||||||
replyToId?: string | null;
|
replyToId?: string | null;
|
||||||
bestEffort?: boolean;
|
bestEffort?: boolean;
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
forceDocument?: boolean;
|
||||||
silent?: boolean;
|
silent?: boolean;
|
||||||
mirror?: OutboundMirror;
|
mirror?: OutboundMirror;
|
||||||
};
|
};
|
||||||
|
|
@ -117,6 +118,7 @@ export async function enqueueDelivery(
|
||||||
replyToId: params.replyToId,
|
replyToId: params.replyToId,
|
||||||
bestEffort: params.bestEffort,
|
bestEffort: params.bestEffort,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
silent: params.silent,
|
silent: params.silent,
|
||||||
mirror: params.mirror,
|
mirror: params.mirror,
|
||||||
retryCount: 0,
|
retryCount: 0,
|
||||||
|
|
@ -379,6 +381,7 @@ export async function recoverPendingDeliveries(opts: {
|
||||||
replyToId: entry.replyToId,
|
replyToId: entry.replyToId,
|
||||||
bestEffort: entry.bestEffort,
|
bestEffort: entry.bestEffort,
|
||||||
gifPlayback: entry.gifPlayback,
|
gifPlayback: entry.gifPlayback,
|
||||||
|
forceDocument: entry.forceDocument,
|
||||||
silent: entry.silent,
|
silent: entry.silent,
|
||||||
mirror: entry.mirror,
|
mirror: entry.mirror,
|
||||||
skipQueue: true, // Prevent re-enqueueing during recovery
|
skipQueue: true, // Prevent re-enqueueing during recovery
|
||||||
|
|
|
||||||
|
|
@ -478,6 +478,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
||||||
}
|
}
|
||||||
params.message = message;
|
params.message = message;
|
||||||
const gifPlayback = readBooleanParam(params, "gifPlayback") ?? false;
|
const gifPlayback = readBooleanParam(params, "gifPlayback") ?? false;
|
||||||
|
const forceDocument = readBooleanParam(params, "forceDocument") ?? false;
|
||||||
const bestEffort = readBooleanParam(params, "bestEffort");
|
const bestEffort = readBooleanParam(params, "bestEffort");
|
||||||
const silent = readBooleanParam(params, "silent");
|
const silent = readBooleanParam(params, "silent");
|
||||||
|
|
||||||
|
|
@ -547,6 +548,7 @@ async function handleSendAction(ctx: ResolvedActionContext): Promise<MessageActi
|
||||||
mediaUrl: mediaUrl || undefined,
|
mediaUrl: mediaUrl || undefined,
|
||||||
mediaUrls: mergedMediaUrls.length ? mergedMediaUrls : undefined,
|
mediaUrls: mergedMediaUrls.length ? mergedMediaUrls : undefined,
|
||||||
gifPlayback,
|
gifPlayback,
|
||||||
|
forceDocument,
|
||||||
bestEffort: bestEffort ?? undefined,
|
bestEffort: bestEffort ?? undefined,
|
||||||
replyToId: replyToId ?? undefined,
|
replyToId: replyToId ?? undefined,
|
||||||
threadId: resolvedThreadId ?? undefined,
|
threadId: resolvedThreadId ?? undefined,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ type MessageSendParams = {
|
||||||
mediaUrl?: string;
|
mediaUrl?: string;
|
||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
forceDocument?: boolean;
|
||||||
accountId?: string;
|
accountId?: string;
|
||||||
replyToId?: string;
|
replyToId?: string;
|
||||||
threadId?: string | number;
|
threadId?: string | number;
|
||||||
|
|
@ -245,6 +246,7 @@ export async function sendMessage(params: MessageSendParams): Promise<MessageSen
|
||||||
replyToId: params.replyToId,
|
replyToId: params.replyToId,
|
||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
deps: params.deps,
|
deps: params.deps,
|
||||||
bestEffort: params.bestEffort,
|
bestEffort: params.bestEffort,
|
||||||
abortSignal: params.abortSignal,
|
abortSignal: params.abortSignal,
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@ export async function executeSendAction(params: {
|
||||||
mediaUrl?: string;
|
mediaUrl?: string;
|
||||||
mediaUrls?: string[];
|
mediaUrls?: string[];
|
||||||
gifPlayback?: boolean;
|
gifPlayback?: boolean;
|
||||||
|
forceDocument?: boolean;
|
||||||
bestEffort?: boolean;
|
bestEffort?: boolean;
|
||||||
replyToId?: string;
|
replyToId?: string;
|
||||||
threadId?: string | number;
|
threadId?: string | number;
|
||||||
|
|
@ -132,6 +133,7 @@ export async function executeSendAction(params: {
|
||||||
replyToId: params.replyToId,
|
replyToId: params.replyToId,
|
||||||
threadId: params.threadId,
|
threadId: params.threadId,
|
||||||
gifPlayback: params.gifPlayback,
|
gifPlayback: params.gifPlayback,
|
||||||
|
forceDocument: params.forceDocument,
|
||||||
dryRun: params.ctx.dryRun,
|
dryRun: params.ctx.dryRun,
|
||||||
bestEffort: params.bestEffort ?? undefined,
|
bestEffort: params.bestEffort ?? undefined,
|
||||||
deps: params.ctx.deps,
|
deps: params.ctx.deps,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { telegramOutbound } from "../../channels/plugins/outbound/telegram.js";
|
import { telegramOutbound } from "../../channels/plugins/outbound/telegram.js";
|
||||||
import type { OpenClawConfig } from "../../config/config.js";
|
import type { OpenClawConfig } from "../../config/config.js";
|
||||||
|
import type { SessionEntry } from "../../config/sessions/types.js";
|
||||||
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
import { setActivePluginRegistry } from "../../plugins/runtime.js";
|
||||||
import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
|
import { createOutboundTestPlugin, createTestRegistry } from "../../test-utils/channel-plugins.js";
|
||||||
import {
|
import {
|
||||||
|
|
@ -326,10 +327,7 @@ describe("resolveSessionDeliveryTarget", () => {
|
||||||
expect(resolved.to).toBe("63448508");
|
expect(resolved.to).toBe("63448508");
|
||||||
});
|
});
|
||||||
|
|
||||||
const resolveHeartbeatTarget = (
|
const resolveHeartbeatTarget = (entry: SessionEntry, directPolicy?: "allow" | "block") =>
|
||||||
entry: Parameters<typeof resolveHeartbeatDeliveryTarget>[0]["entry"],
|
|
||||||
directPolicy?: "allow" | "block",
|
|
||||||
) =>
|
|
||||||
resolveHeartbeatDeliveryTarget({
|
resolveHeartbeatDeliveryTarget({
|
||||||
cfg: {},
|
cfg: {},
|
||||||
entry,
|
entry,
|
||||||
|
|
@ -341,7 +339,7 @@ describe("resolveSessionDeliveryTarget", () => {
|
||||||
|
|
||||||
const expectHeartbeatTarget = (params: {
|
const expectHeartbeatTarget = (params: {
|
||||||
name: string;
|
name: string;
|
||||||
entry: Parameters<typeof resolveHeartbeatDeliveryTarget>[0]["entry"];
|
entry: SessionEntry;
|
||||||
directPolicy?: "allow" | "block";
|
directPolicy?: "allow" | "block";
|
||||||
expectedChannel: string;
|
expectedChannel: string;
|
||||||
expectedTo?: string;
|
expectedTo?: string;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
export type OutboundMediaLoadParams = {
|
export type OutboundMediaLoadParams = {
|
||||||
maxBytes?: number;
|
maxBytes?: number;
|
||||||
mediaLocalRoots?: readonly string[];
|
mediaLocalRoots?: readonly string[];
|
||||||
|
optimizeImages?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type OutboundMediaLoadOptions = {
|
export type OutboundMediaLoadOptions = {
|
||||||
maxBytes?: number;
|
maxBytes?: number;
|
||||||
localRoots?: readonly string[];
|
localRoots?: readonly string[];
|
||||||
|
optimizeImages?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resolveOutboundMediaLocalRoots(
|
export function resolveOutboundMediaLocalRoots(
|
||||||
|
|
@ -21,5 +23,6 @@ export function buildOutboundMediaLoadOptions(
|
||||||
return {
|
return {
|
||||||
...(params.maxBytes !== undefined ? { maxBytes: params.maxBytes } : {}),
|
...(params.maxBytes !== undefined ? { maxBytes: params.maxBytes } : {}),
|
||||||
...(localRoots ? { localRoots } : {}),
|
...(localRoots ? { localRoots } : {}),
|
||||||
|
...(params.optimizeImages !== undefined ? { optimizeImages: params.optimizeImages } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue