Skip to content
Closed
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
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,18 @@ else()
message(STATUS "Using system-installed DPP")
endif()

file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/config.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src/config.json)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/config.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
else()
message(STATUS "src/config.json not found; skipping config copy")
endif()
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/src/res DESTINATION ${CMAKE_CURRENT_BINARY_DIR})

add_executable(bot
src/main.cpp

#globals
src/globals/globals.cpp
src/globals/globals.h

# commands
Expand All @@ -44,6 +49,7 @@ add_executable(bot
src/commands/code_cmd.cpp
src/commands/project_cmd.cpp
src/commands/rule_cmd.cpp
src/commands/beginner.cpp

# utils
src/utils/suggestion/suggestion.cpp
Expand Down
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,20 @@
```git clone https://github.com/cppdiscord/bot.git```

#### 2. Create the config file
Create a file called config.json in the src directory: ``{ "token": "bot token" }``
Create a file called config.json in the src directory with the following values:

```json
{
"token": "your bot token",
"emoji_yes_id": "your yes emoji id",
"emoji_no_id": "your no emoji id",
"channel_rules_id": "your rules channel id",
"channel_jail_id": "your jail channel id",
"category_ticket_id": "your ticket category id",
"role_staff_id": "your staff role id",
"role_jail_id": "your jail role id"
}
```

## Contributing
We appreciate contributions to this community. Connect with other contributors through our [discord server](https://discord.gg/cpp), where we have dedicated channels for chatting and collaborating.
Expand Down
54 changes: 54 additions & 0 deletions src/commands/beginner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#include "commands.h"
#include "../globals/globals.h"

void cmd::beginnerCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
const dpp::embed embed = dpp::embed()
.set_color(globals::color::defaultColor)
.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());

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);
}
30 changes: 22 additions & 8 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 @@ -50,35 +50,49 @@ namespace cmd
*/
void projectCommand(dpp::cluster& bot, const dpp::slashcommand_t& event);

/**
* @brief Handles hint button clicks for project ideas
* @param bot cluster
* @param event button click event
*/
void handleProjectHintButton(dpp::cluster& bot, const dpp::button_click_t& event);

/**
* @brief Replies with the rules
* @param bot cluster
* @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);

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
65 changes: 31 additions & 34 deletions src/commands/project_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,37 @@

using json = nlohmann::json;

void cmd::handleProjectHintButton(dpp::cluster& bot, const dpp::button_click_t& event)
{
// Remove button
dpp::message updatedMsg = event.command.get_context_message();
updatedMsg.components.clear();
bot.message_edit(updatedMsg);

json data;
try
{
std::ifstream projectFile("res/project.json");
projectFile >> data;
}
catch (const json::parse_error& e)
{
event.reply("Failed to parse project file.");
return;
}

const int hintButtonIndex = std::stoi(event.custom_id.substr(event.custom_id.rfind('_') + 1));
const auto& project = data["projects"][hintButtonIndex];
const std::string hint = project.contains("hint") ? project["hint"] : "No hint available.";

dpp::embed hintEmbed = dpp::embed()
.set_color(globals::color::defaultColor)
.add_field("Hint", hint);

dpp::message hintMessage(event.command.channel_id, hintEmbed);
event.reply(hintMessage);
}

void cmd::projectCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
static int index = 0;
Expand Down Expand Up @@ -68,39 +99,5 @@ void cmd::projectCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)

event.reply(message);

bot.on_button_click([&bot](const dpp::button_click_t& event) {
// Ignore if button id does not start with hint_button_
if (event.custom_id.rfind("hint_button_", 0) != 0)
return;

// Remove button
dpp::message updatedMsg = event.command.get_context_message();
updatedMsg.components.clear();
bot.message_edit(updatedMsg);

json data;
try
{
std::ifstream projectFile("res/project.json");
projectFile >> data;
}
catch (const json::parse_error& e)
{
event.reply("Failed to parse project file.");
return;
}

const int hintButtonIndex = std::stoi(event.custom_id.substr(event.custom_id.rfind('_') + 1));
const auto& project = data["projects"][hintButtonIndex];
const std::string hint = project.contains("hint") ? project["hint"] : "No hint available.";

dpp::embed hintEmbed = dpp::embed()
.set_color(globals::color::defaultColor)
.add_field("Hint", hint);

dpp::message hintMessage(event.command.channel_id, hintEmbed);
event.reply(hintMessage);
});

index++;
}
1 change: 0 additions & 1 deletion src/config.json

This file was deleted.

10 changes: 10 additions & 0 deletions src/config.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"token": "bot token",
"emoji_yes_id": "1226134958872199229",
"emoji_no_id": "1226134940006219817",
"channel_rules_id": "1130464978860785705",
"channel_jail_id": "1513269975844917409",
"category_ticket_id": "1234179713182732374",
"role_staff_id": "1130473404345110621",
"role_jail_id": "1506351798900887582"
}
Loading