from __future__ import annotations import logging from fansifter_common.httpclient.base import HTTPClient from httpx._types import HeaderTypes, QueryParamTypes # noqa from dmp.core.types import PlainToken from .exceptions import TikTokClientError from .models import ( AccessToken, AdAccount, BusinessCenter, BusinessCenterInfo, Campaign, ListData, Response, User, ) logger = logging.getLogger(__name__) class TikTokClient(HTTPClient): base_url = "https://business-api.tiktok.com/open_api/v1.3" sensitive_params = { "secret", "auth_token", "access_token", } DEFAULT_REQUEST_TIMEOUT = 10 exception_class = TikTokClientError def __init__( self, client_id: str, client_secret: str, default_timeout: int | None = None, ) -> None: self.client_id = client_id self.client_secret = client_secret super().__init__( client_options={ "timeout": default_timeout or self.DEFAULT_REQUEST_TIMEOUT, "base_url": self.base_url, } ) @staticmethod def convert_to_list_string(lst: list[str]) -> str: return "[{}]".format(",".join(f'"{x}"' for x in lst)) def get_user(self, access_token: PlainToken) -> User: response = self.request( "GET", "/user/info/", type=Response[User], headers={"Access-Token": access_token}, ) return response.data def get_oauth_access_token(self, tmp_token: PlainToken) -> AccessToken: response = self.request( "POST", "/oauth2/access_token/", type=Response[AccessToken], json={ "app_id": self.client_id, "secret": self.client_secret, "auth_code": tmp_token, }, ) return response.data def get_ad_accounts_details( self, access_token: PlainToken, advertiser_ids: list[str] ) -> list[AdAccount]: response = self.request( "GET", "/advertiser/info/", type=Response[ListData[AdAccount]], headers={"Access-Token": access_token}, params={ "advertiser_ids": self.convert_to_list_string(advertiser_ids), }, ) return response.data.list def get_ad_account_campaigns_count( self, access_token: PlainToken, advertiser_id: str ) -> int: response = self.request( "GET", "/campaign/get/", type=Response[ListData[Campaign]], headers={"Access-Token": access_token}, params={"advertiser_id": advertiser_id}, ) if response.data.page_info is not None and response.data.page_info.total_number: return response.data.page_info.total_number return 0 def get_business_center_details( self, access_token: PlainToken, business_center_id: str ) -> BusinessCenterInfo | None: response = self.request( "GET", "/bc/get/", type=Response[ListData[BusinessCenter]], headers={"Access-Token": access_token}, params={"bc_id": business_center_id}, ) if response.data.list: return response.data.list[0].bc_info return None