HTTP owner discovery (uses libcurl via IHttpTransport).
| 260 | |
| 261 | // HTTP owner discovery (uses libcurl via IHttpTransport). |
| 262 | static std::vector<uint64_t> FetchReviewOwnerIds(uint32_t appId) { |
| 263 | std::vector<uint64_t> ids; |
| 264 | auto transport = CreateHttpTransport("[SchemaFetch]"); |
| 265 | if (!transport || !transport->Init()) return ids; |
| 266 | |
| 267 | std::string path = "/appreviews/" + std::to_string(appId) + |
| 268 | "?json=1&filter=recent&language=all&purchase_type=all&num_per_page=20"; |
| 269 | auto resp = transport->Request("GET", "store.steampowered.com", path, {}, {}); |
| 270 | if (resp.status != 200 || resp.body.empty()) return ids; |
| 271 | |
| 272 | // Parse "steamid":"<digits>" from JSON |
| 273 | constexpr uint64_t kSteamId64Base = 76561197960265728ull; |
| 274 | size_t pos = 0; |
| 275 | while ((pos = resp.body.find("\"steamid\"", pos)) != std::string::npos) { |
| 276 | pos = resp.body.find(':', pos); |
| 277 | if (pos == std::string::npos) break; |
| 278 | ++pos; |
| 279 | while (pos < resp.body.size() && (resp.body[pos] == ' ' || resp.body[pos] == '"')) ++pos; |
| 280 | size_t start = pos; |
| 281 | while (pos < resp.body.size() && resp.body[pos] >= '0' && resp.body[pos] <= '9') ++pos; |
| 282 | if (start == pos) continue; |
| 283 | uint64_t sid = strtoull(resp.body.c_str() + start, nullptr, 10); |
| 284 | if (sid >= kSteamId64Base) { |
| 285 | bool dup = false; |
| 286 | for (uint64_t existing : ids) if (existing == sid) { dup = true; break; } |
| 287 | if (!dup) ids.push_back(sid); |
| 288 | } |
| 289 | } |
| 290 | return ids; |
| 291 | } |
| 292 | |
| 293 | static bool HasPublicStats(uint32_t appId, uint64_t steamId) { |
| 294 | auto transport = CreateHttpTransport("[SchemaFetch]"); |
no test coverage detected