diff --git a/browser-use-service/main.py b/browser-use-service/main.py index b24f6b9..deb303b 100644 --- a/browser-use-service/main.py +++ b/browser-use-service/main.py @@ -341,6 +341,7 @@ OFFER_PATTERNS = [ r"\bfree\s+domain\b", r"\bweb\s+hosting\b", r"\bfree\s+website\b", + r"\bfree\s+\w+\s+website\b", r"\bprofessional\s+website", r"\baffordable\s+web\b", r"\bget\s+a\s+free\b", @@ -365,6 +366,88 @@ OFFER_PATTERNS = [ r"\bsponsored\b", r"\bpromoted\b", r"\badvertisement\b", + r"\bdo\s+you\s+(need|want)\b", + r"\bwe\s+(can|will)\s+(build|design|create|develop|do)\b", + r"\bi\s+(can|will)\s+(build|design|create|develop|do)\b", + r"\bmy\s+portfolio\b", + r"\breasonable\s+(price|pricing|rate)\b", + r"\bwhatsapp\s+(me|us|at)\b", + r"\bwhatsapp\b", + r"\blooking\s+for\s+(a\s+)?(business|client|customer)\b", + r"\bhelp\s+your\s+business\b", + r"\bi\s+am\s+a\s+(web|freelance|designer|developer)\b", + r"\bcontact\s+me\b", + r"\bwe\s+(are|offer|provide)\s+(web|website|design|service)\b", + r"\btake\s+(the\s+|this\s+)?quiz\b", + r"\bhomeschool\b", + r"\byour\s+(home\s+)?tutor\b", + r"\blink\s+(in\s+)?(bio|comment|below)\b", + r"\bapply\s+now\b", + r"\bget\s+started\b", + r"\bfor\s+only\b", + r"\blow\s+(price|cost|rate)\b", + r"\bhit\s+me\s+up\b", + r"\bsend\s+me\s+(a\s+)?(message|dm|pm)\b", + r"\bi\s+do\s+(web|website|design|development)\b", + r"\bwe\s+do\s+(web|website|design|development)\b", + r"\bi'm\s+offering\b", + r"\bwe're\s+offering\b", + r"\bstarting\s+a\s+\w+\s+(service|business)\b", + r"\blooking\s+for\s+a\s+few\s+businesses?\b", + r"\blet('?)s\s+connect\b", + r"\bi\s+have\s+projects?\b", + r"\byour\s+business\b", + r"\bwordpress\s+development\b", + r"\be.?commerce\s+website\b", + r"\bwebsite\s+builder\b", + r"\bai\s+studio\b", + r"\bpage\s+speed\b", + r"\bguest\s+post", + r"\bguest\s+blog", + r"\bfor\s+sale\b", + r"\bselling\s+(my|a|the|this)\b", + r"\bpremium\b", + r"\bi'm\s+selling\b", + r"\bgroup\b", + r"\bi\s+can\s+help\b", + r"\binbox\s+me\b", + r"\bpm\s+me\b", + r"\bdm\s+me\b", + r"\bmessage\s+me\s+for\b", + r"\bquote\b", + r"\bdiscount\b", + r"\bbest\s+(deal|price|offer|service)\b", + r"\bprice\s+(start|begin|include|range)\b", + r"\breach\s+out\b", + r"\bclick\s+(the\s+)?link\b", + r"\bcheck\s+(out|this|my)\b", + r"\bwebsite\s+for\s+your\b", + r"\bprobono\b", + r"\bfreelance\s+opportunit", + r"\bfull.?stack\b", + r"\bresponsive\s+(web)?sites?\b", + r"\blooking\s+for\s+(new\s+)?(projects?|work)\b", + r"\bmern\s+stack\b", + r"\bexpress\s+js\b", + r"\breact\s+js\b", + r"\bnode\s+js\b", + r"\bwebsite\s+for\s+\$\b", + r"\bi\s+build\s+(websites?|shopify|wordpress)\b", + r"\bwe\s+build\s+(websites?|shopify|wordpress)\b", + r"\bi\s+create\s+(websites?|shopify|wordpress)\b", + r"\bwe\s+create\s+(websites?|shopify|wordpress)\b", + r"\bfull.?time\s+(position|job|role|work)\b", + r"\bremote\s+(position|job|role|work)\b", + r"\bhelp\s+wanted\b", + r"\bjob\s+(opening|vacancy|opportunity)\b", + r"\bnow\s+hiring\b", + r"\bpart.?time\s+(position|job|role|work)\b", + r"\byears?\s+of\s+(teaching|experience)\b", + r"\bsend\s+(you|me)\s+(the\s+)?link\b", + r"\bcomment\s+.*\bsend\b", + r"\bfor\s+free\b", + r"\bmake\s+money\b", + r"\bno\s+coding\b", ] REQUEST_PATTERNS = [ @@ -549,6 +632,48 @@ FB_SEARCHES = [ "want to build a website for my", ] +TUTORING_SEARCHES = [ + "need a tutor", + "looking for a tutor", + "tutor for my child", + "need help with homework", + "private tutor needed", + "looking for tutoring", + "need a math tutor", + "english tutor needed", + "online tutor for", + "lessons for my child", + "help my child with", + "need someone to tutor", + "looking for someone to teach", + "tutoring for my", + "need help learning", + "recommend a tutor", + "anyone know a tutor", + "need a private tutor", + "tutoring services for my child", + "need academic help", + "need a reading tutor", + "looking for piano teacher", + "need help with maths", + "need a science tutor", + "looking for language tutor", + "need programming tutor", + "coding tutor for my", + "sat tutor needed", + "exam preparation tutor", + "need a homeschool tutor", + "tutor near me for", +] + +def _search_list_for_query(query: str) -> list[str]: + """Pick the appropriate search query pool based on the search term.""" + tl = query.lower() + tutoring_terms = ["tutor", "tutoring", "lessons", "homework", "teach", "learning", "child", "math", "english", "science", "exam", "homeschool", "coding", "programming", "piano", "reading"] + if any(t in tl for t in tutoring_terms): + return TUTORING_SEARCHES + return FB_SEARCHES + VIEWPORTS = [ {'width': 1280, 'height': 800}, {'width': 1366, 'height': 768}, @@ -663,16 +788,31 @@ def _clean_fb_text(text: str) -> str: cleaned_lines.append(stripped) return '\n'.join(cleaned_lines) +# Words that should never be treated as person names +_NON_NAMES = {"online","tutor","tutoring","school","academy","learning","urgently","looking","south","africa","philippines","australia","creative","professional","digital","services","solutions","statistics","actuarial","homeschooling","homeschool","reading","math","english","science","grade","group","page","website","design","development","agency","company","studio","graphic","technical","support","customer","guest","post","fashion","wordpress","shopify","ecommerce","business","marketing","social","media","content","strategy","seo","free","domain","hosting","sponsored","promoted","advertisement","need","want","help","please","contact","send","message","whatsapp","hire","freelance","writer","blogger","expert","specialist","consultant","freelancer","software","creators","village","town","city","university","college","institute","evolve","aesthetics","sale","selling","premium","builder","pro","people","ecommerce","press","anonymous","tuition","parents","tutors","advanced","custom","fields","speed","optimization","elementor","woocommerce","acf","availability","january","february","march","april","may","june","july","august","september","october","november","december","mums","dads","uae","sharjah","dubai","abu","dhabi","hong","kong","canada","usa","united","kingdom","aunty","piano","plus","recommendations","needed"} + # ── Post Extraction ────────────────────────────────────────────────── # Two strategies for extracting posts from page content: # 1. _extract_posts_from_elements — uses structured DOM data from _get_article_elements() # 2. _extract_posts_from_text — fallback that parses raw page text line by line # Both apply dedup (seen_texts), offer filtering, and request scoring. +_GROUP_SUFFIXES = [" - South Africa", " - Australia", " - Philippines", " PH", "Group", "help group", "info group", "public group", "private group", "group for", "community", "creators", "marketplace"] + def _extract_posts_from_elements(elements: list[dict], base_url: str) -> list[dict]: posts = [] seen_texts = set() + now = datetime.utcnow() for el in elements: + # Reject group posts immediately + if el.get('isGroup'): + continue + # Reject posts older than 2 days + ts = el.get('ts', 0) + if ts: + age_seconds = (now.timestamp() * 1000 - ts) / 1000 + if age_seconds > 172800: # 2 days + continue raw_text = el.get('text', '') if len(raw_text) < 40: continue @@ -686,13 +826,23 @@ def _extract_posts_from_elements(elements: list[dict], base_url: str) -> list[di if dekey in seen_texts: continue seen_texts.add(dekey) + # Reject if text contains group-like patterns + lower = text.lower() + if ' / ' in lower: + continue + skip = False + for suff in _GROUP_SUFFIXES: + if suff.lower() in lower: + skip = True + break + if skip: + continue request_score = 2 if is_request(text) else 0 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