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('', 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:
- Enter any claim
- Click Verify
- Review results
- Check sources
""", 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"""
âšī¸ Troubleshooting:
- Make sure you added
GOOGLE_API_KEY in Settings â Repository secrets
- Verify the API key is correct
- Ensure Fact Check Tools API is enabled in Google Cloud Console
""", unsafe_allow_html=True)
elif results["status"] == "no_results":
st.markdown(f"""
â ī¸ {results['message']}
âšī¸ Why no results?
- This specific claim may not have been fact-checked yet
- Try rephrasing (simpler is better)
- Try well-known claims like "The Earth is flat"
- Check spelling
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()