Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ add_executable(bot
src/commands/project_cmd.cpp
src/commands/rule_cmd.cpp
src/commands/beginner.cpp
src/commands/help_cmd.cpp

# utils
src/utils/suggestion/suggestion.cpp
Expand Down
46 changes: 44 additions & 2 deletions src/commands/beginner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,50 @@ void cmd::beginnerCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
const dpp::embed embed = dpp::embed()
.set_color(globals::color::defaultColor)
.add_field("👋 New to C++? Start here!", "If you're learning C++, we recommend these resources:\n\n📘 **Learn C++ (Best beginner tutorial)**\n- https://www.learncpp.com/\n\n📖 **CPP Reference (Language & Standard library reference)**\n- https://en.cppreference.com/\n\n🛠️ **Practice**\n1. Build small projects\n2. Read and write lots of code\n\n**Common advice:**\n* ✅ Learn modern C++, not C with classes\n* ✅ Avoid outdated books, videos, and random blog posts that teach old C++ practices\n* ❌ Don't ask ChatGPT or other AI to write your code\n\nIf you're stuck on something specific, ask in the help channels: <#1130494190615265342>.\nBe sure to include your code, any error messages, what you've tried already, and what you expected to happen.");
.set_title("👋 New to C++? Start here!")
.set_url("https://www.learncpp.com/")
.set_description("Essential resources for C++ beginners")
.add_field("📘 **Best Beginner Tutorial**",
"LearnCpp.com is widely considered the best free resource:\nhttps://www.learncpp.com/", false)
.add_field("📖 **CPP Reference**",
"The definitive C++ language reference:\nhttps://en.cppreference.com/", false)
.add_field("🛠️ **Practice**",
"Start with small projects:\n"
"- Calculator\n"
"- Guess game\n"
"- Dice game", false)
.add_field("✅ **Do this**",
"- Learn Modern C++\n"
"- Use a great IDE\n"
"- Practice what you learn\n"
"- Learn OOP basics\n"
"- Write clean, readable code", true)
.add_field("❌ **Dont do this**",
"- Don't learn from outdated C++ resources\n"
"- Don't let AI write code for you\n"
"- Don't use `using namespace std;` (it's bad practice)\n"
"- Don't ignore compiler warnings", true)
.set_footer(dpp::embed_footer()
.set_text("Need help? Ask in <#" + std::to_string(globals::channels::HELP_CHANNEL_ID) + ">"))
.set_timestamp(dpp::utility::time_f());

const dpp::message message(event.command.channel_id, embed);
dpp::message message(event.command.channel_id, embed);
message.add_component(
dpp::component()
.add_component(
dpp::component()
.set_type(dpp::cot_button)
.set_label("Learn C++")
.set_url("https://www.learncpp.com/")
.set_style(dpp::cos_link)
)
.add_component(
dpp::component()
.set_type(dpp::cot_button)
.set_label("CPP Reference")
.set_url("https://en.cppreference.com/")
.set_style(dpp::cos_link)
)
);
event.reply(message);
}
97 changes: 89 additions & 8 deletions src/commands/coding_cmd.cpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,96 @@
#include "commands.h"
#include "../globals/globals.h"
#include <fstream>
#include <random>
#include <algorithm>
#include <map>
#include <vector>
#include <string>

namespace cmd
{
namespace coding
{
const std::map<std::string, std::string> difficultyFiles = {
{"Beginner", "src/res/coding/beginner.txt"},
{"Intermediate", "src/res/coding/intermediate.txt"},
{"Advanced", "src/res/coding/advanced.txt"},
{"Expert", "src/res/coding/expert.txt"},
{"Master", "src/res/coding/master.txt"}
};

std::map<std::string, std::vector<std::string>> questionCache;
bool loaded = false;

void loadQuestions() {
if (loaded) return;

for (const auto& [difficulty, filepath] : difficultyFiles) {
std::ifstream file(filepath);

if (!file.is_open()) {
std::cerr << "Failed to open: " << filepath << std::endl;
continue;
}

std::vector<std::string> questions;
std::string line;

while (std::getline(file, line)) {
if (!line.empty()) {
questions.push_back(line);
}
}
file.close();

std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(questions.begin(), questions.end(), gen);

questionCache[difficulty] = questions;
}
loaded = true;
}

std::string getRandomQuestion(const std::string& difficulty) {
auto it = questionCache.find(difficulty);
if (it == questionCache.end() || it->second.empty()) {
return "No questions available for " + difficulty + " difficulty.";
}

static std::map<std::string, int> indices;
int& index = indices[difficulty];
const std::vector<std::string>& questions = it->second;

std::string question = questions[index % questions.size()];
index++;

return question;
}
}
}

