import gradio as gr import google.generativeai as genai from bs4 import BeautifulSoup, NavigableString import re import json import random import os # --- Constants & Config --- BLACKLIST_WORDS = [ "landscape", "realm", "navigate", "unveil", "explore", "transformative", "encompass", "examine", "crucial", "discover", "dive", "delve", "uncover", "unlock", "elevate", "unleash", "harness" ] BRITISH_MAPPINGS = { "color": "colour", "flavor": "flavour", "humor": "humour", "labor": "labour", "neighbor": "neighbour", "favor": "favour", "honor": "honour", "behavior": "behaviour", "center": "centre", "fiber": "fibre", "liter": "litre", "theater": "theatre", "meter": "metre", "analyze": "analyse", "breathalyze": "breathalyse", "paralyze": "paralyse", "catalyze": "catalyse", "organization": "organisation", "realize": "realise", "recognize": "recognise", "standardize": "standardise", "appetizer": "appetiser", "leukemia": "leukaemia", "maneuver": "manoeuvre", "estrogen": "oestrogen", "pediatric": "paediatric", "defense": "defence", "license": "licence", "offense": "offence", "pretense": "pretence", "traveler": "traveller", "modeling": "modelling", "cancelled": "cancelled", "program": "programme", } SOCIAL_PROOF_TEMPLATES = [ "We recently hired {KEYWORD} for our project, and the results were outstanding. The team was professional, efficient, and delivered exactly what we needed. I highly recommend their services to anyone looking for reliable {KEYWORD_LOWER}.", "I was struggling to find trustworthy {KEYWORD_LOWER} until I found this company. They exceeded my expectations with their attention to detail and timely completion. It was a refreshing experience to work with such dedicated professionals.", "If you need {KEYWORD_LOWER}, look no further. Their expertise is evident in the quality of their work, and the customer service is top-notch. I am completely satisfied with the outcome and will definitely use them again.", "Finding a dependable {KEYWORD} can be difficult, but this team made it easy. They communicated clearly throughout the process and finished the job to a high standard. I'm very impressed with their workmanship." ] # --- Logic Ports --- def capitalize(s): if not s: return "" return s[0].upper() + s[1:] def parse_growmatic_data(text): term_map = {} if not text: return term_map # Regex to match: "term": number% OR term: number% regex = r'["\']?([\w\s]+)["\']?\s*[:=]\s*(\d+)%?' matches = re.findall(regex, text) for term, score in matches: term_lower = term.strip().lower() if term_lower: term_map[term_lower] = int(score) return term_map def generate_titles(main_keyword, term_map): titles = [] # Templates templates = [ "{KEYWORD} in [location] - {TERM_A} [zip]", "{KEYWORD} in [location] - {TERM_B} Services [zip]", "Expert {KEYWORD} in [location] - {TERM_C} [zip]", "{KEYWORD} Services in [location] - {TERM_A} [zip]", "Leading {KEYWORD} in [location] - {TERM_B} [zip]", "{KEYWORD} Specialists in [location] - {TERM_C} [zip]", "Best {KEYWORD} in [location] - {TERM_A} Solutions [zip]" ] # Sort terms by score descending sorted_terms = sorted(term_map.keys(), key=lambda k: term_map[k], reverse=True) term_a = sorted_terms[0] if len(sorted_terms) > 0 else "Projects" term_b = sorted_terms[1] if len(sorted_terms) > 1 else "Installations" term_c = sorted_terms[2] if len(sorted_terms) > 2 else "Solutions" for tmpl in templates: t = tmpl.replace("{KEYWORD}", main_keyword) t = t.replace("{TERM_A}", capitalize(term_a)) t = t.replace("{TERM_B}", capitalize(term_b)) t = t.replace("{TERM_C}", capitalize(term_c)) titles.append(t) # Variations variations = [ f"{main_keyword} {capitalize(term_a)}", f"{main_keyword} {capitalize(term_b)} Services", f"{capitalize(term_a)} & {main_keyword}" ] return titles + variations def calculate_score(title, term_map): title_lower = title.lower() # Blacklist check for bad_word in BLACKLIST_WORDS: if bad_word in title_lower: return {"title": title, "score": 0, "terms": "BLACKLISTED"} total_score = 0 matched_terms = [] for term, weight in term_map.items(): if term in title_lower: total_score += weight matched_terms.append(f"{term} ({weight}%)") # Scale score (approx 0-10) final_score = round(total_score / 30, 1) if final_score > 10: final_score = 10 return { "title": title, "score": final_score, "terms": ", ".join(matched_terms) } def process_text_nodes(html_content, callback): if not html_content: return "" soup = BeautifulSoup(html_content, 'html.parser') # Recursive function specifically for NavigableStrings def walk(node): if isinstance(node, NavigableString): if node.parent.name not in ['script', 'style']: # Skip script/style tags new_text = callback(str(node)) if new_text != str(node): node.replace_with(new_text) elif hasattr(node, 'children'): for child in node.children: walk(child) walk(soup) return str(soup) def convert_to_british(html_content): if not html_content: return "" def replacer(text): processed = text for us, uk in BRITISH_MAPPINGS.items(): # Regex for whole word match, case insensitive pattern = re.compile(r'\b' + re.escape(us) + r'\b', re.IGNORECASE) def match_handler(m): # Preserve case word = m.group(0) if word[0].isupper(): return capitalize(uk) return uk processed = pattern.sub(match_handler, processed) return processed return process_text_nodes(html_content, replacer) def clean_homepage_content(html_content): if not html_content: return "" def replacer(text): clean = text # 1. Remove phrases phrases_to_remove = [ r'\s+in\s+\[location\]', r'in\s+\[location\]', r'\s+across\s+the\s+\[location\]', r'across\s+the\s+\[location\]', r'\s+across\s+\[location\]', r'across\s+\[location\]', r'\s+around\s+the\s+\[location\]', r'around\s+the\s+\[location\]', r'\s+nearby\s+\[location\]', r'nearby\s+\[location\]', r'\s+throughout\s+\[location\]', r'throughout\s+\[location\]' ] for phrase in phrases_to_remove: clean = re.sub(phrase, '', clean, flags=re.IGNORECASE) # 2. Remove tags tags_to_remove = [ r'\[location\]', r'\[county\]', r'\[region\]', r'\[zip\]' ] for tag in tags_to_remove: clean = re.sub(tag, '', clean, flags=re.IGNORECASE) # 3. Footer text footer_regex = r'in\s*\[region\]\.?\s*Here\s*are\s*some\s*towns\s*we\s*cover\s*near\s*\[location\]\s*\[zip\]\s*\[cities[^\]]*\]' clean = re.sub(footer_regex, '', clean, flags=re.IGNORECASE | re.DOTALL) # 4. Whitespace cleanup clean = re.sub(r'\s{2,}', ' ', clean) clean = re.sub(r'\s+\.', '.', clean) clean = re.sub(r'\s+\?', '?', clean) clean = re.sub(r'\s+\,', ',', clean) return clean.strip() return process_text_nodes(html_content, replacer) # --- Gemini Integration --- def call_gemini(prompt, api_key, model_name="gemini-1.5-flash"): if not api_key: return None try: genai.configure(api_key=api_key) model = genai.GenerativeModel(model_name) response = model.generate_content(prompt) return response.text except Exception as e: return f"Error: {str(e)}" # --- Main Automation Logic --- def run_automation(main_keyword, site_link, growmatic_data, api_key, article_content, model_selection): if not main_keyword: return "Error: Main Keyword is required.", "" term_map = parse_growmatic_data(growmatic_data) # 1. Magic Page Logic magic_output_html = "" # SEO Titles if api_key: # LLM Title Gen terms_str = ", ".join([f"{k} ({v}%)" for k, v in term_map.items()]) prompt = f"""Act as an SEO expert. Main Keyword: "{main_keyword}" Semantic Terms (Growmatic Data): {terms_str} Task: 1. Generate 3 highly optimized Meta Titles for a page targeting "{main_keyword}". Use the semantic terms to increase relevance. 2. Generate a list of 5-8 Meta Keywords (comma separated). 3. Select the "Best" Title from the 3 options based on SEO scoring principles. Output JSON format ONLY (no markdown): {{ "metaTitles": ["Title 1", "Title 2", "Title 3"], "bestTitle": "The Best Title", "metaKeywords": "keyword1, keyword2, keyword3" }}""" llm_resp = call_gemini(prompt, api_key, model_selection) try: # Clean json block if present clean_json = llm_resp.replace('```json', '').replace('```', '').strip() data = json.loads(clean_json) magic_output_html += "

