From 7b0f25d5d0098f204139bb52d6098c412568ea35 Mon Sep 17 00:00:00 2001 From: DevilXD <4180725+DevilXD@users.noreply.github.com> Date: Sun, 23 Jun 2024 09:40:37 +0200 Subject: [PATCH] Introduce back GQL progress querying; to ensure more accurate drop progress --- constants.py | 7 +++++-- inventory.py | 14 +------------- twitch.py | 51 +++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 27 deletions(-) diff --git a/constants.py b/constants.py index 67b679e..a1a8252 100644 --- a/constants.py +++ b/constants.py @@ -284,8 +284,11 @@ GQL_OPERATIONS: dict[str, GQLOperation] = { # returns current state of drops (current drop progress) "CurrentDrop": GQLOperation( "DropCurrentSessionContext", - "2e4b3630b91552eb05b76a94b6850eb25fe42263b7cf6d06bee6d156dd247c1c", - # no variables needed + "4d06b702d25d652afb9ef835d2a550031f1cf762b193523a92166f40ea3d142b", + variables={ + "channelID": ..., # watched channel ID as a str + "channelLogin": "", # always empty string + }, ), # returns all available campaigns "Campaigns": GQLOperation( diff --git a/inventory.py b/inventory.py index 03639aa..aa95b05 100644 --- a/inventory.py +++ b/inventory.py @@ -1,14 +1,13 @@ from __future__ import annotations import re -import logging from itertools import chain from typing import TYPE_CHECKING from functools import cached_property from datetime import datetime, timedelta, timezone from channel import Channel -from constants import CALL, GQL_OPERATIONS, URLType +from constants import GQL_OPERATIONS, URLType from utils import timestamp, invalidate_cache, Game if TYPE_CHECKING: @@ -19,7 +18,6 @@ if TYPE_CHECKING: from gui import GUIManager, InventoryOverview -logger = logging.getLogger("TwitchDrops") DIMS_PATTERN = re.compile(r'-\d+x\d+(?=\.(?:jpg|png|gif)$)', re.I) @@ -250,19 +248,9 @@ class TimedDrop(BaseDrop): self._manager.display_drop(self, countdown=countdown, subone=subone) def bump_minutes(self): - # this may get called more often than once every minute - # we can detect this by checking if the GUI progress display is currently counting down - # if we haven't finished counting down the last minute, then we can ignore this call - if self._manager.progress.is_counting(): - return if self.current_minutes < self.required_minutes: self.current_minutes += 1 self._on_minutes_changed() - drop_text = ( - f"{self.name} ({self.campaign.game}, " - f"{self.current_minutes}/{self.required_minutes})" - ) - logger.log(CALL, f"Drop progress from active search: {drop_text}") self.display() diff --git a/twitch.py b/twitch.py index f559a4a..fe21f8a 100644 --- a/twitch.py +++ b/twitch.py @@ -842,18 +842,45 @@ class Twitch: succeeded: bool = await channel.send_watch() if not succeeded: logger.log(CALL, f"Watch requested failed for channel: {channel.name}") - await self._watch_sleep(interval) - continue - # Drop progress isn't always returned by the websocket, but we can "pretend" - # the progress is constantly advancing, by simply incrementing the minutes - # watched ourselves. Once the websocket "wakes up" to return proper progress, - # it'll update the display automatically. - # NOTE: get_active_drop uses the watching channel by default, - # so there's no point to pass it here - if (drop := self.get_active_drop()) is not None: - drop.bump_minutes() - else: - logger.log(CALL, "No active drop could be determined") + elif not self.gui.progress.is_counting(): + # If the previous update was more than 60s ago, and the progress tracker + # isn't counting down anymore, that means Twitch has temporarily + # stopped reporting drops progress. To ensure the timer keeps at least somewhat + # accurate time, we can use GQL to query for the current drop, + # or even "pretend" mining as a last resort option. + handled: bool = False + + # Solution 1: use GQL to query for the currently mined drop status + context = await self.gql_request( + GQL_OPERATIONS["CurrentDrop"].with_variables({"channelID": str(channel.id)}) + ) + drop_data: JsonType | None = ( + context["data"]["currentUser"]["dropCurrentSession"] + ) + if drop_data is not None: + drop = self._drops.get(drop_data["dropID"]) + if drop is not None and drop.can_earn(channel): + drop.update_minutes(drop_data["currentMinutesWatched"]) + drop_text = ( + f"{drop.name} ({drop.campaign.game}, " + f"{drop.current_minutes}/{drop.required_minutes})" + ) + logger.log(CALL, f"Drop progress from GQL: {drop_text}") + handled = True + + # Solution 2: If GQL fails, figure out which drop we're most likely mining + # right now, and then bump up the minutes on that drop + if not handled: + if (drop := self.get_active_drop(channel)) is not None: + drop.bump_minutes() + drop_text = ( + f"{drop.name} ({drop.campaign.game}, " + f"{drop.current_minutes}/{drop.required_minutes})" + ) + logger.log(CALL, f"Drop progress from active search: {drop_text}") + handled = True + else: + logger.log(CALL, "No active drop could be determined") await self._watch_sleep(interval) @task_wrapper