Skip to content

Commit b574e3f

Browse files
committed
More newline escaping for slash command params
1 parent 20c8626 commit b574e3f

File tree

7 files changed

+49
-18
lines changed

7 files changed

+49
-18
lines changed

TSCppBot.db.example

0 Bytes
Binary file not shown.

src/command_modules/db_commands.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ dpp::task<> db_commands::add_text_command(const dpp::form_submit_t &event, const
9292
command.description = std::get<std::string>(event.components[1].components[0].value);
9393
command.value = std::get<std::string>(event.components[2].components[0].value);
9494
command.global = (std::get<std::string>(event.components[3].components[0].value) == "true");
95+
// Replace escaped newline "\n" in description with actual newline character
96+
util::escape_newlines(command.description);
9597

9698
// Build command SQL row
9799
std::string name = util::sql_escape_string(command_name);

src/command_modules/meta.cpp

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,7 @@ dpp::task<> meta::send_message(const dpp::slashcommand_t &event) {
4848
dpp::async thinking = event.co_thinking(true);
4949
// Replace escaped newline "\n" with actual newline character
5050
std::string message = std::get<std::string>(event.get_parameter("message"));
51-
size_t pos = message.find("\\n");
52-
while (pos < message.size() && pos != std::string::npos) {
53-
// Don't replace if "\\n" is sent (double escape)
54-
if (message[pos - 1] != '\\') {
55-
message.replace(pos, 2, "\n");
56-
}
57-
// Find next occurence, if any
58-
pos = message.find("\\n", pos + 1);
59-
}
51+
util::escape_newlines(message);
6052
// Send message
6153
dpp::confirmation_callback_t msg_conf = co_await event.owner->co_message_create(dpp::message(event.command.channel_id, message));
6254
co_await thinking;
@@ -72,15 +64,7 @@ dpp::task<> meta::announce(const dpp::slashcommand_t &event, const nlohmann::jso
7264
dpp::async thinking = event.co_thinking(true);
7365
// Replace escaped newline "\\n" with actual newline character
7466
std::string message = std::get<std::string>(event.get_parameter("message"));
75-
size_t pos = message.find("\\n");
76-
while (pos < message.size() && pos != std::string::npos) {
77-
// Don't replace if "\\n" is sent (double escape)
78-
if (message[pos - 1] != '\\') {
79-
message.replace(pos, 2, "\n");
80-
}
81-
// Find next occurence, if any
82-
pos = message.find("\\n", pos + 1);
83-
}
67+
util::escape_newlines(message);
8468

8569
dpp::embed embed = dpp::embed().set_color(util::color::DEFAULT).
8670
set_description(message);
@@ -116,6 +100,9 @@ dpp::task<> meta::dm(const dpp::slashcommand_t &event, const nlohmann::json &con
116100
// Get user and message, construct embed, and send DM
117101
dpp::user user = event.command.get_resolved_user(std::get<dpp::snowflake>(event.get_parameter("user")));
118102
std::string message = std::get<std::string>(event.get_parameter("message"));
103+
// Replace escaped newline "\\n" with actual newline character
104+
util::escape_newlines(message);
105+
119106
dpp::embed dm_embed = dpp::embed().set_color(util::color::DEFAULT).set_title("Message from the owners of TSC").set_description(message);
120107
dpp::confirmation_callback_t confirmation = co_await event.owner->co_direct_message_create(user.id, dpp::message(dm_embed));
121108
if (confirmation.is_error()) {
@@ -154,6 +141,8 @@ void meta::remindme(const dpp::slashcommand_t &event, sqlite3* db) {
154141
reminder.user = event.command.get_issuing_user().id;
155142
try {
156143
reminder.text = std::get<std::string>(event.get_parameter("reminder"));
144+
// Replace escaped newline "\\n" with actual newline character
145+
util::escape_newlines(reminder.text);
157146
} catch (const std::bad_variant_access&) {
158147
reminder.text = "No description provided.";
159148
}

src/command_modules/moderation.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ dpp::task<> moderation::purge(const dpp::slashcommand_t &event, const nlohmann::
7171
.add_field("In channel", channel.get_mention(), true);
7272
try {
7373
std::string reason = std::get<std::string>(event.get_parameter("reason"));
74+
// Replace escaped newline "\\n" with actual newline character
75+
util::escape_newlines(reason);
7476
embed.add_field("Reason", reason, false);
7577
} catch (const std::bad_variant_access&) {}
7678
event.owner->message_create(dpp::message(config["log_channel_ids"]["mod_log"], embed));
@@ -198,6 +200,8 @@ dpp::task<> moderation::warn(const dpp::slashcommand_t &event, const nlohmann::j
198200
co_return;
199201
}
200202
std::string reason = std::get<std::string>(event.get_parameter("reason"));
203+
// Replace escaped newline "\\n" with actual newline character
204+
util::escape_newlines(reason);
201205
// Check hierarchy
202206
if (! co_await util::check_perms(event.owner, config, event.command.get_issuing_user().id, user.user_id)) {
203207
co_await thinking;
@@ -256,6 +260,8 @@ dpp::task<> moderation::unwarn(const dpp::slashcommand_t &event, const nlohmann:
256260
// Send "thinking" response to allow time for DB operation
257261
dpp::async thinking = event.co_thinking(true);
258262
std::string reason = std::get<std::string>(event.get_parameter("reason"));
263+
// Replace escaped newline "\\n" with actual newline character
264+
util::escape_newlines(reason);
259265
// Get ID and make sure it's a number
260266
std::string id = std::get<std::string>(event.get_parameter("id"));
261267
if (std::ranges::any_of(id, [](const char& c){return !std::isdigit(c);})) {
@@ -364,6 +370,8 @@ dpp::task<> moderation::mute(const dpp::slashcommand_t &event, const nlohmann::j
364370
std::string reason;
365371
try {
366372
reason = std::get<std::string>(event.get_parameter("reason"));
373+
// Replace escaped newline "\\n" with actual newline character
374+
util::escape_newlines(reason);
367375
} catch (const std::bad_variant_access&) {
368376
reason = "No reason provided.";
369377
}
@@ -466,6 +474,8 @@ dpp::task<> moderation::unmute(const dpp::slashcommand_t &event, const nlohmann:
466474
std::string reason;
467475
try {
468476
reason = std::get<std::string>(event.get_parameter("reason"));
477+
// Replace escaped newline "\\n" with actual newline character
478+
util::escape_newlines(reason);
469479
} catch (const std::bad_variant_access&) {
470480
reason = "No reason provided.";
471481
}
@@ -551,6 +561,8 @@ dpp::task<> moderation::kick(const dpp::slashcommand_t &event, const nlohmann::j
551561
std::string reason;
552562
try {
553563
reason = std::get<std::string>(event.get_parameter("reason"));
564+
// Replace escaped newline "\\n" with actual newline character
565+
util::escape_newlines(reason);
554566
} catch (const std::bad_variant_access&) {
555567
reason = "No reason provided.";
556568
}
@@ -627,6 +639,8 @@ dpp::task<> moderation::ban(const dpp::slashcommand_t &event, const nlohmann::js
627639
std::string reason;
628640
try {
629641
reason = std::get<std::string>(event.get_parameter("reason"));
642+
// Replace escaped newline "\\n" with actual newline character
643+
util::escape_newlines(reason);
630644
} catch (const std::bad_variant_access&) {
631645
reason = "No reason provided.";
632646
}
@@ -720,6 +734,8 @@ dpp::task<> moderation::unban(const dpp::slashcommand_t &event, const nlohmann::
720734
std::string reason;
721735
try {
722736
reason = std::get<std::string>(event.get_parameter("reason"));
737+
// Replace escaped newline "\\n" with actual newline character
738+
util::escape_newlines(reason);
723739
} catch (const std::bad_variant_access&) {
724740
reason = "No reason provided.";
725741
}

src/command_modules/server_info.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ void server_info::rule(const dpp::slashcommand_t &event, const nlohmann::json &c
4040

4141
void server_info::suggest(const dpp::slashcommand_t &event, const nlohmann::json &config) {
4242
std::string suggestion = std::get<std::string>(event.get_parameter("suggestion"));
43+
// Replace escaped newline "\n" with actual newline character
44+
util::escape_newlines(suggestion);
45+
4346
dpp::embed_author author(event.command.member.get_nickname(), "", event.command.member.get_avatar_url());
4447
dpp::embed embed = dpp::embed().set_color(dpp::colors::light_gray).set_author(author)
4548
.set_description(std::string("**Suggestion:** ") + suggestion).add_field("Status: Pending", "");
@@ -77,6 +80,8 @@ dpp::task<> server_info::suggestion_response(const dpp::slashcommand_t &event, c
7780
message.embeds[0].fields[0].name = "Status: Declined";
7881
}
7982
message.embeds[0].fields[0].value = std::get<std::string>(event.get_parameter("response"));
83+
// Replace escaped newline "\\n" with actual newline character
84+
util::escape_newlines(message.embeds[0].fields[0].value);
8085

8186
event.owner->message_edit(message);
8287
co_await thinking;

src/util.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,18 @@ std::string util::sql_escape_string(const std::string_view str, const bool wrap_
133133
return escaped_str;
134134
}
135135

136+
void util::escape_newlines(std::string& str) {
137+
size_t pos = str.find("\\n");
138+
while (pos != std::string::npos) {
139+
// Don't replace if "\\n" is sent (double escape)
140+
if (str[pos - 1] != '\\') {
141+
str.replace(pos, 2, "\n");
142+
}
143+
// Find next occurence, if any
144+
pos = str.find("\\n", pos + 1);
145+
}
146+
}
147+
136148
bool util::is_valid_command_name(const std::string_view command_name) {
137149
bool valid = true;
138150
for (char c : command_name) {

src/util.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,13 @@ namespace util {
260260
*/
261261
std::string sql_escape_string(std::string_view str, bool wrap_single_quotes = false);
262262

263+
/**
264+
* Replace literal "\n" in strings with actual newline character.
265+
* This is needed for slash command parameters because Discord only allows one line in a parameter.
266+
* @param str String to replace newlines in
267+
*/
268+
void escape_newlines(std::string& str);
269+
263270
/**
264271
* Check if a string would be a valid Discord slash command name
265272
* @param command_name Proposed command name

0 commit comments

Comments
 (0)