1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2025-07-01 14:37:10 +02:00

Basic Discord library layout.

Foundation for the discord library bindings. To be gradually exposed to the script.
This commit is contained in:
Sandu Liviu Catalin
2021-09-10 20:13:42 +03:00
parent f6cb8ff8a1
commit 4f70f89b78
138 changed files with 60430 additions and 0 deletions

77
vendor/DPP/src/dpp/auditlog.cpp vendored Normal file
View File

@ -0,0 +1,77 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/auditlog.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
namespace dpp {
using json = nlohmann::json;
auditlog::auditlog()
{
}
auditlog::~auditlog() {
}
auditlog& auditlog::fill_from_json(nlohmann::json* j) {
for (auto & ai : (*j)["audit_log_entries"]) {
audit_entry ae;
ae.id = SnowflakeNotNull(&ai, "id");
ae.event = (audit_type)Int8NotNull(&ai, "action_type");
ae.user_id = SnowflakeNotNull(&ai, "user_id");
ae.target_id = SnowflakeNotNull(&ai, "target_id");
ae.reason = StringNotNull(&ai, "reason");
if (j->find("changes") != j->end()) {
auto &c = ai["changes"];
for (auto & change : c) {
audit_change ac;
ac.key = StringNotNull(&change, "key");
if (change.find("new_value") != change.end()) {
ac.new_value = change["new_value"].dump();
}
if (change.find("old_value") != change.end()) {
ac.old_value = change["old_value"].dump();
}
}
}
if (j->find("options") != j->end()) {
auto &o = ai["options"];
audit_extra opts;
opts.channel_id = SnowflakeNotNull(&o, "channel_id");
opts.count = StringNotNull(&o, "count");
opts.delete_member_days = StringNotNull(&o, "delete_member_days");
opts.id = SnowflakeNotNull(&o, "id");
opts.members_removed = StringNotNull(&o, "members_removed");
opts.message_id = SnowflakeNotNull(&o, "message_id");
opts.role_name = StringNotNull(&o, "role_name");
opts.type = StringNotNull(&o, "type");
ae.options = opts;
}
this->entries.push_back(ae);
}
return *this;
}
};

52
vendor/DPP/src/dpp/ban.cpp vendored Normal file
View File

@ -0,0 +1,52 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/ban.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
namespace dpp {
using json = nlohmann::json;
ban::ban() : user_id(0)
{
}
ban::~ban() {
}
ban& ban::fill_from_json(nlohmann::json* j) {
reason = StringNotNull(j, "reason");
if (j->find("user") != j->end()) {
json & user = (*j)["user"];
user_id = SnowflakeNotNull(&user, "id");
}
return *this;
}
std::string ban::build_json() const {
/* This is an unused stub, because sending a ban is simple as a user id and a reason */
return "{}";
}
};

161
vendor/DPP/src/dpp/cache.cpp vendored Normal file
View File

@ -0,0 +1,161 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <mutex>
#include <iostream>
#include <variant>
#include <dpp/cache.h>
#include <dpp/guild.h>
namespace dpp {
std::unordered_map<managed*, time_t> deletion_queue;
std::mutex deletion_mutex;
#define cache_helper(type, cache_name, setter, getter, counter) \
cache* cache_name = nullptr; \
type * setter (snowflake id) { \
return cache_name ? ( type * ) cache_name ->find(id) : nullptr; \
} \
cache* getter () { \
if (! cache_name ) { \
cache_name = new cache(); \
} \
return cache_name ; \
} \
uint64_t counter () { \
return ( cache_name ? cache_name ->count() : 0 ); \
}
/* Because other threads and systems may run for a short while after an event is received, we don't immediately
* delete pointers when objects are replaced. We put them into a queue, and periodically delete pointers in the
* queue. This also rehashes unordered_maps to ensure they free their memory.
*/
void garbage_collection() {
time_t now = time(NULL);
bool repeat = false;
{
std::lock_guard<std::mutex> delete_lock(deletion_mutex);
do {
repeat = false;
for (auto g = deletion_queue.begin(); g != deletion_queue.end(); ++g) {
if (now > g->second + 60) {
delete g->first;
deletion_queue.erase(g);
repeat = true;
break;
}
}
} while (repeat);
if (deletion_queue.size() == 0) {
deletion_queue = {};
}
}
dpp::get_user_cache()->rehash();
dpp::get_channel_cache()->rehash();
dpp::get_guild_cache()->rehash();
dpp::get_role_cache()->rehash();
dpp::get_emoji_cache()->rehash();
}
cache::cache() {
cache_map = new cache_container();
}
cache::~cache() {
delete cache_map;
}
uint64_t cache::count() {
std::lock_guard<std::mutex> lock(this->cache_mutex);
return cache_map->size();
}
std::mutex& cache::get_mutex() {
return this->cache_mutex;
}
cache_container& cache::get_container() {
return *(this->cache_map);
}
void cache::store(managed* object) {
if (!object) {
return;
}
std::lock_guard<std::mutex> lock(this->cache_mutex);
auto existing = cache_map->find(object->id);
if (existing == cache_map->end()) {
(*cache_map)[object->id] = object;
} else if (object != existing->second) {
/* Flag old pointer for deletion and replace */
std::lock_guard<std::mutex> delete_lock(deletion_mutex);
deletion_queue[existing->second] = time(NULL);
(*cache_map)[object->id] = object;
}
}
size_t cache::bytes() {
std::lock_guard<std::mutex> lock(cache_mutex);
return sizeof(this) + (cache_map->bucket_count() * sizeof(size_t));
}
void cache::rehash() {
std::lock_guard<std::mutex> lock(cache_mutex);
cache_container* n = new cache_container();
n->reserve(cache_map->size());
for (auto t = cache_map->begin(); t != cache_map->end(); ++t) {
n->insert(*t);
}
delete cache_map;
cache_map = n;
}
void cache::remove(managed* object) {
if (!object) {
return;
}
std::lock_guard<std::mutex> lock(cache_mutex);
std::lock_guard<std::mutex> delete_lock(deletion_mutex);
auto existing = cache_map->find(object->id);
if (existing != cache_map->end()) {
cache_map->erase(existing);
deletion_queue[object] = time(NULL);
}
}
managed* cache::find(snowflake id) {
std::lock_guard<std::mutex> lock(cache_mutex);
auto r = cache_map->find(id);
if (r != cache_map->end()) {
return r->second;
}
return nullptr;
}
cache_helper(user, user_cache, find_user, get_user_cache, get_user_count);
cache_helper(channel, channel_cache, find_channel, get_channel_cache, get_channel_count);
cache_helper(role, role_cache, find_role, get_role_cache, get_role_count);
cache_helper(guild, guild_cache, find_guild, get_guild_cache, get_guild_count);
cache_helper(emoji, emoji_cache, find_emoji, get_emoji_cache, get_emoji_count);
};

245
vendor/DPP/src/dpp/channel.cpp vendored Normal file
View File

@ -0,0 +1,245 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/guild.h>
#include <dpp/user.h>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
thread_member& thread_member::fill_from_json(nlohmann::json* j) {
SetSnowflakeNotNull(j, "id", this->thread_id);
SetSnowflakeNotNull(j, "user_id", this->user_id);
SetTimestampNotNull(j, "join_timestamp", this->joined);
SetInt32NotNull(j, "flags", this->flags);
return *this;
}
channel::channel() :
managed(),
flags(0),
guild_id(0),
position(0),
last_message_id(0),
user_limit(0),
rate_limit_per_user(0),
owner_id(0),
parent_id(0),
last_pin_timestamp(0),
message_count(0),
member_count(0)
{
}
channel::~channel()
{
}
bool channel::is_nsfw() const {
return flags & dpp::c_nsfw;
}
bool channel::is_text_channel() const {
return flags & dpp::c_text;
}
bool channel::is_dm() const {
return flags & dpp::c_dm;
}
bool channel::is_voice_channel() const {
return flags & dpp::c_voice;
}
bool channel::is_group_dm() const {
return (flags & (dpp::c_dm | dpp::c_group)) == (dpp::c_dm | dpp::c_group);
}
bool channel::is_category() const {
return flags & dpp::c_category;
}
bool channel::is_stage_channel() const {
return (flags & dpp::c_stage) == dpp::c_stage;
}
bool channel::is_news_channel() const {
/* Important: Stage/News overlap to pack more values in a byte */
return !is_stage_channel() && (flags & dpp::c_news);
}
bool channel::is_store_channel() const {
/* Important: Stage/Store overlap to pack more values in a byte */
return !is_stage_channel() && (flags & dpp::c_store);
}
channel& channel::fill_from_json(json* j) {
this->id = SnowflakeNotNull(j, "id");
SetSnowflakeNotNull(j, "guild_id", this->guild_id);
SetInt16NotNull(j, "position", this->position);
SetStringNotNull(j, "name", this->name);
SetStringNotNull(j, "topic", this->topic);
SetSnowflakeNotNull(j, "last_message_id", this->last_message_id);
SetInt8NotNull(j, "user_limit", this->user_limit);
SetInt16NotNull(j, "rate_limit_per_user", this->rate_limit_per_user);
SetSnowflakeNotNull(j, "owner_id", this->owner_id);
SetSnowflakeNotNull(j, "parent_id", this->parent_id);
//this->last_pin_timestamp
uint8_t type = Int8NotNull(j, "type");
this->flags |= BoolNotNull(j, "nsfw") ? dpp::c_nsfw : 0;
this->flags |= (type == GUILD_TEXT) ? dpp::c_text : 0;
this->flags |= (type == GUILD_VOICE) ? dpp::c_voice : 0;
this->flags |= (type == DM) ? dpp::c_dm : 0;
this->flags |= (type == GROUP_DM) ? (dpp::c_group | dpp::c_dm) : 0;
this->flags |= (type == GUILD_CATEGORY) ? dpp::c_category : 0;
this->flags |= (type == GUILD_NEWS) ? dpp::c_news : 0;
this->flags |= (type == GUILD_STORE) ? dpp::c_store : 0;
this->flags |= (type == GUILD_STAGE) ? dpp::c_stage : 0;
this->flags |= (type == GUILD_NEWS_THREAD) ? dpp::c_news_thread : 0;
this->flags |= (type == GUILD_PUBLIC_THREAD) ? dpp::c_public_thread : 0;
this->flags |= (type == GUILD_PRIVATE_THREAD) ? dpp::c_private_thread : 0;
if (j->find("recipients") != j->end()) {
recipients = {};
for (auto & r : (*j)["recipients"]) {
recipients.push_back(from_string<uint64_t>(r["id"].get<std::string>(), std::dec));
}
}
if (j->find("permission_overwrites") != j->end()) {
permission_overwrites = {};
for (auto & overwrite : (*j)["permission_overwrites"]) {
permission_overwrite po;
po.id = SnowflakeNotNull(&overwrite, "id");
po.allow = SnowflakeNotNull(&overwrite, "allow");
po.deny = SnowflakeNotNull(&overwrite, "deny");
po.type = Int8NotNull(&overwrite, "type");
permission_overwrites.push_back(po);
}
}
if (type == GUILD_NEWS_THREAD || type == GUILD_PUBLIC_THREAD || type == GUILD_PRIVATE_THREAD) {
SetInt8NotNull(j, "message_count", this->message_count);
SetInt8NotNull(j, "memeber_count", this->member_count);
dpp::thread_metadata metadata;
auto json_metadata = (*j)["thread_metadata"];
metadata.archived = BoolNotNull(&json_metadata, "archived");
metadata.archive_timestamp = TimestampNotNull(&json_metadata, "archive_timestamp");
metadata.auto_archive_duration = Int16NotNull(&json_metadata, "auto_archive_duration");
metadata.locked = BoolNotNull(&json_metadata, "locked");
}
return *this;
}
std::string channel::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(id);
}
j["guild_id"] = std::to_string(guild_id);
j["position"] = position;
j["name"] = name;
j["topic"] = topic;
if (is_voice_channel()) {
j["user_limit"] = user_limit;
j["rate_limit_per_user"] = rate_limit_per_user;
}
if (!is_dm()) {
if (parent_id) {
j["parent_id"] = parent_id;
}
if (is_text_channel()) {
j["type"] = GUILD_TEXT;
} else if (is_voice_channel()) {
j["type"] = GUILD_VOICE;
} else if (is_category()) {
j["type"] = GUILD_CATEGORY;
} else if (is_stage_channel()) {
/* Order is important, as GUILD_STAGE overlaps NEWS and STORE */
j["type"] = GUILD_STAGE;
} else if (is_news_channel()) {
j["type"] = GUILD_NEWS;
} else if (is_store_channel()) {
j["type"] = GUILD_STORE;
}
j["nsfw"] = is_nsfw();
} else {
if (is_group_dm()) {
j["type"] = GROUP_DM;
} else {
j["type"] = DM;
}
}
return j.dump();
}
uint64_t channel::get_user_permissions(const user* member) const
{
if (member == nullptr)
return 0;
guild* g = dpp::find_guild(guild_id);
if (g == nullptr)
return 0;
return g->permission_overwrites(g->base_permissions(member), member, this);
}
std::map<snowflake, guild_member*> channel::get_members() {
std::map<snowflake, guild_member*> rv;
guild* g = dpp::find_guild(guild_id);
if (g) {
for (auto m = g->members.begin(); m != g->members.end(); ++m) {
user* u = dpp::find_user(m->second.user_id);
if (u) {
if (get_user_permissions(u) & p_view_channel) {
rv[m->second.user_id] = &(m->second);
}
}
}
}
return rv;
}
std::map<snowflake, voicestate> channel::get_voice_members() {
std::map<snowflake, voicestate> rv;
guild* g = dpp::find_guild(guild_id);
if (g) {
for (auto & m : g->voice_members) {
if (m.second.channel_id == this->id) {
rv[m.second.user_id] = m.second;
}
}
}
return rv;
}
};

1943
vendor/DPP/src/dpp/cluster.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

360
vendor/DPP/src/dpp/commandhandler.cpp vendored Normal file
View File

@ -0,0 +1,360 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/commandhandler.h>
#include <dpp/cache.h>
#include <dpp/cluster.h>
#include <dpp/dispatcher.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
#include <sstream>
namespace dpp {
param_info::param_info(parameter_type t, bool o, const std::string &d, const std::map<std::string, std::string> &opts) : type(t), optional(o), description(d), choices(opts)
{
}
commandhandler::commandhandler(cluster* o, bool auto_hook_events, snowflake application_id) : slash_commands_enabled(false), owner(o), app_id(application_id)
{
if (!application_id && o->me.id) {
app_id = o->me.id;
}
if (auto_hook_events) {
o->on_interaction_create([this](const dpp::interaction_create_t &event) {
this->route(event);
});
o->on_message_create([this](const dpp::message_create_t & event) {
this->route(*event.msg);
});
}
}
commandhandler& commandhandler::set_owner(cluster* o)
{
owner = o;
return *this;
}
commandhandler::~commandhandler()
{
}
commandhandler& commandhandler::add_prefix(const std::string &prefix)
{
prefixes.push_back(prefix);
if (prefix == "/") {
if (!slash_commands_enabled) {
/* Register existing slash commands */
slash_commands_enabled = true;
} else {
slash_commands_enabled = true;
}
}
return *this;
}
commandhandler& commandhandler::add_command(const std::string &command, const parameter_registration_t &parameters, command_handler handler, const std::string &description, snowflake guild_id)
{
command_info_t i;
i.func = handler;
i.guild_id = guild_id;
i.parameters = parameters;
commands[lowercase(command)] = i;
if (slash_commands_enabled) {
if (this->app_id == 0) {
if (owner->me.id == 0) {
throw dpp::exception("Command handler not ready (i don't know my application ID)");
} else {
this->app_id = owner->me.id;
}
}
dpp::slashcommand newcommand;
/* Create a new global command on ready event */
newcommand.set_name(lowercase(command)).set_description(description).set_application_id(this->app_id);
for (auto& parameter : parameters) {
command_option_type cot;
switch (parameter.second.type) {
case pt_boolean:
cot = co_boolean;
break;
case pt_integer:
cot = co_integer;
break;
case pt_string:
cot = co_string;
break;
case pt_user:
cot = co_user;
break;
case pt_role:
cot = co_role;
break;
case pt_channel:
cot = co_channel;
break;
}
command_option opt(cot, parameter.first, parameter.second.description, !parameter.second.optional);
if (!parameter.second.choices.empty()) {
for (auto& c : parameter.second.choices) {
opt.add_choice(dpp::command_option_choice(c.second, c.first));
}
}
newcommand.add_option(opt);
}
/* Register the command */
if (guild_id) {
owner->guild_command_create(newcommand, guild_id, [command, this](const dpp::confirmation_callback_t &callback) {
if (callback.is_error()) {
this->owner->log(dpp::ll_error, fmt::format("Failed to register guild slash command '{}': {}", command, callback.http_info.body));
}
});
} else {
owner->global_command_create(newcommand, [command, this](const dpp::confirmation_callback_t &callback) {
if (callback.is_error()) {
this->owner->log(dpp::ll_error, fmt::format("Failed to register global slash command '{}': {}", command, callback.http_info.body));
}
});
}
}
return *this;
}
bool commandhandler::string_has_prefix(std::string &str)
{
size_t str_length = utility::utf8len(str);
for (auto& p : prefixes) {
size_t prefix_length = utility::utf8len(p);
if (utility::utf8substr(str, 0, prefix_length) == p) {
str.erase(str.begin(), str.begin() + prefix_length);
return true;
}
}
return false;
}
void commandhandler::route(const dpp::message& msg)
{
std::string msg_content = msg.content;
if (string_has_prefix(msg_content)) {
/* Put the string into stringstream to parse parameters at spaces.
* We use stringstream as it handles multiple spaces etc nicely.
*/
std::stringstream ss(msg_content);
std::string command;
ss >> command;
/* Prefixed command, the prefix was removed */
auto found_cmd = commands.find(lowercase(command));
if (found_cmd != commands.end()) {
/* Filter out guild specific commands that are not for the current guild */
if (found_cmd->second.guild_id && found_cmd->second.guild_id != msg.guild_id) {
return;
}
parameter_list_t call_params;
/* Command found; parse parameters */
for (auto& p : found_cmd->second.parameters) {
command_parameter param;
/* Check for end of stream */
if (!ss) {
/* If it's an optional param, we dont care */
if (!p.second.optional) {
/* Trigger missing parameter handler? */
}
break;
}
switch (p.second.type) {
case pt_string: {
std::string x;
ss >> x;
param = x;
}
break;
case pt_role: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '&') {
snowflake rid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
role* r = dpp::find_role(rid);
if (r) {
param = *r;
}
}
}
break;
case pt_channel: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '#') {
snowflake cid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
channel* c = dpp::find_channel(cid);
if (c) {
param = *c;
}
}
}
break;
case pt_user: {
std::string x;
ss >> x;
if (x.length() > 4 && x[0] == '<' && x[1] == '@') {
snowflake uid = from_string<uint64_t>(x.substr(2, x.length() - 1), std::dec);
user* u = dpp::find_user(uid);
if (u) {
param = *u;
}
}
}
break;
case pt_integer: {
int32_t x = 0;
ss >> x;
param = x;
}
case pt_boolean: {
std::string x;
bool y = false;
ss >> x;
x = lowercase(x);
if (x == "yes" || x == "1" || x == "true") {
y = true;
}
param = y;
}
break;
}
/* Add parameter to the list */
call_params.push_back(std::make_pair(p.first, param));
}
/* Call command handler */
command_source source;
source.command_id = 0;
source.guild_id = msg.guild_id;
source.channel_id = msg.channel_id;
source.issuer = msg.author;
found_cmd->second.func(command, call_params, source);
}
}
}
void commandhandler::route(const interaction_create_t & event)
{
/* We don't need to check for prefixes here, slash command interactions
* dont have prefixes at all.
*/
command_interaction cmd = std::get<command_interaction>(event.command.data);
auto found_cmd = commands.find(lowercase(cmd.name));
if (found_cmd != commands.end()) {
/* Command found; parse parameters */
parameter_list_t call_params;
for (auto& p : found_cmd->second.parameters) {
command_parameter param;
const command_value& slash_parameter = event.get_parameter(p.first);
if (p.second.optional && slash_parameter.valueless_by_exception()) {
/* Missing optional parameter, skip this */
continue;
}
try {
switch (p.second.type) {
case pt_string: {
std::string s = std::get<std::string>(slash_parameter);
param = s;
}
break;
case pt_role: {
snowflake rid = std::get<snowflake>(slash_parameter);
role* r = dpp::find_role(rid);
if (r) {
param = *r;
}
}
break;
case pt_channel: {
snowflake cid = std::get<snowflake>(slash_parameter);
channel* c = dpp::find_channel(cid);
if (c) {
param = *c;
}
}
break;
case pt_user: {
snowflake uid = std::get<snowflake>(slash_parameter);
user* u = dpp::find_user(uid);
if (u) {
param = *u;
}
}
break;
case pt_integer: {
int32_t i = std::get<int32_t>(slash_parameter);
param = i;
}
case pt_boolean: {
bool b = std::get<bool>(slash_parameter);
param = b;
}
break;
}
}
catch (const std::bad_variant_access& e) {
/* Missing optional parameter, skip this */
continue;
}
/* Add parameter to the list */
call_params.push_back(std::make_pair(p.first, param));
}
/* Call command handler */
command_source source;
source.command_id = event.command.id;
source.command_token = event.command.token;
source.guild_id = event.command.guild_id;
source.channel_id = event.command.channel_id;
source.issuer = (user*)&event.command.usr;
found_cmd->second.func(cmd.name, call_params, source);
}
}
void commandhandler::reply(const dpp::message &m, command_source source)
{
dpp::message msg = m;
msg.guild_id = source.guild_id;
msg.channel_id = source.channel_id;
if (!source.command_token.empty() && source.command_id) {
owner->interaction_response_create(source.command_id, source.command_token, dpp::interaction_response(ir_channel_message_with_source, msg));
} else {
owner->message_create(msg);
}
}
};

625
vendor/DPP/src/dpp/discordclient.cpp vendored Normal file
View File

