import streamlit as st import requests import pandas as pd import json # To pretty-print JSON for debugging # --- Configuration --- st.set_page_config(page_title="Info Hub by Anand", layout="wide") # App Title st.title("🌐 Info Hub by Anand") st.caption("Bing News Search & Twitter Trends") # --- API Setup --- # Fetch API Keys from Hugging Face secrets try: # IMPORTANT: Use distinct names for your secrets BING_API_KEY = st.secrets["RAPIDAPI_KEY_BING"] TWITTER_API_KEY = st.secrets["RAPIDAPI_KEY_TWITTER"] except KeyError as e: st.error(f"🔴 Error: Missing API Key in Hugging Face secrets: {e}. Please add RAPIDAPI_KEY_BING and RAPIDAPI_KEY_TWITTER.") st.stop() # --- API Constants --- BING_API_HOST = "bing-news-search1.p.rapidapi.com" BING_BASE_URL = f"https://{BING_API_HOST}" TWITTER_API_HOST = "twitter241.p.rapidapi.com" TWITTER_BASE_URL = f"https://{TWITTER_API_HOST}" # --- Helper Function to Fetch API Data --- # Cache data to avoid hitting API limits too quickly on reruns # TTL (time-to-live) set to 300 seconds (5 minutes) @st.cache_data(ttl=300) def fetch_api_data(url, headers, params=None): """Fetches data from a RapidAPI endpoint.""" try: response = requests.get(url, headers=headers, params=params, timeout=15) response.raise_for_status() # Raises HTTPError for bad responses (4XX, 5XX) return response.json() except requests.exceptions.Timeout: st.error(f"🚨 API Error: Request timed out accessing {url}") return None except requests.exceptions.HTTPError as e: # Try to get more info from response if possible error_detail = "" try: error_detail = e.response.json() except json.JSONDecodeError: error_detail = e.response.text st.error(f"🚨 API HTTP Error fetching {url}: {e}. Response: {error_detail}") return None except requests.exceptions.RequestException as e: st.error(f"🚨 API Connection Error fetching {url}: {e}") return None except json.JSONDecodeError: st.error(f"🚨 API Error: Could not decode JSON response from {url}") # st.text(response.text) # Uncomment to see raw response text if needed return None except Exception as e: st.error(f"🚨 An unexpected error occurred: {e}") return None # --- UI Sections --- tab1, tab2 = st.tabs(["📰 Bing News Search", "📈 Twitter Trends"]) # --- Bing News Tab --- with tab1: st.header("Search Bing News") # Input for search query search_query = st.text_input("Enter search term (e.g., 'cricket world cup', 'AI technology'):", key="bing_query") col1, col2 = st.columns([1, 3]) with col1: freshness = st.selectbox("Freshness:", ["Day", "Week", "Month"], index=0, key="bing_freshness") with col2: safe_search = st.selectbox("Safe Search:", ["Off", "Moderate", "Strict"], index=0, key="bing_safe") if st.button("Search News", key="bing_search_button"): if not search_query: st.warning("Please enter a search term.") else: st.write(f"Searching for '{search_query}'...") bing_headers = { "X-BingApis-SDK": "true", # As seen in your cURL example "X-RapidAPI-Key": BING_API_KEY, "X-RapidAPI-Host": BING_API_HOST } bing_params = { "q": search_query, "freshness": freshness, "textFormat": "Raw", "safeSearch": safe_search, "mkt": "en-US" # Market - adjust if needed } endpoint = "/news/search" news_data = fetch_api_data(f"{BING_BASE_URL}{endpoint}", headers=bing_headers, params=bing_params) if news_data: # --- Display Results (Adapt based on actual API response structure) --- st.success("News Results:") # COMMON BING NEWS STRUCTURE: results are often in a 'value' list articles = news_data.get("value", []) if not articles: st.info("No news articles found for this query.") # st.json(news_data) # Uncomment to debug the response structure for i, item in enumerate(articles): title = item.get("name", "No Title") description = item.get("description", "No Description") url = item.get("url", "#") provider_list = item.get("provider", [{}]) provider_name = provider_list[0].get("name", "Unknown Source") if provider_list else "Unknown Source" date_published = item.get("datePublished", "N/A") # Format date nicely if possible try: date_published_dt = pd.to_datetime(date_published) date_published_str = date_published_dt.strftime('%Y-%m-%d %H:%M') except: date_published_str = date_published # Keep original if parsing fails st.subheader(f"{i+1}. {title}") st.caption(f"Source: {provider_name} | Published: {date_published_str}") st.write(description) st.markdown(f"[Read More]({url})", unsafe_allow_html=True) st.divider() # Option to show raw JSON for debugging # with st.expander("Show Raw API Response (Debug)"): # st.json(news_data) else: st.error("Failed to retrieve news data from the API.") # --- Twitter Trends Tab --- with tab2: st.header("Get Twitter Trends by Location") # Input for WOEID # You can find WOEIDs here: http://www.woeidlookup.com/ (or other online sources) # Example: 1 = Global, 23424848 = India, 23424977 = United States, 23424975 = United Kingdom woeid = st.text_input("Enter WOEID (Where On Earth ID):", value="23424848", key="twitter_woeid", help="e.g., 1 (Global), 23424848 (India), 23424977 (USA)") if st.button("Fetch Trends", key="twitter_fetch_button"): if not woeid or not woeid.isdigit(): st.warning("Please enter a valid numeric WOEID.") else: st.write(f"Fetching trends for WOEID: {woeid}...") twitter_headers = { "X-RapidAPI-Key": TWITTER_API_KEY, "X-RapidAPI-Host": TWITTER_API_HOST } # Note: The specific endpoint name might vary. Check RapidAPI docs. # '/trends-by-location' was in your example endpoint = "/trends-by-location" twitter_params = {"woeid": woeid} trends_data = fetch_api_data(f"{TWITTER_BASE_URL}{endpoint}", headers=twitter_headers, params=twitter_params) if trends_data: # --- Display Results (Adapt based on actual API response structure) --- st.success("Trending Topics:") # COMMON TWITTER TRENDS STRUCTURE: Often a list, potentially nested under a key. # Check API docs: Is it trends_data[0]['trends']? Or just trends_data['trends']? Or just trends_data? # Assuming structure: [{"trends": [ { "name": "...", "url": "...", "tweet_volume": ... } ]}] # Or maybe: {"results": [ { "name": "...", "url": "...", "tweet_volume": ... } ]} # Let's try a few common possibilities: trends_list = [] if isinstance(trends_data, list) and len(trends_data) > 0 and "trends" in trends_data[0]: trends_list = trends_data[0].get("trends", []) elif isinstance(trends_data, dict) and "trends" in trends_data: trends_list = trends_data.get("trends", []) elif isinstance(trends_data, dict) and "results" in trends_data: trends_list = trends_data.get("results", []) elif isinstance(trends_data, list): # Maybe the list itself contains trend objects trends_list = trends_data if not trends_list: st.info("No trends found or could not parse the response structure.") st.json(trends_data) # Show raw response for debugging else: # Prepare data for table/list display display_data = [] for trend in trends_list: # Check if 'trend' is actually a dictionary if isinstance(trend, dict): name = trend.get("name", "N/A") url = trend.get("url", "#") volume = trend.get("tweet_volume", None) # May be None or not present display_data.append({ "Trend": name, "Tweet Volume": f"{volume:,}" if volume else "N/A", # Format volume nicely "Link": url }) else: st.warning(f"Unexpected item in trends list: {trend}") if display_data: df_trends = pd.DataFrame(display_data) st.dataframe(df_trends, hide_index=True, use_container_width=True, column_config={"Link": st.column_config.LinkColumn("Link", display_text="Go to Twitter")}) else: st.info("Formatted trend data is empty.") # Option to show raw JSON for debugging # with st.expander("Show Raw API Response (Debug)"): # st.json(trends_data) else: st.error("Failed to retrieve trends data from the API.") # --- Footer --- st.markdown("---") st.caption("Info Hub by Anand | Data powered by RapidAPI (Bing News, Twitter)")