forked from OpenGamers/abaddon
parent
547124c94f
commit
b28bfd6f20
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
- name: Fetch dependencies
|
||||
uses: lukka/run-vcpkg@main
|
||||
with:
|
||||
vcpkgArguments: gtkmm nlohmann-json cpr zlib sqlite3 glibmm openssl ixwebsocket
|
||||
vcpkgArguments: gtkmm nlohmann-json zlib sqlite3 glibmm openssl ixwebsocket curl
|
||||
vcpkgDirectory: ${{ github.workspace }}/ci/vcpkg/
|
||||
vcpkgTriplet: x64-windows
|
||||
|
||||
@ -71,7 +71,6 @@ jobs:
|
||||
run: |
|
||||
brew install gtkmm3
|
||||
brew install nlohmann-json
|
||||
brew install cpr
|
||||
|
||||
- name: Build
|
||||
uses: lukka/run-cmake@main
|
||||
@ -110,15 +109,6 @@ jobs:
|
||||
run: |
|
||||
mkdir deps
|
||||
cd deps
|
||||
git clone https://github.com/whoshuu/cpr
|
||||
cd cpr
|
||||
git checkout 9ff9cef6c794ec3d52d94a62ca791e2f2babca45
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
sudo make install
|
||||
cd ../..
|
||||
git clone https://github.com/nlohmann/json
|
||||
cd json
|
||||
git checkout db78ac1d7716f56fc9f1b030b715f872f93964e4
|
||||
|
@ -10,7 +10,6 @@ set(USE_OPEN_SSL TRUE)
|
||||
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(CURL)
|
||||
find_package(cpr REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(SQLite3 REQUIRED)
|
||||
find_package(gtkmm REQUIRED)
|
||||
@ -51,9 +50,9 @@ file(GLOB ABADDON_SOURCES
|
||||
|
||||
add_executable(abaddon ${ABADDON_SOURCES})
|
||||
target_include_directories(abaddon PUBLIC ${GTKMM_INCLUDE_DIRS})
|
||||
target_include_directories(abaddon PUBLIC ${CPR_INCLUDE_DIRS})
|
||||
target_include_directories(abaddon PUBLIC ${ZLIB_INCLUDE_DIRS})
|
||||
target_include_directories(abaddon PUBLIC ${SQLite3_INCLUDE_DIRS})
|
||||
target_include_directories(abaddon PUBLIC ${NLOHMANN_JSON_INCLUDE_DIRS})
|
||||
|
||||
if ((CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR
|
||||
(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND
|
||||
@ -87,6 +86,5 @@ endif()
|
||||
target_link_libraries(abaddon ${SQLite3_LIBRARIES})
|
||||
target_link_libraries(abaddon ${GTKMM_LIBRARIES})
|
||||
target_link_libraries(abaddon ${CURL_LIBRARIES})
|
||||
target_link_libraries(abaddon ${CPR_LIBRARY})
|
||||
target_link_libraries(abaddon ${ZLIB_LIBRARY})
|
||||
target_link_libraries(abaddon ${NLOHMANN_JSON_LIBRARIES})
|
||||
|
12
README.md
12
README.md
@ -13,7 +13,7 @@
|
||||
### Building:
|
||||
#### Windows:
|
||||
1. `git clone https://github.com/uowuo/abaddon && cd abaddon`
|
||||
2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows cpr:x64-windows zlib:x64-windows simpleini:x64-windows sqlite3:x64-windows openssl:x64-windows`
|
||||
2. `vcpkg install gtkmm:x64-windows nlohmann-json:x64-windows ixwebsocket:x64-windows zlib:x64-windows simpleini:x64-windows sqlite3:x64-windows openssl:x64-windows curl:x64-windows`
|
||||
3. `mkdir build && cd build`
|
||||
4. `cmake -G"Visual Studio 16 2019" -A x64 -DCMAKE_TOOLCHAIN_FILE=c:\path\to\vcpkg\scripts\buildsystems\vcpkg.cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVCPKG_TARGET_TRIPLET=x64-windows ..`
|
||||
5. Build with Visual Studio
|
||||
@ -22,13 +22,13 @@ Or, do steps 1 and 2, and open CMakeLists.txt in Visual Studio if `vcpkg integra
|
||||
|
||||
#### Mac:
|
||||
1. `git clone https://github.com/uowuo/abaddon && cd abaddon`
|
||||
2. `brew install gtkmm3 nlohmann-json cpr`
|
||||
2. `brew install gtkmm3 nlohmann-json`
|
||||
3. `mkdir build && cd build`
|
||||
4. `cmake ..`
|
||||
5. `make`
|
||||
|
||||
#### Linux:
|
||||
1. Install dependencies: `libgtkmm-3.0-dev`, `libcurl4-gnutls-dev`, [cpr](https://github.com/whoshuu/cpr), and [nlohmann-json](https://github.com/nlohmann/json)
|
||||
1. Install dependencies: `libgtkmm-3.0-dev`, `libcurl4-gnutls-dev`, and [nlohmann-json](https://github.com/nlohmann/json)
|
||||
2. `git clone https://github.com/uowuo/abaddon && cd abaddon`
|
||||
3. `mkdir build && cd build`
|
||||
4. `cmake ..`
|
||||
@ -36,8 +36,8 @@ Or, do steps 1 and 2, and open CMakeLists.txt in Visual Studio if `vcpkg integra
|
||||
|
||||
### Downloads (from CI):
|
||||
- Windows: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-windows-RelWithDebInfo.zip)
|
||||
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned, unpackaged, requires gtkmm3 and [cpr](https://github.com/whoshuu/cpr/) (e.g. from homebrew)
|
||||
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now), requires gtkmm3 and [cpr](https://github.com/whoshuu/cpr/). built on Ubuntu 18.04 + gcc9
|
||||
- MacOS: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-macos-RelWithDebInfo.zip) unsigned, unpackaged, requires gtkmm3 (e.g. from homebrew)
|
||||
- Linux: [here](https://nightly.link/uowuo/abaddon/workflows/ci/master/build-linux-MinSizeRel.zip) unpackaged (for now), requires gtkmm3. built on Ubuntu 18.04 + gcc9
|
||||
|
||||
Make sure you start from the directory where `css` and `res` are
|
||||
|
||||
@ -45,7 +45,7 @@ Make sure you start from the directory where `css` and `res` are
|
||||
* [gtkmm](https://www.gtkmm.org/en/)
|
||||
* [JSON for Modern C++](https://github.com/nlohmann/json)
|
||||
* [IXWebSocket](https://github.com/machinezone/IXWebSocket)
|
||||
* [C++ Requests: Curl for People](https://github.com/whoshuu/cpr/)
|
||||
* [libcurl](https://curl.se/)
|
||||
* [zlib](https://zlib.net/)
|
||||
* [simpleini](https://github.com/brofield/simpleini)
|
||||
* [SQLite3](https://www.sqlite.org/index.html)
|
||||
|
@ -1,17 +0,0 @@
|
||||
find_path(CPR_INCLUDE_DIR
|
||||
NAMES cpr/cpr.h)
|
||||
|
||||
find_library(CPR_LIBRARY
|
||||
NAMES cpr
|
||||
HINTS ${CPR_LIBRARY_ROOT})
|
||||
|
||||
set(CPR_LIBRARIES ${CPR_LIBRARY})
|
||||
set(CPR_INCLUDE_DIRS ${CPR_INCLUDE_DIR})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(cpr
|
||||
FOUND_VAR CPR_FOUND
|
||||
REQUIRED_VARS
|
||||
CPR_LIBRARY
|
||||
CPR_INCLUDE_DIR
|
||||
VERSION_VAR CPR_VERSION)
|
@ -101,7 +101,7 @@ std::set<Snowflake> DiscordClient::GetMessagesForChannel(Snowflake id) const {
|
||||
void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback) {
|
||||
sigc::signal<void, std::optional<InviteData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](cpr::Response r) {
|
||||
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](http::response_type r) {
|
||||
if (!CheckCode(r)) {
|
||||
if (r.status_code == 404)
|
||||
callback(std::nullopt);
|
||||
@ -114,7 +114,7 @@ void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<
|
||||
|
||||
void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(const std::vector<Snowflake> &)> cb) {
|
||||
std::string path = "/channels/" + std::to_string(id) + "/messages?limit=50";
|
||||
m_http.MakeGET(path, [this, id, cb](cpr::Response r) {
|
||||
m_http.MakeGET(path, [this, id, cb](const http::response_type &r) {
|
||||
if (!CheckCode(r)) return;
|
||||
|
||||
std::vector<Message> msgs;
|
||||
@ -137,7 +137,7 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(cons
|
||||
|
||||
void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function<void(const std::vector<Snowflake> &)> cb) {
|
||||
std::string path = "/channels/" + std::to_string(channel_id) + "/messages?limit=50&before=" + std::to_string(before_id);
|
||||
m_http.MakeGET(path, [this, channel_id, cb](cpr::Response r) {
|
||||
m_http.MakeGET(path, [this, channel_id, cb](http::response_type r) {
|
||||
if (!CheckCode(r)) return;
|
||||
|
||||
std::vector<Message> msgs;
|
||||
@ -453,7 +453,7 @@ void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::
|
||||
obj.Name = name;
|
||||
sigc::signal<void, bool> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
|
||||
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const http::response_type &r) {
|
||||
const auto success = r.status_code == 200;
|
||||
signal.emit(success);
|
||||
});
|
||||
@ -468,7 +468,7 @@ void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::sl
|
||||
obj.IconData = data;
|
||||
sigc::signal<void, bool> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
|
||||
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const http::response_type &r) {
|
||||
const auto success = r.status_code == 200;
|
||||
signal.emit(success);
|
||||
});
|
||||
@ -481,7 +481,7 @@ void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id) {
|
||||
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback) {
|
||||
sigc::signal<void, bool> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const cpr::Response &response) {
|
||||
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const http::response_type &response) {
|
||||
callback(response.status_code == 204);
|
||||
});
|
||||
}
|
||||
@ -493,7 +493,7 @@ void DiscordClient::DeleteInvite(const std::string &code) {
|
||||
void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(bool success)> callback) {
|
||||
sigc::signal<void, bool> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeDELETE("/invites/" + code, [this, callback](const cpr::Response &response) {
|
||||
m_http.MakeDELETE("/invites/" + code, [this, callback](const http::response_type &response) {
|
||||
callback(CheckCode(response));
|
||||
});
|
||||
}
|
||||
@ -505,7 +505,7 @@ std::vector<BanData> DiscordClient::GetBansInGuild(Snowflake guild_id) {
|
||||
void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback) {
|
||||
sigc::signal<void, BanData> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const cpr::Response &response) {
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const http::response_type &response) {
|
||||
if (!CheckCode(response)) return;
|
||||
auto ban = nlohmann::json::parse(response.text).get<BanData>();
|
||||
m_store.SetBan(guild_id, ban.User.ID, ban);
|
||||
@ -517,7 +517,7 @@ void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::s
|
||||
void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback) {
|
||||
sigc::signal<void, std::vector<BanData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const cpr::Response &response) {
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const http::response_type &response) {
|
||||
if (!CheckCode(response)) return;
|
||||
auto bans = nlohmann::json::parse(response.text).get<std::vector<BanData>>();
|
||||
m_store.BeginTransaction();
|
||||
@ -533,7 +533,7 @@ void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vect
|
||||
void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback) {
|
||||
sigc::signal<void, std::vector<InviteData>> signal;
|
||||
signal.connect(callback);
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const cpr::Response &response) {
|
||||
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const http::response_type &response) {
|
||||
// store?
|
||||
if (!CheckCode(response)) return;
|
||||
auto invites = nlohmann::json::parse(response.text).get<std::vector<InviteData>>();
|
||||
@ -1232,9 +1232,9 @@ void DiscordClient::HandleSocketOpen() {
|
||||
void DiscordClient::HandleSocketClose(uint16_t code) {
|
||||
}
|
||||
|
||||
bool DiscordClient::CheckCode(const cpr::Response &r) {
|
||||
bool DiscordClient::CheckCode(const http::response_type &r) {
|
||||
if (r.status_code >= 300 || r.error) {
|
||||
fprintf(stderr, "api request to %s failed with status code %d\n", r.url.c_str(), r.status_code);
|
||||
fprintf(stderr, "api request to %s failed with status code %d: %s\n", r.url.c_str(), r.status_code, r.error_string.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "websocket.hpp"
|
||||
#include "http.hpp"
|
||||
#include "httpclient.hpp"
|
||||
#include "objects.hpp"
|
||||
#include "store.hpp"
|
||||
#include <sigc++/sigc++.h>
|
||||
@ -175,7 +175,7 @@ private:
|
||||
void HandleSocketOpen();
|
||||
void HandleSocketClose(uint16_t code);
|
||||
|
||||
bool CheckCode(const cpr::Response &r);
|
||||
bool CheckCode(const http::response_type &r);
|
||||
|
||||
void StoreMessageData(Message &msg);
|
||||
|
||||
|
156
discord/http.cpp
156
discord/http.cpp
@ -1,156 +0,0 @@
|
||||
#include "http.hpp"
|
||||
|
||||
//#define USE_LOCAL_PROXY
|
||||
HTTPClient::HTTPClient(std::string api_base)
|
||||
: m_api_base(api_base) {
|
||||
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
|
||||
}
|
||||
|
||||
void HTTPClient::SetUserAgent(std::string agent) {
|
||||
m_agent = agent;
|
||||
}
|
||||
|
||||
void HTTPClient::SetAuth(std::string auth) {
|
||||
m_authorization = auth;
|
||||
}
|
||||
|
||||
void HTTPClient::MakeDELETE(std::string path, std::function<void(cpr::Response r)> cb) {
|
||||
printf("DELETE %s\n", path.c_str());
|
||||
auto url = cpr::Url { m_api_base + path };
|
||||
auto headers = cpr::Header {
|
||||
{ "Authorization", m_authorization },
|
||||
};
|
||||
auto ua = cpr::UserAgent {m_agent != "" ? m_agent : "Abaddon" };
|
||||
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
m_futures.push_back(cpr::DeleteCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, ua,
|
||||
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
|
||||
cpr::VerifySsl { false }));
|
||||
#else
|
||||
m_futures.push_back(cpr::DeleteCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, ua));
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPClient::MakePATCH(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
|
||||
printf("PATCH %s\n", path.c_str());
|
||||
auto url = cpr::Url { m_api_base + path };
|
||||
auto headers = cpr::Header {
|
||||
{ "Authorization", m_authorization },
|
||||
{ "Content-Type", "application/json" },
|
||||
};
|
||||
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
|
||||
|
||||
auto body = cpr::Body { payload };
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
m_futures.push_back(cpr::PatchCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua,
|
||||
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
|
||||
cpr::VerifySsl { false }));
|
||||
#else
|
||||
m_futures.push_back(cpr::PatchCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua));
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPClient::MakePOST(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
|
||||
printf("POST %s\n", path.c_str());
|
||||
auto url = cpr::Url { m_api_base + path };
|
||||
auto headers = cpr::Header {
|
||||
{ "Authorization", m_authorization },
|
||||
{ "Content-Type", "application/json" },
|
||||
};
|
||||
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
|
||||
|
||||
auto body = cpr::Body { payload };
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
m_futures.push_back(cpr::PostCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua,
|
||||
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
|
||||
cpr::VerifySsl { false }));
|
||||
#else
|
||||
m_futures.push_back(cpr::PostCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua));
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPClient::MakePUT(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
|
||||
printf("PUT %s\n", path.c_str());
|
||||
auto url = cpr::Url { m_api_base + path };
|
||||
auto headers = cpr::Header {
|
||||
{ "Authorization", m_authorization },
|
||||
{ "Content-Type", "application/json" },
|
||||
};
|
||||
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
|
||||
|
||||
auto body = cpr::Body { payload };
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
m_futures.push_back(cpr::PutCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua,
|
||||
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
|
||||
cpr::VerifySsl { false }));
|
||||
#else
|
||||
m_futures.push_back(cpr::PutCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, body, ua));
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPClient::MakeGET(std::string path, std::function<void(cpr::Response r)> cb) {
|
||||
printf("GET %s\n", path.c_str());
|
||||
auto url = cpr::Url { m_api_base + path };
|
||||
auto headers = cpr::Header {
|
||||
{ "Authorization", m_authorization },
|
||||
{ "Content-Type", "application/json" },
|
||||
};
|
||||
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
|
||||
|
||||
auto x = cpr::UserAgent {};
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
m_futures.push_back(cpr::GetCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, ua,
|
||||
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
|
||||
cpr::VerifySsl { false }));
|
||||
#else
|
||||
m_futures.push_back(cpr::GetCallback(
|
||||
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
|
||||
url, headers, ua));
|
||||
#endif
|
||||
}
|
||||
|
||||
void HTTPClient::CleanupFutures() {
|
||||
for (auto it = m_futures.begin(); it != m_futures.end();) {
|
||||
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
it = m_futures.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPClient::RunCallbacks() {
|
||||
m_mutex.lock();
|
||||
m_queue.front()();
|
||||
m_queue.pop();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void HTTPClient::OnResponse(cpr::Response r, std::function<void(cpr::Response r)> cb) {
|
||||
CleanupFutures();
|
||||
try {
|
||||
m_mutex.lock();
|
||||
m_queue.push([this, r, cb] { cb(r); });
|
||||
m_dispatcher.emit();
|
||||
m_mutex.unlock();
|
||||
} catch (std::exception &e) {
|
||||
fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what());
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
#include <cpr/cpr.h>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <glibmm.h>
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient(std::string api_base);
|
||||
|
||||
void SetUserAgent(std::string agent);
|
||||
void SetAuth(std::string auth);
|
||||
void MakeDELETE(std::string path, std::function<void(cpr::Response r)> cb);
|
||||
void MakeGET(std::string path, std::function<void(cpr::Response r)> cb);
|
||||
void MakePATCH(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
|
||||
void MakePOST(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
|
||||
void MakePUT(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
|
||||
|
||||
private:
|
||||
void OnResponse(cpr::Response r, std::function<void(cpr::Response r)> cb);
|
||||
void CleanupFutures();
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
Glib::Dispatcher m_dispatcher;
|
||||
std::queue<std::function<void()>> m_queue;
|
||||
void RunCallbacks();
|
||||
|
||||
std::vector<std::future<void>> m_futures;
|
||||
std::string m_api_base;
|
||||
std::string m_authorization;
|
||||
std::string m_agent;
|
||||
};
|
136
discord/httpclient.cpp
Normal file
136
discord/httpclient.cpp
Normal file
@ -0,0 +1,136 @@
|
||||
#include "httpclient.hpp"
|
||||
#include "httpclient.hpp"
|
||||
|
||||
//#define USE_LOCAL_PROXY
|
||||
HTTPClient::HTTPClient(std::string api_base)
|
||||
: m_api_base(api_base) {
|
||||
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
|
||||
}
|
||||
|
||||
void HTTPClient::SetUserAgent(std::string agent) {
|
||||
m_agent = agent;
|
||||
}
|
||||
|
||||
void HTTPClient::SetAuth(std::string auth) {
|
||||
m_authorization = auth;
|
||||
}
|
||||
|
||||
void HTTPClient::MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb) {
|
||||
printf("DELETE %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
|
||||
http::request req(http::REQUEST_DELETE, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("PATCH %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_PATCH, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("POST %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_POST, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("PUT %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_PUT, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakeGET(const std::string &path, std::function<void(http::response_type r)> cb) {
|
||||
printf("GET %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
|
||||
http::request req(http::REQUEST_GET, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::CleanupFutures() {
|
||||
for (auto it = m_futures.begin(); it != m_futures.end();) {
|
||||
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
it = m_futures.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPClient::RunCallbacks() {
|
||||
m_mutex.lock();
|
||||
m_queue.front()();
|
||||
m_queue.pop();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void HTTPClient::OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb) {
|
||||
CleanupFutures();
|
||||
try {
|
||||
m_mutex.lock();
|
||||
m_queue.push([this, r, cb] { cb(r); });
|
||||
m_dispatcher.emit();
|
||||
m_mutex.unlock();
|
||||
} catch (const std::exception &e) {
|
||||
fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what());
|
||||
}
|
||||
}
|
37
discord/httpclient.hpp
Normal file
37
discord/httpclient.hpp
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <glibmm.h>
|
||||
#include "../http.hpp"
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient(std::string api_base);
|
||||
|
||||
void SetUserAgent(std::string agent);
|
||||
void SetAuth(std::string auth);
|
||||
void MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb);
|
||||
void MakeGET(const std::string &path, std::function<void(http::response_type r)> cb);
|
||||
void MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
void MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
void MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
|
||||
private:
|
||||
void OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb);
|
||||
void CleanupFutures();
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
Glib::Dispatcher m_dispatcher;
|
||||
std::queue<std::function<void()>> m_queue;
|
||||
void RunCallbacks();
|
||||
|
||||
std::vector<std::future<void>> m_futures;
|
||||
std::string m_api_base;
|
||||
std::string m_authorization;
|
||||
std::string m_agent;
|
||||
};
|
@ -51,10 +51,10 @@ void Cache::GetFileFromURL(std::string url, callback_type cb) {
|
||||
if (m_canceled) return;
|
||||
m_semaphore->wait();
|
||||
if (m_canceled) return;
|
||||
const auto &r = cpr::Get(cpr::Url { url });
|
||||
http::request req(http::REQUEST_GET, url);
|
||||
m_semaphore->notify();
|
||||
if (m_canceled) return;
|
||||
OnResponse(r);
|
||||
OnResponse(req.execute());
|
||||
});
|
||||
m_futures.push_back(std::move(future));
|
||||
}
|
||||
@ -79,7 +79,7 @@ void Cache::CleanupFutures() {
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::OnResponse(const cpr::Response &r) {
|
||||
void Cache::OnResponse(const http::response_type &r) {
|
||||
CleanupFutures(); // see above comment
|
||||
if (r.error || r.status_code > 300) return;
|
||||
|
||||
|
@ -1,13 +1,12 @@
|
||||
#pragma once
|
||||
#include <cpr/cpr.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <future>
|
||||
#include "util.hpp"
|
||||
|
||||
// todo throttle requests and keep track of active requests to stop redundant requests
|
||||
#include "http.hpp"
|
||||
|
||||
class Cache {
|
||||
public:
|
||||
@ -23,7 +22,7 @@ private:
|
||||
std::string GetCachedName(std::string str);
|
||||
void CleanupFutures();
|
||||
void RespondFromPath(std::filesystem::path path, callback_type cb);
|
||||
void OnResponse(const cpr::Response &r);
|
||||
void OnResponse(const http::response_type &r);
|
||||
|
||||
std::unique_ptr<Semaphore> m_semaphore;
|
||||
|
||||
|
121
http.cpp
Normal file
121
http.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
#include "http.hpp"
|
||||
|
||||
namespace http {
|
||||
request::request(EMethod method, const std::string &url)
|
||||
: m_url(url) {
|
||||
switch (method) {
|
||||
case REQUEST_GET:
|
||||
m_method = "GET";
|
||||
break;
|
||||
case REQUEST_POST:
|
||||
m_method = "POST";
|
||||
break;
|
||||
case REQUEST_PATCH:
|
||||
m_method = "PATCH";
|
||||
break;
|
||||
case REQUEST_PUT:
|
||||
m_method = "PUT";
|
||||
break;
|
||||
case REQUEST_DELETE:
|
||||
m_method = "DELETE";
|
||||
break;
|
||||
default:
|
||||
m_method = "GET";
|
||||
break;
|
||||
}
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
request::~request() {
|
||||
if (m_curl != nullptr)
|
||||
curl_easy_cleanup(m_curl);
|
||||
|
||||
if (m_header_list != nullptr)
|
||||
curl_slist_free_all(m_header_list);
|
||||
}
|
||||
|
||||
void request::set_verify_ssl(bool verify) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_SSL_VERIFYPEER, verify ? 1L : 0L);
|
||||
}
|
||||
|
||||
void request::set_proxy(const std::string &proxy) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_PROXY, proxy.c_str());
|
||||
}
|
||||
|
||||
void request::set_header(const std::string &name, const std::string &value) {
|
||||
m_header_list = curl_slist_append(m_header_list, (name + ": " + value).c_str());
|
||||
}
|
||||
|
||||
void request::set_body(const std::string &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_COPYPOSTFIELDS, data.c_str());
|
||||
}
|
||||
|
||||
void request::set_user_agent(const std::string &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_USERAGENT, data.c_str());
|
||||
}
|
||||
|
||||
response request::execute() {
|
||||
if (m_curl == nullptr) {
|
||||
auto response = detail::make_response(m_url, EStatusCode::ClientErrorCURLInit);
|
||||
response.error_string = "curl pointer is null";
|
||||
}
|
||||
|
||||
detail::check_init();
|
||||
|
||||
std::string str;
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method);
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, detail::curl_write_data_callback);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &str);
|
||||
curl_easy_setopt(m_curl, CURLOPT_ERRORBUFFER, m_error_buf);
|
||||
if (m_header_list != nullptr)
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, m_header_list);
|
||||
|
||||
CURLcode result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK) {
|
||||
auto response = detail::make_response(m_url, EStatusCode::ClientErrorCURLPerform);
|
||||
response.error_string = curl_easy_strerror(result);
|
||||
response.error_string += " " + std::string(m_error_buf);
|
||||
return response;
|
||||
}
|
||||
|
||||
int response_code = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &response_code);
|
||||
|
||||
auto response = detail::make_response(m_url, response_code);
|
||||
response.text = str;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
void request::prepare() {
|
||||
m_curl = curl_easy_init();
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, std::string *outstr) {
|
||||
auto new_length = size * nmemb;
|
||||
outstr->append(reinterpret_cast<char *>(ptr), new_length);
|
||||
return new_length;
|
||||
}
|
||||
|
||||
response make_response(const std::string &url, int code) {
|
||||
response r;
|
||||
r.url = url;
|
||||
r.status_code = static_cast<EStatusCode>(code);
|
||||
if (code < http::EStatusCode::ClientErrorMax)
|
||||
r.error = true;
|
||||
return r;
|
||||
}
|
||||
|
||||
void check_init() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace http
|
129
http.hpp
Normal file
129
http.hpp
Normal file
@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <curl/curl.h>
|
||||
|
||||
// i regret not using snake case for everything oh well
|
||||
|
||||
namespace http {
|
||||
enum EStatusCode : int {
|
||||
Continue = 100,
|
||||
SwitchingProtocols = 101,
|
||||
Processing = 102,
|
||||
EarlyHints = 103,
|
||||
|
||||
OK = 200,
|
||||
Created = 201,
|
||||
Accepted = 202,
|
||||
NonAuthoritativeInformation = 203,
|
||||
NoContent = 204,
|
||||
ResetContent = 205,
|
||||
PartialContent = 206,
|
||||
MultiStatus = 207,
|
||||
AlreadyReported = 208,
|
||||
IMUsed = 226,
|
||||
|
||||
MultipleChoices = 300,
|
||||
MovedPermanently = 301,
|
||||
Found = 302,
|
||||
SeeOther = 303,
|
||||
NotModified = 304,
|
||||
UseProxy = 305,
|
||||
SwitchProxy = 306,
|
||||
TemporaryRedirect = 307,
|
||||
PermanentRedirect = 308,
|
||||
|
||||
BadRequest = 400,
|
||||
Unauthorized = 401,
|
||||
PaymentRequired = 402,
|
||||
Forbidden = 403,
|
||||
NotFound = 404,
|
||||
MethodNotAllowed = 405,
|
||||
NotAcceptable = 406,
|
||||
ProxyAuthorizationRequired = 407,
|
||||
RequestTimeout = 408,
|
||||
Conflict = 409,
|
||||
Gone = 410,
|
||||
LengthRequired = 411,
|
||||
PreconditionFailed = 412,
|
||||
PayloadTooLarge = 413,
|
||||
URITooLong = 414,
|
||||
UnsupportedMediaType = 415,
|
||||
RangeNotSatisfiable = 416,
|
||||
ExpectationFailed = 417,
|
||||
ImATeapot = 418,
|
||||
MisdirectedRequest = 421,
|
||||
UnprocessableEntity = 422,
|
||||
Locked = 423,
|
||||
FailedDependency = 424,
|
||||
TooEarly = 425,
|
||||
UpgradeRequired = 426,
|
||||
PreconditionRequired = 428,
|
||||
TooManyRequests = 429,
|
||||
RequestHeaderFieldsTooLarge = 431,
|
||||
UnavailableForLegalReasons = 451,
|
||||
|
||||
InternalServerError = 500,
|
||||
NotImplemented = 501,
|
||||
BadGateway = 502,
|
||||
ServiceUnavailable = 503,
|
||||
GatewayTimeout = 504,
|
||||
HTTPVersionNotSupported = 505,
|
||||
VariantAlsoNegotiates = 506,
|
||||
InsufficientStorage = 507,
|
||||
LoopDetected = 508,
|
||||
NotExtended = 510,
|
||||
NetworkAuthenticationRequired = 511,
|
||||
|
||||
ClientError = 1,
|
||||
ClientErrorCURLInit,
|
||||
ClientErrorCURLPerform,
|
||||
ClientErrorMax = 99,
|
||||
};
|
||||
|
||||
enum EMethod {
|
||||
REQUEST_GET,
|
||||
REQUEST_POST,
|
||||
REQUEST_PATCH,
|
||||
REQUEST_PUT,
|
||||
REQUEST_DELETE,
|
||||
};
|
||||
|
||||
struct response {
|
||||
EStatusCode status_code;
|
||||
std::string text;
|
||||
std::string url;
|
||||
bool error = false;
|
||||
std::string error_string;
|
||||
};
|
||||
|
||||
struct request {
|
||||
request(EMethod method, const std::string &url);
|
||||
~request();
|
||||
|
||||
void set_verify_ssl(bool verify);
|
||||
void set_proxy(const std::string &proxy);
|
||||
void set_header(const std::string &name, const std::string &value);
|
||||
void set_body(const std::string &data);
|
||||
void set_user_agent(const std::string &data);
|
||||
|
||||
response execute();
|
||||
|
||||
private:
|
||||
void prepare();
|
||||
|
||||
CURL *m_curl;
|
||||
std::string m_url;
|
||||
const char *m_method;
|
||||
curl_slist *m_header_list = nullptr;
|
||||
char m_error_buf[CURL_ERROR_SIZE] = { 0 };
|
||||
};
|
||||
|
||||
using response_type = response;
|
||||
|
||||
namespace detail {
|
||||
size_t curl_write_data_callback(void *ptr, size_t size, size_t nmemb, std::string *outstr);
|
||||
|
||||
response make_response(const std::string &url, int code);
|
||||
void check_init();
|
||||
} // namespace detail
|
||||
} // namespace http
|
Loading…
Reference in New Issue
Block a user