From a0632d299b85dc4ef7c0e03898f38e6deca0edc2 Mon Sep 17 00:00:00 2001 From: Aotrix <9aotrix@protonmail.com> Date: Tue, 28 Apr 2026 03:49:41 +0200 Subject: add HearthstoneTokenBuilder class --- battleNetApi/src/hearthstoneTokenBuilder.cpp | 140 +++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 battleNetApi/src/hearthstoneTokenBuilder.cpp (limited to 'battleNetApi/src/hearthstoneTokenBuilder.cpp') diff --git a/battleNetApi/src/hearthstoneTokenBuilder.cpp b/battleNetApi/src/hearthstoneTokenBuilder.cpp new file mode 100644 index 0000000..8403f46 --- /dev/null +++ b/battleNetApi/src/hearthstoneTokenBuilder.cpp @@ -0,0 +1,140 @@ +#include +#include +#include +#include + +HearthstoneTokenBuilder::HearthstoneTokenBuilder(const std::string &iJsonPath) { + mJsonPaths.push_back(iJsonPath); +} +HearthstoneTokenBuilder::HearthstoneTokenBuilder( + const std::span &iJsonPaths) { + mJsonPaths.append_range(iJsonPaths); +} +HearthstoneTokenBuilder & +HearthstoneTokenBuilder::addJsonPath(const std::string &iJsonPath) { + mJsonPaths.push_back(iJsonPath); + return *this; +} +HearthstoneTokenBuilder & +HearthstoneTokenBuilder::addJsonPaths(const std::string &iJsonPath) { + mJsonPaths.push_back(iJsonPath); + return *this; +} +HearthstoneTokenBuilder &HearthstoneTokenBuilder::buildToken() { + if (hasExpired()) { + const std::pair kTokenContent{ + getTokenWithApi()}; + writeTokenToLocalFile(kTokenContent); + } + // return getTokenFromLocalFile(); + if (aTokenContainer.empty()) { + return std::nullopt; + } + Token aOutputToken{aTokenContainer.front()}; + aTokenContainer.pop(); + // mCurrentToken = std::make_unique(...); + return *this; +} +std::shared_ptr HearthstoneTokenBuilder::getToken() { + return mCurrentToken; +} +HearthstoneTokenBuilder &HearthstoneTokenBuilder::allowUpdateSourceFile() { + mUpdateSourceFile = true; + return *this; +} +bool HearthstoneTokenBuilder::isUpdateSourceFileAllowed() const { + return mUpdateSourceFile; +} + +const bool HearthstoneTokenBuilder::hasExpired(json iCredentials) const { + if (not iCredentials.contains("Token") or + not iCredentials["Token"].contains("ExpirationTime") or + not iCredentials["Token"].contains("Content")) { + return true; + } + if (iCredentials["Token"]["Content"].dump().length() < + kTOKEN_CONTENT_MIN_LENGTH) { + return true; + } + const std::uint64_t kExpirationTime{ + iCredentials["Token"]["ExpirationTime"]}; + const std::uint64_t kCurrentTime{static_cast( + std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count())}; + return kCurrentTime >= kExpirationTime; +} + +size_t HearthstoneTokenBuilder::curlCallbackStringOutput( + void *iPtr, size_t iSize, size_t iNmemb, std::string *iUserData) { + size_t newLength{iSize * iNmemb}; + try { + iUserData->append(static_cast(iPtr), newLength); + } catch (std::bad_alloc &e) { + // handle memory problem + return 0; + } + return newLength; +} + +std::chrono::seconds HearthstoneTokenBuilder::getCurrentTime() const { + return std::chrono::duration_cast( + std::chrono::utc_clock::now().time_since_epoch()); +} + +std::pair +HearthstoneTokenBuilder::getTokenWithApi() const { + std::ifstream aCredentialsFile{mFilePath}; + json aCredentials = json::parse(aCredentialsFile); + if (not aCredentials.contains("Agent") or + not aCredentials["Agent"].contains("Username") or + not aCredentials["Agent"].contains("Password")) { + std::cerr << "Please define Agent/Username and Agent/Password in the " + "JSON credentials file\n"; + std::exit(EXIT_FAILURE); + } + const std::string kAgentUsername{aCredentials["Agent"]["Username"]}; + const std::string kAgentPassword{aCredentials["Agent"]["Password"]}; + CURL *aCurl{curl_easy_init()}; + std::string aTokenInfo; + if (aCurl and not kAgentUsername.empty() and not kAgentPassword.empty()) { + curl_easy_setopt(aCurl, CURLOPT_URL, "https://oauth.battle.net/token"); + curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(aCurl, CURLOPT_SSL_VERIFYHOST, 0L); + curl_easy_setopt(aCurl, CURLOPT_WRITEFUNCTION, + curlCallbackStringOutput); + curl_easy_setopt(aCurl, CURLOPT_WRITEDATA, &aTokenInfo); + curl_easy_setopt( + aCurl, CURLOPT_USERPWD, + std::format("{}:{}", kAgentUsername, kAgentPassword).data()); + curl_easy_setopt(aCurl, CURLOPT_POSTFIELDS, + "grant_type=client_credentials"); + CURLcode result_code{curl_easy_perform(aCurl)}; + curl_easy_cleanup(aCurl); + if (result_code != CURLE_OK) { + std::cerr << "ERROR: token request failed" << std::endl; + std::exit(EXIT_FAILURE); + } + } + json aTokenJson = json::parse(aTokenInfo); + return std::make_pair(aTokenJson["access_token"], aTokenJson["expires_in"]); +} + +std::string HearthstoneTokenBuilder::getTokenFromLocalFile() const { + std::ifstream aTokenFile{mFilePath}; + return json::parse(aTokenFile)["Token"]["Content"]; +} + +void HearthstoneTokenBuilder::writeTokenToLocalFile( + const std::pair &iToken) const { + std::ifstream aTokenFileInput{mFilePath}; + json aCredentialsFile = json::parse(aTokenFileInput); + aCredentialsFile["Token"]["Content"] = iToken.first; + aCredentialsFile["Token"]["ExpirationTime"] = + std::chrono::duration_cast( + getCurrentTime() + std::chrono::seconds(iToken.second)) + .count(); + + std::ofstream aTokenFileOutput{mFilePath}; + aTokenFileOutput << std::setw(4) << aCredentialsFile; +} -- cgit v1.2.3