mirror of
https://github.com/wassname/pi-telegram.git
synced 2026-06-27 16:46:21 +08:00
markdown & queue hardening
This commit is contained in:
@@ -683,34 +683,100 @@ function isMarkdownTableSeparator(line: string): boolean {
|
|||||||
return /^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$/.test(line);
|
return /^\s*\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?\s*$/.test(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseMarkdownFence(
|
||||||
|
line: string,
|
||||||
|
): { marker: "`" | "~"; length: number; info?: string } | undefined {
|
||||||
|
const match = line.match(/^\s*([`~]{3,})(.*)$/);
|
||||||
|
if (!match) return undefined;
|
||||||
|
const fence = match[1] ?? "";
|
||||||
|
const marker = fence[0];
|
||||||
|
if ((marker !== "`" && marker !== "~") || /[^`~]/.test(fence)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (!fence.split("").every((char) => char === marker)) return undefined;
|
||||||
|
return {
|
||||||
|
marker,
|
||||||
|
length: fence.length,
|
||||||
|
info: (match[2] ?? "").trim() || undefined,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isFencedCodeStart(line: string): boolean {
|
function isFencedCodeStart(line: string): boolean {
|
||||||
return /^\s*```/.test(line);
|
return parseMarkdownFence(line) !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMatchingMarkdownFence(
|
||||||
|
line: string,
|
||||||
|
fence: { marker: "`" | "~"; length: number },
|
||||||
|
): boolean {
|
||||||
|
const match = line.match(/^\s*([`~]{3,})\s*$/);
|
||||||
|
if (!match) return false;
|
||||||
|
const candidate = match[1] ?? "";
|
||||||
|
return (
|
||||||
|
candidate.length >= fence.length &&
|
||||||
|
candidate[0] === fence.marker &&
|
||||||
|
candidate.split("").every((char) => char === fence.marker)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isIndentedCodeLine(line: string): boolean {
|
function isIndentedCodeLine(line: string): boolean {
|
||||||
return /^(?:\t| {4,})/.test(line);
|
return /^(?:\t| {4,})/.test(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isIndentedMarkdownStructureLine(line: string): boolean {
|
||||||
|
const trimmed = line.trimStart();
|
||||||
|
return (
|
||||||
|
/^(?:[-*+]|\d+\.)\s+\[([ xX])\]\s+/.test(trimmed) ||
|
||||||
|
/^(?:[-*+]|\d+\.)\s+/.test(trimmed) ||
|
||||||
|
/^>\s?/.test(trimmed) ||
|
||||||
|
/^#{1,6}\s+/.test(trimmed) ||
|
||||||
|
parseMarkdownFence(trimmed) !== undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function canStartIndentedCodeBlock(lines: string[], index: number): boolean {
|
||||||
|
const line = lines[index] ?? "";
|
||||||
|
if (!isIndentedCodeLine(line)) return false;
|
||||||
|
if (isIndentedMarkdownStructureLine(line)) return false;
|
||||||
|
if (index === 0) return true;
|
||||||
|
return (lines[index - 1] ?? "").trim().length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stripIndentedCodePrefix(line: string): string {
|
||||||
|
if (line.startsWith("\t")) return line.slice(1);
|
||||||
|
if (line.startsWith(" ")) return line.slice(4);
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
function renderMarkdownPreviewText(markdown: string): string {
|
function renderMarkdownPreviewText(markdown: string): string {
|
||||||
const normalized = markdown.replace(/\r\n/g, "\n").trim();
|
const normalized = markdown.replace(/\r\n/g, "\n").trim();
|
||||||
if (normalized.length === 0) return "";
|
if (normalized.length === 0) return "";
|
||||||
const output: string[] = [];
|
const output: string[] = [];
|
||||||
const lines = normalized.split("\n");
|
const lines = normalized.split("\n");
|
||||||
let inFence = false;
|
let activeFence: { marker: "`" | "~"; length: number } | undefined;
|
||||||
for (const rawLine of lines) {
|
for (const rawLine of lines) {
|
||||||
const line = rawLine ?? "";
|
const line = rawLine ?? "";
|
||||||
if (isFencedCodeStart(line)) {
|
const fence = parseMarkdownFence(line);
|
||||||
inFence = !inFence;
|
if (activeFence) {
|
||||||
|
if (fence && isMatchingMarkdownFence(line, activeFence)) {
|
||||||
|
activeFence = undefined;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (line.trim().length === 0) {
|
||||||
|
if (output.at(-1) !== "") output.push("");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
output.push(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (fence) {
|
||||||
|
activeFence = { marker: fence.marker, length: fence.length };
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (line.trim().length === 0) {
|
if (line.trim().length === 0) {
|
||||||
if (output.at(-1) !== "") output.push("");
|
if (output.at(-1) !== "") output.push("");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (inFence) {
|
|
||||||
output.push(line);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (isMarkdownTableSeparator(line)) {
|
if (isMarkdownTableSeparator(line)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -758,6 +824,24 @@ function renderMarkdownPreviewText(markdown: string): string {
|
|||||||
|
|
||||||
// --- Rich Markdown Rendering ---
|
// --- Rich Markdown Rendering ---
|
||||||
|
|
||||||
|
function renderDelimitedInlineStyle(
|
||||||
|
text: string,
|
||||||
|
delimiter: string,
|
||||||
|
render: (content: string) => string,
|
||||||
|
): string {
|
||||||
|
const escapedDelimiter = delimiter.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
const pattern = new RegExp(
|
||||||
|
`(^|[^\\p{L}\\p{N}\\\\])(${escapedDelimiter})(?=\\S)(.+?)(?<=\\S)\\2(?=[^\\p{L}\\p{N}]|$)`,
|
||||||
|
"gu",
|
||||||
|
);
|
||||||
|
return text.replace(
|
||||||
|
pattern,
|
||||||
|
(_match, prefix: string, _wrapped: string, content: string) => {
|
||||||
|
return `${prefix}${render(content)}`;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function renderInlineMarkdown(text: string): string {
|
function renderInlineMarkdown(text: string): string {
|
||||||
const tokens: string[] = [];
|
const tokens: string[] = [];
|
||||||
const makeToken = (html: string): string => {
|
const makeToken = (html: string): string => {
|
||||||
@@ -789,10 +873,27 @@ function renderInlineMarkdown(text: string): string {
|
|||||||
return makeToken(`<code>${escapeHtml(code)}</code>`);
|
return makeToken(`<code>${escapeHtml(code)}</code>`);
|
||||||
});
|
});
|
||||||
result = escapeHtml(result);
|
result = escapeHtml(result);
|
||||||
result = result.replace(/(\*\*\*|___)(.+?)\1/g, "<b><i>$2</i></b>");
|
result = renderDelimitedInlineStyle(result, "***", (content) => {
|
||||||
result = result.replace(/~~(.+?)~~/g, "<s>$1</s>");
|
return `<b><i>${content}</i></b>`;
|
||||||
result = result.replace(/(\*\*|__)(.+?)\1/g, "<b>$2</b>");
|
});
|
||||||
result = result.replace(/(\*|_)(.+?)\1/g, "<i>$2</i>");
|
result = renderDelimitedInlineStyle(result, "___", (content) => {
|
||||||
|
return `<b><i>${content}</i></b>`;
|
||||||
|
});
|
||||||
|
result = renderDelimitedInlineStyle(result, "~~", (content) => {
|
||||||
|
return `<s>${content}</s>`;
|
||||||
|
});
|
||||||
|
result = renderDelimitedInlineStyle(result, "**", (content) => {
|
||||||
|
return `<b>${content}</b>`;
|
||||||
|
});
|
||||||
|
result = renderDelimitedInlineStyle(result, "__", (content) => {
|
||||||
|
return `<b>${content}</b>`;
|
||||||
|
});
|
||||||
|
result = renderDelimitedInlineStyle(result, "*", (content) => {
|
||||||
|
return `<i>${content}</i>`;
|
||||||
|
});
|
||||||
|
result = renderDelimitedInlineStyle(result, "_", (content) => {
|
||||||
|
return `<i>${content}</i>`;
|
||||||
|
});
|
||||||
result = result.replace(
|
result = result.replace(
|
||||||
/(^|[\s>(])(\[(?: |x|X)\])(?=($|[\s<).,:;!?]))/g,
|
/(^|[\s>(])(\[(?: |x|X)\])(?=($|[\s<).,:;!?]))/g,
|
||||||
(_match, prefix: string, checkbox: string) => {
|
(_match, prefix: string, checkbox: string) => {
|
||||||
@@ -808,7 +909,7 @@ function renderInlineMarkdown(text: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildListIndent(level: number): string {
|
function buildListIndent(level: number): string {
|
||||||
return "\u00A0".repeat(Math.max(0, Math.min(12, level * 2)));
|
return "\u00A0".repeat(Math.max(0, level) * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseMarkdownTableRow(line: string): string[] {
|
function parseMarkdownTableRow(line: string): string[] {
|
||||||
@@ -945,10 +1046,50 @@ function renderMarkdownTableBlock(lines: string[]): string[] {
|
|||||||
return renderMarkdownCodeBlock(tableLines.join("\n"), "markdown");
|
return renderMarkdownCodeBlock(tableLines.join("\n"), "markdown");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function chunkRenderedHtmlLines(
|
||||||
|
lines: string[],
|
||||||
|
wrapper?: { open: string; close: string },
|
||||||
|
): string[] {
|
||||||
|
if (lines.length === 0) return [];
|
||||||
|
const open = wrapper?.open ?? "";
|
||||||
|
const close = wrapper?.close ?? "";
|
||||||
|
const maxContentLength = MAX_MESSAGE_LENGTH - open.length - close.length;
|
||||||
|
const chunks: string[] = [];
|
||||||
|
let current = "";
|
||||||
|
const pushCurrent = (): void => {
|
||||||
|
if (current.length === 0) return;
|
||||||
|
chunks.push(`${open}${current}${close}`);
|
||||||
|
current = "";
|
||||||
|
};
|
||||||
|
for (const line of lines) {
|
||||||
|
const candidate = current.length === 0 ? line : `${current}\n${line}`;
|
||||||
|
if (candidate.length <= maxContentLength) {
|
||||||
|
current = candidate;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pushCurrent();
|
||||||
|
if (line.length <= maxContentLength) {
|
||||||
|
current = line;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < line.length; i += maxContentLength) {
|
||||||
|
chunks.push(`${open}${line.slice(i, i + maxContentLength)}${close}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushCurrent();
|
||||||
|
return chunks;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderMarkdownTextBlock(block: string): string[] {
|
||||||
|
return chunkRenderedHtmlLines(renderMarkdownTextLines(block));
|
||||||
|
}
|
||||||
|
|
||||||
function renderMarkdownQuoteBlock(lines: string[]): string[] {
|
function renderMarkdownQuoteBlock(lines: string[]): string[] {
|
||||||
const inner = lines.map((line) => line.replace(/^\s*>\s?/, "")).join("\n");
|
const inner = lines.map((line) => line.replace(/^\s*>\s?/, "")).join("\n");
|
||||||
const rendered = renderMarkdownTextLines(inner).join("\n");
|
return chunkRenderedHtmlLines(renderMarkdownTextLines(inner), {
|
||||||
return rendered.length > 0 ? [`<blockquote>${rendered}</blockquote>`] : [];
|
open: "<blockquote>",
|
||||||
|
close: "</blockquote>",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
||||||
@@ -960,11 +1101,14 @@ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
|||||||
while (index < lines.length) {
|
while (index < lines.length) {
|
||||||
const line = lines[index] ?? "";
|
const line = lines[index] ?? "";
|
||||||
const nextLine = lines[index + 1] ?? "";
|
const nextLine = lines[index + 1] ?? "";
|
||||||
if (isFencedCodeStart(line)) {
|
const fence = parseMarkdownFence(line);
|
||||||
const language = line.trim().slice(3).trim() || undefined;
|
if (fence) {
|
||||||
index += 1;
|
index += 1;
|
||||||
const codeLines: string[] = [];
|
const codeLines: string[] = [];
|
||||||
while (index < lines.length && !isFencedCodeStart(lines[index] ?? "")) {
|
while (
|
||||||
|
index < lines.length &&
|
||||||
|
!isMatchingMarkdownFence(lines[index] ?? "", fence)
|
||||||
|
) {
|
||||||
codeLines.push(lines[index] ?? "");
|
codeLines.push(lines[index] ?? "");
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
@@ -972,7 +1116,7 @@ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
|||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
renderedBlocks.push(
|
renderedBlocks.push(
|
||||||
...renderMarkdownCodeBlock(codeLines.join("\n"), language),
|
...renderMarkdownCodeBlock(codeLines.join("\n"), fence.info),
|
||||||
);
|
);
|
||||||
while (index < lines.length && (lines[index] ?? "").trim().length === 0) {
|
while (index < lines.length && (lines[index] ?? "").trim().length === 0) {
|
||||||
index += 1;
|
index += 1;
|
||||||
@@ -997,13 +1141,17 @@ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
|||||||
renderedBlocks.push(...renderMarkdownTableBlock(tableLines));
|
renderedBlocks.push(...renderMarkdownTableBlock(tableLines));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isIndentedCodeLine(line)) {
|
if (canStartIndentedCodeBlock(lines, index)) {
|
||||||
const codeLines: string[] = [];
|
const codeLines: string[] = [];
|
||||||
while (index < lines.length && isIndentedCodeLine(lines[index] ?? "")) {
|
while (index < lines.length) {
|
||||||
const rawLine = lines[index] ?? "";
|
const rawLine = lines[index] ?? "";
|
||||||
codeLines.push(
|
if (rawLine.trim().length === 0) {
|
||||||
rawLine.startsWith("\t") ? rawLine.slice(1) : rawLine.slice(4),
|
codeLines.push("");
|
||||||
);
|
index += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!isIndentedCodeLine(rawLine)) break;
|
||||||
|
codeLines.push(stripIndentedCodePrefix(rawLine));
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
renderedBlocks.push(...renderMarkdownCodeBlock(codeLines.join("\n")));
|
renderedBlocks.push(...renderMarkdownCodeBlock(codeLines.join("\n")));
|
||||||
@@ -1025,7 +1173,7 @@ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
|||||||
if (current.trim().length === 0) break;
|
if (current.trim().length === 0) break;
|
||||||
if (
|
if (
|
||||||
isFencedCodeStart(current) ||
|
isFencedCodeStart(current) ||
|
||||||
isIndentedCodeLine(current) ||
|
canStartIndentedCodeBlock(lines, index) ||
|
||||||
/^\s*>/.test(current)
|
/^\s*>/.test(current)
|
||||||
)
|
)
|
||||||
break;
|
break;
|
||||||
@@ -1033,12 +1181,7 @@ function renderMarkdownToTelegramHtmlChunks(markdown: string): string[] {
|
|||||||
textLines.push(current);
|
textLines.push(current);
|
||||||
index += 1;
|
index += 1;
|
||||||
}
|
}
|
||||||
const renderedTextBlock = renderMarkdownTextLines(
|
renderedBlocks.push(...renderMarkdownTextBlock(textLines.join("\n")));
|
||||||
textLines.join("\n"),
|
|
||||||
).join("\n");
|
|
||||||
if (renderedTextBlock.length > 0) {
|
|
||||||
renderedBlocks.push(renderedTextBlock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const chunks: string[] = [];
|
const chunks: string[] = [];
|
||||||
let current = "";
|
let current = "";
|
||||||
@@ -1179,9 +1322,11 @@ export default function (pi: ExtensionAPI) {
|
|||||||
let nextQueuedTelegramTurnOrder = 0;
|
let nextQueuedTelegramTurnOrder = 0;
|
||||||
let nextPriorityReactionOrder = 0;
|
let nextPriorityReactionOrder = 0;
|
||||||
let activeTelegramTurn: ActiveTelegramTurn | undefined;
|
let activeTelegramTurn: ActiveTelegramTurn | undefined;
|
||||||
|
let telegramTurnDispatchPending = false;
|
||||||
let typingInterval: ReturnType<typeof setInterval> | undefined;
|
let typingInterval: ReturnType<typeof setInterval> | undefined;
|
||||||
let currentAbort: (() => void) | undefined;
|
let currentAbort: (() => void) | undefined;
|
||||||
let preserveQueuedTurnsAsHistory = false;
|
let preserveQueuedTurnsAsHistory = false;
|
||||||
|
let compactionInProgress = false;
|
||||||
let setupInProgress = false;
|
let setupInProgress = false;
|
||||||
let previewState: TelegramPreviewState | undefined;
|
let previewState: TelegramPreviewState | undefined;
|
||||||
let draftSupport: "unknown" | "supported" | "unsupported" = "unknown";
|
let draftSupport: "unknown" | "supported" | "unsupported" = "unknown";
|
||||||
@@ -1197,6 +1342,39 @@ export default function (pi: ExtensionAPI) {
|
|||||||
return nextDraftId;
|
return nextDraftId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canDispatchQueuedTelegramTurn(ctx: ExtensionContext): boolean {
|
||||||
|
return (
|
||||||
|
!compactionInProgress &&
|
||||||
|
!activeTelegramTurn &&
|
||||||
|
!telegramTurnDispatchPending &&
|
||||||
|
ctx.isIdle() &&
|
||||||
|
!ctx.hasPendingMessages()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function dispatchNextQueuedTelegramTurn(ctx: ExtensionContext): void {
|
||||||
|
if (!canDispatchQueuedTelegramTurn(ctx)) {
|
||||||
|
updateStatus(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const nextTurn = queuedTelegramTurns[0];
|
||||||
|
if (!nextTurn) {
|
||||||
|
updateStatus(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
telegramTurnDispatchPending = true;
|
||||||
|
startTypingLoop(ctx, nextTurn.chatId);
|
||||||
|
updateStatus(ctx);
|
||||||
|
try {
|
||||||
|
pi.sendUserMessage(nextTurn.content);
|
||||||
|
} catch (error) {
|
||||||
|
telegramTurnDispatchPending = false;
|
||||||
|
stopTypingLoop();
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
updateStatus(ctx, `dispatch failed: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Status ---
|
// --- Status ---
|
||||||
|
|
||||||
function updateStatus(ctx: ExtensionContext, error?: string): void {
|
function updateStatus(ctx: ExtensionContext, error?: string): void {
|
||||||
@@ -1230,7 +1408,18 @@ export default function (pi: ExtensionAPI) {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (activeTelegramTurn || queuedTelegramTurns.length > 0) {
|
if (compactionInProgress) {
|
||||||
|
const queued = theme.fg(
|
||||||
|
"muted",
|
||||||
|
formatQueuedTelegramTurnsStatus(queuedTelegramTurns),
|
||||||
|
);
|
||||||
|
ctx.ui.setStatus(
|
||||||
|
"telegram",
|
||||||
|
`${label} ${theme.fg("accent", "compacting")}${queued}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (activeTelegramTurn || telegramTurnDispatchPending || queuedTelegramTurns.length > 0) {
|
||||||
const queued = theme.fg(
|
const queued = theme.fg(
|
||||||
"muted",
|
"muted",
|
||||||
formatQueuedTelegramTurnsStatus(queuedTelegramTurns),
|
formatQueuedTelegramTurnsStatus(queuedTelegramTurns),
|
||||||
@@ -2641,32 +2830,59 @@ export default function (pi: ExtensionAPI) {
|
|||||||
message: TelegramMessage,
|
message: TelegramMessage,
|
||||||
ctx: ExtensionContext,
|
ctx: ExtensionContext,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (!ctx.isIdle()) {
|
if (
|
||||||
|
!ctx.isIdle() ||
|
||||||
|
ctx.hasPendingMessages() ||
|
||||||
|
activeTelegramTurn ||
|
||||||
|
telegramTurnDispatchPending ||
|
||||||
|
queuedTelegramTurns.length > 0 ||
|
||||||
|
compactionInProgress
|
||||||
|
) {
|
||||||
await sendTextReply(
|
await sendTextReply(
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.message_id,
|
message.message_id,
|
||||||
"Cannot compact while pi is busy. Send /stop first.",
|
"Cannot compact while pi or the Telegram queue is busy. Wait for queued turns to finish or send /stop first.",
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
compactionInProgress = true;
|
||||||
|
updateStatus(ctx);
|
||||||
|
try {
|
||||||
|
ctx.compact({
|
||||||
|
onComplete: () => {
|
||||||
|
compactionInProgress = false;
|
||||||
|
updateStatus(ctx);
|
||||||
|
dispatchNextQueuedTelegramTurn(ctx);
|
||||||
|
void sendTextReply(
|
||||||
|
message.chat.id,
|
||||||
|
message.message_id,
|
||||||
|
"Compaction completed.",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
compactionInProgress = false;
|
||||||
|
updateStatus(ctx);
|
||||||
|
dispatchNextQueuedTelegramTurn(ctx);
|
||||||
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
|
void sendTextReply(
|
||||||
|
message.chat.id,
|
||||||
|
message.message_id,
|
||||||
|
`Compaction failed: ${errorMessage}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
compactionInProgress = false;
|
||||||
|
updateStatus(ctx);
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
await sendTextReply(
|
||||||
|
message.chat.id,
|
||||||
|
message.message_id,
|
||||||
|
`Compaction failed: ${errorMessage}`,
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.compact({
|
|
||||||
onComplete: () => {
|
|
||||||
void sendTextReply(
|
|
||||||
message.chat.id,
|
|
||||||
message.message_id,
|
|
||||||
"Compaction completed.",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
const errorMessage =
|
|
||||||
error instanceof Error ? error.message : String(error);
|
|
||||||
void sendTextReply(
|
|
||||||
message.chat.id,
|
|
||||||
message.message_id,
|
|
||||||
`Compaction failed: ${errorMessage}`,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await sendTextReply(
|
await sendTextReply(
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
message.message_id,
|
message.message_id,
|
||||||
@@ -2743,9 +2959,7 @@ export default function (pi: ExtensionAPI) {
|
|||||||
const turn = await createTelegramTurn(messages, historyTurns);
|
const turn = await createTelegramTurn(messages, historyTurns);
|
||||||
queuedTelegramTurns.push(turn);
|
queuedTelegramTurns.push(turn);
|
||||||
updateStatus(ctx);
|
updateStatus(ctx);
|
||||||
if (!ctx.isIdle()) return;
|
dispatchNextQueuedTelegramTurn(ctx);
|
||||||
startTypingLoop(ctx, turn.chatId);
|
|
||||||
pi.sendUserMessage(turn.content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function dispatchAuthorizedTelegramMessages(
|
async function dispatchAuthorizedTelegramMessages(
|
||||||
@@ -3051,6 +3265,8 @@ export default function (pi: ExtensionAPI) {
|
|||||||
pi.on("session_start", async (_event, ctx) => {
|
pi.on("session_start", async (_event, ctx) => {
|
||||||
config = await readConfig();
|
config = await readConfig();
|
||||||
currentTelegramModel = ctx.model;
|
currentTelegramModel = ctx.model;
|
||||||
|
telegramTurnDispatchPending = false;
|
||||||
|
compactionInProgress = false;
|
||||||
await mkdir(TEMP_DIR, { recursive: true });
|
await mkdir(TEMP_DIR, { recursive: true });
|
||||||
updateStatus(ctx);
|
updateStatus(ctx);
|
||||||
});
|
});
|
||||||
@@ -3060,6 +3276,8 @@ export default function (pi: ExtensionAPI) {
|
|||||||
nextQueuedTelegramTurnOrder = 0;
|
nextQueuedTelegramTurnOrder = 0;
|
||||||
nextPriorityReactionOrder = 0;
|
nextPriorityReactionOrder = 0;
|
||||||
currentTelegramModel = undefined;
|
currentTelegramModel = undefined;
|
||||||
|
telegramTurnDispatchPending = false;
|
||||||
|
compactionInProgress = false;
|
||||||
for (const state of mediaGroups.values()) {
|
for (const state of mediaGroups.values()) {
|
||||||
if (state.flushTimer) clearTimeout(state.flushTimer);
|
if (state.flushTimer) clearTimeout(state.flushTimer);
|
||||||
}
|
}
|
||||||
@@ -3089,8 +3307,9 @@ export default function (pi: ExtensionAPI) {
|
|||||||
|
|
||||||
pi.on("agent_start", async (_event, ctx) => {
|
pi.on("agent_start", async (_event, ctx) => {
|
||||||
currentAbort = () => ctx.abort();
|
currentAbort = () => ctx.abort();
|
||||||
if (!activeTelegramTurn && queuedTelegramTurns.length > 0) {
|
if (!activeTelegramTurn && telegramTurnDispatchPending) {
|
||||||
const nextTurn = queuedTelegramTurns.shift();
|
const nextTurn = queuedTelegramTurns.shift();
|
||||||
|
telegramTurnDispatchPending = false;
|
||||||
if (nextTurn) {
|
if (nextTurn) {
|
||||||
activeTelegramTurn = { ...nextTurn };
|
activeTelegramTurn = { ...nextTurn };
|
||||||
previewState = createPreviewState();
|
previewState = createPreviewState();
|
||||||
@@ -3131,8 +3350,12 @@ export default function (pi: ExtensionAPI) {
|
|||||||
currentAbort = undefined;
|
currentAbort = undefined;
|
||||||
stopTypingLoop();
|
stopTypingLoop();
|
||||||
activeTelegramTurn = undefined;
|
activeTelegramTurn = undefined;
|
||||||
|
telegramTurnDispatchPending = false;
|
||||||
updateStatus(ctx);
|
updateStatus(ctx);
|
||||||
if (!turn) return;
|
if (!turn) {
|
||||||
|
dispatchNextQueuedTelegramTurn(ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const assistant = extractAssistantText(event.messages);
|
const assistant = extractAssistantText(event.messages);
|
||||||
if (assistant.stopReason === "aborted") {
|
if (assistant.stopReason === "aborted") {
|
||||||
@@ -3173,11 +3396,8 @@ export default function (pi: ExtensionAPI) {
|
|||||||
|
|
||||||
await sendQueuedAttachments(turn);
|
await sendQueuedAttachments(turn);
|
||||||
|
|
||||||
if (queuedTelegramTurns.length > 0 && !preserveQueuedTurnsAsHistory) {
|
if (!preserveQueuedTurnsAsHistory) {
|
||||||
const nextTurn = queuedTelegramTurns[0];
|
dispatchNextQueuedTelegramTurn(ctx);
|
||||||
startTypingLoop(ctx, nextTurn.chatId);
|
|
||||||
updateStatus(ctx);
|
|
||||||
pi.sendUserMessage(nextTurn.content);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user