Fix an issue with campaigns having an incorrect allowed channels list

This commit is contained in:
DevilXD committed 2022-10-31 20:35:46 +01:00
1 parent d8958d746c
commit fdd0172abf
1 file changed
+35 -24
+35 -24
View File
@@ -6,6 +6,7 @@ import asyncio
import logging import logging
from math import ceil from math import ceil
from time import time from time import time
from itertools import chain
from functools import partial from functools import partial
# from base64 import urlsafe_b64decode # from base64 import urlsafe_b64decode
from collections import abc, OrderedDict from collections import abc, OrderedDict
@@ -1059,23 +1060,35 @@ class Twitch:
raise MinerException(f"GQL error: {response_json['errors']}") raise MinerException(f"GQL error: {response_json['errors']}")
return response_json return response_json
async def fetch_campaign( def _merge_data(self, primary_data: JsonType, secondary_data: JsonType) -> JsonType:
self, merged = {}
campaign_id: str, for key in set(chain(primary_data.keys(), secondary_data.keys())):
available_data: JsonType, in_primary = key in primary_data
claimed_benefits: dict[str, datetime], in_secondary = key in secondary_data
) -> DropsCampaign: if in_primary and in_secondary:
vp = primary_data[key]
vs = secondary_data[key]
if not isinstance(vp, type(vs)) or not isinstance(vs, type(vp)):
raise MinerException("Inconsistent merge data")
if isinstance(vp, dict): # both are dicts
merged[key] = self._merge_data(vp, vs)
else:
# use primary value
merged[key] = vp
elif in_primary:
merged[key] = primary_data[key]
else: # in campaigns only
merged[key] = secondary_data[key]
return merged
async def fetch_campaign(self, campaign_id: str, available_data: JsonType) -> JsonType:
auth_state = await self.get_auth() auth_state = await self.get_auth()
response = await self.gql_request( response = await self.gql_request(
GQL_OPERATIONS["CampaignDetails"].with_variables( GQL_OPERATIONS["CampaignDetails"].with_variables(
{"channelLogin": str(auth_state.user_id), "dropID": campaign_id} {"channelLogin": str(auth_state.user_id), "dropID": campaign_id}
) )
) )
campaign_data: JsonType = response["data"]["user"]["dropCampaign"] return self._merge_data(available_data, response["data"]["user"]["dropCampaign"])
# NOTE: we use available_data to add a couple of fields missing from the existing details,
# most notably: game boxart
campaign_data["game"]["boxArtURL"] = available_data["game"]["boxArtURL"]
return DropsCampaign(self, campaign_data, claimed_benefits)
async def fetch_inventory(self) -> None: async def fetch_inventory(self) -> None:
status_update = self.gui.status.update status_update = self.gui.status.update
@@ -1088,22 +1101,15 @@ class Twitch:
claimed_benefits: dict[str, datetime] = { claimed_benefits: dict[str, datetime] = {
b["id"]: timestamp(b["lastAwardedAt"]) for b in inventory["gameEventDrops"] b["id"]: timestamp(b["lastAwardedAt"]) for b in inventory["gameEventDrops"]
} }
campaigns: list[DropsCampaign] = [ inventory_data: dict[str, JsonType] = {c["id"]: c for c in ongoing_campaigns}
DropsCampaign(self, campaign_data, claimed_benefits)
for campaign_data in ongoing_campaigns
]
# fetch all available campaigns data # fetch all available campaigns data
response = await self.gql_request(GQL_OPERATIONS["Campaigns"]) response = await self.gql_request(GQL_OPERATIONS["Campaigns"])
available_list: list[JsonType] = response["data"]["currentUser"]["dropCampaigns"] or [] available_list: list[JsonType] = response["data"]["currentUser"]["dropCampaigns"] or []
applicable_statuses = ("ACTIVE", "UPCOMING") applicable_statuses = ("ACTIVE", "UPCOMING")
existing_campaigns: set[str] = set(c.id for c in campaigns)
available_campaigns: dict[str, JsonType] = { available_campaigns: dict[str, JsonType] = {
c["id"]: c c["id"]: c
for c in available_list for c in available_list
if ( if c["status"] in applicable_statuses # that are currently not expired
c["status"] in applicable_statuses # that are currently ACTIVE
and c["id"] not in existing_campaigns # and they aren't in the inventory already
)
} }
# add campaigns that remained, that can be earned but are not in-progress yet # add campaigns that remained, that can be earned but are not in-progress yet
status_update( status_update(
@@ -1111,23 +1117,28 @@ class Twitch:
counter=f"(0/{len(available_campaigns)})" counter=f"(0/{len(available_campaigns)})"
) )
) )
fetched_campaigns: list[DropsCampaign] = [] fetched_campaigns: dict[str, JsonType] = {}
for i, coro in enumerate( for i, coro in enumerate(
# specifically use an intermediate list per a Python bug # specifically use an intermediate list per a Python bug
# https://github.com/python/cpython/issues/88342 # https://github.com/python/cpython/issues/88342
asyncio.as_completed([ asyncio.as_completed([
self.fetch_campaign(campaign_id, available_data, claimed_benefits) self.fetch_campaign(campaign_id, available_data)
for campaign_id, available_data in available_campaigns.items() for campaign_id, available_data in available_campaigns.items()
]), ]),
start=1, start=1,
): ):
fetched_campaigns.append(await coro)
status_update( status_update(
_("gui", "status", "fetching_campaigns").format( _("gui", "status", "fetching_campaigns").format(
counter=f"({i}/{len(available_campaigns)})" counter=f"({i}/{len(available_campaigns)})"
) )
) )
campaigns.extend(fetched_campaigns) campaign_data = await coro
fetched_campaigns[campaign_data["id"]] = campaign_data
merged_campaigns: dict[str, JsonType] = self._merge_data(inventory_data, fetched_campaigns)
campaigns: list[DropsCampaign] = [
DropsCampaign(self, campaign_data, claimed_benefits)
for campaign_data in merged_campaigns.values()
]
campaigns.sort(key=lambda c: c.active, reverse=True) campaigns.sort(key=lambda c: c.active, reverse=True)
campaigns.sort(key=lambda c: c.upcoming and c.starts_at or c.ends_at) campaigns.sort(key=lambda c: c.upcoming and c.starts_at or c.ends_at)
campaigns.sort(key=lambda c: c.linked, reverse=True) campaigns.sort(key=lambda c: c.linked, reverse=True)