Fix DOM sticker screenshot selection

This commit is contained in:
sevenhill
2026-07-04 14:34:59 +03:00
parent 7c3c8d11bc
commit 438f7323d1
+104 -23
View File
@@ -875,38 +875,119 @@ async function downloadDomAudioThroughPlay(p, request) {
async function downloadDomStickerThroughScreenshot(p, request) {
const testId = String(request?.testId || "").trim();
if (testId) {
const selector = `[data-testid="${testId.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"]`;
const sticker = p.locator(selector).first();
if (await sticker.count()) {
await sticker.scrollIntoViewIfNeeded({ timeout: 3000 }).catch(() => {});
const buffer = await sticker.screenshot({ type: "png", timeout: 10000 });
return { contentType: "image/png", buffer };
const timeText = String(request?.timeText || "").trim();
const selectorForTestId = (value) => `[data-testid="${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"]`;
const screenshotCandidate = async (handles, requestedIndex) => {
const candidates = [];
for (const handle of handles) {
const meta = await handle.evaluate((node, requestedTimeText) => {
const element = node instanceof HTMLElement ? node : node.parentElement;
if (!element) {
return null;
}
const style = window.getComputedStyle(element);
const rect = element.getBoundingClientRect();
if (style.visibility === "hidden" ||
style.display === "none" ||
rect.width <= 0 ||
rect.height <= 0 ||
rect.bottom < 0 ||
rect.right < 0 ||
rect.top > window.innerHeight ||
rect.left > window.innerWidth) {
return null;
}
const wrapper = element.closest("[class*='messageWrapper' i], [role='listitem'], [class*='item' i], [class*='bubbleContent' i], [class*='bordersWrapper' i]") || element;
const cleanText = (value) => String(value || "")
.replace(/\u00a0/g, " ")
.replace(/\s+/g, " ")
.trim();
const wrapperText = cleanText(wrapper.innerText || wrapper.textContent);
const metaText = Array.from(wrapper.querySelectorAll("[class*='meta' i], time"))
.map((item) => cleanText(
item.getAttribute?.("datetime") ||
item.getAttribute?.("aria-label") ||
item.getAttribute?.("title") ||
item.innerText ||
item.textContent))
.filter(Boolean)
.join(" ");
const timeMatches = requestedTimeText &&
(wrapperText.includes(requestedTimeText) || metaText.includes(requestedTimeText));
const semantic = [
element.getAttribute("data-testid"),
element.getAttribute("aria-label"),
element.getAttribute("title"),
element.className,
wrapper.className,
wrapperText
].filter(Boolean).join(" ").toLowerCase();
const looksLikeSticker = /sticker|\u0441\u0442\u0438\u043a\u0435\u0440/.test(semantic) ||
Boolean(element.querySelector("canvas,img,svg")) ||
element.matches("canvas,img,svg");
const area = rect.width * rect.height;
let score = 0;
if (looksLikeSticker) score += 4;
if (timeMatches) score += 40;
if (rect.width >= 72 && rect.height >= 72) score += 16;
if (rect.width >= 120 && rect.height >= 120) score += 8;
score += Math.min(12, area / 2500);
if (rect.width < 40 || rect.height < 40) score -= 30;
if (area < 4096) score -= 20;
return {
score,
width: rect.width,
height: rect.height,
y: rect.y,
area,
timeMatches: Boolean(timeMatches)
};
}, timeText).catch(() => null);
if (!meta || meta.score <= 0) {
await handle.dispose().catch(() => {});
continue;
}
candidates.push({ handle, meta });
}
}
const strict = timeText
? candidates.filter((candidate) => candidate.meta.timeMatches)
: candidates;
const sorted = (strict.length ? strict : candidates)
.sort((a, b) => b.meta.score - a.meta.score || b.meta.area - a.meta.area || b.meta.y - a.meta.y);
const target = sorted[Math.max(0, Math.min(requestedIndex, sorted.length - 1))];
if (!target) {
return null;
}
await target.handle.scrollIntoViewIfNeeded({ timeout: 3000 }).catch(() => {});
const buffer = await target.handle.screenshot({ type: "png", timeout: 10000 });
for (const item of candidates) {
await item.handle.dispose().catch(() => {});
}
return { contentType: "image/png", buffer };
};
const index = Number.isFinite(Number(request?.index)) ? Number(request.index) : 0;
const handles = await p.$$("[data-testid^='sticker-'], button[class*='sticker' i], [class*='sticker' i] canvas, canvas");
const visible = [];
for (const handle of handles) {
const box = await handle.boundingBox().catch(() => null);
if (box && box.width >= 24 && box.height >= 24) {
visible.push({ handle, box });
} else {
await handle.dispose().catch(() => {});
if (testId) {
const handles = await p.$$(selectorForTestId(testId));
const result = await screenshotCandidate(handles, 0);
if (result) {
return result;
}
}
const target = visible[Math.max(0, Math.min(index, visible.length - 1))];
if (!target) {
const handles = await p.$$("[data-testid^='sticker-'], button[class*='sticker' i], [class*='sticker' i] canvas, canvas");
const result = await screenshotCandidate(handles, index);
if (!result) {
throw new Error("MAX sticker canvas was not found.");
}
const buffer = await target.handle.screenshot({ type: "png", timeout: 10000 });
for (const item of visible) {
await item.handle.dispose().catch(() => {});
}
return { contentType: "image/png", buffer };
return result;
}
function contentTypeFromFileName(fileName) {