import streamlit as st import os import requests from typing import Dict, Optional import time from datetime import datetime # Page configuration st.set_page_config( page_title="VeriFact - Claim Verifier", page_icon="🔍", layout="wide", initial_sidebar_state="expanded" ) # FIXED CSS - Much better colors and visibility! st.markdown(""" """, unsafe_allow_html=True) # ============================== # Google Fact Check API Class # ============================== class GoogleFactCheckAPI: """Google Fact Check Tools API Integration""" def __init__(self, api_key: Optional[str] = None): self.api_key = api_key or os.environ.get('GOOGLE_API_KEY') self.base_url = "https://factchecktools.googleapis.com/v1alpha1/claims:search" if not self.api_key: st.markdown("""

âš ī¸ Google API Key not found. Add it in Settings → Repository secrets

""", unsafe_allow_html=True) def verify_claim(self, claim: str, language: str = "en") -> Dict: if not self.api_key: return {"status": "error", "message": "❌ API key not configured. Please add GOOGLE_API_KEY in Hugging Face Secrets."} try: params = {"key": self.api_key, "query": claim, "languageCode": language, "pageSize": 10} response = requests.get(self.base_url, params=params, timeout=15) response.raise_for_status() data = response.json() if "claims" not in data or len(data["claims"]) == 0: simplified_query = self._simplify_claim(claim) if simplified_query != claim: params["query"] = simplified_query response = requests.get(self.base_url, params=params, timeout=15) response.raise_for_status() data = response.json() if "claims" not in data or len(data["claims"]) == 0: return {"status": "no_results", "message": f"No fact-checks found for: '{claim}'"} results = [] for claim_data in data["claims"]: for review in claim_data.get("claimReview", []): results.append({ "claim_text": claim_data.get("text", ""), "claimant": claim_data.get("claimant", "Unknown"), "claim_date": claim_data.get("claimDate", "Unknown"), "publisher": review.get("publisher", {}).get("name", "Unknown"), "url": review.get("url", ""), "title": review.get("title", ""), "rating": review.get("textualRating", "Unknown"), "language": review.get("languageCode", "en") }) return {"status": "success", "claim": claim, "results": results, "count": len(results)} except requests.exceptions.HTTPError as e: if e.response.status_code == 403: return {"status": "error", "message": "❌ API key is invalid or Fact Check API is not enabled. Check Google Cloud Console."} return {"status": "error", "message": f"❌ HTTP Error: {str(e)}"} except requests.exceptions.RequestException as e: return {"status": "error", "message": f"❌ Network error: {str(e)}"} except Exception as e: return {"status": "error", "message": f"❌ Unexpected error: {str(e)}"} def _simplify_claim(self, claim: str) -> str: stop_words = ['the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'should', 'could', 'may', 'might', 'can'] words = claim.lower().split() key_words = [w for w in words if w not in stop_words and len(w) > 2] return ' '.join(key_words[:5]) if key_words else claim # ============================== # Helper Functions # ============================== def determine_verdict_color(rating: str) -> str: rating_lower = rating.lower() true_keywords = ["true", "correct", "accurate", "mostly true", "verified", "confirmed"] false_keywords = ["false", "incorrect", "inaccurate", "mostly false", "debunked", "fake", "pants on fire"] mixed_keywords = ["mixture", "mixed", "partially", "misleading", "unproven", "undetermined"] if any(k in rating_lower for k in true_keywords): return "verdict-true" elif any(k in rating_lower for k in false_keywords): return "verdict-false" elif any(k in rating_lower for k in mixed_keywords): return "verdict-mixed" return "verdict-unverified" def get_verdict_emoji(rating: str) -> str: return {"verdict-true":"✅","verdict-false":"❌","verdict-mixed":"âš ī¸","verdict-unverified":"❓"}.get(determine_verdict_color(rating),"❓") def classify_rating(rating: str) -> str: r = rating.lower() if any(k in r for k in ["false","fake","pants on fire"]): return "false" if any(k in r for k in ["true","correct","accurate"]): return "true" if any(k in r for k in ["mixed","misleading","partially","half","mostly"]): return "mixed" return "unverified" # ============================== # Main App # ============================== def main(): st.markdown('
🔍 VeriFact
', unsafe_allow_html=True) st.markdown('
AI-Powered Fact Checking â€ĸ Built by Areeba Fatima
', unsafe_allow_html=True) # Sidebar with st.sidebar: st.header("âš™ī¸ Configuration") api_key_input = st.text_input("Google API Key (Optional)", type="password", help="Leave empty to use Hugging Face Secrets") st.markdown("---") st.markdown("""

â„šī¸ VeriFact verifies claims using Google's Fact Check Tools API against databases from PolitiFact, Snopes, FactCheck.org, and 50+ other sources.

""", unsafe_allow_html=True) st.markdown("---") st.markdown("""

â„šī¸ How to Use:

""", unsafe_allow_html=True) st.markdown("---") with st.expander("📝 Example Claims to Try"): st.code(""" ✅ TRUE: â€ĸ Water boils at 100°C at sea level â€ĸ Pakistan gained independence in 1947 â€ĸ The Earth orbits the Sun ❌ FALSE: â€ĸ The Earth is flat â€ĸ Vaccines cause autism â€ĸ 5G causes COVID-19 âš ī¸ MIXED: â€ĸ Coffee is good for health â€ĸ Organic food is healthier """) # Initialize API api_key = api_key_input if api_key_input else None fact_checker = GoogleFactCheckAPI(api_key) # Main content st.markdown('
📝 Enter Your Claim
', unsafe_allow_html=True) claim_text = st.text_area("Claim to Verify", height=120, placeholder="Example: COVID-19 vaccines are safe and effective", help="Enter any factual claim you want to verify") col1, col2, col3 = st.columns([2,2,3]) with col1: verify_button = st.button("🔍 Verify Claim", type="primary", use_container_width=True) with col2: clear_button = st.button("đŸ—‘ī¸ Clear", use_container_width=True) if clear_button: st.rerun() # Process verification if verify_button: if not claim_text.strip(): st.markdown("""

âš ī¸ Please enter a claim to verify.

""", unsafe_allow_html=True) else: with st.spinner("🔎 Searching fact-check databases..."): start_time = time.time() results = fact_checker.verify_claim(claim_text) end_time = time.time() st.markdown("---") if results["status"] == "error": st.markdown(f"""

❌ {results['message']}

â„šī¸ Troubleshooting:

""", unsafe_allow_html=True) elif results["status"] == "no_results": st.markdown(f"""

âš ī¸ {results['message']}

â„šī¸ Why no results?

Note: Not all claims have fact-checks available. The database contains claims that have been verified by major fact-checking organizations.

""", unsafe_allow_html=True) elif results["status"] == "success": st.markdown(f"""

✅ Found {results['count']} fact-check(s)

⏱ Time taken: {end_time - start_time:.2f}s

""", unsafe_allow_html=True) st.markdown('
📊 Results
', unsafe_allow_html=True) st.markdown("""

â„šī¸ Important: VeriFact does not perform original fact-checking. It retrieves claims that have already been verified by professional, published fact-checking organizations approved by Google.

""", unsafe_allow_html=True) ratings = [r["rating"] for r in results["results"]] classified = [classify_rating(r) for r in ratings] true_count = classified.count("true") false_count = classified.count("false") mixed_count = classified.count("mixed") col1, col2, col3, col4 = st.columns(4) with col1: st.markdown(f"""

✅ {true_count}

True/Accurate

""", unsafe_allow_html=True) with col2: st.markdown(f"""

❌ {false_count}

False/Incorrect

""", unsafe_allow_html=True) with col3: st.markdown(f"""

âš ī¸ {mixed_count}

Mixed/Misleading

""", unsafe_allow_html=True) with col4: st.markdown(f"""

📰 {results['count']}

Total Sources

""", unsafe_allow_html=True) st.markdown("---") st.markdown('
🔍 Detailed Fact-Check Results
', unsafe_allow_html=True) for idx, result in enumerate(results["results"], 1): verdict_class = determine_verdict_color(result["rating"]) emoji = get_verdict_emoji(result["rating"]) st.markdown(f"""

{emoji} Fact-Check #{idx}

Rating: {result["rating"]}

Publisher: {result["publisher"]}

Claim: {result["claim_text"]}

Claimant: {result["claimant"]}

Date: {result["claim_date"]}

Title: {result["title"]}

Source: View Full Fact-Check →

""", unsafe_allow_html=True) # Export Report st.markdown("---") st.markdown('
📄 Report
', unsafe_allow_html=True) export_text = f"VERIFACT - CLAIM VERIFICATION REPORT\nGenerated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\nCLAIM: {claim_text}\n\nSUMMARY:\n- Total Fact-Checks: {results['count']}\n- True/Accurate: {true_count}\n- False/Incorrect: {false_count}\n- Mixed/Misleading: {mixed_count}\n\nDETAILED RESULTS:\n" for idx, result in enumerate(results["results"],1): export_text += f"\n{idx}. {result['publisher']}\n Rating: {result['rating']}\n URL: {result['url']}\n Claimant: {result['claimant']}\n Date: {result['claim_date']}\n Title: {result['title']}\n" st.download_button( label="📄 Download Report", data=export_text, file_name=f"verifact_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt", mime="text/plain", use_container_width=True ) # Footer st.markdown("---") st.markdown("""

VeriFact | Powered by Google Fact Check Tools API

Built by Areeba Fatima with Python & Streamlit

âš ī¸ Always verify important claims from multiple sources. This tool is for informational purposes.

""", unsafe_allow_html=True) if __name__ == "__main__": main()