Checks done

This commit is contained in:
Ace
2026-06-25 16:04:05 +02:00
parent 5668a63370
commit 9bbaf70145
+78 -18
View File
@@ -147,7 +147,7 @@ def extract_agent_posts(agent_result: str, page_text: str) -> list[dict]:
if isinstance(item, dict) and item.get('content'): if isinstance(item, dict) and item.get('content'):
posts.append({ posts.append({
'content': item.get('content', ''), 'content': item.get('content', ''),
'author': item.get('author', ''), 'author': item.get('author', '') or item.get('publisher', '') or '',
'url': item.get('url', ''), 'url': item.get('url', ''),
'date': item.get('date', ''), 'date': item.get('date', ''),
}) })
@@ -155,11 +155,20 @@ def extract_agent_posts(agent_result: str, page_text: str) -> list[dict]:
pass pass
if not posts: if not posts:
lines = [l.strip() for l in page_text.split('\n') if l.strip()] lines = [l.strip() for l in agent_result.split('\n') if l.strip()]
for line in lines: cur = []
if len(line) > 30 and not any(skip in line.lower() for skip in for l in lines:
['facebook', 'messenger', 'notification', 'comment', 'like', 'share']): if l.startswith(('1.', '2.', '3.', '4.', '5.', '6.', '7.', '8.', '9.', '- ', '* ')):
posts.append({'content': line[:500], 'author': '', 'url': '', 'date': ''}) if cur:
combined = ' '.join(cur)
if len(combined) > 30:
posts.append({'content': combined[:500], 'author': '', 'url': '', 'date': ''})
cur = []
cur.append(l)
if cur:
combined = ' '.join(cur)
if len(combined) > 30:
posts.append({'content': combined[:500], 'author': '', 'url': '', 'date': ''})
return posts return posts
@@ -443,6 +452,18 @@ def _parse_fb_date(block: list[str]) -> str:
pass pass
return datetime.now().strftime('%Y-%m-%d') return datetime.now().strftime('%Y-%m-%d')
def _is_within_days(date_str: str, max_days: int = 3) -> bool:
"""Check if date is within max_days from now. Empty/unparseable = keep."""
if not date_str:
return True
try:
dt = datetime.strptime(date_str.strip(), '%Y-%m-%d')
return (datetime.now() - dt).days <= max_days
except ValueError:
return True
def _clean_fb_text(text: str) -> str: def _clean_fb_text(text: str) -> str:
cleaned_lines = [] cleaned_lines = []
for l in text.split('\n'): for l in text.split('\n'):
@@ -475,13 +496,26 @@ def _extract_posts_from_elements(elements: list[dict], base_url: str) -> list[di
seen_texts.add(dekey) seen_texts.add(dekey)
request_score = 2 if is_request(text) else 0 request_score = 2 if is_request(text) else 0
post_url = el.get('url') or base_url post_url = el.get('url') or base_url
# Prefer JS-extracted date, fall back to text parsing
js_date = (el.get('date') or '').strip()
if js_date:
# Try ISO datetime from <time datetime>
try:
dt = datetime.fromisoformat(js_date.replace('Z', '+00:00'))
date_str = dt.strftime('%Y-%m-%d')
except (ValueError, TypeError):
date_str = _parse_fb_date([js_date])
else:
date_str = _parse_fb_date(lines)
posts.append({ posts.append({
"title": text[:300], "title": text[:300],
"content": text[:1000], "content": text[:1000],
"author": el.get('author', ''), "author": el.get('author', ''),
"url": post_url, "url": post_url,
"source": "facebook", "source": "facebook",
"date": _parse_fb_date(lines), "date": date_str,
"_score": request_score, "_score": request_score,
}) })
posts.sort(key=lambda p: p.get('_score', 0), reverse=True) posts.sort(key=lambda p: p.get('_score', 0), reverse=True)
@@ -588,24 +622,42 @@ async def _get_article_elements(page) -> list[dict]:
const key = text.substring(0, 80); const key = text.substring(0, 80);
if (seenTexts.has(key)) continue; if (seenTexts.has(key)) continue;
seenTexts.add(key); seenTexts.add(key);
const links = el.querySelectorAll('a');
// --- Publisher name ---
let author = ''; let author = '';
for (const a of links) { const nameEl = el.querySelector('h4, strong, a[role="link"]');
const t = (a.innerText || '').trim(); if (nameEl) {
if (t && t.length > 2 && t.length < 80 && /^[a-zA-ZÀ-ÿ][a-zA-ZÀ-ÿ .'-]+$/.test(t)) { author = (nameEl.innerText || '').trim();
author = t; }
break; if (!author) {
const links = el.querySelectorAll('a');
for (const a of links) {
const t = (a.innerText || '').trim();
if (t && t.length > 1 && t.length < 60 && /^[A-Za-zÀ-ÿ]/.test(t) && !t.includes('facebook') && !t.includes('/')) {
author = t;
break;
}
} }
} }
// --- Post URL ---
let postUrl = ''; let postUrl = '';
for (const a of links) { for (const a of el.querySelectorAll('a')) {
const h = (a.getAttribute('href') || ''); const h = (a.getAttribute('href') || '');
if (h.includes('/posts/') || h.includes('/photo/') || h.includes('/video/') || h.includes('/groups/')) { if (h.includes('/posts/') || h.includes('/photo/') || h.includes('/video/') || h.includes('/groups/') || h.includes('/permalink/')) {
postUrl = h.startsWith('http') ? h : 'https://www.facebook.com' + h; postUrl = h.startsWith('http') ? h : 'https://www.facebook.com' + h;
break; break;
} }
} }
results.push({ text, author, url: postUrl });
// --- Date from <time datetime> ---
let date = '';
const timeEl = el.querySelector('time');
if (timeEl) {
date = timeEl.getAttribute('datetime') || timeEl.getAttribute('aria-label') || '';
}
results.push({ text, author, url: postUrl, date });
} }
if (results.length > 0) break; if (results.length > 0) break;
} }
@@ -804,6 +856,9 @@ async def _scrape_with_firefox(profile_path: str, force: bool) -> dict:
seen.add(key) seen.add(key)
deduped.append(p) deduped.append(p)
# Filter to last 3 days only
deduped = [p for p in deduped if _is_within_days(p.get('date', ''), 3)]
leads = deduped[:20] leads = deduped[:20]
if leads: if leads:
leads = await classify_leads(leads) leads = await classify_leads(leads)
@@ -847,10 +902,12 @@ async def _scrape_with_agent(force: bool = False) -> dict:
2. Use the Facebook search to find: {query} 2. Use the Facebook search to find: {query}
3. Scroll through the results 3. Scroll through the results
4. For each relevant post, extract and list: 4. For each relevant post, extract and list:
- The publisher/author name (who posted it)
- The post text content - The post text content
- The author name
- The post URL (if visible) - The post URL (if visible)
5. Collect as many posts as you can (aim for 5-10 per search) - The post date
5. ONLY include posts from the last 3 days
6. Collect as many posts as you can (aim for 5-10 per search)
When done, return the data as a JSON list with keys: content, author, url, date.""", When done, return the data as a JSON list with keys: content, author, url, date.""",
llm=make_ollama(num_ctx=32000, temperature=0.3), llm=make_ollama(num_ctx=32000, temperature=0.3),
@@ -878,6 +935,9 @@ When done, return the data as a JSON list with keys: content, author, url, date.
seen.add(key) seen.add(key)
deduped.append(p) deduped.append(p)
# Filter to last 3 days only
deduped = [p for p in deduped if _is_within_days(p.get('date', ''), 3)]
leads = deduped[:20] leads = deduped[:20]
if leads: if leads:
leads = await classify_leads(leads) leads = await classify_leads(leads)