""" Example: Using Finance Bot API from Python This file shows how to use the new API endpoints to authenticate bot users and make API calls. """ import aiohttp import asyncio import json import logging from typing import Optional, Dict, Any logger = logging.getLogger(__name__) class FinanceBotAPIClient: """ Client for Finance Bot API with authentication support. Features: - Email/password registration - Telegram user quick registration - Telegram binding/linking - Token management and refresh - Authenticated API calls """ def __init__(self, api_base_url: str = "http://web:8000"): self.api_base_url = api_base_url self.session: Optional[aiohttp.ClientSession] = None async def start(self): """Start HTTP session""" self.session = aiohttp.ClientSession() async def close(self): """Close HTTP session""" if self.session: await self.session.close() # ============ EMAIL AUTHENTICATION ============ async def register_email_user( self, email: str, password: str, first_name: Optional[str] = None, last_name: Optional[str] = None, ) -> Dict[str, Any]: """ Register new user with email and password. Returns: { "success": True, "user_id": 123, "access_token": "eyJ...", "refresh_token": "eyJ...", "expires_in": 900 } """ url = f"{self.api_base_url}/api/v1/auth/register" payload = { "email": email, "password": password, "first_name": first_name, "last_name": last_name, } async with self.session.post(url, json=payload) as resp: return await resp.json() async def login_email_user( self, email: str, password: str, ) -> Dict[str, Any]: """ Login user with email and password. Returns: { "access_token": "eyJ...", "refresh_token": "eyJ...", "user_id": 123, "expires_in": 900 } """ url = f"{self.api_base_url}/api/v1/auth/login" payload = { "email": email, "password": password, } async with self.session.post(url, json=payload) as resp: return await resp.json() # ============ TELEGRAM AUTHENTICATION ============ async def quick_register_telegram_user( self, chat_id: int, username: Optional[str] = None, first_name: Optional[str] = None, last_name: Optional[str] = None, ) -> Dict[str, Any]: """ Quick register Telegram user (one API call). Perfect for bot /register command. Returns: { "success": True, "user_id": 123, "jwt_token": "eyJ...", "created": True } """ url = f"{self.api_base_url}/api/v1/auth/telegram/register" params = { "chat_id": chat_id, "username": username or f"user_{chat_id}", "first_name": first_name, "last_name": last_name, } # Remove None values params = {k: v for k, v in params.items() if v is not None} async with self.session.post(url, params=params) as resp: return await resp.json() async def start_telegram_binding( self, chat_id: int, ) -> Dict[str, Any]: """ Start Telegram binding process (for linking existing accounts). Returns: { "code": "PgmL5ZD8vK2mN3oP4qR5sT6uV7wX8yZ9", "expires_in": 600 } """ url = f"{self.api_base_url}/api/v1/auth/telegram/start" payload = {"chat_id": chat_id} async with self.session.post(url, json=payload) as resp: return await resp.json() async def get_token_for_telegram_user( self, chat_id: int, ) -> Dict[str, Any]: """ Get JWT token for Telegram user. Use after binding confirmation or registration. Returns: { "success": True, "access_token": "eyJ...", "expires_in": 900, "user_id": 123 } """ url = f"{self.api_base_url}/api/v1/auth/token/get" payload = {"chat_id": chat_id} async with self.session.post(url, json=payload) as resp: return await resp.json() async def refresh_telegram_token( self, chat_id: int, ) -> Dict[str, Any]: """ Refresh token for Telegram user. Returns: { "success": True, "access_token": "eyJ...", "expires_in": 900, "user_id": 123 } """ url = f"{self.api_base_url}/api/v1/auth/token/refresh-telegram" params = {"chat_id": chat_id} async with self.session.post(url, params=params) as resp: return await resp.json() # ============ API CALLS WITH AUTHENTICATION ============ async def make_authenticated_request( self, method: str, endpoint: str, access_token: str, data: Optional[Dict] = None, params: Optional[Dict] = None, ) -> Dict[str, Any]: """ Make authenticated API request. Args: method: GET, POST, PUT, DELETE endpoint: /api/v1/accounts, etc. access_token: JWT token from login/register data: Request body (for POST/PUT) params: Query parameters Returns: API response JSON """ url = f"{self.api_base_url}{endpoint}" headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json", } async with self.session.request( method, url, json=data, params=params, headers=headers, ) as resp: return await resp.json() async def get_accounts( self, access_token: str, family_id: Optional[int] = None, ) -> Dict[str, Any]: """Get user's accounts""" params = {} if family_id: params["family_id"] = family_id return await self.make_authenticated_request( "GET", "/api/v1/accounts", access_token, params=params, ) async def get_balance( self, access_token: str, account_id: int, ) -> Dict[str, Any]: """Get account balance""" return await self.make_authenticated_request( "GET", f"/api/v1/accounts/{account_id}", access_token, ) async def create_transaction( self, access_token: str, amount: float, category: str, description: Optional[str] = None, **kwargs ) -> Dict[str, Any]: """Create new transaction""" data = { "amount": amount, "category": category, "description": description, **kwargs } return await self.make_authenticated_request( "POST", "/api/v1/transactions", access_token, data=data, ) # ============ TOKEN REFRESH ============ async def refresh_access_token( self, refresh_token: str, ) -> Dict[str, Any]: """ Refresh access token using refresh token. Returns: { "access_token": "eyJ...", "expires_in": 900 } """ url = f"{self.api_base_url}/api/v1/auth/refresh" payload = {"refresh_token": refresh_token} async with self.session.post(url, json=payload) as resp: return await resp.json() # ============ USAGE EXAMPLES ============ async def example_quick_registration(): """Example: Register Telegram user with one command""" api = FinanceBotAPIClient() await api.start() try: # User sends /register command # Bot registers them result = await api.quick_register_telegram_user( chat_id=556399210, username="john_doe", first_name="John", last_name="Doe" ) print("Registration result:", result) if result.get("success"): jwt_token = result.get("jwt_token") user_id = result.get("user_id") print(f"✅ User {user_id} registered!") print(f"Token: {jwt_token[:50]}...") # Now can make API calls with this token # Store in Redis for later use finally: await api.close() async def example_get_balance(): """Example: Get user's account balance""" api = FinanceBotAPIClient() await api.start() try: # First, get token token_result = await api.get_token_for_telegram_user( chat_id=556399210 ) if not token_result.get("success"): print("❌ Could not get token") return access_token = token_result["access_token"] # Make API call accounts = await api.get_accounts(access_token) print("Accounts:", accounts) finally: await api.close() async def example_link_account(): """Example: Link existing account to Telegram""" api = FinanceBotAPIClient() await api.start() try: # Start binding process code_result = await api.start_telegram_binding( chat_id=556399210 ) binding_code = code_result.get("code") print(f"Binding code: {binding_code}") print(f"Expires in: {code_result.get('expires_in')} seconds") # User confirms binding on web # Bot then gets token # After confirmation, get token token_result = await api.get_token_for_telegram_user( chat_id=556399210 ) if token_result.get("success"): print(f"✅ Account linked! User ID: {token_result['user_id']}") finally: await api.close() async def main(): """Run examples""" print("🤖 Finance Bot API Examples\n") print("1. Quick Registration:") print("-" * 50) await example_quick_registration() print("\n2. Get Balance:") print("-" * 50) await example_get_balance() print("\n3. Link Account:") print("-" * 50) await example_link_account() if __name__ == "__main__": asyncio.run(main())