#include #include #include #include #include "../config.h" #include "views.h" static unordered_map last_votes; #define isAuthed() getOptional("discord_authed").has_value() #define authenticate(cb) cb(HttpResponse::newRedirectionResponse(OAUTH_URL)); return #define toStart(cb) cb(HttpResponse::newRedirectionResponse("/")); return #define cantVote(uid) auto _cantvoteres = last_votes.find(uid); auto now = trantor::Date::date(); if (_cantvoteres != last_votes.end() and now < _cantvoteres->second.after(43200)) views::views() { db = drogon::app().getDbClient(); } void views::start( const HttpRequestPtr&, std::function &&callback ) { callback(HttpResponse::newRedirectionResponse("/bots/@all", HttpStatusCode::k301MovedPermanently)); } Bot deserializeBot(orm::Row row) { Bot thisbot; # define sf(f) thisbot.f = row[#f] sf(name).as(); sf(short_description).as(); sf(long_description).as(); sf(avatar_url).as(); sf(owner).as(); sf(support_server).as(); sf(prefix).as(); sf(votes).as(); sf(approved).as(); thisbot.owner_id = std::stoul(row["owner_id"].as()); thisbot.app_id = std::stoul(row["app_id"].as()); return thisbot; } auto dbErr = [](const orm::DrogonDbException &e) { std::cerr << "Database error:" << e.base().what() << std::endl; }; void views::botlist( const HttpRequestPtr& req, std::function &&callback ) { auto session = req->session(); auto authed = session->isAuthed(); auto justMine = req->getPath()=="/bots/@me"; auto modView = req->getPath()=="/bots/@unapproved"; if (justMine and not authed) { authenticate(callback); } std::string q = "SELECT * FROM bots WHERE "; if (justMine) { q.append("owner_id = '"+std::to_string(session->get("discord_user_id"))+"'"); } else { q.append("approved = "+std::string(modView?"false":"true")); } q.append(" ORDER BY votes"); db->execSqlAsync(q, [justMine, authed, callback] (const orm::Result &rows) { std::map bot_list; for (const auto& r : rows) { Bot bot = deserializeBot(r); bot_list[bot.app_id] = bot; } HttpViewData data; data.insert("modView", false); data.insert("justMine", justMine); data.insert("authed", authed); data.insert("bots", bot_list); callback(HttpResponse::newHttpViewResponse("botlist.csp", data)); }, dbErr); } void views::botdetail( const HttpRequestPtr& req, std::function &&callback, uint64_t bot_id) { db->execSqlAsync("SELECT * FROM bots WHERE app_id = '"+std::to_string(bot_id)+"'", [req, callback] (const orm::Result &rows) { if (rows.empty()) { // Bot not found callback(HttpResponse::newNotFoundResponse()); } else { // Bot found auto bot = deserializeBot(rows[0]); HttpViewData data; data.insert("bot_id", bot.app_id); data.insert("bot", bot); {cantVote(req->session()->get("discord_user_id")) { data.insert("canVote", false); } else { data.insert("canVote", true); }} callback(HttpResponse::newHttpViewResponse("botdetail.csp", data)); } }, dbErr); } void views::botvote(const HttpRequestPtr& req, std::function &&callback, uint64_t bot_id) { auto session = req->session(); // Check if user is authenticated if (not session->isAuthed()) { authenticate(callback); } // Check if user is able to vote again auto user_id = session->get("discord_user_id"); {cantVote(user_id) { callback(HttpResponse::newRedirectionResponse("detail")); return; }} // Register vote db->execSqlAsync("UPDATE bots SET votes = votes + 1 WHERE app_id = '"+std::to_string(bot_id)+"'", [user_id, callback] (const orm::Result &rows) { if (rows.affectedRows() == 0) { // Bot not found callback(HttpResponse::newNotFoundResponse()); } else { last_votes[user_id] = trantor::Date::date(); // Redirect back callback(HttpResponse::newRedirectionResponse("detail")); } }, dbErr); } void views::discorddeauth( const HttpRequestPtr& req, std::function &&callback ) { req->session()->clear(); toStart(callback); } void views::discordauth( const HttpRequestPtr& client_req, std::function &&callback, const std::string& code) { if (code.empty()) { authenticate(callback); } else { // Get token from API auto discordapi = HttpClient::newHttpClient("https://discord.com"); auto req = HttpRequest::newHttpRequest(); req->setPath("/api/v8/oauth2/token"); req->setContentTypeCode(ContentType::CT_APPLICATION_X_FORM); req->setMethod(HttpMethod::Post); { req->setParameter("client_id", CLIENT_ID); req->setParameter("client_secret", CLIENT_SECRET); req->setParameter("redirect_uri", REDIRECT_URI); req->setParameter("grant_type", "authorization_code"); req->setParameter("scope", "identify"); req->setParameter("code", code); } discordapi->sendRequest(req, [discordapi, client_req, callback] (ReqResult, const HttpResponsePtr &response) { // Check for success if (response->getStatusCode() == HttpStatusCode::k200OK) { // Auth success auto &data = *response->getJsonObject().get(); auto session = client_req->session(); auto access_token = data["access_token"].asString(); session->clear(); session->insert("discord_access_token", access_token); // Get user data auto req = HttpRequest::newHttpRequest(); req->setPath("/api/v8/users/@me"); req->setMethod(HttpMethod::Get); req->addHeader("Authorization", "Bearer "+access_token); discordapi->sendRequest(req, [client_req, callback, session] (ReqResult, const HttpResponsePtr &response) { if (response->getStatusCode() == HttpStatusCode::k200OK) { // Getting user data success auto &userdata = *response->getJsonObject().get(); auto fullname = userdata["username"].asString()+'#'+userdata["discriminator"].asString(); session->insert("discord_authed", true); session->insert("discord_user_id", std::stoul(userdata["id"].asString())); session->insert("discord_user_fullname", fullname); // Show success page HttpViewData data; data.insert("fullname", fullname); callback(HttpResponse::newHttpViewResponse("authsuccess.csp", data)); } else { toStart(callback); } }); } else { toStart(callback); } }); } }