Spaces:
Sleeping
Sleeping
import requests | |
from xml.etree import ElementTree as ET | |
BGG_BASE_URL = 'https://boardgamegeek.com/xmlapi2' | |
def search(query): | |
""" | |
Search for board games on BoardGameGeek by title. | |
Args: | |
query (str): The name (or partial name) of the board game to search for. | |
Returns: | |
List[dict]: A list of dictionaries, each representing a matching game with the following fields: | |
- 'id' (str): The unique BGG ID of the game. | |
- 'title' (str): The official title of the game. | |
- 'year' (str): The year the game was published (or 'Unknown' if not available). | |
Example: | |
>>> search_game("Catan") | |
[ | |
{"id": "13", "title": "Catan", "year": "1995"}, | |
... | |
] | |
""" | |
response = requests.get(f"{BGG_BASE_URL}/search?query={query}&exact=0&type=boardgame") | |
root = ET.fromstring(response.content) | |
results = [] | |
for item in root.findall("item"): | |
game_id = item.get("id") | |
title = item.find("name").get("value") | |
year = item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown" | |
results.append({"id": game_id, "title": title, "year": year}) | |
return results | |
def get_game_details(game_ids): | |
""" | |
Get detailed information for one or more board games. | |
Args: | |
game_ids (str or list): Single game ID or comma-separated list of IDs | |
Returns: | |
List[dict]: Detailed game information including mechanics, categories, ratings, etc. | |
""" | |
if isinstance(game_ids, list): | |
game_ids = ",".join(game_ids) | |
response = requests.get(f"{BGG_BASE_URL}/thing?id={game_ids}&stats=1") | |
root = ET.fromstring(response.content) | |
games = [] | |
for item in root.findall("item"): | |
game = { | |
"id": item.get("id"), | |
"title": item.find("name[@type='primary']").get("value"), | |
"description": item.find("description").text if item.find("description") is not None else "No description available", | |
"year": item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown", | |
"min_players": item.find("minplayers").get("value") if item.find("minplayers") is not None else "Unknown", | |
"max_players": item.find("maxplayers").get("value") if item.find("maxplayers") is not None else "Unknown", | |
"playing_time": item.find("playingtime").get("value") if item.find("playingtime") is not None else "Unknown", | |
"complexity": None, | |
"rating": None, | |
"categories": [], | |
"mechanics": [] | |
} | |
# Extract ratings and complexity | |
ratings = item.find("statistics/ratings") | |
if ratings is not None: | |
avg_rating = ratings.find("average") | |
if avg_rating is not None: | |
game["rating"] = avg_rating.get("value") | |
complexity_elem = ratings.find("averageweight") | |
if complexity_elem is not None: | |
game["complexity"] = complexity_elem.get("value") | |
# Extract categories and mechanics | |
for link in item.findall("link"): | |
link_type = link.get("type") | |
if link_type == "boardgamecategory": | |
game["categories"].append(link.get("value")) | |
elif link_type == "boardgamemechanic": | |
game["mechanics"].append(link.get("value")) | |
games.append(game) | |
return games | |
def get_hot_games(): | |
""" | |
Get a list of the Top 50 trending games today from BoardGameGeek. | |
Returns: | |
List[dict]: A list of dictionaries, each representing a popular game with the following fields: | |
- 'id' (str): The unique BGG ID of the game. | |
- 'title' (str): The official title of the game. | |
- 'rank' (str): The current hotness rank of the game on BGG. | |
- 'year' (str): The year the game was published (or 'Unknown' if not available). | |
- 'url' (str): URL to the similar game's page on BoardGameGeek. | |
""" | |
response = requests.get(f"{BGG_BASE_URL}/hot?type=boardgame") | |
root = ET.fromstring(response.content) | |
results = [] | |
for item in root.findall("item"): | |
game_id = item.get("id") | |
rank = item.get("rank") | |
title = item.find("name").get("value") | |
year = item.find("yearpublished").get("value") if item.find("yearpublished") is not None else "Unknown" | |
url = f"https://boardgamegeek.com/boardgame/{game_id}" | |
results.append({"id": game_id, "title": title, "rank": rank, "year": year, "url": url}) | |
return results | |
def get_similar_games(game_id, limit=5): | |
""" | |
Get a list of games similar to the specified game from the recommend games API. | |
Args: | |
game_id (str): The unique BGG ID of the game to find similar games for. | |
limit (int): The number of BGG IDs to retrieve. | |
Returns: | |
List[dict]: A list of dictionaries, each representing a similar game with the following fields: | |
- 'id' (str): The unique BGG ID of the similar game. | |
- 'title' (str): The official title of the similar game. | |
- 'year' (str): The year the similar game was published (or 'Unknown' if not available). | |
- 'description' (str): A brief description of the similar game. | |
- 'url' (str): URL to the similar game's page on BoardGameGeek. | |
""" | |
recommended_games = [] | |
api_url = f"https://recommend.games/api/games/{game_id}/similar.json" | |
try: | |
response = requests.get(api_url) | |
response.raise_for_status() | |
api_data = response.json() | |
results = api_data['results'] | |
# Filter out all games with less than 30 votes (num_votes) | |
results = [game for game in results if game.get('num_votes', 0) >= 30] | |
# Filter out all games with a rec_rating of 0 | |
results = [game for game in results if game.get('rec_rating', 0) > 0] | |
# Sort results by rec_rating, bayes_rating and avg_rating | |
results.sort(key=lambda x: (x.get('rec_rating', 0), x.get('bayes_rating', 0), x.get('avg_rating', 0)), reverse=True) | |
for game_data in results[:limit]: | |
bgg_id = game_data.get('bgg_id') | |
title = game_data.get('name') | |
year = game_data.get('year') | |
description = game_data.get('description', 'No description available') | |
url = game_data.get('url') | |
if bgg_id and title: | |
formatted_game = { | |
'id': str(bgg_id), | |
'title': str(title), | |
'year': str(year) if year is not None else 'Unknown', | |
'description': str(description), | |
'url': str(url), | |
} | |
recommended_games.append(formatted_game) | |
return recommended_games | |
except requests.RequestException as e: | |
print(f"Error fetching similar games: {e}") | |
return [] | |
def get_similar_games_v2(game_id, limit=5, start=0, end=25, noblock=False): | |
""" | |
Retrieves a list of games similar to a specified board game from the RecommendGames API. | |
Args: | |
game_id (str): The unique BGG ID of the game to find similar games for. | |
limit (int): The number of similar games to retrieve. | |
start (int, optional): The starting index for the desired range of results. Defaults to 0. | |
end (int, optional): The ending index for the desired range of results. Defaults to 25. | |
noblock (bool, optional): If True, the request will not timeout. Defaults to False, which sets a 10-second timeout. | |
Returns: | |
List[dict]: A list of dictionaries, each representing a similar game with the following fields: | |
- 'id' (str): The unique BGG ID of the similar game. | |
- 'title' (str): The official title of the similar game. | |
- 'year' (str): The year the similar game was published (or 'Unknown' if not available). | |
- 'description' (str): A brief description of the similar game. | |
- 'url' (str): URL to the similar game's page on BoardGameGeek. | |
Returns an empty list if there are any errors during the API request or data processing. | |
""" | |
api_url = f"https://recommend.games/api/games/{game_id}/similar.json" | |
params = { | |
'num_votes__gte': 30, | |
'ordering': '-rec_rating,-bayes_rating,-avg_rating' | |
} | |
all_games = [] | |
try: | |
for i in range(start+1, end+1): | |
params['page'] = i | |
response = requests.get(api_url, params=params, timeout=None if noblock else 10) | |
response.raise_for_status() | |
api_data = response.json() | |
games = api_data.get('results', []) | |
if not games: | |
break | |
processed_games = [ | |
{ | |
'id': str(game.get('bgg_id', '')), | |
'title': str(game.get('name', '')), | |
'year': str(game.get('year', 'Unknown')), | |
'description': str(game.get('description', 'No description available')), | |
'url': str(game.get('url', '')), | |
} | |
for game in games | |
if game.get('num_votes', 0) >= 30 and game.get('rec_rating', 0) > 0.001 | |
] | |
all_games.extend(processed_games) | |
# Check if we have enough results | |
if len(all_games) >= end or not api_data.get('next'): | |
break | |
return all_games[:limit] if limit > 0 else all_games | |
except requests.RequestException as e: | |
print(f"Error fetching similar games: {e}") | |
return [] | |
except ValueError as e: | |
print(f"Error: {e}") | |
return [] | |