mirror of
https://gitlab.com/niansa/discordlistforbots.git
synced 2025-03-06 20:49:22 +01:00
Initial commit
This commit is contained in:
commit
980c9ffb95
15 changed files with 749 additions and 0 deletions
37
.gitignore
vendored
Normal file
37
.gitignore
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
build
|
||||
cmake-build-debug
|
||||
.idea
|
||||
CMakeLists.txt.user
|
68
CMakeLists.txt
Normal file
68
CMakeLists.txt
Normal file
|
@ -0,0 +1,68 @@
|
|||
cmake_minimum_required(VERSION 3.5)
|
||||
project(discordlistforbots CXX)
|
||||
|
||||
include(CheckIncludeFileCXX)
|
||||
|
||||
check_include_file_cxx(any HAS_ANY)
|
||||
check_include_file_cxx(string_view HAS_STRING_VIEW)
|
||||
if(HAS_ANY AND HAS_STRING_VIEW)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
else()
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
add_executable(${PROJECT_NAME} main.cc)
|
||||
|
||||
# ##############################################################################
|
||||
# If you include the drogon source code locally in your project, use this method
|
||||
# to add drogon
|
||||
# add_subdirectory(drogon)
|
||||
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
|
||||
# ##############################################################################
|
||||
|
||||
find_package(Drogon CONFIG REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon)
|
||||
|
||||
if(CMAKE_CXX_STANDARD LESS 17)
|
||||
# With C++14, use boost to support any and string_view
|
||||
message(STATUS "use c++14")
|
||||
find_package(Boost 1.61.0 REQUIRED)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE ${Boost_INCLUDE_DIRS})
|
||||
else()
|
||||
message(STATUS "use c++17")
|
||||
endif()
|
||||
|
||||
aux_source_directory(controllers CTL_SRC)
|
||||
aux_source_directory(filters FILTER_SRC)
|
||||
aux_source_directory(plugins PLUGIN_SRC)
|
||||
aux_source_directory(models MODEL_SRC)
|
||||
|
||||
drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
# use the following line to create views with namespaces.
|
||||
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
|
||||
# ${CMAKE_CURRENT_BINARY_DIR} TRUE)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/models)
|
||||
target_sources(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${SRC_DIR}
|
||||
${CTL_SRC}
|
||||
${FILTER_SRC}
|
||||
${PLUGIN_SRC}
|
||||
${MODEL_SRC})
|
||||
# ##############################################################################
|
||||
# uncomment the following line for dynamically loading views
|
||||
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)
|
||||
|
||||
file(GLOB files "static/*")
|
||||
foreach(file ${files})
|
||||
get_filename_component(filename "${file}" NAME)
|
||||
message("${file}: ${filename}")
|
||||
configure_file("${file}" "./${filename}" COPYONLY)
|
||||
endforeach()
|
57
bots.sql
Normal file
57
bots.sql
Normal file
|
@ -0,0 +1,57 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
-- Dumped from database version 13.1 (Ubuntu 13.1-1.pgdg20.04+1)
|
||||
-- Dumped by pg_dump version 13.1 (Ubuntu 13.1-1.pgdg20.04+1)
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET idle_in_transaction_session_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_table_access_method = heap;
|
||||
|
||||
--
|
||||
-- Name: bots; Type: TABLE; Schema: public; Owner: nils
|
||||
--
|
||||
|
||||
CREATE TABLE public.bots (
|
||||
name text,
|
||||
short_description text,
|
||||
long_description text,
|
||||
avatar_url text,
|
||||
owner text,
|
||||
support_server text,
|
||||
prefix text,
|
||||
owner_id text,
|
||||
app_id text,
|
||||
votes integer,
|
||||
approved boolean
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.bots OWNER TO nils;
|
||||
|
||||
--
|
||||
-- Data for Name: bots; Type: TABLE DATA; Schema: public; Owner: nils
|
||||
--
|
||||
|
||||
COPY public.bots (name, short_description, long_description, avatar_url, owner, support_server, prefix, owner_id, app_id, votes, approved) FROM stdin;
|
||||
DFB This Bot is for the Server DFB This bot was created for our Discordlist for Bots Server https://cdn.discordapp.com/avatars/795612465130897420/c3bd0733f876a664b4b79ec03866f131.png Julius#1755 42vDtZxZSt dfb? 703944517048598568 795612465130897420 3744 t
|
||||
Tuxiflux A fun but simple bot with globalchat Tuxiflux is a funny, useful and intuitive bot for server moderation and play with in-bot money. https://cdn.discordapp.com/embed/avatars/2.png Tuxifan#4660 6smrmKkjP7 t# 609486822715818000 788310535799308288 7 t
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
14
config.h
Normal file
14
config.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#define LISTEN_ADDR "0.0.0.0"
|
||||
#define LISTEN_PORT 8082
|
||||
|
||||
#define DB_TYPE "postgresql"
|
||||
#define DB_HOST "localhost"
|
||||
#define DB_PORT 5432
|
||||
#define DB_NAME "dfb"
|
||||
#define DB_USER "nils"
|
||||
#define DB_PASSWORD "1234"
|
||||
|
||||
#define OAUTH_URL "https://discord.com/api/oauth2/authorize?client_id=797565592835457024&redirect_uri=http%3A%2F%2Flocalhost%3A8082%2Fdiscordauth&response_type=code&scope=identify"
|
||||
#define CLIENT_ID "797565592835457024"
|
||||
#define CLIENT_SECRET "8ZbKPseob8n1UmLLunPb06MNUKfPGRi1"
|
||||
#define REDIRECT_URI "http://localhost:8082/discordauth"
|
202
controllers/views.cc
Normal file
202
controllers/views.cc
Normal file
|
@ -0,0 +1,202 @@
|
|||
#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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
34
controllers/views.h
Normal file
34
controllers/views.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <drogon/HttpController.h>
|
||||
using namespace std;
|
||||
|
||||
|
||||
struct Bot {
|
||||
string name, short_description, long_description, avatar_url, owner, support_server, prefix;
|
||||
uint64_t owner_id, app_id;
|
||||
uint32_t votes = 0;
|
||||
bool approved = false;
|
||||
};
|
||||
|
||||
using namespace drogon;
|
||||
class views: public drogon::HttpController<views> {
|
||||
orm::DbClientPtr db;
|
||||
public:
|
||||
views();
|
||||
void start(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&);
|
||||
void botlist(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&);
|
||||
void botdetail(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&, uint64_t);
|
||||
void botvote(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&, uint64_t);
|
||||
void discordauth(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&, const std::string&);
|
||||
void discorddeauth(const HttpRequestPtr&, std::function<void (const HttpResponsePtr &)> &&);
|
||||
|
||||
METHOD_LIST_BEGIN
|
||||
ADD_METHOD_TO(views::start, "/", Get);
|
||||
ADD_METHOD_TO(views::botlist, "/bots/@all", Get);
|
||||
ADD_METHOD_TO(views::botlist, "/bots/@me", Get);
|
||||
ADD_METHOD_TO(views::botdetail, "/bots/{1}/detail", Get);
|
||||
ADD_METHOD_TO(views::botvote, "/bots/{1}/vote", Get);
|
||||
ADD_METHOD_TO(views::discordauth, "/discordauth?code={1}", Get);
|
||||
ADD_METHOD_TO(views::discorddeauth, "/discorddeauth", Get);
|
||||
METHOD_LIST_END
|
||||
};
|
10
main.cc
Normal file
10
main.cc
Normal file
|
@ -0,0 +1,10 @@
|
|||
#include <drogon/drogon.h>
|
||||
#include "config.h"
|
||||
|
||||
int main() {
|
||||
//Set HTTP listener address and port
|
||||
drogon::app().addListener(LISTEN_ADDR, LISTEN_PORT).enableSession(std::chrono::minutes(1200)).createDbClient(DB_TYPE, DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD);
|
||||
//Run HTTP framework, the method will block in the internal event loop
|
||||
drogon::app().run();
|
||||
return 0;
|
||||
}
|
86
models/model.json
Normal file
86
models/model.json
Normal file
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
//rdbms: server type, postgresql,mysql or sqlite3
|
||||
"rdbms": "postgresql",
|
||||
//filename: sqlite3 db file name
|
||||
//"filename":"",
|
||||
//host: server address,localhost by default;
|
||||
"host": "127.0.0.1",
|
||||
//port: server port, 5432 by default;
|
||||
"port": 5432,
|
||||
//dbname: Database name;
|
||||
"dbname": "",
|
||||
//schema: valid for postgreSQL, "public" by default;
|
||||
"schema": "public",
|
||||
//user: User name
|
||||
"user": "",
|
||||
//password or passwd: Password
|
||||
"password": "",
|
||||
//client_encoding: The character set used by drogon_ctl. it is empty string by default which
|
||||
//means use the default character set.
|
||||
//"client_encoding": "",
|
||||
//table: An array of tables to be modelized. if the array is empty, all revealed tables are modelized.
|
||||
"tables": [],
|
||||
"relationships": {
|
||||
"enabled": false,
|
||||
"items": [{
|
||||
"type": "has one",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "skus",
|
||||
"target_table_alias": "SKU",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "has many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "product",
|
||||
"original_key": "id",
|
||||
"target_table_name": "reviews",
|
||||
"target_table_alias": "",
|
||||
"target_key": "product_id",
|
||||
"enable_reverse": true
|
||||
},
|
||||
{
|
||||
"type": "many to many",
|
||||
"original_table_name": "products",
|
||||
"original_table_alias": "",
|
||||
"original_key": "id",
|
||||
"pivot_table": {
|
||||
"table_name": "carts_products",
|
||||
"original_key": "product_id",
|
||||
"target_key": "cart_id"
|
||||
},
|
||||
"target_table_name": "carts",
|
||||
"target_table_alias": "",
|
||||
"target_key": "id",
|
||||
"enable_reverse": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"restful_api_controllers": {
|
||||
"enabled": false,
|
||||
// resource_uri: The URI to access the resource, the default value
|
||||
// is '/*' in which the asterisk represents the table name.
|
||||
// If this option is set to a empty string, the URI is composed of the namespaces and the class name.
|
||||
"resource_uri": "/*",
|
||||
// class_name: "Restful*Ctrl" by default, the asterisk represents the table name.
|
||||
// This option can contain namespaces.
|
||||
"class_name": "Restful*Ctrl",
|
||||
// filters: an array of filter names.
|
||||
"filters": [],
|
||||
// db_client: the database client used by the controller. this option must be consistent with
|
||||
// the configuration of the application.
|
||||
"db_client": {
|
||||
//name: Name of the client,'default' by default
|
||||
"name": "default",
|
||||
//is_fast:
|
||||
"is_fast": false
|
||||
},
|
||||
// directory: The directory where the controller source files are stored.
|
||||
"directory": "controllers",
|
||||
// generate_base_only: false by default. Set to true to avoid overwriting custom subclasses.
|
||||
"generate_base_only": false
|
||||
}
|
||||
}
|
66
static/botdetail.css
Normal file
66
static/botdetail.css
Normal file
|
@ -0,0 +1,66 @@
|
|||
.bot-flex {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.bot-flex * {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.bot-image {
|
||||
max-height: 175px !important;
|
||||
max-width: 175px !important;
|
||||
}
|
||||
|
||||
.bot-text {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.long-description {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.overview {
|
||||
padding-top: 16px;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.overview-key {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.actionsWrapper {
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 900px) {
|
||||
.bot-flex {
|
||||
flex-direction: column;
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.bot-text {
|
||||
padding-left: 0px;
|
||||
padding-top: 16px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
.container {
|
||||
margin: 5%;
|
||||
}
|
||||
.actions {
|
||||
flex-direction: row;
|
||||
}
|
||||
.actionsWrapper {
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
BIN
static/favicon.ico
Normal file
BIN
static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
45
static/global.css
Normal file
45
static/global.css
Normal file
|
@ -0,0 +1,45 @@
|
|||
body {
|
||||
background-color:#323232;
|
||||
color:#FFFFFF;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 20%;
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.linkButton {
|
||||
background-color:transparent;
|
||||
border-radius:4px;
|
||||
border:2px solid #ffffff;
|
||||
display:inline-block;
|
||||
cursor:pointer;
|
||||
color:#ffffff;
|
||||
font-family:Arial;
|
||||
font-size:15px;
|
||||
text-align:center;
|
||||
padding:9px 23px;
|
||||
text-decoration:none;
|
||||
text-shadow:0px 0px 11px #263666;
|
||||
margin:10px;
|
||||
}
|
||||
.linkButton:hover {
|
||||
background-color: #fff;
|
||||
color: #263666;
|
||||
text-shadow: 0px;
|
||||
transform: scale(1.03);
|
||||
}
|
||||
.linkButton:active {
|
||||
position:relative;
|
||||
top:1px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 30px
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
BIN
static/logo.png
Normal file
BIN
static/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
21
views/authsuccess.csp
Normal file
21
views/authsuccess.csp
Normal file
|
@ -0,0 +1,21 @@
|
|||
<%inc#include "controllers/views.h" %>
|
||||
<%c++ auto fullname = @@.get<std::string>("fullname");%>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
<meta http-equiv = "refresh" content = "2; url = /" />
|
||||
<title>Authentication success - DFB</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<p class="title text-center">Authenticated as: {%fullname%}</p>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
62
views/botdetail.csp
Normal file
62
views/botdetail.csp
Normal file
|
@ -0,0 +1,62 @@
|
|||
<%inc#include "controllers/views.h" %>
|
||||
<%c++ auto bot_id = @@.get<uint64_t>("bot_id");%>
|
||||
<%c++ auto bot = @@.get<Bot>("bot");%>
|
||||
<%c++ auto canVote = @@.get<bool>("canVote");%>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
<link rel="stylesheet" href="/botdetail.css">
|
||||
<title>{%bot.name%} - DFB</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class="text text-center"></h1>
|
||||
<div class="bot-flex">
|
||||
|
||||
<img class="bot-image" src="{%bot.avatar_url%}"></img>
|
||||
<div class="bot-text text text-center title">
|
||||
{%bot.name%}
|
||||
</div>
|
||||
<div class="actionsWrapper">
|
||||
<div class="actions">
|
||||
<a class="linkButton" href="https://discord.com/oauth2/authorize?client_id={%bot_id%}&permissions=8&scope=applications.commands%20bot">Invite</a>
|
||||
<a class="linkButton" {%(canVote?"href='vote'":"href='#' style='color:grey;'")%}>Vote</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<br>
|
||||
<div class="text long-description">
|
||||
{%bot.long_description%}
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
<div class="text overview">
|
||||
<h2>Overview</h2>
|
||||
<table>
|
||||
<tr>
|
||||
<td class="overview-key">Prefix</td>
|
||||
<td class="overview-value">{%bot.prefix%}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="overview-key">Owner</td>
|
||||
<td class="overview-value">{%bot.owner%}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="overview-key">Votes</td>
|
||||
<td class="overview-value">{%bot.votes%}</td>
|
||||
</tr>
|
||||
</table>
|
||||
<br>
|
||||
<a class="linkButton" style="margin:0px;" href="https://discord.gg/{%bot.support_server%}">Support Server</a>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
47
views/botlist.csp
Normal file
47
views/botlist.csp
Normal file
|
@ -0,0 +1,47 @@
|
|||
<%inc#include "controllers/views.h" %>
|
||||
<%c++ auto viewHidden = @@.get<bool>("viewHidden");%>
|
||||
<%c++ auto justMine = @@.get<bool>("justMine");%>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="/global.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>DFB</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container text-center">
|
||||
<img style="width:15%;height:15%;" src="/logo.png">
|
||||
<p class="title">Discordlist for Bots</p>
|
||||
<p>Find a lot of bots that will be useful to your server</p>
|
||||
<%c++ if (@@.get<bool>("authed")) {%>
|
||||
<%c++ if (justMine) {%>
|
||||
<a class="linkButton" href="@all">All bots</a>
|
||||
<%c++ } else {%>
|
||||
<a class="linkButton" href="@me">My bots</a>
|
||||
<%c++ }%>
|
||||
<a class="linkButton" href="/discorddeauth">Logout</a>
|
||||
<%c++ } else {%>
|
||||
<a class="linkButton" href="/discordauth">Login</a>
|
||||
<%c++ }%>
|
||||
<hr>
|
||||
<br>
|
||||
<%c++ for (const auto& [bot_id, bot] : @@.get<std::map<uint64_t, Bot>>("bots")) {%>
|
||||
<%c++ if (bot.approved == viewHidden) continue;%>
|
||||
<a class="linkButton" href="{%bot_id%}/detail">
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<img src="{%bot.avatar_url%}" style="width:128px;height:128px;">
|
||||
</td>
|
||||
<td style="padding:10px;">
|
||||
<h1 style="line-height:0px;">{%bot.name%}</h1>
|
||||
<p>{%bot.short_description%}</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a>
|
||||
<%c++ }%>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue