157 lines
4.7 KiB
Python
157 lines
4.7 KiB
Python
import csv
|
|
import json
|
|
import logging
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
from django.contrib.auth import get_user_model
|
|
from django.conf import settings
|
|
|
|
User = get_user_model()
|
|
if TYPE_CHECKING:
|
|
from scrobbles.models import Scrobble
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
SEARCH_ID_URL = "https://boardgamegeek.com/xmlapi/search?search={query}&exact=1"
|
|
GAME_ID_URL = "https://boardgamegeek.com/xmlapi/boardgame/{id}"
|
|
BGG_ACCESS_TOKEN = getattr(settings, "BGG_ACCESS_TOKEN", "")
|
|
BASE_HEADERS = {
|
|
"User-Agent": "Vrobbler 31.0",
|
|
"Authorization": f"Bearer {BGG_ACCESS_TOKEN}",
|
|
}
|
|
|
|
|
|
def take_first(thing: Optional[list]) -> str:
|
|
first = ""
|
|
try:
|
|
first = thing[0]
|
|
except IndexError:
|
|
pass
|
|
|
|
if first:
|
|
try:
|
|
first = first.get_text()
|
|
except:
|
|
pass
|
|
|
|
return first
|
|
|
|
|
|
def lookup_boardgame_id_from_bgg(title: str) -> Optional[int]:
|
|
soup = None
|
|
game_id = None
|
|
url = SEARCH_ID_URL.format(query=title)
|
|
r = requests.get(url, headers=BASE_HEADERS)
|
|
if r.status_code == 200:
|
|
soup = BeautifulSoup(r.text, "xml")
|
|
|
|
if soup:
|
|
result = soup.findAll("boardgame")
|
|
if not result:
|
|
return game_id
|
|
|
|
game_id = result[0].get("objectid", None)
|
|
|
|
return game_id
|
|
|
|
|
|
def lookup_boardgame_from_bgg(lookup_id: str) -> dict:
|
|
soup = None
|
|
game_dict = {}
|
|
|
|
title = ""
|
|
bgg_id = None
|
|
|
|
try:
|
|
bgg_id = int(lookup_id)
|
|
logger.debug(f"Using BGG ID {bgg_id} to find board game")
|
|
except ValueError:
|
|
title = lookup_id
|
|
logger.debug(f"Using title {title} to find board game")
|
|
|
|
if not bgg_id:
|
|
bgg_id = lookup_boardgame_id_from_bgg(title)
|
|
|
|
url = GAME_ID_URL.format(id=bgg_id)
|
|
r = requests.get(url, headers=BASE_HEADERS)
|
|
if r.status_code == 200:
|
|
soup = BeautifulSoup(r.text, "xml")
|
|
|
|
if soup:
|
|
seconds_to_play = None
|
|
minutes = take_first(soup.findAll("playingtime"))
|
|
if minutes:
|
|
seconds_to_play = int(minutes) * 60
|
|
|
|
game_dict = {
|
|
"bggeek_id": bgg_id,
|
|
"title": take_first(soup.findAll("name", primary="true")),
|
|
"description": take_first(soup.findAll("description")),
|
|
"year_published": take_first(soup.findAll("yearpublished")),
|
|
"publisher_name": take_first(soup.findAll("boardgamepublisher")),
|
|
"cover_url": take_first(soup.findAll("image")),
|
|
"min_players": take_first(soup.findAll("minplayers")),
|
|
"max_players": take_first(soup.findAll("maxplayers")),
|
|
"recommended_age": take_first(soup.findAll("age")),
|
|
"run_time_seconds": seconds_to_play,
|
|
}
|
|
|
|
return game_dict
|
|
|
|
|
|
def push_scrobble_to_bgg(scrobble: "Scrobble", user: User) -> Optional[bool]:
|
|
bgg_username = "secstate" # user.profile.bgg_username
|
|
bgg_password = "yYFCKnfo8AK89lc68q0S"
|
|
|
|
if not bgg_username or bgg_password:
|
|
return
|
|
|
|
login_payload = {
|
|
"credentials": {"username": bgg_username, "password": bgg_password}
|
|
}
|
|
headers = BASE_HEADERS
|
|
headers["content-type"] = "application/json"
|
|
|
|
# TODO Look up past plays for scrobble.media_obj.bggeek_id, and make sure we haven't scrobbled this before
|
|
|
|
with requests.Session() as s:
|
|
p = s.post(
|
|
"https://boardgamegeek.com/login/api/v1",
|
|
data=json.dumps(login_payload),
|
|
headers=headers,
|
|
)
|
|
players = []
|
|
if scrobble.log:
|
|
for player in scrobble.log.get("players"):
|
|
player_person = Person.objects.filter(
|
|
id=player.get("person_id")
|
|
).first()
|
|
if player_person.get("bgg_username"):
|
|
player["username"] = player_person.get("bgg_username")
|
|
player["name"] = player_person.get("name")
|
|
player["win"] = player.get("win")
|
|
# player["role"] = player.get("role")
|
|
player["new"] = player.get("new")
|
|
player["score"] = player.get("score")
|
|
players.append(player)
|
|
|
|
play_payload = {
|
|
"playdate": scrobble.timestamp.date.strftime("%Y-%m-%d"),
|
|
"length": scrobble.playback_position_seconds / 60,
|
|
"comments": "Uploaded from Vrobbler",
|
|
"location": scrobble.log.location or None,
|
|
"objectid": scrobble.media_obj.bggeek_id,
|
|
"quantity": "1",
|
|
"action": "save",
|
|
"players": players,
|
|
"objecttype": "thing",
|
|
"ajax": 1,
|
|
}
|
|
r = s.post(
|
|
"https://boardgamegeek.com/geekplay.php",
|
|
data=json.dumps(play_payload),
|
|
headers=headers,
|
|
)
|