Merge branch 'main' into docs/mermaid-diagrams
This commit is contained in:
commit
9b3edef5af
@ -550,10 +550,11 @@ describe("applyMediaUnderstanding", () => {
|
|||||||
it("escapes XML special characters in filenames to prevent injection", async () => {
|
it("escapes XML special characters in filenames to prevent injection", async () => {
|
||||||
const { applyMediaUnderstanding } = await loadApply();
|
const { applyMediaUnderstanding } = await loadApply();
|
||||||
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-media-"));
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-media-"));
|
||||||
// Create file with XML special characters in the name (what filesystem allows)
|
// Use & in filename — valid on all platforms (including Windows, which
|
||||||
|
// forbids < and > in NTFS filenames) and still requires XML escaping.
|
||||||
// Note: The sanitizeFilename in store.ts would strip most dangerous chars,
|
// Note: The sanitizeFilename in store.ts would strip most dangerous chars,
|
||||||
// but we test that even if some slip through, they get escaped in output
|
// but we test that even if some slip through, they get escaped in output
|
||||||
const filePath = path.join(dir, "file<test>.txt");
|
const filePath = path.join(dir, "file&test.txt");
|
||||||
await fs.writeFile(filePath, "safe content");
|
await fs.writeFile(filePath, "safe content");
|
||||||
|
|
||||||
const ctx: MsgContext = {
|
const ctx: MsgContext = {
|
||||||
@ -575,10 +576,9 @@ describe("applyMediaUnderstanding", () => {
|
|||||||
|
|
||||||
expect(result.appliedFile).toBe(true);
|
expect(result.appliedFile).toBe(true);
|
||||||
// Verify XML special chars are escaped in the output
|
// Verify XML special chars are escaped in the output
|
||||||
expect(ctx.Body).toContain("<");
|
expect(ctx.Body).toContain("&");
|
||||||
expect(ctx.Body).toContain(">");
|
// The name attribute should contain the escaped form, not a raw unescaped &
|
||||||
// The raw < and > should not appear unescaped in the name attribute
|
expect(ctx.Body).toMatch(/name="file&test\.txt"/);
|
||||||
expect(ctx.Body).not.toMatch(/name="[^"]*<[^"]*"/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("normalizes MIME types to prevent attribute injection", async () => {
|
it("normalizes MIME types to prevent attribute injection", async () => {
|
||||||
|
|||||||
@ -228,6 +228,7 @@ export const dispatchTelegramMessage = async ({
|
|||||||
onVoiceRecording: sendRecordVoice,
|
onVoiceRecording: sendRecordVoice,
|
||||||
linkPreview: telegramCfg.linkPreview,
|
linkPreview: telegramCfg.linkPreview,
|
||||||
replyQuoteText,
|
replyQuoteText,
|
||||||
|
notifyEmptyResponse: info.kind === "final",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onError: (err, info) => {
|
onError: (err, info) => {
|
||||||
|
|||||||
@ -488,7 +488,7 @@ export const registerTelegramNativeCommands = ({
|
|||||||
cfg,
|
cfg,
|
||||||
dispatcherOptions: {
|
dispatcherOptions: {
|
||||||
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
responsePrefix: resolveEffectiveMessagesConfig(cfg, route.agentId).responsePrefix,
|
||||||
deliver: async (payload) => {
|
deliver: async (payload, info) => {
|
||||||
await deliverReplies({
|
await deliverReplies({
|
||||||
replies: [payload],
|
replies: [payload],
|
||||||
chatId: String(chatId),
|
chatId: String(chatId),
|
||||||
@ -501,6 +501,7 @@ export const registerTelegramNativeCommands = ({
|
|||||||
tableMode,
|
tableMode,
|
||||||
chunkMode,
|
chunkMode,
|
||||||
linkPreview: telegramCfg.linkPreview,
|
linkPreview: telegramCfg.linkPreview,
|
||||||
|
notifyEmptyResponse: info.kind === "final",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onError: (err, info) => {
|
onError: (err, info) => {
|
||||||
|
|||||||
@ -44,7 +44,9 @@ export async function deliverReplies(params: {
|
|||||||
linkPreview?: boolean;
|
linkPreview?: boolean;
|
||||||
/** Optional quote text for Telegram reply_parameters. */
|
/** Optional quote text for Telegram reply_parameters. */
|
||||||
replyQuoteText?: string;
|
replyQuoteText?: string;
|
||||||
}) {
|
/** If true, send a fallback message when all replies are empty. Default: false */
|
||||||
|
notifyEmptyResponse?: boolean;
|
||||||
|
}): Promise<{ delivered: boolean }> {
|
||||||
const {
|
const {
|
||||||
replies,
|
replies,
|
||||||
chatId,
|
chatId,
|
||||||
@ -58,6 +60,7 @@ export async function deliverReplies(params: {
|
|||||||
} = params;
|
} = params;
|
||||||
const chunkMode = params.chunkMode ?? "length";
|
const chunkMode = params.chunkMode ?? "length";
|
||||||
let hasReplied = false;
|
let hasReplied = false;
|
||||||
|
let skippedEmpty = 0;
|
||||||
const chunkText = (markdown: string) => {
|
const chunkText = (markdown: string) => {
|
||||||
const markdownChunks =
|
const markdownChunks =
|
||||||
chunkMode === "newline"
|
chunkMode === "newline"
|
||||||
@ -85,6 +88,7 @@ export async function deliverReplies(params: {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
runtime.error?.(danger("reply missing text/media"));
|
runtime.error?.(danger("reply missing text/media"));
|
||||||
|
skippedEmpty++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const replyToId = replyToMode === "off" ? undefined : resolveTelegramReplyId(reply.replyToId);
|
const replyToId = replyToMode === "off" ? undefined : resolveTelegramReplyId(reply.replyToId);
|
||||||
@ -268,6 +272,18 @@ export async function deliverReplies(params: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If all replies were empty and notifyEmptyResponse is enabled, send a fallback message
|
||||||
|
// Check both: (1) replies with no content (skippedEmpty), (2) no replies at all (empty array)
|
||||||
|
if (!hasReplied && (skippedEmpty > 0 || replies.length === 0) && params.notifyEmptyResponse) {
|
||||||
|
const fallbackText = "No response generated. Please try again.";
|
||||||
|
await sendTelegramText(bot, chatId, fallbackText, runtime, {
|
||||||
|
messageThreadId,
|
||||||
|
});
|
||||||
|
hasReplied = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { delivered: hasReplied };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function resolveMedia(
|
export async function resolveMedia(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user