Skip to content

Enhance command handling and validation across multiple commands REPOST #23

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
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
58 changes: 37 additions & 21 deletions src/commands/close_cmd.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "commands.h"
#include "commands.h"
#include "../globals/globals.h"

#include <dpp/channel.h>
Expand All @@ -8,18 +8,29 @@

void cmd::closeCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
if (event.command.channel.get_type() == dpp::channel_type::CHANNEL_PUBLIC_THREAD)
const dpp::channel* channel = dpp::find_channel(event.command.channel_id);
if (!channel)
{
event.reply(dpp::message("[!] Channel not found").set_flags(dpp::m_ephemeral));
return;
}

if (channel->get_type() == dpp::channel_type::CHANNEL_PUBLIC_THREAD)
{
bot.thread_get(event.command.channel_id, [&bot, event](const dpp::confirmation_callback_t& callback) {
if (callback.is_error())
return event.reply(dpp::message("[!] Callback error").set_flags(dpp::m_ephemeral));

auto thread = callback.get<dpp::thread>();
dpp::thread thread = callback.get<dpp::thread>();

if (event.command.channel.owner_id != event.command.member.user_id)
return event.reply(dpp::message("You can only close your own posts.").set_flags(dpp::m_ephemeral));
bool isOwner = (thread.owner_id == event.command.member.user_id);
bool hasManagePermission = event.command.member.has_permission(thread.guild_id, dpp::p_manage_threads);

if (!isOwner && !hasManagePermission)
return event.reply(dpp::message("You can only close your own posts or you need manage threads permission.").set_flags(dpp::m_ephemeral));

thread.metadata.locked = true;
thread.metadata.archived = true;

const std::string newThreadName = dpp::unicode_emoji::lock + std::string(" ") + thread.name;
thread.set_name(newThreadName);
Expand All @@ -37,25 +48,30 @@ void cmd::closeCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
});
});
}
else if (event.command.channel.parent_id == globals::category::ticketId)
else if (channel->parent_id == globals::category::ticketId)
{
event.reply(dpp::message("Closed ticket!"));

bot.channel_get(event.command.channel.id, [&bot, event](const dpp::confirmation_callback_t& callback) {
if (!callback.is_error())
bool isOwner = false;
bool hasManagePermission = event.command.member.has_permission(channel->guild_id, dpp::p_manage_channels);

for (const auto& overwrite : channel->permission_overwrites)
{
if (overwrite.id == event.command.member.user_id && overwrite.type == dpp::overwrite_type::ot_member)
{
dpp::channel ticketChannel = std::get<dpp::channel>(callback.value);
std::vector<dpp::permission_overwrite> overwrites = ticketChannel.permission_overwrites;

for (const auto& overwrite : overwrites)
{
if (overwrite.type == dpp::overwrite_type::ot_member)
{
bot.channel_edit_permissions(ticketChannel, overwrite.id, 0, dpp::p_view_channel, true);
}
}
isOwner = true;
break;
}
});
}

if (!isOwner && !hasManagePermission)
{
event.reply(dpp::message("You can only close your own tickets or you need manage channels permission.").set_flags(dpp::m_ephemeral));
return;
}

event.reply(dpp::message("Closed ticket!"));

// Remove user's access to the ticket
bot.channel_edit_permissions(*channel, event.command.member.user_id, 0, dpp::p_view_channel, true);
}
else
{
Expand Down
3 changes: 2 additions & 1 deletion src/commands/commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define COMMANDS_H

#include <list>
#include <functional>
#include <dpp/dpp.h>
#include <dpp/cluster.h>
#include <dpp/dispatcher.h>
Expand Down Expand Up @@ -74,7 +75,7 @@ struct cmdStruct
std::string name;
std::string desc;

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

std::list<dpp::command_option> args;
Expand Down
47 changes: 7 additions & 40 deletions src/commands/project_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ void cmd::projectCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
return;
}

// If index exceeds the number of projects, reset it to 0
if (index >= data["projects"].size())
if (data["projects"].empty())
{
index = 0;
event.reply("No projects available.");
return;
}

index = index % data["projects"].size();

const auto& project = data["projects"][index];
if (!project.contains("title") || !project.contains("description"))
{
Expand All @@ -55,7 +57,6 @@ void cmd::projectCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)

dpp::message message(event.command.channel_id, embed);

// Add hint button with index as custom_id
message.add_component(
dpp::component().add_component(
dpp::component()
Expand All @@ -67,40 +68,6 @@ 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++;

index = (index + 1) % data["projects"].size();
}
10 changes: 5 additions & 5 deletions src/commands/rule_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ void cmd::ruleCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
};

const dpp::command_interaction cmdData = event.command.get_command_interaction();

if (cmdData.options.empty())
return event.reply(dpp::message("Please follow our <#" + globals::channel::rulesId.str() + ">."));

const auto option = cmdData.options[0];

if (option.type != dpp::co_integer)
return; // should never happen
if (cmdData.options.empty() || cmdData.options[0].type != dpp::co_integer)
return event.reply(dpp::message("Invalid rule number provided.").set_flags(dpp::m_ephemeral));

const auto option = cmdData.options[0];
const long index = std::get<long>(option.value);

if ((index < 1 || index > rules.size()))
if (index < 1 || index > (long)rules.size())
return event.reply(dpp::message("Rule number " + std::to_string(index) + " does not exist. Visit <#" + globals::channel::rulesId.str() + "> to see all available rules.").set_flags(dpp::m_ephemeral));

const std::string& rule = rules.at(index-1);
Expand Down
65 changes: 44 additions & 21 deletions src/commands/ticket_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,52 @@

