Skip to content

Commit

Permalink
feat: ✨ add more message api routes; refactor; re-enable auto reconne…
Browse files Browse the repository at this point in the history
…ct; fix heartbeat task not being removed
  • Loading branch information
Xminent committed Nov 15, 2023
1 parent b6af576 commit 756a6cd
Show file tree
Hide file tree
Showing 23 changed files with 568 additions and 62 deletions.
79 changes: 48 additions & 31 deletions examples/simple_with_logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,39 +104,56 @@ std::function<void(Event)> handle_event(HttpClient &http)
{ "https://avatars.githubusercontent.com/u/59069386?v=4" })
.build();

auto res =
http.create_message(
msg.channel_id)
.content(fmt::format(
"{} said: {}\nAvatar: {}",
msg.author
.username,
msg.content,
msg.author.avatar ?
*msg.author
.avatar :
"null"))
.embeds({ embed })
.send();

if (!res) {
logger->error(
"Failed to send message: {}",
res.error().message());
}
http.create_message(msg.channel_id)
.content(fmt::format(
"{} said: {}\nAvatar: {}",
msg.author.username,
msg.content,
msg.author.avatar ?
*msg.author
.avatar :
"null"))
.embeds({ embed })
.send()
.map([&logger](
const auto
&new_msg) {
logger->info(
"Sent message: {}",
new_msg.id);
})
.map_error([&logger](
const auto &
err) {
logger->error(
"Failed to send message: {}",
err.message());
});

if (msg.content == "delete me") {
res = http.delete_message(
msg.channel_id,
msg.id)
.send();

if (!res) {
logger->error(
"Failed to delete message: {}",
res.error()
.message());
}
http.delete_message(
msg.channel_id,
msg.id)
.send()
.map([&logger,
msg_id = msg.id](
const auto &
res) {
if (res.status_code ==
net::HttpStatus::
NoContent) {
logger->info(
"Deleted message: {}",
msg_id);
}
})
.map_error([&logger](
const auto
&err) {
logger->error(
"Failed to delete message: {}",
err.message());
});
}
},
[&logger](const Log &log) {
Expand Down
12 changes: 12 additions & 0 deletions include/ekizu/http_client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,31 @@
#include <ekizu/rate_limiter.hpp>
#include <ekizu/request/bulk_delete_messages.hpp>
#include <ekizu/request/create_message.hpp>
#include <ekizu/request/crosspost_message.hpp>
#include <ekizu/request/delete_message.hpp>
#include <ekizu/request/edit_message.hpp>
#include <ekizu/request/pin_message.hpp>
#include <ekizu/request/unpin_message.hpp>

namespace ekizu
{
struct HttpClient {
explicit HttpClient(std::string_view token);

[[nodiscard]] CreateMessage create_message(Snowflake channel_id);
[[nodiscard]] CrosspostMessage crosspost_message(Snowflake channel_id,
Snowflake message_id);
[[nodiscard]] EditMessage edit_message(Snowflake channel_id,
Snowflake message_id);
[[nodiscard]] DeleteMessage delete_message(Snowflake channel_id,
Snowflake message_id);
[[nodiscard]] BulkDeleteMessages
bulk_delete_messages(Snowflake channel_id,
const std::vector<Snowflake> &message_ids);
[[nodiscard]] PinMessage pin_message(Snowflake channel_id,
Snowflake message_id);
[[nodiscard]] UnpinMessage unpin_message(Snowflake channel_id,
Snowflake message_id);

private:
/// Function which sends an HTTP request. This is wrapped around a ratelimiter and passed around to other structs which need the functionality.
Expand Down
19 changes: 19 additions & 0 deletions include/ekizu/json_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma GCC diagnostic pop
#endif

#include <ekizu/util.hpp>
#include <optional>
#include <type_traits>
#include <variant>
Expand Down Expand Up @@ -251,6 +252,24 @@ T get_or_default(const nlohmann::json &data, std::string_view key)

return T{};
}

template <typename T> Result<T> deserialize(std::string_view str)
{
const auto json = nlohmann::json::parse(str, nullptr, false);

if (json.is_discarded()) {
return tl::make_unexpected(
std::make_error_code(std::errc::invalid_argument));
}

// I don't like exceptions but oh well.
try {
return json.get<T>();
} catch (const nlohmann::json::exception &) {
return tl::make_unexpected(
std::make_error_code(std::errc::invalid_argument));
}
}
} // namespace ekizu::json_util

#endif // EKIZU_JSON_UTIL_HPP
1 change: 1 addition & 0 deletions include/ekizu/presence.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef EKIZU_PRESENCE_HPP
#define EKIZU_PRESENCE_HPP

#include <array>
#include <ekizu/activity_flags.hpp>
#include <ekizu/snowflake.hpp>
#include <optional>
Expand Down
2 changes: 1 addition & 1 deletion include/ekizu/request/bulk_delete_messages.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ struct BulkDeleteMessages {

operator net::HttpRequest() const;

[[nodiscard]] Result<net::HttpResponse> send();
[[nodiscard]] Result<net::HttpResponse> send() const;

private:
Snowflake m_channel_id;
Expand Down
2 changes: 1 addition & 1 deletion include/ekizu/request/create_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ struct CreateMessage {
return *this;
}

[[nodiscard]] Result<net::HttpResponse> send();
[[nodiscard]] Result<Message> send() const;

private:
Snowflake m_channel_id;
Expand Down
40 changes: 40 additions & 0 deletions include/ekizu/request/crosspost_message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef EKIZU_REQUEST_CROSSPOST_MESSAGE_HPP
#define EKIZU_REQUEST_CROSSPOST_MESSAGE_HPP

#include <ekizu/snowflake.hpp>
#include <net/http.hpp>

namespace ekizu
{
/**
* @brief Represents the Crosspost Message REST API endpoint.
*/
struct CrosspostMessage {
CrosspostMessage(
const std::function<Result<net::HttpResponse>(net::HttpRequest)>
&make_request,
Snowflake channel_id, Snowflake message_id);

/**
* @brief Converts the CrosspostMessage to an HTTP request.
*
* @return The HTTP request.
*/
operator net::HttpRequest() const;

/**
* @brief Sends the CrosspostMessage request.
*
* @return The result of the request as an HTTP response.
*/
[[nodiscard]] Result<net::HttpResponse> send() const;

private:
Snowflake m_channel_id;
Snowflake m_message_id;
std::function<Result<net::HttpResponse>(net::HttpRequest)>
m_make_request;
};
} // namespace ekizu

#endif // EKIZU_REQUEST_CROSSPOST_MESSAGE_HPP
2 changes: 1 addition & 1 deletion include/ekizu/request/delete_message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct DeleteMessage {

operator net::HttpRequest() const;

[[nodiscard]] Result<net::HttpResponse> send();
[[nodiscard]] Result<net::HttpResponse> send() const;

private:
Snowflake m_channel_id;
Expand Down
80 changes: 80 additions & 0 deletions include/ekizu/request/edit_message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#ifndef EKIZU_REQUEST_EDIT_MESSAGE_HPP
#define EKIZU_REQUEST_EDIT_MESSAGE_HPP

#include <ekizu/message.hpp>
#include <net/http.hpp>

namespace ekizu
{
struct EditMessageFields {
/// Message contents (up to 2000 characters).
std::optional<std::string> content;
/// Up to 10 rich embeds (up to 6000 characters).
std::optional<std::vector<Embed> > embeds;
/// Message flags combined as a bitfield (only SUPPRESS_EMBEDS and SUPPRESS_NOTIFICATIONS can be set).
std::optional<MessageFlags> flags;
/// Allowed mentions for the message.
std::optional<AllowedMentions> allowed_mentions;
/// Components to include with the message.
std::optional<std::vector<MessageComponent> > components;
/// JSON-encoded body of non-file params, only for multipart/form-data requests. See Uploading Files.
std::optional<std::string> payload_json;
/// Attachment objects with filename and description. See Uploading Files.
std::optional<std::vector<PartialAttachment> > attachments;
};

EKIZU_EXPORT void to_json(nlohmann::json &j, const EditMessageFields &f);
EKIZU_EXPORT void from_json(const nlohmann::json &j, EditMessageFields &f);

struct EditMessage {
EditMessage(
const std::function<Result<net::HttpResponse>(net::HttpRequest)>
&make_request,
Snowflake channel_id, Snowflake message_id);

operator net::HttpRequest() const;

EditMessage &allowed_mentions(AllowedMentions allowed_mentions)
{
m_fields.allowed_mentions = allowed_mentions;
return *this;
}

EditMessage &content(std::string_view content)
{
// TODO: Validate content
m_fields.content = content;
return *this;
}

EditMessage &embeds(const std::vector<Embed> &embeds)
{
// TODO: Validate embeds
m_fields.embeds = embeds;
return *this;
}

EditMessage &flags(MessageFlags flags)
{
m_fields.flags = flags;
return *this;
}

EditMessage &payload_json(std::string_view payload_json)
{
m_fields.payload_json = payload_json;
return *this;
}

[[nodiscard]] Result<net::HttpResponse> send() const;

private:
Snowflake m_channel_id;
Snowflake m_message_id;
EditMessageFields m_fields;
std::function<Result<net::HttpResponse>(net::HttpRequest)>
m_make_request;
};
} // namespace ekizu

#endif // EKIZU_REQUEST_EDIT_MESSAGE_HPP
40 changes: 40 additions & 0 deletions include/ekizu/request/get_channel.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef EKIZU_REQUEST_GET_CHANNEL_HPP
#define EKIZU_REQUEST_GET_CHANNEL_HPP

#include <ekizu/channel.hpp>
#include <ekizu/snowflake.hpp>
#include <net/http.hpp>

namespace ekizu
{
/**
* @brief Represents the Get Channel REST API endpoint.
*/
struct GetChannel {
GetChannel(
const std::function<Result<net::HttpResponse>(net::HttpRequest)>
&make_request,
Snowflake channel_id);

/**
* @brief Converts the GetChannel to an HTTP request.
*
* @return The HTTP request.
*/
operator net::HttpRequest() const;

/**
* @brief Sends the GetChannel request.
*
* @return The result of the request as an HTTP response.
*/
[[nodiscard]] Result<Channel> send() const;

private:
Snowflake m_channel_id;
std::function<Result<net::HttpResponse>(net::HttpRequest)>
m_make_request;
};
} // namespace ekizu

#endif // EKIZU_REQUEST_GET_CHANNEL_HPP
40 changes: 40 additions & 0 deletions include/ekizu/request/pin_message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef EKIZU_REQUEST_PIN_MESSAGE_HPP
#define EKIZU_REQUEST_PIN_MESSAGE_HPP

#include <ekizu/snowflake.hpp>
#include <net/http.hpp>

namespace ekizu
{
/**
* @brief Represents the Pin Message REST API endpoint.
*/
struct PinMessage {
PinMessage(
const std::function<Result<net::HttpResponse>(net::HttpRequest)>
&make_request,
Snowflake channel_id, Snowflake message_id);

/**
* @brief Converts the PinMessage to an HTTP request.
*
* @return The HTTP request.
*/
operator net::HttpRequest() const;

/**
* @brief Sends the PinMessage request.
*
* @return The result of the request as an HTTP response.
*/
[[nodiscard]] Result<net::HttpResponse> send() const;

private:
Snowflake m_channel_id;
Snowflake m_message_id;
std::function<Result<net::HttpResponse>(net::HttpRequest)>
m_make_request;
};
} // namespace ekizu

#endif // EKIZU_REQUEST_PIN_MESSAGE_HPP
Loading

0 comments on commit 756a6cd

Please sign in to comment.