Skip to content

Commit

Permalink
feat: ✨ add HttpClient; add CreateMessage; update examples
Browse files Browse the repository at this point in the history
Added an HttpClient which will be used to interface with the Discord API. Multiple structures like the CreateMessage struct will be committed to model the payloads sent to the documented API routes.
  • Loading branch information
Xminent committed Nov 1, 2023
1 parent 5d8b58b commit 3e3c31c
Show file tree
Hide file tree
Showing 8 changed files with 290 additions and 79 deletions.
63 changes: 43 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,25 @@ A WIP C++ library for Discord applications.

```cpp
#include <dotenv/dotenv.h>
#include <ekizu/http_client.hpp>
#include <ekizu/shard.hpp>
#include <nlohmann/json.hpp>

using namespace ekizu;

void handle_event(Event e);
std::function<void(Event)> handle_event(HttpClient &http);

int main()
{
dotenv::init();
const auto token = dotenv::getenv("DISCORD_TOKEN");

auto http = HttpClient{ token };
const auto intents = Intents::AllIntents;
auto shard =
Shard{ ShardId::ONE, dotenv::getenv("DISCORD_TOKEN"), intents };
auto shard = Shard{ ShardId::ONE, token, intents };

if (const auto res = shard.run(handle_event); !res) {
fmt::print("Failed to run shard: {}\n", res.error().message());
if (const auto res = shard.run(handle_event(http)); !res) {
fmt::println("Failed to run shard: {}", res.error().message());
return 1;
}
}
Expand All @@ -40,22 +42,43 @@ template <typename... Func> struct overload : Func... {

template <typename... Func> overload(Func...) -> overload<Func...>;

void handle_event(Event ev)
std::function<void(Event)> handle_event(HttpClient &http)
{
std::visit(
overload{ [](const Ready &r) {
fmt::print("{} is ready!", r.user.username);
},
[](MessageCreate msg) {
fmt::print("Message: {}\n",
msg.message.content);
},
[](Resumed) { fmt::print("Resumed Event Called\n"); },
[](const auto &e) {
fmt::print("Unhandled event: {}\n",
typeid(e).name());
} },
ev);
return [&http](Event ev) {
std::visit(
overload{
[](const Ready &r) {
fmt::println("{} is ready!",
r.user.username);
},
[&http](const MessageCreate &payload) {
const auto &[msg] = payload;

const auto res =
http.create_message(
msg.channel_id)
.with_content(fmt::format(
"You said: {}",
msg.content))
.send();

if (!res) {
fmt::println(
"Failed to send message: {}",
res.error().message());
}
},
[](Resumed) {
fmt::println("Resumed Event Called");
},
[](const auto &e) {
fmt::println(
"Uncaught {} event: {}",
typeid(e).name(),
nlohmann::json{ e }.dump());
} },
ev);
};
}
```
Expand Down
63 changes: 43 additions & 20 deletions examples/simple.cpp
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
#include <dotenv/dotenv.h>
#include <ekizu/http_client.hpp>
#include <ekizu/shard.hpp>
#include <nlohmann/json.hpp>

using namespace ekizu;

void handle_event(Event e);
std::function<void(Event)> handle_event(HttpClient &http);