void cmd::ticketCommand(dpp::cluster& bot, const dpp::slashcommand_t& event)
{
dpp::message message(event.command.channel_id, "Creating ticket...");
event.reply(message.set_flags(dpp::m_ephemeral));

const dpp::channel ticketChannel = dpp::channel()
.set_name(event.command.get_issuing_user().username)
.set_type(dpp::CHANNEL_TEXT)
.set_guild_id(event.command.guild_id)
.set_parent_id(globals::category::ticketId)
.set_permission_overwrite(event.command.guild_id, dpp::overwrite_type::ot_role, 0, dpp::p_view_channel)
.set_permission_overwrite(event.command.get_issuing_user().id, dpp::overwrite_type::ot_member, dpp::p_view_channel, 0);

bot.channel_create(ticketChannel, [&bot, event](const dpp::confirmation_callback_t& callback) {
if (!callback.is_error())
{
const auto ticketChannel = std::get<dpp::channel>(callback.value);
const auto pingMessage = dpp::message(ticketChannel.id, event.command.get_issuing_user().get_mention() + " opened this ticket.");
bot.message_create(pingMessage);
bot.channels_get(event.command.guild_id, [&bot, event](const dpp::confirmation_callback_t& callback) {
if (callback.is_error()) {
dpp::message message(event.command.channel_id, "Failed to check existing tickets.");
return event.reply(message.set_flags(dpp::m_ephemeral));
}

event.edit_response("Ticket " + ticketChannel.get_mention() + " created!");
const auto channels = callback.get<dpp::channel_map>();
bool hasExistingTicket = false;

for (const auto& [channel_id, channel] : channels) {
if (channel.parent_id == globals::category::ticketId &&
channel.name == event.command.get_issuing_user().username) {
hasExistingTicket = true;
break;
}
}
else
{
event.edit_response("Failed to create ticket channel!");

if (hasExistingTicket) {
dpp::message message(event.command.channel_id, "You already have an open ticket.");
return event.reply(message.set_flags(dpp::m_ephemeral));
}

dpp::message message(event.command.channel_id, "Creating ticket...");
event.reply(message.set_flags(dpp::m_ephemeral));

const dpp::channel ticketChannel = dpp::channel()
.set_name(event.command.get_issuing_user().username)
.set_type(dpp::CHANNEL_TEXT)
.set_guild_id(event.command.guild_id)
.set_parent_id(globals::category::ticketId)
.set_permission_overwrite(event.command.guild_id, dpp::overwrite_type::ot_role, 0, dpp::p_view_channel)
.set_permission_overwrite(event.command.get_issuing_user().id, dpp::overwrite_type::ot_member, dpp::p_view_channel, 0);

bot.channel_create(ticketChannel, [&bot, event](const dpp::confirmation_callback_t& callback) {
if (!callback.is_error())
{
const auto ticketChannel = std::get<dpp::channel>(callback.value);
const auto pingMessage = dpp::message(ticketChannel.id, event.command.get_issuing_user().get_mention() + " opened this ticket.");
bot.message_create(pingMessage);

event.edit_response("Ticket " + ticketChannel.get_mention() + " created!");
}
else
{
event.edit_response("Failed to create ticket channel: " + callback.get_error().message);
}
});
});
}
29 changes: 21 additions & 8 deletions src/commands/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,38 @@

#include <string>
#include <fstream>
#include <vector>

std::string cmd::utils::readFileLine(const std::string& path, int &index)
{
std::ifstream file(path);

std::string line;

if (file.is_open())
{
for (int i = 0; std::getline(file, line) && i < index; i++);
std::vector<std::string> lines;
std::string currentLine;
while (std::getline(file, currentLine))
{
if (!currentLine.empty())
{
lines.push_back(currentLine);
}
}
file.close();

// validate next line
if (std::string next; !getline(file, next) || line.length() == 0)
index = 0;
else
index++;
if (lines.empty())
{
return "[!] No valid lines found in " + path;
}

file.close();
index = index % lines.size();
line = lines[index];
index = (index + 1) % lines.size(); // Prepare for next call
}
else
{
line = "[!] Could not open " + path;
}
return line;
}
48 changes: 48 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,58 @@ int main()
});

bot.on_button_click([&bot](const dpp::button_click_t& event) {
// Handle suggestion buttons
if (event.custom_id == "delSuggestion")
utils::suggestion::deleteSuggestion(bot, event);
else if (event.custom_id == "editSuggestion")
utils::suggestion::editSuggestion(bot, event);
// Handle project hint buttons
else if (event.custom_id.rfind("hint_button_", 0) == 0)
{
// Remove button
dpp::message updatedMsg = event.command.get_context_message();
updatedMsg.components.clear();
bot.message_edit(updatedMsg);

// Parse the index from the button ID
const int hintButtonIndex = std::stoi(event.custom_id.substr(event.custom_id.rfind('_') + 1));

// Load project data
std::ifstream projectFile("res/project.json");
if (!projectFile.is_open())
{
event.reply(dpp::message("Failed to open project file.").set_flags(dpp::m_ephemeral));
return;
}

json data;
try
{
projectFile >> data;
}
catch (const json::parse_error& e)
{
event.reply(dpp::message("Failed to parse project file.").set_flags(dpp::m_ephemeral));
return;
}

if (!data.contains("projects") || !data["projects"].is_array() ||
hintButtonIndex >= (int)data["projects"].size() || hintButtonIndex < 0)
{
event.reply(dpp::message("Invalid project data or index.").set_flags(dpp::m_ephemeral));
return;
}

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(0x004482) // Using the default color directly since we can't access globals here
.add_field("Hint", hint);

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

bot.on_form_submit([&bot](const dpp::form_submit_t& event) {
Expand Down
Loading