Files
dragonsstash/worker/dist/preview/match.js
copilot-swe-agent[bot] 9adbdb2a77 Fix worker getting stuck during sync: add timeouts, stuck detection, and safety limits
- Add invokeWithTimeout wrapper for TDLib API calls (2min timeout per call)
- Add stuck detection to getChannelMessages: break if from_message_id doesn't advance
- Add stuck detection to getTopicMessages: same protection for topic scanning
- Add stuck detection to getForumTopicList: break if pagination offsets don't advance
- Add max page limit (5000) to all scanning loops to prevent infinite pagination
- Add mutex wait timeout (30min) to prevent indefinite blocking when holder hangs
- Add cycle timeout (4h default, configurable via WORKER_CYCLE_TIMEOUT_MINUTES)
- Fix end-of-page detection to use actual limit value instead of hardcoded 100

Co-authored-by: xCyanGrizzly <53275238+xCyanGrizzly@users.noreply.github.com>
2026-03-05 13:14:53 +00:00

53 lines
2.0 KiB
JavaScript

import { childLogger } from "../util/logger.js";
const log = childLogger("preview-match");
/**
* Try to match a photo message to an archive by:
* 1. Caption contains the archive baseName (without extension)
* 2. Photo was posted within ±10 messages (time-window: ±6 hours)
*
* Returns the best match (closest in time), or null.
*/
export function matchPreviewToArchive(photos, archives) {
const results = new Map();
const TIME_WINDOW_MS = 6 * 60 * 60 * 1000; // 6 hours
for (const archive of archives) {
// Normalize the archive base name for matching
const normalizedBase = normalizeForMatch(archive.baseName);
if (!normalizedBase)
continue;
let bestMatch = null;
let bestTimeDiff = Infinity;
for (const photo of photos) {
const timeDiff = Math.abs(photo.date.getTime() - archive.firstMessageDate.getTime());
// Must be within time window
if (timeDiff > TIME_WINDOW_MS)
continue;
// Check if the photo caption contains the archive base name
const normalizedCaption = normalizeForMatch(photo.caption);
if (!normalizedCaption)
continue;
const matches = normalizedCaption.includes(normalizedBase) ||
normalizedBase.includes(normalizedCaption);
if (matches && timeDiff < bestTimeDiff) {
bestMatch = photo;
bestTimeDiff = timeDiff;
}
}
if (bestMatch) {
log.debug({ baseName: archive.baseName, photoId: bestMatch.id.toString() }, "Matched preview photo to archive");
results.set(archive.baseName, bestMatch);
}
}
return results;
}
/**
* Strip extension, punctuation, and normalize for fuzzy matching.
*/
function normalizeForMatch(input) {
return input
.toLowerCase()
.replace(/\.[a-z0-9]{1,5}$/i, "") // strip extension
.replace(/[_\-.\s]+/g, " ") // normalize separators
.trim();
}
//# sourceMappingURL=match.js.map