Spaces:
Sleeping
Sleeping
File size: 9,815 Bytes
2577d67 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
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 []
|