Files

144 lines
4.8 KiB
Python

#!/usr/bin/env python3
"""Search BGG for board game IDs using XML API v2."""
import subprocess
import sys
import time
import xml.etree.ElementTree as ET
# Read BGG key
with open('/tmp/.bggkey') as f:
bgg_key = f.read().strip()
HEADER = f"Authorization: Bearer {bgg_key}"
BASE_URL = "https://boardgamegeek.com/xmlapi2/search"
# Games to search: (full_name, primary_search_word, fallback_search_word)
GAMES = [
("Concordia Special Edition", "Concordia", None),
("Altera", "Altera", None),
("Aeon's End: System Overload", "Aeon's", "Overload"),
("Entropy", "Entropy", None),
("NARCOS", "Narcos", None),
("Stonefall", "Stonefall", None),
("Capitals of Tactoria", "Capitals", "Tactoria"),
("A Life", "Life", None),
("The Council of Anderian", "Anderian", "Council"),
("Ancient Blood", "Ancient", "Blood"),
("Growing Season", "Growing", "Season"),
("Believe in me!", "Believe", None),
("Wilderfeast", "Wilderfeast", None),
("TATANKA", "Tatanka", None),
]
def search_bgg(query_word):
"""Search BGG API and return list of (id, name, year) tuples."""
url = f"{BASE_URL}?query={query_word}&type=boardgame"
result = subprocess.run(
["curl", "-s", "-H", HEADER, url],
capture_output=True, text=True, timeout=30
)
if result.returncode != 0:
print(f" curl error: {result.stderr}", file=sys.stderr)
return []
xml_text = result.stdout.strip()
if not xml_text:
print(f" Empty response", file=sys.stderr)
return []
try:
root = ET.fromstring(xml_text)
except ET.ParseError as e:
print(f" XML parse error: {e}", file=sys.stderr)
return []
items = []
for item in root.findall('item'):
item_id = item.get('id', '')
item_type = item.get('type', '')
if item_type != 'boardgame':
continue
name_el = item.find('name')
if name_el is not None:
name_val = name_el.get('value', '')
else:
name_val = ''
year_el = item.find('yearpublished')
if year_el is not None:
year_val = year_el.get('value', '')
else:
year_val = ''
items.append((item_id, name_val, year_val))
return items
def find_exact_match(items, full_name):
"""Find exact match (case-insensitive) in search results."""
target = full_name.lower().strip()
for item_id, name, year in items:
if name.lower().strip() == target:
return item_id, name, year
return None
def find_best_match(items, full_name):
"""Find best match - first try exact, then first result."""
exact = find_exact_match(items, full_name)
if exact:
return exact
if items:
return items[0]
return None
def main():
results = []
for full_name, primary_word, fallback_word in GAMES:
print(f"\nSearching: {full_name} (word: {primary_word})", file=sys.stderr)
# Primary search
items = search_bgg(primary_word)
print(f" Got {len(items)} results", file=sys.stderr)
for i, (item_id, name, year) in enumerate(items[:5]):
print(f" [{i}] id={item_id} name='{name}' year={year}", file=sys.stderr)
match = find_best_match(items, full_name)
if match:
item_id, name, year = match
url = f"https://boardgamegeek.com/boardgame/{item_id}"
results.append(f"{full_name}|{item_id}|{url}")
print(f" MATCH: {item_id} - {name} ({year})", file=sys.stderr)
elif fallback_word:
# Try fallback
time.sleep(3)
print(f" No match, trying fallback: {fallback_word}", file=sys.stderr)
items2 = search_bgg(fallback_word)
print(f" Got {len(items2)} results", file=sys.stderr)
for i, (item_id, name, year) in enumerate(items2[:5]):
print(f" [{i}] id={item_id} name='{name}' year={year}", file=sys.stderr)
match2 = find_best_match(items2, full_name)
if match2:
item_id, name, year = match2
url = f"https://boardgamegeek.com/boardgame/{item_id}"
results.append(f"{full_name}|{item_id}|{url}")
print(f" MATCH (fallback): {item_id} - {name} ({year})", file=sys.stderr)
else:
results.append(f"{full_name}|NOT_FOUND")
print(f" NOT FOUND", file=sys.stderr)
else:
results.append(f"{full_name}|NOT_FOUND")
print(f" NOT FOUND", file=sys.stderr)
# Wait 3 seconds before next call
if full_name != GAMES[-1][0]:
time.sleep(3)
print("\n=== RESULTS ===", file=sys.stderr)
for r in results:
print(r)
if __name__ == "__main__":
main()