@ -0,0 +1,625 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#endif
#include <dpp/discordclient.h>
#include <dpp/cache.h>
#include <dpp/cluster.h>
#include <thread>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
#include <zlib.h>
#define PATH_UNCOMPRESSED "/?v=" DISCORD_API_VERSION "&encoding=json"
#define PATH_COMPRESSED "/?v=" DISCORD_API_VERSION "&encoding=json&compress=zlib-stream"
#define DECOMP_BUFFER_SIZE 512 * 1024
namespace dpp {
/* This is an internal class, defined externally as just a forward declaration for an opaque pointer.
* This is because we don't want an external dependency on zlib's headers
*/
class zlibcontext {
public:
z_stream d_stream;
};
discord_client::discord_client(dpp::cluster* _cluster, uint32_t _shard_id, uint32_t _max_shards, const std::string &_token, uint32_t _intents, bool comp)
: websocket_client(DEFAULT_GATEWAY, "443", comp ? PATH_COMPRESSED : PATH_UNCOMPRESSED),
creator(_cluster),
shard_id(_shard_id),
max_shards(_max_shards),
token(_token),
last_heartbeat(time(NULL)),
heartbeat_interval(0),
reconnects(0),
resumes(0),
last_seq(0),
sessionid(""),
intents(_intents),
runner(nullptr),
compressed(comp),
decompressed_total(0),
decomp_buffer(nullptr),
ready(false),
ping_start(0.0),
websocket_ping(0.0)
{
zlib = new zlibcontext();
Connect();
}
discord_client::~discord_client()
{
if (runner) {
runner->join();
delete runner;
}
delete zlib;
}
uint64_t discord_client::get_decompressed_bytes_in()
{
return decompressed_total;
}
void discord_client::SetupZLib()
{
if (compressed) {
zlib->d_stream.zalloc = (alloc_func)0;
zlib->d_stream.zfree = (free_func)0;
zlib->d_stream.opaque = (voidpf)0;
if (inflateInit(&(zlib->d_stream)) != Z_OK) {
throw dpp::exception("Can't initialise stream compression!");
}
this->decomp_buffer = new unsigned char[DECOMP_BUFFER_SIZE];
}
}
void discord_client::EndZLib()
{
if (compressed) {
inflateEnd(&(zlib->d_stream));
if (this->decomp_buffer) {
delete[] this->decomp_buffer;
this->decomp_buffer = nullptr;
}
}
}
void discord_client::ThreadRun()
{
SetupZLib();
do {
bool error = false;
ready = false;
message_queue.clear();
ssl_client::read_loop();
ssl_client::close();
EndZLib();
SetupZLib();
do {
error = false;
try {
ssl_client::Connect();
websocket_client::Connect();
}
catch (const std::exception &e) {
log(dpp::ll_error, std::string("Error establishing connection, retry in 5 seconds: ") + e.what());
ssl_client::close();
std::this_thread::sleep_for(std::chrono::seconds(5));
error = true;
}
} while (error);
} while(true);
}
void discord_client::Run()
{
this->runner = new std::thread(&discord_client::ThreadRun, this);
this->thread_id = runner->native_handle();
}
bool discord_client::HandleFrame(const std::string &buffer)
{
std::string& data = (std::string&)buffer;
/* gzip compression is a special case */
if (compressed) {
/* Check that we have a complete compressed frame */
if ((uint8_t)buffer[buffer.size() - 4] == 0x00 && (uint8_t)buffer[buffer.size() - 3] == 0x00 && (uint8_t)buffer[buffer.size() - 2] == 0xFF
&& (uint8_t)buffer[buffer.size() - 1] == 0xFF) {
/* Decompress buffer */
decompressed.clear();
zlib->d_stream.next_in = (Bytef *)buffer.c_str();
zlib->d_stream.avail_in = buffer.size();
do {
int have = 0;
zlib->d_stream.next_out = (Bytef*)decomp_buffer;
zlib->d_stream.avail_out = DECOMP_BUFFER_SIZE;
int ret = inflate(&(zlib->d_stream), Z_NO_FLUSH);
have = DECOMP_BUFFER_SIZE - zlib->d_stream.avail_out;
switch (ret)
{
case Z_NEED_DICT:
case Z_STREAM_ERROR:
this->Error(6000);
this->close();
return true;
break;
case Z_DATA_ERROR:
this->Error(6001);
this->close();
return true;
break;
case Z_MEM_ERROR:
this->Error(6002);
this->close();
return true;
break;
case Z_OK:
this->decompressed.append((const char*)decomp_buffer, have);
this->decompressed_total += have;
break;
default:
/* Stub */
break;
}
} while (zlib->d_stream.avail_out == 0);
data = decompressed;
} else {
/* No complete compressed frame yet */
return false;
}
}
log(dpp::ll_trace, fmt::format("R: {}", data));
json j;
try {
j = json::parse(data);
}
catch (const std::exception &e) {
log(dpp::ll_error, fmt::format("discord_client::HandleFrame {} [{}]", e.what(), data));
return true;
}
if (j.find("s") != j.end() && !j["s"].is_null()) {
last_seq = j["s"].get<uint64_t>();
}
if (j.find("op") != j.end()) {
uint32_t op = j["op"];
switch (op) {
case 9:
/* Reset session state and fall through to 10 */
op = 10;
log(dpp::ll_debug, fmt::format("Failed to resume session {}, will reidentify", sessionid));
this->sessionid = "";
this->last_seq = 0;
/* No break here, falls through to state 10 to cause a reidentify */
case 10:
/* Need to check carefully for the existence of this before we try to access it! */
if (j.find("d") != j.end() && j["d"].find("heartbeat_interval") != j["d"].end() && !j["d"]["heartbeat_interval"].is_null()) {
this->heartbeat_interval = j["d"]["heartbeat_interval"].get<uint32_t>();
}
if (last_seq && !sessionid.empty()) {
/* Resume */
log(dpp::ll_debug, fmt::format("Resuming session {} with seq={}", sessionid, last_seq));
json obj = {
{ "op", 6 },
{ "d", {
{"token", this->token },
{"session_id", this->sessionid },
{"seq", this->last_seq }
}
}
};
this->write(obj.dump());
resumes++;
} else {
/* Full connect */
while (time(NULL) < creator->last_identify + 5) {
uint32_t wait = (creator->last_identify + 5) - time(NULL);
log(dpp::ll_debug, fmt::format("Waiting {} seconds before identifying for session...", wait));
std::this_thread::sleep_for(std::chrono::seconds(wait));
}
log(dpp::ll_debug, "Connecting new session...");
json obj = {
{ "op", 2 },
{
"d",
{
{ "token", this->token },
{ "properties",
{
{ "$os", "Linux" },
{ "$browser", "D++" },
{ "$device", "D++" }
}
},
{ "shard", json::array({ shard_id, max_shards }) },
{ "compress", false },
{ "large_threshold", 250 }
}
}
};
if (this->intents) {
obj["d"]["intents"] = this->intents;
}
this->write(obj.dump());
this->connect_time = creator->last_identify = time(NULL);
reconnects++;
}
this->last_heartbeat_ack = time(nullptr);
websocket_ping = 0;
break;
case 0: {
std::string event = j.find("t") != j.end() && !j["t"].is_null() ? j["t"] : "";
HandleEvent(event, j, data);
}
break;
case 7:
log(dpp::ll_debug, fmt::format("Reconnection requested, closing socket {}", sessionid));
message_queue.clear();
shutdown(sfd, 2);
#ifdef _WIN32
if (sfd >= 0 && sfd < FD_SETSIZE) {
closesocket(sfd);
}
#else
::close(sfd);
#endif
break;
/* Heartbeat ack */
case 11:
this->last_heartbeat_ack = time(nullptr);
websocket_ping = utility::time_f() - ping_start;
break;
}
}
return true;
}
dpp::utility::uptime discord_client::get_uptime()
{
return dpp::utility::uptime(time(NULL) - connect_time);
}
bool discord_client::is_connected()
{
return (this->GetState() == CONNECTED) && (this->ready);
}
void discord_client::Error(uint32_t errorcode)
{
std::map<uint32_t, std::string> errortext = {
{ 1000, "Socket shutdown" },
{ 1001, "Client is leaving" },
{ 1002, "Endpoint received a malformed frame" },
{ 1003, "Endpoint received an unsupported frame" },
{ 1004, "Reserved code" },
{ 1005, "Expected close status, received none" },
{ 1006, "No close code frame has been receieved" },
{ 1007, "Endpoint received inconsistent message (e.g. malformed UTF-8)" },
{ 1008, "Generic error" },
{ 1009, "Endpoint won't process large frame" },
{ 1010, "Client wanted an extension which server did not negotiate" },
{ 1011, "Internal server error while operating" },
{ 1012, "Server/service is restarting" },
{ 1013, "Temporary server condition forced blocking client's request" },
{ 1014, "Server acting as gateway received an invalid response" },
{ 1015, "Transport Layer Security handshake failure" },
{ 4000, "Unknown error" },
{ 4001, "Unknown opcode" },
{ 4002, "Decode error" },
{ 4003, "Not authenticated" },
{ 4004, "Authentication failed" },
{ 4005, "Already authenticated" },
{ 4007, "Invalid seq" },
{ 4008, "Rate limited" },
{ 4009, "Session timed out" },
{ 4010, "Invalid shard" },
{ 4011, "Sharding required" },
{ 4012, "Invalid API version" },
{ 4013, "Invalid intent(s)" },
{ 4014, "Disallowed intent(s)" },
{ 6000, "ZLib Stream Error" },
{ 6001, "ZLib Data Error" },
{ 6002, "ZLib Memory Error" },
{ 6666, "Hell freezing over" }
};
std::string error = "Unknown error";
auto i = errortext.find(errorcode);
if (i != errortext.end()) {
error = i->second;
}
log(dpp::ll_warning, fmt::format("OOF! Error from underlying websocket: {}: {}", errorcode, error));
}
void discord_client::log(dpp::loglevel severity, const std::string &msg) const
{
if (creator->dispatch.log) {
/* Pass to user if theyve hooked the event */
dpp::log_t logmsg(nullptr, msg);
logmsg.severity = severity;
logmsg.message = msg;
creator->dispatch.log(logmsg);
}
}
void discord_client::QueueMessage(const std::string &j, bool to_front)
{
std::lock_guard<std::mutex> locker(queue_mutex);
if (to_front) {
message_queue.push_front(j);
} else {
message_queue.push_back(j);
}
}
void discord_client::ClearQueue()
{
std::lock_guard<std::mutex> locker(queue_mutex);
message_queue.clear();
}
size_t discord_client::GetQueueSize()
{
std::lock_guard<std::mutex> locker(queue_mutex);
return message_queue.size();
}
void discord_client::one_second_timer()
{
websocket_client::one_second_timer();
/* Every minute, rehash all containers from first shard.
* We can't just get shard with the id 0 because this won't
* work on a clustered environment
*/
auto shards = creator->get_shards();
auto first_iter = shards.begin();
if (first_iter != shards.end()) {
dpp::discord_client* first_shard = first_iter->second;
if ((time(NULL) % 60) == 0 && first_shard == this) {
dpp::garbage_collection();
}
}
/* This all only triggers if we are connected (have completed websocket, and received READY or RESUMED) */
if (this->is_connected()) {
/* If we stopped getting heartbeat acknowledgements, this means the connections is dead.
* This can happen to TCP connections which is why we have heartbeats in the first place.
* Miss two ACKS, forces a reconnection.
*/
if ((time(nullptr) - this->last_heartbeat_ack) > heartbeat_interval * 2) {
log(dpp::ll_warning, fmt::format("Missed heartbeat ACK, forcing reconnection to session {}", sessionid));
message_queue.clear();
shutdown(sfd, 2);
#ifdef _WIN32
if (sfd >= 0 && sfd < FD_SETSIZE) {
closesocket(sfd);
}
#else
::close(sfd);
#endif
return;
}
/* Rate limit outbound messages, 1 every odd second, 2 every even second */
for (int x = 0; x < (time(NULL) % 2) + 1; ++x) {
std::lock_guard<std::mutex> locker(queue_mutex);
if (message_queue.size()) {
std::string message = message_queue.front();
message_queue.pop_front();
/* Checking here with .find() saves us having to deserialise the json
* to find pings in our queue. The assumption is that the format of the
* ping isn't going to change.
*/
if (message.find("\"op\":1}") != std::string::npos) {
ping_start = utility::time_f();
}
this->write(message);
}
}
/* Send pings (heartbeat opcodes) before each interval. We send them slightly more regular than expected,
* just to be safe.
*/
if (this->heartbeat_interval && this->last_seq) {
/* Check if we're due to emit a heartbeat */
if (time(NULL) > last_heartbeat + ((heartbeat_interval / 1000.0) * 0.75)) {
QueueMessage(json({{"op", 1}, {"d", last_seq}}).dump(), true);
last_heartbeat = time(NULL);
}
}
}
}
uint64_t discord_client::get_guild_count() {
uint64_t total = 0;
dpp::cache* c = dpp::get_guild_cache();
dpp::cache_container& gc = c->get_container();
/* IMPORTANT: We must lock the container to iterate it */
std::lock_guard<std::mutex> lock(c->get_mutex());
for (auto g = gc.begin(); g != gc.end(); ++g) {
dpp::guild* gp = (dpp::guild*)g->second;
if (gp->shard_id == this->shard_id) {
total++;
}
}
return total;
}
uint64_t discord_client::get_member_count() {
uint64_t total = 0;
dpp::cache* c = dpp::get_guild_cache();
dpp::cache_container& gc = c->get_container();
/* IMPORTANT: We must lock the container to iterate it */
std::lock_guard<std::mutex> lock(c->get_mutex());
for (auto g = gc.begin(); g != gc.end(); ++g) {
dpp::guild* gp = (dpp::guild*)g->second;
if (gp->shard_id == this->shard_id) {
if (creator->cache_policy.user_policy == dpp::cp_aggressive) {
/* We can use actual member count if we are using full user caching */
total += gp->members.size();
} else {
/* Otherwise we use approximate guild member counts from guild_create */
total += gp->member_count;
}
}
}
return total;
}
uint64_t discord_client::get_channel_count() {
uint64_t total = 0;
dpp::cache* c = dpp::get_guild_cache();
dpp::cache_container& gc = c->get_container();
/* IMPORTANT: We must lock the container to iterate it */
std::lock_guard<std::mutex> lock(c->get_mutex());
for (auto g = gc.begin(); g != gc.end(); ++g) {
dpp::guild* gp = (dpp::guild*)g->second;
if (gp->shard_id == this->shard_id) {
total += gp->channels.size();
}
}
return total;
}
void discord_client::connect_voice(snowflake guild_id, snowflake channel_id) {
#ifdef HAVE_VOICE
std::lock_guard<std::mutex> lock(voice_mutex);
if (connecting_voice_channels.find(guild_id) == connecting_voice_channels.end()) {
connecting_voice_channels[guild_id] = new voiceconn(this, channel_id);
/* Once sent, this expects two events (in any order) on the websocket:
* VOICE_SERVER_UPDATE and VOICE_STATUS_UPDATE
*/
log(ll_debug, fmt::format("Sending op 4, guild {}", guild_id));
QueueMessage(json({
{ "op", 4 },
{ "d", {
{ "guild_id", std::to_string(guild_id) },
{ "channel_id", std::to_string(channel_id) },
{ "self_mute", false },
{ "self_deaf", false },
}
}
}).dump(), false);
}
#endif
}
void discord_client::disconnect_voice(snowflake guild_id) {
#ifdef HAVE_VOICE
std::lock_guard<std::mutex> lock(voice_mutex);
auto v = connecting_voice_channels.find(guild_id);
if (v != connecting_voice_channels.end()) {
log(ll_debug, fmt::format("Disconnecting voice, guild: {}", guild_id));
QueueMessage(json({
{ "op", 4 },
{ "d", {
{ "guild_id", std::to_string(guild_id) },
{ "channel_id", json::value_t::null },
{ "self_mute", false },
{ "self_deaf", false },
}
}
}).dump(), false);
delete v->second;
v->second = nullptr;
connecting_voice_channels.erase(v);
}
#endif
}
voiceconn* discord_client::get_voice(snowflake guild_id) {
#ifdef HAVE_VOICE
std::lock_guard<std::mutex> lock(voice_mutex);
auto v = connecting_voice_channels.find(guild_id);
if (v != connecting_voice_channels.end()) {
return v->second;
}
#endif
return nullptr;
}
voiceconn::voiceconn(discord_client* o, snowflake _channel_id) : creator(o), channel_id(_channel_id), voiceclient(nullptr) {
}
bool voiceconn::is_ready() {
return (!websocket_hostname.empty() && !session_id.empty() && !token.empty());
}
bool voiceconn::is_active() {
return voiceclient != nullptr;
}
void voiceconn::disconnect() {
if (this->is_active()) {
voiceclient->terminating = true;
voiceclient->close();
delete voiceclient;
voiceclient = nullptr;
}
}
voiceconn::~voiceconn() {
this->disconnect();
}
void voiceconn::connect(snowflake guild_id) {
if (this->is_ready() && !this->is_active()) {
/* This is wrapped in a thread because instantiating discord_voice_client can initiate a blocking SSL_connect() */
auto t = std::thread([guild_id, this]() {
try {
this->creator->log(ll_debug, fmt::format("Connecting voice for guild {} channel {}", guild_id, this->channel_id));
this->voiceclient = new discord_voice_client(creator->creator, this->channel_id, guild_id, this->token, this->session_id, this->websocket_hostname);
/* Note: Spawns thread! */
this->voiceclient->Run();
}
catch (std::exception &e) {
this->creator->log(ll_error, fmt::format("Can't connect to voice websocket (guild_id: {}, channel_id: {}): {}", guild_id, this->channel_id, e.what()));
}
});
t.detach();
}
}
};

325
vendor/DPP/src/dpp/discordevents.cpp vendored Normal file
View File

@ -0,0 +1,325 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#define _XOPEN_SOURCE
#include <string>
#include <iostream>
#include <fstream>
#include <time.h>
#include <stdlib.h>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/event.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
#ifdef _WIN32
#include <time.h>
#include <iomanip>
#include <sstream>
char* strptime(const char* s, const char* f, struct tm* tm) {
std::istringstream input(s);
input.imbue(std::locale(setlocale(LC_ALL, nullptr)));
input >> std::get_time(tm, f);
if (input.fail()) {
return const_cast< char* >("");
}
return (char*)(s + input.tellg());
}
#endif
namespace dpp {
uint64_t SnowflakeNotNull(const json* j, const char *keyname) {
/* Snowflakes are a special case. Pun intended.
* Because discord drinks the javascript kool-aid, they have to send 64 bit integers as strings as js can't deal with them
* even though we can. So, all snowflakes are sent and received wrapped as string values and must be read by nlohmann::json
* as string types, then converted from string to uint64_t. Checks for existence of the value, and that it is a string containing
* a number. If not, then this function returns 0.
*/
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && k->is_string() ? strtoull(k->get<std::string>().c_str(), nullptr, 10) : 0;
} else {
return 0;
}
}
void SetSnowflakeNotNull(const json* j, const char *keyname, uint64_t &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && k->is_string() ? strtoull(k->get<std::string>().c_str(), nullptr, 10) : 0;
}
}
std::string StringNotNull(const json* j, const char *keyname) {
/* Returns empty string if the value is not a string, or is null or not defined */
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && k->is_string() ? k->get<std::string>() : "";
} else {
return "";
}
}
void SetStringNotNull(const json* j, const char *keyname, std::string &v) {
/* Returns empty string if the value is not a string, or is null or not defined */
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && k->is_string() ? k->get<std::string>() : "";
}
}
uint64_t Int64NotNull(const json* j, const char *keyname) {
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && !k->is_string() ? k->get<uint64_t>() : 0;
} else {
return 0;
}
}
void SetInt64NotNull(const json* j, const char *keyname, uint64_t &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && !k->is_string() ? k->get<uint64_t>() : 0;
}
}
uint32_t Int32NotNull(const json* j, const char *keyname) {
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && !k->is_string() ? k->get<uint32_t>() : 0;
} else {
return 0;
}
}
void SetInt32NotNull(const json* j, const char *keyname, uint32_t &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && !k->is_string() ? k->get<uint32_t>() : 0;
}
}
uint16_t Int16NotNull(const json* j, const char *keyname) {
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && !k->is_string() ? k->get<uint16_t>() : 0;
} else {
return 0;
}
}
void SetInt16NotNull(const json* j, const char *keyname, uint16_t &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && !k->is_string() ? k->get<uint16_t>() : 0;
}
}
uint8_t Int8NotNull(const json* j, const char *keyname) {
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() && !k->is_string() ? k->get<uint8_t>() : 0;
} else {
return 0;
}
}
void SetInt8NotNull(const json* j, const char *keyname, uint8_t &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() && !k->is_string() ? k->get<uint8_t>() : 0;
}
}
bool BoolNotNull(const json* j, const char *keyname) {
auto k = j->find(keyname);
if (k != j->end()) {
return !k->is_null() ? (k->get<bool>() == true) : false;
} else {
return false;
}
}
void SetBoolNotNull(const json* j, const char *keyname, bool &v) {
auto k = j->find(keyname);
if (k != j->end()) {
v = !k->is_null() ? (k->get<bool>() == true) : false;
}
}
std::string base64_encode(unsigned char const* buf, unsigned int buffer_length) {
/* Quick and dirty base64 encode */
static const char to_base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
size_t ret_size = buffer_length + 2;
ret_size = 4 * ret_size / 3;
std::string ret;
ret.reserve(ret_size);
for (unsigned int i=0; i<ret_size/4; ++i)
{
size_t index = i*3;
unsigned char b3[3];
b3[0] = buf[index+0];
b3[1] = buf[index+1];
b3[2] = buf[index+2];
ret.push_back(to_base64[ ((b3[0] & 0xfc) >> 2) ]);
ret.push_back(to_base64[ ((b3[0] & 0x03) << 4) + ((b3[1] & 0xf0) >> 4) ]);
ret.push_back(to_base64[ ((b3[1] & 0x0f) << 2) + ((b3[2] & 0xc0) >> 6) ]);
ret.push_back(to_base64[ ((b3[2] & 0x3f)) ]);
}
return ret;
}
time_t TimestampNotNull(const json* j, const char* keyname)
{
/* Parses discord ISO 8061 timestamps to time_t, accounting for local time adjustment.
* Note that discord timestamps contain a decimal seconds part, which time_t and struct tm
* can't handle. We strip these out.
*/
time_t retval = 0;
if (j->find(keyname) != j->end() && !(*j)[keyname].is_null() && (*j)[keyname].is_string()) {
tm timestamp = {};
std::string timedate = (*j)[keyname].get<std::string>();
if (timedate.find('+') != std::string::npos && timedate.find('.') != std::string::npos) {
std::string tzpart = timedate.substr(timedate.find('+'), timedate.length());
timedate = timedate.substr(0, timedate.find('.')) + tzpart ;
strptime(timedate.substr(0, 19).c_str(), "%FT%TZ%z", &timestamp);
timestamp.tm_isdst = 0;
retval = mktime(&timestamp);
} else {
strptime(timedate.substr(0, 19).c_str(), "%F %T", &timestamp);
retval = mktime(&timestamp);
}
}
return retval;
}
void SetTimestampNotNull(const json* j, const char* keyname, time_t &v)
{
/* Parses discord ISO 8061 timestamps to time_t, accounting for local time adjustment.
* Note that discord timestamps contain a decimal seconds part, which time_t and struct tm
* can't handle. We strip these out.
*/
time_t retval = 0;
if (j->find(keyname) != j->end() && !(*j)[keyname].is_null() && (*j)[keyname].is_string()) {
tm timestamp = {};
std::string timedate = (*j)[keyname].get<std::string>();
if (timedate.find('+') != std::string::npos && timedate.find('.') != std::string::npos) {
std::string tzpart = timedate.substr(timedate.find('+'), timedate.length());
timedate = timedate.substr(0, timedate.find('.')) + tzpart ;
strptime(timedate.substr(0, 19).c_str(), "%FT%TZ%z", &timestamp);
timestamp.tm_isdst = 0;
retval = mktime(&timestamp);
} else {
strptime(timedate.substr(0, 19).c_str(), "%F %T", &timestamp);
retval = mktime(&timestamp);
}
v = retval;
}
}
std::map<std::string, dpp::events::event*> eventmap = {
{ "__LOG__", new dpp::events::logger() },
{ "GUILD_CREATE", new dpp::events::guild_create() },
{ "GUILD_UPDATE", new dpp::events::guild_update() },
{ "GUILD_DELETE", new dpp::events::guild_delete() },
{ "GUILD_MEMBER_UPDATE", new dpp::events::guild_member_update() },
{ "RESUMED", new dpp::events::resumed() },
{ "READY", new dpp::events::ready() },
{ "CHANNEL_CREATE", new dpp::events::channel_create() },
{ "CHANNEL_UPDATE", new dpp::events::channel_update() },
{ "CHANNEL_DELETE", new dpp::events::channel_delete() },
{ "PRESENCE_UPDATE", new dpp::events::presence_update() },
{ "TYPING_START", new dpp::events::typing_start() },
{ "MESSAGE_CREATE", new dpp::events::message_create() },
{ "MESSAGE_UPDATE", new dpp::events::message_update() },
{ "MESSAGE_DELETE", new dpp::events::message_delete() },
{ "MESSAGE_DELETE_BULK", new dpp::events::message_delete_bulk() },
{ "MESSAGE_REACTION_ADD", new dpp::events::message_reaction_add() },
{ "MESSAGE_REACTION_REMOVE", new dpp::events::message_reaction_remove() },
{ "MESSAGE_REACTION_REMOVE_ALL", new dpp::events::message_reaction_remove_all() },
{ "MESSAGE_REACTION_REMOVE_EMOJI", new dpp::events::message_reaction_remove_emoji() },
{ "CHANNEL_PINS_UPDATE", new dpp::events::channel_pins_update() },
{ "GUILD_BAN_ADD", new dpp::events::guild_ban_add() },
{ "GUILD_BAN_REMOVE", new dpp::events::guild_ban_remove() },
{ "GUILD_EMOJIS_UPDATE", new dpp::events::guild_emojis_update() },
{ "GUILD_INTEGRATIONS_UPDATE", new dpp::events::guild_integrations_update() },
{ "INTEGRATION_CREATE", new dpp::events::integration_create() },
{ "INTEGRATION_UPDATE", new dpp::events::integration_update() },
{ "INTEGRATION_DELETE", new dpp::events::integration_delete() },
{ "GUILD_MEMBER_ADD", new dpp::events::guild_member_add() },
{ "GUILD_MEMBER_REMOVE", new dpp::events::guild_member_remove() },
{ "GUILD_MEMBERS_CHUNK", new dpp::events::guild_members_chunk() },
{ "GUILD_ROLE_CREATE", new dpp::events::guild_role_create() },
{ "GUILD_ROLE_UPDATE", new dpp::events::guild_role_update() },
{ "GUILD_ROLE_DELETE", new dpp::events::guild_role_delete() },
{ "VOICE_STATE_UPDATE", new dpp::events::voice_state_update() },
{ "VOICE_SERVER_UPDATE", new dpp::events::voice_server_update() },
{ "WEBHOOKS_UPDATE", new dpp::events::webhooks_update() },
{ "INVITE_CREATE", new dpp::events::invite_create() },
{ "INVITE_DELETE", new dpp::events::invite_delete() },
{ "APPLICATION_COMMAND_CREATE", new dpp::events::application_command_create() },
{ "APPLICATION_COMMAND_UPDATE", new dpp::events::application_command_update() },
{ "APPLICATION_COMMAND_DELETE", new dpp::events::application_command_delete() },
{ "INTERACTION_CREATE", new dpp::events::interaction_create() },
{ "USER_UPDATE", new dpp::events::user_update() },
{ "GUILD_JOIN_REQUEST_DELETE", new dpp::events::guild_join_request_delete() },
{ "STAGE_INSTANCE_CREATE", new dpp::events::stage_instance_create() },
{ "STAGE_INSTANCE_DELETE", new dpp::events::stage_instance_delete() },
{ "THREAD_CREATE", new dpp::events::thread_create() },
{ "THREAD_UPDATE", new dpp::events::thread_update() },
{ "THREAD_DELETE", new dpp::events::thread_delete() },
{ "THREAD_LIST_SYNC", new dpp::events::thread_list_sync() },
{ "THREAD_MEMBER_UPDATE", new dpp::events::thread_member_update() },
{ "THREAD_MEMBERS_UPDATE", new dpp::events::thread_members_update() },
{ "GUILD_APPLICATION_COMMAND_COUNTS_UPDATE", nullptr },
{ "GUILD_STICKERS_UPDATE", new dpp::events::guild_stickers_update() },
{ "APPLICATION_COMMAND_PERMISSIONS_UPDATE", nullptr },
};
void discord_client::HandleEvent(const std::string &event, json &j, const std::string &raw)
{
auto ev_iter = eventmap.find(event);
if (ev_iter != eventmap.end()) {
/* A handler with nullptr is silently ignored. We don't plan to make a handler for it
* so this usually some user-only thing thats crept into the API and shown to bots
* that we dont care about.
*/
if (ev_iter->second != nullptr) {
ev_iter->second->handle(this, j, raw);
}
} else {
log(dpp::ll_debug, fmt::format("Unhandled event: {}, {}", event, j.dump()));
}
}
};

View File

