cleaned up auth (only use supabase)
This commit is contained in:
parent
b396602dee
commit
c02f08a767
|
|
@ -0,0 +1,7 @@
|
|||
TODO:
|
||||
|
||||
- [ ] Schedule opening
|
||||
- [ ] Notification
|
||||
- [ ] Complete API (for selling)
|
||||
- [ ] Add a scoring for cards pulled
|
||||
- [ ] Multiusers
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
37
src/app.py
37
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():
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
)
|
||||
session = response.session
|
||||
username = session.user.user_metadata["username"]
|
||||
self.logger.debug("connected with supabase as {}", 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)
|
||||
|
|
|
|||
35
uv.lock
35
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" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue