Login flow now uses the returned verification_uri;

to prefill the user_code on the activation page
This commit is contained in:
DevilXD committed 2025-02-03 21:31:06 +01:00
1 parent 689afdddbe
commit c4cfa5372f
3 files changed
+13 -11

No files matched your search

+2 -2
View File
@@ -581,7 +581,7 @@ class LoginForm:
continue continue
return login_data return login_data
async def ask_enter_code(self, user_code: str) -> None: async def ask_enter_code(self, page_url: URL, user_code: str) -> None:
self.update(_("gui", "login", "required"), None) self.update(_("gui", "login", "required"), None)
# ensure the window isn't hidden into tray when this runs # ensure the window isn't hidden into tray when this runs
self._manager.grab_attention(sound=False) self._manager.grab_attention(sound=False)
@@ -589,7 +589,7 @@ class LoginForm:
await self.wait_for_login_press() await self.wait_for_login_press()
self._manager.print(f"Enter this code on the Twitch's device activation page: {user_code}") self._manager.print(f"Enter this code on the Twitch's device activation page: {user_code}")
await asyncio.sleep(4) await asyncio.sleep(4)
webopen("https://www.twitch.tv/activate") webopen(page_url)
def update(self, status: str, user_id: int | None): def update(self, status: str, user_id: int | None):
if user_id is not None: if user_id is not None:
+4 -3
View File
@@ -128,6 +128,7 @@ class _AuthState:
} }
while True: while True:
try: try:
now = datetime.now(timezone.utc)
async with self._twitch.request( async with self._twitch.request(
"POST", "https://id.twitch.tv/oauth2/device", headers=headers, data=payload "POST", "https://id.twitch.tv/oauth2/device", headers=headers, data=payload
) as response: ) as response:
@@ -136,17 +137,17 @@ class _AuthState:
# "expires_in": 1800, # "expires_in": 1800,
# "interval": 5, # "interval": 5,
# "user_code": "8 chars [A-Z]", # "user_code": "8 chars [A-Z]",
# "verification_uri": "https://www.twitch.tv/activate" # "verification_uri": "https://www.twitch.tv/activate?device-code=ABCDEFGH"
# } # }
now = datetime.now(timezone.utc)
response_json: JsonType = await response.json() response_json: JsonType = await response.json()
device_code: str = response_json["device_code"] device_code: str = response_json["device_code"]
user_code: str = response_json["user_code"] user_code: str = response_json["user_code"]
interval: int = response_json["interval"] interval: int = response_json["interval"]
verification_uri: URL = URL(response_json["verification_uri"])
expires_at = now + timedelta(seconds=response_json["expires_in"]) expires_at = now + timedelta(seconds=response_json["expires_in"])
# Print the code to the user, open them the activate page so they can type it in # Print the code to the user, open them the activate page so they can type it in
await login_form.ask_enter_code(user_code) await login_form.ask_enter_code(verification_uri, user_code)
payload = { payload = {
"client_id": self._twitch._client_type.CLIENT_ID, "client_id": self._twitch._client_type.CLIENT_ID,
+7 -6
View File
@@ -21,7 +21,7 @@ from datetime import datetime, timezone
from collections import abc, OrderedDict from collections import abc, OrderedDict
from typing import Any, Literal, Callable, Generic, Mapping, TypeVar, ParamSpec, cast from typing import Any, Literal, Callable, Generic, Mapping, TypeVar, ParamSpec, cast
import yarl from yarl import URL
from PIL.ImageTk import PhotoImage from PIL.ImageTk import PhotoImage
from PIL import Image as Image_module from PIL import Image as Image_module
@@ -176,7 +176,7 @@ def _serialize(obj: Any) -> Any:
# NOTE: IntEnum cannot be used, as it will get serialized as a plain integer, # NOTE: IntEnum cannot be used, as it will get serialized as a plain integer,
# then loaded back as an integer as well. # then loaded back as an integer as well.
d = obj.value d = obj.value
elif isinstance(obj, yarl.URL): elif isinstance(obj, URL):
d = str(obj) d = str(obj)
else: else:
raise TypeError(obj) raise TypeError(obj)
@@ -190,7 +190,7 @@ def _serialize(obj: Any) -> Any:
_MISSING = object() _MISSING = object()
SERIALIZE_ENV: dict[str, Callable[[Any], object]] = { SERIALIZE_ENV: dict[str, Callable[[Any], object]] = {
"set": set, "set": set,
"URL": yarl.URL, "URL": URL,
"PriorityMode": PriorityMode, "PriorityMode": PriorityMode,
"datetime": lambda d: datetime.fromtimestamp(d, timezone.utc), "datetime": lambda d: datetime.fromtimestamp(d, timezone.utc),
} }
@@ -254,7 +254,8 @@ def json_save(path: Path, contents: Mapping[Any, Any], *, sort: bool = False) ->
json.dump(contents, file, default=_serialize, sort_keys=sort, indent=4) json.dump(contents, file, default=_serialize, sort_keys=sort, indent=4)
def webopen(url: str): def webopen(url: URL | str):
url_str = str(url)
if IS_PACKAGED and sys.platform == "linux": if IS_PACKAGED and sys.platform == "linux":
# https://pyinstaller.org/en/stable/ # https://pyinstaller.org/en/stable/
# runtime-information.html#ld-library-path-libpath-considerations # runtime-information.html#ld-library-path-libpath-considerations
@@ -268,7 +269,7 @@ def webopen(url: str):
# pop current # pop current
os.environ.pop(ld_env) os.environ.pop(ld_env)
webbrowser.open_new_tab(url) webbrowser.open_new_tab(url_str)
if ld_path_curr is not None: if ld_path_curr is not None:
os.environ[ld_env] = ld_path_curr os.environ[ld_env] = ld_path_curr
@@ -276,7 +277,7 @@ def webopen(url: str):
# pop original # pop original
os.environ.pop(ld_env) os.environ.pop(ld_env)
else: else:
webbrowser.open_new_tab(url) webbrowser.open_new_tab(url_str)
class ExponentialBackoff: class ExponentialBackoff: