mirror of
https://github.com/rangermix/TwitchDropsMiner.git
synced 2026-05-27 07:29:39 +00:00
129 lines
4.5 KiB
Python
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)
|