""" Comprehensive API Functions for Hermes Agent These are the executable tools that the Hermes LLM can call. Each function has a clear docstring so the LLM knows when and how to use it. """ import requests import json import os import random from typing import Dict, List, Optional, Any, Union from datetime import datetime, timedelta # ============================================================ # UTILITY FUNCTIONS # ============================================================ def _safe_get(url: str, params: dict = None, headers: dict = None, timeout: int = 15) -> dict: """Safely make a GET request and return JSON""" try: response = requests.get(url, params=params, headers=headers, timeout=timeout) response.raise_for_status() return response.json() if response.text else {"status": "success", "data": ""} except Exception as e: return {"error": str(e), "url": url} def _safe_post(url: str, data: dict = None, json_data: dict = None, headers: dict = None, timeout: int = 15) -> dict: """Safely make a POST request and return JSON""" try: response = requests.post(url, data=data, json=json_data, headers=headers, timeout=timeout) response.raise_for_status() return response.json() if response.text else {"status": "success"} except Exception as e: return {"error": str(e), "url": url} # ============================================================ # ANIMALS # ============================================================ def get_random_dog(breed: str = None) -> dict: """ Get a random dog image. Optionally specify a breed. Args: breed: Dog breed (e.g., 'labrador', 'bulldog', 'beagle'). Leave empty for random. Returns: dict: Contains message (image URL) and status """ if breed: url = f"https://dog.ceo/api/breed/{breed}/images/random" else: url = "https://dog.ceo/api/breeds/image/random" result = _safe_get(url) if "message" in result and result.get("status") == "success": return {"image_url": result["message"], "breed": breed or "random"} return result def get_dog_breeds() -> dict: """Get a list of all available dog breeds""" return _safe_get("https://dog.ceo/api/breeds/list/all") def get_random_cat(fact_only: bool = False) -> dict: """ Get a random cat fact or image Args: fact_only: If True, returns only a fact. If False, returns both fact and image. """ fact_result = _safe_get("https://catfact.ninja/fact") if fact_only: return fact_result # Also get a random cat image image_result = _safe_get("https://api.thecatapi.com/v1/images/search") return { "fact": fact_result.get("fact", ""), "fact_length": fact_result.get("length", 0), "image": image_result[0] if isinstance(image_result, list) and image_result else None } def get_random_fox() -> dict: """Get a random fox image URL""" result = _safe_get("https://randomfox.ca/floof") return result def get_random_duck() -> dict: """Get a random duck image and message""" return _safe_get("https://random-d.uk/api/random") def get_shibe(count: int = 1) -> dict: """ Get random Shiba Inu images Args: count: Number of images (1-100) """ result = _safe_get(f"https://shibe.online/api/shibes?count={min(count, 100)}") return {"images": result if isinstance(result, list) else []} # ============================================================ # ANIME # ============================================================ def search_anime(query: str, limit: int = 5) -> dict: """ Search for anime by title Args: query: Anime title or keyword limit: Maximum number of results Returns: dict: List of matching anime with details """ result = _safe_get(f"https://api.jikan.moe/v4/anime?q={query}&limit={limit}") if "data" in result: animes = [] for anime in result["data"]: animes.append({ "title": anime.get("title", ""), "title_english": anime.get("title_english", ""), "episodes": anime.get("episodes"), "score": anime.get("score"), "status": anime.get("status"), "synopsis": anime.get("synopsis", "")[:200] if anime.get("synopsis") else "", "url": anime.get("url", ""), "image": anime.get("images", {}).get("jpg", {}).get("image_url", "") }) return {"results": animes, "count": len(animes)} return result def get_anime_details(anime_id: int) -> dict: """ Get detailed information about a specific anime Args: anime_id: The MyAnimeList ID of the anime """ return _safe_get(f"https://api.jikan.moe/v4/anime/{anime_id}/full") def get_top_anime(limit: int = 10) -> dict: """ Get top ranked anime Args: limit: Number of results (max 25) """ result = _safe_get(f"https://api.jikan.moe/v4/top/anime?limit={min(limit, 25)}") if "data" in result: return {"top_anime": [{"rank": a.get("rank"), "title": a.get("title"), "score": a.get("score"), "episodes": a.get("episodes")} for a in result["data"]]} return result def get_current_season_anime() -> dict: """Get anime currently airing this season""" return _safe_get("https://api.jikan.moe/v4/seasons/now") def get_random_anime_quote() -> dict: """Get a random anime quote""" # Try waifu.pics for sfw anime image result = _safe_get("https://api.waifu.pics/sfw/waifu") return result # ============================================================ # ART & CULTURE # ============================================================ def search_artworks(query: str = "", limit: int = 5) -> dict: """ Search artworks from the Art Institute of Chicago Args: query: Search term (artist, title, etc.) limit: Number of results """ if query: url = f"https://api.artic.edu/api/v1/artworks/search?q={query}&limit={limit}&fields=id,title,artist_display,date_display,medium_display,image_id" else: url = f"https://api.artic.edu/api/v1/artworks?limit={limit}&fields=id,title,artist_display,date_display,medium_display,image_id" result = _safe_get(url) if "data" in result: iiif_url = result.get("config", {}).get("iiif_url", "https://www.artic.edu/iiif/2") artworks = [] for art in result["data"]: img_url = f"{iiif_url}/{art.get('image_id')}/full/400,/0/default.jpg" if art.get("image_id") else None artworks.append({ "title": art.get("title", ""), "artist": art.get("artist_display", ""), "date": art.get("date_display", ""), "medium": art.get("medium_display", ""), "image_url": img_url }) return {"artworks": artworks, "total": result.get("pagination", {}).get("total", 0)} return result def search_met_museum(query: str) -> dict: """ Search the Metropolitan Museum of Art collection Args: query: Search term """ search_result = _safe_get(f"https://collectionapi.metmuseum.org/public/collection/v1/search?q={query}&hasImages=true") if "objectIDs" in search_result and search_result["objectIDs"]: objects = [] for obj_id in search_result["objectIDs"][:5]: obj = _safe_get(f"https://collectionapi.metmuseum.org/public/collection/v1/objects/{obj_id}") if "objectID" in obj: objects.append({ "title": obj.get("title", ""), "artist": obj.get("artistDisplayName", ""), "department": obj.get("department", ""), "date": obj.get("objectDate", ""), "medium": obj.get("medium", ""), "image": obj.get("primaryImageSmall", "") }) return {"objects": objects, "total_results": search_result.get("total", 0)} return search_result # ============================================================ # BOOKS # ============================================================ def search_books(query: str, limit: int = 5) -> dict: """ Search books on Open Library Args: query: Book title, author, or ISBN limit: Maximum results """ result = _safe_get(f"https://openlibrary.org/search.json?q={query}&limit={limit}") if "docs" in result: books = [] for book in result["docs"][:limit]: books.append({ "title": book.get("title", ""), "author": book.get("author_name", ["Unknown"])[0] if book.get("author_name") else "Unknown", "first_publish": book.get("first_publish_year"), "isbn": book.get("isbn", [""])[0] if book.get("isbn") else "", "pages": book.get("number_of_pages_median"), "subjects": book.get("subject", [])[:3], "cover_id": book.get("cover_i"), "openlibrary_key": book.get("key", "") }) return {"books": books, "total_found": result.get("numFound", 0)} return result def get_book_by_isbn(isbn: str) -> dict: """ Get book details by ISBN Args: isbn: ISBN-10 or ISBN-13 """ return _safe_get(f"https://openlibrary.org/isbn/{isbn}.json") def get_random_quote(category: str = None) -> dict: """ Get a random quote Args: category: Optional category like 'life', 'love', 'inspirational', 'science' """ if category: result = _safe_get(f"https://api.quotable.io/quotes/random?tags={category}") else: result = _safe_get("https://api.quotable.io/quotes/random") if isinstance(result, list) and result: q = result[0] return { "quote": q.get("content", ""), "author": q.get("author", ""), "tags": q.get("tags", []) } return result def get_quote_of_the_day() -> dict: """Get today's inspirational quote""" result = _safe_get("https://zenquotes.io/api/today") if isinstance(result, list) and result: q = result[0] return {"quote": q.get("q", ""), "author": q.get("a", "")} return result def get_random_gutenberg_book(limit: int = 5) -> dict: """ Get random books from Project Gutenberg Args: limit: Number of books """ result = _safe_get(f"https://gutendex.com/books/?page={random.randint(1, 100)}") if "results" in result: books = [] for book in result["results"][:limit]: books.append({ "title": book.get("title", ""), "authors": [a.get("name") for a in book.get("authors", [])], "languages": book.get("languages", []), "download_count": book.get("download_count"), "subjects": book.get("subjects", [])[:3], "formats": {k: v for k, v in list(book.get("formats", {}).items())[:2]} }) return {"books": books} return result # ============================================================ # CRYPTO # ============================================================ def get_crypto_price(coin_id: str = "bitcoin", currency: str = "usd") -> dict: """ Get cryptocurrency price and market data Args: coin_id: Coin ID (bitcoin, ethereum, cardano, solana, etc.) currency: Price currency (usd, eur, gbp) """ result = _safe_get(f"https://api.coingecko.com/api/v3/coins/{coin_id}?localization=false&tickers=false&market_data=true&community_data=false&developer_data=false") if "market_data" in result: md = result["market_data"] return { "name": result.get("name", ""), "symbol": result.get("symbol", "").upper(), "current_price": md.get("current_price", {}).get(currency.lower()), "market_cap": md.get("market_cap", {}).get(currency.lower()), "24h_high": md.get("high_24h", {}).get(currency.lower()), "24h_low": md.get("low_24h", {}).get(currency.lower()), "24h_change_percent": md.get("price_change_percentage_24h"), "7d_change_percent": md.get("price_change_percentage_7d"), "30d_change_percent": md.get("price_change_percentage_30d"), "circulating_supply": md.get("circulating_supply"), "total_supply": md.get("total_supply"), "max_supply": md.get("max_supply"), "last_updated": md.get("last_updated") } return result def get_trending_crypto() -> dict: """Get top 7 trending cryptocurrencies in the last 24 hours""" result = _safe_get("https://api.coingecko.com/api/v3/search/trending") if "coins" in result: coins = [] for coin in result["coins"]: item = coin.get("item", {}) coins.append({ "rank": coin.get("item", {}).get("market_cap_rank"), "name": item.get("name"), "symbol": item.get("symbol"), "thumb": item.get("thumb"), "price_btc": item.get("price_btc"), "score": item.get("score") }) return {"trending": coins} return result def get_global_crypto_data() -> dict: """Get global cryptocurrency market data""" result = _safe_get("https://api.coingecko.com/api/v3/global") if "data" in result: d = result["data"] return { "active_cryptocurrencies": d.get("active_cryptocurrencies"), "markets": d.get("markets"), "total_market_cap_usd": d.get("total_market_cap", {}).get("usd"), "total_volume_24h_usd": d.get("total_volume", {}).get("usd"), "market_cap_change_24h": d.get("market_cap_change_percentage_24h_usd"), "btc_dominance": d.get("market_cap_percentage", {}).get("btc"), "eth_dominance": d.get("market_cap_percentage", {}).get("eth"), } return result def get_bitcoin_data() -> dict: """Get Bitcoin-specific data including blockchain stats""" result = _safe_get("https://api.blockchain.info/stats") return result def convert_crypto(amount: float, from_currency: str = "bitcoin", to_currency: str = "usd") -> dict: """ Convert cryptocurrency to fiat or another crypto Args: amount: Amount to convert from_currency: Source currency ID to_currency: Target currency ID """ result = _safe_get(f"https://api.coingecko.com/api/v3/simple/price?ids={from_currency}&vs_currencies={to_currency}") if from_currency in result and to_currency in result[from_currency]: rate = result[from_currency][to_currency] converted = amount * rate return { "amount": amount, "from": from_currency, "to": to_currency, "rate": rate, "converted_amount": converted } return result # ============================================================ # CURRENCY # ============================================================ def convert_currency(amount: float, from_currency: str = "USD", to_currency: str = "EUR") -> dict: """ Convert currency using latest exchange rates Args: amount: Amount to convert from_currency: 3-letter currency code (USD, EUR, GBP, etc.) to_currency: 3-letter currency code """ result = _safe_get(f"https://api.exchangerate-api.com/v4/latest/{from_currency.upper()}") if "rates" in result and to_currency.upper() in result["rates"]: rate = result["rates"][to_currency.upper()] converted = amount * rate return { "amount": amount, "from": from_currency.upper(), "to": to_currency.upper(), "rate": rate, "converted_amount": round(converted, 4), "date": result.get("date") } return result def get_exchange_rates(base: str = "USD") -> dict: """ Get latest exchange rates for a base currency Args: base: Base currency code (USD, EUR, etc.) """ return _safe_get(f"https://api.exchangerate-api.com/v4/latest/{base.upper()}") # ============================================================ # DEVELOPMENT & TECH # ============================================================ def github_search(query: str, type: str = "repositories", limit: int = 5) -> dict: """ Search GitHub for repositories, users, or code Args: query: Search query type: 'repositories', 'users', or 'code' limit: Number of results """ headers = {"Accept": "application/vnd.github.v3+json"} result = _safe_get(f"https://api.github.com/search/{type}?q={query}&per_page={limit}", headers=headers) if "items" in result: items = [] for item in result["items"][:limit]: if type == "repositories": items.append({ "name": item.get("full_name"), "description": item.get("description", "")[:100], "stars": item.get("stargazers_count"), "language": item.get("language"), "url": item.get("html_url"), "updated": item.get("updated_at") }) elif type == "users": items.append({ "username": item.get("login"), "url": item.get("html_url"), "avatar": item.get("avatar_url"), "type": item.get("type") }) return {"results": items, "total_count": result.get("total_count", 0)} return result def get_github_user(username: str) -> dict: """ Get GitHub user profile information Args: username: GitHub username """ return _safe_get(f"https://api.github.com/users/{username}") def get_stackoverflow_questions(tag: str = None, limit: int = 5) -> dict: """ Get recent Stack Overflow questions Args: tag: Filter by tag (e.g., 'python', 'javascript') limit: Number of questions """ url = f"https://api.stackexchange.com/2.3/questions?order=desc&sort=votes&pagesize={limit}&site=stackoverflow" if tag: url += f"&tagged={tag}" result = _safe_get(url) if "items" in result: questions = [] for q in result["items"]: questions.append({ "title": q.get("title"), "link": q.get("link"), "score": q.get("score"), "answer_count": q.get("answer_count"), "view_count": q.get("view_count"), "tags": q.get("tags", []), "asked": datetime.fromtimestamp(q.get("creation_date", 0)).isoformat() if q.get("creation_date") else "" }) return {"questions": questions} return result # ============================================================ # DICTIONARY # ============================================================ def define_word(word: str) -> dict: """ Get the definition of a word Args: word: The word to define """ result = _safe_get(f"https://api.dictionaryapi.dev/api/v2/entries/en/{word.lower()}") if isinstance(result, list) and result: entry = result[0] meanings = [] for meaning in entry.get("meanings", []): definitions = [d.get("definition", "") for d in meaning.get("definitions", [])[:3]] meanings.append({ "part_of_speech": meaning.get("partOfSpeech"), "definitions": definitions }) phonetics = entry.get("phonetics", []) audio = next((p.get("audio") for p in phonetics if p.get("audio")), None) return { "word": entry.get("word"), "phonetic": entry.get("phonetic"), "audio": audio, "meanings": meanings, "origin": entry.get("etymology", [""])[0] if entry.get("etymology") else "" } return {"word": word, "error": "Word not found", "suggestions": result} # ============================================================ # ENVIRONMENT & WEATHER # ============================================================ def get_weather_forecast(latitude: float = 51.5074, longitude: float = -0.1278, days: int = 7) -> dict: """ Get weather forecast for any location worldwide using Open-Meteo (no API key needed) Args: latitude: Location latitude longitude: Location longitude days: Number of forecast days (1-16) """ url = (f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}" f"&daily=temperature_2m_max,temperature_2m_min,precipitation_sum,windspeed_10m_max," f"sunrise,sunset,weathercode&hourly=temperature_2m,relativehumidity_2m,apparent_temperature," f"precipitation,weathercode,cloudcover,windspeed_10m" f"&timezone=auto&forecast_days={min(days, 16)}") result = _safe_get(url) if "daily" in result: daily = result["daily"] forecast = [] for i in range(len(daily.get("time", []))): forecast.append({ "date": daily["time"][i], "temp_max_c": daily["temperature_2m_max"][i], "temp_min_c": daily["temperature_2m_min"][i], "precipitation_mm": daily["precipitation_sum"][i], "wind_max_kmh": daily.get("windspeed_10m_max", [None]*len(daily["time"]))[i], "sunrise": daily.get("sunrise", [None]*len(daily["time"]))[i] if daily.get("sunrise") else None, "sunset": daily.get("sunset", [None]*len(daily["time"]))[i] if daily.get("sunset") else None, }) current = result.get("hourly", {}) current_idx = 0 if current.get("time") else None return { "location": { "latitude": result.get("latitude"), "longitude": result.get("longitude"), "timezone": result.get("timezone"), "elevation": result.get("elevation") }, "current_hour": { "temperature_c": current.get("temperature_2m", [None])[current_idx] if current_idx is not None else None, "humidity_percent": current.get("relativehumidity_2m", [None])[current_idx] if current_idx is not None else None, "feels_like_c": current.get("apparent_temperature", [None])[current_idx] if current_idx is not None else None, } if current_idx is not None else None, "daily_forecast": forecast, "weather_codes": { 0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast", 45: "Fog", 48: "Depositing rime fog", 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle", 61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", 71: "Slight snow", 73: "Moderate snow", 75: "Heavy snow", 77: "Snow grains", 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers", 95: "Thunderstorm", 96: "Thunderstorm with hail" } } return result def get_weather_by_city(city: str) -> dict: """ Get weather for a city by name (uses geocoding first) Args: city: City name (e.g., 'London', 'New York', 'Tokyo') """ # First geocode the city geo = _safe_get(f"https://geocoding-api.open-meteo.com/v1/search?name={city}&count=1&language=en&format=json") if "results" in geo and geo["results"]: location = geo["results"][0] lat = location.get("latitude") lon = location.get("longitude") name = location.get("name") country = location.get("country") weather = get_weather_forecast(lat, lon, days=7) weather["searched_city"] = city weather["resolved_location"] = f"{name}, {country}" return weather return {"error": f"Could not find city: {city}"} def get_air_quality(latitude: float = 51.5074, longitude: float = -0.1278) -> dict: """ Get current air quality data Args: latitude: Location latitude longitude: Location longitude """ url = (f"https://air-quality-api.open-meteo.com/v1/air-quality?latitude={latitude}&longitude={longitude}" f"¤t=us_aqi,pm10,pm2_5,carbon_monoxide,nitrogen_dioxide,ozone" f"&timezone=auto") result = _safe_get(url) if "current" in result: c = result["current"] aqi = c.get("us_aqi") quality = "Good" if aqi <= 50 else "Moderate" if aqi <= 100 else "Unhealthy for Sensitive" if aqi <= 150 else "Unhealthy" if aqi <= 200 else "Very Unhealthy" if aqi <= 300 else "Hazardous" return { "aqi_us": aqi, "quality": quality, "pm2_5": c.get("pm2_5"), "pm10": c.get("pm10"), "co": c.get("carbon_monoxide"), "no2": c.get("nitrogen_dioxide"), "o3": c.get("ozone"), "time": c.get("time") } return result def get_iss_location() -> dict: """Get current location of the International Space Station""" result = _safe_get("http://api.open-notify.org/iss-now.json") if "iss_position" in result: lat = float(result["iss_position"].get("latitude", 0)) lon = float(result["iss_position"].get("longitude", 0)) # Get geographic location name geo = _safe_get(f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json") return { "latitude": lat, "longitude": lon, "timestamp": result.get("timestamp"), "over_location": geo.get("display_name", "Unknown location (likely over ocean)"), "timezone": geo.get("address", {}).get("country", "Unknown") } return result def get_astronauts_in_space() -> dict: """Get list of astronauts currently in space""" return _safe_get("http://api.open-notify.org/astros.json") def get_nasa_apod() -> dict: """ Get NASA's Astronomy Picture of the Day """ # Try without API key first (rate limited) result = _safe_get("https://api.nasa.gov/planetary/apod") if "code" in result and result.get("code") == 403: return {"error": "NASA API key required for repeated use", "info": "Visit https://api.nasa.gov to get a free API key"} return result def get_space_x_launches(limit: int = 5, upcoming: bool = False) -> dict: """ Get SpaceX launch data Args: limit: Number of launches upcoming: If True, show upcoming launches. If False, show past launches. """ if upcoming: result = _safe_get("https://api.spacexdata.com/v4/launches/upcoming") else: result = _safe_get(f"https://api.spacexdata.com/v4/launches/past?limit={limit}&order=desc") if isinstance(result, list): launches = [] for launch in result[:limit]: launches.append({ "name": launch.get("name"), "date": launch.get("date_utc"), "success": launch.get("success"), "details": (launch.get("details") or "")[:200], "rocket": launch.get("rocket"), "launchpad": launch.get("launchpad"), "links": { "webcast": launch.get("links", {}).get("webcast"), "article": launch.get("links", {}).get("article"), "wikipedia": launch.get("links", {}).get("wikipedia") } }) return {"launches": launches} return result # ============================================================ # FOOD & DRINK # ============================================================ def search_meal(name: str = None, ingredient: str = None, category: str = None) -> dict: """ Search for meal recipes Args: name: Meal name to search for ingredient: Filter by main ingredient category: Category like 'Seafood', 'Vegetarian', 'Dessert', 'Chicken' """ if name: result = _safe_get(f"https://www.themealdb.com/api/json/v1/1/search.php?s={name}") elif ingredient: result = _safe_get(f"https://www.themealdb.com/api/json/v1/1/filter.php?i={ingredient}") elif category: result = _safe_get(f"https://www.themealdb.com/api/json/v1/1/filter.php?c={category}") else: result = _safe_get("https://www.themealdb.com/api/json/v1/1/random.php") meals = result.get("meals", []) if meals and name: # Full details available m = meals[0] ingredients = [] for i in range(1, 21): ing = m.get(f"strIngredient{i}") measure = m.get(f"strMeasure{i}") if ing and ing.strip(): ingredients.append(f"{measure} {ing}".strip()) return { "name": m.get("strMeal"), "category": m.get("strCategory"), "area": m.get("strArea"), "instructions": m.get("strInstructions", "")[:500], "image": m.get("strMealThumb"), "youtube": m.get("strYoutube"), "source": m.get("strSource"), "ingredients": ingredients, } elif meals: return {"meals": [{"name": m.get("strMeal"), "id": m.get("idMeal"), "image": m.get("strMealThumb")} for m in meals[:10]]} return result def get_random_meal() -> dict: """Get a random meal recipe with full details""" return search_meal() def search_cocktail(name: str = None, ingredient: str = None) -> dict: """ Search cocktail recipes Args: name: Cocktail name ingredient: Filter by ingredient (e.g., 'vodka', 'gin', 'rum') """ if name: result = _safe_get(f"https://www.thecocktaildb.com/api/json/v1/1/search.php?s={name}") elif ingredient: result = _safe_get(f"https://www.thecocktaildb.com/api/json/v1/1/filter.php?i={ingredient}") else: result = _safe_get("https://www.thecocktaildb.com/api/json/v1/1/random.php") drinks = result.get("drinks", []) if drinks and name: d = drinks[0] ingredients = [] for i in range(1, 16): ing = d.get(f"strIngredient{i}") measure = d.get(f"strMeasure{i}") if ing and ing.strip(): ingredients.append(f"{measure} {ing}".strip()) return { "name": d.get("strDrink"), "category": d.get("strCategory"), "alcoholic": d.get("strAlcoholic"), "glass": d.get("strGlass"), "instructions": d.get("strInstructions", "")[:500], "image": d.get("strDrinkThumb"), "ingredients": ingredients, } elif drinks: return {"drinks": [{"name": d.get("strDrink"), "id": d.get("idDrink"), "image": d.get("strDrinkThumb")} for d in drinks[:10]]} return result def get_random_cocktail() -> dict: """Get a random cocktail recipe""" return search_cocktail() def get_random_coffee_image() -> dict: """Get a random coffee image""" return _safe_get("https://coffee.alexflipnote.dev/random.json") # ============================================================ # GAMES # ============================================================ def get_pokemon(name_or_id: str = "1") -> dict: """ Get Pokemon data by name or Pokedex number Args: name_or_id: Pokemon name (e.g., 'pikachu') or ID number """ result = _safe_get(f"https://pokeapi.co/api/v2/pokemon/{name_or_id.lower()}") if "name" in result: types = [t.get("type", {}).get("name", "") for t in result.get("types", [])] abilities = [a.get("ability", {}).get("name", "") for a in result.get("abilities", [])] stats = {s.get("stat", {}).get("name", ""): s.get("base_stat") for s in result.get("stats", [])} return { "name": result.get("name", "").title(), "id": result.get("id"), "height_dm": result.get("height"), "weight_hg": result.get("weight"), "types": types, "abilities": abilities, "stats": stats, "image_front": result.get("sprites", {}).get("other", {}).get("official-artwork", {}).get("front_default"), "image_back": result.get("sprites", {}).get("back_default"), "base_experience": result.get("base_experience"), } return result def get_random_pokemon() -> dict: """Get a random Pokemon""" random_id = random.randint(1, 1010) return get_pokemon(str(random_id)) def get_pokemon_by_type(pokemon_type: str) -> dict: """ Get Pokemon by type Args: pokemon_type: Pokemon type (e.g., 'fire', 'water', 'grass', 'electric') """ result = _safe_get(f"https://pokeapi.co/api/v2/type/{pokemon_type.lower()}") if "pokemon" in result: pokemon_list = [p.get("pokemon", {}).get("name", "").title() for p in result["pokemon"][:20]] return { "type": pokemon_type.title(), "pokemon_count": len(result["pokemon"]), "examples": pokemon_list } return result def get_dnd_class(class_name: str = None) -> dict: """ Get D&D 5e class information Args: class_name: Class name (barbarian, bard, cleric, druid, fighter, monk, paladin, ranger, rogue, sorcerer, warlock, wizard) """ if class_name: return _safe_get(f"https://www.dnd5eapi.co/api/classes/{class_name.lower()}") return _safe_get("https://www.dnd5eapi.co/api/classes") def get_dnd_monster(name_or_index: str) -> dict: """ Get D&D 5e monster information Args: name_or_index: Monster name or index """ return _safe_get(f"https://www.dnd5eapi.co/api/monsters/{name_or_index.lower()}") def get_dnd_spell(name_or_index: str) -> dict: """ Get D&D 5e spell information Args: name_or_index: Spell name or index """ return _safe_get(f"https://www.dnd5eapi.co/api/spells/{name_or_index.lower()}") def get_chess_profile(username: str) -> dict: """ Get Chess.com player profile Args: username: Chess.com username """ result = _safe_get(f"https://api.chess.com/pub/player/{username.lower()}") if "username" in result: return { "username": result.get("username"), "player_id": result.get("player_id"), "title": result.get("title"), "status": result.get("status"), "name": result.get("name"), "avatar": result.get("avatar"), "location": result.get("location"), "country": result.get("country"), "joined": datetime.fromtimestamp(result.get("joined", 0)).isoformat() if result.get("joined") else None, "last_online": datetime.fromtimestamp(result.get("last_online", 0)).isoformat() if result.get("last_online") else None, "followers": result.get("followers"), "is_streamer": result.get("is_streamer"), } return result def draw_cards(count: int = 1) -> dict: """ Draw cards from a deck Args: count: Number of cards to draw (1-52) """ # First get a deck deck = _safe_get("https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1") if "deck_id" in deck: deck_id = deck["deck_id"] result = _safe_get(f"https://deckofcardsapi.com/api/deck/{deck_id}/draw/?count={min(count, 52)}") if "cards" in result: return { "cards": [{"value": c.get("value"), "suit": c.get("suit"), "image": c.get("image")} for c in result["cards"]], "remaining": result.get("remaining") } return deck # ============================================================ # GEOCODING & LOCATION # ============================================================ def geocode_location(query: str) -> dict: """ Convert address/place name to coordinates Args: query: Address or place name """ result = _safe_get(f"https://nominatim.openstreetmap.org/search?q={query}&format=json&limit=1") if isinstance(result, list) and result: loc = result[0] return { "name": loc.get("display_name"), "latitude": float(loc.get("lat", 0)), "longitude": float(loc.get("lon", 0)), "type": loc.get("type"), "importance": loc.get("importance"), "osm_id": loc.get("osm_id") } return {"error": "Location not found"} def reverse_geocode(latitude: float, longitude: float) -> dict: """ Convert coordinates to address Args: latitude: Latitude longitude: Longitude """ result = _safe_get(f"https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json") if "display_name" in result: return { "address": result.get("display_name"), "address_details": result.get("address"), "type": result.get("type"), "osm_id": result.get("osm_id") } return result def get_my_ip() -> dict: """Get your public IP address and basic geolocation""" return _safe_get("https://ipapi.co/json") # ============================================================ # JOKES & ENTERTAINMENT # ============================================================ def get_joke(category: str = "Any", safe_mode: bool = True) -> dict: """ Get a random joke Args: category: 'Any', 'Programming', 'Misc', 'Dark', 'Pun', 'Spooky', 'Christmas' safe_mode: If True, exclude explicit jokes """ flags = "&blacklistFlags=nsfw,religious,political,racist,sexist,explicit" if safe_mode else "" result = _safe_get(f"https://v2.jokeapi.dev/joke/{category}?type=single{flags}") if "joke" in result: return {"joke": result["joke"], "category": result.get("category"), "safe": result.get("safe")} elif "setup" in result and "delivery" in result: return {"setup": result["setup"], "delivery": result["delivery"], "category": result.get("category")} return result def get_chuck_norris_joke(category: str = None) -> dict: """ Get a Chuck Norris joke Args: category: Optional category (animal, career, celebrity, dev, fashion, food, history, money, movie, music, political, religion, science, sport, travel) """ if category: result = _safe_get(f"https://api.chucknorris.io/jokes/random?category={category}") else: result = _safe_get("https://api.chucknorris.io/jokes/random") if "value" in result: return {"joke": result["value"], "url": result.get("url"), "categories": result.get("categories", [])} return result def get_chuck_norris_categories() -> dict: """Get available Chuck Norris joke categories""" return _safe_get("https://api.chucknorris.io/jokes/categories") def get_random_meme(subreddit: str = None) -> dict: """ Get a random meme Args: subreddit: Specific subreddit (e.g., 'memes', 'dankmemes', 'wholesomememes') """ if subreddit: result = _safe_get(f"https://meme-api.com/gimme/{subreddit}") else: result = _safe_get("https://meme-api.com/gimme") if "url" in result: return { "title": result.get("title"), "url": result.get("url"), "author": result.get("author"), "subreddit": result.get("subreddit"), "ups": result.get("ups"), "permalink": result.get("postLink") } return result def get_rick_and_morty_character(name_or_id: str = "1") -> dict: """ Get Rick and Morty character info Args: name_or_id: Character ID number or name """ if name_or_id.isdigit(): result = _safe_get(f"https://rickandmortyapi.com/api/character/{name_or_id}") else: search = _safe_get(f"https://rickandmortyapi.com/api/character/?name={name_or_id}") if "results" in search and search["results"]: result = search["results"][0] else: return search if "name" in result: return { "name": result.get("name"), "status": result.get("status"), "species": result.get("species"), "type": result.get("type") or "None", "gender": result.get("gender"), "origin": result.get("origin", {}).get("name"), "location": result.get("location", {}).get("name"), "image": result.get("image"), "episodes_appeared": len(result.get("episode", [])) } return result def get_random_activity(participants: int = None, type: str = None) -> dict: """ Get a random activity suggestion Args: participants: Number of participants (1-8) type: Activity type (education, recreational, social, diy, charity, cooking, relaxation, music, busywork) """ params = [] if participants: params.append(f"participants={participants}") if type: params.append(f"type={type}") query = "&" + "&".join(params) if params else "" return _safe_get(f"https://www.boredapi.com/api/activity{query}") def get_useless_fact() -> dict: """Get a random useless but interesting fact""" return _safe_get("https://uselessfacts.jsph.pl/api/v2/facts/random") # ============================================================ # MUSIC # ============================================================ def get_song_lyrics(artist: str, title: str) -> dict: """ Get song lyrics Args: artist: Song artist name title: Song title """ result = _safe_get(f"https://api.lyrics.ovh/v1/{artist}/{title}") if "lyrics" in result: return { "artist": artist, "title": title, "lyrics": result["lyrics"][:1000] if len(result["lyrics"]) > 1000 else result["lyrics"] } return {"error": "Lyrics not found", "artist": artist, "title": title} def search_lastfm(artist: str = None, method: str = "artist.search") -> dict: """ Search Last.fm for music Args: artist: Artist name method: 'artist.search', 'artist.getinfo', 'artist.gettoptracks', 'chart.gettopartists' """ # Using a demo key - users should get their own for production api_key = os.getenv("LASTFM_API_KEY", "demo") if method == "artist.search": url = f"https://ws.audioscrobbler.com/2.0/?method=artist.search&artist={artist}&api_key={api_key}&format=json" elif method == "artist.getinfo": url = f"https://ws.audioscrobbler.com/2.0/?method=artist.getinfo&artist={artist}&api_key={api_key}&format=json" elif method == "artist.gettoptracks": url = f"https://ws.audioscrobbler.com/2.0/?method=artist.gettoptracks&artist={artist}&api_key={api_key}&format=json" elif method == "chart.gettopartists": url = f"https://ws.audioscrobbler.com/2.0/?method=chart.gettopartists&api_key={api_key}&format=json" else: return {"error": f"Unknown method: {method}"} return _safe_get(url) # ============================================================ # NEWS # ============================================================ def get_hacker_news(story_type: str = "top", limit: int = 5) -> dict: """ Get top stories from Hacker News Args: story_type: 'top', 'new', 'best', 'ask', 'show', 'job' limit: Number of stories """ ids = _safe_get(f"https://hacker-news.firebaseio.com/v0/{story_type}stories.json") if isinstance(ids, list): stories = [] for story_id in ids[:limit]: story = _safe_get(f"https://hacker-news.firebaseio.com/v0/item/{story_id}.json") if story: stories.append({ "title": story.get("title"), "url": story.get("url"), "score": story.get("score"), "author": story.get("by"), "comments": story.get("descendants"), "time": datetime.fromtimestamp(story.get("time", 0)).isoformat() if story.get("time") else "" }) return {"stories": stories} return ids def get_space_news(limit: int = 5) -> dict: """ Get latest space-related news Args: limit: Number of articles """ result = _safe_get(f"https://api.spaceflightnewsapi.net/v4/articles/?limit={limit}") if "results" in result: articles = [] for article in result["results"]: articles.append({ "title": article.get("title"), "summary": article.get("summary", "")[:200] if article.get("summary") else "", "url": article.get("url"), "published": article.get("published_at"), "source": article.get("news_site"), "image": article.get("image_url") }) return {"articles": articles, "count": len(articles)} return result # ============================================================ # QUOTES # ============================================================ def get_breaking_bad_quote(count: int = 1) -> dict: """ Get quotes from Breaking Bad Args: count: Number of quotes """ result = _safe_get(f"https://api.breakingbadquotes.xyz/v1/quotes/{count}") if isinstance(result, list): return {"quotes": result} return result def get_game_of_thrones_quote() -> dict: """Get a random Game of Thrones quote""" result = _safe_get("https://api.gameofthronesquotes.xyz/v1/random") if isinstance(result, dict) and "sentence" in result: return { "quote": result.get("sentence"), "character": result.get("character", {}).get("name"), "house": result.get("character", {}).get("house", {}).get("name") } return result def get_zen_quote() -> dict: """Get a random inspirational zen quote""" result = _safe_get("https://zenquotes.io/api/random") if isinstance(result, list) and result: q = result[0] return {"quote": q.get("q"), "author": q.get("a"), "html": q.get("h")} return result def get_ron_swanson_quote(count: int = 1) -> dict: """ Get Ron Swanson quotes Args: count: Number of quotes (1-50) """ result = _safe_get(f"https://ron-swanson-quotes.herokuapp.com/v2/quotes/{count}") if isinstance(result, list): return {"quotes": result} return result # ============================================================ # SPORTS # ============================================================ def get_football_standings(league_code: str = "PL") -> dict: """ Get football league standings Args: league_code: League code (PL=Premier League, BL1=Bundesliga, SA=Serie A, PD=La Liga, FL1=Ligue 1, CL=Champions League) """ # Using football-data.org demo - users need their own API key for production api_key = os.getenv("FOOTBALL_DATA_KEY", "") headers = {"X-Auth-Token": api_key} if api_key else {} result = _safe_get(f"https://api.football-data.org/v4/competitions/{league_code}/standings", headers=headers) if "standings" in result: table = result["standings"][0].get("table", []) standings = [] for team in table[:20]: standings.append({ "position": team.get("position"), "team": team.get("team", {}).get("name"), "played": team.get("playedGames"), "won": team.get("won"), "draw": team.get("draw"), "lost": team.get("lost"), "points": team.get("points"), "goals_for": team.get("goalsFor"), "goals_against": team.get("goalsAgainst"), "goal_difference": team.get("goalDifference"), }) return { "league": result.get("competition", {}).get("name"), "season": result.get("season", {}).get("startDate"), "standings": standings } return result def get_nba_games(date: str = None) -> dict: """ Get NBA games for a specific date or today Args: date: Date in YYYY-MM-DD format. Leave empty for today. """ if not date: date = datetime.now().strftime("%Y-%m-%d") result = _safe_get(f"https://www.balldontlie.io/api/v1/games?dates[]={date}") if "data" in result: games = [] for game in result["data"]: games.append({ "home_team": game.get("home_team", {}).get("full_name"), "away_team": game.get("visitor_team", {}).get("full_name"), "home_score": game.get("home_team_score"), "away_score": game.get("visitor_team_score"), "status": game.get("status"), "time": game.get("time"), "date": game.get("date"), "season": game.get("season"), "period": game.get("period"), }) return {"date": date, "games": games, "count": len(games)} return result def get_f1_results(season: int = None, round_num: int = None) -> dict: """ Get Formula 1 race results Args: season: Year (e.g., 2024). Leave empty for current season. round_num: Race round number. Leave empty for season standings. """ if season and round_num: url = f"https://ergast.com/api/f1/{season}/{round_num}/results.json" elif season: url = f"https://ergast.com/api/f1/{season}/driverStandings.json" else: url = "https://ergast.com/api/f1/current/driverStandings.json" result = _safe_get(url) if "MRData" in result and "StandingsTable" in result["MRData"]: standings_lists = result["MRData"]["StandingsTable"].get("StandingsLists", []) if standings_lists: standings = [] for standing in standings_lists[0].get("DriverStandings", []): driver = standing.get("Driver", {}) constructors = standing.get("Constructors", [{}]) standings.append({ "position": standing.get("position"), "points": standing.get("points"), "wins": standing.get("wins"), "driver": f"{driver.get('givenName', '')} {driver.get('familyName', '')}", "number": driver.get("permanentNumber"), "nationality": driver.get("nationality"), "constructor": constructors[0].get("name") if constructors else "" }) return {"season": season or "current", "standings": standings} if "MRData" in result and "RaceTable" in result["MRData"]: races = result["MRData"]["RaceTable"].get("Races", []) if races: race = races[0] results = [] for r in race.get("Results", []): results.append({ "position": r.get("position"), "driver": f"{r.get('Driver', {}).get('givenName', '')} {r.get('Driver', {}).get('familyName', '')}", "constructor": r.get("Constructor", {}).get("name"), "time": r.get("Time", {}).get("time") if r.get("Time") else r.get("status"), "points": r.get("points"), }) return { "race": race.get("raceName"), "circuit": race.get("Circuit", {}).get("circuitName"), "date": race.get("date"), "results": results } return result def get_city_bikes(city: str = None) -> dict: """ Get bike sharing stations for a city Args: city: City name. Leave empty for list of all networks. """ if city: networks = _safe_get("https://api.citybik.es/v2/networks") if "networks" in networks: for network in networks["networks"]: if city.lower() in network.get("location", {}).get("city", "").lower(): return _safe_get(f"https://api.citybik.es/v2/networks/{network.get('id')}") return {"error": f"No bike sharing found for {city}"} return _safe_get("https://api.citybik.es/v2/networks") # ============================================================ # TEST DATA # ============================================================ def generate_random_user(seed: str = None) -> dict: """ Generate a random fake user profile Args: seed: Optional seed for reproducible results """ url = "https://randomuser.me/api/1.4/" if seed: url += f"?seed={seed}" result = _safe_get(url) if "results" in result and result["results"]: user = result["results"][0] return { "name": f"{user.get('name', {}).get('first', '')} {user.get('name', {}).get('last', '')}", "gender": user.get("gender"), "email": user.get("email"), "phone": user.get("phone"), "cell": user.get("cell"), "location": f"{user.get('location', {}).get('city', '')}, {user.get('location', {}).get('country', '')}", "age": user.get("dob", {}).get("age"), "picture": user.get("picture", {}).get("large"), "username": user.get("login", {}).get("username"), "uuid": user.get("login", {}).get("uuid"), } return result def generate_fake_data(type: str = "users", count: int = 1) -> dict: """ Generate fake data for testing Args: type: Data type - 'users', 'addresses', 'banks', 'beers', 'credit_cards', 'products' count: Number of items (max 100) """ result = _safe_get(f"https://fakerapi.it/api/v2/{type}?_quantity={min(count, 100)}") return result def get_random_user(count: int = 1) -> dict: """ Get random user data Args: count: Number of users (1-5000) """ return _safe_get(f"https://randomuser.me/api/?results={min(count, 100)}") # ============================================================ # TEXT UTILITIES # ============================================================ def translate_text(text: str, target_lang: str = "es", source_lang: str = "en") -> dict: """ Translate text using LibreTranslate (free, no API key needed for small usage) Args: text: Text to translate target_lang: Target language code (es, fr, de, it, pt, ru, ja, zh, ar, hi, etc.) source_lang: Source language code (auto-detected if not specified) """ # Try multiple LibreTranslate instances instances = [ "https://libretranslate.de", "https://libretranslate.com", "https://translate.argosopentech.com", ] for base_url in instances: try: result = _safe_post( f"{base_url}/translate", json_data={ "q": text, "source": source_lang if source_lang != "auto" else "auto", "target": target_lang, "format": "text" }, timeout=10 ) if "translatedText" in result: return { "original": text, "translated": result["translatedText"], "source_language": result.get("detectedLanguage", {}).get("language", source_lang) if "detectedLanguage" in result else source_lang, "target_language": target_lang } except: continue return {"error": "Translation service unavailable", "original": text} def analyze_sentiment(text: str) -> dict: """ Analyze sentiment of text Args: text: Text to analyze """ result = _safe_post("https://text-processing.com/api/sentiment/", data={"text": text}) if "label" in result: return { "sentiment": result.get("label"), "confidence": result.get("probability", {}).get(result.get("label")), "probabilities": result.get("probability", {}) } return result def detect_language(text: str) -> dict: """ Detect the language of text Args: text: Text to analyze """ result = _safe_post("https://libretranslate.de/detect", json_data={"q": text}) if isinstance(result, list) and result: return { "language": result[0].get("language"), "confidence": result[0].get("confidence") } return result # ============================================================ # URL SHORTENING # ============================================================ def shorten_url(long_url: str) -> dict: """ Shorten a URL Args: long_url: The URL to shorten """ result = _safe_post("https://cleanuri.com/api/v1/shorten", data={"url": long_url}) if "result_url" in result: return {"original": long_url, "short_url": result["result_url"]} return result # ============================================================ # HOLIDAYS # ============================================================ def get_public_holidays(country_code: str = "US", year: int = None) -> dict: """ Get public holidays for a country Args: country_code: 2-letter country code (US, GB, CA, AU, DE, FR, JP, etc.) year: Year (default: current year) """ if not year: year = datetime.now().year return _safe_get(f"https://date.nager.at/api/v3/publicholidays/{year}/{country_code.upper()}") def get_country_info(country_code: str) -> dict: """ Get information about a country including holiday info Args: country_code: 2-letter country code """ return _safe_get(f"https://date.nager.at/api/v3/countryinfo/{country_code.upper()}") def get_available_countries() -> dict: """Get list of countries with holiday data available""" return _safe_get("https://date.nager.at/api/v3/availablecountries") # ============================================================ # SCIENCE # ============================================================ def get_element(atomic_number: int = None, symbol: str = None) -> dict: """ Get periodic table element data Args: atomic_number: Atomic number (1-118) symbol: Element symbol (H, He, Li, etc.) """ if symbol: result = _safe_get(f"https://periodic-table-elements-info.herokuapp.com/element/atomicSymbol/{symbol.upper()}") elif atomic_number: result = _safe_get(f"https://periodic-table-elements-info.herokuapp.com/element/atomicNumber/{atomic_number}") else: return {"error": "Please provide atomic_number or symbol"} if "atomicNumber" in result: return { "name": result.get("name"), "symbol": result.get("atomicSymbol"), "atomic_number": result.get("atomicNumber"), "atomic_mass": result.get("atomicMass"), "category": result.get("groupBlock"), "period": result.get("period"), "group": result.get("group"), "electron_configuration": result.get("electronicConfiguration"), "discovered_by": result.get("discoveredBy"), "named_by": result.get("namedBy"), "summary": result.get("summary", "")[:300] if result.get("summary") else "", "density": result.get("density"), "melting_point": result.get("meltingPoint"), "boiling_point": result.get("boilingPoint"), } return result def get_number_fact(number: int = None, type: str = "trivia") -> dict: """ Get an interesting fact about a number Args: number: The number (leave empty for random) type: 'trivia', 'math', 'year', or 'date' """ num = str(number) if number else "random" result = requests.get(f"http://numbersapi.com/{num}/{type}", timeout=10) if result.status_code == 200: return {"number": number or "random", "type": type, "fact": result.text} return {"error": "Could not fetch number fact"} def get_random_number_fact() -> dict: """Get a random interesting number fact""" return get_number_fact() # ============================================================ # CODE/PROGRAMMING # ============================================================ def get_codeforces_user(handle: str) -> dict: """ Get Codeforces user profile Args: handle: Codeforces handle/username """ result = _safe_get(f"https://codeforces.com/api/user.info?handles={handle}") if "result" in result and result["result"]: user = result["result"][0] return { "handle": user.get("handle"), "rating": user.get("rating"), "max_rating": user.get("maxRating"), "rank": user.get("rank"), "max_rank": user.get("maxRank"), "contribution": user.get("contribution"), "friend_count": user.get("friendOfCount"), "registration_time": datetime.fromtimestamp(user.get("registrationTimeSeconds", 0)).isoformat() if user.get("registrationTimeSeconds") else None, "last_online": datetime.fromtimestamp(user.get("lastOnlineTimeSeconds", 0)).isoformat() if user.get("lastOnlineTimeSeconds") else None, } return result def get_codeforces_contests() -> dict: """Get list of Codeforces contests""" return _safe_get("https://codeforces.com/api/contest.list") # ============================================================ # TV & MOVIES # ============================================================ def search_tv_show(query: str) -> dict: """ Search for TV shows Args: query: Show name """ result = _safe_get(f"https://api.tvmaze.com/search/shows?q={query}") if isinstance(result, list): shows = [] for item in result[:5]: show = item.get("show", {}) shows.append({ "name": show.get("name"), "premiered": show.get("premiered"), "status": show.get("status"), "language": show.get("language"), "genres": show.get("genres", []), "summary": (show.get("summary") or "").replace("

