Skip to content

Commit

Permalink
Add a prioritization strategy for games outside of the priority list
Browse files Browse the repository at this point in the history
  • Loading branch information
DevilXD committed Sep 11, 2024
1 parent ab37b0a commit a30e8d3
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 53 deletions.
7 changes: 7 additions & 0 deletions constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def _merge_vars(base_vars: JsonType, vars: JsonType) -> None:
URLType = NewType("URLType", str)
TopicProcess: TypeAlias = "abc.Callable[[int, JsonType], Any]"
# Values
MAX_INT = sys.maxsize
BASE_TOPICS = 3
MAX_WEBSOCKETS = 8
WS_TOPICS_LIMIT = 50
Expand Down Expand Up @@ -223,6 +224,12 @@ class State(Enum):
EXIT = auto()


class PriorityMode(Enum):
PRIORITY_ONLY = 0
ENDING_SOONEST = 1
LOW_AVBL_FIRST = 2


class GQLOperation(JsonType):
def __init__(self, name: str, sha256: str, *, variables: JsonType | None = None):
super().__init__(
Expand Down
57 changes: 37 additions & 20 deletions gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
WS_TOPICS_LIMIT,
OUTPUT_FORMATTER,
State,
PriorityMode,
)
if sys.platform == "win32":
from registry import RegistryKey, ValueType, ValueNotFound
Expand Down Expand Up @@ -388,18 +389,24 @@ def __init__(
self,
master: tk.Misc,
*args,
width_offset: int = 0,
width: int | None = None,
textvariable: tk.StringVar,
values: list[str] | tuple[str, ...],
command: abc.Callable[[tk.Event[SelectCombobox]], None] | None = None,
**kwargs,
) -> None:
if width is None:
width = max(len(v) for v in values)
width += width_offset
super().__init__(
master,
*args,
width=width,
values=values,
state="readonly",
exportselection=False,
textvariable=textvariable,
state="readonly",
**kwargs,
)
if command is not None:
Expand Down Expand Up @@ -1216,7 +1223,9 @@ def __init__(self, manager: GUIManager, master: ttk.Widget):
self._cache: ImageCache = manager._cache
self._settings: Settings = manager._twitch.settings
self._filters = {
"not_linked": IntVar(master, 1),
"not_linked": IntVar(
master, self._settings.priority_mode is PriorityMode.PRIORITY_ONLY
),
"upcoming": IntVar(master, 1),
"expired": IntVar(master, 0),
"excluded": IntVar(master, 0),
Expand Down Expand Up @@ -1304,7 +1313,7 @@ def _update_visibility(self, campaign: DropsCampaign):
excluded = bool(self._filters["excluded"].get())
upcoming = bool(self._filters["upcoming"].get())
finished = bool(self._filters["finished"].get())
priority_only = self._settings.priority_only
priority_only = self._settings.priority_mode is PriorityMode.PRIORITY_ONLY
if (
campaign.remaining_minutes > 0 # don't show sub-only campaigns
and (not_linked or campaign.linked)
Expand Down Expand Up @@ -1534,23 +1543,32 @@ class _SettingsVars(TypedDict):
proxy: StringVar
autostart: IntVar
language: StringVar
priority_only: IntVar
priority_mode: StringVar
tray_notifications: IntVar


class SettingsPanel:
AUTOSTART_NAME: str = "TwitchDropsMiner"
AUTOSTART_KEY: str = "HKCU/Software/Microsoft/Windows/CurrentVersion/Run"
PRIORITY_MODES: dict[PriorityMode, str] = {
PriorityMode.PRIORITY_ONLY: _("gui", "settings", "priority_modes", "priority_only"),
PriorityMode.ENDING_SOONEST: _("gui", "settings", "priority_modes", "ending_soonest"),
PriorityMode.LOW_AVBL_FIRST: _("gui", "settings", "priority_modes", "low_availability"),
}

def __init__(self, manager: GUIManager, master: ttk.Widget):
self._twitch = manager._twitch
self._settings: Settings = manager._twitch.settings
priority_mode = self._settings.priority_mode
if priority_mode not in self.PRIORITY_MODES:
priority_mode = PriorityMode.PRIORITY_ONLY
self._settings.priority_mode = priority_mode
self._vars: _SettingsVars = {
"autostart": IntVar(master, 0),
"language": StringVar(master, _.current),
"proxy": StringVar(master, str(self._settings.proxy)),
"tray": IntVar(master, self._settings.autostart_tray),
"autostart": IntVar(master, 0),
"priority_only": IntVar(master, self._settings.priority_only),
"priority_mode": StringVar(master, self.PRIORITY_MODES[priority_mode]),
"tray_notifications": IntVar(master, self._settings.tray_notifications),
}
self._game_names: set[str] = set()
Expand All @@ -1577,7 +1595,6 @@ def __init__(self, manager: GUIManager, master: ttk.Widget):
ttk.Label(language_frame, text="Language 🌐 (requires restart): ").grid(column=0, row=0)
SelectCombobox(
language_frame,
width=12,
values=list(_.languages),
textvariable=self._vars["language"],
command=lambda e: setattr(self._settings, "language", self._vars["language"].get()),
Expand Down Expand Up @@ -1607,10 +1624,13 @@ def __init__(self, manager: GUIManager, master: ttk.Widget):
command=self.update_notifications,
).grid(column=1, row=irow, sticky="w")
ttk.Label(
checkboxes_frame, text=_("gui", "settings", "general", "priority_only")
checkboxes_frame, text=_("gui", "settings", "general", "priority_mode")
).grid(column=0, row=(irow := irow + 1), sticky="e")
ttk.Checkbutton(
checkboxes_frame, variable=self._vars["priority_only"], command=self.priority_only
SelectCombobox(
checkboxes_frame,
command=self.priority_mode,
textvariable=self._vars["priority_mode"],
values=list(self.PRIORITY_MODES.values()),
).grid(column=1, row=irow, sticky="w")

# proxy frame
Expand Down Expand Up @@ -1808,13 +1828,6 @@ def set_games(self, games: set[Game]) -> None:
self.update_excluded_choices()
self.update_priority_choices()

def priorities(self) -> dict[str, int]:
# NOTE: we shift the indexes so that 0 can be used as the default one
size = self._priority_list.size()
return {
game_name: size - i for i, game_name in enumerate(self._priority_list.get(0, "end"))
}

def priority_add(self) -> None:
game_name: str = self._priority_entry.get()
if not game_name:
Expand Down Expand Up @@ -1868,8 +1881,12 @@ def priority_delete(self) -> None:
self._settings.alter()
self.update_priority_choices()

def priority_only(self) -> None:
self._settings.priority_only = bool(self._vars["priority_only"].get())
def priority_mode(self, event: tk.Event[ttk.Combobox]) -> None:
mode_name: str = self._vars["priority_mode"].get()
for value, name in self.PRIORITY_MODES.items():
if mode_name == name:
self._settings.priority_mode = value
break

def exclude_add(self) -> None:
game_name: str = self._exclude_entry.get()
Expand Down Expand Up @@ -2390,11 +2407,11 @@ async def main(exit_event: asyncio.Event):
proxy=URL(),
autostart=False,
language="English",
priority_only=False,
autostart_tray=False,
exclude={"Lit Game"},
tray_notifications=True,
alter=lambda: None,
priority_mode=PriorityMode.PRIORITY_ONLY,
)
)
mock.change_state = lambda state: mock.gui.print(f"State change: {state.value}")
Expand Down
13 changes: 13 additions & 0 deletions inventory.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import re
import math
from itertools import chain
from typing import TYPE_CHECKING
from functools import cached_property
Expand Down Expand Up @@ -232,6 +233,14 @@ def progress(self) -> float:
return 1.0
return self.current_minutes / self.required_minutes

@property
def availability(self) -> float:
if not self._base_can_earn():
# this verifies "self.total_remaining_minutes > 0" and "now < self.ends_at"
return math.inf
now = datetime.now(timezone.utc)
return ((self.ends_at - now).total_seconds() / 60) / self.total_remaining_minutes

def _base_earn_conditions(self) -> bool:
return super()._base_earn_conditions() and self.required_minutes > 0

Expand Down Expand Up @@ -346,6 +355,10 @@ def remaining_minutes(self) -> int:
def progress(self) -> float:
return sum(d.progress for d in self.drops) / self.total_drops

@property
def availability(self) -> float:
return min(d.availability for d in self.drops)

def _on_claim(self) -> None:
invalidate_cache(self, "finished", "claimed_drops", "remaining_drops")
for drop in self.drops:
Expand Down
8 changes: 4 additions & 4 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from yarl import URL

from utils import json_load, json_save
from constants import SETTINGS_PATH, DEFAULT_LANG
from constants import SETTINGS_PATH, DEFAULT_LANG, PriorityMode

if TYPE_CHECKING:
from main import ParsedArgs
Expand All @@ -16,21 +16,21 @@ class SettingsFile(TypedDict):
language: str
exclude: set[str]
priority: list[str]
priority_only: bool
autostart_tray: bool
connection_quality: int
tray_notifications: bool
priority_mode: PriorityMode


default_settings: SettingsFile = {
"proxy": URL(),
"priority": [],
"exclude": set(),
"priority_only": True,
"autostart_tray": False,
"connection_quality": 1,
"language": DEFAULT_LANG,
"tray_notifications": True,
"priority_mode": PriorityMode.PRIORITY_ONLY,
}


Expand All @@ -48,10 +48,10 @@ class Settings:
language: str
exclude: set[str]
priority: list[str]
priority_only: bool
autostart_tray: bool
connection_quality: int
tray_notifications: bool
priority_mode: PriorityMode

PASSTHROUGH = ("_settings", "_args", "_altered")

Expand Down
16 changes: 14 additions & 2 deletions translate.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,19 @@ class GUISettingsGeneral(TypedDict):
autostart: str
tray: str
tray_notifications: str
priority_only: str
priority_mode: str
proxy: str


class GUIPriorityModes(TypedDict):
priority_only: str
ending_soonest: str
low_availability: str


class GUISettings(TypedDict):
general: GUISettingsGeneral
priority_modes: GUIPriorityModes
game_name: str
priority: str
exclude: str
Expand Down Expand Up @@ -362,9 +369,14 @@ class Translation(TypedDict):
"autostart": "Autostart: ",
"tray": "Autostart into tray: ",
"tray_notifications": "Tray notifications: ",
"priority_only": "Priority Only: ",
"priority_mode": "Priority mode: ",
"proxy": "Proxy (requires restart):",
},
"priority_modes": {
"priority_only": "Priority list only",
"ending_soonest": "Ending soonest",
"low_availability": "Low availability first",
},
"game_name": "Game name",
"priority": "Priority",
"exclude": "Exclude",
Expand Down
Loading

0 comments on commit a30e8d3

Please sign in to comment.