Add initial user fetching support
This commit is contained in:
		
							parent
							
								
									e657432ac1
								
							
						
					
					
						commit
						b864b3d290
					
				| 
						 | 
				
			
			@ -23,7 +23,7 @@ list(APPEND FLAGS -Werror -Wall -Wextra -Wshadow -Wpedantic -Wno-gnu-anonymous-s
 | 
			
		|||
add_link_options(${FLAGS})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp blankie/serializer.cpp blankie/escape.cpp
 | 
			
		||||
add_executable(${PROJECT_NAME} main.cpp misc.cpp config.cpp servehelper.cpp pixivclient.cpp blankie/serializer.cpp blankie/escape.cpp
 | 
			
		||||
    routes/home.cpp routes/css.cpp)
 | 
			
		||||
set_target_properties(${PROJECT_NAME}
 | 
			
		||||
    PROPERTIES
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								main.cpp
								
								
								
								
							
							
						
						
									
										13
									
								
								main.cpp
								
								
								
								
							| 
						 | 
				
			
			@ -3,6 +3,7 @@
 | 
			
		|||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#include "pixivclient.h"
 | 
			
		||||
#include "servehelper.h"
 | 
			
		||||
#include "routes/routes.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -20,6 +21,7 @@ int main(int argc, char** argv) {
 | 
			
		|||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    PixivClient pixiv_client;
 | 
			
		||||
    httplib::Server server;
 | 
			
		||||
    server.Get("/", [&](const httplib::Request& req, httplib::Response& res) {
 | 
			
		||||
        home_route(req, res, config);
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +54,17 @@ int main(int argc, char** argv) {
 | 
			
		|||
    server.Get("/debug/exception/unknown", [](const httplib::Request& req, httplib::Response& res) {
 | 
			
		||||
        throw "cope";
 | 
			
		||||
    });
 | 
			
		||||
    // TODO stop it
 | 
			
		||||
    server.Get("/debug/getuser/good", [&](const httplib::Request& req, httplib::Response& res) {
 | 
			
		||||
        User user = pixiv_client.get_user(2583663);
 | 
			
		||||
        printf("%s\n", user.username.c_str());
 | 
			
		||||
        res.set_content(user.username, "text/plain");
 | 
			
		||||
    });
 | 
			
		||||
    server.Get("/debug/getuser/bad", [&](const httplib::Request& req, httplib::Response& res) {
 | 
			
		||||
        User user = pixiv_client.get_user(1);
 | 
			
		||||
        printf("%s\n", user.username.c_str());
 | 
			
		||||
        res.set_content(user.username, "text/plain");
 | 
			
		||||
    });
 | 
			
		||||
#endif
 | 
			
		||||
    server.Get(".*", [&](const httplib::Request& req, httplib::Response& res) {
 | 
			
		||||
        res.status = 404;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,115 @@
 | 
			
		|||
#include <regex>
 | 
			
		||||
 | 
			
		||||
#include "pixivclient.h"
 | 
			
		||||
 | 
			
		||||
static std::regex cover_image_thumbnail_regex(
 | 
			
		||||
    "((?:https?://)?(?:i\\.pximg\\.net)?)" // optional scheme and host
 | 
			
		||||
    "/c/[0-9a-z_-]+(/.+)_master\\d+(\\.\\w{3,4})"
 | 
			
		||||
);
 | 
			
		||||
static inline std::optional<std::string> get_original_cover_image(const std::string& thumbnail);
 | 
			
		||||
static std::regex profile_picture_thumbnail_regex(
 | 
			
		||||
    "((?:https?://)?(?:i\\.pximg\\.net)?)" // optional scheme and host
 | 
			
		||||
    "(/.+)_\\d+(\\.\\w{3,4})"
 | 
			
		||||
);
 | 
			
		||||
static inline std::optional<std::string> get_original_profile_picture(const std::string& thumbnail);
 | 
			
		||||
static inline uint64_t to_ull(const std::string& str);
 | 
			
		||||
 | 
			
		||||
PixivClient::PixivClient() {
 | 
			
		||||
    this->_www_pixiv_net_client.set_keep_alive(true);
 | 
			
		||||
    this->_www_pixiv_net_client.set_default_headers({
 | 
			
		||||
        {"User-Agent", "Mozilla/5.0 (Android 12; Mobile; rv:97.0) Gecko/97.0 Firefox/97.0"}
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
User PixivClient::get_user(uint64_t user_id) {
 | 
			
		||||
    httplib::Result res = this->_www_pixiv_net_client.Get("/touch/ajax/user/details", {
 | 
			
		||||
        {"lang", "en"}, {"id", std::to_string(user_id)}
 | 
			
		||||
    }, httplib::Headers());
 | 
			
		||||
    return this->_handle_result(std::move(res)).at("user_details").get<User>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
nlohmann::json PixivClient::_handle_result(httplib::Result res) {
 | 
			
		||||
    if (!res) {
 | 
			
		||||
        throw HTTPLibException(res.error());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nlohmann::json j = nlohmann::json::parse(std::move(res->body));
 | 
			
		||||
    if (j.at("error")) {
 | 
			
		||||
        throw PixivException(res->status, j.at("message").get<std::string>());
 | 
			
		||||
    }
 | 
			
		||||
    return j.at("body");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void from_json(const nlohmann::json& j, User& user) {
 | 
			
		||||
    j.at("user_account").get_to(user.username);
 | 
			
		||||
    j.at("user_name").get_to(user.display_name);
 | 
			
		||||
    user.user_id = to_ull(j.at("user_id").get_ref<const nlohmann::json::string_t&>());
 | 
			
		||||
 | 
			
		||||
    if (j.contains("cover_image")) {
 | 
			
		||||
        nlohmann::json cover_image = j["cover_image"];
 | 
			
		||||
        std::string c_720x360 = cover_image.at("profile_cover_image").at("720x360").get<std::string>();
 | 
			
		||||
        std::optional<std::string> original = get_original_cover_image(c_720x360);
 | 
			
		||||
 | 
			
		||||
        user.cover_images = {std::move(original), {std::move(c_720x360)}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    nlohmann::json profile_img = j.at("profile_img");
 | 
			
		||||
    if (profile_img.contains("main_s")) {
 | 
			
		||||
        user.profile_pictures.thumbnails.push_back(profile_img["main_s"].get<std::string>());
 | 
			
		||||
    }
 | 
			
		||||
    user.profile_pictures.thumbnails.push_back(profile_img.at("main").get<std::string>());
 | 
			
		||||
    user.profile_pictures.original = get_original_profile_picture(user.profile_pictures.thumbnails.back());
 | 
			
		||||
 | 
			
		||||
    std::string user_webpage = j.at("user_webpage").get<std::string>();
 | 
			
		||||
    if (!user_webpage.empty()) {
 | 
			
		||||
        user.links.push_back({"webpage", std::move(user_webpage)});
 | 
			
		||||
    }
 | 
			
		||||
    auto add_social_as_needed = [&](const char* key) {
 | 
			
		||||
        nlohmann::json social = j.at("social");
 | 
			
		||||
        if (!social.is_object()) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        if (!social.contains(key)) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::string url = social[key].at("url").get<std::string>();
 | 
			
		||||
        user.links.push_back({key, std::move(url)});
 | 
			
		||||
    };
 | 
			
		||||
    add_social_as_needed("twitter");
 | 
			
		||||
    add_social_as_needed("instagram");
 | 
			
		||||
    add_social_as_needed("tumblr");
 | 
			
		||||
    add_social_as_needed("facebook");
 | 
			
		||||
    add_social_as_needed("circlems");
 | 
			
		||||
    add_social_as_needed("pawoo");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline std::optional<std::string> get_original_cover_image(const std::string& thumbnail) {
 | 
			
		||||
    std::smatch sm;
 | 
			
		||||
    if (!std::regex_match(thumbnail, sm, cover_image_thumbnail_regex)) {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
    return sm[1].str() + sm[2].str() + sm[3].str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline std::optional<std::string> get_original_profile_picture(const std::string& thumbnail) {
 | 
			
		||||
    std::smatch sm;
 | 
			
		||||
    if (!std::regex_match(thumbnail, sm, profile_picture_thumbnail_regex)) {
 | 
			
		||||
        return std::nullopt;
 | 
			
		||||
    }
 | 
			
		||||
    return sm[1].str() + sm[2].str() + sm[3].str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint64_t to_ull(const std::string& str) {
 | 
			
		||||
    char* endptr;
 | 
			
		||||
 | 
			
		||||
    errno = 0;
 | 
			
		||||
    unsigned long long res = strtoull(str.c_str(), &endptr, 10);
 | 
			
		||||
    if (endptr[0] != '\0') {
 | 
			
		||||
        throw std::invalid_argument(str + " contains trailing data");
 | 
			
		||||
    } else if (res == ULLONG_MAX && errno == ERANGE) {
 | 
			
		||||
        throw std::overflow_error(str + " overflows uint64_t");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,64 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <optional>
 | 
			
		||||
 | 
			
		||||
#include <httplib/httplib.h>
 | 
			
		||||
#include <nlohmann/json.hpp>
 | 
			
		||||
 | 
			
		||||
struct Images {
 | 
			
		||||
    std::optional<std::string> original;
 | 
			
		||||
    std::vector<std::string> thumbnails;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct User {
 | 
			
		||||
    std::string username;
 | 
			
		||||
    std::string display_name;
 | 
			
		||||
    uint64_t user_id;
 | 
			
		||||
 | 
			
		||||
    std::optional<Images> cover_images;
 | 
			
		||||
    Images profile_pictures;
 | 
			
		||||
    std::vector<std::pair<std::string, std::string>> links;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PixivClient {
 | 
			
		||||
public:
 | 
			
		||||
    PixivClient();
 | 
			
		||||
 | 
			
		||||
    User get_user(uint64_t user_id);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    nlohmann::json _handle_result(httplib::Result res);
 | 
			
		||||
    httplib::Client _www_pixiv_net_client{"https://www.pixiv.net"};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HTTPLibException : public std::exception {
 | 
			
		||||
public:
 | 
			
		||||
    HTTPLibException(httplib::Error error) {
 | 
			
		||||
        this->_message = httplib::to_string(error);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const constexpr char* what() const noexcept {
 | 
			
		||||
        return this->_message.c_str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string _message;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class PixivException : public std::exception {
 | 
			
		||||
public:
 | 
			
		||||
    PixivException(int status_, std::string message) : status(status_), _message(std::move(message)) {}
 | 
			
		||||
 | 
			
		||||
    const constexpr char* what() const noexcept {
 | 
			
		||||
        return this->_message.c_str();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int status;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    std::string _message;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void from_json(const nlohmann::json& j, User& user);
 | 
			
		||||
		Loading…
	
		Reference in New Issue