139:def refresh_access_token(client: dict, refresh_token: str) -> dict: 140- data = { 141- "client_id": client["client_id"], 142- "client_secret": client["client_secret"], 143- "refresh_token": refresh_token, 144- "grant_type": "refresh_token", 145- } 146- resp = requests.post(GOOGLE_TOKEN_URL, data=data, timeout=20) 147- if resp.status_code != 200: -- 173:class GmailClient: 174- """Authenticated Gmail client for one account (API or XOAUTH2 SMTP).""" 175- 176- def __init__(self, account_email: str, access_token: str, refresh_token: str, 177- client_config: dict, sending_method: str = "api", token_expiry=None): 178- self.account_email = account_email 179- self.client_config = client_config 180- self.method = sending_method if sending_method in ("api", "smtp") else "api" 181- creds = gcred.Credentials( -- 193: def _ensure_valid(self): 194- request = google.auth.transport.requests.Request() 195- if not self._creds.valid: 196- self._creds.refresh(request) 197- return self._creds.token 198- 199: def _api(self): 200- if self._service is None: 201- self._ensure_valid() 202- self._service = build("gmail", "v1", credentials=self._creds, cache_discovery=False) 203- else: 204- self._ensure_valid() 205- return self._service 206- 207- # -- API send ------------------------------------------------------------ -- 249:def build_gmail_client_from_account(account, oauth_token, client_config=None) -> GmailClient: 250- """Build a GmailClient from DB rows (sync session context - worker uses sync ORM).""" 251- cfg = client_config or resolve_client_config_for_account(account) 252- return GmailClient( 253- account_email=account.email, 254- access_token=oauth_token.access_token, # decrypted 255- refresh_token=oauth_token.refresh_token, # decrypted 256- client_config=cfg, 257- sending_method=account.sending_method,