void cmd::codingCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
static int index;
const std::string question = cmd::utils::readFileLine("res/coding.txt", index);
{
coding::loadQuestions();
std::string difficulty = "Beginner";
try {
auto param = event.get_parameter("difficulty");
if (!std::holds_alternative<std::monostate>(param)) {
difficulty = std::get<std::string>(param);
}
}
catch (...) {}

const dpp::embed embed = dpp::embed()
std::string question = coding::getRandomQuestion(difficulty);
dpp::embed embed = dpp::embed()
.set_color(globals::color::defaultColor)
.add_field(question, "");
.set_title("Coding Challenge - " + difficulty)
.set_description(question)
.add_field("Difficulty", difficulty, true)
.add_field("Need help?", "Ask in <#" + std::to_string(globals::channels::HELP_CHANNEL_ID) + ">", true)
.set_footer(dpp::embed_footer().set_text("Good luck! Share your solution in #code-review"))
.set_timestamp(time(0));

const dpp::message message(event.command.channel_id, embed);
event.reply(message);
}
event.reply(embed);
}
25 changes: 16 additions & 9 deletions src/commands/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
#include <dpp/dispatcher.h>

namespace cmd
{
{
/**
* @brief Replies with a question in the chat to change the topic
* @param bot cluster
* @brief Replies with a question in the chat to change the topic
* @param bot cluster
* @param event slash command event
*/
void topicCommand(dpp::cluster& bot, const dpp::slashcommand_t& event);
Expand Down Expand Up @@ -63,36 +63,43 @@ namespace cmd
* @param event slash command event
*/
void ruleCommand(dpp::cluster& bot, const dpp::slashcommand_t& event);

/**
* @brief Replies with a beginner's guide to C++
* @param bot cluster
* @param event slash command event
*/
void beginnerCommand(dpp::cluster& bot, const dpp::slashcommand_t& event);

/**
* @brief Replies whit the avaibles commands
* @param bot cluster
* @param event slash command event
*/
void helpCommand(dpp::cluster& bot, const dpp::slashcommand_t& event);

namespace utils
{
{
/**
* @brief Read next line of file, jump to beginning if no next line
* @param path to the file
* @param index
* @return content of next line
*/
std::string readFileLine(const std::string& path, int& index);
}
}
}

struct cmdStruct
{
{
std::string name;
std::string desc;

typedef std::function<void(dpp::cluster&, dpp::slashcommand_t)> cmdFunc;
cmdFunc function;

std::list<dpp::command_option> args;
std::vector<dpp::command_option> args;
dpp::permissions permissions;
};
};

#endif // COMMANDS_H
43 changes: 43 additions & 0 deletions src/commands/help_cmd.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include "commands.h"
#include "../globals/globals.h"

void cmd::helpCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
dpp::embed helpEmbed = dpp::embed()
.set_color(globals::color::defaultColor)
.set_title("📚 Command List")
.set_description("Here are all the available commands:")
.add_field(
"💻 **Coding Help**",
"- /beginner - Get a beginner's guide to C++\n"
"- /coding - Get a coding question\n"
"- /topic - Get a topic question\n"
"- /project - Get a project idea",
false
)
.add_field(
"🛠️ **Moderation**",
"- /rule - Get the server rules\n"
"- /ticket - Open a ticket\n"
"- /close - Close a ticket",
false
)
.add_field(
"📝 **Utility**",
"- /code - Format code on Discord\n"
"- /help - Show this help message",
false
)
.add_field(
"💡 **Tips**",
"- Use /coding difficulty:Advanced for harder questions\n"
"- Use /rule number:1 to see a specific rule\n"
"- Use /ticket participant:@user to add someone to your ticket",
false
)
.set_footer(dpp::embed_footer()
.set_text("Need more help? Ask in <#" + std::to_string(globals::channels::HELP_CHANNEL_ID) + ">"))
.set_timestamp(dpp::utility::time_f());

event.reply(dpp::message(event.command.channel_id, helpEmbed));
}
5 changes: 5 additions & 0 deletions src/globals/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ namespace globals
static constexpr int defaultColor = 0x004482;
}