", "").replace("

", "").replace("", "").replace("", "")[:200], "rating": show.get("rating", {}).get("average"), "image": show.get("image", {}).get("medium") if show.get("image") else None, "network": show.get("network", {}).get("name") if show.get("network") else show.get("webChannel", {}).get("name") }) return {"shows": shows} return result def get_tv_schedule(country: str = "US", date: str = None) -> dict: """ Get TV schedule for a country Args: country: 2-letter country code date: Date in YYYY-MM-DD format """ if date: url = f"https://api.tvmaze.com/schedule?country={country}&date={date}" else: url = f"https://api.tvmaze.com/schedule?country={country}" return _safe_get(url) # ============================================================ # UNIVERSITIES # ============================================================ def search_universities(name: str = None, country: str = None) -> dict: """ Search for universities Args: name: University name to search country: Filter by country """ if name and country: url = f"http://universities.hipolabs.com/search?name={name}&country={country}" elif name: url = f"http://universities.hipolabs.com/search?name={name}" elif country: url = f"http://universities.hipolabs.com/search?country={country}" else: return {"error": "Please provide name or country"} result = _safe_get(url) if isinstance(result, list): return {"universities": [{"name": u.get("name"), "country": u.get("country"), "domains": u.get("domains"), "websites": u.get("web_pages")} for u in result[:20]], "count": len(result)} return result # ============================================================ # COLORS # ============================================================ def get_random_color() -> dict: """Get a random color with details""" result = _safe_get("https://www.thecolorapi.com/random") if "name" in result: return { "hex": result.get("hex", {}).get("value"), "rgb": result.get("rgb", {}).get("value"), "hsl": result.get("hsl", {}).get("value"), "hsv": result.get("hsv", {}).get("value"), "cmyk": result.get("cmyk", {}).get("value"), "name": result.get("name", {}).get("value"), "contrast": result.get("contrast", {}).get("value"), "image": result.get("image", {}).get("bare") } return result def get_color_by_hex(hex_code: str) -> dict: """ Get color details by hex code Args: hex_code: Hex color code (with or without #) """ hex_clean = hex_code.lstrip("#") result = _safe_get(f"https://www.thecolorapi.com/id?hex={hex_clean}") return result # ============================================================ # QR CODE # ============================================================ def generate_qr_code(data: str, size: int = 200) -> dict: """ Generate a QR code for any text/URL Args: data: Text or URL to encode size: Image size in pixels """ encoded_data = requests.utils.quote(data) url = f"https://api.qrserver.com/v1/create-qr-code/?size={size}x{size}&data={encoded_data}" return { "qr_code_url": url, "data": data, "size": size } # ============================================================ # FACTS & TRIVIA # ============================================================ def get_cat_fact() -> dict: """Get a random cat fact""" return _safe_get("https://catfact.ninja/fact") def get_dog_fact() -> dict: """Get a random dog fact""" result = _safe_get("https://dog-api.kinduff.com/api/facts") if "facts" in result: return {"fact": result["facts"][0] if result["facts"] else ""} return result def get_trivia_question(amount: int = 1, category: int = None, difficulty: str = None, type: str = None) -> dict: """ Get trivia questions Args: amount: Number of questions (1-50) category: Category ID (9=General Knowledge, 10=Books, 11=Film, 12=Music, 14=TV, 15=Video Games, 17=Science, 18=Computers, 21=Sports, 22=Geography, 23=History, 27=Animals) difficulty: 'easy', 'medium', 'hard' type: 'multiple' or 'boolean' """ params = [f"amount={min(amount, 50)}"] if category: params.append(f"category={category}") if difficulty: params.append(f"difficulty={difficulty}") if type: params.append(f"type={type}") url = "https://opentdb.com/api.php?" + "&".join(params) result = _safe_get(url) if "results" in result: questions = [] for q in result["results"]: questions.append({ "category": q.get("category"), "type": q.get("type"), "difficulty": q.get("difficulty"), "question": q.get("question"), "correct_answer": q.get("correct_answer"), "incorrect_answers": q.get("incorrect_answers", []) }) return {"questions": questions, "response_code": result.get("response_code")} return result # ============================================================ # REDDIT # ============================================================ def get_reddit_posts(subreddit: str = "all", limit: int = 5, sort: str = "hot") -> dict: """ Get posts from a subreddit Args: subreddit: Subreddit name limit: Number of posts (max 25) sort: 'hot', 'new', 'top', 'rising' """ headers = {"User-Agent": "HermesAgent/1.0"} result = _safe_get(f"https://www.reddit.com/r/{subreddit}/{sort}.json?limit={min(limit, 25)}", headers=headers) if "data" in result and "children" in result["data"]: posts = [] for post in result["data"]["children"]: p = post.get("data", {}) posts.append({ "title": p.get("title"), "author": p.get("author"), "subreddit": p.get("subreddit"), "score": p.get("score"), "comments": p.get("num_comments"), "url": p.get("url"), "permalink": f"https://reddit.com{p.get('permalink')}" if p.get("permalink") else None, "is_self": p.get("is_self"), "selftext": p.get("selftext", "")[:300] if p.get("selftext") else "", "created": datetime.fromtimestamp(p.get("created_utc", 0)).isoformat() if p.get("created_utc") else "" }) return {"posts": posts, "subreddit": subreddit} return result # ============================================================ # TRAFFIC & TRANSPORT # ============================================================ def get_random_highway_webcam(country: str = "US") -> dict: """ Get random highway webcam image (US only) Args: country: Country code (only US supported) """ # This is a simplified version - real implementation would use a traffic API return {"note": "Traffic webcam requires a specialized API key. Try using map services or government traffic APIs."} # ============================================================ # WORLD BANK # ============================================================ def get_world_bank_indicator(country_code: str = "US", indicator: str = "NY.GDP.MKTP.CD", date: str = "2022") -> dict: """ Get World Bank economic indicators Args: country_code: 2-letter country code or 'WLD' for world indicator: Indicator code (NY.GDP.MKTP.CD=GDP, SP.POP.TOTL=Population, etc.) date: Year or range (e.g., '2020:2022') """ result = _safe_get(f"https://api.worldbank.org/v2/country/{country_code}/indicator/{indicator}?date={date}&format=json") if isinstance(result, list) and len(result) > 1 and result[1]: data = result[1][0] return { "indicator": indicator, "country": data.get("country", {}).get("value"), "country_code": data.get("country", {}).get("id"), "value": data.get("value"), "date": data.get("date"), } return result def get_world_bank_countries() -> dict: """Get list of countries from World Bank""" return _safe_get("https://api.worldbank.org/v2/country?format=json&per_page=300") # ============================================================ # HEALTH # ============================================================ def get_drug_info(search: str) -> dict: """ Search for FDA drug information Args: search: Drug name or active ingredient """ return _safe_get(f"https://api.fda.gov/drug/label.json?search={search}&limit=1") def get_covid_global() -> dict: """Get global COVID-19 statistics""" result = _safe_get("https://disease.sh/v3/covid-19/all") if "cases" in result: return { "total_cases": result.get("cases"), "total_deaths": result.get("deaths"), "total_recovered": result.get("recovered"), "active_cases": result.get("active"), "today_cases": result.get("todayCases"), "today_deaths": result.get("todayDeaths"), "today_recovered": result.get("todayRecovered"), "tests": result.get("tests"), "affected_countries": result.get("affectedCountries"), "updated": datetime.fromtimestamp(result.get("updated", 0)/1000).isoformat() if result.get("updated") else "" } return result # ============================================================ # FUNCTION REGISTRY FOR HERMES AGENT # ============================================================ ALL_FUNCTIONS = [ # Animals get_random_dog, get_dog_breeds, get_random_cat, get_random_fox, get_random_duck, get_shibe, # Anime search_anime, get_anime_details, get_top_anime, get_current_season_anime, get_random_anime_quote, # Art search_artworks, search_met_museum, # Books search_books, get_book_by_isbn, get_random_quote, get_quote_of_the_day, get_random_gutenberg_book, # Calendar get_public_holidays, get_country_info, get_available_countries, # Crypto get_crypto_price, get_trending_crypto, get_global_crypto_data, get_bitcoin_data, convert_crypto, # Currency convert_currency, get_exchange_rates, # Development github_search, get_github_user, get_stackoverflow_questions, # Dictionary define_word, # Environment/Weather get_weather_forecast, get_weather_by_city, get_air_quality, get_iss_location, get_astronauts_in_space, get_nasa_apod, get_space_x_launches, # Food search_meal, get_random_meal, search_cocktail, get_random_cocktail, get_random_coffee_image, # Games get_pokemon, get_random_pokemon, get_pokemon_by_type, get_dnd_class, get_dnd_monster, get_dnd_spell, get_chess_profile, draw_cards, # Geocoding geocode_location, reverse_geocode, get_my_ip, # Jokes get_joke, get_chuck_norris_joke, get_chuck_norris_categories, get_random_meme, get_rick_and_morty_character, get_random_activity, get_useless_fact, # Music get_song_lyrics, search_lastfm, # News get_hacker_news, get_space_news, # Quotes get_breaking_bad_quote, get_game_of_thrones_quote, get_zen_quote, get_ron_swanson_quote, # Sports get_football_standings, get_nba_games, get_f1_results, get_city_bikes, # Test Data generate_random_user, generate_fake_data, get_random_user, # Text translate_text, analyze_sentiment, detect_language, # URL shorten_url, # Science get_element, get_number_fact, get_random_number_fact, # Code get_codeforces_user, get_codeforces_contests, # TV/Movies search_tv_show, get_tv_schedule, # Universities search_universities, # Colors get_random_color, get_color_by_hex, # QR generate_qr_code, # Facts get_cat_fact, get_dog_fact, get_trivia_question, # Reddit get_reddit_posts, # World Bank get_world_bank_indicator, get_world_bank_countries, # Health get_drug_info, get_covid_global, ] def get_function_by_name(name: str): """Get a function by its name""" for func in ALL_FUNCTIONS: if func.__name__ == name: return func return None def get_all_function_names() -> List[str]: """Get list of all available function names""" return [func.__name__ for func in ALL_FUNCTIONS] def get_function_schemas() -> List[Dict]: """ Generate OpenAI-compatible function schemas for all functions This uses the function docstrings to create schemas """ import inspect schemas = [] for func in ALL_FUNCTIONS: sig = inspect.signature(func) doc = inspect.getdoc(func) or "" # Parse parameters params = {} required = [] for name, param in sig.parameters.items(): param_type = "string" param_default = param.default if param.default is not inspect.Parameter.empty else None param_annotation = param.annotation if param.annotation is not inspect.Parameter.empty else None if param_annotation == int or param_annotation == float: param_type = "number" elif param_annotation == bool: param_type = "boolean" param_info = {"type": param_type, "description": f"Parameter {name}"} if param_default is not None: param_info["default"] = param_default else: required.append(name) params[name] = param_info schema = { "type": "function", "function": { "name": func.__name__, "description": doc.split("\n")[0] if doc else f"Execute {func.__name__}", "parameters": { "type": "object", "properties": params, "required": required if required else list(params.keys()) } } } schemas.append(schema) return schemas