@ -0,0 +1,747 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#ifndef WIN32
#include <unistd.h>
#include <arpa/inet.h>
#endif
#include <dpp/discordvoiceclient.h>
#include <dpp/cache.h>
#include <dpp/cluster.h>
#include <thread>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
#include <zlib.h>
namespace dpp {
std::string external_ip;
/**
* @brief Represents an RTP packet. Size should always be exactly 12.
*/
struct rtp_header {
uint16_t constant;
uint16_t sequence;
uint32_t timestamp;
uint32_t ssrc;
rtp_header(uint16_t _seq, uint32_t _ts, uint32_t _ssrc) : constant(htons(0x8078)), sequence(htons(_seq)), timestamp(htonl(_ts)), ssrc(htonl(_ssrc)) {
}
};
bool discord_voice_client::sodium_initialised = false;
discord_voice_client::discord_voice_client(dpp::cluster* _cluster, snowflake _channel_id, snowflake _server_id, const std::string &_token, const std::string &_session_id, const std::string &_host)
: websocket_client(_host.substr(0, _host.find(":")), _host.substr(_host.find(":") + 1, _host.length()), "/?v=4"),
creator(_cluster),
channel_id(_channel_id),
server_id(_server_id),
token(_token),
last_heartbeat(time(NULL)),
heartbeat_interval(0),
sessionid(_session_id),
runner(nullptr),
terminating(false),
fd(-1),
secret_key(nullptr),
sequence(0),
timestamp(0),
sending(false),
paused(false),
tracks(0)
{
#if HAVE_VOICE
if (!discord_voice_client::sodium_initialised) {
if (sodium_init() < 0) {
throw dpp::exception("discord_voice_client::discord_voice_client; sodium_init() failed");
}
int opusError = 0;
encoder = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &opusError);
if (opusError) {
throw dpp::exception(fmt::format("discord_voice_client::discord_voice_client; opus_encoder_create() failed: {}", opusError));
}
opusError = 0;
decoder = opus_decoder_create(48000, 2, &opusError);
if (opusError) {
throw dpp::exception(fmt::format("discord_voice_client::discord_voice_client; opus_decoder_create() failed: {}", opusError));
}
repacketizer = opus_repacketizer_create();
discord_voice_client::sodium_initialised = true;
}
Connect();
#endif
}
discord_voice_client::~discord_voice_client()
{
if (runner) {
this->terminating = true;
runner->join();
delete runner;
runner = nullptr;
}
#if HAVE_VOICE
if (encoder) {
opus_encoder_destroy(encoder);
encoder = nullptr;
}
if (decoder) {
opus_decoder_destroy(decoder);
decoder = nullptr;
}
if (repacketizer) {
opus_repacketizer_destroy(repacketizer);
repacketizer = nullptr;
}
#endif
if (secret_key) {
delete[] secret_key;
secret_key = nullptr;
}
}
bool discord_voice_client::is_ready() {
return secret_key != nullptr;
}
bool discord_voice_client::is_playing() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
return (!this->outbuf.empty());
}
void discord_voice_client::ThreadRun()
{
do {
ssl_client::read_loop();
ssl_client::close();
if (!terminating) {
ssl_client::Connect();
websocket_client::Connect();
}
} while(!terminating);
}
void discord_voice_client::Run()
{
this->runner = new std::thread(&discord_voice_client::ThreadRun, this);
this->thread_id = runner->native_handle();
}
int discord_voice_client::UDPSend(const char* data, size_t length)
{
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(this->port);
servaddr.sin_addr.s_addr = inet_addr(this->ip.c_str());
return sendto(this->fd, data, length, 0, (const struct sockaddr*)&servaddr, sizeof(sockaddr_in));
}
int discord_voice_client::UDPRecv(char* data, size_t max_length)
{
struct sockaddr sa;
socklen_t sl;
return recvfrom(this->fd, data, max_length, 0, (struct sockaddr*)&sa, &sl);
}
bool discord_voice_client::HandleFrame(const std::string &data)
{
log(dpp::ll_trace, fmt::format("R: {}", data));
json j;
try {
j = json::parse(data);
}
catch (const std::exception &e) {
log(dpp::ll_error, fmt::format("discord_voice_client::HandleFrame {} [{}]", e.what(), data));
return true;
}
if (j.find("op") != j.end()) {
uint32_t op = j["op"];
switch (op) {
/* Voice resume */
case 9:
log(ll_debug, "Voice connection resumed");
break;
/* Voice HELLO */
case 8: {
if (j.find("d") != j.end() && j["d"].find("heartbeat_interval") != j["d"].end() && !j["d"]["heartbeat_interval"].is_null()) {
this->heartbeat_interval = j["d"]["heartbeat_interval"].get<uint32_t>();
}
if (modes.size()) {
log(dpp::ll_debug, "Resuming voice session...");
json obj = {
{ "op", 7 },
{
"d",
{
{ "server_id", std::to_string(this->server_id) },
{ "session_id", this->sessionid },
{ "token", this->token },
}
}
};
this->write(obj.dump());
} else {
log(dpp::ll_debug, "Connecting new voice session...");
json obj = {
{ "op", 0 },
{
"d",
{
{ "user_id", creator->me.id },
{ "server_id", std::to_string(this->server_id) },
{ "session_id", this->sessionid },
{ "token", this->token },
}
}
};
this->write(obj.dump());
}
this->connect_time = time(NULL);
}
break;
/* Session description */
case 4: {
json &d = j["d"];
secret_key = new uint8_t[32];
size_t ofs = 0;
for (auto & c : d["secret_key"]) {
*(secret_key + ofs) = (uint8_t)c;
ofs++;
if (ofs > 31) {
break;
}
}
if (creator->dispatch.voice_ready) {
voice_ready_t rdy(nullptr, data);
rdy.voice_client = this;
rdy.voice_channel_id = this->channel_id;
creator->dispatch.voice_ready(rdy);
}
}
break;
/* Voice ready */
case 2: {
/* Video stream stuff comes in this frame too, but we can't use it (YET!) */
json &d = j["d"];
this->ip = d["ip"].get<std::string>();
this->port = d["port"].get<uint16_t>();
this->ssrc = d["ssrc"].get<uint64_t>();
// Modes
for (auto & m : d["modes"]) {
this->modes.push_back(m.get<std::string>());
}
log(ll_debug, fmt::format("Voice websocket established; UDP endpoint: {}:{} [ssrc={}] with {} modes", ip, port, ssrc, modes.size()));
external_ip = discover_ip();
int newfd = -1;
if ((newfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(0);
if (bind(newfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
throw dpp::exception("Can't bind() client UDP socket");
}
#ifdef _WIN32
u_long mode = 1;
int result = ioctlsocket(newfd, FIONBIO, &mode);
if (result != NO_ERROR)
throw dpp::exception("Can't switch socket to non-blocking mode!");
#else
int ofcmode;
ofcmode = fcntl(newfd, F_GETFL, 0);
ofcmode |= O_NDELAY;
if (fcntl(newfd, F_SETFL, ofcmode)) {
throw dpp::exception("Can't switch socket to non-blocking mode!");
}
#endif
/* Hook select() in the ssl_client to add a new file descriptor */
this->fd = newfd;
this->custom_writeable_fd = std::bind(&discord_voice_client::WantWrite, this);
this->custom_readable_fd = std::bind(&discord_voice_client::WantRead, this);
this->custom_writeable_ready = std::bind(&discord_voice_client::WriteReady, this);
this->custom_readable_ready = std::bind(&discord_voice_client::ReadReady, this);
int bound_port = 0;
struct sockaddr_in sin;
socklen_t len = sizeof(sin);
if (getsockname(this->fd, (struct sockaddr *)&sin, &len) > -1) {
bound_port = ntohs(sin.sin_port);
}
log(ll_debug, fmt::format("External IP address: {}", external_ip));
this->write(json({
{ "op", 1 },
{ "d", {
{ "protocol", "udp" },
{ "data", {
{ "address", external_ip },
{ "port", bound_port },
{ "mode", "xsalsa20_poly1305" }
}
}
}
}
}).dump());
}
}
break;
}
}
return true;
}
void discord_voice_client::pause_audio(bool pause) {
this->paused = pause;
}
bool discord_voice_client::is_paused() {
return this->paused;
}
float discord_voice_client::get_secs_remaining() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
/* Audio stream sends one packet every 60ms which means there are 16.666 packets per second */
return (outbuf.size() / 16.666666);
}
dpp::utility::uptime discord_voice_client::get_remaining() {
float fp_secs = get_secs_remaining();
return dpp::utility::uptime((time_t)ceil(fp_secs));
}
void discord_voice_client::stop_audio() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
outbuf.clear();
}
void discord_voice_client::Send(const char* packet, size_t len) {
std::lock_guard<std::mutex> lock(this->stream_mutex);
outbuf.push_back(std::string(packet, len));
}
void discord_voice_client::ReadReady()
{
/* XXX Decoding of voice not currently supported.
* Audio stream will always be a nullptr until then.
* See: https://github.com/discord/discord-api-docs/issues/365
* See also: https://github.com/discord/discord-api-docs/issues/1337
*/
uint8_t buffer[65535];
int r = this->UDPRecv((char*)buffer, sizeof(buffer));
if (r > 0 && creator->dispatch.voice_receive) {
voice_receive_t vr(nullptr, std::string((const char*)buffer, r));
vr.voice_client = this;
vr.audio = nullptr;
vr.audio_size = 0;
creator->dispatch.voice_receive(vr);
}
}
void discord_voice_client::WriteReady()
{
bool call_event = false;
bool track_marker_found = false;
uint64_t bufsize = 0;
{
std::lock_guard<std::mutex> lock(this->stream_mutex);
if (!this->paused && outbuf.size()) {
if (outbuf[0].size() == 2 && ((uint16_t)(*(outbuf[0].data()))) == AUDIO_TRACK_MARKER) {
outbuf.erase(outbuf.begin());
track_marker_found = true;
if (tracks > 0)
tracks--;
}
if (outbuf.size()) {
if (this->UDPSend(outbuf[0].data(), outbuf[0].length()) == outbuf[0].length()) {
outbuf.erase(outbuf.begin());
call_event = true;
bufsize = outbuf.size();
}
}
}
}
if (call_event) {
std::this_thread::sleep_for(std::chrono::milliseconds(60));
if (creator->dispatch.voice_buffer_send) {
voice_buffer_send_t snd(nullptr, "");
snd.buffer_size = bufsize;
snd.voice_client = this;
creator->dispatch.voice_buffer_send(snd);
}
}
if (track_marker_found) {
if (creator->dispatch.voice_track_marker) {
voice_track_marker_t vtm(nullptr, "");
vtm.voice_client = this;
{
std::lock_guard<std::mutex> lock(this->stream_mutex);
if (track_meta.size()) {
vtm.track_meta = track_meta[0];
track_meta.erase(track_meta.begin());
}
}
creator->dispatch.voice_track_marker(vtm);
}
}
}
dpp::utility::uptime discord_voice_client::get_uptime()
{
return dpp::utility::uptime(time(NULL) - connect_time);
}
bool discord_voice_client::is_connected()
{
return (this->GetState() == CONNECTED);
}
int discord_voice_client::WantWrite() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
if (!this->paused && outbuf.size()) {
return fd;
} else {
return -1;
}
}
int discord_voice_client::WantRead() {
return fd;
}
void discord_voice_client::Error(uint32_t errorcode)
{
std::map<uint32_t, std::string> errortext = {
{ 1000, "Socket shutdown" },
{ 1001, "Client is leaving" },
{ 1002, "Endpoint received a malformed frame" },
{ 1003, "Endpoint received an unsupported frame" },
{ 1004, "Reserved code" },
{ 1005, "Expected close status, received none" },
{ 1006, "No close code frame has been receieved" },
{ 1007, "Endpoint received inconsistent message (e.g. malformed UTF-8)" },
{ 1008, "Generic error" },
{ 1009, "Endpoint won't process large frame" },
{ 1010, "Client wanted an extension which server did not negotiate" },
{ 1011, "Internal server error while operating" },
{ 1012, "Server/service is restarting" },
{ 1013, "Temporary server condition forced blocking client's request" },
{ 1014, "Server acting as gateway received an invalid response" },
{ 1015, "Transport Layer Security handshake failure" },
{ 4001, "Unknown opcode" },
{ 4002, "Failed to decode payload" },
{ 4003, "Not authenticated" },
{ 4004, "Authentication failed" },
{ 4005, "Already authenticated" },
{ 4006, "Session no longer valid" },
{ 4009, "Session timeout" },
{ 4011, "Server not found" },
{ 4012, "Unknown protocol" },
{ 4014, "Disconnected" },
{ 4015, "Voice server crashed" },
{ 4016, "Unknown encryption mode" }
};
std::string error = "Unknown error";
auto i = errortext.find(errorcode);
if (i != errortext.end()) {
error = i->second;
}
log(dpp::ll_warning, fmt::format("Voice session error: {} on channel {}: {}", errorcode, channel_id, error));
/* Errors 4004...4016 except 4014 are fatal and cause termination of the voice session */
if (errorcode >= 4003 && errorcode != 4014) {
stop_audio();
this->terminating = true;
log(dpp::ll_error, "This is a non-recoverable error, giving up on voice connection");
}
}
void discord_voice_client::log(dpp::loglevel severity, const std::string &msg)
{
creator->log(severity, msg);
}
void discord_voice_client::QueueMessage(const std::string &j, bool to_front)
{
std::lock_guard<std::mutex> locker(queue_mutex);
if (to_front) {
message_queue.push_front(j);
} else {
message_queue.push_back(j);
}
}
void discord_voice_client::ClearQueue()
{
std::lock_guard<std::mutex> locker(queue_mutex);
message_queue.clear();
}
size_t discord_voice_client::GetQueueSize()
{
std::lock_guard<std::mutex> locker(queue_mutex);
return message_queue.size();
}
const std::vector<std::string> discord_voice_client::get_marker_metadata() {
std::lock_guard<std::mutex> locker(queue_mutex);
return track_meta;
}
void discord_voice_client::one_second_timer()
{
if (terminating) {
throw dpp::exception("Terminating voice connection");
}
/* Rate limit outbound messages, 1 every odd second, 2 every even second */
if (this->GetState() == CONNECTED) {
for (int x = 0; x < (time(NULL) % 2) + 1; ++x) {
std::lock_guard<std::mutex> locker(queue_mutex);
if (message_queue.size()) {
std::string message = message_queue.front();
message_queue.pop_front();
this->write(message);
}
}
if (this->heartbeat_interval) {
/* Check if we're due to emit a heartbeat */
if (time(NULL) > last_heartbeat + ((heartbeat_interval / 1000.0) * 0.75)) {
QueueMessage(json({{"op", 3}, {"d", rand()}}).dump(), true);
last_heartbeat = time(NULL);
}
}
}
}
size_t discord_voice_client::encode(uint8_t *input, size_t inDataSize, uint8_t *output, size_t &outDataSize)
{
#if HAVE_VOICE
outDataSize = 0;
int mEncFrameBytes = 11520;
int mEncFrameSize = 2880;
if (0 == (inDataSize % mEncFrameBytes)) {
bool isOk = true;
size_t cur = 0;
uint8_t *out = encode_buffer;
memset(out, 0, sizeof(encode_buffer));
repacketizer = opus_repacketizer_init(repacketizer);
for (size_t i = 0; i < (inDataSize / mEncFrameBytes); ++ i) {
const opus_int16* pcm = (opus_int16*)(input + i * mEncFrameBytes);
int ret = opus_encode(encoder, pcm, mEncFrameSize, out, 65536);
if (ret > 0) {
int retval = opus_repacketizer_cat(repacketizer, out, ret);
if (retval != OPUS_OK) {
isOk = false;
log(ll_warning, fmt::format("opus_repacketizer_cat(): {}", opus_strerror(retval)));
break;
}
out += ret;
cur += ret;
} else {
isOk = false;
log(ll_warning, fmt::format("opus_encode(): {}", opus_strerror(ret)));
break;
}
}
if (isOk) {
int ret = opus_repacketizer_out(repacketizer, output, 65536);
if (ret > 0) {
outDataSize = ret;
} else {
log(ll_warning, fmt::format("opus_repacketizer_out(): {}", opus_strerror(ret)));
}
}
} else {
throw dpp::exception(fmt::format("Invalid input data length: {}, must be n times of {}", inDataSize, mEncFrameBytes));
}
#endif
return outDataSize;
}
void discord_voice_client::insert_marker(const std::string& metadata) {
/* Insert a track marker. A track marker is a single 16 bit value of 0xFFFF.
* This is too small to be a valid RTP packet so the send function knows not
* to actually send it, and instead to skip it
*/
uint16_t tm = AUDIO_TRACK_MARKER;
Send((const char*)&tm, sizeof(uint16_t));
{
std::lock_guard<std::mutex> lock(this->stream_mutex);
track_meta.push_back(metadata);
tracks++;
}
}
uint32_t discord_voice_client::get_tracks_remaining() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
if (outbuf.size() == 0)
return 0;
else
return tracks + 1;
}
void discord_voice_client::skip_to_next_marker() {
std::lock_guard<std::mutex> lock(this->stream_mutex);
/* Keep popping the first entry off the outbuf until the first entry is a track marker */
while (outbuf.size() && outbuf[0].size() != sizeof(uint16_t) && ((uint16_t)(*(outbuf[0].data()))) != AUDIO_TRACK_MARKER) {
outbuf.erase(outbuf.begin());
}
if (outbuf.size()) {
/* Remove the actual track marker out of the buffer */
outbuf.erase(outbuf.begin());
}
if (tracks > 0)
tracks--;
if (track_meta.size()) {
track_meta.erase(track_meta.begin());
}
}
void discord_voice_client::send_audio(uint16_t* audio_data, const size_t length, bool use_opus) {
#if HAVE_VOICE
const size_t max_frame_bytes = 11520;
uint8_t pad[max_frame_bytes] = { 0 };
if (length > max_frame_bytes && use_opus) {
std::string s_audio_data((const char*)audio_data, length);
while (s_audio_data.length() > max_frame_bytes) {
std::string packet(s_audio_data.substr(0, max_frame_bytes));
s_audio_data.erase(s_audio_data.begin(), s_audio_data.begin() + max_frame_bytes);
if (packet.size() < max_frame_bytes) {
packet.resize(max_frame_bytes, 0);
}
send_audio((uint16_t*)packet.data(), max_frame_bytes, use_opus);
}
return;
}
int frameSize = 2880;
opus_int32 encodedAudioMaxLength = length;
std::vector<uint8_t> encodedAudioData(encodedAudioMaxLength);
size_t encodedAudioLength = encodedAudioMaxLength;
if (use_opus) {
encodedAudioLength = this->encode((uint8_t*)audio_data, length, encodedAudioData.data(), encodedAudioLength);
} else {
}
++sequence;
const int headerSize = 12;
const int nonceSize = 24;
rtp_header header(sequence, timestamp, ssrc);
int8_t nonce[nonceSize];
std::memcpy(nonce, &header, sizeof(header));
std::memset(nonce + sizeof(header), 0, sizeof(nonce) - sizeof(header));
std::vector<uint8_t> audioDataPacket(sizeof(header) + encodedAudioLength + crypto_secretbox_MACBYTES);
std::memcpy(audioDataPacket.data(), &header, sizeof(header));
crypto_secretbox_easy(audioDataPacket.data() + sizeof(header), encodedAudioData.data(), encodedAudioLength, (const unsigned char*)nonce, secret_key);
Send((const char*)audioDataPacket.data(), audioDataPacket.size());
timestamp += frameSize;
if (!this->sending) {
this->QueueMessage(json({
{"op", 5},
{"d", {
{"speaking", 1},
{"delay", 0},
{"ssrc", ssrc}
}}
}).dump(), true);
sending = true;
}
#endif
}
std::string discord_voice_client::discover_ip() {
SOCKET newfd = -1;
unsigned char packet[74] = { 0 };
(*(uint16_t*)(packet)) = htons(0x01);
(*(uint16_t*)(packet + 2)) = htons(70);
(*(uint32_t*)(packet + 4)) = htonl(this->ssrc);
if ((newfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
sockaddr_in servaddr;
socklen_t sl = sizeof(servaddr);
memset(&servaddr, 0, sizeof(sockaddr_in));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(0);
if (bind(newfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
log(ll_warning, "Could not bind socket for IP discovery");
return "";
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(this->port);
servaddr.sin_addr.s_addr = inet_addr(this->ip.c_str());
if (::connect(newfd, (const struct sockaddr*)&servaddr, sizeof(sockaddr_in)) < 0) {
log(ll_warning, "Could not connect socket for IP discovery");
return "";
}
if (send(newfd, (const char*)packet, 74, 0) == -1) {
log(ll_warning, "Could not send packet for IP discovery");
return "";
}
if (recv(newfd, (char*)packet, 74, 0) == -1) {
log(ll_warning, "Could not receive packet for IP discovery");
return "";
}
shutdown(newfd, 2);
#ifdef _WIN32
if (newfd >= 0 && newfd < FD_SETSIZE) {
closesocket(newfd);
}
#else
::close(newfd);
#endif
//utility::debug_dump(packet, 74);
return std::string((const char*)(packet + 8));
}
return "";
}
};

155
vendor/DPP/src/dpp/dispatcher.cpp vendored Normal file
View File

@ -0,0 +1,155 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/slashcommand.h>
#include <dpp/dispatcher.h>
#include <dpp/cluster.h>
#include <fmt/format.h>
#include <variant>
#define event_ctor(a, b) a::a(discord_client* client, const std::string &raw) : b(client, raw) {}
namespace dpp {
event_dispatch_t::event_dispatch_t(discord_client* client, const std::string &raw) : from(client), raw_event(raw)
{
}
void interaction_create_t::reply(interaction_response_type t, const message & m) const
{
from->creator->interaction_response_create(this->command.id, this->command.token, dpp::interaction_response(t, m));
}
void interaction_create_t::reply(interaction_response_type t, const std::string & mt) const
{
this->reply(t, dpp::message(this->command.channel_id, mt, mt_application_command));
}
void interaction_create_t::get_original_response(command_completion_event_t callback) const
{
from->creator->post_rest(API_PATH "/webhooks", std::to_string(command.application_id), command.token + "/messages/@original", m_get, "", [callback](json &j, const http_request_completion_t& http) {
if (callback) {
callback(confirmation_callback_t("message", message().fill_from_json(&j), http));
}
});
}
void interaction_create_t::edit_response(const message & m) const
{
from->creator->interaction_response_edit(this->command.token, m);
}
void interaction_create_t::edit_response(const std::string & mt) const
{
this->edit_response(dpp::message(this->command.channel_id, mt, mt_application_command));
}
const command_value& interaction_create_t::get_parameter(const std::string& name) const
{
/* Dummy STATIC return value for unknown options so we arent returning a value off the stack */
static command_value dummy_value = {};
const command_interaction& ci = std::get<command_interaction>(command.data);
for (auto i = ci.options.begin(); i != ci.options.end(); ++i) {
if (i->name == name) {
return i->value;
}
}
return dummy_value;
}
const command_value& button_click_t::get_parameter(const std::string& name) const
{
/* Buttons don't have parameters, so override this */
static command_value dummy_b_value = {};
return dummy_b_value;
}
const command_value& select_click_t::get_parameter(const std::string& name) const
{
/* Selects don't have parameters, so override this */
static command_value dummy_b_value = {};
return dummy_b_value;
}
/* Standard default constructors that call the parent constructor, for events */
event_ctor(guild_join_request_delete_t, event_dispatch_t);
event_ctor(stage_instance_create_t, event_dispatch_t);
event_ctor(stage_instance_delete_t, event_dispatch_t);
event_ctor(log_t, event_dispatch_t);
event_ctor(voice_state_update_t, event_dispatch_t);
event_ctor(interaction_create_t, event_dispatch_t);
event_ctor(button_click_t, interaction_create_t);
event_ctor(select_click_t, interaction_create_t);
event_ctor(guild_delete_t, event_dispatch_t);
event_ctor(channel_delete_t, event_dispatch_t);
event_ctor(channel_update_t, event_dispatch_t);
event_ctor(ready_t, event_dispatch_t);
event_ctor(message_delete_t, event_dispatch_t);
event_ctor(application_command_delete_t, event_dispatch_t);
event_ctor(application_command_create_t, event_dispatch_t);
event_ctor(resumed_t, event_dispatch_t);
event_ctor(guild_role_create_t, event_dispatch_t);
event_ctor(typing_start_t, event_dispatch_t);
event_ctor(message_reaction_add_t, event_dispatch_t);
event_ctor(message_reaction_remove_t, event_dispatch_t);
event_ctor(guild_create_t, event_dispatch_t);
event_ctor(channel_create_t, event_dispatch_t);
event_ctor(message_reaction_remove_emoji_t, event_dispatch_t);
event_ctor(message_delete_bulk_t, event_dispatch_t);
event_ctor(guild_role_update_t, event_dispatch_t);
event_ctor(guild_role_delete_t, event_dispatch_t);
event_ctor(channel_pins_update_t, event_dispatch_t);
event_ctor(message_reaction_remove_all_t, event_dispatch_t);
event_ctor(voice_server_update_t, event_dispatch_t);
event_ctor(guild_emojis_update_t, event_dispatch_t);
event_ctor(presence_update_t, event_dispatch_t);
event_ctor(webhooks_update_t, event_dispatch_t);
event_ctor(guild_member_add_t, event_dispatch_t);
event_ctor(invite_delete_t, event_dispatch_t);
event_ctor(guild_update_t, event_dispatch_t);
event_ctor(guild_integrations_update_t, event_dispatch_t);
event_ctor(guild_member_update_t, event_dispatch_t);
event_ctor(application_command_update_t, event_dispatch_t);
event_ctor(invite_create_t, event_dispatch_t);
event_ctor(message_update_t, event_dispatch_t);
event_ctor(user_update_t, event_dispatch_t);
event_ctor(message_create_t, event_dispatch_t);
event_ctor(guild_ban_add_t, event_dispatch_t);
event_ctor(guild_ban_remove_t, event_dispatch_t);
event_ctor(integration_create_t, event_dispatch_t);
event_ctor(integration_update_t, event_dispatch_t);
event_ctor(integration_delete_t, event_dispatch_t);
event_ctor(guild_member_remove_t, event_dispatch_t);
event_ctor(guild_members_chunk_t, event_dispatch_t);
event_ctor(thread_create_t, event_dispatch_t);
event_ctor(thread_update_t, event_dispatch_t);
event_ctor(thread_delete_t, event_dispatch_t);
event_ctor(thread_list_sync_t, event_dispatch_t);
event_ctor(thread_member_update_t, event_dispatch_t);
event_ctor(thread_members_update_t, event_dispatch_t);
event_ctor(voice_buffer_send_t, event_dispatch_t);
event_ctor(voice_user_talking_t, event_dispatch_t);
event_ctor(voice_ready_t, event_dispatch_t);
event_ctor(voice_receive_t, event_dispatch_t);
event_ctor(voice_track_marker_t, event_dispatch_t);
event_ctor(guild_stickers_update_t, event_dispatch_t);
};

64
vendor/DPP/src/dpp/dtemplate.cpp vendored Normal file
View File

@ -0,0 +1,64 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/dtemplate.h>
#include <dpp/discordevents.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
dtemplate::dtemplate() : code(""), name(""), description(""), usage_count(0), creator_id(0), source_guild_id(0)
{
}
dtemplate::~dtemplate() {
}
dtemplate& dtemplate::fill_from_json(nlohmann::json* j) {
code = StringNotNull(j, "code");
name = StringNotNull(j, "name");
description = StringNotNull(j, "description");
usage_count = Int32NotNull(j, "usage_count");
creator_id = SnowflakeNotNull(j, "creator_id");
created_at = TimestampNotNull(j, "created_at");
updated_at = TimestampNotNull(j, "updated_at");
source_guild_id = SnowflakeNotNull(j, "source_guild_id");
is_dirty = BoolNotNull(j, "is_dirty");
return *this;
}
std::string dtemplate::build_json() const {
json j({
{"code", code},
{"name", name},
{"description", description},
{"usage_count", usage_count},
{"creator_id", creator_id},
{"updated_at", updated_at},
{"source_guild_id", source_guild_id,
"is_dirty", is_dirty}
});
return j.dump();
}
};

116
vendor/DPP/src/dpp/emoji.cpp vendored Normal file
View File

@ -0,0 +1,116 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/emoji.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/dispatcher.h>
namespace dpp {
using json = nlohmann::json;
emoji::emoji() : managed(), user_id(0), flags(0), image_data(nullptr)
{
}
emoji::emoji(const std::string n, const snowflake i, const uint8_t f)
: managed(i), user_id(0), flags(f), image_data(nullptr), name(n)
{
}
emoji::~emoji() {
if (image_data) {
delete image_data;
}
}
emoji& emoji::fill_from_json(nlohmann::json* j) {
id = SnowflakeNotNull(j, "id");
name = StringNotNull(j, "name");
if (j->find("user") != j->end()) {
json & user = (*j)["user"];
user_id = SnowflakeNotNull(&user, "id");
}
if (BoolNotNull(j, "require_colons"))
flags |= e_require_colons;
if (BoolNotNull(j, "managed"))
flags |= e_managed;
if (BoolNotNull(j, "animated"))
flags |= e_animated;
if (BoolNotNull(j, "available"))
flags |= e_available;
return *this;
}
std::string emoji::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(id);
}
j["name"] = name;
if (image_data) {
j["image"] = *image_data;
}
return j.dump();
}
bool emoji::requires_colons() const {
return flags & e_require_colons;
}
bool emoji::is_managed() const {
return flags & e_managed;
}
bool emoji::is_animated() const {
return flags & e_animated;
}
bool emoji::is_available() const {
return flags & e_available;
}
emoji& emoji::load_image(const std::string &image_blob, image_type type) {
static std::map<image_type, std::string> mimetypes = {
{ i_gif, "image/gif" },
{ i_jpg, "image/jpeg" },
{ i_png, "image/png" }
};
if (image_blob.size() > MAX_EMOJI_SIZE) {
throw dpp::exception("Emoji file exceeds discord limit of 256 kilobytes");
}
if (image_data) {
/* If there's already image data defined, free the old data, to prevent a memory leak */
delete image_data;
}
image_data = new std::string("data:" + mimetypes[type] + ";base64," + base64_encode((unsigned char const*)image_blob.data(), image_blob.length()));
return *this;
}
std::string emoji::format() const
{
return id ? (name + ":" + std::to_string(id)) : name;
}
};

View File

@ -0,0 +1,48 @@
#include <dpp/discord.h>
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void application_command_create::handle(discord_client* client, json &j, const std::string &raw) {
}
}};

View File

@ -0,0 +1,48 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void application_command_delete::handle(discord_client* client, json &j, const std::string &raw) {
}
}};

View File

@ -0,0 +1,48 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void application_command_update::handle(discord_client* client, json &j, const std::string &raw) {
}
}};

View File

@ -0,0 +1,75 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void channel_create::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::channel* c = dpp::find_channel(SnowflakeNotNull(&d, "id"));
if (!c) {
c = new dpp::channel();
}
c->fill_from_json(&d);
dpp::get_channel_cache()->store(c);
if (c->recipients.size()) {
for (auto & u : c->recipients) {
client->log(dpp::ll_debug, fmt::format("Got a DM channel {} for user {}", c->id, u));
client->creator->set_dm_channel(u, c->id);
}
}
dpp::guild* g = dpp::find_guild(c->guild_id);
if (g) {
g->channels.push_back(c->id);
if (client->creator->dispatch.channel_create) {
dpp::channel_create_t cc(client, raw);
cc.created = c;
cc.creating_guild = g;
client->creator->dispatch.channel_create(cc);
}
}
}
}};

View File

@ -0,0 +1,69 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void channel_delete::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::channel* c = dpp::find_channel(SnowflakeNotNull(&d, "id"));
if (c) {
dpp::guild* g = dpp::find_guild(c->guild_id);
if (g) {
auto gc = std::find(g->channels.begin(), g->channels.end(), c->id);
if (gc != g->channels.end()) {
g->channels.erase(gc);
}
if (client->creator->dispatch.channel_delete) {
dpp::channel_delete_t cd(client, raw);
cd.deleted = c;
cd.deleting_guild = g;
client->creator->dispatch.channel_delete(cd);
}
}
dpp::get_channel_cache()->remove(c);
}
}
}};

View File

@ -0,0 +1,61 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void channel_pins_update::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.channel_pins_update) {
json& d = j["d"];
dpp::channel_pins_update_t cpu(client, raw);
cpu.pin_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
cpu.pin_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
cpu.timestamp = TimestampNotNull(&d, "last_pin_timestamp");
client->creator->dispatch.channel_pins_update(cpu);
}
}
}};

View File

@ -0,0 +1,59 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void channel_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::channel* c = dpp::find_channel(from_string<uint64_t>(d["id"].get<std::string>(), std::dec));
if (c) {
c->fill_from_json(&d);
if (client->creator->dispatch.channel_update) {
dpp::channel_update_t cu(client, raw);
cu.updated = c;
cu.updating_guild = dpp::find_guild(c->guild_id);
client->creator->dispatch.channel_update(cu);
}
}
}
}};

View File

@ -0,0 +1,57 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_ban_add::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.guild_ban_add) {
json &d = j["d"];
dpp::guild_ban_add_t gba(client, raw);
gba.banning_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
gba.banned = dpp::user().fill_from_json(&(d["user"]));
client->creator->dispatch.guild_ban_add(gba);
}
}
}};

View File

@ -0,0 +1,57 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_ban_remove::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.guild_ban_remove) {
json &d = j["d"];
dpp::guild_ban_remove_t gbr(client, raw);
gbr.unbanning_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
gbr.unbanned = dpp::user().fill_from_json(&(d["user"]));
client->creator->dispatch.guild_ban_remove(gbr);
}
}
}};

View File

@ -0,0 +1,149 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_create::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
bool newguild = false;
if (SnowflakeNotNull(&d, "id") == 0)
return;
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "id"));
if (!g) {
g = new dpp::guild();
newguild = true;
}
g->fill_from_json(client, &d);
g->shard_id = client->shard_id;
if (!g->is_unavailable() && newguild) {
if (client->creator->cache_policy.role_policy != dpp::cp_none) {
/* Store guild roles */
g->roles.clear();
g->roles.reserve(d["roles"].size());
for (auto & role : d["roles"]) {
dpp::role *r = dpp::find_role(SnowflakeNotNull(&role, "id"));
if (!r) {
r = new dpp::role();
}
r->fill_from_json(g->id, &role);
dpp::get_role_cache()->store(r);
g->roles.push_back(r->id);
}
}
/* Store guild channels */
g->channels.clear();
g->channels.reserve(d["channels"].size());
for (auto & channel : d["channels"]) {
dpp::channel* c = dpp::find_channel(SnowflakeNotNull(&channel, "id"));
if (!c) {
c = new dpp::channel();
}
c->fill_from_json(&channel);
c->guild_id = g->id;
dpp::get_channel_cache()->store(c);
g->channels.push_back(c->id);
}
/* Store guild threads */
g->threads.clear();
g->threads.reserve(d["threads"].size());
for (auto & channel : d["threads"]) {
g->threads.push_back(SnowflakeNotNull(&channel, "id"));
}
/* Store guild members */
if (client->creator->cache_policy.user_policy == cp_aggressive) {
g->members.reserve(d["members"].size());
for (auto & user : d["members"]) {
snowflake userid = SnowflakeNotNull(&(user["user"]), "id");
/* Only store ones we don't have already otherwise gm will leak */
if (g->members.find(userid) == g->members.end()) {
dpp::user* u = dpp::find_user(userid);
if (!u) {
u = new dpp::user();
u->fill_from_json(&(user["user"]));
dpp::get_user_cache()->store(u);
} else {
u->refcount++;
}
dpp::guild_member gm;
gm.fill_from_json(&(user["user"]), g->id, userid);
g->members[userid] = gm;
}
}
}
if (client->creator->cache_policy.emoji_policy != dpp::cp_none) {
/* Store emojis */
g->emojis.reserve(d["emojis"].size());
g->emojis = {};
for (auto & emoji : d["emojis"]) {
dpp::emoji* e = dpp::find_emoji(SnowflakeNotNull(&emoji, "id"));
if (!e) {
e = new dpp::emoji();
e->fill_from_json(&emoji);
dpp::get_emoji_cache()->store(e);
}
g->emojis.push_back(e->id);
}
}
}
dpp::get_guild_cache()->store(g);
if (newguild && g->id && (client->intents & dpp::i_guild_members)) {
if (client->creator->cache_policy.user_policy == cp_aggressive) {
json chunk_req = json({{"op", 8}, {"d", {{"guild_id",std::to_string(g->id)},{"query",""},{"limit",0}}}});
if (client->intents & dpp::i_guild_presences) {
chunk_req["d"]["presences"] = true;
}
client->QueueMessage(chunk_req.dump());
}
}
if (client->creator->dispatch.guild_create) {
dpp::guild_create_t gc(client, raw);
gc.created = g;
client->creator->dispatch.guild_create(gc);
}
}
}};

View File

@ -0,0 +1,98 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_delete::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "id"));
if (g) {
if (!BoolNotNull(&d, "unavailable")) {
dpp::get_guild_cache()->remove(g);
if (client->creator->cache_policy.emoji_policy != dpp::cp_none) {
for (auto & ee : g->emojis) {
dpp::emoji* fe = dpp::find_emoji(ee);
if (fe) {
dpp::get_emoji_cache()->remove(fe);
}
}
}
if (client->creator->cache_policy.role_policy != dpp::cp_none) {
for (auto & rr : g->roles) {
dpp::role* role = dpp::find_role(rr);
if (role) {
dpp::get_role_cache()->remove(role);
}
}
}
for (auto & cc : g->channels) {
dpp::channel* ch = dpp::find_channel(cc);
if (ch) {
dpp::get_channel_cache()->remove(ch);
}
}
if (client->creator->cache_policy.user_policy != dpp::cp_none) {
for (auto gm = g->members.begin(); gm != g->members.end(); ++gm) {
dpp::user* u = dpp::find_user(gm->second.user_id);
if (u) {
u->refcount--;
if (u->refcount < 1) {
dpp::get_user_cache()->remove(u);
}
}
}
}
g->members.clear();
} else {
g->flags |= dpp::g_unavailable;
}
if (client->creator->dispatch.guild_delete) {
dpp::guild_delete_t gd(client, raw);
gd.deleted = g;
client->creator->dispatch.guild_delete(gd);
}
}
}
}};

View File

@ -0,0 +1,78 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_emojis_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->cache_policy.emoji_policy != dpp::cp_none) {
for (auto & ee : g->emojis) {
dpp::emoji* fe = dpp::find_emoji(ee);
if (fe) {
dpp::get_emoji_cache()->remove(fe);
}
}
g->emojis.clear();
for (auto & emoji : d["emojis"]) {
dpp::emoji* e = dpp::find_emoji(SnowflakeNotNull(&emoji, "id"));
if (!e) {
e = new dpp::emoji();
e->fill_from_json(&emoji);
dpp::get_emoji_cache()->store(e);
}
g->emojis.push_back(e->id);
}
}
if (client->creator->dispatch.guild_emojis_update) {
dpp::guild_emojis_update_t geu(client, raw);
geu.emojis = g->emojis;
geu.updating_guild = g;
client->creator->dispatch.guild_emojis_update(geu);
}
}
}
}};

View File

@ -0,0 +1,55 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_integrations_update::handle(class discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.guild_integrations_update) {
json& d = j["d"];
dpp::guild_integrations_update_t giu(client, raw);
giu.updating_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
client->creator->dispatch.guild_integrations_update(giu);
}
}
}};

View File

@ -0,0 +1,56 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_join_request_delete::handle(class discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.guild_join_request_delete) {
json& d = j["d"];
dpp::guild_join_request_delete_t grd(client, raw);
grd.user_id = SnowflakeNotNull(&d, "user_id");
grd.guild_id = SnowflakeNotNull(&d, "guild_id");
client->creator->dispatch.guild_join_request_delete(grd);
}
}
}};

View File

@ -0,0 +1,86 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_member_add::handle(discord_client* client, json &j, const std::string &raw) {
json d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
dpp::guild_member_add_t gmr(client, raw);
if (g) {
if (client->creator->cache_policy.user_policy == dpp::cp_none) {
dpp::guild_member gm;
gm.fill_from_json(&d, g->id, SnowflakeNotNull(&(d["user"]), "id"));
gmr.added = gm;
if (client->creator->dispatch.guild_member_add) {
gmr.adding_guild = g;
client->creator->dispatch.guild_member_add(gmr);
}
} else {
dpp::user* u = dpp::find_user(SnowflakeNotNull(&(d["user"]), "id"));
if (!u) {
u = new dpp::user();
u->fill_from_json(&(d["user"]));
dpp::get_user_cache()->store(u);
} else {
u->refcount++;
}
dpp::guild_member gm;
gmr.added = {};
if (u && u->id && g->members.find(u->id) == g->members.end()) {
gm.fill_from_json(&d, g->id, u->id);
g->members[u->id] = gm;
gmr.added = gm;
} else if (u && u->id) {
gmr.added = g->members.find(u->id)->second;
}
if (client->creator->dispatch.guild_member_add) {
gmr.adding_guild = g;
client->creator->dispatch.guild_member_add(gmr);
}
}
}
}
}};

View File

@ -0,0 +1,83 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_member_remove::handle(discord_client* client, json &j, const std::string &raw) {
json d = j["d"];
dpp::guild_member_remove_t gmr(client, raw);
gmr.removing_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (client->creator->cache_policy.user_policy == dpp::cp_none) {
dpp::user u;
u.fill_from_json(&(d["user"]));
gmr.removed = &u;
if (client->creator->dispatch.guild_member_remove)
client->creator->dispatch.guild_member_remove(gmr);
} else {
gmr.removed = dpp::find_user(SnowflakeNotNull(&(d["user"]), "id"));
if (client->creator->dispatch.guild_member_remove)
client->creator->dispatch.guild_member_remove(gmr);
if (gmr.removing_guild && gmr.removed) {
auto i = gmr.removing_guild->members.find(gmr.removed->id);
if (i != gmr.removing_guild->members.end()) {
dpp::user* u = dpp::find_user(gmr.removed->id);
if (u) {
u->refcount--;
if (u->refcount < 1) {
dpp::get_user_cache()->remove(u);
}
}
gmr.removing_guild->members.erase(i);
}
}
}
}
}};

View File

@ -0,0 +1,78 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_member_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(from_string<uint64_t>(d["guild_id"].get<std::string>(), std::dec));
if (client->creator->cache_policy.user_policy == dpp::cp_none) {
dpp::user u;
u.fill_from_json(&(d["user"]));
if (g && client->creator->dispatch.guild_member_update) {
dpp::guild_member_update_t gmu(client, raw);
gmu.updating_guild = g;
guild_member m;
auto& user = d;//d["user"]; // d contains roles and other member stuff already
m.fill_from_json(&user, g->id, u.id);
gmu.updated = m;
client->creator->dispatch.guild_member_update(gmu);
}
} else {
dpp::user* u = dpp::find_user(from_string<uint64_t>(d["user"]["id"].get<std::string>(), std::dec));
if (g && u) {
auto& user = d;//d["user"]; // d contains roles and other member stuff already
guild_member m;
m.fill_from_json(&user, g->id, u->id);
g->members[u->id] = m;
if (client->creator->dispatch.guild_member_update) {
dpp::guild_member_update_t gmu(client, raw);
gmu.updating_guild = g;
gmu.updated = m;
client->creator->dispatch.guild_member_update(gmu);
}
}
}
}
}};

View File

@ -0,0 +1,79 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_members_chunk::handle(discord_client* client, json &j, const std::string &raw) {
json &d = j["d"];
dpp::guild_member_map um;
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
/* Store guild members */
if (client->creator->cache_policy.user_policy == cp_aggressive) {
for (auto & userrec : d["members"]) {
json & userspart = userrec["user"];
dpp::user* u = dpp::find_user(SnowflakeNotNull(&userspart, "id"));
if (!u) {
u = new dpp::user();
u->fill_from_json(&userspart);
dpp::get_user_cache()->store(u);
}
if (g->members.find(u->id) == g->members.end()) {
dpp::guild_member gm;
gm.fill_from_json(&userrec, g->id, u->id);
g->members[u->id] = gm;
if (client->creator->dispatch.guild_members_chunk)
um[u->id] = gm;
}
}
}
if (client->creator->dispatch.guild_members_chunk) {
dpp::guild_members_chunk_t gmc(client, raw);
gmc.adding = g;
gmc.members = &um;
client->creator->dispatch.guild_members_chunk(gmc);
}
}
}
}};

View File

@ -0,0 +1,79 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_role_create::handle(discord_client* client, json &j, const std::string &raw) {
json &d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->cache_policy.role_policy == dpp::cp_none) {
json &role = d["role"];
dpp::role r;
r.fill_from_json(g->id, &role);
if (client->creator->dispatch.guild_role_create) {
dpp::guild_role_create_t grc(client, raw);
grc.creating_guild = g;
grc.created = &r;
client->creator->dispatch.guild_role_create(grc);
}
} else {
json &role = d["role"];
dpp::role *r = dpp::find_role(SnowflakeNotNull(&role, "id"));
if (!r) {
r = new dpp::role();
}
r->fill_from_json(g->id, &role);
dpp::get_role_cache()->store(r);
g->roles.push_back(r->id);
if (client->creator->dispatch.guild_role_create) {
dpp::guild_role_create_t grc(client, raw);
grc.creating_guild = g;
grc.created = r;
client->creator->dispatch.guild_role_create(grc);
}
}
}
}
}};

View File

@ -0,0 +1,80 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_role_delete::handle(discord_client* client, json &j, const std::string &raw) {
json &d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->cache_policy.role_policy == dpp::cp_none) {
json& role = d["role"];
dpp::role r;
r.fill_from_json(g->id, &d);
if (client->creator->dispatch.guild_role_delete) {
dpp::guild_role_delete_t grd(client, raw);
grd.deleting_guild = g;
grd.deleted = &r;
client->creator->dispatch.guild_role_delete(grd);
}
} else {
json& role = d["role"];
dpp::role *r = dpp::find_role(SnowflakeNotNull(&role, "id"));
if (r) {
if (client->creator->dispatch.guild_role_delete) {
dpp::guild_role_delete_t grd(client, raw);
grd.deleting_guild = g;
grd.deleted = r;
client->creator->dispatch.guild_role_delete(grd);
}
auto i = std::find(g->roles.begin(), g->roles.end(), r->id);
if (i != g->roles.end()) {
g->roles.erase(i);
}
dpp::get_role_cache()->remove(r);
}
}
}
}
}};

View File

@ -0,0 +1,76 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_role_update::handle(discord_client* client, json &j, const std::string &raw) {
json &d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->cache_policy.role_policy == dpp::cp_none) {
json& role = d["role"];
dpp::role r;
r.fill_from_json(g->id, &d);
if (client->creator->dispatch.guild_role_update) {
dpp::guild_role_update_t gru(client, raw);
gru.updating_guild = g;
gru.updated = &r;
client->creator->dispatch.guild_role_update(gru);
}
} else {
json& role = d["role"];
dpp::role *r = dpp::find_role(SnowflakeNotNull(&role, "id"));
if (r) {
r->fill_from_json(g->id, &role);
if (client->creator->dispatch.guild_role_update) {
dpp::guild_role_update_t gru(client, raw);
gru.updating_guild = g;
gru.updated = r;
client->creator->dispatch.guild_role_update(gru);
}
}
}
}
}
}};

View File

@ -0,0 +1,64 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_stickers_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->dispatch.stickers_update) {
dpp::guild_stickers_update_t gsu(client, raw);
for (auto & sticker : d["stickers"]) {
dpp::sticker s;
s.fill_from_json(&sticker);
gsu.stickers.push_back(s);
}
gsu.updating_guild = g;
client->creator->dispatch.stickers_update(gsu);
}
}
}
}};

View File

@ -0,0 +1,74 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void guild_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(from_string<uint64_t>(d["id"].get<std::string>(), std::dec));
if (g) {
g->fill_from_json(client, &d);
if (!g->is_unavailable()) {
if (client->creator->cache_policy.role_policy != dpp::cp_none && d.find("roles") != d.end()) {
for (int rc = 0; rc < g->roles.size(); ++rc) {
dpp::role* oldrole = dpp::find_role(g->roles[rc]);
dpp::get_role_cache()->remove(oldrole);
}
g->roles.clear();
for (auto & role : d["roles"]) {
dpp::role *r = new dpp::role();
r->fill_from_json(g->id, &role);
dpp::get_role_cache()->store(r);
g->roles.push_back(r->id);
}
}
}
if (client->creator->dispatch.guild_update) {
dpp::guild_update_t gu(client, raw);
gu.updated = g;
client->creator->dispatch.guild_update(gu);
}
}
}
}};

View File

@ -0,0 +1,56 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void integration_create::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.integration_create) {
json& d = j["d"];
dpp::integration_create_t ic(client, raw);
ic.created_integration = dpp::integration().fill_from_json(&d);
client->creator->dispatch.integration_create(ic);
}
}
}};

View File

@ -0,0 +1,55 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void integration_delete::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.integration_delete) {
json& d = j["d"];
dpp::integration_delete_t id(client, raw);
id.deleted_integration = dpp::integration().fill_from_json(&d);
client->creator->dispatch.integration_delete(id);
}
}
}};

View File

@ -0,0 +1,55 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void integration_update::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.integration_update) {
json& d = j["d"];
dpp::integration_update_t iu(client, raw);
iu.updated_integration = dpp::integration().fill_from_json(&d);
client->creator->dispatch.integration_update(iu);
}
}
}};

View File

@ -0,0 +1,83 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void interaction_create::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::interaction i;
i.fill_from_json(&d);
/* There are two types of interactions, component interactions and
* slash command interactions. Both fire different library events
* so ensure they are dispatched properly.
*/
if (i.type == it_application_command) {
if (client->creator->dispatch.interaction_create) {
dpp::interaction_create_t ic(client, raw);
ic.command = i;
client->creator->dispatch.interaction_create(ic);
}
} else if (i.type == it_component_button) {
dpp::component_interaction bi = std::get<component_interaction>(i.data);
if (bi.component_type == cotype_button) {
if (client->creator->dispatch.button_click) {
dpp::button_click_t ic(client, raw);
ic.command = i;
ic.custom_id = bi.custom_id;
ic.component_type = bi.component_type;
client->creator->dispatch.button_click(ic);
}
}
if (bi.component_type == cotype_select) {
if (client->creator->dispatch.select_click) {
dpp::select_click_t ic(client, raw);
ic.command = i;
ic.custom_id = bi.custom_id;
ic.component_type = bi.component_type;
ic.values = bi.values;
client->creator->dispatch.select_click(ic);
}
}
}
}
}};

View File

@ -0,0 +1,54 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void invite_create::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.invite_create) {
json& d = j["d"];
dpp::invite_create_t ci(client, raw);
ci.created_invite = dpp::invite().fill_from_json(&d);
client->creator->dispatch.invite_create(ci);
}
}
}};

View File

@ -0,0 +1,54 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void invite_delete::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.invite_delete) {
json& d = j["d"];
dpp::invite_delete_t cd(client, raw);
cd.deleted_invite = dpp::invite().fill_from_json(&d);
client->creator->dispatch.invite_delete(cd);
}
}
}};

54
vendor/DPP/src/dpp/events/logger.cpp vendored Normal file
View File

@ -0,0 +1,54 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void logger::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.log) {
dpp::log_t logmsg(client, raw);
logmsg.severity = (dpp::loglevel)from_string<uint32_t>(raw.substr(0, raw.find(';')), std::dec);
logmsg.message = raw.substr(raw.find(';') + 1, raw.length());
client->creator->dispatch.log(logmsg);
}
}
}};

View File

@ -0,0 +1,59 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/dispatcher.h>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_create::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_create) {
json d = j["d"];
dpp::message_create_t msg(client, raw);
dpp::message m;
m.fill_from_json(&d, client->creator->cache_policy);
msg.msg = &m;
client->creator->dispatch.message_create(msg);
}
}
}};

View File

@ -0,0 +1,57 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_delete::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_delete) {
json d = j["d"];
dpp::message_delete_t msg(client, raw);
dpp::message m;
m.fill_from_json(&d);
msg.deleted = &m;
client->creator->dispatch.message_delete(msg);
}
}
}};

View File

@ -0,0 +1,61 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_delete_bulk::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_delete_bulk) {
json& d = j["d"];
dpp::message_delete_bulk_t msg(client, raw);
msg.deleting_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
msg.deleting_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
msg.deleting_user = dpp::find_user(SnowflakeNotNull(&d, "user_id"));
for (auto& m : d["ids"]) {
msg.deleted.push_back(from_string<uint64_t>(m.get<std::string>(), std::dec));
}
client->creator->dispatch.message_delete_bulk(msg);
}
}
}};

View File

@ -0,0 +1,61 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_reaction_add::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_reaction_add) {
json &d = j["d"];
dpp::message_reaction_add_t mra(client, raw);
mra.reacting_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
mra.reacting_user = dpp::find_user(SnowflakeNotNull(&d, "user_id"));
mra.reacting_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
mra.message_id = SnowflakeNotNull(&d, "message_id");
mra.reacting_emoji = dpp::find_emoji(SnowflakeNotNull(&(d["emoji"]), "id"));
if (mra.reacting_user && mra.reacting_channel && mra.message_id) {
client->creator->dispatch.message_reaction_add(mra);
}
}
}
}};

View File

@ -0,0 +1,61 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_reaction_remove::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_reaction_remove) {
json &d = j["d"];
dpp::message_reaction_remove_t mrr(client, raw);
mrr.reacting_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
mrr.reacting_user = dpp::find_user(SnowflakeNotNull(&d, "user_id"));
mrr.reacting_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
mrr.message_id = SnowflakeNotNull(&d, "message_id");
mrr.reacting_emoji = dpp::find_emoji(SnowflakeNotNull(&(d["emoji"]), "id"));
if (mrr.reacting_user && mrr.reacting_channel && mrr.message_id) {
client->creator->dispatch.message_reaction_remove(mrr);
}
}
}
}};

View File

@ -0,0 +1,59 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_reaction_remove_all::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_reaction_remove_all) {
json &d = j["d"];
dpp::message_reaction_remove_all_t mrra(client, raw);
mrra.reacting_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
mrra.reacting_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
mrra.message_id = SnowflakeNotNull(&d, "message_id");
if (mrra.reacting_channel && mrra.message_id) {
client->creator->dispatch.message_reaction_remove_all(mrra);
}
}
}
}};

View File

@ -0,0 +1,61 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_reaction_remove_emoji::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_reaction_remove_emoji) {
json &d = j["d"];
dpp::message_reaction_remove_emoji_t mrre(client, raw);
mrre.reacting_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
mrre.reacting_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
mrre.message_id = SnowflakeNotNull(&d, "message_id");
mrre.reacting_emoji = dpp::find_emoji(SnowflakeNotNull(&(d["emoji"]), "id"));
if (mrre.reacting_channel && mrre.message_id) {
client->creator->dispatch.message_reaction_remove_emoji(mrre);
}
}
}
}};

View File

@ -0,0 +1,57 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void message_update::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.message_update) {
json d = j["d"];
dpp::message_update_t msg(client, raw);
dpp::message m;
m.fill_from_json(&d);
msg.updated = &m;
client->creator->dispatch.message_update(msg);
}
}
}};

View File

@ -0,0 +1,55 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void presence_update::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.presence_update) {
json& d = j["d"];
dpp::presence_update_t pu(client, raw);
pu.rich_presence = dpp::presence().fill_from_json(&d);
client->creator->dispatch.presence_update(pu);
}
}
}};

68
vendor/DPP/src/dpp/events/ready.cpp vendored Normal file
View File

@ -0,0 +1,68 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
std::mutex protect_the_loot;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void ready::handle(discord_client* client, json &j, const std::string &raw) {
client->log(dpp::ll_info, fmt::format("Shard {}/{} ready!", client->shard_id, client->max_shards));
client->sessionid = j["d"]["session_id"];
client->ready = true;
/* Mutex this to make sure multiple threads don't change it at the same time */
{
std::lock_guard<std::mutex> lockit(protect_the_loot);
client->creator->me.fill_from_json(&(j["d"]["user"]));
}
if (client->creator->dispatch.ready) {
dpp::ready_t r(client, raw);
r.session_id = client->sessionid;
r.shard_id = client->shard_id;
client->creator->dispatch.ready(r);
}
}
}};

59
vendor/DPP/src/dpp/events/resumed.cpp vendored Normal file
View File

@ -0,0 +1,59 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void resumed::handle(discord_client* client, json &j, const std::string &raw) {
client->log(dpp::ll_debug, fmt::format("Successfully resumed session id {}", client->sessionid));
client->ready = true;
if (client->creator->dispatch.resumed) {
dpp::resumed_t r(client, raw);
r.session_id = client->sessionid;
r.shard_id = client->shard_id;
client->creator->dispatch.resumed(r);
}
}
}};

View File

@ -0,0 +1,59 @@
#include <dpp/discord.h>
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void stage_instance_create::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.stage_instance_create) {
json& d = j["d"];
dpp::stage_instance_create_t sic(client, raw);
sic.id = SnowflakeNotNull(&d, "id");
sic.channel_id = SnowflakeNotNull(&d, "channel_id");
sic.guild_id = SnowflakeNotNull(&d, "channel_id");
sic.privacy_level = dpp::Int8NotNull(&d, "privacy_level");
sic.topic = StringNotNull(&d, "topic");
client->creator->dispatch.stage_instance_create(sic);
}
}
}};

View File

@ -0,0 +1,59 @@
#include <dpp/discord.h>
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void stage_instance_delete::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.stage_instance_delete) {
json& d = j["d"];
dpp::stage_instance_delete_t sid(client, raw);
sid.id = SnowflakeNotNull(&d, "id");
sid.channel_id = SnowflakeNotNull(&d, "channel_id");
sid.guild_id = SnowflakeNotNull(&d, "channel_id");
sid.privacy_level = dpp::Int8NotNull(&d, "privacy_level");
sid.topic = StringNotNull(&d, "topic");
client->creator->dispatch.stage_instance_delete(sid);
}
}
}};

View File

@ -0,0 +1,35 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_create::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
dpp::channel t;
t.fill_from_json(&d);
dpp::guild* g = dpp::find_guild(t.guild_id);
if (g) {
g->threads.push_back(t.id);
if (client->creator->dispatch.thread_create) {
dpp::thread_create_t tc(client, raw);
tc.created = t;
tc.creating_guild = g;
client->creator->dispatch.thread_create(tc);
}
}
}
}};

View File

@ -0,0 +1,38 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_delete::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
dpp::channel t;
t.fill_from_json(&d);
dpp::guild* g = dpp::find_guild(t.guild_id);
if (g) {
auto gt = std::find(g->threads.begin(), g->threads.end(), t.id);
if (gt != g->threads.end()) {
g->threads.erase(gt);
}
if (client->creator->dispatch.thread_delete) {
dpp::thread_delete_t tc(client, raw);
tc.deleted = t;
tc.deleting_guild = g;
client->creator->dispatch.thread_delete(tc);
}
}
}
}};

View File

@ -0,0 +1,46 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_list_sync::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
/** Store thread IDs*/
if (d.find("threads") != d.end()) {
for (auto& t : d["threads"]) {
g->threads.push_back(SnowflakeNotNull(&t, "id"));
}
}
if (client->creator->dispatch.thread_list_sync) {
dpp::thread_list_sync_t tls(client, raw);
if (d.find("threads") != d.end()) {
for (auto& t : d["threads"]) {
tls.threads.push_back(channel().fill_from_json(&t));
}
}
if (d.find("members") != d.end()) {
for (auto& tm : d["members"]) {
tls.members.push_back(thread_member().fill_from_json(&tm));
}
}
client->creator->dispatch.thread_list_sync(tls);
}
}
}
}};

View File

@ -0,0 +1,28 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_member_update::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
if (client->creator->dispatch.thread_member_update) {
dpp::thread_member_update_t tm(client, raw);
tm.updated = thread_member().fill_from_json(&j);
client->creator->dispatch.thread_member_update(tm);
}
}
}};

View File

@ -0,0 +1,47 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_members_update::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
dpp::guild* g = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
if (g) {
if (client->creator->dispatch.thread_members_update) {
dpp::thread_members_update_t tms(client, raw);
tms.updating_guild = g;
SetSnowflakeNotNull(&d, "id", tms.thread_id);
SetInt8NotNull(&d, "member_count", tms.member_count);
if (d.find("added_members") != d.end()) {
for (auto& tm : d["added_members"]) {
tms.added.push_back(thread_member().fill_from_json(&tm));
}
}
if (d.find("removed_member_ids") != d.end()) {
try {
for (auto& rm : d["removed_member_ids"]) {
tms.removed_ids.push_back(std::stoull(static_cast<std::string>(rm)));
}
} catch (const std::exception& e) {
client->creator->log(dpp::ll_error, fmt::format("thread_members_update: {}", e.what()));
}
}
client->creator->dispatch.thread_members_update(tms);
}
}
}
}};

View File

@ -0,0 +1,34 @@
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
void thread_update::handle(discord_client* client, json& j, const std::string& raw) {
json& d = j["d"];
dpp::channel t;
t.fill_from_json(&d);
dpp::guild* g = dpp::find_guild(t.guild_id);
if (g) {
if (client->creator->dispatch.thread_update) {
dpp::thread_update_t tc(client, raw);
tc.updated = t;
tc.updating_guild = g;
client->creator->dispatch.thread_update(tc);
}
}
}
}};

View File

@ -0,0 +1,58 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void typing_start::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.typing_start) {
json& d = j["d"];
dpp::typing_start_t ts(client, raw);
ts.typing_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
ts.typing_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
ts.typing_user = dpp::find_user(SnowflakeNotNull(&d, "user_id"));
ts.timestamp = TimestampNotNull(&d, "timestamp");
client->creator->dispatch.typing_start(ts);
}
}
}};

View File

@ -0,0 +1,74 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void user_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::snowflake user_id = SnowflakeNotNull(&d, "id");
if (user_id) {
if (client->creator->cache_policy.user_policy != dpp::cp_none) {
dpp::user* u = dpp::find_user(user_id);
if (u) {
u->fill_from_json(&d);
}
if (client->creator->dispatch.user_update) {
dpp::user_update_t uu(client, raw);
uu.updated = *u;
client->creator->dispatch.user_update(uu);
}
} else {
if (client->creator->dispatch.user_update) {
dpp::user u;
u.fill_from_json(&d);
dpp::user_update_t uu(client, raw);
uu.updated = u;
client->creator->dispatch.user_update(uu);
}
}
}
}
}};

View File

