From 17e2987ab3c29d17b381a490275dbd194188ce72 Mon Sep 17 00:00:00 2001 From: "Mike A." Date: Wed, 16 Jul 2025 19:59:04 +0200 Subject: [PATCH] fix(reports): Update examples to use new api --- examples/_login.py | 43 +++++++++++++++++++++++---------- examples/fetch_reports.py | 26 ++++++++++++++------ examples/fetch_reports_async.py | 24 +++++++++++++----- examples/real_airtag.py | 23 ++++++++++++++---- 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/examples/_login.py b/examples/_login.py index 6891b19..c8397eb 100644 --- a/examples/_login.py +++ b/examples/_login.py @@ -1,15 +1,14 @@ # ruff: noqa: ASYNC230 +from __future__ import annotations from findmy.reports import ( AppleAccount, AsyncAppleAccount, - BaseAnisetteProvider, LoginState, SmsSecondFactorMethod, TrustedDeviceSecondFactorMethod, ) - -ACCOUNT_STORE = "account.json" +from findmy.reports.anisette import LocalAnisetteProvider, RemoteAnisetteProvider def _login_sync(account: AppleAccount) -> None: @@ -66,27 +65,45 @@ async def _login_async(account: AsyncAppleAccount) -> None: await method.submit(code) -def get_account_sync(anisette: BaseAnisetteProvider) -> AppleAccount: +def get_account_sync( + store_path: str, + anisette_url: str | None, + libs_path: str | None, +) -> AppleAccount: """Tries to restore a saved Apple account, or prompts the user for login otherwise. (sync)""" - acc = AppleAccount(anisette=anisette) - acc_store = "account.json" try: - acc.from_json(acc_store) + acc = AppleAccount.from_json(store_path, anisette_libs_path=libs_path) except FileNotFoundError: + ani = ( + LocalAnisetteProvider(libs_path=libs_path) + if anisette_url is None + else RemoteAnisetteProvider(anisette_url) + ) + acc = AppleAccount(ani) _login_sync(acc) - acc.to_json(acc_store) + + acc.to_json(store_path) return acc -async def get_account_async(anisette: BaseAnisetteProvider) -> AsyncAppleAccount: +async def get_account_async( + store_path: str, + anisette_url: str | None, + libs_path: str | None, +) -> AsyncAppleAccount: """Tries to restore a saved Apple account, or prompts the user for login otherwise. (async)""" - acc = AsyncAppleAccount(anisette=anisette) - acc_store = "account.json" try: - acc.from_json(acc_store) + acc = AsyncAppleAccount.from_json(store_path, anisette_libs_path=libs_path) except FileNotFoundError: + ani = ( + LocalAnisetteProvider(libs_path=libs_path) + if anisette_url is None + else RemoteAnisetteProvider(anisette_url) + ) + acc = AsyncAppleAccount(ani) await _login_async(acc) - acc.to_json(acc_store) + + acc.to_json(store_path) return acc diff --git a/examples/fetch_reports.py b/examples/fetch_reports.py index 3758417..6eff849 100644 --- a/examples/fetch_reports.py +++ b/examples/fetch_reports.py @@ -4,19 +4,28 @@ import sys from _login import get_account_sync from findmy import KeyPair -from findmy.reports import RemoteAnisetteProvider -# URL to (public or local) anisette server -ANISETTE_SERVER = "http://localhost:6969" +# Path where login session will be stored. +# This is necessary to avoid generating a new session every time we log in. +STORE_PATH = "account.json" + +# URL to LOCAL anisette server. Set to None to use built-in Anisette generator instead (recommended) +# IF YOU USE A PUBLIC SERVER, DO NOT COMPLAIN THAT YOU KEEP RUNNING INTO AUTHENTICATION ERRORS! +# If you change this value, make sure to remove the account store file. +ANISETTE_SERVER = None + +# Path where Anisette libraries will be stored. +# This is only relevant when using the built-in Anisette server. +# It can be omitted (set to None) to avoid saving to disk, +# but specifying a path is highly recommended to avoid downloading the bundle on every run. +ANISETTE_LIBS_PATH = "ani_libs.bin" logging.basicConfig(level=logging.INFO) def fetch_reports(priv_key: str) -> int: key = KeyPair.from_b64(priv_key) - acc = get_account_sync( - RemoteAnisetteProvider(ANISETTE_SERVER), - ) + acc = get_account_sync(STORE_PATH, ANISETTE_SERVER, ANISETTE_LIBS_PATH) print(f"Logged in as: {acc.account_name} ({acc.first_name} {acc.last_name})") @@ -25,7 +34,10 @@ def fetch_reports(priv_key: str) -> int: for report in sorted(reports): print(report) - return 1 + # Make sure to save account state when you're done! + acc.to_json(STORE_PATH) + + return 0 if __name__ == "__main__": diff --git a/examples/fetch_reports_async.py b/examples/fetch_reports_async.py index d267a6d..b5508b9 100644 --- a/examples/fetch_reports_async.py +++ b/examples/fetch_reports_async.py @@ -5,19 +5,28 @@ import sys from _login import get_account_async from findmy import KeyPair -from findmy.reports import RemoteAnisetteProvider -# URL to (public or local) anisette server -ANISETTE_SERVER = "http://localhost:6969" +# Path where login session will be stored. +# This is necessary to avoid generating a new session every time we log in. +STORE_PATH = "account.json" + +# URL to LOCAL anisette server. Set to None to use built-in Anisette generator instead (recommended) +# IF YOU USE A PUBLIC SERVER, DO NOT COMPLAIN THAT YOU KEEP RUNNING INTO AUTHENTICATION ERRORS! +# If you change this value, make sure to remove the account store file. +ANISETTE_SERVER = None + +# Path where Anisette libraries will be stored. +# This is only relevant when using the built-in Anisette server. +# It can be omitted (set to None) to avoid saving to disk, +# but specifying a path is highly recommended to avoid downloading the bundle on every run. +ANISETTE_LIBS_PATH = "ani_libs.bin" logging.basicConfig(level=logging.INFO) async def fetch_reports(priv_key: str) -> int: key = KeyPair.from_b64(priv_key) - acc = await get_account_async( - RemoteAnisetteProvider(ANISETTE_SERVER), - ) + acc = await get_account_async(STORE_PATH, ANISETTE_SERVER, ANISETTE_LIBS_PATH) try: print(f"Logged in as: {acc.account_name} ({acc.first_name} {acc.last_name})") @@ -29,6 +38,9 @@ async def fetch_reports(priv_key: str) -> int: finally: await acc.close() + # Make sure to save account state when you're done! + acc.to_json(STORE_PATH) + return 0 diff --git a/examples/real_airtag.py b/examples/real_airtag.py index 5eeb858..ff91378 100644 --- a/examples/real_airtag.py +++ b/examples/real_airtag.py @@ -11,10 +11,21 @@ from pathlib import Path from _login import get_account_sync from findmy import FindMyAccessory -from findmy.reports import RemoteAnisetteProvider -# URL to (public or local) anisette server -ANISETTE_SERVER = "http://localhost:6969" +# Path where login session will be stored. +# This is necessary to avoid generating a new session every time we log in. +STORE_PATH = "account.json" + +# URL to LOCAL anisette server. Set to None to use built-in Anisette generator instead (recommended) +# IF YOU USE A PUBLIC SERVER, DO NOT COMPLAIN THAT YOU KEEP RUNNING INTO AUTHENTICATION ERRORS! +# If you change this value, make sure to remove the account store file. +ANISETTE_SERVER = None + +# Path where Anisette libraries will be stored. +# This is only relevant when using the built-in Anisette server. +# It can be omitted (set to None) to avoid saving to disk, +# but specifying a path is highly recommended to avoid downloading the bundle on every run. +ANISETTE_LIBS_PATH = "ani_libs.bin" logging.basicConfig(level=logging.INFO) @@ -26,8 +37,7 @@ def main(plist_path: str) -> int: # Step 1: log into an Apple account print("Logging into account") - anisette = RemoteAnisetteProvider(ANISETTE_SERVER) - acc = get_account_sync(anisette) + acc = get_account_sync(STORE_PATH, ANISETTE_SERVER, ANISETTE_LIBS_PATH) # step 2: fetch reports! print("Fetching reports") @@ -39,6 +49,9 @@ def main(plist_path: str) -> int: for report in sorted(reports): print(f" - {report}") + # step 4: save current account state to disk + acc.to_json(STORE_PATH) + return 0