From 980c9ffb954ebddd60f503eb1ca76d8a74b22450 Mon Sep 17 00:00:00 2001 From: niansa Date: Sun, 10 Jan 2021 17:29:59 +0100 Subject: [PATCH] Initial commit --- .gitignore | 37 ++++++++ CMakeLists.txt | 68 ++++++++++++++ bots.sql | 57 ++++++++++++ config.h | 14 +++ controllers/views.cc | 202 ++++++++++++++++++++++++++++++++++++++++++ controllers/views.h | 34 +++++++ main.cc | 10 +++ models/model.json | 86 ++++++++++++++++++ static/botdetail.css | 66 ++++++++++++++ static/favicon.ico | Bin 0 -> 1086 bytes static/global.css | 45 ++++++++++ static/logo.png | Bin 0 -> 34282 bytes views/authsuccess.csp | 21 +++++ views/botdetail.csp | 62 +++++++++++++ views/botlist.csp | 47 ++++++++++ 15 files changed, 749 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 bots.sql create mode 100644 config.h create mode 100644 controllers/views.cc create mode 100644 controllers/views.h create mode 100644 main.cc create mode 100644 models/model.json create mode 100644 static/botdetail.css create mode 100644 static/favicon.ico create mode 100644 static/global.css create mode 100644 static/logo.png create mode 100644 views/authsuccess.csp create mode 100644 views/botdetail.csp create mode 100644 views/botlist.csp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..613beba --- /dev/null +++ b/.gitignore @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..3b1061e --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/bots.sql b/bots.sql new file mode 100644 index 0000000..cb13120 --- /dev/null +++ b/bots.sql @@ -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 +-- + diff --git a/config.h b/config.h new file mode 100644 index 0000000..935bb82 --- /dev/null +++ b/config.h @@ -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" diff --git a/controllers/views.cc b/controllers/views.cc new file mode 100644 index 0000000..01b824f --- /dev/null +++ b/controllers/views.cc @@ -0,0 +1,202 @@ +#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); + } + }); + } +} diff --git a/controllers/views.h b/controllers/views.h new file mode 100644 index 0000000..62406ad --- /dev/null +++ b/controllers/views.h @@ -0,0 +1,34 @@ +#pragma once +#include +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 { + orm::DbClientPtr db; +public: + views(); + void start(const HttpRequestPtr&, std::function &&); + void botlist(const HttpRequestPtr&, std::function &&); + void botdetail(const HttpRequestPtr&, std::function &&, uint64_t); + void botvote(const HttpRequestPtr&, std::function &&, uint64_t); + void discordauth(const HttpRequestPtr&, std::function &&, const std::string&); + void discorddeauth(const HttpRequestPtr&, std::function &&); + + 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 +}; diff --git a/main.cc b/main.cc new file mode 100644 index 0000000..737642c --- /dev/null +++ b/main.cc @@ -0,0 +1,10 @@ +#include +#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; +} diff --git a/models/model.json b/models/model.json new file mode 100644 index 0000000..cdc1d14 --- /dev/null +++ b/models/model.json @@ -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 + } +} diff --git a/static/botdetail.css b/static/botdetail.css new file mode 100644 index 0000000..4240c0d --- /dev/null +++ b/static/botdetail.css @@ -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; +} +} diff --git a/static/favicon.ico b/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7482460bf496d11636547a9129a13f4c3f9a0ac7 GIT binary patch literal 1086 zcmchWy-EW?6os!z6l~m}q9_(AMAE4sK7emwX&1y!Y@>y(APByKg^iW<7B(Ulia(%W zkw!$&7tkHQv)mCQn?ymJ<=Z>w-g{=2-C46fuB2+*YgVtAjhR^;5~zXl8fiHX|9%T0 z7?fcFhFmKr5}1Yg0_Mn#!U~+gBt&07^L==QmjWKi9eDnpI04aHWquBwMn>V?i+99M z-cxFO@a(-U;x1gf?&e+2vP{o&=)GD;dfjz5?-4y~@Zi0d#0I3UzkBtL&%D+v+|n=( z@m-aeZ@>j;A8`XuaOFDYo9GJUXH3!cFj3!e1ZH3drd%tp`lqkxfKu05>fMGx>7NYs zIg7+&(EfR@mp}FqwWr1^{MM^8yb06t>YM-N2rL&`{M#k7t*vX{Et=W#eA7DU6 ANdN!< literal 0 HcmV?d00001 diff --git a/static/global.css b/static/global.css new file mode 100644 index 0000000..66268a7 --- /dev/null +++ b/static/global.css @@ -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; +} diff --git a/static/logo.png b/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..18fb0df4fabbf903f53b3e9d90187c158332c54b GIT binary patch literal 34282 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¬OKD}X_3ZLBJd>3^ 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 s^_O;JCEVQ2SqWnOzn}k~Prwh~# +<%c++ auto fullname = @@.get("fullname");%> + + + + + + + + + + Authentication success - DFB + + + +
+

Authenticated as: {%fullname%}

+
+ + + diff --git a/views/botdetail.csp b/views/botdetail.csp new file mode 100644 index 0000000..b8dcb2b --- /dev/null +++ b/views/botdetail.csp @@ -0,0 +1,62 @@ +<%inc#include "controllers/views.h" %> +<%c++ auto bot_id = @@.get("bot_id");%> +<%c++ auto bot = @@.get("bot");%> +<%c++ auto canVote = @@.get("canVote");%> + + + + + + + + + + {%bot.name%} - DFB + + + +
+

+
+ + +
+ {%bot.name%} +
+
+
+ Invite + Vote +
+
+
+
+
+
+ {%bot.long_description%} +
+ +
+
+

Overview

+ + + + + + + + + + + + + +
Prefix{%bot.prefix%}
Owner{%bot.owner%}
Votes{%bot.votes%}
+
+ Support Server +
+
+ + + diff --git a/views/botlist.csp b/views/botlist.csp new file mode 100644 index 0000000..0f3205a --- /dev/null +++ b/views/botlist.csp @@ -0,0 +1,47 @@ +<%inc#include "controllers/views.h" %> +<%c++ auto viewHidden = @@.get("viewHidden");%> +<%c++ auto justMine = @@.get("justMine");%> + + + + + + + DFB + + +
+ +

Discordlist for Bots

+

Find a lot of bots that will be useful to your server

+ <%c++ if (@@.get("authed")) {%> + <%c++ if (justMine) {%> + All bots + <%c++ } else {%> + My bots + <%c++ }%> + Logout + <%c++ } else {%> + Login + <%c++ }%> +
+
+ <%c++ for (const auto& [bot_id, bot] : @@.get>("bots")) {%> + <%c++ if (bot.approved == viewHidden) continue;%> + + + + + + +
+ + +

{%bot.name%}

+

{%bot.short_description%}

+
+
+ <%c++ }%> +
+ +