namespace channels
{
constexpr dpp::snowflake HELP_CHANNEL_ID = 1130466207431135394ULL;
}

/**
* @brief Load configured IDs used by the bot.
* @param config Parsed config JSON object.
Expand Down
48 changes: 29 additions & 19 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,35 @@ using json = nlohmann::json;
std::vector<cmdStruct> cmdList = {
{ "topic", "Get a topic question", cmd::topicCommand },
{ "beginner", "Get a beginner's guide to C++", cmd::beginnerCommand },
{ "coding", "Get a coding question", cmd::codingCommand },
{ "coding", "Get a coding question", cmd::codingCommand,
{
dpp::command_option(dpp::co_string, "difficulty", "Select difficulty", false)
.add_choice(dpp::command_option_choice("Beginner", "Beginner"))
.add_choice(dpp::command_option_choice("Intermediate", "Intermediate"))
.add_choice(dpp::command_option_choice("Advanced", "Advanced"))
.add_choice(dpp::command_option_choice("Expert", "Expert"))
.add_choice(dpp::command_option_choice("Master", "Master"))
}
},
{ "close", "Close a ticket or forum post", cmd::closeCommand },
{ "ticket", "Open a ticket", cmd::ticketCommand, { dpp::command_option(dpp::command_option_type::co_user, "participant", "Add participant", false) }},
{ "code", "Formatting code on Discord", cmd::codeCommand },
{ "project", "Get a project idea", cmd::projectCommand },
{ "rule", "Get the server rules", cmd::ruleCommand, { dpp::command_option(dpp::command_option_type::co_integer, "number", "Rule to mention", false) }}
};
{ "rule", "Get the server rules", cmd::ruleCommand, { dpp::command_option(dpp::command_option_type::co_integer, "number", "Rule to mention", false) }},
{ "help", "Show all avaible commands", cmd::helpCommand}
};

int main()
{
{
std::ifstream configFile("config.json");
json config = json::parse(configFile);

std::string globalsConfigError;
if (!globals::loadFromConfig(config, globalsConfigError))
{
{
std::cerr << "[!] Invalid configuration: " << globalsConfigError << std::endl;
return 1;
}
}

dpp::cluster bot(config["token"], dpp::i_default_intents | dpp::i_message_content);
ModerationService moderationService(bot);
Expand All @@ -41,10 +51,10 @@ int main()
bot.set_presence(dpp::presence(dpp::presence_status::ps_online, dpp::activity_type::at_watching, "cppdiscord.com"));

if (dpp::run_once<struct bulkRegister>())
{
{
std::vector<dpp::slashcommand> slashcommands;
for (const auto& item : cmdList)
{
{
dpp::slashcommand slashCommand;
slashCommand.set_name(item.name);
slashCommand.set_description(item.desc);
Expand All @@ -57,21 +67,21 @@ int main()
slashCommand.set_default_permissions(dpp::permission(item.permissions));

slashcommands.push_back(slashCommand);
}
}
bot.global_bulk_command_create(slashcommands);
}
});
}
});

bot.on_slashcommand([&bot](const dpp::slashcommand_t& event) {
for (const auto& item : cmdList)
{
if (item.name == event.command.get_command_name())
{
if (item.name == event.command.get_command_name())
{
item.function(bot, event);
return;
}
}
}
});
});

bot.on_message_create([&bot, &moderationService](const dpp::message_create_t& event) {
if (moderationService.handleMessage(event))
Expand All @@ -81,7 +91,7 @@ int main()

if (channel && channel->name == "suggestions")
utils::suggestion::createSuggestion(bot, event);
});
});

bot.on_button_click([&bot](const dpp::button_click_t& event) {
if (event.custom_id == "delSuggestion")
Expand All @@ -90,13 +100,13 @@ int main()
utils::suggestion::editSuggestion(bot, event);
else if (event.custom_id.starts_with("hint_button_"))
cmd::handleProjectHintButton(bot, event);
});
});

bot.on_form_submit([&bot](const dpp::form_submit_t& event) {
if (event.custom_id == "editModal")
utils::suggestion::showSuggestionEditModal(bot, event);
});
});

bot.start(dpp::st_wait);
return 0;
}
}
Loading