diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index f7cec818c4..a3a632e95e 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -1368,11 +1368,9 @@ class DPP_EXPORT cluster { * @param method Method, e.g. GET, POST * @param postdata Post data (usually JSON encoded) * @param callback Function to call when the HTTP call completes. The callback parameter will contain amongst other things, the decoded json. - * @param filename List of filenames to post for POST requests (for uploading files) - * @param filecontent List of file content to post for POST requests (for uploading files) - * @param filemimetypes List of mime types for each file to post for POST requests (for uploading files) + * @param file_data List of files to post for POST requests (for uploading files) */ - void post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &filename = {}, const std::vector& filecontent = {}, const std::vector& filemimetypes = {}); + void post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &file_data = {}); /** * @brief Make a HTTP(S) request. For use when wanting asynchronous access to HTTP APIs outside of Discord. diff --git a/include/dpp/message.h b/include/dpp/message.h index a2a4a57575..ac9ca0716f 100644 --- a/include/dpp/message.h +++ b/include/dpp/message.h @@ -114,6 +114,30 @@ struct component_emoji { bool animated{false}; }; +/** + * @brief The data for a file attached to a message. + * + * @todo Change the naming of this and make stickers (and potentially anything else that has data like this) use this. + */ +struct message_file_data { + /** + * @brief Name of file to upload (for use server-side in discord's url). + */ + std::string name{}; + + /** + * @brief File content to upload (raw binary) + */ + std::string content{}; + + /** + * @brief Mime type of files to upload. + * + * @todo Look at turning this into an enum? This would allow people to easily compare mimetypes if they happen to change. + */ + std::string mimetype{}; +}; + /** * @brief Types of text input */ @@ -1828,19 +1852,11 @@ struct DPP_EXPORT message : public managed, json_interface { std::vector stickers; /** - * @brief Name of file to upload (for use server-side in discord's url). - */ - std::vector filename; - - /** - * @brief File content to upload (raw binary) - */ - std::vector filecontent; - - /** - * @brief Mime type of files to upload. + * @brief An array of file data to use for uploading files. + * + * @note You should use dpp::message::add_file to add data to this! */ - std::vector filemimetype; + std::vector file_data; /** * @brief Reference to another message, e.g. a reply diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 5341e87b71..ece2949d9f 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -339,7 +339,17 @@ void cluster::post_rest(const std::string &endpoint, const std::string &major_pa }, postdata, method, get_audit_reason(), filename, filecontent, filemimetype, protocol)); } -void cluster::post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &filename, const std::vector &filecontent, const std::vector &filemimetypes) { +void cluster::post_rest_multipart(const std::string &endpoint, const std::string &major_parameters, const std::string ¶meters, http_method method, const std::string &postdata, json_encode_t callback, const std::vector &file_data) { + std::vector file_names{}; + std::vector file_contents{}; + std::vector file_mimetypes{}; + + for(const message_file_data& data : file_data) { + file_names.push_back(data.name); + file_contents.push_back(data.content); + file_mimetypes.push_back(data.mimetype); + } + /* NOTE: This is not a memory leak! The request_queue will free the http_request once it reaches the end of its lifecycle */ rest->post_request(new http_request(endpoint + (!major_parameters.empty() ? "/" : "") + major_parameters, parameters, [endpoint, callback](http_request_completion_t rv) { json j; @@ -354,7 +364,7 @@ void cluster::post_rest_multipart(const std::string &endpoint, const std::string if (callback) { callback(j, rv); } - }, postdata, method, get_audit_reason(), filename, filecontent, filemimetypes)); + }, postdata, method, get_audit_reason(), file_names, file_contents, file_mimetypes)); } diff --git a/src/dpp/cluster/appcommand.cpp b/src/dpp/cluster/appcommand.cpp index eb1f056d5c..15a2bb1416 100644 --- a/src/dpp/cluster/appcommand.cpp +++ b/src/dpp/cluster/appcommand.cpp @@ -140,7 +140,7 @@ void cluster::interaction_response_create(snowflake interaction_id, const std::s if (callback) { callback(confirmation_callback_t(this, confirmation(), http)); } - }, r.msg.filename, r.msg.filecontent, r.msg.filemimetype); + }, r.msg.file_data); } void cluster::interaction_response_edit(const std::string &token, const message &m, command_completion_event_t callback) { @@ -148,7 +148,7 @@ void cluster::interaction_response_edit(const std::string &token, const message if (callback) { callback(confirmation_callback_t(this, confirmation(), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::interaction_response_get_original(const std::string &token, command_completion_event_t callback) { @@ -160,7 +160,7 @@ void cluster::interaction_followup_create(const std::string &token, const messag if (callback) { callback(confirmation_callback_t(this, confirmation(), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::interaction_followup_edit_original(const std::string &token, const message &m, command_completion_event_t callback) { @@ -168,7 +168,7 @@ void cluster::interaction_followup_edit_original(const std::string &token, const if (callback) { callback(confirmation_callback_t(this, confirmation(), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::interaction_followup_delete(const std::string &token, command_completion_event_t callback) { @@ -180,7 +180,7 @@ void cluster::interaction_followup_edit(const std::string &token, const message if (callback) { callback(confirmation_callback_t(this, confirmation(), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::interaction_followup_get(const std::string &token, snowflake message_id, command_completion_event_t callback) { diff --git a/src/dpp/cluster/message.cpp b/src/dpp/cluster/message.cpp index 36ddaa433b..231670b31c 100644 --- a/src/dpp/cluster/message.cpp +++ b/src/dpp/cluster/message.cpp @@ -40,7 +40,7 @@ void cluster::message_create(const message &m, command_completion_event_t callba if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } @@ -116,7 +116,7 @@ void cluster::message_edit(const message &m, command_completion_event_t callback if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } diff --git a/src/dpp/cluster/thread.cpp b/src/dpp/cluster/thread.cpp index be58b82b41..8e8ab147e3 100644 --- a/src/dpp/cluster/thread.cpp +++ b/src/dpp/cluster/thread.cpp @@ -111,6 +111,7 @@ void cluster::thread_create_in_forum(const std::string& thread_name, snowflake c j["auto_archive_duration"] = 10080; break; } + this->post_rest_multipart(API_PATH "/channels", std::to_string(channel_id), "threads", m_post, j.dump(), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { auto t = thread().fill_from_json(&j); @@ -122,7 +123,7 @@ void cluster::thread_create_in_forum(const std::string& thread_name, snowflake c } callback(confirmation_callback_t(this, t, http)); } - }, msg.filename, msg.filecontent, msg.filemimetype); + }, msg.file_data); } void cluster::thread_create(const std::string& thread_name, snowflake channel_id, uint16_t auto_archive_duration, channel_type thread_type, bool invitable, uint16_t rate_limit_per_user, command_completion_event_t callback) diff --git a/src/dpp/cluster/webhook.cpp b/src/dpp/cluster/webhook.cpp index 609db9ae1b..52b895c77c 100644 --- a/src/dpp/cluster/webhook.cpp +++ b/src/dpp/cluster/webhook.cpp @@ -50,11 +50,12 @@ void cluster::edit_webhook_message(const class webhook &wh, const struct message std::string parameters = utility::make_url_parameters({ {"thread_id", thread_id}, }); + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token: token) + "/messages/" + std::to_string(m.id) + parameters, m_patch, m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::edit_webhook_with_token(const class webhook& wh, command_completion_event_t callback) { @@ -84,11 +85,12 @@ void cluster::execute_webhook(const class webhook &wh, const struct message& m, } body = j.dump(); } + this->post_rest_multipart(API_PATH "/webhooks", std::to_string(wh.id), utility::url_encode(!wh.token.empty() ? wh.token : token) + parameters, m_post, !body.empty() ? body : m.build_json(false), [this, callback](json &j, const http_request_completion_t& http) { if (callback) { callback(confirmation_callback_t(this, message(this).fill_from_json(&j), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void cluster::get_channel_webhooks(snowflake channel_id, command_completion_event_t callback) { diff --git a/src/dpp/dispatcher.cpp b/src/dpp/dispatcher.cpp index 7c42125dec..1d700efdec 100644 --- a/src/dpp/dispatcher.cpp +++ b/src/dpp/dispatcher.cpp @@ -162,11 +162,21 @@ void interaction_create_t::get_original_response(command_completion_event_t call } void interaction_create_t::edit_original_response(const message& m, command_completion_event_t callback) const { + std::vector file_names{}; + std::vector file_contents{}; + std::vector file_mimetypes{}; + + for(message_file_data data : m.file_data) { + file_names.push_back(data.name); + file_contents.push_back(data.content); + file_mimetypes.push_back(data.mimetype); + } + from->creator->post_rest_multipart(API_PATH "/webhooks", std::to_string(command.application_id), command.token + "/messages/@original", m_patch, m.build_json(), [creator = this->from->creator, cb = std::move(callback)](json& j, const http_request_completion_t& http) { if (cb) { cb(confirmation_callback_t(creator, message().fill_from_json(&j), http)); } - }, m.filename, m.filecontent, m.filemimetype); + }, m.file_data); } void interaction_create_t::delete_original_response(command_completion_event_t callback) const { diff --git a/src/dpp/message.cpp b/src/dpp/message.cpp index c3218c8fe3..abb2d5f8af 100644 --- a/src/dpp/message.cpp +++ b/src/dpp/message.cpp @@ -559,30 +559,39 @@ message& message::set_type(message_type t) return *this; } -message& message::set_filename(const std::string &fn) -{ - if (filename.empty()) { - filename.push_back(fn); +message& message::set_filename(const std::string &fn) { + if (file_data.empty()) { + message_file_data data; + data.name = fn; + + file_data.push_back(data); } else { - filename[filename.size() - 1] = fn; + file_data[file_data.size() - 1].name = fn; } + return *this; } -message& message::set_file_content(const std::string &fc) -{ - if (filecontent.empty()) { - filecontent.push_back(fc); +message& message::set_file_content(const std::string &fc) { + if (file_data.empty()) { + message_file_data data; + data.content = fc; + + file_data.push_back(data); } else { - filecontent[filecontent.size() - 1] = fc; + file_data[file_data.size() - 1].content = fc; } + return *this; } message& message::add_file(const std::string &fn, const std::string &fc, const std::string &fm) { - filename.push_back(fn); - filecontent.push_back(fc); - filemimetype.push_back(fm); + message_file_data data; + data.name = fn; + data.content = fc; + data.mimetype = fm; + + file_data.push_back(data); return *this; }