Files
TwitchDropsMiner/inventory.py
2021-12-03 17:48:51 +01:00

129 lines
4.5 KiB
Python

from __future__ import annotations
from datetime import datetime
from typing import Any, Optional, List, Dict, TYPE_CHECKING
from constants import GQL_OPERATIONS
if TYPE_CHECKING:
from twitch import Twitch
async def claim_drop(twitch: Twitch, claim_id: str) -> bool:
op = GQL_OPERATIONS["ClaimDrop"].with_variables(
{"input": {"dropInstanceID": claim_id}}
)
response = await twitch.gql_request(op)
data = response["data"]
if "errors" in data and data["errors"]:
return False
elif "claimDropRewards" in data:
if not data["claimDropRewards"]:
return False
elif (
data["claimDropRewards"]["status"]
in ["ELIGIBLE_FOR_ALL", "DROP_INSTANCE_ALREADY_CLAIMED"]
):
return True
return False
class Game:
def __init__(self, data: Dict[str, Any]):
self.id: int = int(data["id"])
self.name: str = data["name"]
def __eq__(self, other: object):
if isinstance(other, self.__class__):
return self.id == other.id
return NotImplemented
def __hash__(self) -> int:
return self.id
class BaseDrop:
def __init__(self, campaign: DropsCampaign, data: Dict[str, Any]):
self._twitch: Twitch = campaign._twitch
self.id = data["id"]
self.name: str = data["name"]
self.campaign: DropsCampaign = campaign
self.rewards: List[str] = [b["benefit"]["name"] for b in data["benefitEdges"]]
self.starts_at: datetime = datetime.strptime(data["startAt"], "%Y-%m-%dT%H:%M:%SZ")
self.ends_at: datetime = datetime.strptime(data["endAt"], "%Y-%m-%dT%H:%M:%SZ")
# If claim_id is not None, we can use it to claim the drop
self.claim_id: Optional[str] = data["self"]["dropInstanceID"]
self.is_claimed: bool = data["self"]["isClaimed"]
self._preconditions: bool = data["self"]["hasPreconditionsMet"]
@property
def can_earn(self) -> bool:
return (
self._preconditions # preconditions are met
and self.campaign.active # campaign is active
and not self.is_claimed # drop isn't already claimed
)
@property
def can_claim(self) -> bool:
return self.claim_id is not None and not self.is_claimed
async def claim(self) -> bool:
"""
Returns True if the claim succeeded, False otherwise.
"""
if not self.can_claim:
return False
assert self.claim_id is not None
self.is_claimed = await claim_drop(self._twitch, self.claim_id)
return self.is_claimed
class TimedDrop(BaseDrop):
def __init__(self, campaign: DropsCampaign, data: Dict[str, Any]):
super().__init__(campaign, data)
self.current_minutes: int = data["self"]["currentMinutesWatched"]
self.required_minutes: int = data["requiredMinutesWatched"]
if self.is_claimed:
# correct minutes for claimed drops
self.current_minutes = self.required_minutes
@property
def remaining_minutes(self):
return self.required_minutes - self.current_minutes
@property
def progress(self):
return self.current_minutes / self.required_minutes
def update(self, message: Dict[str, Any]):
# {"type": "drop-progress", data: {"current_progress_min": 3, "required_progress_min": 10}}
# {"type": "drop-claim", data: {"drop_instance_id": ...}}
msg_type = message["type"]
if msg_type == "drop-progress":
self.current_minutes = message["data"]["current_progress_min"]
self.required_minutes = message["data"]["required_progress_min"]
elif msg_type == "drop-claim":
self.claim_id = message["data"]["drop_instance_id"]
class DropsCampaign:
def __init__(self, twitch: Twitch, data: Dict[str, Any]):
self._twitch: Twitch = twitch
self.id: str = data["id"]
self.name: str = data["name"]
self.game: Game = Game(data["game"])
self.starts_at: datetime = datetime.strptime(data["startAt"], "%Y-%m-%dT%H:%M:%SZ")
self.ends_at: datetime = datetime.strptime(data["endAt"], "%Y-%m-%dT%H:%M:%SZ")
self.status: str = data["status"]
self.timed_drops: Dict[str, TimedDrop] = {
d["id"]: TimedDrop(self, d) for d in data["timeBasedDrops"]
}
@property
def active(self):
return self.status == "ACTIVE"
def get_drop(self, drop_id: str) -> Optional[TimedDrop]:
return self.timed_drops.get(drop_id)