Aktueller Stand

This commit is contained in:
MDeeApp
2025-10-20 15:27:33 +02:00
parent 9745d38995
commit f602adb4c1
2 changed files with 164 additions and 18 deletions

View File

@@ -21,6 +21,7 @@ const trackerElementsByPost = new WeakMap();
const postAdditionalNotes = new WeakMap(); const postAdditionalNotes = new WeakMap();
const REELS_PATH_PREFIX = '/reel/'; const REELS_PATH_PREFIX = '/reel/';
const POST_TEXT_LOG_TAG = '[FB PostText]';
function isOnReelsPage() { function isOnReelsPage() {
try { try {
@@ -2376,21 +2377,51 @@ async function createTrackerUI(postElement, buttonBar, postNum = '?', options =
// Insert UI - try multiple strategies to find stable insertion point // Insert UI - try multiple strategies to find stable insertion point
let inserted = false; let inserted = false;
const tryInsertBeforeReelsCommentComposer = () => {
const textboxCandidates = postElement ? postElement.querySelectorAll('div[role="textbox"]') : [];
const composerElement = Array.from(textboxCandidates).find((element) => {
const ariaLabel = (element.getAttribute('aria-label') || '').toLowerCase();
const ariaPlaceholder = (element.getAttribute('aria-placeholder') || '').toLowerCase();
const combined = `${ariaLabel} ${ariaPlaceholder}`;
return combined.includes('komment') || combined.includes('comment');
});
if (!composerElement) {
return false;
}
const anchorRoot = composerElement.closest('form[role="presentation"]')
|| composerElement.closest('form')
|| composerElement.parentElement;
if (!anchorRoot || !anchorRoot.parentElement) {
return false;
}
anchorRoot.parentElement.insertBefore(container, anchorRoot);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted before comment composer (Reels complementary). ID: #' + container.id);
return true;
};
if (!inserted && isOnReelsPage() && postElement && postElement.matches('div[role="complementary"]')) {
inserted = tryInsertBeforeReelsCommentComposer();
}
// Strategy 1: After button bar's parent (more stable) // Strategy 1: After button bar's parent (more stable)
if (buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement) { if (!inserted && buttonBar && buttonBar.parentElement && buttonBar.parentElement.parentElement) {
const grandParent = buttonBar.parentElement.parentElement; const grandParent = buttonBar.parentElement.parentElement;
grandParent.insertBefore(container, buttonBar.parentElement.nextSibling); grandParent.insertBefore(container, buttonBar.parentElement.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar parent. ID: #' + container.id); console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar parent. ID: #' + container.id);
inserted = true; inserted = true;
} }
// Strategy 2: After button bar directly // Strategy 2: After button bar directly
else if (buttonBar && buttonBar.parentElement) { if (!inserted && buttonBar && buttonBar.parentElement) {
buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling); buttonBar.parentElement.insertBefore(container, buttonBar.nextSibling);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar. ID: #' + container.id); console.log('[FB Tracker] Post #' + postNum + ' - UI inserted after button bar. ID: #' + container.id);
inserted = true; inserted = true;
} }
// Strategy 3: Append to post element // Strategy 3: Append to post element
else { if (!inserted) {
postElement.appendChild(container); postElement.appendChild(container);
console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id); console.log('[FB Tracker] Post #' + postNum + ' - UI inserted into article (fallback). ID: #' + container.id);
inserted = true; inserted = true;
@@ -3027,13 +3058,64 @@ function extractPostText(postElement) {
return ''; return '';
} }
// Try to find the post content div const logPostText = (...args) => {
try {
console.log(POST_TEXT_LOG_TAG, ...args);
} catch (error) {
// ignore logging failure
}
};
const SKIP_TEXT_CONTAINERS_SELECTOR = [
'div[role="textbox"]',
'[contenteditable="true"]',
'[data-lexical-editor="true"]',
'form[role="presentation"]',
'form[method]',
'.fb-tracker-ui',
'.fb-tracker-ai-wrapper',
'[aria-label*="komment"]',
'[aria-label*="comment"]',
'[aria-roledescription*="komment"]',
'[aria-roledescription*="comment"]'
].join(', ');
const KEYWORD_HINTS = ['meta', 'facebook', 'instagram'];
const isInsideSkippedRegion = (element) => {
if (!element || typeof element.closest !== 'function') {
return false;
}
return Boolean(element.closest(SKIP_TEXT_CONTAINERS_SELECTOR));
};
const scoreCandidate = (text) => {
const base = text.length;
const lower = text.toLowerCase();
let bonus = 0;
for (const keyword of KEYWORD_HINTS) {
if (lower.includes(keyword)) {
bonus += 200;
}
}
return base + bonus;
};
const makeSnippet = (text) => {
if (!text) {
return '';
}
const trimmed = text.trim();
return trimmed.length > 140 ? `${trimmed.substring(0, 137)}` : trimmed;
};
const contentSelectors = [ const contentSelectors = [
'[data-ad-preview="message"]', '[data-ad-preview="message"]',
'[data-ad-comet-preview="message"]', '[data-ad-comet-preview="message"]',
'div[dir="auto"][style*="text-align"]',
'div[data-ad-comet-preview] > div > div > span', 'div[data-ad-comet-preview] > div > div > span',
'.x193iq5w.xeuugli' // Common Facebook text class '.x193iq5w.xeuugli', // Common Facebook text class
'span[dir="auto"]',
'div[dir="auto"]'
]; ];
const uiTextPattern = /(Gefällt mir|Kommentieren|Teilen|Like|Comment|Share)/gi; const uiTextPattern = /(Gefällt mir|Kommentieren|Teilen|Like|Comment|Share)/gi;
@@ -3053,11 +3135,13 @@ function extractPostText(postElement) {
.trim(); .trim();
if (!cleaned) { if (!cleaned) {
logPostText('Discard empty candidate after cleaning');
return ''; return '';
} }
// Ignore very short snippets that are likely button labels // Ignore very short snippets that are likely button labels
if (cleaned.length < 5 && cleaned.split(/\s+/).length < 2) { if (cleaned.length < 5 && cleaned.split(/\s+/).length < 2) {
logPostText('Discard very short candidate', makeSnippet(text));
return ''; return '';
} }
@@ -3065,33 +3149,85 @@ function extractPostText(postElement) {
}; };
const candidates = []; const candidates = [];
const seen = new Set();
const tryAddCandidate = (rawText, element = null, context = {}) => {
const candidate = cleanCandidate(rawText);
if (!candidate) {
if (rawText) {
logPostText('Candidate rejected during cleaning', makeSnippet(rawText), context);
}
return;
}
if (seen.has(candidate)) {
logPostText('Candidate skipped as duplicate', makeSnippet(candidate), context);
return;
}
if (element && isInsideSkippedRegion(element)) {
logPostText('Candidate inside skipped region', makeSnippet(candidate), context);
return;
}
seen.add(candidate);
candidates.push({
text: candidate,
score: scoreCandidate(candidate)
});
logPostText('Candidate accepted', {
score: scoreCandidate(candidate),
snippet: makeSnippet(candidate),
context
});
};
logPostText('Begin extraction');
for (const selector of contentSelectors) { for (const selector of contentSelectors) {
const elements = postElement.querySelectorAll(selector); const elements = postElement.querySelectorAll(selector);
for (const element of elements) { for (const element of elements) {
const candidate = cleanCandidate(element.innerText || element.textContent || ''); if (isInsideSkippedRegion(element)) {
if (candidate) { logPostText('Selector result skipped (inside disallowed region)', selector, makeSnippet(element.innerText || element.textContent || ''));
candidates.push(candidate); continue;
} }
} tryAddCandidate(element.innerText || element.textContent || '', element, { selector });
if (candidates.length) {
break;
} }
} }
let textContent = ''; let textContent = '';
if (candidates.length) { if (candidates.length) {
textContent = candidates.reduce((longest, current) => ( const best = candidates.reduce((top, current) => (
current.length > longest.length ? current : longest current.score > top.score ? current : top
), ''); ), candidates[0]);
textContent = best.text;
logPostText('Best candidate selected', {
score: best.score,
snippet: makeSnippet(best.text)
});
} }
// Fallback: Get all text but filter out common UI elements
if (!textContent) { if (!textContent) {
let fallbackText = '';
try {
const clone = postElement.cloneNode(true);
const elementsToRemove = clone.querySelectorAll(SKIP_TEXT_CONTAINERS_SELECTOR);
elementsToRemove.forEach((node) => node.remove());
const cloneText = clone.innerText || clone.textContent || '';
fallbackText = cleanCandidate(cloneText);
logPostText('Fallback extracted from clone', makeSnippet(fallbackText || cloneText));
} catch (error) {
const allText = postElement.innerText || postElement.textContent || ''; const allText = postElement.innerText || postElement.textContent || '';
textContent = cleanCandidate(allText); fallbackText = cleanCandidate(allText);
logPostText('Fallback extracted from original element', makeSnippet(fallbackText || allText));
} }
textContent = fallbackText;
}
if (!textContent) {
logPostText('No usable text found');
return '';
}
logPostText('Final post text', makeSnippet(textContent));
return textContent.substring(0, 2000); // Limit length return textContent.substring(0, 2000); // Limit length
} }

10
fb_complementaryPost.txt Normal file

File diff suppressed because one or more lines are too long