From c02f08a7677b0964df7a8fcf13a12bbf5d969be1 Mon Sep 17 00:00:00 2001 From: "Pascal P." Date: Thu, 6 Aug 2026 08:40:31 +0200 Subject: [PATCH] cleaned up auth (only use supabase) --- README.md | 7 +++ pyproject.toml | 1 + src/app.py | 37 +++++++++++- src/services/wiki_masters.py | 114 +++++++++++++++++++---------------- uv.lock | 35 +++++++++++ 5 files changed, 138 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index e69de29..98aacdb 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,7 @@ +TODO: + +- [ ] Schedule opening +- [ ] Notification +- [ ] Complete API (for selling) +- [ ] Add a scoring for cards pulled +- [ ] Multiusers \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index ba0cdff..059c453 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,6 +5,7 @@ description = "Add your description here" readme = "README.md" requires-python = ">=3.13" dependencies = [ + "apscheduler>=3.11.3", "dotenv>=0.9.9", "httpx>=0.28.1", "loguru>=0.7.3", diff --git a/src/app.py b/src/app.py index 2def5ec..a24ce4d 100644 --- a/src/app.py +++ b/src/app.py @@ -4,9 +4,10 @@ from loguru import logger from pathlib import Path # from sqlalchemy import create_engine -from .lib.utils import EveryRunRotator +from .lib.utils import EveryRunRotator, HumanReadable as hr from .services.wiki_masters import WikiMastersClient from dotenv import load_dotenv +from datetime import datetime, timedelta, timezone class App: @@ -42,8 +43,38 @@ class App: load_dotenv() - truc = WikiMastersClient() - truc.login(environ.get("ACCOUNT_EMAIL"), environ.get("ACCOUNT_PASSWORD")) + wm_client = WikiMastersClient() + user = wm_client.login( + environ.get("ACCOUNT_EMAIL"), environ.get("ACCOUNT_PASSWORD") + ) + + d = user.sync() + logger.debug("sync user {}", d.username) + packs_remaining = d.packs_remaining + packs_last_regen_at = d.packs_last_regen_at + + if packs_remaining == 10: + packs_remaining, packs_last_regen_at = wm_client.open_pack( + user.auth_cookies + ) + + # Does this math still works when we are opening the 9th pack ? + next_pack_in = WikiMastersClient.ONE_PACK_TIME - ( + datetime.now(tz=timezone.utc) - packs_last_regen_at + ) + next_run_in = ( + next_pack_in + + WikiMastersClient.ONE_PACK_TIME * (9 - packs_remaining) + - timedelta(seconds=10) + ) + + logger.info( + "Next pack is in {}", + hr.seconds_to_human(next_pack_in.total_seconds()), + ) + logger.debug( + "We should schedule in {}", hr.seconds_to_human(next_run_in.total_seconds()) + ) async def main(): diff --git a/src/services/wiki_masters.py b/src/services/wiki_masters.py index 5f4539e..2bb83b9 100644 --- a/src/services/wiki_masters.py +++ b/src/services/wiki_masters.py @@ -1,9 +1,11 @@ import httpx +from dataclasses import dataclass from supabase import create_client, Client +from supabase_auth import Session from loguru import logger -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta from ..lib.supabase_cookie import get_auth_cookies -from ..lib.utils import HumanReadable as hr +from typing import Self # supabase ? # https://cyrxjeppjqsxxjayfrur.supabase.co/auth/v1/token?grant_type=password # https://cyrxjeppjqsxxjayfrur.supabase.co/auth/v1/user @@ -11,6 +13,44 @@ from ..lib.utils import HumanReadable as hr # https://cyrxjeppjqsxxjayfrur.supabase.co/rest/v1/rpc/sync_profile_packs +@dataclass +class SyncDataSubset: + _id: str + username: str + packs_remaining: int + packs_last_regen_at: datetime + + @staticmethod + def from_json(d: dict) -> Self: + return SyncDataSubset( + d["id"], + d["username"], + d["packs_remaining"], + datetime.fromisoformat(d["packs_last_regen_at"]), + ) + + +@dataclass +class UserSession: + username: str + sb_client: Client + + @property + def auth_cookies(self): + return get_auth_cookies(self.session.model_dump_json().encode("utf-8")) + + @property + def session(self): + return self.sb_client.auth.get_session() + + def sync(self) -> SyncDataSubset: + response = self.sb_client.postgrest.rpc( + "sync_profile_packs", {"user_id": self.session.user.id} + ).execute() + + return SyncDataSubset.from_json(response.data[0]) + + class WikiMastersClient: BASE_URL = "https://www.wiki-masters.com" @@ -22,12 +62,9 @@ class WikiMastersClient: def __init__(self) -> None: self.logger = logger.bind(service=self.__class__.__name__) + self.users: dict[str, UserSession] = {} - self.logger.info("creating client for supabase") - self.sb_client: Client = create_client(self.SUPABASE_URL, self.SUPABASE_KEY) - self.logger.info("done creating client for supabase: {}", self.sb_client) - - def open_pack(self, cookies): + def open_pack(self, cookies) -> tuple[int, str]: url = f"{self.BASE_URL}/api/packs/open" logger.debug("trying to request a pull (cookies:{})", cookies) @@ -37,57 +74,28 @@ class WikiMastersClient: cookies=cookies, ) - if response.status_code == 200: - logger.debug("trying to pull: {}", response.json()) - else: + if response.status_code != 200: logger.error( "error while opening [{}] {}", response.status_code, response.content ) + raise Exception("Could not pull :'(") - def login(self, email, password): - url = f"{self.SUPABASE_URL}/auth/v1/token?grant_type=password" - response = httpx.post( - url, - json={"email": email, "password": password, "gotrue_meta_security": {}}, - headers={ - "ApiKey": self.SUPABASE_KEY, - "Authorization": f"Bearer {self.SUPABASE_KEY}", - }, + pull_data = response.json() + logger.debug("trying to pull: {}", pull_data) + + return (pull_data["packs_remaining"], datetime.fromisoformat(pull_data["packs_last_regen_at"])) + + def login(self, email, password) -> UserSession: + sb_client = create_client(self.SUPABASE_URL, self.SUPABASE_KEY) + response = sb_client.auth.sign_in_with_password( + {"email": email, "password": password} ) - logger.debug("trying to login {}", response) - if response.status_code == 200: - cookies_compute = get_auth_cookies(response.content) - # TODO: save cookies for the current user + if not response.session: + raise Exception(f"Login failed for {email}") - content = response.json() - session = self.sb_client.auth.set_session( - content["access_token"], content["refresh_token"] - ) - username = session.user.user_metadata["username"] - self.logger.debug("connected with supabase as {}", username) + session = response.session + username = session.user.user_metadata["username"] + logger.debug("logged user {}", username) - response = self.sb_client.postgrest.rpc( - "sync_profile_packs", {"user_id": session.user.id} - ).execute() - self.logger.info("info sync: {}", response) - - d = response.data[0] - _id = d["id"] - _username = d["username"] - _packs_remaining = d["packs_remaining"] - _packs_last_regen_at = datetime.fromisoformat( - d["packs_last_regen_at"] - ) - - # Does this math still works when we are opening the 9th pack ? - next_pack_in = WikiMastersClient.ONE_PACK_TIME - ( - datetime.now(tz=timezone.utc) - _packs_last_regen_at - ) - next_run_in = next_pack_in + WikiMastersClient.ONE_PACK_TIME * (9 - _packs_remaining) - timedelta(seconds=10) - - self.logger.info( - "Next pack is in {}", - hr.seconds_to_human(next_pack_in.total_seconds()), - ) - self.logger.debug("We should schedule in {}", hr.seconds_to_human(next_run_in.total_seconds())) + return UserSession(username, sb_client) diff --git a/uv.lock b/uv.lock index 061beec..2d98caa 100644 --- a/uv.lock +++ b/uv.lock @@ -23,6 +23,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/da/35/f2287558c17e29fafc8ef3daf819bb9834061cfa43bff8014f7df7f63bdc/anyio-4.14.2-py3-none-any.whl", hash = "sha256:9f505dda5ac9f0c8309b5e8bd445a8c2bf7246f3ce950121e45ea15bc41d1494", size = 125813, upload-time = "2026-07-12T20:29:05.763Z" }, ] +[[package]] +name = "apscheduler" +version = "3.11.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzlocal" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8c/6b/eeff360196bb20b312c9e762a820fd1b2c6d809466c755ef57863478e454/apscheduler-3.11.3.tar.gz", hash = "sha256:cd2fcc9330039a81a5893472ad49facf23a6d5604cbe1d918c835c6de7834d5a", size = 110312, upload-time = "2026-06-28T19:39:22.493Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/c9/8638db32514dbb9157b3d82680c6faea89283523edf9ed2415ea3884f2ae/apscheduler-3.11.3-py3-none-any.whl", hash = "sha256:bbeb2ec02d23d3c06a6c07ed7f0f3939ada6680eb121fae809a69bb42c537a30", size = 66024, upload-time = "2026-06-28T19:39:20.982Z" }, +] + [[package]] name = "certifi" version = "2026.7.22" @@ -753,6 +765,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, ] +[[package]] +name = "tzdata" +version = "2026.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/92/ff/5a28bdfd8c3ebec42564ac7d0e54ca3db65044a9314a97f9564fa7a1e926/tzdata-2026.3.tar.gz", hash = "sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415", size = 198674, upload-time = "2026-07-10T08:50:37.887Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/6d/b53b99a9f2766d095985947a5782f1702cabb129a34f7a802d7197af832f/tzdata-2026.3-py2.py3-none-any.whl", hash = "sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931", size = 348168, upload-time = "2026-07-10T08:50:36.46Z" }, +] + +[[package]] +name = "tzlocal" +version = "5.4.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "tzdata", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/81/5b/879b2f932adfa7a053c360d50bc896c977fa6426109185f7c12ebdd0cb9d/tzlocal-5.4.4.tar.gz", hash = "sha256:8dbb8660838688a7b6ba4fed31d18dedf842afb4d47ca050d6d891c2c15f3be4", size = 31170, upload-time = "2026-06-29T08:03:40.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/a4/017a7a6cbe387d961a688ec31364ae60a5c4e22c96ae9921b79a947c855d/tzlocal-5.4.4-py3-none-any.whl", hash = "sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15", size = 18115, upload-time = "2026-06-29T08:03:38.666Z" }, +] + [[package]] name = "websockets" version = "15.0.1" @@ -778,6 +811,7 @@ name = "wiki-bot" version = "0.1.0" source = { virtual = "." } dependencies = [ + { name = "apscheduler" }, { name = "dotenv" }, { name = "httpx" }, { name = "loguru" }, @@ -787,6 +821,7 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "apscheduler", specifier = ">=3.11.3" }, { name = "dotenv", specifier = ">=0.9.9" }, { name = "httpx", specifier = ">=0.28.1" }, { name = "loguru", specifier = ">=0.7.3" },