int main()
{
dotenv::init();
const auto token = dotenv::getenv("DISCORD_TOKEN");

auto http = HttpClient{ token };
const auto intents = Intents::AllIntents;
auto shard =
Shard{ ShardId::ONE, dotenv::getenv("DISCORD_TOKEN"), intents };
auto shard = Shard{ ShardId::ONE, token, intents };

if (const auto res = shard.run(handle_event); !res) {
fmt::print("Failed to run shard: {}\n", res.error().message());
if (const auto res = shard.run(handle_event(http)); !res) {
fmt::println("Failed to run shard: {}", res.error().message());
return 1;
}
}
Expand All @@ -26,20 +28,41 @@ template <typename... Func> struct overload : Func... {

template <typename... Func> overload(Func...) -> overload<Func...>;

void handle_event(Event ev)
std::function<void(Event)> handle_event(HttpClient &http)
{
std::visit(
overload{ [](const Ready &r) {
fmt::print("{} is ready!", r.user.username);
},
[](MessageCreate msg) {
fmt::print("Message: {}\n",
msg.message.content);
},
[](Resumed) { fmt::print("Resumed Event Called\n"); },
[](const auto &e) {
fmt::print("Unhandled event: {}\n",
typeid(e).name());
} },
ev);
return [&http](Event ev) {
std::visit(
overload{
[](const Ready &r) {
fmt::println("{} is ready!",
r.user.username);
},
[&http](const MessageCreate &payload) {
const auto &[msg] = payload;

const auto res =
http.create_message(
msg.channel_id)
.with_content(fmt::format(
"You said: {}",
msg.content))
.send();

if (!res) {
fmt::println(
"Failed to send message: {}",
res.error().message());
}
},
[](Resumed) {
fmt::println("Resumed Event Called");
},
[](const auto &e) {
fmt::println(
"Uncaught {} event: {}",
typeid(e).name(),
nlohmann::json{ e }.dump());
} },
ev);
};
}
103 changes: 65 additions & 38 deletions examples/simple_with_logging.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include <dotenv/dotenv.h>
#include <ekizu/http_client.hpp>
#include <ekizu/shard.hpp>
#include <nlohmann/json.hpp>
#include <spdlog/sinks/stdout_color_sinks.h>
Expand All @@ -25,43 +26,7 @@ template <typename... Func> struct overload : Func... {

template <typename... Func> overload(Func...) -> overload<Func...>;

void handle_event(Event ev)
{
const auto &logger = get_logger();

std::visit(overload{ [&logger](const Ready &payload) {
logger->info(
fmt::format("Logged in as {}",
payload.user.username));

logger->info(fmt::format("API version: {}",
payload.v));
logger->info(
fmt::format("Guilds: {}",
payload.guilds.size()));

logger->info(fmt::format(
"Ready object: {}",
nlohmann::json{ payload }.dump()));
},
[&logger](const MessageCreate &payload) {
const auto &[msg] = payload;

logger->info(fmt::format("Message: {}",
msg.content));
},
[&logger](const Log &log) {
logger->debug(fmt::format("Log: {}",
log.message));
},
[&logger](const auto &e) {
logger->info(fmt::format(
"Uncaught {} event: {}",
typeid(e).name(),
nlohmann::json{ e }.dump()));
} },
ev);
}
std::function<void(Event)> handle_event(HttpClient &http);

int main()
{
Expand All @@ -74,12 +39,74 @@ int main()
return 1;
}

auto http = HttpClient{ token };
const auto intents = Intents::AllIntents;
auto shard = Shard{ ShardId::ONE, token, intents };

if (const auto res = shard.run(handle_event); !res) {
if (const auto res = shard.run(handle_event(http)); !res) {
get_logger()->error("Failed to run shard: {}",
res.error().message());
return 1;
}
}

std::function<void(Event)> handle_event(HttpClient &http)
{
return [&http, bot_id = Snowflake{}](Event ev) mutable {
const auto &logger = get_logger();

std::visit(
overload{
[&logger, &bot_id](const Ready &payload) {
const auto &user = payload.user;

logger->info("Logged in as {}",
user.username);
bot_id = user.id;

logger->info("API version: {}",
payload.v);
logger->info("Guilds: {}",
payload.guilds.size());

logger->info("Ready object: {}",
nlohmann::json{ payload }
.dump());
},
[&logger, &http,
&bot_id](const MessageCreate &payload) {
const auto &[msg] = payload;

if (msg.author.id == bot_id) {
return;
}

logger->info("Message: {}",
msg.content);

const auto res =
http.create_message(
msg.channel_id)
.with_content(fmt::format(
"You said: {}",
msg.content))
.send();

if (!res) {
fmt::println(
"Failed to send message: {}",
res.error().message());
}
},
[&logger](const Log &log) {
logger->debug("Log: {}", log.message);
},
[&logger](const auto &e) {
logger->info(
"Uncaught {} event: {}",
typeid(e).name(),
nlohmann::json{ e }.dump());
} },
ev);
};
}
22 changes: 22 additions & 0 deletions include/ekizu/http_client.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef EKIZU_HTTP_CLIENT_HPP
#define EKIZU_HTTP_CLIENT_HPP

#include <ekizu/request/create_message.hpp>
#include <ekizu/snowflake.hpp>

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

[[nodiscard]] CreateMessage create_message(Snowflake channel_id);

private:
[[nodiscard]] Result<net::HttpResponse> send(net::HttpRequest req);

std::optional<net::HttpConnection> m_http;
std::optional<std::string> m_token;
};
} // namespace ekizu

#endif // EKIZU_HTTP_CLIENT_HPP
2 changes: 1 addition & 1 deletion include/ekizu/message.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#ifndef EKIZU_MESSAGE_HPP
#define EKIZU_MESSAGE_HPP

#include <ekizu/channel.hpp>
#include <ekizu/attachment.hpp>
#include <ekizu/channel.hpp>
#include <ekizu/embed.hpp>
#include <ekizu/function_view.hpp>
#include <ekizu/interaction_type.hpp>
Expand Down
31 changes: 31 additions & 0 deletions include/ekizu/request/create_message.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#ifndef EKIZU_REQUEST_CREATE_MESSAGE_HPP
#define EKIZU_REQUEST_CREATE_MESSAGE_HPP

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

namespace ekizu
{
struct CreateMessage {
CreateMessage(std::function<Result<net::HttpResponse>(net::HttpRequest)>,
Snowflake channel_id);

operator net::HttpRequest() const;

CreateMessage &with_content(std::string_view content)
{
m_content = std::string{ content };
return *this;
}

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

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

#endif // EKIZU_REQUEST_CREATE_MESSAGE_HPP
Loading

0 comments on commit 3e3c31c

Please sign in to comment.