122 lines
4.2 KiB
C++
122 lines
4.2 KiB
C++
#include "vk.h"
|
|
#include "curl/curl.h"
|
|
#include "http.h"
|
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
#include <chrono>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <nlohmann/json.hpp>
|
|
|
|
using namespace vk;
|
|
using namespace nlohmann;
|
|
using std::chrono::duration_cast, std::chrono::milliseconds, std::chrono::steady_clock;
|
|
|
|
|
|
const char *API_BASE_URL = "https://api.vk.com/method/";
|
|
const char *API_VERSION = "5.199";
|
|
const char *LOGGER_TAG = "vk";
|
|
const long API_CALL_INTERVAL = 250;
|
|
|
|
VKClient::VKClient(uv_loop_t *eventLoop) : m_eventLoop(eventLoop), m_httpClient(eventLoop) {
|
|
m_logger = spdlog::get(LOGGER_TAG);
|
|
if (!m_logger) {
|
|
m_logger = spdlog::stdout_color_mt(LOGGER_TAG);
|
|
m_logger->set_level(spdlog::level::debug);
|
|
}
|
|
};
|
|
|
|
VKClient::VKClient(uv_loop_t *eventLoop, std::string serviceKey) : VKClient(eventLoop) {
|
|
set_service_api_key(serviceKey);
|
|
}
|
|
|
|
void VKClient::set_service_api_key(std::string key) {
|
|
m_serviceApiKey = {key};
|
|
}
|
|
|
|
void VKClient::get_posts(std::variant<long, std::string> wall, int offset, int count, std::function<void(std::optional<WallChunk>, int)> callback) {
|
|
long delay = API_CALL_INTERVAL - duration_cast<milliseconds>(steady_clock::now() - m_lastCallTime).count();
|
|
if (delay <= 0) {
|
|
get_posts_now(wall, offset, count, callback);
|
|
} else {
|
|
m_logger->debug("delaying request by {} ms", delay);
|
|
struct params {
|
|
VKClient *self;
|
|
std::variant<long, std::string> wall;
|
|
int offset, count;
|
|
std::function<void(std::optional<WallChunk>, int)> callback;
|
|
};
|
|
uv_timer_t *timer = new uv_timer_t;
|
|
params *p = new params{ this, wall, offset, count, callback };
|
|
timer->data = p;
|
|
|
|
uv_timer_init(m_eventLoop, timer);
|
|
uv_timer_start(timer, [](uv_timer_t *h){
|
|
uv_timer_stop(h);
|
|
uv_close((uv_handle_t*)h, [](uv_handle_t *h){ delete h; });
|
|
|
|
params *p = (params*)h->data;
|
|
p->self->get_posts(p->wall, p->offset, p->count, p->callback);
|
|
delete p;
|
|
}, delay, 0);
|
|
}
|
|
}
|
|
|
|
void VKClient::get_posts_now(std::variant<long, std::string> wall, int offset, int count, std::function<void(std::optional<WallChunk>, int)> callback) {
|
|
if (!m_serviceApiKey) {
|
|
m_logger->error("get_posts called without authorization");
|
|
return;
|
|
}
|
|
m_lastCallTime = std::chrono::steady_clock::now();
|
|
std::string url(API_BASE_URL);
|
|
url += "wall.get?access_token=";
|
|
url += *m_serviceApiKey;
|
|
url += "&v=";
|
|
url += API_VERSION;
|
|
url += "&offset=";
|
|
url += std::to_string(offset);
|
|
url += "&count=";
|
|
// + 1 here to handle the pinned post
|
|
int increasedCount = count < std::numeric_limits<int>::max() ? count + 1 : count;
|
|
url += std::to_string(increasedCount);
|
|
if (wall.index() == 0) {
|
|
url += "&owner_id=";
|
|
url += std::to_string(std::get<long>(wall));
|
|
} else {
|
|
url += "&domain=";
|
|
url += std::get<std::string>(wall);
|
|
}
|
|
m_logger->debug("using URL: {}", url);
|
|
|
|
m_httpClient.send_request("GET", url, {}, [this, callback, requestedCount = count](std::unique_ptr<http::HttpResponse> resp, CURLcode r){
|
|
if (r == 0) {
|
|
auto parsedResponse = json::parse(resp->body);
|
|
|
|
if (parsedResponse.contains("error")) {
|
|
auto err = parsedResponse["error"];
|
|
m_logger->error("get_posts error {} {}", (int)err["error_code"], (std::string)err["error_msg"]);
|
|
callback({}, -1);
|
|
return;
|
|
}
|
|
|
|
auto responsePayload = parsedResponse["response"];
|
|
int count = responsePayload["count"];
|
|
std::vector<Post> posts;
|
|
int checkedPosts = 0;
|
|
for (auto post : responsePayload["items"]) {
|
|
if (checkedPosts == requestedCount) continue;
|
|
++checkedPosts;
|
|
if (post.contains("is_pinned") && post["is_pinned"] == 1) continue;
|
|
if (!post.contains("id") || !post.contains("date") || !post.contains("from_id") || !post.contains("type") || !post.contains("text")) {
|
|
m_logger->warn("strange post: {}", post.dump());
|
|
continue;
|
|
}
|
|
posts.emplace_back(post["id"], post["date"], post.contains("edited") ? post["edited"].get<long>() : 0, post["from_id"], post["type"], post["text"]);
|
|
}
|
|
|
|
callback({{count, std::move(posts)}}, 0);
|
|
} else {
|
|
m_logger->error("get_posts network error");
|
|
callback({}, r);
|
|
}
|
|
});
|
|
} |