diff --git a/dailyreleases/gog.py b/dailyreleases/gog.py index 518a486..3ded6a7 100644 --- a/dailyreleases/gog.py +++ b/dailyreleases/gog.py @@ -4,7 +4,7 @@ import logging logger = logging.getLogger(__name__) -class Gog(object): +class GOG(object): def __init__(self, cache) -> None: self.cache = cache diff --git a/dailyreleases/main.py b/dailyreleases/main.py index 7f66a3f..2e971d2 100644 --- a/dailyreleases/main.py +++ b/dailyreleases/main.py @@ -13,6 +13,7 @@ import requests_cache from tabulate import tabulate from dailyreleases import config, __version__ +from dailyreleases.gog import GOG from dailyreleases.predb import Predb from dailyreleases.reddit import Reddit from dailyreleases.steam import Steam @@ -39,6 +40,7 @@ class DailyReleasesBot(object): self.web = Web(self.config, self.cache) self.predb = Predb(self.cache) self.steam = Steam(self.cache) + self.gog = GOG(self.cache) self.reddit = Reddit(self.config) def run(self): @@ -70,21 +72,25 @@ class DailyReleasesBot(object): print("Exiting (KeyboardInterrupt)") break - def find_store_link(self, game_name): - """ - Try to find hyperlink to a store selling this game. + def find_store_links(self, game_name) -> dict: + links = {} - :return: (Link, name of store) - """ - # Try searching the Steam store for the game - link = self.steam.search(game_name) - if link: - return link, "Steam" + # Steam + steam_link = self.steam.search(game_name) + if steam_link: + links["Steam"] = steam_link - # If that didn't work, try Googling the game + # GOG + gog_link = self.gog.search(game_name) + if gog_link: + links["GOG"] = f"{gog_link} 'DRM-Free! 👍'" # hover text + + if links: + return links + + # If none of those worked, try Googling the game known_stores = { - "store.steampowered.com/(app|sub)": "Steam", # order doesn't matter - "store.steampowered.com/bundle": "Steam Bundle", + "store.steampowered.com/(app|sub|bundle)": "Steam", # order doesn't matter "gog.com/game": "GOG", "origin.com": "Origin", "ubi(soft)?.com": "Ubisoft", @@ -99,10 +105,10 @@ class DailyReleasesBot(object): for link in self.web.search(f"{game_name} buy"): for store_url, store_name in known_stores.items(): if re.search(store_url, link, flags=re.IGNORECASE): - return link, store_name + return {store_name: link} - logger.debug("Unable to find store link for %s", game_name) - return None, None + logger.debug("Unable to find store links for %s", game_name) + return {} def parse_dirname(self, dirname): logger.info("---") @@ -156,11 +162,11 @@ class DailyReleasesBot(object): logger.info("Offline: %s %s : %s - %s", platform, rls_type, game_name, group) logger.info("Tags: %s. Highlights: %s", tags, highlights) - # Find store link - store_link, store_name = self.find_store_link(game_name) + # Find store links + store_links = self.find_store_links(game_name) # No store link? Probably software and not a game - if not store_link: + if not store_links: logger.info("Skipping %s: no store link (probably software)", dirname) return @@ -169,11 +175,11 @@ class DailyReleasesBot(object): popularity = -1 review_score = -1 - # If the store link we found is to Steam, use their API to get (better) information about the game. + # If one of the store links we found is to Steam, use their API to get (better) information about the game. # Note: Doesn't apply to Steam bundles, as Steam has no public API for those. - if store_name == "Steam": + try: + steam_type, steam_appid = re.search("(app|sub)(?:/)([0-9]+)", store_links["Steam"]).groups() logger.debug("Getting information about game using Steam API") - steam_type, steam_appid = re.search("(app|sub)(?:/)([0-9]+)", store_link).groups() # If the release is a package on Steam (e.g. game + dlc), we need to find the base game of the package if steam_type == "sub": @@ -212,9 +218,10 @@ class DailyReleasesBot(object): logger.info("'denuvo' found in EULA; adding 'DENUVO' to highlights") highlights.append("DENUVO") - # We only called it "Steam Bundle" to bypass the Steam-API logic. Fix for aesthetics. - if store_name == "Steam Bundle": - store_name = "Steam" + except KeyError: + pass # no link to Steam store + except AttributeError: + logger.debug("Steam link is to bundle: not utilizing API") release = { "dirname": dirname, @@ -223,8 +230,7 @@ class DailyReleasesBot(object): "game_name": game_name, "type": rls_type, "platform": platform, - "store_link": store_link, - "store_name": store_name, + "store_links": store_links, "popularity": popularity, "review_score": review_score, "tags": tags, @@ -309,13 +315,13 @@ class DailyReleasesBot(object): name = "[{}{}]({}){}".format(r["game_name"], tags, r["nfo_link"], highlights) if r["review_score"] != -1: - review_score = "{:.0%} ^^\({}\)".format(r["review_score"], r["popularity"]) + reviews = "{:.0%} ^^\({}\)".format(r["review_score"], r["popularity"]) else: - review_score = "-" + reviews = "-" - store = "[{}]({})".format(r["store_name"], r["store_link"]) + stores = ", ".join(f"[{name}]({link})" for name, link in r["store_links"].items()) - return name, r["group"], review_score, store + return name, r["group"], reviews, stores # Releases in the sub-tables are grouped by release group, and the groups are ordered according to the # most popular game within the group. Games are sorted by popularity internally in the groups. @@ -327,7 +333,7 @@ class DailyReleasesBot(object): for rls in sorted(type_releases, key=lambda r: (group_popularity[r["group"]], r["group"], r["popularity"]), reverse=True)] - post.append(tabulate(table, headers=(type_name, "Group", "Score", "Store"), tablefmt="pipe")) + post.append(tabulate(table, headers=(type_name, "Group", "Reviews", "Stores"), tablefmt="pipe")) post.append("") post.append(" ") diff --git a/tests/test_parse_dirname.py b/tests/test_parse_dirname.py index 13702f6..d8d5d1d 100644 --- a/tests/test_parse_dirname.py +++ b/tests/test_parse_dirname.py @@ -16,8 +16,7 @@ class ParseDirnameTestCase(unittest.TestCase): self.assertEqual("Windows", p["platform"]) self.assertEqual("Games", p["type"]) self.assertEqual("DARKSiDERS", p["group"]) - self.assertIn("store.steampowered.com/app/244750", p["store_link"]) - self.assertEqual("Steam", p["store_name"]) + self.assertIn("store.steampowered.com/app/244750", p["store_links"]["Steam"]) self.assertEqual([], p["tags"]) self.assertEqual([], p["highlights"]) @@ -56,7 +55,7 @@ class ParseDirnameTestCase(unittest.TestCase): def test_dlc_implicit(self): p = self.bot.parse_dirname("Euro.Truck.Simulator.2.Italia-CODEX") self.assertEqual("DLC", p["type"]) - self.assertIn("store.steampowered.com/app/558244", p["store_link"]) + self.assertIn("store.steampowered.com/app/558244", p["store_links"]["Steam"]) def test_incl_dlc_update(self): p = self.bot.parse_dirname("Wolfenstein.II.The.New.Colossus.Update.5.incl.DLC-CODEX") @@ -73,14 +72,12 @@ class ParseDirnameTestCase(unittest.TestCase): def test_non_steam(self): p = self.bot.parse_dirname("Battlefield.1.REPACK-CPY") - self.assertIn("www.origin.com/usa/en-us/store/battlefield/battlefield-1", p["store_link"]) - self.assertEqual("Origin", p["store_name"]) + self.assertIn("www.origin.com/usa/en-us/store/battlefield/battlefield-1", p["store_links"]["Origin"]) self.assertEqual(-1, p["popularity"]) def test_gog_exclusive(self): p = self.bot.parse_dirname("Dungeons.and.Dragons.Dragonshard.v2.0.0.10.Multilingual-DELiGHT") - self.assertIn("gog.com/game/dungeons_dragons_dragonshard", p["store_link"]) - self.assertEqual("GOG", p["store_name"]) + self.assertIn("gog.com/game/dungeons_dragons_dragonshard", p["store_links"]["GOG"]) self.assertEqual(-1, p["popularity"]) def test_popularity_non_steam(self): @@ -99,14 +96,13 @@ class ParseDirnameTestCase(unittest.TestCase): p = self.bot.parse_dirname("Farming.Simulator.17.Platinum.Edition.Update.v1.5.3-BAT") self.assertEqual("Farming Simulator 17 - Platinum Edition", p["game_name"]) self.assertEqual("Updates", p["type"]) - self.assertIn("store.steampowered.com/sub/202103", p["store_link"]) - self.assertEqual("Steam", p["store_name"]) + self.assertIn("store.steampowered.com/sub/202103", p["store_links"]["Steam"]) def test_steam_package_with_dlc_first(self): p = self.bot.parse_dirname("The.Witcher.3.Wild.Hunt.Game.of.The.Year.Edition-RELOADED") self.assertEqual("The Witcher 3: Wild Hunt - Game of the Year Edition", p["game_name"]) self.assertEqual("Games", p["type"]) - self.assertIn("store.steampowered.com/sub/124923", p["store_link"]) + self.assertIn("store.steampowered.com/sub/124923", p["store_links"]["Steam"]) def test_steam_bundle(self): p = self.bot.parse_dirname("Valve.Complete.Pack-FAKE") @@ -114,18 +110,18 @@ class ParseDirnameTestCase(unittest.TestCase): self.assertEqual("Valve Complete Pack", p["game_name"]) self.assertEqual("Windows", p["platform"]) self.assertEqual("Games", p["type"]) - self.assertIn("store.steampowered.com/bundle/232", p["store_link"]) + self.assertIn("store.steampowered.com/bundle/232", p["store_links"]["Steam"]) def test_denuvo_eula(self): p = self.bot.parse_dirname("Deus.Ex.Mankind.Divided-CPY") - self.assertIn("store.steampowered.com/app/337000", p["store_link"]) + self.assertIn("store.steampowered.com/app/337000", p["store_links"]["Steam"]) self.assertEqual(["DENUVO"], p["highlights"]) def test_episode_release(self): p = self.bot.parse_dirname("Life.is.Strange.Before.the.Storm.Episode.3-CODEX") self.assertEqual("Life is Strange: Before the Storm Episode 3", p["game_name"]) self.assertEqual("DLC", p["type"]) - self.assertIn("store.steampowered.com/app/704740", p["store_link"]) + self.assertIn("store.steampowered.com/app/704740", p["store_links"]["Steam"]) def test_season_and_episode_release(self): p = self.bot.parse_dirname("Minecraft.Story.Mode.Season.Two.Episode.5.MacOSX-RELOADED") @@ -137,7 +133,7 @@ class ParseDirnameTestCase(unittest.TestCase): def test_readnfo_microsoft_store(self): p = self.bot.parse_dirname("Zoo.Tycoon.Ultimate.Animal.Collection.READNFO-CODEX") - self.assertIn("microsoft.com/en-us/p/zoo-tycoon-ultimate-animal-collection", p["store_link"]) + self.assertIn("microsoft.com/en-us/p/zoo-tycoon-ultimate-animal-collection", p["store_links"]["Microsoft Store"]) if __name__ == '__main__':