mirror of
https://github.com/brian8544/turtle-wow.git
synced 2025-01-05 22:34:35 +00:00
581 lines
16 KiB
C++
581 lines
16 KiB
C++
/************************************************************************************
|
|
*
|
|
* 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/channel.h>
|
|
#include <dpp/cache.h>
|
|
#include <dpp/guild.h>
|
|
#include <dpp/user.h>
|
|
#include <dpp/role.h>
|
|
#include <dpp/discordevents.h>
|
|
#include <dpp/stringops.h>
|
|
#include <dpp/nlohmann/json.hpp>
|
|
|
|
using json = nlohmann::json;
|
|
|
|
namespace dpp {
|
|
|
|
permission_overwrite::permission_overwrite() : id(0), allow(0), deny(0), type(0) {}
|
|
|
|
permission_overwrite::permission_overwrite(snowflake id, uint64_t allow, uint64_t deny, overwrite_type type) : id(id), allow(allow), deny(deny), type(type) {}
|
|
|
|
forum_tag::forum_tag() : managed(), moderated(false) {}
|
|
|
|
forum_tag::forum_tag(const std::string& name) : forum_tag() {
|
|
this->set_name(name);
|
|
}
|
|
|
|
forum_tag::~forum_tag()
|
|
{
|
|
}
|
|
|
|
forum_tag& forum_tag::fill_from_json(nlohmann::json *j) {
|
|
set_snowflake_not_null(j, "id", this->id);
|
|
set_string_not_null(j, "name", this->name);
|
|
set_bool_not_null(j, "moderated", this->moderated);
|
|
auto emoji_id = snowflake_not_null(j, "emoji_id");
|
|
auto emoji_name = string_not_null(j, "emoji_name");
|
|
if (emoji_id) {
|
|
this->emoji = emoji_id;
|
|
} else if (!emoji_name.empty()) {
|
|
this->emoji = emoji_name;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
std::string forum_tag::build_json(bool with_id) const {
|
|
json j;
|
|
if (with_id && id) {
|
|
j["id"] = std::to_string(id);
|
|
}
|
|
j["name"] = name;
|
|
j["moderated"] = moderated;
|
|
if (std::holds_alternative<snowflake>(emoji)) {
|
|
j["emoji_id"] = std::get<snowflake>(emoji);
|
|
} else if (std::holds_alternative<std::string>(emoji)) {
|
|
j["emoji_name"] = std::get<std::string>(emoji);
|
|
}
|
|
return j.dump();
|
|
}
|
|
|
|
forum_tag &forum_tag::set_name(const std::string &name) {
|
|
this->name = utility::utf8substr(name, 0, 20);
|
|
return *this;
|
|
}
|
|
|
|
const uint8_t CHANNEL_TYPE_MASK = 0b0000000000001111;
|
|
|
|
thread_member& thread_member::fill_from_json(nlohmann::json* j) {
|
|
set_snowflake_not_null(j, "id", this->thread_id);
|
|
set_snowflake_not_null(j, "user_id", this->user_id);
|
|
set_ts_not_null(j, "join_timestamp", this->joined);
|
|
set_int32_not_null(j, "flags", this->flags);
|
|
return *this;
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const thread_metadata& tmdata) {
|
|
j["archived"] = tmdata.archived;
|
|
j["auto_archive_duration"] = tmdata.auto_archive_duration;
|
|
j["locked"] = tmdata.locked;
|
|
j["invitable"] = tmdata.invitable;
|
|
}
|
|
|
|
void to_json(nlohmann::json& j, const permission_overwrite& po) {
|
|
j["id"] = std::to_string(po.id);
|
|
j["allow"] = std::to_string(po.allow);
|
|
j["deny"] = std::to_string(po.deny);
|
|
j["type"] = po.type;
|
|
}
|
|
|
|
channel::channel() :
|
|
managed(),
|
|
owner_id(0),
|
|
parent_id(0),
|
|
guild_id(0),
|
|
last_message_id(0),
|
|
last_pin_timestamp(0),
|
|
permissions(0),
|
|
position(0),
|
|
bitrate(0),
|
|
rate_limit_per_user(0),
|
|
default_thread_rate_limit_per_user(0),
|
|
default_auto_archive_duration(arc_1_day),
|
|
default_sort_order(so_latest_activity),
|
|
flags(0),
|
|
user_limit(0)
|
|
{
|
|
}
|
|
|
|
channel::~channel()
|
|
{
|
|
}
|
|
|
|
std::string channel::get_mention() const {
|
|
return "<#" + std::to_string(id) + ">";
|
|
}
|
|
|
|
channel& channel::set_name(const std::string& name) {
|
|
this->name = utility::validate(name, 1, 100, "name must be at least 1 character");
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_topic(const std::string& topic) {
|
|
this->topic = utility::utf8substr(topic, 0, 1024);
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_type(channel_type type) {
|
|
this->flags &= ~CHANNEL_TYPE_MASK;
|
|
this->flags |= type;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_guild_id(const snowflake guild_id) {
|
|
this->guild_id = guild_id;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_parent_id(const snowflake parent_id) {
|
|
this->parent_id = parent_id;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_rate_limit_per_user(const uint16_t rate_limit_per_user) {
|
|
this->rate_limit_per_user = rate_limit_per_user;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_position(const uint16_t position) {
|
|
this->position = position;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_bitrate(const uint16_t bitrate) {
|
|
this->bitrate = bitrate;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_flags(const uint16_t flags) {
|
|
this->flags = flags;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::add_flag(const channel_flags flag) {
|
|
this->flags |= flag;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::remove_flag(const channel_flags flag) {
|
|
this->flags &= ~flag;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_nsfw(const bool is_nsfw) {
|
|
this->flags = (is_nsfw) ? this->flags | dpp::c_nsfw : this->flags & ~dpp::c_nsfw;;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_lock_permissions(const bool is_lock_permissions) {
|
|
this->flags = (is_lock_permissions) ? this->flags | dpp::c_lock_permissions : this->flags & ~dpp::c_lock_permissions;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::set_user_limit(const uint8_t user_limit) {
|
|
this->user_limit = user_limit;
|
|
return *this;
|
|
}
|
|
|
|
channel& channel::add_permission_overwrite(const snowflake id, const overwrite_type type, const uint64_t allowed_permissions, const uint64_t denied_permissions) {
|
|
permission_overwrite po {id, allowed_permissions, denied_permissions, type};
|
|
this->permission_overwrites.push_back(po);
|
|
return *this;
|
|
}
|
|
|
|
bool channel::is_nsfw() const {
|
|
return flags & dpp::c_nsfw;
|
|
}
|
|
|
|
bool channel::is_locked_permissions() const {
|
|
return flags & dpp::c_lock_permissions;
|
|
}
|
|
|
|
bool channel::is_text_channel() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_TEXT;
|
|
}
|
|
|
|
bool channel::is_dm() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == DM;
|
|
}
|
|
|
|
bool channel::is_voice_channel() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_VOICE;
|
|
}
|
|
|
|
bool channel::is_group_dm() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == GROUP_DM;
|
|
}
|
|
|
|
bool channel::is_category() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_CATEGORY;
|
|
}
|
|
|
|
bool channel::is_forum() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_FORUM;
|
|
}
|
|
|
|
bool channel::is_stage_channel() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_STAGE;
|
|
}
|
|
|
|
bool channel::is_news_channel() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_ANNOUNCEMENT;
|
|
}
|
|
|
|
bool channel::is_store_channel() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_STORE;
|
|
}
|
|
|
|
bool channel::is_video_auto() const {
|
|
/* Note: c_video_auto has no real flag (its value is 0)
|
|
* as absence of the 720p FULL quality flag indicates it must be
|
|
* c_video_auto instead -- discord decided to put what is basically
|
|
* a bool into two potential values, 1 and 2. hmmm...
|
|
*/
|
|
return !is_video_720p();
|
|
}
|
|
|
|
bool channel::is_video_720p() const {
|
|
return flags & dpp::c_video_quality_720p;
|
|
}
|
|
|
|
bool channel::is_pinned_thread() const {
|
|
return flags & dpp::c_pinned_thread;
|
|
}
|
|
|
|
bool channel::is_tag_required() const {
|
|
return flags & dpp::c_require_tag;
|
|
}
|
|
|
|
bool thread::is_news_thread() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_ANNOUNCEMENT_THREAD;
|
|
}
|
|
|
|
bool thread::is_public_thread() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_PUBLIC_THREAD;
|
|
}
|
|
|
|
bool thread::is_private_thread() const {
|
|
return (flags & CHANNEL_TYPE_MASK) == CHANNEL_PRIVATE_THREAD;
|
|
}
|
|
|
|
thread& thread::fill_from_json(json* j) {
|
|
channel::fill_from_json(j);
|
|
|
|
uint8_t type = int8_not_null(j, "type");
|
|
this->flags |= (type & CHANNEL_TYPE_MASK);
|
|
|
|
if (j->contains("applied_tags")) {
|
|
for (const auto &t : (*j)["applied_tags"]) {
|
|
this->applied_tags.push_back(t);
|
|
}
|
|
}
|
|
|
|
set_int32_not_null(j, "total_message_sent", this->total_messages_sent);
|
|
set_int8_not_null(j, "message_count", this->message_count);
|
|
set_int8_not_null(j, "member_count", this->member_count);
|
|
auto json_metadata = (*j)["thread_metadata"];
|
|
metadata.archived = bool_not_null(&json_metadata, "archived");
|
|
metadata.archive_timestamp = ts_not_null(&json_metadata, "archive_timestamp");
|
|
metadata.auto_archive_duration = int16_not_null(&json_metadata, "auto_archive_duration");
|
|
metadata.locked = bool_not_null(&json_metadata, "locked");
|
|
metadata.invitable = bool_not_null(&json_metadata, "invitable");
|
|
|
|
/* Only certain events set this */
|
|
if (j->contains("member")) {
|
|
member.fill_from_json(&((*j)["member"]));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
thread::thread() : channel(), total_messages_sent(0), message_count(0), member_count(0) {
|
|
}
|
|
|
|
thread::~thread() {
|
|
}
|
|
|
|
channel& channel::fill_from_json(json* j) {
|
|
this->id = snowflake_not_null(j, "id");
|
|
set_snowflake_not_null(j, "guild_id", this->guild_id);
|
|
set_int16_not_null(j, "position", this->position);
|
|
set_string_not_null(j, "name", this->name);
|
|
set_string_not_null(j, "topic", this->topic);
|
|
set_snowflake_not_null(j, "last_message_id", this->last_message_id);
|
|
set_int8_not_null(j, "user_limit", this->user_limit);
|
|
set_int16_not_null(j, "rate_limit_per_user", this->rate_limit_per_user);
|
|
set_int16_not_null(j, "default_thread_rate_limit_per_user", this->default_thread_rate_limit_per_user);
|
|
set_snowflake_not_null(j, "owner_id", this->owner_id);
|
|
set_snowflake_not_null(j, "parent_id", this->parent_id);
|
|
this->bitrate = int16_not_null(j, "bitrate")/1024;
|
|
this->flags |= bool_not_null(j, "nsfw") ? dpp::c_nsfw : 0;
|
|
|
|
uint16_t arc = int16_not_null(j, "default_auto_archive_duration");
|
|
switch (arc) {
|
|
case 60:
|
|
this->default_auto_archive_duration = arc_1_hour;
|
|
break;
|
|
case 1440:
|
|
this->default_auto_archive_duration = arc_1_day;
|
|
break;
|
|
case 4320:
|
|
this->default_auto_archive_duration = arc_3_days;
|
|
break;
|
|
case 10080:
|
|
this->default_auto_archive_duration = arc_1_week;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (j->contains("available_tags")) {
|
|
available_tags = {};
|
|
for (auto & available_tag : (*j)["available_tags"]) {
|
|
this->available_tags.emplace_back(forum_tag().fill_from_json(&available_tag));
|
|
}
|
|
}
|
|
|
|
if (j->contains("default_reaction_emoji")) {
|
|
auto emoji_id = snowflake_not_null(&(*j)["default_reaction_emoji"], "emoji_id");
|
|
auto emoji_name = string_not_null(&(*j)["default_reaction_emoji"], "emoji_name");
|
|
if (emoji_id) {
|
|
this->default_reaction = emoji_id;
|
|
} else if (!emoji_name.empty()) {
|
|
this->default_reaction = emoji_name;
|
|
}
|
|
}
|
|
|
|
this->default_sort_order = (default_forum_sort_order_t)int8_not_null(j, "default_sort_order");
|
|
|
|
uint8_t type = int8_not_null(j, "type");
|
|
this->flags |= (type & CHANNEL_TYPE_MASK);
|
|
|
|
uint8_t dflags = int8_not_null(j, "flags");
|
|
this->flags |= (dflags & dpp::dc_pinned_thread) ? dpp::c_pinned_thread : 0;
|
|
this->flags |= (dflags & dpp::dc_require_tag) ? dpp::c_require_tag : 0;
|
|
|
|
uint8_t vqm = int8_not_null(j, "video_quality_mode");
|
|
if (vqm == 2) {
|
|
/* If this is set to 2, this means full quality 720p video for voice channel.
|
|
* Undefined, or a value of 1 (the other two possibilities right now) means
|
|
* video quality AUTO.
|
|
*/
|
|
this->flags |= dpp::c_video_quality_720p;
|
|
}
|
|
|
|
if (j->contains("recipients")) {
|
|
recipients = {};
|
|
for (auto & r : (*j)["recipients"]) {
|
|
recipients.push_back(from_string<uint64_t>(r["id"].get<std::string>()));
|
|
}
|
|
}
|
|
|
|
if (j->contains("permission_overwrites")) {
|
|
permission_overwrites = {};
|
|
for (auto & overwrite : (*j)["permission_overwrites"]) {
|
|
permission_overwrite po;
|
|
po.id = snowflake_not_null(&overwrite, "id");
|
|
po.allow = snowflake_not_null(&overwrite, "allow");
|
|
po.deny = snowflake_not_null(&overwrite, "deny");
|
|
po.type = int8_not_null(&overwrite, "type");
|
|
permission_overwrites.emplace_back(po);
|
|
}
|
|
}
|
|
|
|
/* Note: This is only set when the channel is in the resolved set from an interaction.
|
|
* When set it contains the invokers permissions on channel. Any other time, contains 0.
|
|
*/
|
|
if (j->contains("permissions")) {
|
|
set_snowflake_not_null(j, "permissions", permissions);
|
|
}
|
|
|
|
std::string _icon = string_not_null(j, "icon");
|
|
|
|
if (!_icon.empty()) {
|
|
this->icon = _icon;
|
|
}
|
|
|
|
set_string_not_null(j, "rtc_region", rtc_region);
|
|
|
|
return *this;
|
|
}
|
|
|
|
std::string thread::build_json(bool with_id) const {
|
|
json j = json::parse(channel::build_json(with_id));
|
|
j["type"] = (flags & CHANNEL_TYPE_MASK);
|
|
j["thread_metadata"] = this->metadata;
|
|
if (!this->applied_tags.empty()) {
|
|
j["applied_tags"] = json::array();
|
|
for (auto &tag_id: this->applied_tags) {
|
|
if (tag_id) {
|
|
j["applied_tags"].push_back(tag_id);
|
|
}
|
|
}
|
|
}
|
|
return j.dump();
|
|
}
|
|
|
|
std::string channel::build_json(bool with_id) const {
|
|
json j;
|
|
if (with_id && id) {
|
|
j["id"] = std::to_string(id);
|
|
}
|
|
j["guild_id"] = std::to_string(guild_id);
|
|
if (position) {
|
|
j["position"] = position;
|
|
}
|
|
j["name"] = name;
|
|
if (!topic.empty()) {
|
|
j["topic"] = topic;
|
|
}
|
|
if (!permission_overwrites.empty()) {
|
|
j["permission_overwrites"] = json::array();
|
|
for (const auto& po : permission_overwrites) {
|
|
json jpo = po;
|
|
j["permission_overwrites"].push_back(jpo);
|
|
}
|
|
}
|
|
if (rate_limit_per_user) {
|
|
j["rate_limit_per_user"] = rate_limit_per_user;
|
|
}
|
|
if (default_thread_rate_limit_per_user) {
|
|
j["default_thread_rate_limit_per_user"] = default_thread_rate_limit_per_user;
|
|
}
|
|
if (is_voice_channel()) {
|
|
j["user_limit"] = user_limit;
|
|
j["bitrate"] = bitrate*1024;
|
|
}
|
|
if (is_forum()) {
|
|
j["flags"] = (flags & dpp::c_require_tag) ? dpp::dc_require_tag : 0;
|
|
}
|
|
j["type"] = (flags & CHANNEL_TYPE_MASK);
|
|
if (!is_dm()) {
|
|
if (parent_id) {
|
|
j["parent_id"] = std::to_string(parent_id);
|
|
}
|
|
j["nsfw"] = is_nsfw();
|
|
}
|
|
switch (default_auto_archive_duration) {
|
|
case arc_1_hour:
|
|
j["default_auto_archive_duration"] = 60;
|
|
break;
|
|
case arc_1_day:
|
|
j["default_auto_archive_duration"] = 1440;
|
|
break;
|
|
case arc_3_days:
|
|
j["default_auto_archive_duration"] = 4320;
|
|
break;
|
|
case arc_1_week:
|
|
j["default_auto_archive_duration"] = 10080;
|
|
break;
|
|
}
|
|
if (!available_tags.empty()) {
|
|
j["available_tags"] = json::array();
|
|
for (const auto &available_tag : this->available_tags) {
|
|
j["available_tags"].push_back(json::parse(available_tag.build_json()));
|
|
}
|
|
}
|
|
if (std::holds_alternative<snowflake>(this->default_reaction)) {
|
|
j["default_reaction_emoji"]["emoji_id"] = std::get<snowflake>(this->default_reaction);
|
|
} else if (std::holds_alternative<std::string>(this->default_reaction)) {
|
|
j["default_reaction_emoji"]["emoji_name"] = std::get<std::string>(this->default_reaction);
|
|
}
|
|
if (default_sort_order) {
|
|
j["default_sort_order"] = default_sort_order;
|
|
}
|
|
if (flags & c_lock_permissions) {
|
|
j["lock_permissions"] = true;
|
|
}
|
|
|
|
return j.dump();
|
|
}
|
|
|
|
permission channel::get_user_permissions(const user* user) const {
|
|
if (user == nullptr)
|
|
return 0;
|
|
|
|
guild* g = dpp::find_guild(guild_id);
|
|
if (g == nullptr)
|
|
return 0;
|
|
|
|
return g->permission_overwrites(g->base_permissions(user), user, this);
|
|
}
|
|
|
|
permission channel::get_user_permissions(const guild_member &member) const {
|
|
|
|
guild* g = dpp::find_guild(guild_id);
|
|
if (g == nullptr)
|
|
return 0;
|
|
|
|
return g->permission_overwrites(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) {
|
|
if (g->permission_overwrites(m->second, *this) & 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;
|
|
}
|
|
|
|
std::string channel::get_icon_url(uint16_t size) const {
|
|
/* XXX: Discord were supposed to change their CDN over to discord.com, they haven't.
|
|
* At some point in the future this URL *will* change!
|
|
*/
|
|
if (this->id && !this->icon.to_string().empty()) {
|
|
return utility::cdn_host + "/channel-icons/" + std::to_string(this->id) + "/" + this->icon.to_string() + ".png" + utility::avatar_size(size);
|
|
} else {
|
|
return std::string();
|
|
}
|
|
}
|
|
|
|
channel_type channel::get_type() const {
|
|
return static_cast<channel_type>(flags & CHANNEL_TYPE_MASK);
|
|
}
|
|
|
|
|
|
};
|