--- GENERATED SEO TITLES (LLM) ---

" for t in data.get("metaTitles", []): is_best = t == data.get("bestTitle") style = "color: blue; font-weight: bold;" if is_best else "" suffix = "(Best Match)" if is_best else "" magic_output_html += f'

• {t} {suffix}

' magic_output_html += f"

Meta Keywords: {data.get('metaKeywords', '')}


" except: magic_output_html += f"

Error parsing LLM response: {llm_resp}

" else: # Template Gen titles = generate_titles(main_keyword, term_map) scored = [calculate_score(t, term_map) for t in titles] scored.sort(key=lambda x: x['score'], reverse=True) magic_output_html += "

--- GENERATED SEO TITLES (Template) ---

" for item in scored[:5]: magic_output_html += f"

• [Score: {item['score']}] {item['title']}

" magic_output_html += "
" # Social Proof social_proof_text = "" if api_key: sp_prompt = f"""Write 2 positive testimonials for a service provider offering "{main_keyword}". Create two very non-generic names including last names. Each testimonial should be max 3-4 sentences. Focus on professionalism, result quality, and ease of working with them.""" social_proof_text = call_gemini(sp_prompt, api_key, model_selection) else: tmpl = random.choice(SOCIAL_PROOF_TEMPLATES) social_proof_text = tmpl.replace("{KEYWORD}", main_keyword).replace("{KEYWORD_LOWER}", main_keyword.lower()) magic_output_html += f"

--- MAGIC PAGE METADATA ---

" magic_output_html += f"

Target Keyword: {main_keyword}

" magic_output_html += f"

Site URL: {site_link}


" magic_output_html += f"

--- SOCIAL PROOF ---

" magic_output_html += f"

{social_proof_text.replace(chr(10), '
')}

" # 2. Homepage Logic clean_html = clean_homepage_content(article_content) british_html = convert_to_british(clean_html) return magic_output_html, british_html # --- Gradio UI --- with gr.Blocks(title="Content Automation Tool") as app: gr.Markdown("# Content Automation Tool (Gradio Edition)") gr.Markdown("Generate Magic Page & Optimized Homepage Content Instantly") with gr.Row(): with gr.Column(): main_keyword = gr.Textbox(label="Main Keyword", placeholder="e.g. Suspended Ceiling Contractors") site_link = gr.Textbox(label="Site Link", placeholder="e.g. https://example.com") growmatic_data = gr.TextArea(label="Growmatic Data", placeholder='"suspended": 100%, "ceiling": 73%') with gr.Row(): api_key = gr.Textbox(label="Gemini API Key", type="password", placeholder="AIza...") model_selection = gr.Dropdown( choices=["gemini-1.5-flash", "gemini-1.5-pro", "gemini-1.0-pro"], value="gemini-1.5-flash", label="Gemini Model" ) with gr.Column(): article_content = gr.Textbox(label="Article Content (HTML/Text)", lines=15, placeholder="Paste content with [tags] here...") generate_btn = gr.Button("Generate Output ✨", variant="primary") with gr.Row(): with gr.Column(): gr.Markdown("### Magic Page Output") magic_output = gr.HTML(label="Magic Page Result") with gr.Column(): gr.Markdown("### Homepage Output") home_output = gr.HTML(label="Homepage Result") generate_btn.click( fn=run_automation, inputs=[main_keyword, site_link, growmatic_data, api_key, article_content, model_selection], outputs=[magic_output, home_output] ) if __name__ == "__main__": app.launch()