@ -0,0 +1,74 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void voice_server_update::handle(discord_client* client, json &j, const std::string &raw) {
json &d = j["d"];
dpp::voice_server_update_t vsu(client, raw);
vsu.guild_id = SnowflakeNotNull(&d, "guild_id");
vsu.token = StringNotNull(&d, "token");
vsu.endpoint = StringNotNull(&d, "endpoint");
{
std::lock_guard<std::mutex> lock(client->voice_mutex);
auto v = client->connecting_voice_channels.find(vsu.guild_id);
/* Check to see if there is a connection in progress for a voice channel on this guild */
if (v != client->connecting_voice_channels.end()) {
if (!v->second->is_ready()) {
v->second->token = vsu.token;
v->second->websocket_hostname = vsu.endpoint;
if (!v->second->is_active()) {
v->second->connect(vsu.guild_id);
}
}
}
}
if (client->creator->dispatch.voice_server_update) {
client->creator->dispatch.voice_server_update(vsu);
}
}
}};

View File

@ -0,0 +1,83 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void voice_state_update::handle(discord_client* client, json &j, const std::string &raw) {
json& d = j["d"];
dpp::voice_state_update_t vsu(client, raw);
vsu.state = dpp::voicestate().fill_from_json(&d);
vsu.state.shard = client;
/* Update guild voice states */
dpp::guild* g = dpp::find_guild(vsu.state.guild_id);
if (g) {
if (vsu.state.channel_id == 0) {
auto ve = g->voice_members.find(vsu.state.user_id);
if (ve != g->voice_members.end()) {
g->voice_members.erase(ve);
}
} else {
g->voice_members[vsu.state.user_id] = vsu.state;
}
}
if (vsu.state.user_id == client->creator->me.id)
{
std::lock_guard<std::mutex> lock(client->voice_mutex);
auto v = client->connecting_voice_channels.find(vsu.state.guild_id);
/* Check to see if we have a connection to a voice channel in progress on this guild */
if (v != client->connecting_voice_channels.end()) {
v->second->session_id = vsu.state.session_id;
if (v->second->is_ready() && !v->second->is_active()) {
v->second->connect(vsu.state.guild_id);
}
}
}
if (client->creator->dispatch.voice_state_update) {
client->creator->dispatch.voice_state_update(vsu);
}
}
}};

View File

@ -0,0 +1,56 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/event.h>
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/discordclient.h>
#include <dpp/discord.h>
#include <dpp/cache.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
using json = nlohmann::json;
namespace dpp { namespace events {
using namespace dpp;
/**
* @brief Handle event
*
* @param client Websocket client (current shard)
* @param j JSON data for the event
* @param raw Raw JSON string
*/
void webhooks_update::handle(discord_client* client, json &j, const std::string &raw) {
if (client->creator->dispatch.webhooks_update) {
json& d = j["d"];
dpp::webhooks_update_t wu(client, raw);
wu.webhook_guild = dpp::find_guild(SnowflakeNotNull(&d, "guild_id"));
wu.webhook_channel = dpp::find_channel(SnowflakeNotNull(&d, "channel_id"));
client->creator->dispatch.webhooks_update(wu);
}
}
}};

469
vendor/DPP/src/dpp/guild.cpp vendored Normal file
View File

@ -0,0 +1,469 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/discordclient.h>
#include <dpp/voicestate.h>
#include <dpp/cache.h>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
std::map<std::string, dpp::guild_flags> featuremap = {
{"INVITE_SPLASH", dpp::g_invite_splash },
{"VIP_REGIONS", dpp::g_vip_regions },
{"VANITY_URL", dpp::g_vanity_url },
{"VERIFIED", dpp::g_verified },
{"PARTNERED", dpp::g_partnered },
{"COMMUNITY", dpp::g_community },
{"COMMERCE", dpp::g_commerce },
{"NEWS", dpp::g_news },
{"DISCOVERABLE", dpp::g_discoverable },
{"FEATUREABLE", dpp::g_featureable },
{"ANIMATED_ICON", dpp::g_animated_icon },
{"BANNER", dpp::g_banner },
{"WELCOME_SCREEN_ENABLED", dpp::g_welcome_screen_enabled },
{"MEMBER_VERIFICATION_GATE_ENABLED", dpp::g_member_verification_gate },
{"PREVIEW_ENABLED", dpp::g_preview_enabled }
};
std::map<std::string, dpp::region> regionmap = {
{ "brazil", dpp::r_brazil },
{ "central-europe", dpp::r_central_europe },
{ "hong-kong", dpp::r_hong_kong },
{ "india", dpp::r_india },
{ "japan", dpp::r_japan },
{ "russia", dpp::r_russia },
{ "singapore", dpp::r_singapore },
{ "south-africa", dpp::r_south_africa },
{ "sydney", dpp::r_sydney },
{ "us-central", dpp::r_us_central },
{ "us-east", dpp::r_us_east },
{ "us-south", dpp::r_us_south },
{ "us-west", dpp::r_us_west },
{ "western-europe", dpp::r_western_europe }
};
namespace dpp {
guild::guild() :
managed(),
shard_id(0),
flags(0),
owner_id(0),
voice_region(r_us_central),
afk_channel_id(0),
afk_timeout(0),
widget_channel_id(0),
verification_level(0),
default_message_notifications(0),
explicit_content_filter(0),
mfa_level(0),
application_id(0),
system_channel_id(0),
rules_channel_id(0),
member_count(0),
premium_tier(0),
premium_subscription_count(0),
public_updates_channel_id(0),
max_video_channel_users(0)
{
}
guild_member::guild_member() :
joined_at(0),
premium_since(0),
flags(0)
{
}
guild_member& guild_member::fill_from_json(nlohmann::json* j, snowflake g_id, snowflake u_id) {
this->guild_id = g_id;
this->user_id = u_id;
j->get_to(*this);
return *this;
}
void from_json(const nlohmann::json& j, guild_member& gm) {
gm.nickname = StringNotNull(&j, "nick");
gm.joined_at = TimestampNotNull(&j, "joined_at");
gm.premium_since = TimestampNotNull(&j, "premium_since");
gm.roles.clear();
if (j.contains("roles") && !j.at("roles").is_null()) {
for (auto& role : j.at("roles")) {
gm.roles.push_back(std::stoull(role.get<std::string>()));
}
}
gm.flags |= BoolNotNull(&j, "deaf") ? gm_deaf : 0;
gm.flags |= BoolNotNull(&j, "mute") ? gm_mute : 0;
gm.flags |= BoolNotNull(&j, "pending") ? gm_pending : 0;
}
std::string guild_member::build_json() const {
json j;
if (!this->nickname.empty())
j["nick"] = this->nickname;
if (this->roles.size()) {
j["roles"] = {};
for (auto & role : roles) {
j["roles"].push_back(std::to_string(role));
}
}
if (is_muted()) {
j["muted"] = true;
}
if (is_deaf()) {
j["deaf"] = true;
}
return j.dump();
}
bool guild_member::is_deaf() const {
return flags & dpp::gm_deaf;
}
bool guild_member::is_muted() const {
return flags & dpp::gm_mute;
}
bool guild_member::is_pending() const {
return flags & dpp::gm_pending;
}
bool guild::is_large() const {
return this->flags & g_large;
}
bool guild::is_unavailable() const {
return this->flags & g_unavailable;
}
bool guild::widget_enabled() const {
return this->flags & g_widget_enabled;
}
bool guild::has_invite_splash() const {
return this->flags & g_invite_splash;
}
bool guild::has_vip_regions() const {
return this->flags & g_vip_regions;
}
bool guild::has_vanity_url() const {
return this->flags & g_vanity_url;
}
bool guild::is_verified() const {
return this->flags & g_verified;
}
bool guild::is_partnered() const {
return this->flags & g_partnered;
}
bool guild::is_community() const {
return this->flags & g_community;
}
bool guild::has_commerce() const {
return this->flags & g_commerce;
}
bool guild::has_news() const {
return this->flags & g_news;
}
bool guild::is_discoverable() const {
return this->flags & g_discoverable;
}
bool guild::is_featureable() const {
return this->flags & g_featureable;
}
bool guild::has_animated_icon() const {
return this->flags & g_animated_icon;
}
bool guild::has_banner() const {
return this->flags & g_banner;
}
bool guild::is_welcome_screen_enabled() const {
return this->flags & g_welcome_screen_enabled;
}
bool guild::has_member_verification_gate() const {
return this->flags & g_member_verification_gate;
}
bool guild::is_preview_enabled() const {
return this->flags & g_preview_enabled;
}
bool guild::has_animated_icon_hash() const {
return this->flags & g_has_animated_icon;
}
std::string guild::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(id);
}
if (!name.empty()) {
j["name"] = name;
}
j["widget_enabled"] = widget_enabled();
if (afk_channel_id) {
j["afk_channel_id"] = afk_channel_id;
}
if (afk_channel_id) {
j["afk_timeout"] = afk_timeout;
}
if (widget_enabled()) {
j["widget_channel_id"] = widget_channel_id;
}
j["default_message_notifications"] = default_message_notifications;
j["explicit_content_filter"] = explicit_content_filter;
j["mfa_level"] = mfa_level;
if (system_channel_id) {
j["system_channel_id"] = system_channel_id;
}
if (rules_channel_id) {
j["rules_channel_id"] = rules_channel_id;
}
if (!vanity_url_code.empty()) {
j["vanity_url_code"] = vanity_url_code;
}
if (!description.empty()) {
j["description"] = description;
}
return j.dump();
}
void guild::rehash_members() {
members_container n;
n.reserve(members.size());
for (auto t = members.begin(); t != members.end(); ++t) {
n.insert(*t);
}
members = n;
}
guild& guild::fill_from_json(discord_client* shard, nlohmann::json* d) {
/* NOTE: This can be called by GUILD_UPDATE and also GUILD_CREATE.
* GUILD_UPDATE sends a partial guild object, so we use Set*NotNull functions
* for a lot of the values under the assumption they may sometimes be missing.
*/
this->id = SnowflakeNotNull(d, "id");
if (d->find("unavailable") == d->end() || (*d)["unavailable"].get<bool>() == false) {
/* Clear unavailable flag if set */
if (this->flags & dpp::g_unavailable) {
this->flags -= dpp::g_unavailable;
}
SetStringNotNull(d, "name", this->name);
/* Special case for guild icon to allow for animated icons.
* Animated icons start with a_ on the name, so we use this to set a flag
* in the flags field and then just store the iconhash separately.
*/
std::string _icon = StringNotNull(d, "icon");
if (!_icon.empty()) {
if (_icon.length() > 2 && _icon.substr(0, 2) == "a_") {
_icon = _icon.substr(2, _icon.length());
this->flags |= g_has_animated_icon;
}
this->icon = _icon;
}
std::string _dsplash = StringNotNull(d, "discovery_splash");
if (!_dsplash.empty()) {
this->discovery_splash = _dsplash;
}
SetSnowflakeNotNull(d, "owner_id", this->owner_id);
if (!(*d)["region"].is_null()) {
auto r = regionmap.find((*d)["region"].get<std::string>());
if (r != regionmap.end()) {
this->voice_region = r->second;
}
}
this->flags |= BoolNotNull(d, "large") ? dpp::g_large : 0;
this->flags |= BoolNotNull(d, "widget_enabled") ? dpp::g_widget_enabled : 0;
for (auto & feature : (*d)["features"]) {
auto f = featuremap.find(feature.get<std::string>());
if (f != featuremap.end()) {
this->flags |= f->second;
}
}
uint8_t scf = Int8NotNull(d, "system_channel_flags");
if (scf & 1) {
this->flags |= dpp::g_no_join_notifications;
}
if (scf & 2) {
this->flags |= dpp::g_no_boost_notifications;
}
SetSnowflakeNotNull(d, "afk_channel_id", this->afk_channel_id);
SetInt8NotNull(d, "afk_timeout", this->afk_timeout);
SetSnowflakeNotNull(d, "widget_channel_id", this->widget_channel_id);
SetInt8NotNull(d, "verification_level", this->verification_level);
SetInt8NotNull(d, "default_message_notifications", this->default_message_notifications);
SetInt8NotNull(d, "explicit_content_filter", this->explicit_content_filter);
SetInt8NotNull(d, "mfa_level", this->mfa_level);
SetSnowflakeNotNull(d, "application_id", this->application_id);
SetSnowflakeNotNull(d, "system_channel_id", this->system_channel_id);
SetSnowflakeNotNull(d, "rules_channel_id", this->rules_channel_id);
SetInt32NotNull(d, "member_count", this->member_count);
SetStringNotNull(d, "vanity_url_code", this->vanity_url_code);
SetStringNotNull(d, "description", this->description);
if (d->find("voice_states") != d->end()) {
this->voice_members.clear();
for (auto & vm : (*d)["voice_states"]) {
voicestate vs;
vs.fill_from_json(&vm);
vs.shard = shard;
vs.guild_id = this->id;
this->voice_members[vs.user_id] = vs;
}
}
std::string _banner = StringNotNull(d, "banner");
if (!_banner.empty()) {
this->banner = _banner;
}
SetInt8NotNull(d, "premium_tier", this->premium_tier);
SetInt16NotNull(d, "premium_subscription_count", this->premium_subscription_count);
SetSnowflakeNotNull(d, "public_updates_channel_id", this->public_updates_channel_id);
SetInt16NotNull(d, "max_video_channel_users", this->max_video_channel_users);
} else {
this->flags |= dpp::g_unavailable;
}
return *this;
}
guild_widget::guild_widget() : enabled(false), channel_id(0)
{
}
guild_widget& guild_widget::fill_from_json(nlohmann::json* j) {
enabled = BoolNotNull(j, "enabled");
channel_id = SnowflakeNotNull(j, "channel_id");
return *this;
}
std::string guild_widget::build_json() const {
return json({{"channel_id", channel_id}, {"enabled", enabled}}).dump();
}
uint64_t guild::base_permissions(const user* member) const
{
if (owner_id == member->id)
return ~0;
role* everyone = dpp::find_role(id);
auto mi = members.find(member->id);
if (mi == members.end())
return 0;
guild_member gm = mi->second;
uint64_t permissions = everyone->permissions;
for (auto& rid : gm.roles) {
role* r = dpp::find_role(rid);
permissions |= r->permissions;
}
if (permissions & p_administrator)
return ~0;
return permissions;
}
uint64_t guild::permission_overwrites(const uint64_t base_permissions, const user* member, const channel* channel) const
{
if (base_permissions & p_administrator)
return ~0;
int64_t permissions = base_permissions;
for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) {
if (it->id == id && it->type == ot_role) {
permissions &= ~it->deny;
permissions |= it->allow;
break;
}
}
auto mi = members.find(member->id);
if (mi == members.end())
return 0;
guild_member gm = mi->second;
uint64_t allow = 0;
uint64_t deny = 0;
for (auto& rid : gm.roles) {
/* Skip \@everyone, calculated above */
if (rid == id)
continue;
for (auto it = channel->permission_overwrites.begin(); it != channel->permission_overwrites.end(); ++it) {
if ((rid == it->id && it->type == ot_role) || (member->id == it->id && it->type == ot_member)) {
allow |= it->allow;
deny |= it->deny;
break;
}
}
}
permissions &= ~deny;
permissions |= allow;
return permissions;
}
bool guild::connect_member_voice(snowflake user_id) {
for (auto & c : channels) {
channel* ch = dpp::find_channel(c);
if (!ch || (!ch->is_voice_channel() && !ch->is_stage_channel())) {
continue;
}
auto vcmembers = ch->get_voice_members();
auto vsi = vcmembers.find(user_id);
if (vsi != vcmembers.end()) {
if (vsi->second.shard) {
vsi->second.shard->connect_voice(this->id, vsi->second.channel_id);
return true;
}
}
}
return false;
}
};

6193
vendor/DPP/src/dpp/httplib.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

120
vendor/DPP/src/dpp/integration.cpp vendored Normal file
View File

@ -0,0 +1,120 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
#include <dpp/integration.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/cache.h>
using json = nlohmann::json;
namespace dpp {
integration::integration() :
managed(),
type(i_twitch),
role_id(0),
user_id(0),
expire_grace_period(0),
flags(0),
synced_at(0),
subscriber_count(0)
{
app.id = 0;
app.bot = nullptr;
}
integration::~integration()
{
}
integration& integration::fill_from_json(nlohmann::json* j)
{
std::map<std::string, integration_type> type_map = {
{ "", i_discord },
{ "youtube", i_youtube },
{ "twitch", i_twitch },
{ "discord", i_discord }
};
this->id = SnowflakeNotNull(j, "id");
this->name = StringNotNull(j, "name");
this->type = type_map[StringNotNull(j, "type")];
if (BoolNotNull(j, "enabled"))
this->flags |= if_enabled;
if (BoolNotNull(j, "syncing"))
this->flags |= if_syncing;
if (BoolNotNull(j, "enable_emoticons"))
this->flags |= if_emoticons;
if (BoolNotNull(j, "revoked"))
this->flags |= if_revoked;
if (Int8NotNull(j, "expire_behavior"))
this->flags |= if_expire_kick;
this->expire_grace_period = Int32NotNull(j, "expire_grace_period");
if (j->find("user") != j->end()) {
auto t = (*j)["user"];
this->user_id = SnowflakeNotNull(&t, "user_id");
}
if (j->find("application") != j->end()) {
auto & t = (*j)["application"];
this->app.id = SnowflakeNotNull(&t, "id");
if (t.find("bot") != t.end()) {
auto & b = t["bot"];
this->app.bot = dpp::find_user(SnowflakeNotNull(&b, "id"));
}
}
this->subscriber_count = Int32NotNull(j, "subscriber_count");
this->account_id = StringNotNull(&((*j)["account"]), "id");
this->account_name = StringNotNull(&((*j)["account"]), "name");
return *this;
}
std::string integration::build_json() const {
return json({
{ "expire_behavior", (flags & if_expire_kick) ? 1 : 0 },
{ "expire_grace_period", expire_grace_period },
{ "enable_emoticons", emoticons_enabled() }
}).dump();
}
bool integration::emoticons_enabled() const {
return flags & if_emoticons;
}
bool integration::is_enabled() const {
return flags & if_enabled;
}
bool integration::is_syncing() const {
return flags & if_syncing;
}
bool integration::is_revoked() const {
return flags & if_revoked;
}
bool integration::expiry_kicks_user() const {
return flags & if_expire_kick;
}
};

70
vendor/DPP/src/dpp/invite.cpp vendored Normal file
View File

@ -0,0 +1,70 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/invite.h>
#include <dpp/discordevents.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
invite::invite() : guild_id(0), channel_id(0), inviter_id(0), target_user_id(0), target_user_type(1), approximate_presence_count(0), approximate_member_count(0)
{
}
invite::~invite() {
}
invite& invite::fill_from_json(nlohmann::json* j) {
code = StringNotNull(j, "code");
guild_id = (j->find("guild") != j->end()) ? SnowflakeNotNull(&((*j)["guild_id"]), "id") : 0;
channel_id = (j->find("channel") != j->end()) ? SnowflakeNotNull(&((*j)["channel"]), "id") : 0;
inviter_id = (j->find("inviter") != j->end()) ? SnowflakeNotNull(&((*j)["inviter"]), "id") : 0;
target_user_id = (j->find("target_user") != j->end()) ? SnowflakeNotNull(&((*j)["target_user"]), "id") : 0;
target_user_type = Int8NotNull(j, "target_user_type");
approximate_presence_count = Int32NotNull(j, "approximate_presence_count");
approximate_member_count = Int32NotNull(j, "approximate_member_count");
max_age = Int32NotNull(j, "max_age");
max_uses = Int32NotNull(j, "max_uses");
temporary = BoolNotNull(j, "temporary");
unique = BoolNotNull(j, "unique");
return *this;
}
std::string invite::build_json() const {
json j;
if (max_age > 0)
j["max_age"] = max_age;
if (max_uses > 0)
j["max_uses"] = max_uses;
if (target_user_id > 0)
j["target_user"] = target_user_id;
if (target_user_type > 0)
j["target_user_type"] = target_user_type;
if (temporary)
j["temporary"] = temporary;
if (unique)
j["unique"] = unique;
return j.dump();
}
};

29
vendor/DPP/src/dpp/managed.cpp vendored Normal file
View File

@ -0,0 +1,29 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
namespace dpp {
managed::managed(const snowflake nid) : id(nid)
{
}
};

996
vendor/DPP/src/dpp/message.cpp vendored Normal file
View File

@ -0,0 +1,996 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/message.h>
#include <dpp/user.h>
#include <dpp/channel.h>
#include <dpp/guild.h>
#include <dpp/cache.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
using json = nlohmann::json;
namespace dpp {
component::component() : type(static_cast<component_type>(1)), label(""), style(static_cast<component_style>(1)), custom_id(""), disabled(false), min_values(-1), max_values(-1)
{
emoji.animated = false;
emoji.id = 0;
emoji.name = "";
}
component& component::fill_from_json(nlohmann::json* j) {
type = static_cast<component_type>(Int8NotNull(j, "type"));
if (type == cot_action_row) {
components;
for (json sub_component : (*j)["components"]) {
dpp::component new_component;
new_component.fill_from_json(&sub_component);
components.push_back(new_component);
}
} else if (type == cot_button) {
label = StringNotNull(j, "label");
style = static_cast<component_style>(Int8NotNull(j, "style"));
custom_id = StringNotNull(j, "custom_id");
disabled = BoolNotNull(j, "disabled");
} else if (type == cot_selectmenu) {
label = "";
custom_id = StringNotNull(j, "custom_id");
disabled = BoolNotNull(j, "disabled");
}
return *this;
}
component& component::add_component(const component& c)
{
set_type(cot_action_row);
components.push_back(c);
return *this;
}
component& component::set_type(component_type ct)
{
type = ct;
return *this;
}
component& component::set_label(const std::string &l)
{
if (type == cot_action_row) {
set_type(cot_button);
}
label = utility::utf8substr(l, 0, 80);
return *this;
}
component& component::set_style(component_style cs)
{
set_type(cot_button);
style = cs;
return *this;
}
component& component::set_url(const std::string& u)
{
set_type(cot_button);
set_style(cos_link);
url = utility::utf8substr(u, 0, 512);
return *this;
}
component& component::set_id(const std::string &id)
{
if (type == cot_action_row) {
set_type(cot_button);
}
custom_id = utility::utf8substr(id, 0, 100);
return *this;
}
component& component::set_disabled(bool disable)
{
if (type == cot_action_row) {
set_type(cot_button);
}
disabled = disable;
return *this;
}
component& component::set_emoji(const std::string& name, dpp::snowflake id, bool animated)
{
if (type == cot_action_row) {
set_type(cot_button);
}
this->emoji.id = id;
this->emoji.name = name;
this->emoji.animated = animated;
return *this;
}
std::string component::build_json() const {
json j;
if (type == component_type::cot_action_row) {
j["type"] = 1;
json new_components;
for (component new_component : components) {
new_components.push_back(new_component.build_json());
}
j["components"] = new_components;
} else if (type == component_type::cot_button) {
j["type"] = 2;
j["label"] = label;
j["style"] = int(style);
j["custom_id"] = custom_id;
j["disabled"] = disabled;
} else if (type == component_type::cot_selectmenu) {
j["type"] = 3;
j["custom_id"] = custom_id;
//j["disabled"] = disabled;
if (!placeholder.empty()) {
j["placeholder"] = placeholder;
}
if (min_values >= 0) {
j["min_values"] = min_values;
}
if (max_values >= 0) {
j["max_values"] = max_values;
}
j["options"] = json::array();
for (auto opt : options) {
json o;
if (!opt.description.empty()) {
o["description"] = opt.description;
}
if (!opt.label.empty()) {
o["label"] = opt.label;
}
if (!opt.value.empty()) {
o["value"] = opt.value;
}
if (opt.is_default) {
o["default"] = true;
}
if (!opt.emoji.name.empty()) {
o["emoji"] = json::object();
o["emoji"]["name"] = opt.emoji.name;
if (opt.emoji.id) {
o["emoji"]["id"] = std::to_string(opt.emoji.id);
}
}
j["options"].push_back(o);
}
}
return j.dump();
}
select_option::select_option() : is_default(false) {
}
select_option::select_option(const std::string &_label, const std::string &_value, const std::string &_description) : is_default(false), label(_label), value(_value), description(_description) {
}
select_option& select_option::set_label(const std::string &l) {
label = dpp::utility::utf8substr(l, 0, 100);
return *this;
}
select_option& select_option::set_default(bool def) {
is_default = def;
return *this;
}
select_option& select_option::set_value(const std::string &v) {
value = dpp::utility::utf8substr(v, 0, 100);
return *this;
}
select_option& select_option::set_description(const std::string &d) {
description = dpp::utility::utf8substr(d, 0, 100);
return *this;
}
select_option& select_option::set_emoji(const std::string &n, dpp::snowflake id, bool animated) {
emoji.name = n;
emoji.id = id;
emoji.animated = animated;
return *this;
}
select_option& select_option::set_animated(bool anim) {
emoji.animated = anim;
return *this;
}
component& component::set_placeholder(const std::string &_placeholder) {
placeholder = dpp::utility::utf8substr(_placeholder, 0, 100);
return *this;
}
component& component::set_min_values(uint32_t _min_values) {
min_values = _min_values;
return *this;
}
component& component::set_max_values(uint32_t _max_values) {
max_values = _max_values;
return *this;
}
component& component::add_select_option(const select_option &option) {
if (options.size() <= 25) {
options.push_back(option);
}
return *this;
}
embed::~embed() {
}
embed::embed() : timestamp(0), color(0) {
}
message::message() : id(0), channel_id(0), guild_id(0), author(nullptr), sent(0), edited(0), flags(0),
type(mt_default), tts(false), mention_everyone(false), pinned(false), webhook_id(0)
{
message_reference.channel_id = 0;
message_reference.guild_id = 0;
message_reference.message_id = 0;
message_reference.fail_if_not_exists = false;
interaction.id = 0;
interaction.type = interaction_type::it_ping;
interaction.usr.id = 0;
allowed_mentions.parse_users = false;
allowed_mentions.parse_everyone = false;
allowed_mentions.parse_roles = false;
/* The documentation for discord is INCORRECT. This defaults to true, and must be set to false.
* The default ctor reflects this.
*/
allowed_mentions.replied_user = true;
}
message& message::set_reference(snowflake _message_id, snowflake _guild_id, snowflake _channel_id, bool fail_if_not_exists) {
message_reference.channel_id = _channel_id;
message_reference.guild_id = _guild_id;
message_reference.message_id = _message_id;
message_reference.fail_if_not_exists = fail_if_not_exists;
return *this;
}
message& message::set_allowed_mentions(bool _parse_users, bool _parse_roles, bool _parse_everyone, bool _replied_user, const std::vector<snowflake> &users, const std::vector<snowflake> &roles) {
allowed_mentions.parse_users = _parse_users;
allowed_mentions.parse_everyone = _parse_everyone;
allowed_mentions.parse_roles = _parse_roles;
allowed_mentions.replied_user = _replied_user;
allowed_mentions.users = users;
allowed_mentions.roles = roles;
return *this;
}
message::message(snowflake _channel_id, const std::string &_content, message_type t) : message() {
channel_id = _channel_id;
content = utility::utf8substr(_content, 0, 2000);
type = t;
}
message& message::add_component(const component& c)
{
components.push_back(c);
return *this;
}
message& message::add_embed(const embed& e)
{
embeds.push_back(e);
return *this;
}
message& message::set_flags(uint8_t f)
{
flags = f;
return *this;
}
message& message::set_type(message_type t)
{
type = t;
return *this;
}
message& message::set_filename(const std::string &fn)
{
filename = fn;
return *this;
}
message& message::set_file_content(const std::string &fc)
{
filecontent = fc;
return *this;
}
message& message::set_content(const std::string &c)
{
content = utility::utf8substr(c, 0, 2000);
return *this;
}
message::message(const std::string &_content, message_type t) : message() {
content = utility::utf8substr(_content, 0, 2000);
type = t;
}
message::message(snowflake _channel_id, const embed& _embed) : message() {
channel_id = _channel_id;
embeds.push_back(_embed);
}
embed::embed(json* j) : embed() {
title = StringNotNull(j, "title");
type = StringNotNull(j, "type");
description = StringNotNull(j, "description");
url = StringNotNull(j, "url");
timestamp = TimestampNotNull(j, "timestamp");
color = Int32NotNull(j, "color");
if (j->find("footer") != j->end()) {
dpp::embed_footer f;
json& fj = (*j)["footer"];
f.text = StringNotNull(&fj, "text");
f.icon_url = StringNotNull(&fj, "icon_url");
f.proxy_url = StringNotNull(&fj, "proxy_url");
footer = f;
}
std::vector<std::string> type_list = { "image", "video", "thumbnail" };
for (auto& s : type_list) {
if (j->find(s) != j->end()) {
embed_image curr;
json& fi = (*j)[s];
curr.url = StringNotNull(&fi, "url");
curr.height = StringNotNull(&fi, "height");
curr.width = StringNotNull(&fi, "width");
curr.proxy_url = StringNotNull(&fi, "proxy_url");
if (s == "image") {
image = curr;
} else if (s == "video") {
video = curr;
} else if (s == "thumbnail") {
thumbnail = curr;
}
}
}
if (j->find("provider") != j->end()) {
json &p = (*j)["provider"];
dpp::embed_provider pr;
pr.name = StringNotNull(&p, "name");
pr.url = StringNotNull(&p, "url");
provider = pr;
}
if (j->find("author") != j->end()) {
json &a = (*j)["author"];
dpp::embed_author au;
au.name = StringNotNull(&a, "name");
au.url = StringNotNull(&a, "url");
au.icon_url = StringNotNull(&a, "icon_url");
au.proxy_icon_url = StringNotNull(&a, "proxy_icon_url");
author = au;
}
if (j->find("fields") != j->end()) {
json &fl = (*j)["fields"];
for (auto & field : fl) {
embed_field f;
f.name = StringNotNull(&field, "name");
f.value = StringNotNull(&field, "value");
f.is_inline = BoolNotNull(&field, "inline");
fields.push_back(f);
}
}
}
embed& embed::add_field(const std::string& name, const std::string &value, bool is_inline) {
if (fields.size() < 25) {
embed_field f;
f.name = utility::utf8substr(name, 0, 256);
f.value = utility::utf8substr(value, 0, 1024);
f.is_inline = is_inline;
fields.push_back(f);
}
return *this;
}
embed& embed::set_author(const embed_author& a)
{
author = a;
return *this;
}
embed& embed::set_author(const std::string& name, const std::string& url, const std::string& icon_url) {
dpp::embed_author a;
a.name = utility::utf8substr(name, 0, 256);
a.url = url;
a.icon_url = icon_url;
author = a;
return *this;
}
embed& embed::set_footer(const embed_footer& f) {
footer = f;
return *this;
}
embed& embed::set_provider(const std::string& name, const std::string& url) {
dpp::embed_provider p;
p.name = utility::utf8substr(name, 0, 256);
p.url = url;
provider = p;
return *this;
}
embed& embed::set_image(const std::string& url) {
dpp::embed_image i;
i.url = url;
image = i;
return *this;
}
embed& embed::set_video(const std::string& url) {
dpp::embed_image v;
v.url = url;
video = v;
return *this;
}
embed& embed::set_thumbnail(const std::string& url) {
dpp::embed_image t;
t.url = url;
thumbnail = t;
return *this;
}
embed& embed::set_title(const std::string &text) {
title = utility::utf8substr(text, 0, 256);
return *this;
}
embed& embed::set_description(const std::string &text) {
description = utility::utf8substr(text, 0, 4096);
return *this;
}
embed& embed::set_color(uint32_t col) {
// Mask off alpha, as discord doesn't use it
color = col & 0x00FFFFFF;
return *this;
}
embed& embed::set_url(const std::string &u) {
url = u;
return *this;
}
embed_footer& embed_footer::set_text(const std::string& t){
text = t;
return *this;
}
embed_footer& embed_footer::set_icon(const std::string& i){
icon_url = i;
return *this;
}
embed_footer& embed_footer::set_proxy(const std::string& p){
proxy_url = p;
return *this;
}
reaction::reaction() {
count = 0;
me = false;
emoji_id = 0;
}
reaction::reaction(json* j) {
count = (*j)["count"];
me = (*j)["me"];
json emoji = (*j)["emoji"];
emoji_id = SnowflakeNotNull(&emoji, "id");
emoji_name = StringNotNull(&emoji, "name");
}
attachment::attachment() {
id = 0;
size = 0;
width = 0;
height = 0;
}
attachment::attachment(json *j) : attachment() {
this->id = SnowflakeNotNull(j, "id");
this->size = (*j)["size"];
this->filename = (*j)["filename"];
this->url = (*j)["url"];
this->proxy_url = (*j)["proxy_url"];
this->width = Int32NotNull(j, "width");
this->height = Int32NotNull(j, "height");
this->content_type = StringNotNull(j, "content_type");
}
std::string message::build_json(bool with_id, bool is_interaction_response) const {
/* This is the basics. once it works, expand on it. */
json j({
{"channel_id", channel_id},
{"tts", tts},
{"nonce", nonce},
{"flags", flags},
{"type", type}
});
if (with_id) {
j["id"] = std::to_string(id);
}
if (!content.empty()) {
j["content"] = content;
}
/* Populate message reference */
if (message_reference.channel_id || message_reference.guild_id || message_reference.message_id) {
j["message_reference"] = json::object();
if (message_reference.channel_id) {
j["message_reference"]["channel_id"] = std::to_string(message_reference.channel_id);
}
if (message_reference.guild_id) {
j["message_reference"]["guild_id"] = std::to_string(message_reference.guild_id);
}
if (message_reference.message_id) {
j["message_reference"]["message_id"] = std::to_string(message_reference.message_id);
}
j["message_reference"]["fail_if_not_exists"] = message_reference.fail_if_not_exists;
}
j["allowed_mentions"] = json::object();
j["allowed_mentions"]["parse"] = json::array();
if (allowed_mentions.parse_everyone || allowed_mentions.parse_roles || allowed_mentions.parse_users || !allowed_mentions.replied_user || allowed_mentions.users.size() || allowed_mentions.roles.size()) {
if (allowed_mentions.parse_everyone) {
j["allowed_mentions"]["parse"].push_back("everyone");
}
if (allowed_mentions.parse_roles) {
j["allowed_mentions"]["parse"].push_back("roles");
}
if (allowed_mentions.parse_users) {
j["allowed_mentions"]["parse"].push_back("users");
}
if (!allowed_mentions.replied_user) {
j["allowed_mentions"]["replied_user"] = false;
}
if (allowed_mentions.users.size()) {
j["allowed_mentions"]["users"] = json::array();
for (auto& user : allowed_mentions.users) {
j["allowed_mentions"]["users"].push_back(std::to_string(user));
}
}
if (allowed_mentions.roles.size()) {
j["allowed_mentions"]["roles"] = json::array();
for (auto& role : allowed_mentions.roles) {
j["allowed_mentions"]["roles"].push_back(std::to_string(role));
}
}
}
if (components.size()) {
j["components"] = json::array();
}
for (auto & component : components) {
json n;
n["type"] = cot_action_row;
n["components"] = {};
json sn;
for (auto & subcomponent : component.components) {
if (subcomponent.type == cot_button) {
sn["type"] = subcomponent.type;
sn["label"] = subcomponent.label;
sn["style"] = int(subcomponent.style);
if (subcomponent.type == cot_button && subcomponent.style != cos_link && !subcomponent.custom_id.empty()) {
/* Links cannot have a custom id */
sn["custom_id"] = subcomponent.custom_id;
}
if (subcomponent.type == cot_button && subcomponent.style == cos_link && !subcomponent.url.empty()) {
sn["url"] = subcomponent.url;
}
sn["disabled"] = subcomponent.disabled;
if (subcomponent.emoji.id || !subcomponent.emoji.name.empty()) {
sn["emoji"] = {};
sn["emoji"]["animated"] = subcomponent.emoji.animated;
}
if (subcomponent.emoji.id) {
sn["emoji"]["id"] = std::to_string(subcomponent.emoji.id);
}
if (!subcomponent.emoji.name.empty()) {
sn["emoji"]["name"] = subcomponent.emoji.name;
}
} else if (subcomponent.type == cot_selectmenu) {
sn["type"] = subcomponent.type;
sn["custom_id"] = subcomponent.custom_id;
//sn["disabled"] = subcomponent.disabled;
if (!subcomponent.placeholder.empty()) {
sn["placeholder"] = subcomponent.placeholder;
}
if (subcomponent.min_values >= 0) {
sn["min_values"] = subcomponent.min_values;
}
if (subcomponent.max_values >= 0) {
sn["max_values"] = subcomponent.max_values;
}
sn["options"] = json::array();
for (auto opt : subcomponent.options) {
json o;
if (!opt.description.empty()) {
o["description"] = opt.description;
}
if (!opt.label.empty()) {
o["label"] = opt.label;
}
if (!opt.value.empty()) {
o["value"] = opt.value;
}
if (opt.is_default) {
o["default"] = true;
}
if (!opt.emoji.name.empty()) {
o["emoji"] = json::object();
o["emoji"]["name"] = opt.emoji.name;
if (opt.emoji.id) {
o["emoji"]["id"] = std::to_string(opt.emoji.id);
}
if (opt.emoji.animated) {
o["emoji"]["animated"] = true;
}
}
sn["options"].push_back(o);
}
}
n["components"].push_back(sn);
}
j["components"].push_back(n);
}
if (embeds.size()) {
j["embeds"] = json::array();
for (auto& embed : embeds) {
json e;
if (!embed.description.empty())
e["description"] = embed.description;
if (!embed.title.empty())
e["title"] = embed.title;
if (!embed.url.empty())
e["url"] = embed.url;
e["color"] = embed.color;
if (embed.footer.has_value()) {
e["footer"]["text"] = embed.footer->text;
e["footer"]["icon_url"] = embed.footer->icon_url;
}
if (embed.image.has_value()) {
e["image"]["url"] = embed.image->url;
}
if (embed.thumbnail.has_value()) {
e["thumbnail"]["url"] = embed.thumbnail->url;
}
if (embed.author.has_value()) {
e["author"]["name"] = embed.author->name;
e["author"]["url"] = embed.author->url;
e["author"]["icon_url"] = embed.author->icon_url;
}
if (embed.fields.size()) {
e["fields"] = json();
for (auto& field : embed.fields) {
json f({ {"name", field.name}, {"value", field.value}, {"inline", field.is_inline} });
e["fields"].push_back(f);
}
}
if (embed.timestamp != 0) {
std::ostringstream ss;
struct tm t;
#ifdef _WIN32
gmtime_s(&t, &embed.timestamp);
#else
gmtime_r(&embed.timestamp, &t);
#endif
ss << std::put_time(&t, "%FT%TZ");
e["timestamp"] = ss.str();
}
j["embeds"].push_back(e);
}
}
return j.dump();
}
bool message::is_crossposted() const {
return flags & m_crossposted;
}
bool message::is_crosspost() const {
return flags & m_is_crosspost;
}
bool message::supress_embeds() const {
return flags & m_supress_embeds;
}
bool message::is_source_message_deleted() const {
return flags & m_source_message_deleted;
}
bool message::is_urgent() const {
return flags & m_urgent;
}
bool message::is_ephemeral() const {
return flags & m_ephemeral;
}
bool message::is_loading() const {
return flags & m_loading;
}
message::~message() {
}
message& message::fill_from_json(json* d, cache_policy_t cp) {
this->id = SnowflakeNotNull(d, "id");
this->channel_id = SnowflakeNotNull(d, "channel_id");
this->guild_id = SnowflakeNotNull(d, "guild_id");
/* We didn't get a guild id. See if we can find one in the channel */
if (guild_id == 0 && channel_id != 0) {
dpp::channel* c = dpp::find_channel(this->channel_id);
if (c) {
this->guild_id = c->guild_id;
}
}
this->flags = Int8NotNull(d, "flags");
this->type = Int8NotNull(d, "type");
this->author = nullptr;
user* authoruser = nullptr;
/* May be null, if its null cache it from the partial */
if (d->find("author") != d->end()) {
json &j_author = (*d)["author"];
if (cp.user_policy == dpp::cp_none) {
/* User caching off! Allocate a temp user to be deleted in destructor */
authoruser = &self_author;
this->author = &self_author;
self_author.fill_from_json(&j_author);
} else {
/* User caching on - aggressive or lazy - create a cached user entry */
authoruser = find_user(SnowflakeNotNull(&j_author, "id"));
if (!authoruser) {
/* User does not exist yet, cache the partial as a user record */
authoruser = new user();
authoruser->fill_from_json(&j_author);
get_user_cache()->store(authoruser);
}
this->author = authoruser;
}
}
if (d->find("interaction") != d->end()) {
json& inter = (*d)["interaction"];
interaction.id = SnowflakeNotNull(&inter, "id");
interaction.name = StringNotNull(&inter, "name");
interaction.type = Int8NotNull(&inter, "type");
if (inter.contains("user") && !inter["user"].is_null()) from_json(inter["user"], interaction.usr);
}
if (d->find("sticker_items") != d->end()) {
json &sub = (*d)["sticker_items"];
for (auto & sticker_raw : sub) {
stickers.push_back(dpp::sticker().fill_from_json(&sticker_raw));
}
}
if (d->find("mentions") != d->end()) {
json &sub = (*d)["mentions"];
for (auto & m : sub) {
mentions.push_back(SnowflakeNotNull(&m, "id"));
}
}
if (d->find("mention_roles") != d->end()) {
json &sub = (*d)["mention_roles"];
for (auto & m : sub) {
mention_roles.push_back(from_string<snowflake>(m, std::dec));
}
}
if (d->find("mention_channels") != d->end()) {
json &sub = (*d)["mention_channels"];
for (auto & m : sub) {
mention_channels.push_back(SnowflakeNotNull(&m, "id"));
}
}
/* Fill in member record, cache uncached ones */
guild* g = find_guild(this->guild_id);
this->member = {};
if (g && d->find("member") != d->end()) {
json& mi = (*d)["member"];
snowflake uid = SnowflakeNotNull(&(mi["user"]), "id");
if (!uid && authoruser) {
uid = authoruser->id;
}
if (cp.user_policy == dpp::cp_none) {
/* User caching off! Just fill in directly but dont store member to guild */
this->member.fill_from_json(&mi, g->id, uid);
} else {
/* User caching on, lazy or aggressive - cache the member information */
auto thismember = g->members.find(uid);
if (thismember == g->members.end()) {
if (uid != 0 && authoruser) {
guild_member gm;
gm.fill_from_json(&mi, g->id, uid);
g->members[authoruser->id] = gm;
this->member = gm;
}
} else {
/* Update roles etc */
this->member = thismember->second;
if (authoruser) {
this->member.fill_from_json(&mi, g->id, authoruser->id);
g->members[authoruser->id] = this->member;
}
}
}
}
if (d->find("embeds") != d->end()) {
json & el = (*d)["embeds"];
for (auto& e : el) {
this->embeds.push_back(embed(&e));
}
}
this->content = StringNotNull(d, "content");
this->sent = TimestampNotNull(d, "timestamp");
this->edited = TimestampNotNull(d, "edited_timestamp");
this->tts = BoolNotNull(d, "tts");
this->mention_everyone = BoolNotNull(d, "mention_everyone");
if (d->find("reactions") != d->end()) {
json & el = (*d)["reactions"];
for (auto& e : el) {
this->reactions.push_back(reaction(&e));
}
}
if (((*d)["nonce"]).is_string()) {
this->nonce = StringNotNull(d, "nonce");
} else {
this->nonce = std::to_string(SnowflakeNotNull(d, "nonce"));
}
this->pinned = BoolNotNull(d, "pinned");
this->webhook_id = SnowflakeNotNull(d, "webhook_id");
for (auto& e : (*d)["attachments"]) {
this->attachments.push_back(attachment(&e));
}
return *this;
}
sticker::sticker() : id(0), pack_id(0), guild_id(0), type(st_standard), format_type(sf_png), available(true), sort_value(0) {
}
sticker& sticker::fill_from_json(nlohmann::json* j) {
this->id = SnowflakeNotNull(j, "id");
this->pack_id = SnowflakeNotNull(j, "pack_id");
this->name = StringNotNull(j, "name");
this->description = StringNotNull(j, "description");
this->tags = StringNotNull(j, "tags");
this->asset = StringNotNull(j, "asset");
this->guild_id = SnowflakeNotNull(j, "guild_id");
this->type = static_cast<sticker_type>(Int8NotNull(j, "type"));
this->format_type = static_cast<sticker_format>(Int8NotNull(j, "format_type"));
this->available = BoolNotNull(j, "available");
this->sort_value = Int8NotNull(j, "sort_value");
if (j->find("user") != j->end()) {
sticker_user.fill_from_json(&((*j)["user"]));
}
return *this;
}
std::string sticker::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(this->id);
}
j["pack_id"] = std::to_string(this->pack_id);
if (this->guild_id) {
j["guild_id"] = std::to_string(this->guild_id);
}
j["name"] = this->name;
j["description"] = this->description;
if (!this->tags.empty()) {
j["tags"] = this->tags;
}
if (!this->asset.empty()) {
j["asset"] = this->asset;
}
j["type"] = this->type;
j["format_type"] = this->format_type;
j["available"] = this->available;
j["sort_value"] = this->sort_value;
return j.dump();
}
sticker_pack::sticker_pack() : id(0), sku_id(0), cover_sticker_id(0), banner_asset_id(0) {
}
sticker_pack& sticker_pack::fill_from_json(nlohmann::json* j) {
this->id = SnowflakeNotNull(j, "id");
this->sku_id = SnowflakeNotNull(j, "sku_id");
this->cover_sticker_id = SnowflakeNotNull(j, "cover_sticker_id");
this->banner_asset_id = SnowflakeNotNull(j, "banner_asset_id");
this->name = StringNotNull(j, "name");
this->description = StringNotNull(j, "description");
if (j->find("stickers") != j->end()) {
json & sl = (*j)["stickers"];
for (auto& s : sl) {
this->stickers[SnowflakeNotNull(&s, "id")] = sticker().fill_from_json(&s);
}
}
return *this;
}
std::string sticker_pack::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(this->id);
}
if (sku_id) {
j["sku_id"] = std::to_string(sku_id);
}
if (cover_sticker_id) {
j["cover_sticker_id"] = std::to_string(cover_sticker_id);
}
if (banner_asset_id) {
j["banner_asset_id"] = std::to_string(banner_asset_id);
}
j["name"] = name;
j["description"] = description;
j["stickers"] = json::array();
for (auto& s : stickers) {
j["stickers"].push_back(json::parse(s.second.build_json(with_id)));
}
return j.dump();
}
sticker& sticker::set_filename(const std::string &fn) {
filename = fn;
return *this;
}
sticker& sticker::set_file_content(const std::string &fc) {
filecontent = fc;
return *this;
}
};

211
vendor/DPP/src/dpp/presence.cpp vendored Normal file
View File

@ -0,0 +1,211 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/presence.h>
#include <dpp/discordevents.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
activity::activity(const activity_type typ, const std::string& nam, const std::string& stat, const std::string& url_) : type(typ), name(nam), state(stat), url(url_)
{
}
presence::presence() : guild_id(0), user_id(0), flags(0)
{
}
presence::presence(presence_status status, activity_type type, const std::string& activity_description) {
dpp::activity a;
a.name = activity_description;
a.type = type;
activities.clear();
activities.push_back(a);
flags &= PF_CLEAR_STATUS;
if (status == ps_online)
flags |= p_status_online;
else if (status == ps_idle)
flags |= p_status_idle;
else if (status == ps_dnd)
flags |= p_status_dnd;
}
presence::presence(presence_status status, activity a) {
activities.clear();
activities.push_back(std::move(a));
flags &= PF_CLEAR_STATUS;
if (status == ps_online)
flags |= p_status_online;
else if (status == ps_idle)
flags |= p_status_idle;
else if (status == ps_dnd)
flags |= p_status_dnd;
}
presence::~presence() {
}
presence& presence::fill_from_json(nlohmann::json* j) {
guild_id = SnowflakeNotNull(j, "guild_id");
user_id = SnowflakeNotNull(&((*j)["user"]), "id");
auto f = j->find("client_status");
if (f != j->end()) {
bool update_desktop = false, update_web = false, update_mobile = false;
std::string desktop, mobile, web;
if (f->find("desktop") != f->end()) {
desktop = StringNotNull(&((*j)["client_status"]), "desktop");
update_desktop = true;
}
if (f->find("mobile") != f->end()) {
mobile = StringNotNull(&((*j)["client_status"]), "mobile");
update_mobile = true;
}
if (f->find("web") != f->end()) {
web = StringNotNull(&((*j)["client_status"]), "web");
update_web = true;
}
if (update_desktop) {
flags &= PF_CLEAR_DESKTOP;
if (desktop == "online")
flags |= p_desktop_online;
else if (desktop == "idle")
flags |= p_desktop_idle;
else if (desktop == "dnd")
flags |= p_desktop_dnd;
}
if (update_mobile) {
flags &= PF_CLEAR_MOBILE;
if (mobile == "online")
flags |= p_mobile_online;
else if (mobile == "idle")
flags |= p_mobile_idle;
else if (mobile == "dnd")
flags |= p_mobile_dnd;
}
if (update_web) {
flags &= PF_CLEAR_WEB;
if (web == "online")
flags |= p_web_online;
else if (web == "idle")
flags |= p_web_idle;
else if (web == "dnd")
flags |= p_web_dnd;
}
}
if (j->find("status") != j->end()) {
flags &= PF_CLEAR_STATUS;
std::string main = StringNotNull(j, "status");
if (main == "online")
flags |= p_status_online;
else if (main == "idle")
flags |= p_status_idle;
else if (main == "dnd")
flags |= p_status_dnd;
}
if (j->find("activities") != j->end()) {
activities.clear();
for (auto & act : (*j)["activities"]) {
activity a;
a.name = StringNotNull(&act, "name");
a.state = StringNotNull(&act, "state"); // if user
if (a.state.empty()) a.state = StringNotNull(&act, "details"); // if activity from bot, maybe?
a.type = (activity_type)Int8NotNull(&act, "type");
a.url = StringNotNull(&act, "url");
a.created_at = Int64NotNull(&act, "created_at");
if (act.find("timestamps") != act.end()) {
a.start = Int64NotNull(&(act["timestamps"]), "start");
a.end = Int64NotNull(&(act["timestamps"]), "end");
}
a.application_id = SnowflakeNotNull(&act, "application_id");
a.flags = Int8NotNull(&act, "flags");
activities.push_back(a);
}
}
return *this;
}
std::string presence::build_json() const {
std::map<presence_status, std::string> status_name_mapping = {
{ps_online, "online"},
{ps_offline, "offline"},
{ps_idle, "idle"},
{ps_dnd, "dnd"}
};
json j({
{"op", 3},
{"d",
{
{ "status", status_name_mapping[status()] },
{ "since", json::value_t::null },
{ "afk", false }
}
}
});
if (activities.size()) {
for(const auto& i : activities){
json j2({
{ "name", i.name },
{ "type", i.type }
});
if (!i.url.empty()) j2["url"] = i.url;
if (!i.state.empty()) j2["details"] = i.state; // bot activity is details, not state
j["d"]["activities"].push_back(j2);
}
/*j["d"]["game"] = json({ // use activities instead.
{ "name", activities[0].name },
{ "type", activities[0].type }
});*/
}
return j.dump();
}
presence_status presence::desktop_status() const {
return (presence_status)((flags >> PF_SHIFT_DESKTOP) & PF_STATUS_MASK);
}
presence_status presence::web_status() const {
return (presence_status)((flags >> PF_SHIFT_WEB) & PF_STATUS_MASK);
}
presence_status presence::mobile_status() const {
return (presence_status)((flags >> PF_SHIFT_MOBILE) & PF_STATUS_MASK);
}
presence_status presence::status() const {
return (presence_status)((flags >> PF_SHIFT_MAIN) & PF_STATUS_MASK);
}
};

53
vendor/DPP/src/dpp/prune.cpp vendored Normal file
View File

@ -0,0 +1,53 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/prune.h>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
namespace dpp {
using json = nlohmann::json;
prune& prune::fill_from_json(nlohmann::json* j) {
days = Int32NotNull(j, "days");
compute_prune_count = BoolNotNull(j, "compute_prune_count");
if (j->find("include_roles") != j->end()) {
for (auto & r : (*j)["include_roles"]) {
include_roles.push_back(from_string<uint64_t>(r.get<std::string>(), std::dec));
}
}
return *this;
}
std::string prune::build_json(bool with_prune_count) const {
json j;
for (auto & r : include_roles) {
j["include_roles"].push_back(std::to_string(r));
}
if (with_prune_count) {
j["compute_prune_count"] = compute_prune_count;
}
j["days"] = days;
return j.dump();
}
};

472
vendor/DPP/src/dpp/queues.cpp vendored Normal file
View File

