bgg.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. import csv
  2. import json
  3. import logging
  4. from typing import TYPE_CHECKING, Optional
  5. import requests
  6. from bs4 import BeautifulSoup
  7. from django.contrib.auth import get_user_model
  8. User = get_user_model()
  9. if TYPE_CHECKING:
  10. from scrobbles.models import Scrobble
  11. logger = logging.getLogger(__name__)
  12. SEARCH_ID_URL = (
  13. "https://boardgamegeek.com/xmlapi/search?search={query}&exact=1"
  14. )
  15. GAME_ID_URL = "https://boardgamegeek.com/xmlapi/boardgame/{id}"
  16. def take_first(thing: Optional[list]) -> str:
  17. first = ""
  18. try:
  19. first = thing[0]
  20. except IndexError:
  21. pass
  22. if first:
  23. try:
  24. first = first.get_text()
  25. except:
  26. pass
  27. return first
  28. def lookup_boardgame_id_from_bgg(title: str) -> Optional[int]:
  29. soup = None
  30. headers = {"User-Agent": "Vrobbler 0.11.12"}
  31. game_id = None
  32. url = SEARCH_ID_URL.format(query=title)
  33. r = requests.get(url, headers=headers)
  34. if r.status_code == 200:
  35. soup = BeautifulSoup(r.text, "xml")
  36. if soup:
  37. result = soup.findAll("boardgame")
  38. if not result:
  39. return game_id
  40. game_id = result[0].get("objectid", None)
  41. return game_id
  42. def lookup_boardgame_from_bgg(lookup_id: str) -> dict:
  43. soup = None
  44. game_dict = {}
  45. headers = {"User-Agent": "Vrobbler 0.11.12"}
  46. title = ""
  47. bgg_id = None
  48. try:
  49. bgg_id = int(lookup_id)
  50. logger.debug(f"Using BGG ID {bgg_id} to find board game")
  51. except ValueError:
  52. title = lookup_id
  53. logger.debug(f"Using title {title} to find board game")
  54. if not bgg_id:
  55. bgg_id = lookup_boardgame_id_from_bgg(title)
  56. url = GAME_ID_URL.format(id=bgg_id)
  57. r = requests.get(url, headers=headers)
  58. if r.status_code == 200:
  59. soup = BeautifulSoup(r.text, "xml")
  60. if soup:
  61. seconds_to_play = None
  62. minutes = take_first(soup.findAll("playingtime"))
  63. if minutes:
  64. seconds_to_play = int(minutes) * 60
  65. game_dict = {
  66. "bggeek_id": bgg_id,
  67. "title": take_first(soup.findAll("name", primary="true")),
  68. "description": take_first(soup.findAll("description")),
  69. "year_published": take_first(soup.findAll("yearpublished")),
  70. "publisher_name": take_first(soup.findAll("boardgamepublisher")),
  71. "cover_url": take_first(soup.findAll("image")),
  72. "min_players": take_first(soup.findAll("minplayers")),
  73. "max_players": take_first(soup.findAll("maxplayers")),
  74. "recommended_age": take_first(soup.findAll("age")),
  75. "run_time_seconds": seconds_to_play,
  76. }
  77. return game_dict
  78. def push_scrobble_to_bgg(scrobble: "Scrobble", user: User) -> Optional[bool]:
  79. bgg_username = user.profile.bgg_username
  80. bgg_password = user.profile.bgg_password
  81. if not bgg_username or bgg_password:
  82. return
  83. login_payload = {
  84. "credentials": {"username": bgg_username, "password": bgg_password}
  85. }
  86. headers = {"content-type": "application/json"}
  87. # TODO Look up past plays for scrobble.media_obj.bggeek_id, and make sure we haven't scrobbled this before
  88. with requests.Session() as s:
  89. p = s.post(
  90. "https://boardgamegeek.com/login/api/v1",
  91. data=json.dumps(login_payload),
  92. headers=headers,
  93. )
  94. players = []
  95. if scrobble.metadata:
  96. for player in scrobble.metadata.players:
  97. if player["user_id"]:
  98. player_user = User.objects.filter(
  99. id=player["user_id"]
  100. ).first()
  101. if player_user:
  102. if player_user.bgg_username:
  103. player["username"] = player_user.bgg_username
  104. else:
  105. player["name"] = player_user.username
  106. player["win"] = player.get("win")
  107. player["color"] = player.get("color")
  108. player["new"] = player.get("new")
  109. player["score"] = player.get("score")
  110. players.append(player)
  111. play_payload = {
  112. "playdate": scrobble.timestamp.date.strftime("%Y-%m-%d"),
  113. "length": scrobble.playback_position_seconds / 60,
  114. "comments": "Uploaded from Vrobbler",
  115. "location": scrobble.log.location or None,
  116. "objectid": scrobble.media_obj.bggeek_id,
  117. "quantity": "1",
  118. "action": "save",
  119. "players": players,
  120. "objecttype": "thing",
  121. "ajax": 1,
  122. }