Fix chat bulk delete URL resolution

This commit is contained in:
sevenhill
2026-07-05 18:30:46 +03:00
parent 4db643b028
commit 343a3a17f5
7 changed files with 351 additions and 1 deletions
+134
View File
@@ -338,6 +338,34 @@ app.post("/chat/history", async (req, res) => {
}
});
app.post("/chat/resolve-url", async (req, res) => {
try {
const externalChatId = String(req.body?.externalChatId || "").trim();
if (!externalChatId) {
return res.status(400).json({ success: false, chatUrl: null, error: "externalChatId is required." });
}
const result = await withPageLock(async () => {
const p = await ensurePage("incoming");
const currentStatus = await status(null, p);
if (!currentStatus.isAuthorized) {
return { success: false, chatUrl: null, error: "MAX is not authorized." };
}
const chatUrl = await resolveDomChatUrl(p, externalChatId);
return {
success: Boolean(chatUrl),
chatUrl: chatUrl || null,
error: chatUrl ? null : "MAX chat URL was not found."
};
}, "incoming");
res.json(result);
} catch (error) {
res.status(500).json(errorStatus(error));
}
});
app.post("/chat/clear-history", async (req, res) => {
try {
const externalChatId = String(req.body?.externalChatId || "").trim();
@@ -3064,6 +3092,112 @@ async function scrollSidebarForward(p) {
}).catch(() => false);
}
function normalizeDomTitle(value) {
return cleanComparableText(value, 160).toLowerCase();
}
function rowMatchesDomChatDescriptor(row, descriptor) {
const wanted = normalizeDomTitle(descriptor?.title);
const current = normalizeDomTitle(row?.title);
return Boolean(wanted && current && (
current === wanted ||
current.includes(wanted) ||
wanted.includes(current)
));
}
function chooseDomChatRow(rows, descriptor) {
const matches = rows.filter((row) => rowMatchesDomChatDescriptor(row, descriptor));
if (matches.length === 0) {
return null;
}
const expectedAvatar = normalizeDomChatFingerprint(descriptor?.avatarUrl);
if (expectedAvatar) {
const strict = matches.filter((row) => normalizeDomChatFingerprint(row.avatarUrl) === expectedAvatar);
if (strict.length > 0) {
return strict[0];
}
}
return matches.length === 1 ? matches[0] : null;
}
async function clickMatchingSidebarRowAndReadUrl(p, descriptor) {
const row = chooseDomChatRow(await readVisibleSidebarChatRows(p), descriptor);
if (!row) {
return null;
}
await p.mouse.click(row.x, row.y);
await waitForDomChatReady(p, descriptor.title, 5000, false);
await p.waitForTimeout(150);
return await getCurrentChatUrl(p);
}
async function resolveDomChatUrl(p, externalChatId) {
const descriptor = decodeDomChatDescriptor(externalChatId);
if (!descriptor?.title) {
return null;
}
const embeddedUrl = normalizeMaxChatUrl(descriptor.href);
if (embeddedUrl) {
return embeddedUrl;
}
const opened = await ensureDomChatOpen(p, externalChatId, 5000, false, null);
if (opened) {
const currentUrl = await getCurrentChatUrl(p);
if (currentUrl) {
return currentUrl;
}
}
await clearSidebarSearch(p);
await resetSidebarListToTop(p);
await waitForMaxChatListReady(p, 15000);
let chatUrl = await clickMatchingSidebarRowAndReadUrl(p, descriptor);
if (chatUrl) {
await clearSidebarSearch(p);
return chatUrl;
}
const searched = await searchSidebarChat(p, descriptor.title);
if (searched) {
chatUrl = await clickMatchingSidebarRowAndReadUrl(p, descriptor);
if (chatUrl) {
await clearSidebarSearch(p);
return chatUrl;
}
}
await clearSidebarSearch(p);
await resetSidebarListToTop(p);
await waitForMaxChatListReady(p, 15000);
let stagnantPages = 0;
for (let pageIndex = 0; pageIndex < 220; pageIndex++) {
chatUrl = await clickMatchingSidebarRowAndReadUrl(p, descriptor);
if (chatUrl) {
await clearSidebarSearch(p);
return chatUrl;
}
const moved = await scrollSidebarForward(p);
if (!moved) break;
await p.waitForTimeout(250);
const rows = await readVisibleSidebarChatRows(p);
stagnantPages = rows.length === 0 ? stagnantPages + 1 : 0;
if (stagnantPages >= 4) break;
}
await clearSidebarSearch(p);
return null;
}
async function collectSidebarChatUrls(p, limit = 600) {
await clearSidebarSearch(p);
await resetSidebarListToTop(p);