@ -0,0 +1,472 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <io.h>
#pragma comment(lib,"ws2_32")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <random>
#include <dpp/queues.h>
#include <dpp/cluster.h>
#define CPPHTTPLIB_OPENSSL_SUPPORT
#include <dpp/httplib.h>
#include <fmt/format.h>
#include <dpp/stringops.h>
namespace dpp {
http_request::http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata, http_method _method, const std::string &filename, const std::string &filecontent) : endpoint(_endpoint), parameters(_parameters), complete_handler(completion), postdata(_postdata), method(_method), completed(false), file_name(filename), file_content(filecontent)
{
}
http_request::~http_request() {
}
void http_request::complete(const http_request_completion_t &c) {
/* Call completion handler only if the request has been completed */
if (is_completed() && complete_handler)
complete_handler(c);
}
/* Fill a http_request_completion_t from a HTTP result */
void populate_result(const std::string &url, const cluster* owner, http_request_completion_t& rv, const httplib::Result &res) {
rv.status = res->status;
rv.body = res->body;
for (auto &v : res->headers) {
rv.headers[v.first] = v.second;
}
rv.ratelimit_limit = from_string<uint64_t>(res->get_header_value("X-RateLimit-Limit"), std::dec);
rv.ratelimit_remaining = from_string<uint64_t>(res->get_header_value("X-RateLimit-Remaining"), std::dec);
rv.ratelimit_reset_after = from_string<uint64_t>(res->get_header_value("X-RateLimit-Reset-After"), std::dec);
rv.ratelimit_bucket = res->get_header_value("X-RateLimit-Bucket");
rv.ratelimit_global = (res->get_header_value("X-RateLimit-Global") == "true");
if (res->get_header_value("X-RateLimit-Retry-After") != "") {
rv.ratelimit_retry_after = from_string<uint64_t>(res->get_header_value("X-RateLimit-Retry-After"), std::dec);
}
if (rv.status == 429) {
owner->log(ll_warning, fmt::format("Rate limited on endpoint {}, reset after {}s!", url, rv.ratelimit_retry_after ? rv.ratelimit_retry_after : rv.ratelimit_reset_after));
}
if (url != "/api/v" DISCORD_API_VERSION "/gateway/bot") { // Squelch this particular api endpoint or it generates a warning the minute we boot a cluster
if (rv.ratelimit_global) {
owner->log(ll_warning, fmt::format("At global rate limit on endpoint {}, reset after {}s", url, rv.ratelimit_retry_after ? rv.ratelimit_retry_after : rv.ratelimit_reset_after));
} else if (rv.ratelimit_remaining == 1) {
owner->log(ll_warning, fmt::format("Near endpoint {} rate limit, reset after {}s", url, rv.ratelimit_retry_after ? rv.ratelimit_retry_after : rv.ratelimit_reset_after));
}
}
}
/* Returns true if the request has been made */
bool http_request::is_completed()
{
return completed;
}
/* Execute a HTTP request */
http_request_completion_t http_request::Run(const cluster* owner) {
http_request_completion_t rv;
httplib::Client cli("https://discord.com");
/* This is for a reason :( - Some systems have really out of date cert stores */
cli.enable_server_certificate_verification(false);
cli.set_follow_location(true);
/* TODO: Once we have a version number header, use it here */
httplib::Headers headers = {
{"Authorization", std::string("Bot ") + owner->token},
{"User-Agent", "DiscordBot (https://github.com/brainboxdotcc/DPP, 0.0.1)"}
};
cli.set_default_headers(headers);
rv.ratelimit_limit = rv.ratelimit_remaining = rv.ratelimit_reset_after = rv.ratelimit_retry_after = 0;
rv.status = 0;
rv.ratelimit_global = false;
std::string _url = endpoint;
if (!empty(parameters)) {
_url = endpoint + "/" +parameters;
}
/* Because of the design of cpp-httplib we can't create a httplib::Result once and make this code
* shorter. We have to use "auto res = ...". This is because httplib::Result has no default constructor
* and needs to be passed a result and some other blackboxed rammel.
*/
switch (method) {
case m_get: {
if (auto res = cli.Get(_url.c_str())) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
}
break;
case m_post: {
/* POST supports post data body */
if (!file_name.empty() && !file_content.empty()) {
httplib::MultipartFormDataItems items = {
{ "payload_json", postdata, "", "application/json" },
{ "file", file_content, file_name, "application/octet-stream" }
};
if (auto res = cli.Post(_url.c_str(), items)) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
} else {
if (auto res = cli.Post(_url.c_str(), postdata.c_str(), "application/json")) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
}
}
break;
case m_patch: {
if (auto res = cli.Patch(_url.c_str(), postdata.c_str(), "application/json")) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
}
break;
case m_put: {
/* PUT supports post data body */
if (auto res = cli.Put(_url.c_str(), postdata.c_str(), "application/json")) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
}
break;
case m_delete: {
if (auto res = cli.Delete(_url.c_str())) {
populate_result(_url, owner, rv, res);
} else {
rv.error = (http_error)res.error();
}
}
break;
}
/* Set completion flag */
completed = true;
return rv;
}
request_queue::request_queue(const class cluster* owner) : creator(owner), terminating(false), globally_ratelimited(false), globally_limited_for(0)
{
in_queue_listen_sock = socket(AF_INET, SOCK_STREAM, 0);
out_queue_listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if (in_queue_listen_sock == -1 || out_queue_listen_sock == -1) {
throw dpp::exception("Can't initialise request queue sockets");
}
std::mt19937 generator(time(NULL));
std::uniform_real_distribution<double> distribution(8192, 32760);
in_queue_port = distribution(generator);
out_queue_port = distribution(generator);
struct sockaddr_in in_server, out_server;
in_server.sin_family = out_server.sin_family = AF_INET;
in_server.sin_addr.s_addr = out_server.sin_addr.s_addr = htonl(0x7f000001); /* Localhost */
in_server.sin_port = htons(in_queue_port);
out_server.sin_port = htons(out_queue_port);
if ((bind(in_queue_listen_sock, (struct sockaddr *)&in_server , sizeof(in_server)) < 0) || (bind(out_queue_listen_sock, (struct sockaddr *)&out_server , sizeof(out_server)) < 0)) {
throw dpp::exception("Can't bind request queue sockets");
}
/* Backlog is only 1, because we only expect our own system to connect back to this once */
listen(in_queue_listen_sock, 1);
listen(out_queue_listen_sock, 1);
in_thread = new std::thread(&request_queue::in_loop, this);
out_thread = new std::thread(&request_queue::out_loop, this);
std::this_thread::sleep_for(std::chrono::milliseconds(250));
in_queue_connect_sock = socket(AF_INET, SOCK_STREAM, 0);
out_queue_connect_sock = socket(AF_INET, SOCK_STREAM, 0);
if (in_queue_connect_sock == -1 || out_queue_connect_sock == -1) {
throw dpp::exception("Can't initialise request queue notifier sockets");
}
struct sockaddr_in in_client, out_client;
in_client.sin_addr.s_addr = out_client.sin_addr.s_addr = inet_addr("127.0.0.1");
in_client.sin_family = out_client.sin_family = AF_INET;
in_client.sin_port = htons(in_queue_port);
out_client.sin_port = htons(out_queue_port);
if ((connect(in_queue_connect_sock, (struct sockaddr *)&in_client, sizeof(in_client)) < 0) || (connect(out_queue_connect_sock, (struct sockaddr *)&out_client, sizeof(out_client)) < 0)) {
throw dpp::exception("Can't connect notifiers");
}
}
request_queue::~request_queue()
{
terminating = true;
in_thread->join();
out_thread->join();
delete in_thread;
delete out_thread;
}
void request_queue::in_loop()
{
int c = sizeof(struct sockaddr_in);
fd_set readfds;
timeval ts;
char n;
struct sockaddr_in client;
int notifier = accept(in_queue_listen_sock, (struct sockaddr *)&client, (socklen_t*)&c);
#ifndef _WIN32
close(in_queue_listen_sock);
#endif
while (!terminating) {
/* select for one second, waiting for new data */
FD_ZERO(&readfds);
FD_SET(notifier, &readfds);
ts.tv_sec = 1;
ts.tv_usec = 0;
int r = select(FD_SETSIZE, &readfds, 0, 0, &ts);
time_t now = time(nullptr);
if (r > 0 && FD_ISSET(notifier, &readfds)) {
if (recv(notifier, &n, 1, 0) > 0) {
/* New request to be sent! */
if (!globally_ratelimited) {
std::map<std::string, std::vector<http_request*>> requests_in_copy;
{
/* Make a safe copy within a mutex */
std::lock_guard<std::mutex> lock(in_mutex);
requests_in_copy = requests_in;
}
for (auto & bucket : requests_in_copy) {
for (auto req : bucket.second) {
http_request_completion_t rv;
auto currbucket = buckets.find(bucket.first);
if (currbucket != buckets.end()) {
/* Theres a bucket for this request. Check its status. If the bucket says to wait,
* skip all requests in this bucket till its ok.
*/
if (currbucket->second.remaining < 1) {
uint64_t wait = (currbucket->second.retry_after ? currbucket->second.retry_after : currbucket->second.reset_after);
if (time(NULL) > currbucket->second.timestamp + wait) {
/* Time has passed, we can process this bucket again. send its request. */
rv = req->Run(creator);
} else {
/* Time not up yet, emit signal and wait */
std::this_thread::sleep_for(std::chrono::milliseconds(50));
emit_in_queue_signal();
break;
}
} else {
/* There's limit remaining, we can just run the request */
rv = req->Run(creator);
}
} else {
/* No bucket for this endpoint yet. Just send it, and make one from its reply */
rv = req->Run(creator);
}
bucket_t newbucket;
newbucket.limit = rv.ratelimit_limit;
newbucket.remaining = rv.ratelimit_remaining;
newbucket.reset_after = rv.ratelimit_reset_after;
newbucket.retry_after = rv.ratelimit_retry_after;
newbucket.timestamp = time(NULL);
globally_ratelimited = rv.ratelimit_global;
if (globally_ratelimited) {
globally_limited_for = (newbucket.retry_after ? newbucket.retry_after : newbucket.reset_after);
}
buckets[req->endpoint] = newbucket;
/* Make a new entry in the completion list and notify */
{
std::lock_guard<std::mutex> lock(out_mutex);
http_request_completion_t* hrc = new http_request_completion_t();
*hrc = rv;
responses_out.push(std::make_pair(hrc, req));
emit_out_queue_signal();
}
}
}
{
std::lock_guard<std::mutex> lock(in_mutex);
bool again = false;
do {
again = false;
for (auto & bucket : requests_in) {
for (auto req = bucket.second.begin(); req != bucket.second.end(); ++req) {
if ((*req)->is_completed()) {
requests_in[bucket.first].erase(req);
again = true;
goto out; /* Only clean way out of a nested loop */
}
}
}
out:;
} while (again);
}
} else {
if (globally_limited_for > 0) {
std::this_thread::sleep_for(std::chrono::seconds(globally_limited_for));
globally_limited_for = 0;
}
globally_ratelimited = false;
emit_in_queue_signal();
}
}
}
}
close(notifier);
}
void request_queue::out_loop()
{
int c = sizeof(struct sockaddr_in);
fd_set readfds;
timeval ts;
char n;
struct sockaddr_in client;
SOCKET notifier = accept(out_queue_listen_sock, (struct sockaddr *)&client, (socklen_t*)&c);
#ifndef _WIN32
close(out_queue_listen_sock);
#endif
while (!terminating) {
/* select for one second, waiting for new data */
FD_ZERO(&readfds);
FD_SET(notifier, &readfds);
ts.tv_sec = 1;
ts.tv_usec = 0;
int r = select(FD_SETSIZE, &readfds, 0, 0, &ts);
time_t now = time(nullptr);
if (r > 0 && FD_ISSET(notifier, &readfds)) {
if (recv(notifier, &n, 1, 0) > 0) {
/* A request has been completed! */
std::pair<http_request_completion_t*, http_request*> queue_head = {};
{
std::lock_guard<std::mutex> lock(out_mutex);
if (responses_out.size()) {
queue_head = responses_out.front();
responses_out.pop();
}
}
if (queue_head.first && queue_head.second) {
queue_head.second->complete(*queue_head.first);
}
/* Queue deletions for 60 seconds from now */
responses_to_delete.insert(std::make_pair(now + 60, queue_head));
}
}
/* Check for deletable items every second regardless of select status */
while (responses_to_delete.size() && now >= responses_to_delete.begin()->first) {
delete responses_to_delete.begin()->second.first;
delete responses_to_delete.begin()->second.second;
responses_to_delete.erase(responses_to_delete.begin());
}
}
shutdown(notifier, 2);
#ifdef _WIN32
if (notifier >= 0 && notifier < FD_SETSIZE) {
closesocket(notifier);
}
#else
::close(notifier);
#endif
}
/* These only need to send a byte to notify the other end of something to do. any byte will do.
*/
void request_queue::emit_in_queue_signal()
{
send(in_queue_connect_sock, "X", 1, 0);
}
void request_queue::emit_out_queue_signal()
{
send(out_queue_connect_sock, "X", 1, 0);
}
/* Post a http_request into the queue */
void request_queue::post_request(http_request* req)
{
std::lock_guard<std::mutex> lock(in_mutex);
requests_in[req->endpoint].push_back(req);
emit_in_queue_signal();
}
std::string url_encode(const std::string &value) {
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (std::string::const_iterator i = value.begin(), n = value.end(); i != n; ++i) {
std::string::value_type c = (*i);
// Keep alphanumeric and other accepted characters intact
if (isalnum((unsigned char)c) || c == '-' || c == '_' || c == '.' || c == '~') {
escaped << c;
continue;
}
// Any other characters are percent-encoded
escaped << std::uppercase;
escaped << '%' << std::setw(2) << int((unsigned char) c);
escaped << std::nouppercase;
}
return escaped.str();
}
};

238
vendor/DPP/src/dpp/role.cpp vendored Normal file
View File

@ -0,0 +1,238 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/discordevents.h>
#include <dpp/stringops.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
role::role() :
managed(),
guild_id(0),
colour(0),
position(0),
permissions(0),
flags(0),
integration_id(0),
bot_id(0)
{
}
role::~role()
{
}
role& role::fill_from_json(snowflake _guild_id, nlohmann::json* j)
{
this->guild_id = _guild_id;
this->name = StringNotNull(j, "name");
this->id = SnowflakeNotNull(j, "id");
this->colour = Int32NotNull(j, "color");
this->position = Int8NotNull(j, "position");
this->permissions = SnowflakeNotNull(j, "permissions");
this->flags |= BoolNotNull(j, "hoist") ? dpp::r_hoist : 0;
this->flags |= BoolNotNull(j, "managed") ? dpp::r_managed : 0;
this->flags |= BoolNotNull(j, "mentionable") ? dpp::r_mentionable : 0;
if (j->find("tags") != j->end()) {
auto t = (*j)["tags"];
this->flags |= BoolNotNull(&t, "premium_subscriber") ? dpp::r_premium_subscriber : 0;
this->bot_id = SnowflakeNotNull(&t, "bot_id");
this->integration_id = SnowflakeNotNull(&t, "integration_id");
}
return *this;
}
std::string role::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(id);
}
if (colour) {
j["color"] = colour;
}
j["position"] = position;
j["permissions"] = permissions;
j["hoist"] = is_hoisted();
j["mentionable"] = is_mentionable();
return j.dump();
}
bool role::is_hoisted() const {
return this->flags & dpp::r_hoist;
}
bool role::is_mentionable() const {
return this->flags & dpp::r_mentionable;
}
bool role::is_managed() const {
return this->flags & dpp::r_managed;
}
bool role::has_create_instant_invite() const {
return ((this->permissions & p_administrator) | (this->permissions & p_create_instant_invite));
}
bool role::has_kick_members() const {
return ((this->permissions & p_administrator) | (this->permissions & p_kick_members));
}
bool role::has_ban_members() const {
return ((this->permissions & p_administrator) | (this->permissions & p_ban_members));
}
bool role::has_administrator() const {
return (this->permissions & p_administrator);
}
bool role::has_manage_channels() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_channels));
}
bool role::has_manage_guild() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_guild));
}
bool role::has_add_reactions() const {
return ((this->permissions & p_administrator) | (this->permissions & p_add_reactions));
}
bool role::has_view_audit_log() const {
return ((this->permissions & p_administrator) | (this->permissions & p_view_audit_log));
}
bool role::has_priority_speaker() const {
return ((this->permissions & p_administrator) | (this->permissions & p_priority_speaker));
}
bool role::has_stream() const {
return ((this->permissions & p_administrator) | (this->permissions & p_stream));
}
bool role::has_view_channel() const {
return ((this->permissions & p_administrator) | (this->permissions & p_view_channel));
}
bool role::has_send_messages() const {
return ((this->permissions & p_administrator) | (this->permissions & p_send_messages));
}
bool role::has_send_tts_messages() const {
return ((this->permissions & p_administrator) | (this->permissions & p_send_tts_messages));
}
bool role::has_manage_messages() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_messages));
}
bool role::has_embed_links() const {
return ((this->permissions & p_administrator) | (this->permissions & p_embed_links));
}
bool role::has_attach_files() const {
return ((this->permissions & p_administrator) | (this->permissions & p_attach_files));
}
bool role::has_read_message_history() const {
return ((this->permissions & p_administrator) | (this->permissions & p_read_message_history));
}
bool role::has_mention_everyone() const {
return ((this->permissions & p_administrator) | (this->permissions & p_mention_everyone));
}
bool role::has_use_external_emojis() const {
return ((this->permissions & p_administrator) | (this->permissions & p_use_external_emojis));
}
bool role::has_view_guild_insights() const {
return ((this->permissions & p_administrator) | (this->permissions & p_view_guild_insights));
}
bool role::has_connect() const {
return ((this->permissions & p_administrator) | (this->permissions & p_connect));
}
bool role::has_speak() const {
return ((this->permissions & p_administrator) | (this->permissions & p_speak));
}
bool role::has_mute_members() const {
return ((this->permissions & p_administrator) | (this->permissions & p_mute_members));
}
bool role::has_deafen_members() const {
return ((this->permissions & p_administrator) | (this->permissions & p_deafen_members));
}
bool role::has_move_members() const {
return ((this->permissions & p_administrator) | (this->permissions & p_move_members));
}
bool role::has_use_vad() const {
return ((this->permissions & p_administrator) | (this->permissions & p_use_vad));
}
bool role::has_change_nickname() const {
return ((this->permissions & p_administrator) | (this->permissions & p_change_nickname));
}
bool role::has_manage_nicknames() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_nicknames));
}
bool role::has_manage_roles() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_roles));
}
bool role::has_manage_webhooks() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_webhooks));
}
bool role::has_manage_emojis() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_emojis));
}
bool role::has_use_slash_commands() const {
return ((this->permissions & p_administrator) | (this->permissions & p_use_slash_commands));
}
bool role::has_request_to_speak() const {
return ((this->permissions & p_administrator) | (this->permissions & p_request_to_speak));
}
bool role::has_manage_threads() const {
return ((this->permissions & p_administrator) | (this->permissions & p_manage_threads));
}
bool role::has_use_public_threads() const {
return ((this->permissions & p_administrator) | (this->permissions & p_use_public_threads));
}
bool role::has_use_private_threads() const {
return ((this->permissions & p_administrator) | (this->permissions & p_use_private_threads));
}
};

332
vendor/DPP/src/dpp/slashcommand.cpp vendored Normal file
View File

@ -0,0 +1,332 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/slashcommand.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
#include <iostream>
namespace dpp {
using json = nlohmann::json;
slashcommand::slashcommand() : managed(), default_permission(true), type(ctxm_none) {
}
slashcommand::~slashcommand() {
}
slashcommand& slashcommand::fill_from_json(nlohmann::json* j) {
id = SnowflakeNotNull(j, "id");
name = StringNotNull(j, "name");
description = StringNotNull(j, "description");
return *this;
}
void to_json(json& j, const command_option_choice& choice) {
j["name"] = choice.name;
if (std::holds_alternative<int32_t>(choice.value)) {
j["value"] = std::get<int32_t>(choice.value);
} else if (std::holds_alternative<bool>(choice.value)) {
j["value"] = std::get<bool>(choice.value);
} else if (std::holds_alternative<snowflake>(choice.value)) {
j["value"] = std::to_string(std::get<uint64_t>(choice.value));
} else {
j["value"] = std::get<std::string>(choice.value);
}
}
void to_json(json& j, const command_option& opt) {
j["name"] = opt.name;
j["description"] = opt.description;
j["type"] = opt.type;
j["required"] = opt.required;
if (opt.options.size()) {
j["options"] = json();
for (const auto& opt : opt.options) {
json jopt = opt;
j["options"].push_back(jopt);
}
}
if (opt.choices.size()) {
j["choices"] = json();
for (const auto& choice : opt.choices) {
json jchoice = choice;
j["choices"].push_back(jchoice);
}
}
}
void to_json(nlohmann::json& j, const command_permission& cp) {
j["id"] = std::to_string(cp.id);
j["type"] = cp.type;
j["permission"] = cp.permission;
}
void to_json(nlohmann::json& j, const guild_command_permissions& gcp) {
j["id"] = std::to_string(gcp.id);
j["application_id"] = std::to_string(gcp.application_id);
j["guild_id"] = std::to_string(gcp.guild_id);
j["permissions"] = gcp.permissions;
}
void to_json(json& j, const slashcommand& p) {
j["name"] = p.name;
if (p.type != ctxm_user && p.type != ctxm_message) {
j["description"] = p.description;
}
/* Only send this if set to something other than ctxm_none */
if (p.type != ctxm_none) {
j["type"] = p.type;
}
if (p.type != ctxm_user && p.type != ctxm_message) {
if (p.options.size()) {
j["options"] = json();
for (const auto& opt : p.options) {
json jopt = opt;
j["options"].push_back(jopt);
}
}
}
if(p.permissions.size()) {
j["permissions"] = json();
for(const auto& perm : p.permissions) {
json jperm = perm;
j["permissions"].push_back(jperm);
}
}
j["default_permission"] = p.default_permission;
}
std::string slashcommand::build_json(bool with_id) const {
json j = *this;
if (with_id) {
j["id"] = std::to_string(id);
}
return j.dump();
}
slashcommand& slashcommand::set_type(slashcommand_contextmenu_type t) {
type = t;
return *this;
}
slashcommand& slashcommand::set_name(const std::string &n) {
name = n;
return *this;
}
slashcommand& slashcommand::set_description(const std::string &d) {
description = d;
return *this;
}
slashcommand& slashcommand::set_application_id(snowflake i) {
application_id = i;
return *this;
}
slashcommand& slashcommand::add_permission(const command_permission& p) {
this->permissions.push_back(p);
return *this;
}
slashcommand& slashcommand::disable_default_permissions() {
this->default_permission = false;
return *this;
}
command_option_choice::command_option_choice(const std::string &n, command_value v) : name(n), value(v)
{
}
command_option::command_option(command_option_type t, const std::string &n, const std::string &d, bool r) :
type(t), name(n), description(d), required(r)
{
}
command_option& command_option::add_choice(const command_option_choice &o)
{
choices.push_back(o);
return *this;
}
command_option& command_option::add_option(const command_option &o)
{
options.push_back(o);
return *this;
}
slashcommand& slashcommand::add_option(const command_option &o)
{
options.push_back(o);
return *this;
}
interaction& interaction::fill_from_json(nlohmann::json* j) {
j->get_to(*this);
return *this;
}
std::string interaction::build_json(bool with_id) const {
return "";
}
void from_json(const nlohmann::json& j, command_data_option& cdo) {
cdo.name = StringNotNull(&j, "name");
cdo.type = (command_option_type)Int8NotNull(&j, "type");
if (j.contains("options") && !j.at("options").is_null()) {
j.at("options").get_to(cdo.options);
}
/* If there's a target ID, define it */
if (j.contains("target_id") && !j.at("target_id").is_null()) {
cdo.target_id = (dpp::snowflake)SnowflakeNotNull(&j, "target_id");
}
if (j.contains("value") && !j.at("value").is_null()) {
switch (cdo.type) {
case co_boolean:
cdo.value = j.at("value").get<bool>();
break;
case co_channel:
case co_role:
case co_user:
cdo.value = SnowflakeNotNull(&j, "value");
break;
case co_integer:
cdo.value = j.at("value").get<int32_t>();
break;
case co_string:
cdo.value = j.at("value").get<std::string>();
break;
case co_sub_command:
case co_sub_command_group:
/* Silences warning on clang, handled elsewhere */
break;
}
}
}
void from_json(const nlohmann::json& j, command_interaction& ci) {
ci.id = SnowflakeNotNull(&j, "id");
ci.name = StringNotNull(&j, "name");
if (j.contains("options") && !j.at("options").is_null()) {
j.at("options").get_to(ci.options);
}
}
void from_json(const nlohmann::json& j, component_interaction& bi) {
bi.component_type = Int8NotNull(&j, "component_type");
bi.custom_id = StringNotNull(&j, "custom_id");
if (bi.component_type == cotype_select && j.find("values") != j.end()) {
/* Get values */
for (auto& entry : j["values"]) {
bi.values.push_back(entry.get<std::string>());
}
}
}
void from_json(const nlohmann::json& j, interaction& i) {
i.id = SnowflakeNotNull(&j, "id");
i.application_id = SnowflakeNotNull(&j, "application_id");
i.channel_id = SnowflakeNotNull(&j, "channel_id");
i.guild_id = SnowflakeNotNull(&j, "guild_id");
if (j.find("message") != j.end()) {
const json& m = j["message"];
SetSnowflakeNotNull(&m, "id", i.message_id);
}
i.type = Int8NotNull(&j, "type");
i.token = StringNotNull(&j, "token");
i.version = Int8NotNull(&j, "version");
if (j.contains("member") && !j.at("member").is_null()) {
j.at("member").get_to(i.member);
if (j.at("member").contains("user") && !j.at("member").at("user").is_null()) {
j.at("member").at("user").get_to(i.usr);
}
}
if (j.contains("data") && !j.at("data").is_null()) {
if (i.type == it_application_command) {
command_interaction ci;
j.at("data").get_to(ci);
i.data = ci;
} else if (i.type == it_component_button) {
component_interaction bi;
j.at("data").get_to(bi);
i.data = bi;
}
}
}
interaction_response::interaction_response() {
msg = new message();
}
interaction_response::~interaction_response() {
delete msg;
}
interaction_response::interaction_response(interaction_response_type t, const message& m) : interaction_response() {
type = t;
*msg = m;
}
interaction_response& interaction_response::fill_from_json(nlohmann::json* j) {
type = (interaction_response_type)Int8NotNull(j, "type");
if (j->find("data") != j->end()) {
msg->fill_from_json(&((*j)["data"]));
}
return *this;
}
std::string interaction_response::build_json() const {
json j;
json msg_json = json::parse(msg->build_json(false, true));
j["type"] = this->type;
auto cid = msg_json.find("channel_id");
if (cid != msg_json.end()) {
msg_json.erase(cid);
}
j["data"] = msg_json;
return j.dump();
}
};

408
vendor/DPP/src/dpp/sslclient.cpp vendored Normal file
View File

@ -0,0 +1,408 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <errno.h>
#ifdef _WIN32
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <io.h>
#pragma comment(lib,"ws2_32")
#else
#include <resolv.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#endif
#ifdef OPENSSL_SYS_WIN32
#undef X509_NAME
#undef X509_EXTENSIONS
#undef X509_CERT_PAIR
#undef PKCS7_ISSUER_AND_SERIAL
#undef OCSP_REQUEST
#undef OCSP_RESPONSE
#endif
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <exception>
#include <string>
#include <iostream>
#include <fmt/format.h>
#include <dpp/sslclient.h>
#include <dpp/discord.h>
#include <dpp/dispatcher.h>
namespace dpp {
class opensslcontext {
public:
/** OpenSSL session */
SSL* ssl;
/** OpenSSL context */
SSL_CTX* ctx;
};
/* You'd think that we would get better performance with a bigger buffer, but SSL frames are 16k each.
* SSL_read in non-blocking mode will only read 16k at a time. There's no point in a bigger buffer as
* it'd go unused.
*/
#define BUFSIZZ 1024 * 16
const int ERROR_STATUS = -1;
ssl_client::ssl_client(const std::string &_hostname, const std::string &_port) : last_tick(time(NULL)), hostname(_hostname), port(_port), bytes_in(0), bytes_out(0)
{
#ifndef WIN32
signal(SIGALRM, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGCHLD, SIG_IGN);
signal(SIGXFSZ, SIG_IGN);
#endif
ssl = new opensslcontext();
Connect();
}
/* SSL Client constructor throws std::runtime_error if it can't connect to the host */
void ssl_client::Connect()
{
/* Initial connection is done in blocking mode. There is a timeout on it. */
nonblocking = false;
const SSL_METHOD *method = TLS_client_method(); /* Create new client-method instance */
/* Create SSL context */
ssl->ctx = SSL_CTX_new(method);
if (ssl->ctx == nullptr)
throw dpp::exception("Failed to create SSL client context!");
/* Create SSL session */
ssl->ssl = SSL_new(ssl->ctx);
if (ssl->ssl == nullptr)
throw dpp::exception("SSL_new failed!");
/* Resolve hostname to IP */
struct hostent *host;
if ((host = gethostbyname(hostname.c_str())) == nullptr)
throw dpp::exception(fmt::format("Couldn't resolve hostname '{}'", hostname));
struct addrinfo hints = {0}, *addrs;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int status = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &addrs);
if (status != 0)
throw dpp::exception(fmt::format("getaddrinfo (host={}, port={}): ", hostname, port, gai_strerror(status)));
/* Attempt each address in turn, if there are multiple IP addresses on the hostname */
int err;
for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next) {
sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sfd == ERROR_STATUS) {
err = errno;
continue;
} else if (connect(sfd, addr->ai_addr, addr->ai_addrlen) == 0) {
break;
}
err = errno;
shutdown(sfd, 2);
#ifdef _WIN32
if (sfd >= 0 && sfd < FD_SETSIZE) {
closesocket(sfd);
}
#else
::close(sfd);
#endif
sfd = ERROR_STATUS;
}
freeaddrinfo(addrs);
/* Check if none of the IPs yielded a valid connection */
if (sfd == ERROR_STATUS)
throw dpp::exception(strerror(err));
/* We're good to go - hand the fd over to openssl */
SSL_set_fd(ssl->ssl, sfd);
status = SSL_connect(ssl->ssl);
if (status != 1) {
throw dpp::exception("SSL_connect error");
}
this->cipher = SSL_get_cipher(ssl->ssl);
}
void ssl_client::write(const std::string &data)
{
/* If we are in nonblocking mode, append to the buffer,
* otherwise just use SSL_write directly. The only time we
* use SSL_write directly is during connection before the
* ReadLoop is called, which allows for guaranteed simple
* lock-step delivery e.g. for HTTP header negotiation
*/
if (nonblocking) {
obuffer += data;
} else {
SSL_write(ssl->ssl, data.data(), data.length());
}
}
void ssl_client::one_second_timer()
{
}
std::string ssl_client::get_cipher() {
return cipher;
}
void ssl_client::log(dpp::loglevel severity, const std::string &msg) const
{
}
void ssl_client::read_loop()
{
/* The read loop is non-blocking using select(). This method
* cannot read while it is waiting for write, or write while it is
* waiting for read. This is a limitation of the openssl libraries,
* as SSL is sent and received in low level ~16k frames which must
* be synchronised and ordered correctly. Attempting to send while
* we need another frame or receive while we are due to send a frame
* would cause the protocol to break.
*/
int width;
int r = 0;
size_t ClientToServerLength = 0, ClientToServerOffset = 0;
bool read_blocked_on_write = false, write_blocked_on_read = false,read_blocked = false;
fd_set readfds, writefds, efds;
char ClientToServerBuffer[BUFSIZZ], ServerToClientBuffer[BUFSIZZ];
/* Make the socket nonblocking */
#ifdef _WIN32
u_long mode = 1;
int result = ioctlsocket(sfd, FIONBIO, &mode);
if (result != NO_ERROR)
throw dpp::exception("Can't switch socket to non-blocking mode!");
#else
int ofcmode;
ofcmode = fcntl(sfd, F_GETFL, 0);
ofcmode |= O_NDELAY;
if (fcntl(sfd, F_SETFL, ofcmode)) {
throw dpp::exception("Can't switch socket to non-blocking mode!");
}
#endif
nonblocking = true;
width = sfd + 1;
try {
/* Loop until there is a socket error */
while(true) {
if (last_tick != time(NULL)) {
this->one_second_timer();
last_tick = time(NULL);
}
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_ZERO(&efds);
FD_SET(sfd,&readfds);
FD_SET(sfd,&efds);
if (custom_readable_fd && custom_readable_fd() >= 0) {
int cfd = custom_readable_fd();
FD_SET(cfd, &readfds);
FD_SET(cfd, &efds);
}
if (custom_writeable_fd && custom_writeable_fd() >= 0) {
int cfd = custom_writeable_fd();
FD_SET(cfd, &writefds);
}
/* If we're waiting for a read on the socket don't try to write to the server */
if (ClientToServerLength || obuffer.length() || read_blocked_on_write) {
FD_SET(sfd,&writefds);
}
timeval ts;
ts.tv_sec = 0;
ts.tv_usec = 50000;
r = select(FD_SETSIZE, &readfds, &writefds, &efds, &ts);
if (r == 0)
continue;
if (custom_writeable_fd && FD_ISSET(custom_writeable_fd(), &writefds)) {
custom_writeable_ready();
}
if (custom_readable_fd && FD_ISSET(custom_readable_fd(), &readfds)) {
custom_readable_ready();
}
if (custom_readable_fd && FD_ISSET(custom_readable_fd(), &efds)) {
}
if (FD_ISSET(sfd, &efds)) {
this->log(dpp::ll_error, fmt::format("Error on SSL connection: {}", strerror(errno)));
return;
}
/* Now check if there's data to read */
if((FD_ISSET(sfd,&readfds) && !write_blocked_on_read) || (read_blocked_on_write && FD_ISSET(sfd,&writefds))) {
do {
read_blocked_on_write = false;
read_blocked = false;
r = SSL_read(ssl->ssl,ServerToClientBuffer,BUFSIZZ);
int e = SSL_get_error(ssl->ssl,r);
switch (e) {
case SSL_ERROR_NONE:
/* Data received, add it to the buffer */
buffer.append(ServerToClientBuffer, r);
this->handle_buffer(buffer);
bytes_in += r;
break;
case SSL_ERROR_ZERO_RETURN:
/* End of data */
SSL_shutdown(ssl->ssl);
return;
break;
case SSL_ERROR_WANT_READ:
read_blocked = true;
break;
/* We get a WANT_WRITE if we're trying to rehandshake and we block on a write during that rehandshake.
* We need to wait on the socket to be writeable but reinitiate the read when it is
*/
case SSL_ERROR_WANT_WRITE:
read_blocked_on_write = true;
break;
default:
return;
break;
}
/* We need a check for read_blocked here because SSL_pending() doesn't work properly during the
* handshake. This check prevents a busy-wait loop around SSL_read()
*/
} while (SSL_pending(ssl->ssl) && !read_blocked);
}
/* Check for input on the sendq */
if (obuffer.length() && ClientToServerLength == 0) {
memcpy(&ClientToServerBuffer, obuffer.data(), obuffer.length() > BUFSIZZ ? BUFSIZZ : obuffer.length());
ClientToServerLength = obuffer.length() > BUFSIZZ ? BUFSIZZ : obuffer.length();
obuffer = obuffer.substr(ClientToServerLength, obuffer.length());
ClientToServerOffset = 0;
}
/* If the socket is writeable... */
if ((FD_ISSET(sfd,&writefds) && ClientToServerLength) || (write_blocked_on_read && FD_ISSET(sfd,&readfds))) {
write_blocked_on_read = false;
/* Try to write */
r = SSL_write(ssl->ssl, ClientToServerBuffer + ClientToServerOffset, ClientToServerLength);
switch(SSL_get_error(ssl->ssl,r)) {
/* We wrote something */
case SSL_ERROR_NONE:
ClientToServerLength -= r;
ClientToServerOffset += r;
bytes_out += r;
break;
/* We would have blocked */
case SSL_ERROR_WANT_WRITE:
break;
/* We get a WANT_READ if we're trying to rehandshake and we block onwrite during the current connection.
* We need to wait on the socket to be readable but reinitiate our write when it is
*/
case SSL_ERROR_WANT_READ:
write_blocked_on_read = true;
break;
/* Some other error */
default:
return;
break;
}
}
}
}
catch (const std::exception &e) {
log(ll_warning, fmt::format("Read loop ended: {}", e.what()));
}
}
uint64_t ssl_client::get_bytes_out()
{
return bytes_out;
}
uint64_t ssl_client::get_bytes_in()
{
return bytes_in;
}
bool ssl_client::handle_buffer(std::string &buffer)
{
return true;
}
void ssl_client::close()
{
if (ssl->ssl) {
SSL_free(ssl->ssl);
ssl->ssl = nullptr;
}
shutdown(sfd, 2);
#ifdef _WIN32
if (sfd >= 0 && sfd < FD_SETSIZE) {
closesocket(sfd);
}
#else
::close(sfd);
#endif
if (ssl->ctx) {
SSL_CTX_free(ssl->ctx);
ssl->ctx = nullptr;
}
sfd = -1;
obuffer.clear();
buffer.clear();
}
ssl_client::~ssl_client()
{
this->close();
delete ssl;
}
};

