mirror of
https://gitlab.com/niansa/discordlistforbots.git
synced 2025-03-06 20:49:22 +01:00
202 lines
7.6 KiB
C++
202 lines
7.6 KiB
C++
#include <string>
|
|
#include <unordered_map>
|
|
#include <fstream>
|
|
#include <drogon/drogon.h>
|
|
#include "../config.h"
|
|
#include "views.h"
|
|
|
|
static unordered_map <uint64_t, trantor::Date> last_votes;
|
|
#define isAuthed() getOptional<bool>("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<void (const HttpResponsePtr &)> &&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<std::string>();
|
|
sf(short_description).as<std::string>();
|
|
sf(long_description).as<std::string>();
|
|
sf(avatar_url).as<std::string>();
|
|
sf(owner).as<std::string>();
|
|
sf(support_server).as<std::string>();
|
|
sf(prefix).as<std::string>();
|
|
sf(votes).as<uint32_t>();
|
|
sf(approved).as<bool>();
|
|
thisbot.owner_id = std::stoul(row["owner_id"].as<std::string>());
|
|
thisbot.app_id = std::stoul(row["app_id"].as<std::string>());
|
|
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<void (const HttpResponsePtr &)> &&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<uint64_t>("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<uint64_t, Bot> 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<void (const HttpResponsePtr &)> &&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<uint64_t>("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<void (const HttpResponsePtr &)> &&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<uint64_t>("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<void (const HttpResponsePtr &)> &&callback
|
|
) {
|
|
req->session()->clear();
|
|
toStart(callback);
|
|
}
|
|
|
|
void views::discordauth(
|
|
const HttpRequestPtr& client_req, std::function<void (const HttpResponsePtr &)> &&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);
|
|
}
|
|
});
|
|
}
|
|
}
|