183
vendor/DPP/src/dpp/user.cpp vendored Normal file
View File

@ -0,0 +1,183 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/discordevents.h>
#include <dpp/nlohmann/json.hpp>
#include <fmt/format.h>
using json = nlohmann::json;
/* A mapping of discord's flag values to our bitmap (theyre different bit positions to fit other stuff in) */
std::map<uint32_t, dpp::user_flags> usermap = {
{ 1 << 0, dpp::u_discord_employee },
{ 1 << 1, dpp::u_partnered_owner },
{ 1 << 2, dpp::u_hypesquad_events },
{ 1 << 3, dpp::u_bughunter_1 },
{ 1 << 6, dpp::u_house_bravery },
{ 1 << 7, dpp::u_house_brilliance },
{ 1 << 8, dpp::u_house_balanace },
{ 1 << 9, dpp::u_early_supporter },
{ 1 << 10, dpp::u_team_user },
{ 1 << 14, dpp::u_bughunter_2 },
{ 1 << 16, dpp::u_verified_bot },
{ 1 << 17, dpp::u_verified_bot_dev },
{ 1 << 18, dpp::u_certified_moderator }
};
namespace dpp {
user::user() :
managed(),
discriminator(0),
flags(0),
refcount(1)
{
}
user::~user()
{
}
std::string user::get_avatar_url() const {
/* XXX: Discord were supposed to change their CDN over to discord.com, they havent.
* At some point in the future this URL *will* change!
*/
return fmt::format("https://cdn.discordapp.com/avatars/{}/{}{}.{}",
this->id,
(has_animated_icon() ? "a_" : ""),
this->avatar.to_string(),
(has_animated_icon() ? "gif" : "png")
);
}
bool user::is_bot() const {
return this->flags & u_bot;
}
bool user::is_system() const {
return this->flags & u_system;
}
bool user::is_mfa_enabled() const {
return this->flags & u_mfa_enabled;
}
bool user::is_verified() const {
return this->flags & u_verified;
}
bool user::has_nitro_full() const {
return this->flags & u_nitro_full;
}
bool user::has_nitro_classic() const {
return this->flags & u_nitro_classic;
}
bool user::is_discord_employee() const {
return this->flags & u_discord_employee;
}
bool user::is_partnered_owner() const {
return this->flags & u_partnered_owner;
}
bool user::has_hypesquad_events() const {
return this->flags & u_hypesquad_events;
}
bool user::is_bughunter_1() const {
return this->flags & u_bughunter_1;
}
bool user::is_house_bravery() const {
return this->flags & u_house_bravery;
}
bool user::is_house_brilliance() const {
return this->flags & u_house_brilliance;
}
bool user::is_house_balanace() const {
return this->flags & u_house_balanace;
}
bool user::is_early_supporter() const {
return this->flags & u_early_supporter;
}
bool user::is_team_user() const {
return this->flags & u_team_user;
}
bool user::is_bughunter_2() const {
return this->flags & u_bughunter_2;
}
bool user::is_verified_bot() const {
return this->flags & u_verified_bot;
}
bool user::is_verified_bot_dev() const {
return this->flags & u_verified_bot_dev;
}
bool user::is_certified_moderator() const {
return this->flags & u_certified_moderator;
}
bool user::has_animated_icon() const {
return this->flags & u_animated_icon;
}
user& user::fill_from_json(json* j) {
j->get_to(*this);
return *this;
}
void from_json(const nlohmann::json& j, user& u) {
u.id = SnowflakeNotNull(&j, "id");
u.username = StringNotNull(&j, "username");
std::string av = StringNotNull(&j, "avatar");
if (av.length() > 2 && av.substr(0, 2) == "a_") {
av = av.substr(2, av.length());
u.flags |= u_animated_icon;
}
u.avatar = av;
u.discriminator = SnowflakeNotNull(&j, "discriminator");
u.flags |= BoolNotNull(&j, "bot") ? dpp::u_bot : 0;
u.flags |= BoolNotNull(&j, "system") ? dpp::u_system : 0;
u.flags |= BoolNotNull(&j, "mfa_enabled") ? dpp::u_mfa_enabled : 0;
u.flags |= BoolNotNull(&j, "verified") ? dpp::u_verified : 0;
u.flags |= Int8NotNull(&j, "premium_type") == 1 ? dpp::u_nitro_classic : 0;
u.flags |= Int8NotNull(&j, "premium_type") == 2 ? dpp::u_nitro_full : 0;
uint32_t flags = Int32NotNull(&j, "flags");
for (auto & flag : usermap) {
if (flags & flag.first) {
u.flags |= flag.second;
}
}
}
};

274
vendor/DPP/src/dpp/utility.cpp vendored Normal file
View File

@ -0,0 +1,274 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/stringops.h>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <thread>
#include <functional>
#include <chrono>
#include <ctime>
#include <algorithm>
#include <iomanip>
#include <fmt/format.h>
using namespace std::literals;
namespace dpp {
namespace utility {
double time_f()
{
using namespace std::chrono;
auto tp = system_clock::now() + 0ns;
return tp.time_since_epoch().count() / 1000000000.0;
}
bool has_voice() {
#if HAVE_VOICE
return true;
#else
return false;
#endif
}
std::string current_date_time() {
#ifdef _WIN32
std::time_t curr_time = time(nullptr);
return std::ctime(&curr_time);
#else
auto t = std::time(nullptr);
struct tm timedata;
localtime_r(&t, &timedata);
std::stringstream s;
s << std::put_time(&timedata, "%Y-%m-%d %H:%M:%S");
return s.str();
#endif
}
std::string loglevel(dpp::loglevel in) {
switch (in) {
case dpp::ll_trace: return "TRACE";
case dpp::ll_debug: return "DEBUG";
case dpp::ll_info: return "INFO";
case dpp::ll_warning: return "WARN";
case dpp::ll_error: return "ERROR";
case dpp::ll_critical: return "CRIT";
default: return "???";
}
}
uptime::uptime() : days(0), hours(0), mins(0), secs(0) {
}
uptime::uptime(time_t diff) : uptime() {
days = (uint16_t)(diff / (3600 * 24));
hours = (uint8_t)(diff % (3600 * 24) / 3600);
mins = (uint8_t)(diff % 3600 / 60);
secs = (uint8_t)(diff % 60);
}
std::string uptime::to_string() {
if (hours == 0 && days == 0) {
return fmt::format("{:02d}:{:02d}", mins, secs);
} else {
return fmt::format("{}{:02d}:{:02d}:{:02d}", (days ? fmt::format("{} day{}, ", days, (days > 1 ? "s" : "")) : ""), hours, mins, secs);
}
}
uint64_t uptime::to_secs() {
return secs + (mins * 60) + (hours * 60 * 60) + (days * 60 * 60 * 24);
}
uint64_t uptime::to_msecs() {
return to_secs() * 1000;
}
iconhash::iconhash() : first(0), second(0) {
}
void iconhash::set(const std::string &hash) {
if (hash.empty()) { // Clear values if empty hash
first = second = 0;
return;
}
if (hash.length() != 32)
throw std::length_error("iconhash must be exactly 32 characters in length");
this->first = from_string<uint64_t>(hash.substr(0, 16), std::hex);
this->second = from_string<uint64_t>(hash.substr(16, 16), std::hex);
}
iconhash::iconhash(const std::string &hash) {
set(hash);
}
iconhash& iconhash::operator=(const std::string &assignment) {
set(assignment);
return *this;
}
std::string iconhash::to_string() const {
if (first == 0 && second == 0)
return "";
else
return fmt::format("{:016x}{:016x}", this->first, this->second);
}
void debug_dump(uint8_t* data, size_t length) {
size_t addr = (size_t)data;
size_t extra = addr % 16;
if (extra != 0) {
addr -= extra;
std::cout << fmt::format("[{:016X}] : ", addr);
}
for (size_t n = 0; n < extra; ++n) {
std::cout << "-- ";
}
for (uint8_t* ptr = data; ptr < data + length; ++ptr) {
if (((size_t)ptr % 16) == 0) {
std::cout << fmt::format("\n[{:016X}] : ", (size_t)ptr);
}
std::cout << fmt::format("{:02X} ", *ptr);
}
std::cout << "\n";
}
std::string bytes(uint64_t c) {
if (c > 1099511627776) { // 1TB
return fmt::format("{:.2f}T", (c / 1099511627776.0));
} else if (c > 1073741824) { // 1GB
return fmt::format("{:.2f}G", (c / 1073741824.0));
} else if (c > 1048576) { // 1MB
return fmt::format("{:.2f}M", (c / 1048576.0));
} else if (c > 1024) { // 1KB
return fmt::format("{:.2f}K", (c / 1024.0));
} else { // Bytes
return std::to_string(c);
}
}
void exec(const std::string& cmd, std::vector<std::string> parameters, cmd_result_t callback) {
#ifndef _WIN32
auto t = std::thread([cmd, parameters, callback]() {
std::array<char, 128> buffer;
std::vector<std::string> my_parameters = parameters;
std::string result;
std::stringstream cmd_and_parameters;
cmd_and_parameters << cmd;
for (auto & parameter : my_parameters) {
cmd_and_parameters << " " << std::quoted(parameter);
}
/* Capture stderr */
cmd_and_parameters << " 2>&1";
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd_and_parameters.str().c_str(), "r"), pclose);
if (!pipe) {
return "";
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
result += buffer.data();
}
if (callback) {
callback(result);
}
return "";
});
t.detach();
#endif
}
size_t utf8len(const std::string &str)
{
size_t i = 0, iBefore = 0, count = 0;
const char* s = str.c_str();
if (*s == 0)
return 0;
while (s[i] > 0) {
ascii:
i++;
}
count += i - iBefore;
while (s[i]) {
if (s[i] > 0) {
iBefore = i;
goto ascii;
} else {
switch (0xF0 & s[i]) {
case 0xE0:
i += 3;
break;
case 0xF0:
i += 4;
break;
default:
i += 2;
break;
}
}
count++;
}
return count;
}
std::string utf8substr(const std::string& str, std::string::size_type start, std::string::size_type leng)
{
if (leng == 0) {
return "";
}
if (start == 0 && leng >= utf8len(str)) {
return str;
}
std::string::size_type i, ix, q, min = std::string::npos, max = std::string::npos;
for (q = 0, i = 0, ix = str.length(); i < ix; i++, q++)
{
if (q == start)
min = i;
if (q <= start + leng || leng == std::string::npos)
max = i;
unsigned char c = (unsigned char)str[i];
if (c < 0x80)
i += 0;
else if ((c & 0xE0) == 0xC0)
i += 1;
else if ((c & 0xF0) == 0xE0)
i += 2;
else if ((c & 0xF8) == 0xF0)
i += 3;
else
return ""; //invalid utf8
}
if (q <= start + leng || leng == std::string::npos)
max = i;
if (min == std::string::npos || max == std::string::npos)
return "";
return str.substr(min, max);
}
};
};

80
vendor/DPP/src/dpp/voiceregion.cpp vendored Normal file
View File

@ -0,0 +1,80 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/voiceregion.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
namespace dpp {
using json = nlohmann::json;
voiceregion::voiceregion() : flags(0)
{
}
voiceregion::~voiceregion() {
}
voiceregion& voiceregion::fill_from_json(nlohmann::json* j) {
id = StringNotNull(j, "id");
name = StringNotNull(j, "id");
if (BoolNotNull(j, "optimal"))
flags |= v_optimal;
if (BoolNotNull(j, "deprecated"))
flags |= v_deprecated;
if (BoolNotNull(j, "custom"))
flags |= v_custom;
if (BoolNotNull(j, "vip"))
flags |= v_vip;
return *this;
}
std::string voiceregion::build_json() const {
return json({
{ "id", id },
{ "name", name },
{ "optimal", is_optimal() },
{ "deprecated", is_deprecated() },
{ "custom", is_custom() },
{ "vip", is_vip() }
}).dump();
}
bool voiceregion::is_optimal() const {
return flags & v_optimal;
}
bool voiceregion::is_deprecated() const {
return flags & v_deprecated;
}
bool voiceregion::is_custom() const {
return flags & v_custom;
}
bool voiceregion::is_vip() const {
return flags & v_vip;
}
};

91
vendor/DPP/src/dpp/voicestate.cpp vendored Normal file
View File

@ -0,0 +1,91 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/discord.h>
#include <dpp/discordevents.h>
#include <dpp/nlohmann/json.hpp>
using json = nlohmann::json;
namespace dpp {
voicestate::voicestate() : shard(nullptr), guild_id(0), channel_id(0), user_id(0)
{
}
voicestate::~voicestate() {
}
voicestate& voicestate::fill_from_json(nlohmann::json* j) {
guild_id = SnowflakeNotNull(j, "guild_id");
channel_id = SnowflakeNotNull(j, "channel_id");
user_id = SnowflakeNotNull(j, "user_id");
session_id = StringNotNull(j, "session_id");
flags = 0;
if (BoolNotNull(j, "deaf"))
flags |= vs_deaf;
if (BoolNotNull(j, "mute"))
flags |= vs_mute;
if (BoolNotNull(j, "self_mute"))
flags |= vs_self_mute;
if (BoolNotNull(j, "self_deaf"))
flags |= vs_self_deaf;
if (BoolNotNull(j, "self_stream"))
flags |= vs_self_stream;
if (BoolNotNull(j, "self_video"))
flags |= vs_self_video;
if (BoolNotNull(j, "supress"))
flags |= vs_supress;
return *this;
}
bool voicestate::is_deaf() const {
return flags & vs_deaf;
}
bool voicestate::is_mute() const {
return flags & vs_mute;
}
bool voicestate::is_self_mute() const {
return flags & vs_self_mute;
}
bool voicestate::is_self_deaf() const {
return flags & vs_self_deaf;
}
bool voicestate::self_stream() const {
return flags & vs_self_stream;
}
bool voicestate::self_video() const {
return flags & vs_self_video;
}
bool voicestate::is_supressed() const {
return flags & vs_supress;
}
std::string voicestate::build_json() const {
return json({}).dump();
}
};

97
vendor/DPP/src/dpp/webhook.cpp vendored Normal file
View File

@ -0,0 +1,97 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <dpp/webhook.h>
#include <dpp/discordevents.h>
#include <dpp/discord.h>
#include <dpp/nlohmann/json.hpp>
#include <dpp/dispatcher.h>
namespace dpp {
using json = nlohmann::json;
webhook::webhook() : managed(), type(w_incoming), guild_id(0), channel_id(0), user_id(0), application_id(0), image_data(nullptr)
{
}
webhook::~webhook() {
if (image_data) {
delete image_data;
}
}
webhook& webhook::fill_from_json(nlohmann::json* j) {
id = SnowflakeNotNull(j, "id");
type = Int8NotNull(j, "type");
channel_id = SnowflakeNotNull(j, "channel_id");
guild_id = SnowflakeNotNull(j, "guild_id");
if (j->find("user") != j->end()) {
json & user = (*j)["user"];
user_id = SnowflakeNotNull(&user, "id");
}
name = StringNotNull(j, "name");
avatar = StringNotNull(j, "name");
token = StringNotNull(j, "token");
application_id = SnowflakeNotNull(j, "application_id");
return *this;
}
std::string webhook::build_json(bool with_id) const {
json j;
if (with_id) {
j["id"] = std::to_string(id);
}
j["name"] = name;
j["type"] = type;
if (channel_id)
j["channel_id"] = channel_id;
if (guild_id)
j["guild_id"] = guild_id;
if (!name.empty())
j["name"] = name;
if (image_data)
j["avatar"] = *image_data;
if (application_id)
j["application_id"] = application_id;
return j.dump();
}
webhook& webhook::load_image(const std::string &image_blob, image_type type) {
static std::map<image_type, std::string> mimetypes = {
{ i_gif, "image/gif" },
{ i_jpg, "image/jpeg" },
{ i_png, "image/png" }
};
if (image_blob.size() > MAX_EMOJI_SIZE) {
throw dpp::exception("Webhook icon file exceeds discord limit of 256 kilobytes");
}
if (image_data) {
/* If there's already image data defined, free the old data, to prevent a memory leak */
delete image_data;
}
image_data = new std::string("data:" + mimetypes[type] + ";base64," + base64_encode((unsigned char const*)image_blob.data(), image_blob.length()));
return *this;
}
};

325
vendor/DPP/src/dpp/wsclient.cpp vendored Normal file
View File

@ -0,0 +1,325 @@
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#include <string>
#include <iostream>
#include <fstream>
#include <dpp/wsclient.h>
#include <fmt/format.h>
namespace dpp {
const unsigned char WS_MASKBIT = (1 << 7);
const unsigned char WS_FINBIT = (1 << 7);
const unsigned char WS_PAYLOAD_LENGTH_MAGIC_LARGE = 126;
const unsigned char WS_PAYLOAD_LENGTH_MAGIC_HUGE = 127;
const size_t WS_MAX_PAYLOAD_LENGTH_SMALL = 125;
const size_t WS_MAX_PAYLOAD_LENGTH_LARGE = 65535;
const size_t MAXHEADERSIZE = sizeof(uint64_t) + 2;
websocket_client::websocket_client(const std::string &hostname, const std::string &port, const std::string &urlpath)
: ssl_client(hostname, port),
state(HTTP_HEADERS),
key(fmt::format("{:16x}", time(nullptr))),
path(urlpath)
{
}
void websocket_client::Connect()
{
state = HTTP_HEADERS;
/* Send headers synchronously */
this->write(
fmt::format(
"GET {} HTTP/1.1\r\n"
"Host: {}\r\n"
"pragma: no-cache\r\n"
"User-Agent: DPP/0.1\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: {}\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n",
this->path, this->hostname, this->key
)
);
}
websocket_client::~websocket_client()
{
}
bool websocket_client::HandleFrame(const std::string &buffer)
{
/* This is a stub for classes that derive the websocket client */
return true;
}
size_t websocket_client::FillHeader(unsigned char* outbuf, size_t sendlength, ws_opcode opcode)
{
size_t pos = 0;
outbuf[pos++] = WS_FINBIT | opcode;
if (sendlength <= WS_MAX_PAYLOAD_LENGTH_SMALL)
{
outbuf[pos++] = sendlength;
}
else if (sendlength <= WS_MAX_PAYLOAD_LENGTH_LARGE)
{
outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_LARGE;
outbuf[pos++] = (sendlength >> 8) & 0xff;
outbuf[pos++] = sendlength & 0xff;
}
else
{
outbuf[pos++] = WS_PAYLOAD_LENGTH_MAGIC_HUGE;
const uint64_t len = sendlength;
for (int i = sizeof(uint64_t)-1; i >= 0; i--)
outbuf[pos++] = ((len >> i*8) & 0xff);
}
/* Masking - We don't care about masking, but discord insists on it. We send a mask of 0x00000000 because
* any value XOR 0 is itself, meaning we dont have to waste time and effort on this crap.
*/
outbuf[1] |= WS_MASKBIT;
outbuf[pos++] = 0;
outbuf[pos++] = 0;
outbuf[pos++] = 0;
outbuf[pos++] = 0;
return pos;
}
void websocket_client::write(const std::string &data)
{
if (state == HTTP_HEADERS) {
/* Simple write */
ssl_client::write(data);
} else {
unsigned char out[MAXHEADERSIZE];
size_t s = this->FillHeader(out, data.length(), OP_TEXT);
std::string header((const char*)out, s);
ssl_client::write(header);
ssl_client::write(data);
}
}
std::vector<std::string> tokenize(std::string const &in, const char* sep = "\r\n") {
std::string::size_type b = 0;
std::vector<std::string> result;
while ((b = in.find_first_not_of(sep, b)) != std::string::npos) {
auto e = in.find(sep, b);
result.push_back(in.substr(b, e-b));
b = e;
}
return result;
}
bool websocket_client::handle_buffer(std::string &buffer)
{
switch (state) {
case HTTP_HEADERS:
if (buffer.find("\r\n\r\n") != std::string::npos) {
/* Got all headers, proceed to new state */
/* Get headers string */
std::string headers = buffer.substr(0, buffer.find("\r\n\r\n"));
/* Modify buffer, remove headers section */
buffer.erase(0, buffer.find("\r\n\r\n") + 4);
/* Process headers into map */
std::vector<std::string> h = tokenize(headers);
if (h.size()) {
std::string status_line = h[0];
h.erase(h.begin());
/* HTTP/1.1 101 Switching Protocols */
std::vector<std::string> status = tokenize(status_line, " ");
if (status.size() >= 3 && status[1] == "101") {
for(auto &hd : h) {
std::string::size_type sep = hd.find(": ");
if (sep != std::string::npos) {
std::string key = hd.substr(0, sep);
std::string value = hd.substr(sep + 2, hd.length());
HTTPHeaders[key] = value;
}
}
state = CONNECTED;
} else {
return false;
}
}
}
break;
case CONNECTED:
/* Process packets until we can't */
while (this->parseheader(buffer));
break;
}
return true;
}
ws_state websocket_client::GetState()
{
return this->state;
}
bool websocket_client::parseheader(std::string &data)
{
if (data.size() < 4) {
/* Not enough data to form a frame yet */
return false;
} else {
unsigned char opcode = data[0];
switch (opcode & ~WS_FINBIT)
{
case OP_CONTINUATION:
case OP_TEXT:
case OP_BINARY:
case OP_PING:
case OP_PONG:
{
std::string payload;
unsigned char len1 = data[1];
unsigned int payloadstartoffset = 2;
if (len1 & WS_MASKBIT) {
len1 &= ~WS_MASKBIT;
payloadstartoffset += 2;
/* We don't handle masked data, because discord doesnt send it */
return true;
}
/* 6 bit ("small") length frame */
uint64_t len = len1;
if (len1 == WS_PAYLOAD_LENGTH_MAGIC_LARGE) {
/* 24 bit ("large") length frame */
if (data.length() < 8) {
/* We don't have a complete header yet */
return false;
}
unsigned char len2 = (unsigned char)data[2];
unsigned char len3 = (unsigned char)data[3];
len = (len2 << 8) | len3;
payloadstartoffset += 2;
} else if (len1 == WS_PAYLOAD_LENGTH_MAGIC_HUGE) {
/* 64 bit ("huge") length frame */
if (data.length() < 10) {
/* We don't have a complete header yet */
return false;
}
len = 0;
for (int v = 2, shift = 56; v < 10; ++v, shift -= 8) {
unsigned char l = (unsigned char)data[v];
len |= (uint64_t)(l & 0xff) << shift;
}
payloadstartoffset += 8;
}
if (data.length() < payloadstartoffset + len) {
/* We don't have a complete frame yet */
return false;
}
/* Copy from buffer into string */
const std::string::iterator endit = data.begin() + payloadstartoffset + len;
for (std::string::const_iterator i = data.begin() + payloadstartoffset; i != endit; ++i) {
const unsigned char c = (unsigned char)*i;
payload.push_back(c);
}
/* Remove this frame from the input buffer */
data.erase(data.begin(), endit);
if ((opcode & ~WS_FINBIT) == OP_PING || (opcode & ~WS_FINBIT) == OP_PONG) {
HandlePingPong((opcode & ~WS_FINBIT) == OP_PING, payload);
} else {
/* Pass this frame to the deriving class */
this->HandleFrame(payload);
}
return true;
}
break;
case OP_CLOSE:
{
uint16_t error = data[2] & 0xff;
error <<= 8;
error |= (data[3] & 0xff);
this->Error(error);
return false;
}
break;
default:
{
this->Error(0);
return false;
}
break;
}
}
return false;
}
void websocket_client::one_second_timer()
{
if (((time(NULL) % 20) == 0) && (state == CONNECTED)) {
/* For sending pings, we send with payload */
unsigned char out[MAXHEADERSIZE];
std::string payload = "keepalive";
size_t s = this->FillHeader(out, payload.length(), OP_PING);
std::string header((const char*)out, s);
ssl_client::write(header);
ssl_client::write(payload);
}
}
void websocket_client::HandlePingPong(bool ping, const std::string &payload)
{
unsigned char out[MAXHEADERSIZE];
if (ping) {
/* For receving pings we echo back their payload with the type OP_PONG */
size_t s = this->FillHeader(out, payload.length(), OP_PONG);
std::string header((const char*)out, s);
ssl_client::write(header);
ssl_client::write(payload);
}
}
void websocket_client::Error(uint32_t errorcode)
{
}
void websocket_client::close()
{
this->state = HTTP_HEADERS;
ssl_client::close();
}
};