mirror of
https://github.com/celisej567/abaddon.git
synced 2026-09-27 16:16:35 +03:00
Restructure source and resource files (#46)
importantly, res is now res/res and css is now res/css
This commit is contained in:
committed by
GitHub
parent
d88079000a
commit
a51a54bc59
116
src/discord/activity.cpp
Normal file
116
src/discord/activity.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "activity.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivityTimestamps &m) {
|
||||
JS_O("start", m.Start);
|
||||
JS_O("end", m.End);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivityTimestamps &m) {
|
||||
JS_IF("start", m.Start);
|
||||
JS_IF("end", m.End);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivityEmoji &m) {
|
||||
JS_D("name", m.Name);
|
||||
JS_O("id", m.ID);
|
||||
JS_O("animated", m.IsAnimated);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivityEmoji &m) {
|
||||
j["name"] = m.Name;
|
||||
if (m.ID.has_value())
|
||||
j["id"] = *m.ID;
|
||||
if (m.IsAnimated.has_value())
|
||||
j["animated"] = *m.IsAnimated;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivityParty &m) {
|
||||
JS_O("id", m.ID);
|
||||
JS_O("size", m.Size);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivityParty &m) {
|
||||
JS_IF("id", m.ID);
|
||||
JS_IF("size", m.Size);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivityAssets &m) {
|
||||
JS_O("large_image", m.LargeImage);
|
||||
JS_O("large_text", m.LargeText);
|
||||
JS_O("small_image", m.SmallImage);
|
||||
JS_O("small_text", m.SmallText);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivityAssets &m) {
|
||||
JS_IF("large_image", m.LargeImage);
|
||||
JS_IF("large_text", m.LargeText);
|
||||
JS_IF("small_image", m.SmallImage);
|
||||
JS_IF("small_text", m.SmallText);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivitySecrets &m) {
|
||||
JS_O("join", m.Join);
|
||||
JS_O("spectate", m.Spectate);
|
||||
JS_O("match", m.Match);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivitySecrets &m) {
|
||||
JS_IF("join", m.Join);
|
||||
JS_IF("spectate", m.Spectate);
|
||||
JS_IF("match", m.Match);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ActivityData &m) {
|
||||
JS_D("name", m.Name);
|
||||
JS_D("type", m.Type);
|
||||
JS_ON("url", m.URL);
|
||||
JS_D("created_at", m.CreatedAt);
|
||||
JS_O("timestamps", m.Timestamps);
|
||||
JS_O("application_id", m.ApplicationID);
|
||||
JS_ON("details", m.Details);
|
||||
JS_ON("state", m.State);
|
||||
JS_ON("emoji", m.Emoji);
|
||||
JS_ON("party", m.Party);
|
||||
JS_O("assets", m.Assets);
|
||||
JS_O("secrets", m.Secrets);
|
||||
JS_O("instance", m.IsInstance);
|
||||
JS_O("flags", m.Flags);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ActivityData &m) {
|
||||
if (m.Type == ActivityType::Custom) {
|
||||
j["name"] = "Custom Status";
|
||||
j["state"] = m.Name;
|
||||
} else {
|
||||
j["name"] = m.Name;
|
||||
JS_IF("state", m.State);
|
||||
}
|
||||
|
||||
j["type"] = m.Type;
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("created_at", m.CreatedAt);
|
||||
JS_IF("timestamps", m.Timestamps);
|
||||
JS_IF("application_id", m.ApplicationID);
|
||||
JS_IF("details", m.Details);
|
||||
JS_IF("emoji", m.Emoji);
|
||||
JS_IF("party", m.Party);
|
||||
JS_IF("assets", m.Assets);
|
||||
JS_IF("secrets", m.Secrets);
|
||||
JS_IF("instance", m.IsInstance);
|
||||
JS_IF("flags", m.Flags);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, PresenceData &m) {
|
||||
JS_N("activities", m.Activities);
|
||||
JS_D("status", m.Status);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const PresenceData &m) {
|
||||
j["activities"] = m.Activities;
|
||||
j["status"] = m.Status;
|
||||
JS_IF("afk", m.IsAFK);
|
||||
if (m.Since.has_value())
|
||||
j["since"] = *m.Since;
|
||||
else
|
||||
j["since"] = 0;
|
||||
}
|
||||
137
src/discord/activity.hpp
Normal file
137
src/discord/activity.hpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "util.hpp"
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
|
||||
enum class PresenceStatus : uint8_t {
|
||||
Online,
|
||||
Offline,
|
||||
Idle,
|
||||
DND,
|
||||
};
|
||||
|
||||
constexpr inline const char *GetPresenceString(PresenceStatus s) {
|
||||
switch (s) {
|
||||
case PresenceStatus::Online:
|
||||
return "online";
|
||||
case PresenceStatus::Offline:
|
||||
return "offline";
|
||||
case PresenceStatus::Idle:
|
||||
return "idle";
|
||||
case PresenceStatus::DND:
|
||||
return "dnd";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
constexpr inline const char* GetPresenceDisplayString(PresenceStatus s) {
|
||||
switch (s) {
|
||||
case PresenceStatus::Online:
|
||||
return "Online";
|
||||
case PresenceStatus::Offline:
|
||||
return "Offline";
|
||||
case PresenceStatus::Idle:
|
||||
return "Away";
|
||||
case PresenceStatus::DND:
|
||||
return "Do Not Disturb";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
enum class ActivityType : int {
|
||||
Game = 0,
|
||||
Streaming = 1,
|
||||
Listening = 2,
|
||||
Watching = 3, // not documented
|
||||
Custom = 4,
|
||||
Competing = 5,
|
||||
};
|
||||
|
||||
enum class ActivityFlags {
|
||||
INSTANCE = (1 << 0),
|
||||
JOIN = (1 << 1),
|
||||
SPECTATE = (1 << 2),
|
||||
JOIN_REQUEST = (1 << 3),
|
||||
SYNC = (1 << 4),
|
||||
PLAY = (1 << 5),
|
||||
};
|
||||
template<>
|
||||
struct Bitwise<ActivityFlags> {
|
||||
static const bool enable = true;
|
||||
};
|
||||
|
||||
struct ActivityTimestamps {
|
||||
std::optional<int> Start;
|
||||
std::optional<int> End;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivityTimestamps &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivityTimestamps &m);
|
||||
};
|
||||
|
||||
struct ActivityEmoji {
|
||||
std::string Name;
|
||||
std::optional<Snowflake> ID;
|
||||
std::optional<bool> IsAnimated;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivityEmoji &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivityEmoji &m);
|
||||
};
|
||||
|
||||
struct ActivityParty {
|
||||
std::optional<std::string> ID;
|
||||
std::optional<std::array<int, 2>> Size;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivityParty &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivityParty &m);
|
||||
};
|
||||
|
||||
struct ActivityAssets {
|
||||
std::optional<std::string> LargeImage;
|
||||
std::optional<std::string> LargeText;
|
||||
std::optional<std::string> SmallImage;
|
||||
std::optional<std::string> SmallText;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivityAssets &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivityAssets &m);
|
||||
};
|
||||
|
||||
struct ActivitySecrets {
|
||||
std::optional<std::string> Join;
|
||||
std::optional<std::string> Spectate;
|
||||
std::optional<std::string> Match;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivitySecrets &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivitySecrets &m);
|
||||
};
|
||||
|
||||
struct ActivityData {
|
||||
std::string Name; //
|
||||
ActivityType Type; //
|
||||
std::optional<std::string> URL; // null
|
||||
std::optional<uint64_t> CreatedAt; //
|
||||
std::optional<ActivityTimestamps> Timestamps; //
|
||||
std::optional<Snowflake> ApplicationID; //
|
||||
std::optional<std::string> Details; // null
|
||||
std::optional<std::string> State; // null
|
||||
std::optional<ActivityEmoji> Emoji; // null
|
||||
std::optional<ActivityParty> Party; //
|
||||
std::optional<ActivityAssets> Assets; //
|
||||
std::optional<ActivitySecrets> Secrets; //
|
||||
std::optional<bool> IsInstance; //
|
||||
std::optional<ActivityFlags> Flags; //
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ActivityData &m);
|
||||
friend void to_json(nlohmann::json &j, const ActivityData &m);
|
||||
};
|
||||
|
||||
struct PresenceData {
|
||||
std::vector<ActivityData> Activities; // null (but never sent as such)
|
||||
std::string Status;
|
||||
std::optional<bool> IsAFK;
|
||||
std::optional<int> Since;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, PresenceData &m);
|
||||
friend void to_json(nlohmann::json &j, const PresenceData &m);
|
||||
};
|
||||
34
src/discord/auditlog.cpp
Normal file
34
src/discord/auditlog.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#include "auditlog.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogChange &m) {
|
||||
JS_D("key", m.Key);
|
||||
JS_O("old_value", m.OldValue);
|
||||
JS_O("new_value", m.NewValue);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogOptions &m) {
|
||||
JS_O("delete_member_days", m.DeleteMemberDays);
|
||||
JS_O("members_removed", m.MembersRemoved);
|
||||
JS_O("channel_id", m.ChannelID);
|
||||
JS_O("message_id", m.MessageID);
|
||||
JS_O("count", m.Count);
|
||||
JS_O("id", m.ID);
|
||||
JS_O("type", m.Type);
|
||||
JS_O("role_name", m.RoleName);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogEntry &m) {
|
||||
JS_N("target_id", m.TargetID);
|
||||
JS_O("changes", m.Changes);
|
||||
JS_N("user_id", m.UserID);
|
||||
JS_D("id", m.ID);
|
||||
JS_D("action_type", m.Type);
|
||||
JS_O("options", m.Options);
|
||||
JS_O("reason", m.Reason);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AuditLogData &m) {
|
||||
JS_D("audit_log_entries", m.Entries);
|
||||
JS_D("users", m.Users);
|
||||
JS_D("webhooks", m.Webhooks);
|
||||
}
|
||||
120
src/discord/auditlog.hpp
Normal file
120
src/discord/auditlog.hpp
Normal file
@@ -0,0 +1,120 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
#include "json.hpp"
|
||||
#include "webhook.hpp"
|
||||
|
||||
enum class AuditLogActionType {
|
||||
GUILD_UPDATE = 1,
|
||||
CHANNEL_CREATE = 10,
|
||||
CHANNEL_UPDATE = 11,
|
||||
CHANNEL_DELETE = 12,
|
||||
CHANNEL_OVERWRITE_CREATE = 13,
|
||||
CHANNEL_OVERWRITE_UPDATE = 14,
|
||||
CHANNEL_OVERWRITE_DELETE = 15,
|
||||
MEMBER_KICK = 20,
|
||||
MEMBER_PRUNE = 21,
|
||||
MEMBER_BAN_ADD = 22,
|
||||
MEMBER_BAN_REMOVE = 23,
|
||||
MEMBER_UPDATE = 24,
|
||||
MEMBER_ROLE_UPDATE = 25,
|
||||
MEMBER_MOVE = 26,
|
||||
MEMBER_DISCONNECT = 27,
|
||||
BOT_ADD = 28,
|
||||
ROLE_CREATE = 30,
|
||||
ROLE_UPDATE = 31,
|
||||
ROLE_DELETE = 32,
|
||||
INVITE_CREATE = 40,
|
||||
INVITE_UPDATE = 41,
|
||||
INVITE_DELETE = 42,
|
||||
WEBHOOK_CREATE = 50,
|
||||
WEBHOOK_UPDATE = 51,
|
||||
WEBHOOK_DELETE = 52,
|
||||
EMOJI_CREATE = 60,
|
||||
EMOJI_UPDATE = 61,
|
||||
EMOJI_DELETE = 62,
|
||||
MESSAGE_DELETE = 72,
|
||||
MESSAGE_BULK_DELETE = 73,
|
||||
MESSAGE_PIN = 74,
|
||||
MESSAGE_UNPIN = 75,
|
||||
INTEGRATION_CREATE = 80,
|
||||
INTEGRATION_UPDATE = 81,
|
||||
INTEGRATION_DELETE = 82,
|
||||
STAGE_INSTANCE_CREATE = 83,
|
||||
STAGE_INSTANCE_UPDATE = 84,
|
||||
STAGE_INSTANCE_DELETE = 85,
|
||||
STICKER_CREATE = 90,
|
||||
STICKER_UPDATE = 91,
|
||||
STICKER_DELETE = 92,
|
||||
THREAD_CREATE = 110,
|
||||
THREAD_UPDATE = 111,
|
||||
THREAD_DELETE = 112,
|
||||
};
|
||||
|
||||
struct AuditLogChange {
|
||||
std::string Key;
|
||||
std::optional<nlohmann::json> OldValue;
|
||||
std::optional<nlohmann::json> NewValue;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogChange &m);
|
||||
};
|
||||
|
||||
struct AuditLogOptions {
|
||||
std::optional<std::string> DeleteMemberDays; // MEMBER_PRUNE
|
||||
std::optional<std::string> MembersRemoved; // MEMBER_PRUNE
|
||||
std::optional<Snowflake> ChannelID; // MEMBER_MOVE, MESSAGE_PIN, MESSAGE_UNPIN, MESSAGE_DELETE
|
||||
std::optional<Snowflake> MessageID; // MESSAGE_PIN, MESSAGE_UNPIN,
|
||||
std::optional<std::string> Count; // MESSAGE_DELETE, MESSAGE_BULK_DELETE, MEMBER_DISCONNECT, MEMBER_MOVE
|
||||
std::optional<Snowflake> ID; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
std::optional<std::string> Type; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
std::optional<std::string> RoleName; // CHANNEL_OVERWRITE_CREATE, CHANNEL_OVERWRITE_UPDATE, CHANNEL_OVERWRITE_DELETE
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogOptions &m);
|
||||
};
|
||||
|
||||
struct AuditLogEntry {
|
||||
Snowflake ID;
|
||||
std::string TargetID; // null
|
||||
std::optional<Snowflake> UserID;
|
||||
AuditLogActionType Type;
|
||||
std::optional<std::string> Reason;
|
||||
std::optional<std::vector<AuditLogChange>> Changes;
|
||||
std::optional<AuditLogOptions> Options;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogEntry &m);
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> GetOldFromKey(const std::string &key) const;
|
||||
|
||||
template<typename T>
|
||||
std::optional<T> GetNewFromKey(const std::string &key) const;
|
||||
};
|
||||
|
||||
struct AuditLogData {
|
||||
std::vector<AuditLogEntry> Entries;
|
||||
std::vector<UserData> Users;
|
||||
std::vector<WebhookData> Webhooks;
|
||||
// std::vector<IntegrationData> Integrations;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, AuditLogData &m);
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> AuditLogEntry::GetOldFromKey(const std::string &key) const {
|
||||
if (!Changes.has_value()) return std::nullopt;
|
||||
for (const auto &change : *Changes)
|
||||
if (change.Key == key && change.OldValue.has_value())
|
||||
return change.OldValue->get<T>();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::optional<T> AuditLogEntry::GetNewFromKey(const std::string &key) const {
|
||||
if (!Changes.has_value()) return std::nullopt;
|
||||
for (const auto &change : *Changes)
|
||||
if (change.Key == key && change.NewValue.has_value())
|
||||
return change.NewValue->get<T>();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
6
src/discord/ban.cpp
Normal file
6
src/discord/ban.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "ban.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, BanData &m) {
|
||||
JS_N("reason", m.Reason);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
10
src/discord/ban.hpp
Normal file
10
src/discord/ban.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include "user.hpp"
|
||||
|
||||
struct BanData {
|
||||
std::string Reason; // null
|
||||
UserData User; // access id
|
||||
|
||||
friend void from_json(const nlohmann::json &j, BanData &m);
|
||||
};
|
||||
97
src/discord/channel.cpp
Normal file
97
src/discord/channel.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "abaddon.hpp"
|
||||
#include "channel.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMetadataData &m) {
|
||||
JS_D("archived", m.IsArchived);
|
||||
JS_D("auto_archive_duration", m.AutoArchiveDuration);
|
||||
JS_D("archive_timestamp", m.ArchiveTimestamp);
|
||||
JS_O("locked", m.IsLocked);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMemberObject &m) {
|
||||
JS_O("id", m.ThreadID);
|
||||
JS_O("user_id", m.UserID);
|
||||
JS_D("join_timestamp", m.JoinTimestamp);
|
||||
JS_D("flags", m.Flags);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ChannelData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_O("position", m.Position);
|
||||
JS_O("permission_overwrites", m.PermissionOverwrites);
|
||||
JS_ON("name", m.Name);
|
||||
JS_ON("topic", m.Topic);
|
||||
JS_O("nsfw", m.IsNSFW);
|
||||
JS_ON("last_message_id", m.LastMessageID);
|
||||
JS_O("bitrate", m.Bitrate);
|
||||
JS_O("user_limit", m.UserLimit);
|
||||
JS_O("rate_limit_per_user", m.RateLimitPerUser);
|
||||
JS_O("recipients", m.Recipients);
|
||||
JS_O("recipient_ids", m.RecipientIDs);
|
||||
JS_ON("icon", m.Icon);
|
||||
JS_O("owner_id", m.OwnerID);
|
||||
JS_O("application_id", m.ApplicationID);
|
||||
JS_ON("parent_id", m.ParentID);
|
||||
JS_ON("last_pin_timestamp", m.LastPinTimestamp);
|
||||
JS_O("thread_metadata", m.ThreadMetadata);
|
||||
JS_O("member", m.ThreadMember);
|
||||
}
|
||||
|
||||
void ChannelData::update_from_json(const nlohmann::json &j) {
|
||||
JS_RD("type", Type);
|
||||
JS_RD("guild_id", GuildID);
|
||||
JS_RV("position", Position, -1);
|
||||
JS_RD("permission_overwrites", PermissionOverwrites);
|
||||
JS_RD("name", Name);
|
||||
JS_RD("topic", Topic);
|
||||
JS_RD("nsfw", IsNSFW);
|
||||
JS_RD("last_message_id", LastMessageID);
|
||||
JS_RD("bitrate", Bitrate);
|
||||
JS_RD("user_limit", UserLimit);
|
||||
JS_RD("rate_limit_per_user", RateLimitPerUser);
|
||||
JS_RD("recipients", Recipients);
|
||||
JS_RD("icon", Icon);
|
||||
JS_RD("owner_id", OwnerID);
|
||||
JS_RD("application_id", ApplicationID);
|
||||
JS_RD("parent_id", ParentID);
|
||||
JS_RD("last_pin_timestamp", LastPinTimestamp);
|
||||
}
|
||||
|
||||
bool ChannelData::NSFW() const {
|
||||
return IsNSFW.has_value() && *IsNSFW;
|
||||
}
|
||||
|
||||
bool ChannelData::IsThread() const noexcept {
|
||||
return Type == ChannelType::GUILD_PUBLIC_THREAD ||
|
||||
Type == ChannelType::GUILD_PRIVATE_THREAD ||
|
||||
Type == ChannelType::GUILD_NEWS_THREAD;
|
||||
}
|
||||
|
||||
bool ChannelData::IsJoinedThread() const {
|
||||
return Abaddon::Get().GetDiscordClient().IsThreadJoined(ID);
|
||||
}
|
||||
|
||||
std::optional<PermissionOverwrite> ChannelData::GetOverwrite(Snowflake id) const {
|
||||
return Abaddon::Get().GetDiscordClient().GetPermissionOverwrite(ID, id);
|
||||
}
|
||||
|
||||
std::vector<UserData> ChannelData::GetDMRecipients() const {
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
if (Recipients.has_value())
|
||||
return *Recipients;
|
||||
|
||||
if (RecipientIDs.has_value()) {
|
||||
std::vector<UserData> ret;
|
||||
for (const auto &id : *RecipientIDs) {
|
||||
auto user = discord.GetUser(id);
|
||||
if (user.has_value())
|
||||
ret.push_back(std::move(*user));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return std::vector<UserData>();
|
||||
}
|
||||
92
src/discord/channel.hpp
Normal file
92
src/discord/channel.hpp
Normal file
@@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include "user.hpp"
|
||||
#include "permissions.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
enum class ChannelType : int {
|
||||
GUILD_TEXT = 0,
|
||||
DM = 1,
|
||||
GUILD_VOICE = 2,
|
||||
GROUP_DM = 3,
|
||||
GUILD_CATEGORY = 4,
|
||||
GUILD_NEWS = 5,
|
||||
GUILD_STORE = 6,
|
||||
/* 7 and 8 were used for LFG */
|
||||
/* 9 was used for threads */
|
||||
GUILD_NEWS_THREAD = 10,
|
||||
GUILD_PUBLIC_THREAD = 11,
|
||||
GUILD_PRIVATE_THREAD = 12,
|
||||
GUILD_STAGE_VOICE = 13,
|
||||
};
|
||||
|
||||
enum class StagePrivacy {
|
||||
PUBLIC = 1,
|
||||
GUILD_ONLY = 2,
|
||||
};
|
||||
|
||||
constexpr const char *GetStagePrivacyDisplayString(StagePrivacy e) {
|
||||
switch (e) {
|
||||
case StagePrivacy::PUBLIC:
|
||||
return "Public";
|
||||
case StagePrivacy::GUILD_ONLY:
|
||||
return "Guild Only";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// should be moved somewhere?
|
||||
|
||||
struct ThreadMetadataData {
|
||||
bool IsArchived;
|
||||
int AutoArchiveDuration;
|
||||
std::string ArchiveTimestamp;
|
||||
std::optional<bool> IsLocked;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMetadataData &m);
|
||||
};
|
||||
|
||||
struct ThreadMemberObject {
|
||||
std::optional<Snowflake> ThreadID;
|
||||
std::optional<Snowflake> UserID;
|
||||
std::string JoinTimestamp;
|
||||
int Flags;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMemberObject &m);
|
||||
};
|
||||
|
||||
struct ChannelData {
|
||||
Snowflake ID;
|
||||
ChannelType Type;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::optional<int> Position;
|
||||
std::optional<std::vector<PermissionOverwrite>> PermissionOverwrites; // shouldnt be accessed
|
||||
std::optional<std::string> Name; // null for dm's
|
||||
std::optional<std::string> Topic; // null
|
||||
std::optional<bool> IsNSFW;
|
||||
std::optional<Snowflake> LastMessageID; // null
|
||||
std::optional<int> Bitrate;
|
||||
std::optional<int> UserLimit;
|
||||
std::optional<int> RateLimitPerUser;
|
||||
std::optional<std::vector<UserData>> Recipients; // only access id
|
||||
std::optional<std::vector<Snowflake>> RecipientIDs;
|
||||
std::optional<std::string> Icon; // null
|
||||
std::optional<Snowflake> OwnerID;
|
||||
std::optional<Snowflake> ApplicationID;
|
||||
std::optional<Snowflake> ParentID; // null
|
||||
std::optional<std::string> LastPinTimestamp; // null
|
||||
std::optional<ThreadMetadataData> ThreadMetadata;
|
||||
std::optional<ThreadMemberObject> ThreadMember;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ChannelData &m);
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
|
||||
bool NSFW() const;
|
||||
bool IsThread() const noexcept;
|
||||
bool IsJoinedThread() const;
|
||||
std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
|
||||
std::vector<UserData> GetDMRecipients() const;
|
||||
};
|
||||
2310
src/discord/discord.cpp
Normal file
2310
src/discord/discord.cpp
Normal file
File diff suppressed because it is too large
Load Diff
449
src/discord/discord.hpp
Normal file
449
src/discord/discord.hpp
Normal file
@@ -0,0 +1,449 @@
|
||||
#pragma once
|
||||
#include "websocket.hpp"
|
||||
#include "httpclient.hpp"
|
||||
#include "objects.hpp"
|
||||
#include "store.hpp"
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <mutex>
|
||||
#include <zlib.h>
|
||||
#include <glibmm.h>
|
||||
#include <queue>
|
||||
|
||||
#ifdef GetMessage
|
||||
#undef GetMessage
|
||||
#endif
|
||||
|
||||
class HeartbeatWaiter {
|
||||
public:
|
||||
template<class R, class P>
|
||||
bool wait_for(std::chrono::duration<R, P> const &time) const {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
return !cv.wait_for(lock, time, [&] { return terminate; });
|
||||
}
|
||||
|
||||
void kill() {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
terminate = true;
|
||||
cv.notify_all();
|
||||
}
|
||||
|
||||
void revive() {
|
||||
std::unique_lock<std::mutex> lock(m);
|
||||
terminate = false;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable std::condition_variable cv;
|
||||
mutable std::mutex m;
|
||||
bool terminate = false;
|
||||
};
|
||||
|
||||
class Abaddon;
|
||||
class DiscordClient {
|
||||
friend class Abaddon;
|
||||
|
||||
public:
|
||||
DiscordClient(bool mem_store = false);
|
||||
void Start();
|
||||
void Stop();
|
||||
bool IsStarted() const;
|
||||
bool IsStoreValid() const;
|
||||
|
||||
std::unordered_set<Snowflake> GetGuilds() const;
|
||||
const UserData &GetUserData() const;
|
||||
const UserSettings &GetUserSettings() const;
|
||||
std::vector<Snowflake> GetUserSortedGuilds() const;
|
||||
std::vector<Message> GetMessagesForChannel(Snowflake id, size_t limit = 50) const;
|
||||
std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit = 50) const;
|
||||
std::set<Snowflake> GetPrivateChannels() const;
|
||||
|
||||
EPremiumType GetSelfPremiumType() const;
|
||||
|
||||
void FetchMessagesInChannel(Snowflake id, sigc::slot<void(const std::vector<Message> &)> cb);
|
||||
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, sigc::slot<void(const std::vector<Message> &)> cb);
|
||||
std::optional<Message> GetMessage(Snowflake id) const;
|
||||
std::optional<ChannelData> GetChannel(Snowflake id) const;
|
||||
std::optional<EmojiData> GetEmoji(Snowflake id) const;
|
||||
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
|
||||
std::optional<UserData> GetUser(Snowflake id) const;
|
||||
std::optional<RoleData> GetRole(Snowflake id) const;
|
||||
std::optional<GuildData> GetGuild(Snowflake id) const;
|
||||
std::optional<GuildMember> GetMember(Snowflake user_id, Snowflake guild_id) const;
|
||||
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
|
||||
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
|
||||
std::optional<RoleData> GetMemberHighestRole(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::set<Snowflake> GetUsersInGuild(Snowflake id) const;
|
||||
std::set<Snowflake> GetChannelsInGuild(Snowflake id) const;
|
||||
std::vector<Snowflake> GetUsersInThread(Snowflake id) const;
|
||||
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const;
|
||||
void GetArchivedPublicThreads(Snowflake channel_id, sigc::slot<void(DiscordError, const ArchivedThreadsResponseData &)> callback);
|
||||
|
||||
bool IsThreadJoined(Snowflake thread_id) const;
|
||||
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;
|
||||
|
||||
bool HasAnyChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
|
||||
bool HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
|
||||
Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const;
|
||||
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
|
||||
bool CanManageMember(Snowflake guild_id, Snowflake actor, Snowflake target) const; // kick, ban, edit nickname (cant think of a better name)
|
||||
|
||||
void ChatMessageCallback(std::string nonce, const http::response_type &response);
|
||||
|
||||
void SendChatMessage(const std::string &content, Snowflake channel);
|
||||
void SendChatMessage(const std::string &content, Snowflake channel, Snowflake referenced_message);
|
||||
void DeleteMessage(Snowflake channel_id, Snowflake id);
|
||||
void EditMessage(Snowflake channel_id, Snowflake id, std::string content);
|
||||
void SendLazyLoad(Snowflake id);
|
||||
void SendThreadLazyLoad(Snowflake id);
|
||||
void JoinGuild(std::string code);
|
||||
void LeaveGuild(Snowflake id);
|
||||
void KickUser(Snowflake user_id, Snowflake guild_id);
|
||||
void BanUser(Snowflake user_id, Snowflake guild_id); // todo: reason, delete messages
|
||||
void UpdateStatus(PresenceStatus status, bool is_afk);
|
||||
void UpdateStatus(PresenceStatus status, bool is_afk, const ActivityData &obj);
|
||||
void CreateDM(Snowflake user_id);
|
||||
void CreateDM(Snowflake user_id, sigc::slot<void(DiscordError code, Snowflake channel_id)> callback);
|
||||
void CloseDM(Snowflake channel_id);
|
||||
std::optional<Snowflake> FindDM(Snowflake user_id); // wont find group dms
|
||||
void AddReaction(Snowflake id, Glib::ustring param);
|
||||
void RemoveReaction(Snowflake id, Glib::ustring param);
|
||||
void SetGuildName(Snowflake id, const Glib::ustring &name);
|
||||
void SetGuildName(Snowflake id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
|
||||
void SetGuildIcon(Snowflake id, const std::string &data);
|
||||
void SetGuildIcon(Snowflake id, const std::string &data, sigc::slot<void(DiscordError code)> callback);
|
||||
void UnbanUser(Snowflake guild_id, Snowflake user_id);
|
||||
void UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(DiscordError code)> callback);
|
||||
void DeleteInvite(const std::string &code);
|
||||
void DeleteInvite(const std::string &code, sigc::slot<void(DiscordError code)> callback);
|
||||
void AddGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
|
||||
void RemoveGroupDMRecipient(Snowflake channel_id, Snowflake user_id);
|
||||
void ModifyRolePermissions(Snowflake guild_id, Snowflake role_id, Permission permissions, sigc::slot<void(DiscordError code)> callback);
|
||||
void ModifyRoleName(Snowflake guild_id, Snowflake role_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
|
||||
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, uint32_t color, sigc::slot<void(DiscordError code)> callback);
|
||||
void ModifyRoleColor(Snowflake guild_id, Snowflake role_id, Gdk::RGBA color, sigc::slot<void(DiscordError code)> callback);
|
||||
void ModifyRolePosition(Snowflake guild_id, Snowflake role_id, int position, sigc::slot<void(DiscordError code)> callback);
|
||||
void ModifyEmojiName(Snowflake guild_id, Snowflake emoji_id, const Glib::ustring &name, sigc::slot<void(DiscordError code)> callback);
|
||||
void DeleteEmoji(Snowflake guild_id, Snowflake emoji_id, sigc::slot<void(DiscordError code)> callback);
|
||||
std::optional<GuildApplicationData> GetGuildApplication(Snowflake guild_id) const;
|
||||
void RemoveRelationship(Snowflake id, sigc::slot<void(DiscordError code)> callback);
|
||||
void SendFriendRequest(const Glib::ustring &username, int discriminator, sigc::slot<void(DiscordError code)> callback);
|
||||
void PutRelationship(Snowflake id, sigc::slot<void(DiscordError code)> callback); // send fr by id, accept incoming
|
||||
void Pin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback);
|
||||
void Unpin(Snowflake channel_id, Snowflake message_id, sigc::slot<void(DiscordError code)> callback);
|
||||
void LeaveThread(Snowflake channel_id, const std::string &location, sigc::slot<void(DiscordError code)> callback);
|
||||
void ArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
|
||||
void UnArchiveThread(Snowflake channel_id, sigc::slot<void(DiscordError code)> callback);
|
||||
|
||||
bool CanModifyRole(Snowflake guild_id, Snowflake role_id) const;
|
||||
bool CanModifyRole(Snowflake guild_id, Snowflake role_id, Snowflake user_id) const;
|
||||
|
||||
// real client doesn't seem to use the single role endpoints so neither do we
|
||||
template<typename Iter>
|
||||
auto SetMemberRoles(Snowflake guild_id, Snowflake user_id, Iter begin, Iter end, sigc::slot<void(DiscordError code)> callback) {
|
||||
ModifyGuildMemberObject obj;
|
||||
obj.Roles = { begin, end };
|
||||
m_http.MakePATCH("/guilds/" + std::to_string(guild_id) + "/members/" + std::to_string(user_id), nlohmann::json(obj).dump(), [this, callback](const http::response_type &response) {
|
||||
if (CheckCode(response))
|
||||
callback(DiscordError::NONE);
|
||||
else
|
||||
callback(GetCodeFromResponse(response));
|
||||
});
|
||||
}
|
||||
|
||||
// FetchGuildBans fetches all bans+reasons via api, this func fetches stored bans (so usually just GUILD_BAN_ADD data)
|
||||
std::vector<BanData> GetBansInGuild(Snowflake guild_id);
|
||||
void FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback);
|
||||
void FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback);
|
||||
|
||||
void FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback);
|
||||
void FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback);
|
||||
|
||||
void FetchAuditLog(Snowflake guild_id, sigc::slot<void(AuditLogData)> callback);
|
||||
|
||||
void FetchGuildEmojis(Snowflake guild_id, sigc::slot<void(std::vector<EmojiData>)> callback);
|
||||
|
||||
void FetchUserProfile(Snowflake user_id, sigc::slot<void(UserProfileData)> callback);
|
||||
void FetchUserNote(Snowflake user_id, sigc::slot<void(std::string note)> callback);
|
||||
void SetUserNote(Snowflake user_id, std::string note);
|
||||
void SetUserNote(Snowflake user_id, std::string note, sigc::slot<void(DiscordError code)> callback);
|
||||
void FetchUserRelationships(Snowflake user_id, sigc::slot<void(std::vector<UserData>)> callback);
|
||||
|
||||
void FetchPinned(Snowflake id, sigc::slot<void(std::vector<Message>, DiscordError code)> callback);
|
||||
|
||||
bool IsVerificationRequired(Snowflake guild_id);
|
||||
void GetVerificationGateInfo(Snowflake guild_id, sigc::slot<void(std::optional<VerificationGateInfoObject>)> callback);
|
||||
void AcceptVerificationGate(Snowflake guild_id, VerificationGateInfoObject info, sigc::slot<void(DiscordError code)> callback);
|
||||
|
||||
void UpdateToken(std::string token);
|
||||
void SetUserAgent(std::string agent);
|
||||
|
||||
PresenceStatus GetUserStatus(Snowflake id) const;
|
||||
|
||||
std::map<Snowflake, RelationshipType> GetRelationships() const;
|
||||
std::set<Snowflake> GetRelationships(RelationshipType type) const;
|
||||
std::optional<RelationshipType> GetRelationship(Snowflake id) const;
|
||||
|
||||
private:
|
||||
static const constexpr int InflateChunkSize = 0x10000;
|
||||
std::vector<uint8_t> m_compressed_buf;
|
||||
std::vector<uint8_t> m_decompress_buf;
|
||||
z_stream m_zstream;
|
||||
|
||||
std::string GetAPIURL();
|
||||
std::string GetGatewayURL();
|
||||
|
||||
static DiscordError GetCodeFromResponse(const http::response_type &response);
|
||||
|
||||
void ProcessNewGuild(GuildData &guild);
|
||||
|
||||
void HandleGatewayMessageRaw(std::string str);
|
||||
void HandleGatewayMessage(std::string str);
|
||||
void HandleGatewayHello(const GatewayMessage &msg);
|
||||
void HandleGatewayReady(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageDeleteBulk(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildMemberUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayPresenceUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayChannelDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayChannelUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayChannelCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildRoleUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildRoleCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildRoleDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageReactionAdd(const GatewayMessage &msg);
|
||||
void HandleGatewayMessageReactionRemove(const GatewayMessage &msg);
|
||||
void HandleGatewayChannelRecipientAdd(const GatewayMessage &msg);
|
||||
void HandleGatewayChannelRecipientRemove(const GatewayMessage &msg);
|
||||
void HandleGatewayTypingStart(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildBanRemove(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildBanAdd(const GatewayMessage &msg);
|
||||
void HandleGatewayInviteCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayInviteDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayUserNoteUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildEmojisUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildJoinRequestCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildJoinRequestUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayGuildJoinRequestDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayRelationshipRemove(const GatewayMessage &msg);
|
||||
void HandleGatewayRelationshipAdd(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadCreate(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadDelete(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadListSync(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadMembersUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadMemberUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayThreadMemberListUpdate(const GatewayMessage &msg);
|
||||
void HandleGatewayReadySupplemental(const GatewayMessage &msg);
|
||||
void HandleGatewayReconnect(const GatewayMessage &msg);
|
||||
void HandleGatewayInvalidSession(const GatewayMessage &msg);
|
||||
void HeartbeatThread();
|
||||
void SendIdentify();
|
||||
void SendResume();
|
||||
|
||||
void HandleSocketOpen();
|
||||
void HandleSocketClose(uint16_t code);
|
||||
|
||||
bool CheckCode(const http::response_type &r);
|
||||
bool CheckCode(const http::response_type &r, int expected);
|
||||
|
||||
void StoreMessageData(Message &msg);
|
||||
|
||||
std::string m_token;
|
||||
|
||||
void AddUserToGuild(Snowflake user_id, Snowflake guild_id);
|
||||
std::map<Snowflake, std::set<Snowflake>> m_guild_to_users;
|
||||
std::map<Snowflake, std::set<Snowflake>> m_guild_to_channels;
|
||||
std::map<Snowflake, GuildApplicationData> m_guild_join_requests;
|
||||
std::map<Snowflake, PresenceStatus> m_user_to_status;
|
||||
std::map<Snowflake, RelationshipType> m_user_relationships;
|
||||
std::set<Snowflake> m_joined_threads;
|
||||
std::map<Snowflake, std::vector<Snowflake>> m_thread_members;
|
||||
|
||||
UserData m_user_data;
|
||||
UserSettings m_user_settings;
|
||||
|
||||
Store m_store;
|
||||
HTTPClient m_http;
|
||||
Websocket m_websocket;
|
||||
std::atomic<bool> m_client_connected = false;
|
||||
std::atomic<bool> m_ready_received = false;
|
||||
bool m_client_started = false;
|
||||
|
||||
std::map<std::string, GatewayEvent> m_event_map;
|
||||
void LoadEventMap();
|
||||
|
||||
std::thread m_heartbeat_thread;
|
||||
std::atomic<int> m_last_sequence = -1;
|
||||
std::atomic<int> m_heartbeat_msec = 0;
|
||||
HeartbeatWaiter m_heartbeat_waiter;
|
||||
std::atomic<bool> m_heartbeat_acked = true;
|
||||
|
||||
bool m_reconnecting = false; // reconnecting either to resume or reidentify
|
||||
bool m_wants_resume = false; // reconnecting specifically to resume
|
||||
std::string m_session_id;
|
||||
|
||||
mutable std::mutex m_msg_mutex;
|
||||
Glib::Dispatcher m_msg_dispatch;
|
||||
std::queue<std::string> m_msg_queue;
|
||||
void MessageDispatch();
|
||||
|
||||
mutable std::mutex m_generic_mutex;
|
||||
Glib::Dispatcher m_generic_dispatch;
|
||||
std::queue<std::function<void()>> m_generic_queue;
|
||||
|
||||
std::set<Snowflake> m_channels_pinned_requested;
|
||||
std::set<Snowflake> m_channels_lazy_loaded;
|
||||
|
||||
// signals
|
||||
public:
|
||||
typedef sigc::signal<void> type_signal_gateway_ready;
|
||||
typedef sigc::signal<void, Message> type_signal_message_create;
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_message_delete;
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_message_update;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_guild_member_list_update;
|
||||
typedef sigc::signal<void, GuildData> type_signal_guild_create;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_guild_delete;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_channel_delete;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_channel_update;
|
||||
typedef sigc::signal<void, ChannelData> type_signal_channel_create;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_guild_update;
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_role_update; // guild id, role id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_role_create; // guild id, role id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_role_delete; // guild id, role id
|
||||
typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_add;
|
||||
typedef sigc::signal<void, Snowflake, Glib::ustring> type_signal_reaction_remove;
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_typing_start; // user id, channel id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_member_update; // guild id, user id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_remove; // guild id, user id
|
||||
typedef sigc::signal<void, Snowflake, Snowflake> type_signal_guild_ban_add; // guild id, user id
|
||||
typedef sigc::signal<void, InviteData> type_signal_invite_create;
|
||||
typedef sigc::signal<void, InviteDeleteObject> type_signal_invite_delete;
|
||||
typedef sigc::signal<void, UserData, PresenceStatus> type_signal_presence_update;
|
||||
typedef sigc::signal<void, Snowflake, std::string> type_signal_note_update;
|
||||
typedef sigc::signal<void, Snowflake, std::vector<EmojiData>> type_signal_guild_emojis_update; // guild id
|
||||
typedef sigc::signal<void, GuildJoinRequestCreateData> type_signal_guild_join_request_create;
|
||||
typedef sigc::signal<void, GuildJoinRequestUpdateData> type_signal_guild_join_request_update;
|
||||
typedef sigc::signal<void, GuildJoinRequestDeleteData> type_signal_guild_join_request_delete;
|
||||
typedef sigc::signal<void, Snowflake, RelationshipType> type_signal_relationship_remove;
|
||||
typedef sigc::signal<void, RelationshipAddData> type_signal_relationship_add;
|
||||
typedef sigc::signal<void, ChannelData> type_signal_thread_create;
|
||||
typedef sigc::signal<void, ThreadDeleteData> type_signal_thread_delete;
|
||||
typedef sigc::signal<void, ThreadListSyncData> type_signal_thread_list_sync;
|
||||
typedef sigc::signal<void, ThreadMembersUpdateData> type_signal_thread_members_update;
|
||||
typedef sigc::signal<void, ThreadUpdateData> type_signal_thread_update;
|
||||
typedef sigc::signal<void, ThreadMemberListUpdateData> type_signal_thread_member_list_update;
|
||||
|
||||
// not discord dispatch events
|
||||
typedef sigc::signal<void, Snowflake> type_signal_added_to_thread;
|
||||
typedef sigc::signal<void, Snowflake> type_signal_removed_from_thread;
|
||||
typedef sigc::signal<void, Message> type_signal_message_unpinned;
|
||||
typedef sigc::signal<void, Message> type_signal_message_pinned;
|
||||
typedef sigc::signal<void, Message> type_signal_message_sent;
|
||||
|
||||
typedef sigc::signal<void, std::string /* nonce */, float /* retry_after */> type_signal_message_send_fail; // retry after param will be 0 if it failed for a reason that isnt slowmode
|
||||
typedef sigc::signal<void, bool, GatewayCloseCode> type_signal_disconnected; // bool true if reconnecting
|
||||
typedef sigc::signal<void> type_signal_connected;
|
||||
|
||||
type_signal_gateway_ready signal_gateway_ready();
|
||||
type_signal_message_create signal_message_create();
|
||||
type_signal_message_delete signal_message_delete();
|
||||
type_signal_message_update signal_message_update();
|
||||
type_signal_guild_member_list_update signal_guild_member_list_update();
|
||||
type_signal_guild_create signal_guild_create(); // structs are complete in this signal
|
||||
type_signal_guild_delete signal_guild_delete();
|
||||
type_signal_channel_delete signal_channel_delete();
|
||||
type_signal_channel_update signal_channel_update();
|
||||
type_signal_channel_create signal_channel_create();
|
||||
type_signal_guild_update signal_guild_update();
|
||||
type_signal_role_update signal_role_update();
|
||||
type_signal_role_create signal_role_create();
|
||||
type_signal_role_delete signal_role_delete();
|
||||
type_signal_reaction_add signal_reaction_add();
|
||||
type_signal_reaction_remove signal_reaction_remove();
|
||||
type_signal_typing_start signal_typing_start();
|
||||
type_signal_guild_member_update signal_guild_member_update();
|
||||
type_signal_guild_ban_remove signal_guild_ban_remove();
|
||||
type_signal_guild_ban_add signal_guild_ban_add();
|
||||
type_signal_invite_create signal_invite_create();
|
||||
type_signal_invite_delete signal_invite_delete(); // safe to assume guild id is set
|
||||
type_signal_presence_update signal_presence_update();
|
||||
type_signal_note_update signal_note_update();
|
||||
type_signal_guild_emojis_update signal_guild_emojis_update();
|
||||
type_signal_guild_join_request_create signal_guild_join_request_create();
|
||||
type_signal_guild_join_request_update signal_guild_join_request_update();
|
||||
type_signal_guild_join_request_delete signal_guild_join_request_delete();
|
||||
type_signal_relationship_remove signal_relationship_remove();
|
||||
type_signal_relationship_add signal_relationship_add();
|
||||
type_signal_message_unpinned signal_message_unpinned();
|
||||
type_signal_message_pinned signal_message_pinned();
|
||||
type_signal_thread_create signal_thread_create();
|
||||
type_signal_thread_delete signal_thread_delete();
|
||||
type_signal_thread_list_sync signal_thread_list_sync();
|
||||
type_signal_thread_members_update signal_thread_members_update();
|
||||
type_signal_thread_update signal_thread_update();
|
||||
type_signal_thread_member_list_update signal_thread_member_list_update();
|
||||
|
||||
type_signal_added_to_thread signal_added_to_thread();
|
||||
type_signal_removed_from_thread signal_removed_from_thread();
|
||||
type_signal_message_sent signal_message_sent();
|
||||
type_signal_message_send_fail signal_message_send_fail();
|
||||
type_signal_disconnected signal_disconnected();
|
||||
type_signal_connected signal_connected();
|
||||
|
||||
protected:
|
||||
type_signal_gateway_ready m_signal_gateway_ready;
|
||||
type_signal_message_create m_signal_message_create;
|
||||
type_signal_message_delete m_signal_message_delete;
|
||||
type_signal_message_update m_signal_message_update;
|
||||
type_signal_guild_member_list_update m_signal_guild_member_list_update;
|
||||
type_signal_guild_create m_signal_guild_create;
|
||||
type_signal_guild_delete m_signal_guild_delete;
|
||||
type_signal_channel_delete m_signal_channel_delete;
|
||||
type_signal_channel_update m_signal_channel_update;
|
||||
type_signal_channel_create m_signal_channel_create;
|
||||
type_signal_guild_update m_signal_guild_update;
|
||||
type_signal_role_update m_signal_role_update;
|
||||
type_signal_role_create m_signal_role_create;
|
||||
type_signal_role_delete m_signal_role_delete;
|
||||
type_signal_reaction_add m_signal_reaction_add;
|
||||
type_signal_reaction_remove m_signal_reaction_remove;
|
||||
type_signal_typing_start m_signal_typing_start;
|
||||
type_signal_guild_member_update m_signal_guild_member_update;
|
||||
type_signal_guild_ban_remove m_signal_guild_ban_remove;
|
||||
type_signal_guild_ban_add m_signal_guild_ban_add;
|
||||
type_signal_invite_create m_signal_invite_create;
|
||||
type_signal_invite_delete m_signal_invite_delete;
|
||||
type_signal_presence_update m_signal_presence_update;
|
||||
type_signal_note_update m_signal_note_update;
|
||||
type_signal_guild_emojis_update m_signal_guild_emojis_update;
|
||||
type_signal_guild_join_request_create m_signal_guild_join_request_create;
|
||||
type_signal_guild_join_request_update m_signal_guild_join_request_update;
|
||||
type_signal_guild_join_request_delete m_signal_guild_join_request_delete;
|
||||
type_signal_relationship_remove m_signal_relationship_remove;
|
||||
type_signal_relationship_add m_signal_relationship_add;
|
||||
type_signal_message_unpinned m_signal_message_unpinned;
|
||||
type_signal_message_pinned m_signal_message_pinned;
|
||||
type_signal_thread_create m_signal_thread_create;
|
||||
type_signal_thread_delete m_signal_thread_delete;
|
||||
type_signal_thread_list_sync m_signal_thread_list_sync;
|
||||
type_signal_thread_members_update m_signal_thread_members_update;
|
||||
type_signal_thread_update m_signal_thread_update;
|
||||
type_signal_thread_member_list_update m_signal_thread_member_list_update;
|
||||
|
||||
type_signal_removed_from_thread m_signal_removed_from_thread;
|
||||
type_signal_added_to_thread m_signal_added_to_thread;
|
||||
type_signal_message_sent m_signal_message_sent;
|
||||
type_signal_message_send_fail m_signal_message_send_fail;
|
||||
type_signal_disconnected m_signal_disconnected;
|
||||
type_signal_connected m_signal_connected;
|
||||
};
|
||||
51
src/discord/emoji.cpp
Normal file
51
src/discord/emoji.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "emoji.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, EmojiData &m) {
|
||||
JS_N("id", m.ID);
|
||||
JS_N("name", m.Name);
|
||||
JS_O("roles", m.Roles);
|
||||
JS_O("user", m.Creator);
|
||||
JS_O("require_colons", m.NeedsColons);
|
||||
JS_O("managed", m.IsManaged);
|
||||
JS_O("animated", m.IsAnimated);
|
||||
JS_O("available", m.IsAvailable);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmojiData &m) {
|
||||
if (m.ID.IsValid())
|
||||
j["id"] = m.ID;
|
||||
else
|
||||
j["id"] = nullptr;
|
||||
if (m.Name != "")
|
||||
j["name"] = m.Name;
|
||||
else
|
||||
j["name"] = nullptr;
|
||||
JS_IF("roles", m.Roles);
|
||||
JS_IF("user", m.Creator);
|
||||
JS_IF("require_colons", m.NeedsColons);
|
||||
JS_IF("managed", m.IsManaged);
|
||||
JS_IF("animated", m.IsAnimated);
|
||||
JS_IF("available", m.IsAvailable);
|
||||
}
|
||||
|
||||
std::string EmojiData::GetURL(const char *ext, const char *size) const {
|
||||
if (size != nullptr)
|
||||
return "https://cdn.discordapp.com/emojis/" + std::to_string(ID) + "." + ext + "?size=" + size;
|
||||
else
|
||||
return "https://cdn.discordapp.com/emojis/" + std::to_string(ID) + "." + ext;
|
||||
}
|
||||
|
||||
std::string EmojiData::URLFromID(const std::string &emoji_id, const char *ext, const char *size) {
|
||||
if (size != nullptr)
|
||||
return "https://cdn.discordapp.com/emojis/" + emoji_id + "." + ext + "?size=" + size;
|
||||
else
|
||||
return "https://cdn.discordapp.com/emojis/" + emoji_id + "." + ext;
|
||||
}
|
||||
|
||||
std::string EmojiData::URLFromID(Snowflake emoji_id, const char *ext, const char *size) {
|
||||
return URLFromID(std::to_string(emoji_id), ext, size);
|
||||
}
|
||||
|
||||
std::string EmojiData::URLFromID(const Glib::ustring &emoji_id, const char *ext, const char *size) {
|
||||
return URLFromID(emoji_id.raw(), ext, size);
|
||||
}
|
||||
25
src/discord/emoji.hpp
Normal file
25
src/discord/emoji.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
|
||||
struct EmojiData {
|
||||
Snowflake ID; // null
|
||||
std::string Name; // null (in reactions)
|
||||
std::optional<std::vector<Snowflake>> Roles;
|
||||
std::optional<UserData> Creator; // only reliable to access ID
|
||||
std::optional<bool> NeedsColons;
|
||||
std::optional<bool> IsManaged;
|
||||
std::optional<bool> IsAnimated;
|
||||
std::optional<bool> IsAvailable;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, EmojiData &m);
|
||||
friend void to_json(nlohmann::json &j, const EmojiData &m);
|
||||
|
||||
std::string GetURL(const char *ext = "png", const char *size = nullptr) const;
|
||||
static std::string URLFromID(const std::string &emoji_id, const char *ext = "png", const char *size = nullptr);
|
||||
static std::string URLFromID(Snowflake emoji_id, const char *ext = "png", const char *size = nullptr);
|
||||
static std::string URLFromID(const Glib::ustring &emoji_id, const char *ext = "png", const char *size = nullptr);
|
||||
};
|
||||
36
src/discord/errors.hpp
Normal file
36
src/discord/errors.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
enum class DiscordError {
|
||||
GENERIC = 0,
|
||||
INVALID_FORM_BODY = 50035,
|
||||
RELATIONSHIP_INCOMING_DISABLED = 80000,
|
||||
RELATIONSHIP_INCOMING_BLOCKED = 80001,
|
||||
RELATIONSHIP_INVALID_USER_BOT = 80002, // this is misspelled in discord's source lul
|
||||
RELATIONSHIP_INVALID_SELF = 80003,
|
||||
RELATIONSHIP_INVALID_DISCORD_TAG = 80004,
|
||||
RELATIONSHIP_ALREADY_FRIENDS = 80007,
|
||||
|
||||
NONE = -1,
|
||||
};
|
||||
|
||||
constexpr const char *GetDiscordErrorDisplayString(DiscordError error) {
|
||||
switch (error) {
|
||||
case DiscordError::INVALID_FORM_BODY:
|
||||
return "Something's wrong with your input";
|
||||
case DiscordError::RELATIONSHIP_INCOMING_DISABLED:
|
||||
return "This user isn't accepting friend requests";
|
||||
case DiscordError::RELATIONSHIP_INCOMING_BLOCKED:
|
||||
return "You are blocked by this user";
|
||||
case DiscordError::RELATIONSHIP_INVALID_USER_BOT:
|
||||
return "You can't send a request to a bot";
|
||||
case DiscordError::RELATIONSHIP_INVALID_SELF:
|
||||
return "You can't send a request to yourself";
|
||||
case DiscordError::RELATIONSHIP_INVALID_DISCORD_TAG:
|
||||
return "No users with that tag exist";
|
||||
case DiscordError::RELATIONSHIP_ALREADY_FRIENDS:
|
||||
return "You are already friends with that user";
|
||||
case DiscordError::GENERIC:
|
||||
default:
|
||||
return "An error occurred";
|
||||
}
|
||||
}
|
||||
221
src/discord/guild.cpp
Normal file
221
src/discord/guild.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "guild.hpp"
|
||||
#include "abaddon.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildData &m) {
|
||||
JS_D("id", m.ID);
|
||||
if (j.contains("unavailable")) {
|
||||
m.IsUnavailable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
JS_D("name", m.Name);
|
||||
JS_N("icon", m.Icon);
|
||||
JS_N("splash", m.Splash);
|
||||
JS_ON("discovery_splash", m.DiscoverySplash);
|
||||
JS_O("owner", m.IsOwner);
|
||||
JS_O("owner_id", m.OwnerID);
|
||||
std::optional<std::string> tmp;
|
||||
JS_O("permissions", tmp);
|
||||
if (tmp.has_value())
|
||||
m.Permissions = std::stoull(*tmp);
|
||||
JS_O("region", m.VoiceRegion);
|
||||
JS_ON("afk_channel_id", m.AFKChannelID);
|
||||
JS_O("afk_timeout", m.AFKTimeout);
|
||||
JS_O("embed_enabled", m.IsEmbedEnabled);
|
||||
JS_ON("embed_channel_id", m.EmbedChannelID);
|
||||
JS_O("verification_level", m.VerificationLevel);
|
||||
JS_O("default_message_notifications", m.DefaultMessageNotifications);
|
||||
JS_O("explicit_content_filter", m.ExplicitContentFilter);
|
||||
JS_O("roles", m.Roles);
|
||||
JS_O("emojis", m.Emojis);
|
||||
JS_O("features", m.Features);
|
||||
JS_O("mfa_level", m.MFALevel);
|
||||
JS_ON("application_id", m.ApplicationID);
|
||||
JS_O("widget_enabled", m.IsWidgetEnabled);
|
||||
JS_ON("widget_channel_id", m.WidgetChannelID);
|
||||
JS_ON("system_channel_id", m.SystemChannelID);
|
||||
JS_O("system_channel_flags", m.SystemChannelFlags);
|
||||
JS_ON("rules_channel_id", m.RulesChannelID);
|
||||
JS_O("joined_at", m.JoinedAt);
|
||||
JS_O("large", m.IsLarge);
|
||||
JS_O("unavailable", m.IsUnavailable);
|
||||
JS_O("member_count", m.MemberCount);
|
||||
// JS_O("voice_states", m.VoiceStates);
|
||||
// JS_O("members", m.Members);
|
||||
JS_O("channels", m.Channels);
|
||||
JS_O("threads", m.Threads);
|
||||
// JS_O("presences", m.Presences);
|
||||
JS_ON("max_presences", m.MaxPresences);
|
||||
JS_O("max_members", m.MaxMembers);
|
||||
JS_ON("vanity_url_code", m.VanityURL);
|
||||
JS_ON("description", m.Description);
|
||||
JS_ON("banner", m.BannerHash);
|
||||
JS_O("premium_tier", m.PremiumTier);
|
||||
JS_O("premium_subscription_count", m.PremiumSubscriptionCount);
|
||||
JS_O("preferred_locale", m.PreferredLocale);
|
||||
JS_ON("public_updates_channel_id", m.PublicUpdatesChannelID);
|
||||
JS_O("max_video_channel_users", m.MaxVideoChannelUsers);
|
||||
JS_O("approximate_member_count", tmp);
|
||||
if (tmp.has_value())
|
||||
m.ApproximateMemberCount = std::stol(*tmp);
|
||||
JS_O("approximate_presence_count", tmp);
|
||||
if (tmp.has_value())
|
||||
m.ApproximatePresenceCount = std::stol(*tmp);
|
||||
}
|
||||
|
||||
void GuildData::update_from_json(const nlohmann::json &j) {
|
||||
if (j.contains("unavailable")) {
|
||||
IsUnavailable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
JS_RD("name", Name);
|
||||
JS_RD("icon", Icon);
|
||||
JS_RD("splash", Splash);
|
||||
JS_RD("discovery_splash", DiscoverySplash);
|
||||
JS_RD("owner", IsOwner);
|
||||
JS_RD("owner_id", OwnerID);
|
||||
std::string tmp;
|
||||
JS_RD("permissions", tmp);
|
||||
if (tmp != "")
|
||||
Permissions = std::stoull(tmp);
|
||||
JS_RD("region", VoiceRegion);
|
||||
JS_RD("afk_channel_id", AFKChannelID);
|
||||
JS_RD("afk_timeout", AFKTimeout);
|
||||
JS_RD("embed_enabled", IsEmbedEnabled);
|
||||
JS_RD("embed_channel_id", EmbedChannelID);
|
||||
JS_RD("verification_level", VerificationLevel);
|
||||
JS_RD("default_message_notifications", DefaultMessageNotifications);
|
||||
JS_RD("explicit_content_filter", ExplicitContentFilter);
|
||||
JS_RD("roles", Roles);
|
||||
JS_RD("emojis", Emojis);
|
||||
JS_RD("features", Features);
|
||||
JS_RD("mfa_level", MFALevel);
|
||||
JS_RD("application_id", ApplicationID);
|
||||
JS_RD("widget_enabled", IsWidgetEnabled);
|
||||
JS_RD("widget_channel_id", WidgetChannelID);
|
||||
JS_RD("system_channel_id", SystemChannelID);
|
||||
JS_RD("system_channel_flags", SystemChannelFlags);
|
||||
JS_RD("rules_channel_id", RulesChannelID);
|
||||
JS_RD("joined_at", JoinedAt);
|
||||
JS_RD("large", IsLarge);
|
||||
JS_RD("unavailable", IsUnavailable);
|
||||
JS_RD("member_count", MemberCount);
|
||||
// JS_O("voice_states", m.VoiceStates);
|
||||
// JS_O("members", m.Members);
|
||||
JS_RD("channels", Channels);
|
||||
// JS_O("presences", m.Presences);
|
||||
JS_RD("max_presences", MaxPresences);
|
||||
JS_RD("max_members", MaxMembers);
|
||||
JS_RD("vanity_url_code", VanityURL);
|
||||
JS_RD("description", Description);
|
||||
JS_RD("banner", BannerHash);
|
||||
JS_RD("premium_tier", PremiumTier);
|
||||
JS_RD("premium_subscription_count", PremiumSubscriptionCount);
|
||||
JS_RD("preferred_locale", PreferredLocale);
|
||||
JS_RD("public_updates_channel_id", PublicUpdatesChannelID);
|
||||
JS_RD("max_video_channel_users", MaxVideoChannelUsers);
|
||||
JS_RD("approximate_member_count", ApproximateMemberCount);
|
||||
JS_RD("approximate_presence_count", ApproximatePresenceCount);
|
||||
}
|
||||
|
||||
bool GuildData::HasFeature(const std::string &search_feature) {
|
||||
if (!Features.has_value()) return false;
|
||||
for (const auto &feature : *Features)
|
||||
if (search_feature == feature)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GuildData::HasIcon() const {
|
||||
return Icon != "";
|
||||
}
|
||||
|
||||
bool GuildData::HasAnimatedIcon() const {
|
||||
return HasIcon() && Icon[0] == 'a' && Icon[1] == '_';
|
||||
}
|
||||
|
||||
std::string GuildData::GetIconURL(std::string ext, std::string size) const {
|
||||
return "https://cdn.discordapp.com/icons/" + std::to_string(ID) + "/" + Icon + "." + ext + "?size=" + size;
|
||||
}
|
||||
|
||||
std::vector<Snowflake> GuildData::GetSortedChannels(Snowflake ignore) const {
|
||||
std::vector<Snowflake> ret;
|
||||
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
auto channels = discord.GetChannelsInGuild(ID);
|
||||
|
||||
std::unordered_map<Snowflake, std::vector<ChannelData>> category_to_channels;
|
||||
std::map<int, std::vector<ChannelData>> position_to_categories;
|
||||
std::map<int, std::vector<ChannelData>> orphan_channels;
|
||||
for (const auto &channel_id : channels) {
|
||||
const auto data = discord.GetChannel(channel_id);
|
||||
if (!data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS))
|
||||
orphan_channels[*data->Position].push_back(*data);
|
||||
else if (data->ParentID.has_value() && (data->Type == ChannelType::GUILD_TEXT || data->Type == ChannelType::GUILD_NEWS))
|
||||
category_to_channels[*data->ParentID].push_back(*data);
|
||||
else if (data->Type == ChannelType::GUILD_CATEGORY)
|
||||
position_to_categories[*data->Position].push_back(*data);
|
||||
}
|
||||
|
||||
for (auto &[pos, channels] : orphan_channels) {
|
||||
std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
|
||||
return a.ID < b.ID;
|
||||
});
|
||||
for (const auto &chan : channels)
|
||||
ret.push_back(chan.ID);
|
||||
}
|
||||
|
||||
for (auto &[pos, categories] : position_to_categories) {
|
||||
std::sort(categories.begin(), categories.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
|
||||
return a.ID < b.ID;
|
||||
});
|
||||
for (const auto &category : categories) {
|
||||
ret.push_back(category.ID);
|
||||
if (ignore == category.ID) continue; // stupid hack to save me some time
|
||||
auto it = category_to_channels.find(category.ID);
|
||||
if (it == category_to_channels.end()) continue;
|
||||
auto &channels = it->second;
|
||||
std::sort(channels.begin(), channels.end(), [&](const ChannelData &a, const ChannelData &b) -> bool {
|
||||
return a.Position < b.Position;
|
||||
});
|
||||
for (auto &channel : channels) {
|
||||
ret.push_back(channel.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<RoleData> GuildData::FetchRoles() const {
|
||||
if (!Roles.has_value()) return {};
|
||||
std::vector<RoleData> ret;
|
||||
ret.reserve(Roles->size());
|
||||
for (const auto thing : *Roles) {
|
||||
auto r = Abaddon::Get().GetDiscordClient().GetRole(thing.ID);
|
||||
if (r.has_value())
|
||||
ret.push_back(*r);
|
||||
}
|
||||
std::sort(ret.begin(), ret.end(), [](const RoleData &a, const RoleData &b) -> bool {
|
||||
return a.Position > b.Position;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildApplicationData &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
auto tmp = j.at("application_status").get<std::string_view>();
|
||||
if (tmp == "STARTED")
|
||||
m.ApplicationStatus = GuildApplicationStatus::STARTED;
|
||||
else if (tmp == "PENDING")
|
||||
m.ApplicationStatus = GuildApplicationStatus::PENDING;
|
||||
else if (tmp == "REJECTED")
|
||||
m.ApplicationStatus = GuildApplicationStatus::REJECTED;
|
||||
else if (tmp == "APPROVED")
|
||||
m.ApplicationStatus = GuildApplicationStatus::APPROVED;
|
||||
JS_N("rejection_reason", m.RejectionReason);
|
||||
JS_N("last_seen", m.LastSeen);
|
||||
JS_N("created_at", m.CreatedAt);
|
||||
}
|
||||
100
src/discord/guild.hpp
Normal file
100
src/discord/guild.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include "role.hpp"
|
||||
#include "channel.hpp"
|
||||
#include "emoji.hpp"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
enum class GuildApplicationStatus {
|
||||
STARTED,
|
||||
PENDING,
|
||||
REJECTED,
|
||||
APPROVED,
|
||||
UNKNOWN,
|
||||
};
|
||||
|
||||
struct GuildApplicationData {
|
||||
Snowflake UserID;
|
||||
Snowflake GuildID;
|
||||
GuildApplicationStatus ApplicationStatus;
|
||||
std::optional<std::string> RejectionReason;
|
||||
std::optional<std::string> LastSeen;
|
||||
std::optional<std::string> CreatedAt;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildApplicationData &m);
|
||||
};
|
||||
|
||||
// a bot is apparently only supposed to receive the `id` and `unavailable` as false
|
||||
// but user tokens seem to get the full objects (minus users)
|
||||
|
||||
// everythings optional cuz of muh partial guild object
|
||||
// anything not marked optional in https://discord.com/developers/docs/resources/guild#guild-object is guaranteed to be set when returned from Store::GetGuild
|
||||
struct GuildData {
|
||||
Snowflake ID;
|
||||
std::string Name;
|
||||
std::string Icon; // null
|
||||
std::string Splash; // null
|
||||
std::optional<std::string> DiscoverySplash; // null
|
||||
std::optional<bool> IsOwner;
|
||||
std::optional<Snowflake> OwnerID;
|
||||
std::optional<uint64_t> Permissions;
|
||||
std::optional<std::string> PermissionsNew;
|
||||
std::optional<std::string> VoiceRegion;
|
||||
std::optional<Snowflake> AFKChannelID; // null
|
||||
std::optional<int> AFKTimeout;
|
||||
std::optional<bool> IsEmbedEnabled; // deprecated
|
||||
std::optional<Snowflake> EmbedChannelID; // null, deprecated
|
||||
std::optional<int> VerificationLevel;
|
||||
std::optional<int> DefaultMessageNotifications;
|
||||
std::optional<int> ExplicitContentFilter;
|
||||
std::optional<std::vector<RoleData>> Roles; // only access id
|
||||
std::optional<std::vector<EmojiData>> Emojis; // only access id
|
||||
std::optional<std::unordered_set<std::string>> Features;
|
||||
std::optional<int> MFALevel;
|
||||
std::optional<Snowflake> ApplicationID; // null
|
||||
std::optional<bool> IsWidgetEnabled;
|
||||
std::optional<Snowflake> WidgetChannelID; // null
|
||||
std::optional<Snowflake> SystemChannelID; // null
|
||||
std::optional<int> SystemChannelFlags;
|
||||
std::optional<Snowflake> RulesChannelID; // null
|
||||
std::optional<std::string> JoinedAt; // *
|
||||
std::optional<bool> IsLarge; // *
|
||||
std::optional<bool> IsUnavailable; // *
|
||||
std::optional<int> MemberCount; // *
|
||||
// std::vector<VoiceStateData> VoiceStates; // opt*
|
||||
// std::vector<MemberData> Members; // opt* - incomplete anyways
|
||||
std::optional<std::vector<ChannelData>> Channels; // *
|
||||
// std::vector<PresenceUpdateData> Presences; // opt*
|
||||
std::optional<int> MaxPresences; // null
|
||||
std::optional<int> MaxMembers;
|
||||
std::optional<std::string> VanityURL; // null
|
||||
std::optional<std::string> Description; // null
|
||||
std::optional<std::string> BannerHash; // null
|
||||
std::optional<int> PremiumTier;
|
||||
std::optional<int> PremiumSubscriptionCount;
|
||||
std::optional<std::string> PreferredLocale;
|
||||
std::optional<Snowflake> PublicUpdatesChannelID; // null
|
||||
std::optional<int> MaxVideoChannelUsers;
|
||||
std::optional<int> ApproximateMemberCount;
|
||||
std::optional<int> ApproximatePresenceCount;
|
||||
std::optional<std::vector<ChannelData>> Threads; // only with permissions to view, id only
|
||||
|
||||
// undocumented
|
||||
// std::map<std::string, Unknown> GuildHashes;
|
||||
bool IsLazy = false;
|
||||
|
||||
// * - documentation says only sent in GUILD_CREATE, but these can be sent anyways in the READY event
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildData &m);
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
|
||||
bool HasFeature(const std::string &feature);
|
||||
bool HasIcon() const;
|
||||
bool HasAnimatedIcon() const;
|
||||
std::string GetIconURL(std::string ext = "png", std::string size = "32") const;
|
||||
std::vector<Snowflake> GetSortedChannels(Snowflake ignore = Snowflake::Invalid) const;
|
||||
std::vector<RoleData> FetchRoles() const; // sorted
|
||||
};
|
||||
139
src/discord/httpclient.cpp
Normal file
139
src/discord/httpclient.cpp
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "httpclient.hpp"
|
||||
|
||||
//#define USE_LOCAL_PROXY
|
||||
HTTPClient::HTTPClient() {
|
||||
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
|
||||
}
|
||||
|
||||
void HTTPClient::SetBase(const std::string &url) {
|
||||
m_api_base = url;
|
||||
}
|
||||
|
||||
void HTTPClient::SetUserAgent(std::string agent) {
|
||||
m_agent = agent;
|
||||
}
|
||||
|
||||
void HTTPClient::SetAuth(std::string auth) {
|
||||
m_authorization = auth;
|
||||
}
|
||||
|
||||
void HTTPClient::MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb) {
|
||||
printf("DELETE %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
|
||||
http::request req(http::REQUEST_DELETE, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("PATCH %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_PATCH, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("POST %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_POST, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
|
||||
printf("PUT %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
|
||||
http::request req(http::REQUEST_PUT, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
if (payload != "")
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
req.set_body(payload);
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::MakeGET(const std::string &path, std::function<void(http::response_type r)> cb) {
|
||||
printf("GET %s\n", path.c_str());
|
||||
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
|
||||
http::request req(http::REQUEST_GET, m_api_base + path);
|
||||
req.set_header("Authorization", m_authorization);
|
||||
req.set_header("Content-Type", "application/json");
|
||||
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
|
||||
#ifdef USE_LOCAL_PROXY
|
||||
req.set_proxy("http://127.0.0.1:8888");
|
||||
req.set_verify_ssl(false);
|
||||
#endif
|
||||
|
||||
auto res = req.execute();
|
||||
|
||||
OnResponse(res, cb);
|
||||
}));
|
||||
}
|
||||
|
||||
void HTTPClient::CleanupFutures() {
|
||||
for (auto it = m_futures.begin(); it != m_futures.end();) {
|
||||
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
it = m_futures.erase(it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPClient::RunCallbacks() {
|
||||
m_mutex.lock();
|
||||
m_queue.front()();
|
||||
m_queue.pop();
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void HTTPClient::OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb) {
|
||||
CleanupFutures();
|
||||
try {
|
||||
m_mutex.lock();
|
||||
m_queue.push([this, r, cb] { cb(r); });
|
||||
m_dispatcher.emit();
|
||||
m_mutex.unlock();
|
||||
} catch (const std::exception &e) {
|
||||
fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what());
|
||||
}
|
||||
}
|
||||
39
src/discord/httpclient.hpp
Normal file
39
src/discord/httpclient.hpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <glibmm.h>
|
||||
#include "http.hpp"
|
||||
|
||||
class HTTPClient {
|
||||
public:
|
||||
HTTPClient();
|
||||
|
||||
void SetBase(const std::string &url);
|
||||
|
||||
void SetUserAgent(std::string agent);
|
||||
void SetAuth(std::string auth);
|
||||
void MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb);
|
||||
void MakeGET(const std::string &path, std::function<void(http::response_type r)> cb);
|
||||
void MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
void MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
void MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
|
||||
|
||||
private:
|
||||
void OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb);
|
||||
void CleanupFutures();
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
Glib::Dispatcher m_dispatcher;
|
||||
std::queue<std::function<void()>> m_queue;
|
||||
void RunCallbacks();
|
||||
|
||||
std::vector<std::future<void>> m_futures;
|
||||
std::string m_api_base;
|
||||
std::string m_authorization;
|
||||
std::string m_agent;
|
||||
};
|
||||
11
src/discord/interactions.cpp
Normal file
11
src/discord/interactions.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "interactions.hpp"
|
||||
#include "json.hpp"
|
||||
#include "abaddon.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageInteractionData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_D("name", m.Name);
|
||||
JS_D("user", m.User);
|
||||
JS_O("member", m.Member);
|
||||
}
|
||||
25
src/discord/interactions.hpp
Normal file
25
src/discord/interactions.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include "member.hpp"
|
||||
#include "user.hpp"
|
||||
#include "snowflake.hpp"
|
||||
|
||||
enum class InteractionType {
|
||||
Pong = 1, // ACK a Ping
|
||||
Acknowledge = 2, // DEPRECATED ACK a command without sending a message, eating the user's input
|
||||
ChannelMessage = 3, // DEPRECATED respond with a message, eating the user's input
|
||||
ChannelMessageWithSource = 4, // respond to an interaction with a message
|
||||
DeferredChannelMessageWithSource = 5, // ACK an interaction and edit to a response later, the user sees a loading state
|
||||
};
|
||||
|
||||
struct MessageInteractionData {
|
||||
Snowflake ID; // id of the interaction
|
||||
InteractionType Type; // the type of interaction
|
||||
std::string Name; // the name of the ApplicationCommand
|
||||
UserData User; // the user who invoked the interaction
|
||||
// undocumented???
|
||||
std::optional<GuildMember> Member; // the member who invoked the interaction (in a guild)
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageInteractionData &m);
|
||||
};
|
||||
39
src/discord/invite.cpp
Normal file
39
src/discord/invite.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "invite.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteChannelData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_ON("name", m.Name);
|
||||
if (j.contains("recipients") && j.at("recipients").is_null()) {
|
||||
m.RecipientUsernames.emplace();
|
||||
for (const auto &x : j.at("recipients"))
|
||||
m.RecipientUsernames->push_back(x.at("username").get<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteData &m) {
|
||||
JS_D("code", m.Code);
|
||||
JS_O("guild", m.Guild);
|
||||
JS_O("channel", m.Channel);
|
||||
JS_O("inviter", m.Inviter);
|
||||
JS_O("target_user", m.TargetUser);
|
||||
JS_O("target_user_type", m.TargetUserType);
|
||||
JS_O("approximate_presence_count", m.PresenceCount);
|
||||
JS_O("approximate_member_count", m.MemberCount);
|
||||
JS_O("uses", m.Uses);
|
||||
JS_O("max_uses", m.MaxUses);
|
||||
JS_O("max_age", m.MaxAge);
|
||||
JS_O("temporary", m.IsTemporary);
|
||||
JS_O("created_at", m.CreatedAt);
|
||||
}
|
||||
|
||||
InviteChannelData::InviteChannelData(const ChannelData &c) {
|
||||
ID = c.ID;
|
||||
Type = c.Type;
|
||||
Name = c.Name;
|
||||
if (Type == ChannelType::GROUP_DM) {
|
||||
RecipientUsernames.emplace();
|
||||
for (const auto &r : c.GetDMRecipients())
|
||||
RecipientUsernames->push_back(r.Username);
|
||||
}
|
||||
}
|
||||
41
src/discord/invite.hpp
Normal file
41
src/discord/invite.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
#include "guild.hpp"
|
||||
#include <string>
|
||||
|
||||
enum class ETargetUserType {
|
||||
STREAM = 1
|
||||
};
|
||||
|
||||
class InviteChannelData {
|
||||
public:
|
||||
InviteChannelData() = default;
|
||||
InviteChannelData(const ChannelData &c);
|
||||
|
||||
Snowflake ID;
|
||||
ChannelType Type;
|
||||
std::optional<std::string> Name;
|
||||
std::optional<std::vector<std::string>> RecipientUsernames;
|
||||
// std::optional<??> Icon;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteChannelData &m);
|
||||
};
|
||||
|
||||
class InviteData {
|
||||
public:
|
||||
std::string Code;
|
||||
std::optional<GuildData> Guild;
|
||||
std::optional<InviteChannelData> Channel;
|
||||
std::optional<UserData> Inviter;
|
||||
std::optional<UserData> TargetUser;
|
||||
std::optional<ETargetUserType> TargetUserType;
|
||||
std::optional<int> PresenceCount;
|
||||
std::optional<int> MemberCount;
|
||||
std::optional<int> Uses;
|
||||
std::optional<int> MaxUses;
|
||||
std::optional<int> MaxAge;
|
||||
std::optional<bool> IsTemporary;
|
||||
std::optional<std::string> CreatedAt;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteData &m);
|
||||
};
|
||||
148
src/discord/json.hpp
Normal file
148
src/discord/json.hpp
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <optional>
|
||||
#include "util.hpp"
|
||||
|
||||
namespace detail { // more or less because idk what to name this stuff
|
||||
template<typename T>
|
||||
inline void json_direct(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (::util::is_optional<T>::value)
|
||||
val = j.at(key).get<typename T::value_type>();
|
||||
else
|
||||
j.at(key).get_to(val);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void json_optional(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key))
|
||||
val = j.at(key).get<typename T::value_type>();
|
||||
else
|
||||
val = ::std::nullopt;
|
||||
} else {
|
||||
if (j.contains(key))
|
||||
j.at(key).get_to(val);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void json_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
val = at.get<typename T::value_type>();
|
||||
else
|
||||
val = ::std::nullopt;
|
||||
} else {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
at.get_to(val);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void json_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
val = at.get<typename T::value_type>();
|
||||
else
|
||||
val = ::std::nullopt;
|
||||
} else {
|
||||
val = ::std::nullopt;
|
||||
}
|
||||
} else {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
at.get_to(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void json_update_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
val = at.get<typename T::value_type>();
|
||||
else
|
||||
val = ::std::nullopt;
|
||||
}
|
||||
} else {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
at.get_to(val);
|
||||
else
|
||||
val = T();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
inline void json_update_optional_nullable_default(const ::nlohmann::json &j, const char *key, T &val, const U &fallback) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (at.is_null())
|
||||
val = fallback;
|
||||
else
|
||||
val = at.get<typename T::value_type>();
|
||||
}
|
||||
} else {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (at.is_null())
|
||||
val = fallback;
|
||||
else
|
||||
at.get_to(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
// get a json value that is guaranteed to be present and non-null
|
||||
#define JS_D(k, t) \
|
||||
do { \
|
||||
detail::json_direct(j, k, t); \
|
||||
} while (0)
|
||||
|
||||
// get a json value that may not be present
|
||||
#define JS_O(k, t) \
|
||||
do { \
|
||||
detail::json_optional(j, k, t); \
|
||||
} while (0)
|
||||
|
||||
// get a json value that may be null
|
||||
#define JS_N(k, t) \
|
||||
do { \
|
||||
detail::json_nullable(j, k, t); \
|
||||
} while (0)
|
||||
|
||||
// get a json value that may not be present or may be null
|
||||
#define JS_ON(k, t) \
|
||||
do { \
|
||||
detail::json_optional_nullable(j, k, t); \
|
||||
} while (0)
|
||||
|
||||
// set from a json value only if it is present. null will assign default-constructed value
|
||||
#define JS_RD(k, t) \
|
||||
do { \
|
||||
detail::json_update_optional_nullable(j, k, t); \
|
||||
} while (0)
|
||||
|
||||
// set from a json value only if it is present. null will assign the given default
|
||||
#define JS_RV(k, t, d) \
|
||||
do { \
|
||||
detail::json_update_optional_nullable_default(j, k, t, d); \
|
||||
} while (0)
|
||||
|
||||
// set a json value from a std::optional only if it has a value
|
||||
#define JS_IF(k, v) \
|
||||
do { \
|
||||
if (v.has_value()) \
|
||||
j[k] = *v; \
|
||||
} while (0)
|
||||
40
src/discord/member.cpp
Normal file
40
src/discord/member.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "member.hpp"
|
||||
#include "abaddon.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMember &m) {
|
||||
JS_O("user", m.User);
|
||||
JS_ON("nick", m.Nickname);
|
||||
JS_D("roles", m.Roles);
|
||||
JS_D("joined_at", m.JoinedAt);
|
||||
JS_ON("premium_since", m.PremiumSince);
|
||||
JS_D("deaf", m.IsDeafened);
|
||||
JS_D("mute", m.IsMuted);
|
||||
JS_O("user_id", m.UserID);
|
||||
JS_ON("avatar", m.Avatar);
|
||||
JS_O("pending", m.IsPending);
|
||||
}
|
||||
|
||||
std::vector<RoleData> GuildMember::GetSortedRoles() const {
|
||||
std::vector<RoleData> roles;
|
||||
for (const auto role_id : Roles) {
|
||||
const auto role = Abaddon::Get().GetDiscordClient().GetRole(role_id);
|
||||
if (!role.has_value()) continue;
|
||||
roles.push_back(std::move(*role));
|
||||
}
|
||||
|
||||
std::sort(roles.begin(), roles.end(), [](const RoleData &a, const RoleData &b) {
|
||||
return a.Position > b.Position;
|
||||
});
|
||||
|
||||
return roles;
|
||||
}
|
||||
|
||||
void GuildMember::update_from_json(const nlohmann::json &j) {
|
||||
JS_RD("roles", Roles);
|
||||
JS_RD("user", User);
|
||||
JS_RD("nick", Nickname);
|
||||
JS_RD("joined_at", JoinedAt);
|
||||
JS_RD("premium_since", PremiumSince);
|
||||
JS_RD("avatar", Avatar);
|
||||
JS_RD("pending", IsPending);
|
||||
}
|
||||
27
src/discord/member.hpp
Normal file
27
src/discord/member.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include "user.hpp"
|
||||
#include "role.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct GuildMember {
|
||||
std::optional<UserData> User; // only reliable to access id. only opt in MESSAGE_*
|
||||
std::string Nickname;
|
||||
std::vector<Snowflake> Roles;
|
||||
std::string JoinedAt;
|
||||
std::optional<std::string> PremiumSince; // null
|
||||
bool IsDeafened;
|
||||
bool IsMuted;
|
||||
std::optional<Snowflake> UserID; // present in merged_members
|
||||
std::optional<bool> IsPending; // this uses `pending` not `is_pending`
|
||||
|
||||
// undocuemtned moment !!!1
|
||||
std::optional<std::string> Avatar;
|
||||
|
||||
std::vector<RoleData> GetSortedRoles() const;
|
||||
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
friend void from_json(const nlohmann::json &j, GuildMember &m);
|
||||
};
|
||||
265
src/discord/message.cpp
Normal file
265
src/discord/message.cpp
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "message.hpp"
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedFooterData &m) {
|
||||
j["text"] = m.Text;
|
||||
JS_IF("icon_url", m.IconURL);
|
||||
JS_IF("proxy_icon_url", m.ProxyIconURL);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedFooterData &m) {
|
||||
JS_D("text", m.Text);
|
||||
JS_O("icon_url", m.IconURL);
|
||||
JS_O("proxy_icon_url", m.ProxyIconURL);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedImageData &m) {
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("proxy_url", m.ProxyURL);
|
||||
JS_IF("height", m.Height);
|
||||
JS_IF("width", m.Width);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedImageData &m) {
|
||||
JS_O("url", m.URL);
|
||||
JS_O("proxy_url", m.ProxyURL);
|
||||
JS_O("height", m.Height);
|
||||
JS_O("width", m.Width);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedThumbnailData &m) {
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("proxy_url", m.ProxyURL);
|
||||
JS_IF("height", m.Height);
|
||||
JS_IF("width", m.Width);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedThumbnailData &m) {
|
||||
JS_O("url", m.URL);
|
||||
JS_O("proxy_url", m.ProxyURL);
|
||||
JS_O("height", m.Height);
|
||||
JS_O("width", m.Width);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedVideoData &m) {
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("height", m.Height);
|
||||
JS_IF("width", m.Width);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedVideoData &m) {
|
||||
JS_O("url", m.URL);
|
||||
JS_O("height", m.Height);
|
||||
JS_O("width", m.Width);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedProviderData &m) {
|
||||
JS_IF("name", m.Name);
|
||||
JS_IF("url", m.URL);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedProviderData &m) {
|
||||
JS_O("name", m.Name);
|
||||
JS_ON("url", m.URL);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedAuthorData &m) {
|
||||
JS_IF("name", m.Name);
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("icon_url", m.IconURL);
|
||||
JS_IF("proxy_icon_url", m.ProxyIconURL);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedAuthorData &m) {
|
||||
JS_O("name", m.Name);
|
||||
JS_O("url", m.URL);
|
||||
JS_O("icon_url", m.IconURL);
|
||||
JS_O("proxy_icon_url", m.ProxyIconURL);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedFieldData &m) {
|
||||
j["name"] = m.Name;
|
||||
j["value"] = m.Value;
|
||||
JS_IF("inline", m.Inline);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedFieldData &m) {
|
||||
JS_D("name", m.Name);
|
||||
JS_D("value", m.Value);
|
||||
JS_O("inline", m.Inline);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const EmbedData &m) {
|
||||
JS_IF("title", m.Title);
|
||||
JS_IF("type", m.Type);
|
||||
JS_IF("description", m.Description);
|
||||
JS_IF("url", m.URL);
|
||||
JS_IF("timestamp", m.Timestamp);
|
||||
JS_IF("color", m.Color);
|
||||
JS_IF("footer", m.Footer);
|
||||
JS_IF("image", m.Image);
|
||||
JS_IF("thumbnail", m.Thumbnail);
|
||||
JS_IF("video", m.Video);
|
||||
JS_IF("provider", m.Provider);
|
||||
JS_IF("author", m.Author);
|
||||
JS_IF("fields", m.Fields);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, EmbedData &m) {
|
||||
JS_O("title", m.Title);
|
||||
JS_O("type", m.Type);
|
||||
JS_O("description", m.Description);
|
||||
JS_O("url", m.URL);
|
||||
JS_O("timestamp", m.Timestamp);
|
||||
JS_O("color", m.Color);
|
||||
JS_O("footer", m.Footer);
|
||||
JS_O("image", m.Image);
|
||||
JS_O("thumbnail", m.Thumbnail);
|
||||
JS_O("video", m.Video);
|
||||
JS_O("provider", m.Provider);
|
||||
JS_O("author", m.Author);
|
||||
JS_O("fields", m.Fields);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const AttachmentData &m) {
|
||||
j["id"] = m.ID;
|
||||
j["filename"] = m.Filename;
|
||||
j["size"] = m.Bytes;
|
||||
j["url"] = m.URL;
|
||||
j["proxy_url"] = m.ProxyURL;
|
||||
JS_IF("height", m.Height);
|
||||
JS_IF("width", m.Width);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, AttachmentData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("filename", m.Filename);
|
||||
JS_D("size", m.Bytes);
|
||||
JS_D("url", m.URL);
|
||||
JS_D("proxy_url", m.ProxyURL);
|
||||
JS_ON("height", m.Height);
|
||||
JS_ON("width", m.Width);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageReferenceData &m) {
|
||||
JS_O("message_id", m.MessageID);
|
||||
JS_O("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const MessageReferenceData &m) {
|
||||
JS_IF("message_id", m.MessageID);
|
||||
JS_IF("channel_id", m.ChannelID);
|
||||
JS_IF("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ReactionData &m) {
|
||||
JS_D("count", m.Count);
|
||||
JS_D("me", m.HasReactedWith);
|
||||
JS_D("emoji", m.Emoji);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ReactionData &m) {
|
||||
j["count"] = m.Count;
|
||||
j["me"] = m.HasReactedWith;
|
||||
j["emoji"] = m.Emoji;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageApplicationData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_O("cover_image", m.CoverImage);
|
||||
JS_D("description", m.Description);
|
||||
JS_N("icon", m.Icon);
|
||||
JS_D("name", m.Name);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const MessageApplicationData &m) {
|
||||
j["id"] = m.ID;
|
||||
JS_IF("cover_image", m.CoverImage);
|
||||
j["description"] = m.Description;
|
||||
if (m.Icon == "")
|
||||
j["icon"] = nullptr;
|
||||
else
|
||||
j["icon"] = m.Icon;
|
||||
j["name"] = m.Name;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, Message &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("author", m.Author);
|
||||
JS_O("member", m.Member);
|
||||
JS_D("content", m.Content);
|
||||
JS_D("timestamp", m.Timestamp);
|
||||
JS_N("edited_timestamp", m.EditedTimestamp);
|
||||
if (!j.at("edited_timestamp").is_null())
|
||||
m.SetEdited();
|
||||
JS_D("tts", m.IsTTS);
|
||||
JS_D("mention_everyone", m.DoesMentionEveryone);
|
||||
JS_D("mentions", m.Mentions);
|
||||
// JS_D("mention_roles", m.MentionRoles);
|
||||
// JS_O("mention_channels", m.MentionChannels);
|
||||
JS_D("attachments", m.Attachments);
|
||||
JS_D("embeds", m.Embeds);
|
||||
JS_O("reactions", m.Reactions);
|
||||
JS_O("nonce", m.Nonce);
|
||||
JS_D("pinned", m.IsPinned);
|
||||
JS_O("webhook_id", m.WebhookID);
|
||||
JS_D("type", m.Type);
|
||||
// JS_O("activity", m.Activity);
|
||||
JS_O("application", m.Application);
|
||||
JS_O("message_reference", m.MessageReference);
|
||||
JS_O("flags", m.Flags);
|
||||
JS_O("stickers", m.Stickers);
|
||||
if (j.contains("referenced_message")) {
|
||||
if (!j.at("referenced_message").is_null()) {
|
||||
m.ReferencedMessage = std::make_shared<Message>(j.at("referenced_message").get<Message>());
|
||||
} else
|
||||
m.ReferencedMessage = nullptr;
|
||||
}
|
||||
JS_O("interaction", m.Interaction);
|
||||
JS_O("sticker_items", m.StickerItems);
|
||||
}
|
||||
|
||||
void Message::from_json_edited(const nlohmann::json &j) {
|
||||
JS_D("id", ID);
|
||||
JS_D("channel_id", ChannelID);
|
||||
JS_O("guild_id", GuildID);
|
||||
JS_O("author", Author);
|
||||
JS_O("member", Member);
|
||||
JS_O("content", Content);
|
||||
JS_O("timestamp", Timestamp);
|
||||
JS_ON("edited_timestamp", EditedTimestamp);
|
||||
if (EditedTimestamp.size() > 0)
|
||||
SetEdited();
|
||||
JS_O("tts", IsTTS);
|
||||
JS_O("mention_everyone", DoesMentionEveryone);
|
||||
JS_O("mentions", Mentions);
|
||||
JS_O("embeds", Embeds);
|
||||
JS_O("nonce", Nonce);
|
||||
JS_O("pinned", IsPinned);
|
||||
JS_O("webhook_id", WebhookID);
|
||||
JS_O("type", Type);
|
||||
JS_O("application", Application);
|
||||
JS_O("message_reference", MessageReference);
|
||||
JS_O("flags", Flags);
|
||||
JS_O("stickers", Stickers);
|
||||
JS_O("interaction", Interaction);
|
||||
JS_O("sticker_items", StickerItems);
|
||||
}
|
||||
|
||||
void Message::SetDeleted() {
|
||||
m_deleted = true;
|
||||
}
|
||||
|
||||
void Message::SetEdited() {
|
||||
m_edited = true;
|
||||
}
|
||||
|
||||
bool Message::IsDeleted() const {
|
||||
return m_deleted;
|
||||
}
|
||||
|
||||
bool Message::IsEdited() const {
|
||||
return m_edited;
|
||||
}
|
||||
218
src/discord/message.hpp
Normal file
218
src/discord/message.hpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include "user.hpp"
|
||||
#include "sticker.hpp"
|
||||
#include "emoji.hpp"
|
||||
#include "member.hpp"
|
||||
#include "interactions.hpp"
|
||||
|
||||
enum class MessageType {
|
||||
DEFAULT = 0, // yep
|
||||
RECIPIENT_ADD = 1, // yep
|
||||
RECIPIENT_REMOVE = 2, // yep
|
||||
CALL = 3, // yep (sorta)
|
||||
CHANNEL_NAME_CHANGE = 4, // yep
|
||||
CHANNEL_ICON_CHANGE = 5, // yep
|
||||
CHANNEL_PINNED_MESSAGE = 6, // yep
|
||||
GUILD_MEMBER_JOIN = 7, // yep
|
||||
USER_PREMIUM_GUILD_SUBSCRIPTION = 8, // yep
|
||||
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_1 = 9, // yep
|
||||
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_2 = 10, // yep
|
||||
USER_PREMIUM_GUILD_SUBSCRIPTION_TIER_3 = 11, // yep
|
||||
CHANNEL_FOLLOW_ADD = 12, // yep
|
||||
GUILD_DISCOVERY_DISQUALIFIED = 14, // yep
|
||||
GUILD_DISCOVERY_REQUALIFIED = 15, // yep
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_INITIAL_WARNING = 16, // yep
|
||||
GUILD_DISCOVERY_GRACE_PERIOD_FINAL_WARNING = 17, // yep
|
||||
THREAD_CREATED = 18, // yep
|
||||
INLINE_REPLY = 19, // yep
|
||||
APPLICATION_COMMAND = 20, // yep
|
||||
THREAD_STARTER_MESSAGE = 21, // nope
|
||||
};
|
||||
|
||||
enum class MessageFlags {
|
||||
NONE = 0,
|
||||
CROSSPOSTED = 1 << 0, // this message has been published to subscribed channels (via Channel Following)
|
||||
IS_CROSSPOST = 1 << 1, // this message originated from a message in another channel (via Channel Following)
|
||||
SUPPRESS_EMBEDS = 1 << 2, // do not include any embeds when serializing this message
|
||||
SOURCE_MESSAGE_DELETE = 1 << 3, // the source message for this crosspost has been deleted (via Channel Following)
|
||||
URGENT = 1 << 4, // this message came from the urgent message system
|
||||
HAS_THREAD = 1 << 5, // this message has an associated thread, with the same id as the message
|
||||
EPHEMERAL = 1 << 6, // this message is only visible to the user who invoked the Interaction
|
||||
LOADING = 1 << 7, // this message is an Interaction Response and the bot is "thinking"
|
||||
};
|
||||
|
||||
struct EmbedFooterData {
|
||||
std::string Text;
|
||||
std::optional<std::string> IconURL;
|
||||
std::optional<std::string> ProxyIconURL;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedFooterData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedFooterData &m);
|
||||
};
|
||||
|
||||
struct EmbedImageData {
|
||||
std::optional<std::string> URL;
|
||||
std::optional<std::string> ProxyURL;
|
||||
std::optional<int> Height;
|
||||
std::optional<int> Width;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedImageData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedImageData &m);
|
||||
};
|
||||
|
||||
struct EmbedThumbnailData {
|
||||
std::optional<std::string> URL;
|
||||
std::optional<std::string> ProxyURL;
|
||||
std::optional<int> Height;
|
||||
std::optional<int> Width;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedThumbnailData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedThumbnailData &m);
|
||||
};
|
||||
|
||||
struct EmbedVideoData {
|
||||
std::optional<std::string> URL;
|
||||
std::optional<int> Height;
|
||||
std::optional<int> Width;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedVideoData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedVideoData &m);
|
||||
};
|
||||
|
||||
struct EmbedProviderData {
|
||||
std::optional<std::string> Name;
|
||||
std::optional<std::string> URL; // null
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedProviderData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedProviderData &m);
|
||||
};
|
||||
|
||||
struct EmbedAuthorData {
|
||||
std::optional<std::string> Name;
|
||||
std::optional<std::string> URL;
|
||||
std::optional<std::string> IconURL;
|
||||
std::optional<std::string> ProxyIconURL;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedAuthorData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedAuthorData &m);
|
||||
};
|
||||
|
||||
struct EmbedFieldData {
|
||||
std::string Name;
|
||||
std::string Value;
|
||||
std::optional<bool> Inline;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedFieldData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedFieldData &m);
|
||||
};
|
||||
|
||||
struct EmbedData {
|
||||
std::optional<std::string> Title;
|
||||
std::optional<std::string> Type;
|
||||
std::optional<std::string> Description;
|
||||
std::optional<std::string> URL;
|
||||
std::optional<std::string> Timestamp;
|
||||
std::optional<int> Color;
|
||||
std::optional<EmbedFooterData> Footer;
|
||||
std::optional<EmbedImageData> Image;
|
||||
std::optional<EmbedThumbnailData> Thumbnail;
|
||||
std::optional<EmbedVideoData> Video;
|
||||
std::optional<EmbedProviderData> Provider;
|
||||
std::optional<EmbedAuthorData> Author;
|
||||
std::optional<std::vector<EmbedFieldData>> Fields;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const EmbedData &m);
|
||||
friend void from_json(const nlohmann::json &j, EmbedData &m);
|
||||
};
|
||||
|
||||
struct AttachmentData {
|
||||
Snowflake ID;
|
||||
std::string Filename;
|
||||
int Bytes;
|
||||
std::string URL;
|
||||
std::string ProxyURL;
|
||||
std::optional<int> Height; // null
|
||||
std::optional<int> Width; // null
|
||||
|
||||
friend void to_json(nlohmann::json &j, const AttachmentData &m);
|
||||
friend void from_json(const nlohmann::json &j, AttachmentData &m);
|
||||
};
|
||||
|
||||
struct MessageReferenceData {
|
||||
std::optional<Snowflake> MessageID;
|
||||
std::optional<Snowflake> ChannelID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageReferenceData &m);
|
||||
friend void to_json(nlohmann::json &j, const MessageReferenceData &m);
|
||||
};
|
||||
|
||||
struct ReactionData {
|
||||
int Count;
|
||||
bool HasReactedWith;
|
||||
EmojiData Emoji;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ReactionData &m);
|
||||
friend void to_json(nlohmann::json &j, const ReactionData &m);
|
||||
};
|
||||
|
||||
struct MessageApplicationData {
|
||||
Snowflake ID;
|
||||
std::optional<std::string> CoverImage;
|
||||
std::string Description;
|
||||
std::string Icon; // null
|
||||
std::string Name;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageApplicationData &m);
|
||||
friend void to_json(nlohmann::json &j, const MessageApplicationData &m);
|
||||
};
|
||||
|
||||
struct Message {
|
||||
Snowflake ID;
|
||||
Snowflake ChannelID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
UserData Author;
|
||||
std::optional<GuildMember> Member;
|
||||
std::string Content;
|
||||
std::string Timestamp;
|
||||
std::string EditedTimestamp; // null
|
||||
bool IsTTS;
|
||||
bool DoesMentionEveryone;
|
||||
std::vector<UserData> Mentions; // full user accessible
|
||||
// std::vector<RoleData> MentionRoles;
|
||||
// std::optional<std::vector<ChannelMentionData>> MentionChannels;
|
||||
std::vector<AttachmentData> Attachments;
|
||||
std::vector<EmbedData> Embeds;
|
||||
std::optional<std::vector<ReactionData>> Reactions;
|
||||
std::optional<std::string> Nonce;
|
||||
bool IsPinned;
|
||||
std::optional<Snowflake> WebhookID;
|
||||
MessageType Type;
|
||||
// std::optional<MessageActivityData> ActivityData;
|
||||
std::optional<MessageApplicationData> Application;
|
||||
std::optional<MessageReferenceData> MessageReference;
|
||||
std::optional<MessageFlags> Flags = MessageFlags::NONE;
|
||||
std::optional<std::vector<StickerData>> Stickers;
|
||||
std::optional<std::shared_ptr<Message>> ReferencedMessage; // has_value && null means deleted
|
||||
std::optional<MessageInteractionData> Interaction;
|
||||
std::optional<std::vector<StickerItem>> StickerItems;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, Message &m);
|
||||
void from_json_edited(const nlohmann::json &j); // for MESSAGE_UPDATE
|
||||
|
||||
// custom fields to track changes
|
||||
bool IsPending = false; // for user-sent messages yet to be received in a MESSAGE_CREATE
|
||||
|
||||
void SetDeleted();
|
||||
void SetEdited();
|
||||
bool IsDeleted() const;
|
||||
bool IsEdited() const;
|
||||
|
||||
private:
|
||||
bool m_deleted = false;
|
||||
bool m_edited = false;
|
||||
};
|
||||
534
src/discord/objects.cpp
Normal file
534
src/discord/objects.cpp
Normal file
@@ -0,0 +1,534 @@
|
||||
#include "objects.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, GatewayMessage &m) {
|
||||
JS_D("op", m.Opcode);
|
||||
m.Data = j.at("d");
|
||||
|
||||
JS_ON("t", m.Type);
|
||||
JS_ON("s", m.Sequence);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, HelloMessageData &m) {
|
||||
JS_D("heartbeat_interval", m.HeartbeatInterval);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageDeleteData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageDeleteBulkData &m) {
|
||||
JS_D("ids", m.IDs);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::GroupItem &m) {
|
||||
m.Type = "group";
|
||||
JS_D("id", m.ID);
|
||||
JS_D("count", m.Count);
|
||||
}
|
||||
|
||||
GuildMember GuildMemberListUpdateMessage::MemberItem::GetAsMemberData() const {
|
||||
return m_member_data;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::MemberItem &m) {
|
||||
m.Type = "member";
|
||||
JS_D("user", m.User);
|
||||
JS_D("roles", m.Roles);
|
||||
JS_D("mute", m.IsMuted);
|
||||
JS_D("joined_at", m.JoinedAt);
|
||||
JS_D("deaf", m.IsDefeaned);
|
||||
JS_N("hoisted_role", m.HoistedRole);
|
||||
JS_ON("premium_since", m.PremiumSince);
|
||||
JS_ON("nick", m.Nickname);
|
||||
JS_ON("presence", m.Presence);
|
||||
m.m_member_data = j;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage::OpObject &m) {
|
||||
JS_D("op", m.Op);
|
||||
if (m.Op == "SYNC") {
|
||||
m.Items.emplace();
|
||||
JS_D("range", m.Range);
|
||||
for (const auto &ij : j.at("items")) {
|
||||
if (ij.contains("group"))
|
||||
m.Items->push_back(std::make_unique<GuildMemberListUpdateMessage::GroupItem>(ij.at("group")));
|
||||
else if (ij.contains("member"))
|
||||
m.Items->push_back(std::make_unique<GuildMemberListUpdateMessage::MemberItem>(ij.at("member")));
|
||||
}
|
||||
} else if (m.Op == "UPDATE") {
|
||||
JS_D("index", m.Index);
|
||||
const auto &ij = j.at("item");
|
||||
if (ij.contains("member"))
|
||||
m.OpItem = std::make_unique<GuildMemberListUpdateMessage::MemberItem>(ij.at("member"));
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m) {
|
||||
JS_D("online_count", m.OnlineCount);
|
||||
JS_D("member_count", m.MemberCount);
|
||||
JS_D("id", m.ListIDHash);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("groups", m.Groups);
|
||||
JS_D("ops", m.Ops);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m) {
|
||||
j["op"] = GatewayOp::LazyLoadRequest;
|
||||
j["d"] = nlohmann::json::object();
|
||||
j["d"]["guild_id"] = m.GuildID;
|
||||
if (m.Channels.has_value()) {
|
||||
j["d"]["channels"] = nlohmann::json::object();
|
||||
for (const auto &[key, chans] : *m.Channels)
|
||||
j["d"]["channels"][std::to_string(key)] = chans;
|
||||
}
|
||||
if (m.ShouldGetTyping)
|
||||
j["d"]["typing"] = *m.ShouldGetTyping;
|
||||
if (m.ShouldGetActivities)
|
||||
j["d"]["activities"] = *m.ShouldGetActivities;
|
||||
if (m.ShouldGetThreads)
|
||||
j["d"]["threads"] = *m.ShouldGetThreads;
|
||||
if (m.Members.has_value())
|
||||
j["d"]["members"] = *m.Members;
|
||||
if (m.ThreadIDs.has_value())
|
||||
j["d"]["thread_member_lists"] = *m.ThreadIDs;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UpdateStatusMessage &m) {
|
||||
j["op"] = GatewayOp::UpdateStatus;
|
||||
j["d"] = nlohmann::json::object();
|
||||
j["d"]["since"] = m.Since;
|
||||
j["d"]["activities"] = m.Activities;
|
||||
j["d"]["afk"] = m.IsAFK;
|
||||
switch (m.Status) {
|
||||
case PresenceStatus::Online:
|
||||
j["d"]["status"] = "online";
|
||||
break;
|
||||
case PresenceStatus::Offline:
|
||||
j["d"]["status"] = "invisible";
|
||||
break;
|
||||
case PresenceStatus::Idle:
|
||||
j["d"]["status"] = "idle";
|
||||
break;
|
||||
case PresenceStatus::DND:
|
||||
j["d"]["status"] = "dnd";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ReadyEventData &m) {
|
||||
JS_D("v", m.GatewayVersion);
|
||||
JS_D("user", m.SelfUser);
|
||||
JS_D("guilds", m.Guilds);
|
||||
JS_D("session_id", m.SessionID);
|
||||
JS_O("analytics_token", m.AnalyticsToken);
|
||||
JS_O("friend_suggestion_count", m.FriendSuggestionCount);
|
||||
JS_D("user_settings", m.Settings);
|
||||
JS_D("private_channels", m.PrivateChannels);
|
||||
JS_O("users", m.Users);
|
||||
JS_ON("merged_members", m.MergedMembers);
|
||||
JS_O("relationships", m.Relationships);
|
||||
JS_O("guild_join_requests", m.GuildJoinRequests);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MergedPresence &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_O("last_modified", m.LastModified);
|
||||
m.Presence = j;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m) {
|
||||
JS_D("guilds", m.Guilds);
|
||||
JS_D("friends", m.Friends);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ReadySupplementalData &m) {
|
||||
JS_D("merged_presences", m.MergedPresences);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const IdentifyProperties &m) {
|
||||
j["os"] = m.OS;
|
||||
j["browser"] = m.Browser;
|
||||
j["device"] = m.Device;
|
||||
j["system_locale"] = m.SystemLocale;
|
||||
j["browser_user_agent"] = m.BrowserUserAgent;
|
||||
j["browser_version"] = m.BrowserVersion;
|
||||
j["os_version"] = m.OSVersion;
|
||||
j["referrer"] = m.Referrer;
|
||||
j["referring_domain"] = m.ReferringDomain;
|
||||
j["referrer_current"] = m.ReferrerCurrent;
|
||||
j["referring_domain_current"] = m.ReferringDomainCurrent;
|
||||
j["release_channel"] = m.ReleaseChannel;
|
||||
j["client_build_number"] = m.ClientBuildNumber;
|
||||
if (m.ClientEventSource == "")
|
||||
j["client_event_source"] = nullptr;
|
||||
else
|
||||
j["client_event_source"] = m.ClientEventSource;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ClientStateProperties &m) {
|
||||
j["guild_hashes"] = m.GuildHashes;
|
||||
j["highest_last_message_id"] = m.HighestLastMessageID;
|
||||
j["read_state_version"] = m.ReadStateVersion;
|
||||
j["user_guild_settings_version"] = m.UserGuildSettingsVersion;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const IdentifyMessage &m) {
|
||||
j["op"] = GatewayOp::Identify;
|
||||
j["d"] = nlohmann::json::object();
|
||||
j["d"]["token"] = m.Token;
|
||||
j["d"]["capabilities"] = m.Capabilities;
|
||||
j["d"]["properties"] = m.Properties;
|
||||
j["d"]["presence"] = m.Presence;
|
||||
j["d"]["compress"] = m.DoesSupportCompression;
|
||||
j["d"]["client_state"] = m.ClientState;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const HeartbeatMessage &m) {
|
||||
j["op"] = GatewayOp::Heartbeat;
|
||||
if (m.Sequence == -1)
|
||||
j["d"] = nullptr;
|
||||
else
|
||||
j["d"] = m.Sequence;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const CreateMessageObject &m) {
|
||||
j["content"] = m.Content;
|
||||
JS_IF("message_reference", m.MessageReference);
|
||||
JS_IF("nonce", m.Nonce);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const MessageEditObject &m) {
|
||||
if (m.Content.size() > 0)
|
||||
j["content"] = m.Content;
|
||||
|
||||
// todo EmbedData to_json
|
||||
// if (m.Embeds.size() > 0)
|
||||
// j["embeds"] = m.Embeds;
|
||||
|
||||
if (m.Flags != -1)
|
||||
j["flags"] = m.Flags;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildMemberUpdateMessage &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("roles", m.Roles);
|
||||
JS_D("user", m.User);
|
||||
JS_ON("nick", m.Nick);
|
||||
JS_D("joined_at", m.JoinedAt);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ClientStatusData &m) {
|
||||
JS_O("desktop", m.Desktop);
|
||||
JS_O("mobile", m.Mobile);
|
||||
JS_O("web", m.Web);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, PresenceUpdateMessage &m) {
|
||||
m.User = j.at("user");
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("status", m.StatusMessage);
|
||||
JS_D("activities", m.Activities);
|
||||
JS_D("client_status", m.ClientStatus);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const CreateDMObject &m) {
|
||||
std::vector<std::string> conv;
|
||||
for (const auto &id : m.Recipients)
|
||||
conv.push_back(std::to_string(id));
|
||||
j["recipients"] = conv;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ResumeMessage &m) {
|
||||
j["op"] = GatewayOp::Resume;
|
||||
j["d"] = nlohmann::json::object();
|
||||
j["d"]["token"] = m.Token;
|
||||
j["d"]["session_id"] = m.SessionID;
|
||||
j["d"]["seq"] = m.Sequence;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildRoleUpdateObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("role", m.Role);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildRoleCreateObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("role", m.Role);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("role_id", m.RoleID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageReactionAddObject &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_D("message_id", m.MessageID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_O("member", m.Member);
|
||||
JS_D("emoji", m.Emoji);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_D("message_id", m.MessageID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("emoji", m.Emoji);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ChannelRecipientAdd &m) {
|
||||
JS_D("user", m.User);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ChannelRecipientRemove &m) {
|
||||
JS_D("user", m.User);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, TypingStartObject &m) {
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("timestamp", m.Timestamp);
|
||||
JS_O("member", m.Member);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildObject &m) {
|
||||
JS_IF("name", m.Name);
|
||||
JS_IF("icon", m.IconData);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildBanRemoveObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildBanAddObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteCreateObject &m) {
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_D("code", m.Code);
|
||||
JS_D("created_at", m.CreatedAt);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_O("inviter", m.Inviter);
|
||||
JS_D("max_age", m.MaxAge);
|
||||
JS_D("max_uses", m.MaxUses);
|
||||
JS_O("target_user", m.TargetUser);
|
||||
JS_O("target_user_type", m.TargetUserType);
|
||||
JS_D("temporary", m.IsTemporary);
|
||||
JS_D("uses", m.Uses);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, InviteDeleteObject &m) {
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("code", m.Code);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ConnectionData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_D("name", m.Name);
|
||||
JS_D("verified", m.IsVerified);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, MutualGuildData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_ON("nick", m.Nick);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserProfileData &m) {
|
||||
JS_D("connected_accounts", m.ConnectedAccounts);
|
||||
JS_D("mutual_guilds", m.MutualGuilds);
|
||||
JS_ON("premium_guild_since", m.PremiumGuildSince);
|
||||
JS_ON("premium_since", m.PremiumSince);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserNoteObject &m) {
|
||||
JS_ON("note", m.Note);
|
||||
JS_ON("note_user_id", m.NoteUserID);
|
||||
JS_ON("user_id", m.UserID);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UserSetNoteObject &m) {
|
||||
j["note"] = m.Note;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserNoteUpdateMessage &m) {
|
||||
JS_D("note", m.Note);
|
||||
JS_D("id", m.ID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, RelationshipsData &m) {
|
||||
j.get_to(m.Users);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m) {
|
||||
JS_IF("roles", m.Roles);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildRoleObject &m) {
|
||||
JS_IF("name", m.Name);
|
||||
JS_IF("color", m.Color);
|
||||
JS_IF("hoist", m.IsHoisted);
|
||||
JS_IF("mentionable", m.Mentionable);
|
||||
if (m.Permissions.has_value())
|
||||
j["permissions"] = std::to_string(static_cast<uint64_t>(*m.Permissions));
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject::PositionParam &m) {
|
||||
j["id"] = m.ID;
|
||||
JS_IF("position", m.Position);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject &m) {
|
||||
j = m.Positions;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildEmojisUpdateObject &m) {
|
||||
JS_D("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyGuildEmojiObject &m) {
|
||||
JS_IF("name", m.Name);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildJoinRequestCreateData &m) {
|
||||
auto tmp = j.at("status").get<std::string_view>();
|
||||
if (tmp == "STARTED")
|
||||
m.Status = GuildApplicationStatus::STARTED;
|
||||
else if (tmp == "PENDING")
|
||||
m.Status = GuildApplicationStatus::PENDING;
|
||||
else if (tmp == "REJECTED")
|
||||
m.Status = GuildApplicationStatus::REJECTED;
|
||||
else if (tmp == "APPROVED")
|
||||
m.Status = GuildApplicationStatus::APPROVED;
|
||||
JS_D("request", m.Request);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, GuildJoinRequestDeleteData &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, VerificationFieldObject &m) {
|
||||
JS_D("field_type", m.Type);
|
||||
JS_D("label", m.Label);
|
||||
JS_D("required", m.Required);
|
||||
JS_D("values", m.Values);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, VerificationGateInfoObject &m) {
|
||||
JS_O("description", m.Description);
|
||||
JS_O("form_fields", m.VerificationFields);
|
||||
JS_O("version", m.Version);
|
||||
JS_O("enabled", m.Enabled);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const VerificationFieldObject &m) {
|
||||
j["field_type"] = m.Type;
|
||||
j["label"] = m.Label;
|
||||
j["required"] = m.Required;
|
||||
j["values"] = m.Values;
|
||||
JS_IF("response", m.Response);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const VerificationGateInfoObject &m) {
|
||||
JS_IF("description", m.Description);
|
||||
JS_IF("form_fields", m.VerificationFields);
|
||||
JS_IF("version", m.Version);
|
||||
JS_IF("enabled", m.Enabled);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, RateLimitedResponse &m) {
|
||||
JS_D("code", m.Code);
|
||||
JS_D("global", m.Global);
|
||||
JS_O("message", m.Message);
|
||||
JS_D("retry_after", m.RetryAfter);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, RelationshipRemoveData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, RelationshipAddData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_D("user", m.User);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const FriendRequestObject &m) {
|
||||
j["username"] = m.Username;
|
||||
j["discriminator"] = m.Discriminator;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const PutRelationshipObject &m) {
|
||||
JS_IF("type", m.Type);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadCreateData &m) {
|
||||
j.get_to(m.Channel);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadDeleteData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("parent_id", m.ParentID);
|
||||
JS_D("type", m.Type);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadListSyncData &m) {
|
||||
JS_D("threads", m.Threads);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("member_count", m.MemberCount);
|
||||
JS_O("added_members", m.AddedMembers);
|
||||
JS_O("removed_member_ids", m.RemovedMemberIDs);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m) {
|
||||
JS_D("threads", m.Threads);
|
||||
JS_D("members", m.Members);
|
||||
JS_D("has_more", m.HasMore);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMemberUpdateData &m) {
|
||||
m.Member = j;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadUpdateData &m) {
|
||||
m.Thread = j;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMemberListUpdateData::UserEntry &m) {
|
||||
JS_D("user_id", m.UserID);
|
||||
JS_D("member", m.Member);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, ThreadMemberListUpdateData &m) {
|
||||
JS_D("thread_id", m.ThreadID);
|
||||
JS_D("guild_id", m.GuildID);
|
||||
JS_D("members", m.Members);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const ModifyChannelObject &m) {
|
||||
JS_IF("archived", m.Archived);
|
||||
JS_IF("locked", m.Locked);
|
||||
}
|
||||
747
src/discord/objects.hpp
Normal file
747
src/discord/objects.hpp
Normal file
@@ -0,0 +1,747 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
#include "role.hpp"
|
||||
#include "member.hpp"
|
||||
#include "channel.hpp"
|
||||
#include "guild.hpp"
|
||||
#include "usersettings.hpp"
|
||||
#include "message.hpp"
|
||||
#include "invite.hpp"
|
||||
#include "permissions.hpp"
|
||||
#include "emoji.hpp"
|
||||
#include "activity.hpp"
|
||||
#include "sticker.hpp"
|
||||
#include "ban.hpp"
|
||||
#include "auditlog.hpp"
|
||||
#include "relationship.hpp"
|
||||
#include "errors.hpp"
|
||||
|
||||
// most stuff below should just be objects that get processed and thrown away immediately
|
||||
|
||||
enum class GatewayOp : int {
|
||||
Event = 0,
|
||||
Heartbeat = 1,
|
||||
Identify = 2,
|
||||
UpdateStatus = 3,
|
||||
Resume = 6,
|
||||
Reconnect = 7,
|
||||
InvalidSession = 9,
|
||||
Hello = 10,
|
||||
HeartbeatAck = 11,
|
||||
LazyLoadRequest = 14,
|
||||
};
|
||||
|
||||
enum class GatewayEvent : int {
|
||||
READY,
|
||||
MESSAGE_CREATE,
|
||||
MESSAGE_DELETE,
|
||||
MESSAGE_UPDATE,
|
||||
GUILD_MEMBER_LIST_UPDATE,
|
||||
GUILD_CREATE,
|
||||
GUILD_DELETE,
|
||||
MESSAGE_DELETE_BULK,
|
||||
GUILD_MEMBER_UPDATE,
|
||||
PRESENCE_UPDATE,
|
||||
CHANNEL_DELETE,
|
||||
CHANNEL_UPDATE,
|
||||
CHANNEL_CREATE,
|
||||
GUILD_UPDATE,
|
||||
GUILD_ROLE_UPDATE,
|
||||
GUILD_ROLE_CREATE,
|
||||
GUILD_ROLE_DELETE,
|
||||
MESSAGE_REACTION_ADD,
|
||||
MESSAGE_REACTION_REMOVE,
|
||||
CHANNEL_RECIPIENT_ADD,
|
||||
CHANNEL_RECIPIENT_REMOVE,
|
||||
TYPING_START,
|
||||
GUILD_BAN_REMOVE,
|
||||
GUILD_BAN_ADD,
|
||||
INVITE_CREATE,
|
||||
INVITE_DELETE,
|
||||
USER_NOTE_UPDATE,
|
||||
READY_SUPPLEMENTAL,
|
||||
GUILD_EMOJIS_UPDATE,
|
||||
GUILD_JOIN_REQUEST_CREATE,
|
||||
GUILD_JOIN_REQUEST_UPDATE,
|
||||
GUILD_JOIN_REQUEST_DELETE,
|
||||
RELATIONSHIP_REMOVE,
|
||||
RELATIONSHIP_ADD,
|
||||
THREAD_CREATE,
|
||||
THREAD_UPDATE,
|
||||
THREAD_DELETE,
|
||||
THREAD_LIST_SYNC,
|
||||
THREAD_MEMBER_UPDATE,
|
||||
THREAD_MEMBERS_UPDATE,
|
||||
THREAD_MEMBER_LIST_UPDATE,
|
||||
};
|
||||
|
||||
enum class GatewayCloseCode : uint16_t {
|
||||
// standard
|
||||
Normal = 1000,
|
||||
GoingAway = 1001,
|
||||
ProtocolError = 1002,
|
||||
Unsupported = 1003,
|
||||
NoStatus = 1005,
|
||||
Abnormal = 1006,
|
||||
UnsupportedPayload = 1007,
|
||||
PolicyViolation = 1008,
|
||||
TooLarge = 1009,
|
||||
MandatoryExtension = 1010,
|
||||
ServerError = 1011,
|
||||
ServiceRestart = 1012,
|
||||
TryAgainLater = 1013,
|
||||
BadGateway = 1014,
|
||||
TLSHandshakeFailed = 1015,
|
||||
|
||||
// discord
|
||||
UnknownError = 4000,
|
||||
UnknownOpcode = 4001,
|
||||
DecodeError = 4002,
|
||||
NotAuthenticated = 4003,
|
||||
AuthenticationFailed = 4004,
|
||||
AlreadyAuthenticated = 4005,
|
||||
InvalidSequence = 4007,
|
||||
RateLimited = 4008,
|
||||
SessionTimedOut = 4009,
|
||||
InvalidShard = 4010,
|
||||
ShardingRequired = 4011,
|
||||
InvalidAPIVersion = 4012,
|
||||
InvalidIntents = 4013,
|
||||
DisallowedIntents = 4014,
|
||||
|
||||
// internal
|
||||
UserDisconnect = 4091,
|
||||
Reconnecting = 4092,
|
||||
};
|
||||
|
||||
struct GatewayMessage {
|
||||
GatewayOp Opcode;
|
||||
nlohmann::json Data;
|
||||
std::string Type;
|
||||
int Sequence = -1;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GatewayMessage &m);
|
||||
};
|
||||
|
||||
struct HelloMessageData {
|
||||
int HeartbeatInterval;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, HelloMessageData &m);
|
||||
};
|
||||
|
||||
struct MessageDeleteData {
|
||||
Snowflake ID; //
|
||||
Snowflake ChannelID; //
|
||||
Snowflake GuildID; // opt
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageDeleteData &m);
|
||||
};
|
||||
|
||||
struct MessageDeleteBulkData {
|
||||
std::vector<Snowflake> IDs; //
|
||||
Snowflake ChannelID; //
|
||||
Snowflake GuildID; // opt
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageDeleteBulkData &m);
|
||||
};
|
||||
|
||||
struct GuildMemberListUpdateMessage {
|
||||
struct Item {
|
||||
virtual ~Item() = default;
|
||||
|
||||
std::string Type;
|
||||
};
|
||||
|
||||
struct GroupItem : Item {
|
||||
std::string ID;
|
||||
int Count;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GroupItem &m);
|
||||
};
|
||||
|
||||
struct MemberItem : Item {
|
||||
UserData User;
|
||||
std::vector<Snowflake> Roles;
|
||||
std::optional<PresenceData> Presence;
|
||||
std::string PremiumSince; // opt
|
||||
std::string Nickname; // opt
|
||||
bool IsMuted;
|
||||
std::string JoinedAt;
|
||||
std::string HoistedRole; // null
|
||||
bool IsDefeaned;
|
||||
|
||||
GuildMember GetAsMemberData() const;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MemberItem &m);
|
||||
|
||||
private:
|
||||
GuildMember m_member_data;
|
||||
};
|
||||
|
||||
struct OpObject {
|
||||
std::string Op;
|
||||
std::optional<int> Index;
|
||||
std::optional<std::vector<std::unique_ptr<Item>>> Items; // SYNC
|
||||
std::optional<std::pair<int, int>> Range; // SYNC
|
||||
std::optional<std::unique_ptr<Item>> OpItem; // UPDATE
|
||||
|
||||
friend void from_json(const nlohmann::json &j, OpObject &m);
|
||||
};
|
||||
|
||||
int OnlineCount;
|
||||
int MemberCount;
|
||||
std::string ListIDHash;
|
||||
std::string GuildID;
|
||||
std::vector<GroupItem> Groups;
|
||||
std::vector<OpObject> Ops;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildMemberListUpdateMessage &m);
|
||||
};
|
||||
|
||||
struct LazyLoadRequestMessage {
|
||||
Snowflake GuildID;
|
||||
std::optional<bool> ShouldGetTyping;
|
||||
std::optional<bool> ShouldGetActivities;
|
||||
std::optional<bool> ShouldGetThreads;
|
||||
std::optional<std::vector<std::string>> Members; // snowflake?
|
||||
std::optional<std::map<Snowflake, std::vector<std::pair<int, int>>>> Channels; // channel ID -> range of sidebar
|
||||
std::optional<std::vector<Snowflake>> ThreadIDs;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const LazyLoadRequestMessage &m);
|
||||
};
|
||||
|
||||
struct UpdateStatusMessage {
|
||||
int Since = 0;
|
||||
std::vector<ActivityData> Activities;
|
||||
PresenceStatus Status;
|
||||
bool IsAFK = false;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const UpdateStatusMessage &m);
|
||||
};
|
||||
|
||||
struct ReadyEventData {
|
||||
int GatewayVersion;
|
||||
UserData SelfUser;
|
||||
std::vector<GuildData> Guilds;
|
||||
std::string SessionID;
|
||||
std::vector<ChannelData> PrivateChannels;
|
||||
|
||||
// undocumented
|
||||
std::optional<std::vector<UserData>> Users;
|
||||
std::optional<std::string> AnalyticsToken;
|
||||
std::optional<int> FriendSuggestionCount;
|
||||
UserSettings Settings;
|
||||
std::optional<std::vector<std::vector<GuildMember>>> MergedMembers;
|
||||
std::optional<std::vector<RelationshipData>> Relationships;
|
||||
std::optional<std::vector<GuildApplicationData>> GuildJoinRequests;
|
||||
// std::vector<Unknown> ConnectedAccounts; // opt
|
||||
// std::map<std::string, Unknown> Consents; // opt
|
||||
// std::vector<Unknown> Experiments; // opt
|
||||
// std::vector<Unknown> GuildExperiments; // opt
|
||||
// std::map<Unknown, Unknown> Notes; // opt
|
||||
// std::vector<PresenceData> Presences; // opt
|
||||
// std::vector<ReadStateData> ReadStates; // opt
|
||||
// Unknown Tutorial; // opt, null
|
||||
// std::vector<GuildSettingData> UserGuildSettings; // opt
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ReadyEventData &m);
|
||||
};
|
||||
|
||||
struct MergedPresence {
|
||||
Snowflake UserID;
|
||||
std::optional<uint64_t> LastModified;
|
||||
PresenceData Presence;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MergedPresence &m);
|
||||
};
|
||||
|
||||
struct SupplementalMergedPresencesData {
|
||||
std::vector<std::vector<MergedPresence>> Guilds;
|
||||
std::vector<MergedPresence> Friends;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, SupplementalMergedPresencesData &m);
|
||||
};
|
||||
|
||||
struct ReadySupplementalData {
|
||||
SupplementalMergedPresencesData MergedPresences;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ReadySupplementalData &m);
|
||||
};
|
||||
|
||||
struct IdentifyProperties {
|
||||
std::string OS;
|
||||
std::string Browser;
|
||||
std::string Device;
|
||||
std::string SystemLocale;
|
||||
std::string BrowserUserAgent;
|
||||
std::string BrowserVersion;
|
||||
std::string OSVersion;
|
||||
std::string Referrer;
|
||||
std::string ReferringDomain;
|
||||
std::string ReferrerCurrent;
|
||||
std::string ReferringDomainCurrent;
|
||||
std::string ReleaseChannel;
|
||||
int ClientBuildNumber;
|
||||
std::string ClientEventSource; // empty -> null
|
||||
|
||||
friend void to_json(nlohmann::json &j, const IdentifyProperties &m);
|
||||
};
|
||||
|
||||
struct ClientStateProperties {
|
||||
std::map<std::string, std::string> GuildHashes;
|
||||
std::string HighestLastMessageID = "0";
|
||||
int ReadStateVersion = 0;
|
||||
int UserGuildSettingsVersion = -1;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ClientStateProperties &m);
|
||||
};
|
||||
|
||||
struct IdentifyMessage : GatewayMessage {
|
||||
std::string Token;
|
||||
IdentifyProperties Properties;
|
||||
PresenceData Presence;
|
||||
ClientStateProperties ClientState;
|
||||
bool DoesSupportCompression = false;
|
||||
int Capabilities;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const IdentifyMessage &m);
|
||||
};
|
||||
|
||||
struct HeartbeatMessage : GatewayMessage {
|
||||
int Sequence;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const HeartbeatMessage &m);
|
||||
};
|
||||
|
||||
struct CreateMessageObject {
|
||||
std::string Content;
|
||||
std::optional<MessageReferenceData> MessageReference;
|
||||
std::optional<std::string> Nonce;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const CreateMessageObject &m);
|
||||
};
|
||||
|
||||
struct MessageEditObject {
|
||||
std::string Content; // opt, null
|
||||
std::vector<EmbedData> Embeds; // opt, null
|
||||
int Flags = -1; // opt, null
|
||||
|
||||
friend void to_json(nlohmann::json &j, const MessageEditObject &m);
|
||||
};
|
||||
|
||||
struct GuildMemberUpdateMessage {
|
||||
Snowflake GuildID; //
|
||||
std::vector<Snowflake> Roles; //
|
||||
UserData User; //
|
||||
std::string Nick; // opt, null
|
||||
std::string JoinedAt;
|
||||
std::string PremiumSince; // opt, null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildMemberUpdateMessage &m);
|
||||
};
|
||||
|
||||
struct ClientStatusData {
|
||||
std::optional<std::string> Desktop;
|
||||
std::optional<std::string> Mobile;
|
||||
std::optional<std::string> Web;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ClientStatusData &m);
|
||||
};
|
||||
|
||||
struct PresenceUpdateMessage {
|
||||
nlohmann::json User; // the client updates an existing object from this data
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::string StatusMessage;
|
||||
std::vector<ActivityData> Activities;
|
||||
ClientStatusData ClientStatus;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, PresenceUpdateMessage &m);
|
||||
};
|
||||
|
||||
struct CreateDMObject {
|
||||
std::vector<Snowflake> Recipients;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const CreateDMObject &m);
|
||||
};
|
||||
|
||||
struct ResumeMessage : GatewayMessage {
|
||||
std::string Token;
|
||||
std::string SessionID;
|
||||
int Sequence;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ResumeMessage &m);
|
||||
};
|
||||
|
||||
struct GuildRoleUpdateObject {
|
||||
Snowflake GuildID;
|
||||
RoleData Role;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildRoleUpdateObject &m);
|
||||
};
|
||||
|
||||
struct GuildRoleCreateObject {
|
||||
Snowflake GuildID;
|
||||
RoleData Role;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildRoleCreateObject &m);
|
||||
};
|
||||
|
||||
struct GuildRoleDeleteObject {
|
||||
Snowflake GuildID;
|
||||
Snowflake RoleID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildRoleDeleteObject &m);
|
||||
};
|
||||
|
||||
struct MessageReactionAddObject {
|
||||
Snowflake UserID;
|
||||
Snowflake ChannelID;
|
||||
Snowflake MessageID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::optional<GuildMember> Member;
|
||||
EmojiData Emoji;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageReactionAddObject &m);
|
||||
};
|
||||
|
||||
struct MessageReactionRemoveObject {
|
||||
Snowflake UserID;
|
||||
Snowflake ChannelID;
|
||||
Snowflake MessageID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
EmojiData Emoji;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MessageReactionRemoveObject &m);
|
||||
};
|
||||
|
||||
struct ChannelRecipientAdd {
|
||||
UserData User;
|
||||
Snowflake ChannelID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ChannelRecipientAdd &m);
|
||||
};
|
||||
|
||||
struct ChannelRecipientRemove {
|
||||
UserData User;
|
||||
Snowflake ChannelID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ChannelRecipientRemove &m);
|
||||
};
|
||||
|
||||
struct TypingStartObject {
|
||||
Snowflake ChannelID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
Snowflake UserID;
|
||||
uint64_t Timestamp;
|
||||
std::optional<GuildMember> Member;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, TypingStartObject &m);
|
||||
};
|
||||
|
||||
// implement rest as needed
|
||||
struct ModifyGuildObject {
|
||||
std::optional<std::string> Name;
|
||||
std::optional<std::string> IconData;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyGuildObject &m);
|
||||
};
|
||||
|
||||
struct GuildBanRemoveObject {
|
||||
Snowflake GuildID;
|
||||
UserData User;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildBanRemoveObject &m);
|
||||
};
|
||||
|
||||
struct GuildBanAddObject {
|
||||
Snowflake GuildID;
|
||||
UserData User;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildBanAddObject &m);
|
||||
};
|
||||
|
||||
struct InviteCreateObject {
|
||||
Snowflake ChannelID;
|
||||
std::string Code;
|
||||
std::string CreatedAt;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::optional<UserData> Inviter;
|
||||
int MaxAge;
|
||||
int MaxUses;
|
||||
UserData TargetUser;
|
||||
std::optional<ETargetUserType> TargetUserType;
|
||||
bool IsTemporary;
|
||||
int Uses;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteCreateObject &m);
|
||||
};
|
||||
|
||||
struct InviteDeleteObject {
|
||||
Snowflake ChannelID;
|
||||
std::optional<Snowflake> GuildID;
|
||||
std::string Code;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, InviteDeleteObject &m);
|
||||
};
|
||||
|
||||
struct ConnectionData {
|
||||
std::string ID;
|
||||
std::string Type;
|
||||
std::string Name;
|
||||
bool IsVerified;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ConnectionData &m);
|
||||
};
|
||||
|
||||
struct MutualGuildData {
|
||||
Snowflake ID;
|
||||
std::optional<std::string> Nick; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, MutualGuildData &m);
|
||||
};
|
||||
|
||||
struct UserProfileData {
|
||||
std::vector<ConnectionData> ConnectedAccounts;
|
||||
std::vector<MutualGuildData> MutualGuilds;
|
||||
std::optional<std::string> PremiumGuildSince; // null
|
||||
std::optional<std::string> PremiumSince; // null
|
||||
UserData User;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserProfileData &m);
|
||||
};
|
||||
|
||||
struct UserNoteObject {
|
||||
// idk if these can be null or missing but i play it safe
|
||||
std::optional<std::string> Note;
|
||||
std::optional<Snowflake> NoteUserID;
|
||||
std::optional<Snowflake> UserID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserNoteObject &m);
|
||||
};
|
||||
|
||||
struct UserSetNoteObject {
|
||||
std::string Note;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const UserSetNoteObject &m);
|
||||
};
|
||||
|
||||
struct UserNoteUpdateMessage {
|
||||
std::string Note;
|
||||
Snowflake ID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserNoteUpdateMessage &m);
|
||||
};
|
||||
|
||||
struct RelationshipsData {
|
||||
std::vector<UserData> Users;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RelationshipsData &m);
|
||||
};
|
||||
|
||||
struct ModifyGuildMemberObject {
|
||||
// std::optional<std::string> Nick;
|
||||
// std::optional<bool> IsMuted;
|
||||
// std::optional<bool> IsDeaf;
|
||||
// std::optional<Snowflake> ChannelID;
|
||||
|
||||
std::optional<std::vector<Snowflake>> Roles;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyGuildMemberObject &m);
|
||||
};
|
||||
|
||||
struct ModifyGuildRoleObject {
|
||||
std::optional<std::string> Name;
|
||||
std::optional<Permission> Permissions;
|
||||
std::optional<uint32_t> Color;
|
||||
std::optional<bool> IsHoisted;
|
||||
std::optional<bool> Mentionable;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyGuildRoleObject &m);
|
||||
};
|
||||
|
||||
struct ModifyGuildRolePositionsObject {
|
||||
struct PositionParam {
|
||||
Snowflake ID;
|
||||
std::optional<int> Position; // no idea why this can be optional
|
||||
|
||||
friend void to_json(nlohmann::json &j, const PositionParam &m);
|
||||
};
|
||||
std::vector<PositionParam> Positions;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyGuildRolePositionsObject &m);
|
||||
};
|
||||
|
||||
struct GuildEmojisUpdateObject {
|
||||
Snowflake GuildID;
|
||||
// std::vector<EmojiData> Emojis;
|
||||
// GuildHashes, undocumented
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildEmojisUpdateObject &m);
|
||||
};
|
||||
|
||||
struct ModifyGuildEmojiObject {
|
||||
std::optional<std::string> Name;
|
||||
// std::optional<std::vector<Snowflake>> Roles;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyGuildEmojiObject &m);
|
||||
};
|
||||
|
||||
struct GuildJoinRequestCreateData {
|
||||
GuildApplicationStatus Status;
|
||||
GuildApplicationData Request;
|
||||
Snowflake GuildID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildJoinRequestCreateData &m);
|
||||
};
|
||||
|
||||
using GuildJoinRequestUpdateData = GuildJoinRequestCreateData;
|
||||
|
||||
struct GuildJoinRequestDeleteData {
|
||||
Snowflake UserID;
|
||||
Snowflake GuildID;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, GuildJoinRequestDeleteData &m);
|
||||
};
|
||||
|
||||
struct VerificationFieldObject {
|
||||
std::string Type;
|
||||
std::string Label;
|
||||
bool Required;
|
||||
std::vector<std::string> Values;
|
||||
std::optional<bool> Response; // present in client to server
|
||||
|
||||
friend void from_json(const nlohmann::json &j, VerificationFieldObject &m);
|
||||
friend void to_json(nlohmann::json &j, const VerificationFieldObject &m);
|
||||
};
|
||||
|
||||
struct VerificationGateInfoObject {
|
||||
std::optional<std::string> Description;
|
||||
std::optional<std::vector<VerificationFieldObject>> VerificationFields;
|
||||
std::optional<std::string> Version;
|
||||
std::optional<bool> Enabled; // present only in client to server in modify gate
|
||||
|
||||
friend void from_json(const nlohmann::json &j, VerificationGateInfoObject &m);
|
||||
friend void to_json(nlohmann::json &j, const VerificationGateInfoObject &m);
|
||||
};
|
||||
|
||||
// not sure what the structure for this really is
|
||||
struct RateLimitedResponse {
|
||||
int Code;
|
||||
bool Global;
|
||||
std::optional<std::string> Message;
|
||||
float RetryAfter;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RateLimitedResponse &m);
|
||||
};
|
||||
|
||||
struct RelationshipRemoveData {
|
||||
Snowflake ID;
|
||||
RelationshipType Type;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RelationshipRemoveData &m);
|
||||
};
|
||||
|
||||
struct RelationshipAddData {
|
||||
Snowflake ID;
|
||||
// Nickname; same deal as the other comment somewhere else
|
||||
RelationshipType Type;
|
||||
UserData User;
|
||||
// std::optional<bool> ShouldNotify; // i guess if the client should send a notification. not worth caring about
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RelationshipAddData &m);
|
||||
};
|
||||
|
||||
struct FriendRequestObject {
|
||||
std::string Username;
|
||||
int Discriminator;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const FriendRequestObject &m);
|
||||
};
|
||||
|
||||
struct PutRelationshipObject {
|
||||
std::optional<RelationshipType> Type;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const PutRelationshipObject &m);
|
||||
};
|
||||
|
||||
struct ThreadCreateData {
|
||||
ChannelData Channel;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadCreateData &m);
|
||||
};
|
||||
|
||||
struct ThreadDeleteData {
|
||||
Snowflake ID;
|
||||
Snowflake GuildID;
|
||||
Snowflake ParentID;
|
||||
ChannelType Type;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadDeleteData &m);
|
||||
};
|
||||
|
||||
// pretty different from docs
|
||||
struct ThreadListSyncData {
|
||||
std::vector<ChannelData> Threads;
|
||||
Snowflake GuildID;
|
||||
// std::optional<std::vector<???>> MostRecentMessages;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadListSyncData &m);
|
||||
};
|
||||
|
||||
struct ThreadMembersUpdateData {
|
||||
Snowflake ID;
|
||||
Snowflake GuildID;
|
||||
int MemberCount;
|
||||
std::optional<std::vector<ThreadMemberObject>> AddedMembers;
|
||||
std::optional<std::vector<Snowflake>> RemovedMemberIDs;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMembersUpdateData &m);
|
||||
};
|
||||
|
||||
struct ArchivedThreadsResponseData {
|
||||
std::vector<ChannelData> Threads;
|
||||
std::vector<ThreadMemberObject> Members;
|
||||
bool HasMore;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ArchivedThreadsResponseData &m);
|
||||
};
|
||||
|
||||
struct ThreadMemberUpdateData {
|
||||
ThreadMemberObject Member;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMemberUpdateData &m);
|
||||
};
|
||||
|
||||
struct ThreadUpdateData {
|
||||
ChannelData Thread;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadUpdateData &m);
|
||||
};
|
||||
|
||||
struct ThreadMemberListUpdateData {
|
||||
struct UserEntry {
|
||||
Snowflake UserID;
|
||||
// PresenceData Presence;
|
||||
GuildMember Member;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserEntry &m);
|
||||
};
|
||||
|
||||
Snowflake ThreadID;
|
||||
Snowflake GuildID;
|
||||
std::vector<UserEntry> Members;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, ThreadMemberListUpdateData &m);
|
||||
};
|
||||
|
||||
struct ModifyChannelObject {
|
||||
std::optional<bool> Archived;
|
||||
std::optional<bool> Locked;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const ModifyChannelObject &m);
|
||||
};
|
||||
11
src/discord/permissions.cpp
Normal file
11
src/discord/permissions.cpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#include "permissions.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, PermissionOverwrite &m) {
|
||||
JS_D("id", m.ID);
|
||||
std::string tmp;
|
||||
m.Type = j.at("type").get<int>() == 0 ? PermissionOverwrite::ROLE : PermissionOverwrite::MEMBER;
|
||||
JS_D("allow", tmp);
|
||||
m.Allow = static_cast<Permission>(std::stoull(tmp));
|
||||
JS_D("deny", tmp);
|
||||
m.Deny = static_cast<Permission>(std::stoull(tmp));
|
||||
}
|
||||
224
src/discord/permissions.hpp
Normal file
224
src/discord/permissions.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
constexpr static uint64_t PERMISSION_MAX_BIT = 36;
|
||||
enum class Permission : uint64_t {
|
||||
NONE = 0,
|
||||
CREATE_INSTANT_INVITE = (1ULL << 0), // Allows creation of instant invites
|
||||
KICK_MEMBERS = (1ULL << 1), // Allows kicking members
|
||||
BAN_MEMBERS = (1ULL << 2), // Allows banning members
|
||||
ADMINISTRATOR = (1ULL << 3), // Allows all permissions and bypasses channel permission overwrites
|
||||
MANAGE_CHANNELS = (1ULL << 4), // Allows management and editing of channels
|
||||
MANAGE_GUILD = (1ULL << 5), // Allows management and editing of the guild
|
||||
ADD_REACTIONS = (1ULL << 6), // Allows for the addition of reactions to messages
|
||||
VIEW_AUDIT_LOG = (1ULL << 7), // Allows for viewing of audit logs
|
||||
PRIORITY_SPEAKER = (1ULL << 8), // Allows for using priority speaker in a voice channel
|
||||
STREAM = (1ULL << 9), // Allows the user to go live
|
||||
VIEW_CHANNEL = (1ULL << 10), // Allows guild members to view a channel, which includes reading messages in text channels
|
||||
SEND_MESSAGES = (1ULL << 11), // Allows for sending messages in a channel
|
||||
SEND_TTS_MESSAGES = (1ULL << 12), // Allows for sending of /tts messages
|
||||
MANAGE_MESSAGES = (1ULL << 13), // Allows for deletion of other users messages
|
||||
EMBED_LINKS = (1ULL << 14), // Links sent by users with this permission will be auto-embedded
|
||||
ATTACH_FILES = (1ULL << 15), // Allows for uploading images and files
|
||||
READ_MESSAGE_HISTORY = (1ULL << 16), // Allows for reading of message history
|
||||
MENTION_EVERYONE = (1ULL << 17), // Allows for using the @everyone tag to notify all users in a channel, and the @here tag to notify all online users in a channel
|
||||
USE_EXTERNAL_EMOJIS = (1ULL << 18), // Allows the usage of custom emojis from other servers
|
||||
VIEW_GUILD_INSIGHTS = (1ULL << 19), // Allows for viewing guild insights
|
||||
CONNECT = (1ULL << 20), // Allows for joining of a voice channel
|
||||
SPEAK = (1ULL << 21), // Allows for speaking in a voice channel
|
||||
MUTE_MEMBERS = (1ULL << 22), // Allows for muting members in a voice channel
|
||||
DEAFEN_MEMBERS = (1ULL << 23), // Allows for deafening of members in a voice channel
|
||||
MOVE_MEMBERS = (1ULL << 24), // Allows for moving of members between voice channels
|
||||
USE_VAD = (1ULL << 25), // Allows for using voice-activity-detection in a voice channel
|
||||
CHANGE_NICKNAME = (1ULL << 26), // Allows for modification of own nickname
|
||||
MANAGE_NICKNAMES = (1ULL << 27), // Allows for modification of other users nicknames
|
||||
MANAGE_ROLES = (1ULL << 28), // Allows management and editing of roles
|
||||
MANAGE_WEBHOOKS = (1ULL << 29), // Allows management and editing of webhooks
|
||||
MANAGE_EMOJIS = (1ULL << 30), // Allows management and editing of emojis
|
||||
USE_SLASH_COMMANDS = (1ULL << 31), // Allows members to use slash commands in text channels
|
||||
REQUEST_TO_SPEAK = (1ULL << 32), // Allows for requesting to speak in stage channels
|
||||
MANAGE_THREADS = (1ULL << 34), // Allows for deleting and archiving threads, and viewing all private threads
|
||||
USE_PUBLIC_THREADS = (1ULL << 35), // Allows for creating and participating in threads
|
||||
USE_PRIVATE_THREADS = (1ULL << 36), // Allows for creating and participating in private threads
|
||||
|
||||
ALL = 0x1FFFFFFFFFULL,
|
||||
};
|
||||
template<>
|
||||
struct Bitwise<Permission> {
|
||||
static const bool enable = true;
|
||||
};
|
||||
|
||||
struct PermissionOverwrite {
|
||||
enum OverwriteType : uint8_t {
|
||||
ROLE = 0,
|
||||
MEMBER = 1,
|
||||
};
|
||||
|
||||
Snowflake ID;
|
||||
OverwriteType Type;
|
||||
Permission Allow;
|
||||
Permission Deny;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, PermissionOverwrite &m);
|
||||
};
|
||||
|
||||
constexpr const char *GetPermissionString(Permission perm) {
|
||||
switch (perm) {
|
||||
case Permission::NONE:
|
||||
return "None";
|
||||
case Permission::CREATE_INSTANT_INVITE:
|
||||
return "Create Invite";
|
||||
case Permission::KICK_MEMBERS:
|
||||
return "Kick Members";
|
||||
case Permission::BAN_MEMBERS:
|
||||
return "Ban Members";
|
||||
case Permission::ADMINISTRATOR:
|
||||
return "Administrator";
|
||||
case Permission::MANAGE_CHANNELS:
|
||||
return "Manage Channels";
|
||||
case Permission::MANAGE_GUILD:
|
||||
return "Manage Server";
|
||||
case Permission::ADD_REACTIONS:
|
||||
return "Add Reactions";
|
||||
case Permission::VIEW_AUDIT_LOG:
|
||||
return "View Audit Log";
|
||||
case Permission::PRIORITY_SPEAKER:
|
||||
return "Use Priority Speaker";
|
||||
case Permission::STREAM:
|
||||
return "Video";
|
||||
case Permission::VIEW_CHANNEL:
|
||||
return "View Channel";
|
||||
case Permission::SEND_MESSAGES:
|
||||
return "Send Messages";
|
||||
case Permission::SEND_TTS_MESSAGES:
|
||||
return "Use TTS";
|
||||
case Permission::MANAGE_MESSAGES:
|
||||
return "Manage Messages";
|
||||
case Permission::EMBED_LINKS:
|
||||
return "Embed Links";
|
||||
case Permission::ATTACH_FILES:
|
||||
return "Attach Files";
|
||||
case Permission::READ_MESSAGE_HISTORY:
|
||||
return "Read Message History";
|
||||
case Permission::MENTION_EVERYONE:
|
||||
return "Mention @everyone";
|
||||
case Permission::USE_EXTERNAL_EMOJIS:
|
||||
return "Use External Emojis";
|
||||
case Permission::VIEW_GUILD_INSIGHTS:
|
||||
return "View Server Insights";
|
||||
case Permission::CONNECT:
|
||||
return "Connect to Voice";
|
||||
case Permission::SPEAK:
|
||||
return "Speak in Voice";
|
||||
case Permission::MUTE_MEMBERS:
|
||||
return "Mute Members";
|
||||
case Permission::DEAFEN_MEMBERS:
|
||||
return "Deafen Members";
|
||||
case Permission::MOVE_MEMBERS:
|
||||
return "Move Members";
|
||||
case Permission::USE_VAD:
|
||||
return "Use Voice Activation";
|
||||
case Permission::CHANGE_NICKNAME:
|
||||
return "Change Nickname";
|
||||
case Permission::MANAGE_NICKNAMES:
|
||||
return "Manage Nicknames";
|
||||
case Permission::MANAGE_ROLES:
|
||||
return "Manage Roles";
|
||||
case Permission::MANAGE_WEBHOOKS:
|
||||
return "Manage Webhooks";
|
||||
case Permission::MANAGE_EMOJIS:
|
||||
return "Manage Emojis";
|
||||
case Permission::USE_SLASH_COMMANDS:
|
||||
return "Use Slash Commands";
|
||||
case Permission::MANAGE_THREADS:
|
||||
return "Manage Threads";
|
||||
case Permission::USE_PUBLIC_THREADS:
|
||||
return "Use Public Threads";
|
||||
case Permission::USE_PRIVATE_THREADS:
|
||||
return "Use Private Threads";
|
||||
default:
|
||||
return "Unknown Permission";
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const char *GetPermissionDescription(Permission perm) {
|
||||
switch (perm) {
|
||||
case Permission::NONE:
|
||||
return "";
|
||||
case Permission::CREATE_INSTANT_INVITE:
|
||||
return "Allows members to invite new people to this server.";
|
||||
case Permission::KICK_MEMBERS:
|
||||
return "Allows members to remove other members from this server. Kicked members will be able to rejoin if they have another invite.";
|
||||
case Permission::BAN_MEMBERS:
|
||||
return "Allows members to permanently ban other members from this server.";
|
||||
case Permission::ADMINISTRATOR:
|
||||
return "Members with this permission will have every permission and will also bypass all channel specific permissions or restrictions (for example, these members would get access to all private channels). This is a dangerous permission to grant.";
|
||||
case Permission::MANAGE_CHANNELS:
|
||||
return "Allows members to create, edit, or delete channels.";
|
||||
case Permission::MANAGE_GUILD:
|
||||
return "Allows members to change this server's name, switch regions, and add bots to this server.";
|
||||
case Permission::ADD_REACTIONS:
|
||||
return "Allows members to add new emoji reactions to a message. If this permission is disabled, members can still react using any existing reactions on a message.";
|
||||
case Permission::VIEW_AUDIT_LOG:
|
||||
return "Allows members to view a record of who made which changes in this server.";
|
||||
case Permission::PRIORITY_SPEAKER:
|
||||
return "Allows members to be more easily heard in voice channels. When activated, the volume of others without this permission will be automatically lowered. Priority Speaker is activated by using the Push to Talk (Priority) keybind.";
|
||||
case Permission::STREAM:
|
||||
return "Allows members to share their video, screen share, or stream a game in this server.";
|
||||
case Permission::VIEW_CHANNEL:
|
||||
return "Allows members to view channels by default (excluding private channels).";
|
||||
case Permission::SEND_MESSAGES:
|
||||
return "Allows members to send messages in text channels.";
|
||||
case Permission::SEND_TTS_MESSAGES:
|
||||
return "Allows members to send text-to-speech messages by starting a message with /tts. These messages can be heard by anyone focused on thsi channel.";
|
||||
case Permission::MANAGE_MESSAGES:
|
||||
return "Allows members to delete messages by other members or pin any message";
|
||||
case Permission::EMBED_LINKS:
|
||||
return "Allows links that members share to show embedded content in text channels.";
|
||||
case Permission::ATTACH_FILES:
|
||||
return "Allows members to upload files or media in text channels.";
|
||||
case Permission::READ_MESSAGE_HISTORY:
|
||||
return "Allows members to read previous messages sent in channels. If this permission is disabled, members only see messages sent when they are online and focused on that channel.";
|
||||
case Permission::MENTION_EVERYONE:
|
||||
return "Allows members to use @everyone (everyone in the server) or @here (only online members in that channel). They can also @mention all roles, even if the role's \"Allow anyone to mention this role\" permission is disabled.";
|
||||
case Permission::USE_EXTERNAL_EMOJIS:
|
||||
return "Allows members to use emoji from other servers, if they're a Discord Nitro member";
|
||||
case Permission::VIEW_GUILD_INSIGHTS:
|
||||
return "Allows members to view Server Insights, which shows data on community growth, engagement, and more.";
|
||||
case Permission::CONNECT:
|
||||
return "Allows members to join voice channels and hear others.";
|
||||
case Permission::SPEAK:
|
||||
return "Allows members to talk in voice channels. If this permission is disabled, members are default muted until somebody with the \"Mute Members\" permission un-mutes them.";
|
||||
case Permission::MUTE_MEMBERS:
|
||||
return "Allows members to mute other members in voice channels for everyone.";
|
||||
case Permission::DEAFEN_MEMBERS:
|
||||
return "Allows members to deafen other members in voice channels, which means they won't be able to speak or hear others.";
|
||||
case Permission::MOVE_MEMBERS:
|
||||
return "Allows members to move other members between voice channels that the member with the permission has access to.";
|
||||
case Permission::USE_VAD:
|
||||
return "Allows members to speak in voice channels by simply talking. If this permission is disabled, members are required to use Push-to-talk. Good for controlling background noise or noisy members.";
|
||||
case Permission::CHANGE_NICKNAME:
|
||||
return "Allows members to change their own nickname, a custom name for just this server.";
|
||||
case Permission::MANAGE_NICKNAMES:
|
||||
return "Allows members to change the nicknames of other members.";
|
||||
case Permission::MANAGE_ROLES:
|
||||
return "Allows members to create new roles and edit or delete roles lower than their highest role. Also allows members to change permissions of individual channels that they have access to.";
|
||||
case Permission::MANAGE_WEBHOOKS:
|
||||
return "Allows members to create, edit, or delete webhooks, which can post messages from other apps or sites into this server.";
|
||||
case Permission::MANAGE_EMOJIS:
|
||||
return "Allows members to add or remove custom emojis in this server.";
|
||||
case Permission::USE_SLASH_COMMANDS:
|
||||
return "Allows members to use slash commands in text channels.";
|
||||
case Permission::MANAGE_THREADS:
|
||||
return "Allows members to rename, delete, archive/unarchive, and turn on slow mode for threads.";
|
||||
case Permission::USE_PUBLIC_THREADS:
|
||||
return "Allows members to talk in threads. The \"Send Messages\" permission must be enabled for members to start new threads; if it's disabled, they can only respond to existing threads.";
|
||||
case Permission::USE_PRIVATE_THREADS:
|
||||
return "Allows members to create and chat in private threads. The \"Send Messages\" permission must be enabled for members to start new private threads; if it's disabled, they can only respond to private threads they're added to.";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
6
src/discord/relationship.cpp
Normal file
6
src/discord/relationship.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "relationship.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, RelationshipData &m) {
|
||||
JS_D("type", m.Type);
|
||||
JS_D("id", m.ID);
|
||||
}
|
||||
21
src/discord/relationship.hpp
Normal file
21
src/discord/relationship.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
#include "user.hpp"
|
||||
|
||||
enum class RelationshipType {
|
||||
None = 0,
|
||||
Friend = 1,
|
||||
Blocked = 2,
|
||||
PendingIncoming = 3,
|
||||
PendingOutgoing = 4,
|
||||
Implicit = 5,
|
||||
};
|
||||
|
||||
struct RelationshipData {
|
||||
// Snowflake UserID; this is the same as ID apparently but it looks new so i wont touch it
|
||||
RelationshipType Type;
|
||||
Snowflake ID;
|
||||
// Unknown Nickname; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RelationshipData &m);
|
||||
};
|
||||
14
src/discord/role.cpp
Normal file
14
src/discord/role.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include "role.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, RoleData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("name", m.Name);
|
||||
JS_D("color", m.Color);
|
||||
JS_D("hoist", m.IsHoisted);
|
||||
JS_D("position", m.Position);
|
||||
std::string tmp;
|
||||
JS_D("permissions", tmp);
|
||||
m.Permissions = static_cast<Permission>(std::stoull(tmp));
|
||||
JS_D("managed", m.IsManaged);
|
||||
JS_D("mentionable", m.IsMentionable);
|
||||
}
|
||||
20
src/discord/role.hpp
Normal file
20
src/discord/role.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include "permissions.hpp"
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
|
||||
struct RoleData {
|
||||
Snowflake ID;
|
||||
std::string Name;
|
||||
int Color;
|
||||
bool IsHoisted;
|
||||
int Position;
|
||||
int PermissionsLegacy;
|
||||
Permission Permissions;
|
||||
bool IsManaged;
|
||||
bool IsMentionable;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, RoleData &m);
|
||||
};
|
||||
67
src/discord/snowflake.cpp
Normal file
67
src/discord/snowflake.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "snowflake.hpp"
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <chrono>
|
||||
|
||||
constexpr static uint64_t DiscordEpochSeconds = 1420070400;
|
||||
|
||||
const Snowflake Snowflake::Invalid = -1ULL;
|
||||
|
||||
Snowflake::Snowflake()
|
||||
: m_num(Invalid) {}
|
||||
|
||||
Snowflake::Snowflake(uint64_t n)
|
||||
: m_num(n) {}
|
||||
|
||||
Snowflake::Snowflake(const std::string &str) {
|
||||
if (str.size())
|
||||
m_num = std::stoull(str);
|
||||
else
|
||||
m_num = Invalid;
|
||||
}
|
||||
Snowflake::Snowflake(const Glib::ustring &str) {
|
||||
if (str.size())
|
||||
m_num = std::strtoull(str.c_str(), nullptr, 10);
|
||||
else
|
||||
m_num = Invalid;
|
||||
};
|
||||
|
||||
Snowflake Snowflake::FromNow() {
|
||||
using namespace std::chrono;
|
||||
// not guaranteed to work but it probably will anyway
|
||||
static uint64_t counter = 0;
|
||||
const auto millis_since_epoch = static_cast<uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
|
||||
const auto epoch = millis_since_epoch - DiscordEpochSeconds * 1000;
|
||||
uint64_t snowflake = epoch << 22;
|
||||
// worker id and process id would be OR'd in here but there's no point
|
||||
snowflake |= counter++ % 4096;
|
||||
return snowflake;
|
||||
}
|
||||
|
||||
bool Snowflake::IsValid() const {
|
||||
return m_num != Invalid;
|
||||
}
|
||||
|
||||
std::string Snowflake::GetLocalTimestamp() const {
|
||||
const time_t secs_since_epoch = (m_num / SecondsInterval) + DiscordEpochSeconds;
|
||||
const std::tm tm = *localtime(&secs_since_epoch);
|
||||
std::stringstream ss;
|
||||
const static std::locale locale("");
|
||||
ss.imbue(locale);
|
||||
ss << std::put_time(&tm, "%X %x");
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, Snowflake &s) {
|
||||
if (j.is_string()) {
|
||||
std::string tmp;
|
||||
j.get_to(tmp);
|
||||
s.m_num = std::stoull(tmp);
|
||||
} else {
|
||||
j.get_to(s.m_num);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const Snowflake &s) {
|
||||
j = std::to_string(s);
|
||||
}
|
||||
55
src/discord/snowflake.hpp
Normal file
55
src/discord/snowflake.hpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
struct Snowflake {
|
||||
Snowflake();
|
||||
Snowflake(uint64_t n);
|
||||
Snowflake(const std::string &str);
|
||||
Snowflake(const Glib::ustring &str);
|
||||
|
||||
static Snowflake FromNow(); // not thread safe
|
||||
|
||||
bool IsValid() const;
|
||||
std::string GetLocalTimestamp() const;
|
||||
|
||||
bool operator==(const Snowflake &s) const noexcept {
|
||||
return m_num == s.m_num;
|
||||
}
|
||||
|
||||
bool operator<(const Snowflake &s) const noexcept {
|
||||
return m_num < s.m_num;
|
||||
}
|
||||
|
||||
operator uint64_t() const noexcept {
|
||||
return m_num;
|
||||
}
|
||||
|
||||
const static Snowflake Invalid; // makes sense to me
|
||||
const static uint64_t SecondsInterval = 4194304000ULL; // the "difference" between two snowflakes one second apart
|
||||
|
||||
friend void from_json(const nlohmann::json &j, Snowflake &s);
|
||||
friend void to_json(nlohmann::json &j, const Snowflake &s);
|
||||
|
||||
private:
|
||||
friend struct std::hash<Snowflake>;
|
||||
friend struct std::less<Snowflake>;
|
||||
unsigned long long m_num;
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<Snowflake> {
|
||||
std::size_t operator()(const Snowflake &k) const {
|
||||
return k.m_num;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct less<Snowflake> {
|
||||
bool operator()(const Snowflake &l, const Snowflake &r) const {
|
||||
return l.m_num < r.m_num;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
52
src/discord/sticker.cpp
Normal file
52
src/discord/sticker.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include "sticker.hpp"
|
||||
|
||||
void to_json(nlohmann::json &j, const StickerData &m) {
|
||||
j["id"] = m.ID;
|
||||
j["pack_id"] = m.PackID;
|
||||
j["name"] = m.Name;
|
||||
j["description"] = m.Description;
|
||||
JS_IF("tags", m.Tags);
|
||||
JS_IF("asset", m.AssetHash);
|
||||
JS_IF("preview_asset", m.PreviewAssetHash);
|
||||
j["format_type"] = m.FormatType;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, StickerData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("pack_id", m.PackID);
|
||||
JS_D("name", m.Name);
|
||||
JS_D("description", m.Description);
|
||||
JS_O("tags", m.Tags);
|
||||
JS_O("asset", m.AssetHash);
|
||||
JS_ON("preview_asset", m.PreviewAssetHash);
|
||||
JS_D("format_type", m.FormatType);
|
||||
}
|
||||
|
||||
std::string StickerData::GetURL() const {
|
||||
if (!AssetHash.has_value()) return "";
|
||||
if (FormatType == StickerFormatType::PNG || FormatType == StickerFormatType::APNG)
|
||||
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".png?size=256";
|
||||
else if (FormatType == StickerFormatType::LOTTIE)
|
||||
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + "/" + *AssetHash + ".json";
|
||||
return "";
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const StickerItem &m) {
|
||||
j["id"] = m.ID;
|
||||
j["name"] = m.Name;
|
||||
j["format_type"] = m.FormatType;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, StickerItem &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("name", m.Name);
|
||||
JS_D("format_type", m.FormatType);
|
||||
}
|
||||
|
||||
std::string StickerItem::GetURL() const {
|
||||
if (FormatType == StickerFormatType::PNG || FormatType == StickerFormatType::APNG)
|
||||
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + ".png?size=256";
|
||||
else if (FormatType == StickerFormatType::LOTTIE)
|
||||
return "https://media.discordapp.net/stickers/" + std::to_string(ID) + ".json";
|
||||
return "";
|
||||
}
|
||||
40
src/discord/sticker.hpp
Normal file
40
src/discord/sticker.hpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
|
||||
// unstable
|
||||
|
||||
enum class StickerFormatType {
|
||||
PNG = 1,
|
||||
APNG = 2,
|
||||
LOTTIE = 3,
|
||||
};
|
||||
|
||||
struct StickerData {
|
||||
Snowflake ID;
|
||||
Snowflake PackID;
|
||||
std::string Name;
|
||||
std::string Description;
|
||||
std::optional<std::string> Tags;
|
||||
std::optional<std::string> AssetHash;
|
||||
std::optional<std::string> PreviewAssetHash;
|
||||
StickerFormatType FormatType;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const StickerData &m);
|
||||
friend void from_json(const nlohmann::json &j, StickerData &m);
|
||||
|
||||
std::string GetURL() const;
|
||||
};
|
||||
|
||||
struct StickerItem {
|
||||
StickerFormatType FormatType;
|
||||
Snowflake ID;
|
||||
std::string Name;
|
||||
|
||||
friend void to_json(nlohmann::json &j, const StickerItem &m);
|
||||
friend void from_json(const nlohmann::json &j, StickerItem &m);
|
||||
|
||||
std::string GetURL() const;
|
||||
};
|
||||
2232
src/discord/store.cpp
Normal file
2232
src/discord/store.cpp
Normal file
File diff suppressed because it is too large
Load Diff
302
src/discord/store.hpp
Normal file
302
src/discord/store.hpp
Normal file
@@ -0,0 +1,302 @@
|
||||
#pragma once
|
||||
#include "util.hpp"
|
||||
#include "objects.hpp"
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#ifdef GetMessage // fuck you windows.h
|
||||
#undef GetMessage
|
||||
#endif
|
||||
|
||||
class Store {
|
||||
public:
|
||||
Store(bool mem_store = false);
|
||||
~Store();
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
void SetUser(Snowflake id, const UserData &user);
|
||||
void SetChannel(Snowflake id, const ChannelData &chan);
|
||||
void SetGuild(Snowflake id, const GuildData &guild);
|
||||
void SetRole(Snowflake guild_id, const RoleData &role);
|
||||
void SetMessage(Snowflake id, const Message &message);
|
||||
void SetGuildMember(Snowflake guild_id, Snowflake user_id, const GuildMember &data);
|
||||
void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm);
|
||||
void SetEmoji(Snowflake id, const EmojiData &emoji);
|
||||
void SetBan(Snowflake guild_id, Snowflake user_id, const BanData &ban);
|
||||
|
||||
std::optional<ChannelData> GetChannel(Snowflake id) const;
|
||||
std::optional<EmojiData> GetEmoji(Snowflake id) const;
|
||||
std::optional<GuildData> GetGuild(Snowflake id) const;
|
||||
std::optional<GuildMember> GetGuildMember(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::optional<Message> GetMessage(Snowflake id) const;
|
||||
std::optional<PermissionOverwrite> GetPermissionOverwrite(Snowflake channel_id, Snowflake id) const;
|
||||
std::optional<RoleData> GetRole(Snowflake id) const;
|
||||
std::optional<UserData> GetUser(Snowflake id) const;
|
||||
std::optional<BanData> GetBan(Snowflake guild_id, Snowflake user_id) const;
|
||||
std::vector<BanData> GetBans(Snowflake guild_id) const;
|
||||
|
||||
std::vector<Message> GetLastMessages(Snowflake id, size_t num) const;
|
||||
std::vector<Message> GetMessagesBefore(Snowflake channel_id, Snowflake message_id, size_t limit) const;
|
||||
std::vector<Message> GetPinnedMessages(Snowflake channel_id) const;
|
||||
std::vector<ChannelData> GetActiveThreads(Snowflake channel_id) const; // public
|
||||
|
||||
void AddReaction(const MessageReactionAddObject &data, bool byself);
|
||||
void RemoveReaction(const MessageReactionRemoveObject &data, bool byself);
|
||||
|
||||
void ClearGuild(Snowflake id);
|
||||
void ClearChannel(Snowflake id);
|
||||
void ClearBan(Snowflake guild_id, Snowflake user_id);
|
||||
void ClearRecipient(Snowflake channel_id, Snowflake user_id);
|
||||
|
||||
std::unordered_set<Snowflake> GetChannels() const;
|
||||
std::unordered_set<Snowflake> GetGuilds() const;
|
||||
|
||||
void ClearAll();
|
||||
|
||||
void BeginTransaction();
|
||||
void EndTransaction();
|
||||
|
||||
private:
|
||||
class Statement;
|
||||
class Database {
|
||||
public:
|
||||
Database(const char *path);
|
||||
~Database();
|
||||
|
||||
int Close();
|
||||
int StartTransaction();
|
||||
int EndTransaction();
|
||||
int Execute(const char *command);
|
||||
int Error() const;
|
||||
bool OK() const;
|
||||
const char *ErrStr() const;
|
||||
int SetError(int err);
|
||||
sqlite3 *obj();
|
||||
|
||||
private:
|
||||
sqlite3 *m_db;
|
||||
int m_err = SQLITE_OK;
|
||||
mutable char m_err_scratch[256] { 0 };
|
||||
|
||||
// stupid shit i dont like to allow closing properly
|
||||
using type_signal_close = sigc::signal<void>;
|
||||
type_signal_close m_signal_close;
|
||||
|
||||
public:
|
||||
type_signal_close signal_close();
|
||||
};
|
||||
|
||||
class Statement {
|
||||
public:
|
||||
Statement() = delete;
|
||||
Statement(const Statement &other) = delete;
|
||||
Statement(Database &db, const char *command);
|
||||
~Statement();
|
||||
Statement &operator=(Statement &other) = delete;
|
||||
|
||||
bool OK() const;
|
||||
|
||||
int Bind(int index, Snowflake id);
|
||||
int Bind(int index, const char *str, size_t len = -1);
|
||||
int Bind(int index, const std::string &str);
|
||||
int Bind(int index);
|
||||
|
||||
template<typename T>
|
||||
int Bind(int index, std::optional<T> opt) {
|
||||
if (opt.has_value())
|
||||
return Bind(index, opt.value());
|
||||
else
|
||||
return Bind(index);
|
||||
}
|
||||
|
||||
template<typename Iter>
|
||||
int BindIDsAsJSON(int index, Iter start, Iter end) {
|
||||
std::vector<Snowflake> x;
|
||||
for (Iter it = start; it != end; it++) {
|
||||
x.push_back((*it).ID);
|
||||
}
|
||||
return Bind(index, nlohmann::json(x).dump());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int BindAsJSONArray(int index, const std::optional<T> &obj) {
|
||||
if (obj.has_value())
|
||||
return Bind(index, nlohmann::json(obj.value()).dump());
|
||||
else
|
||||
return Bind(index, std::string("[]"));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int BindAsJSON(int index, const T &obj) {
|
||||
return Bind(index, nlohmann::json(obj).dump());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<std::is_enum<T>::value, int>::type
|
||||
Bind(int index, T val) {
|
||||
return Bind(index, static_cast<typename std::underlying_type<T>::type>(val));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value, int>::type
|
||||
Bind(int index, T val) {
|
||||
return m_db->SetError(sqlite3_bind_int64(m_stmt, index, val));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int BindAsJSON(int index, const std::optional<T> &obj) {
|
||||
if (obj.has_value())
|
||||
return Bind(index, nlohmann::json(obj.value()).dump());
|
||||
else
|
||||
return Bind(index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_integral<T>::value>::type
|
||||
Get(int index, T &out) const {
|
||||
out = static_cast<T>(sqlite3_column_int64(m_stmt, index));
|
||||
}
|
||||
|
||||
void Get(int index, Snowflake &out) const;
|
||||
void Get(int index, std::string &out) const;
|
||||
|
||||
template<typename T>
|
||||
void GetJSON(int index, std::optional<T> &out) const {
|
||||
if (IsNull(index))
|
||||
out = std::nullopt;
|
||||
else {
|
||||
std::string stuff;
|
||||
Get(index, stuff);
|
||||
if (stuff == "")
|
||||
out = std::nullopt;
|
||||
else
|
||||
out = nlohmann::json::parse(stuff).get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void GetJSON(int index, T &out) const {
|
||||
std::string stuff;
|
||||
Get(index, stuff);
|
||||
nlohmann::json::parse(stuff).get_to(out);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Get(int index, std::optional<T> &out) const {
|
||||
if (IsNull(index))
|
||||
out = std::nullopt;
|
||||
else {
|
||||
T tmp;
|
||||
Get(index, tmp);
|
||||
out = std::optional<T>(std::move(tmp));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline typename std::enable_if<std::is_enum<T>::value, void>::type
|
||||
Get(int index, T &val) const {
|
||||
typename std::underlying_type<T>::type tmp;
|
||||
Get(index, tmp);
|
||||
val = static_cast<T>(tmp);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void GetIDOnlyStructs(int index, std::optional<std::vector<T>> &out) const {
|
||||
out.emplace();
|
||||
std::string str;
|
||||
Get(index, str);
|
||||
for (const auto &id : nlohmann::json::parse(str))
|
||||
out->emplace_back().ID = id.get<Snowflake>();
|
||||
}
|
||||
|
||||
template<typename T, typename OutputIt>
|
||||
void GetArray(int index, OutputIt first) const {
|
||||
std::string str;
|
||||
Get(index, str);
|
||||
for (const auto &id : nlohmann::json::parse(str))
|
||||
*first++ = id.get<T>();
|
||||
}
|
||||
|
||||
bool IsNull(int index) const;
|
||||
int Step();
|
||||
bool Insert();
|
||||
bool FetchOne();
|
||||
int Reset();
|
||||
|
||||
sqlite3_stmt *obj();
|
||||
|
||||
private:
|
||||
Database *m_db;
|
||||
sqlite3_stmt *m_stmt;
|
||||
};
|
||||
|
||||
Message GetMessageBound(std::unique_ptr<Statement> &stmt) const;
|
||||
|
||||
void SetMessageInteractionPair(Snowflake message_id, const MessageInteractionData &interaction);
|
||||
|
||||
bool CreateTables();
|
||||
bool CreateStatements();
|
||||
|
||||
bool m_ok = true;
|
||||
|
||||
std::filesystem::path m_db_path;
|
||||
Database m_db;
|
||||
#define STMT(x) mutable std::unique_ptr<Statement> m_stmt_##x
|
||||
STMT(set_guild);
|
||||
STMT(get_guild);
|
||||
STMT(get_guild_ids);
|
||||
STMT(clr_guild);
|
||||
STMT(set_chan);
|
||||
STMT(get_chan);
|
||||
STMT(get_chan_ids);
|
||||
STMT(clr_chan);
|
||||
STMT(set_msg);
|
||||
STMT(get_msg);
|
||||
STMT(set_msg_ref);
|
||||
STMT(get_last_msgs);
|
||||
STMT(set_user);
|
||||
STMT(get_user);
|
||||
STMT(set_member);
|
||||
STMT(get_member);
|
||||
STMT(set_role);
|
||||
STMT(get_role);
|
||||
STMT(set_emoji);
|
||||
STMT(get_emoji);
|
||||
STMT(set_perm);
|
||||
STMT(get_perm);
|
||||
STMT(set_ban);
|
||||
STMT(get_ban);
|
||||
STMT(get_bans);
|
||||
STMT(clr_ban);
|
||||
STMT(set_interaction);
|
||||
STMT(set_member_roles);
|
||||
STMT(get_member_roles);
|
||||
STMT(set_guild_emoji);
|
||||
STMT(get_guild_emojis);
|
||||
STMT(clr_guild_emoji);
|
||||
STMT(set_guild_feature);
|
||||
STMT(get_guild_features);
|
||||
STMT(get_guild_chans);
|
||||
STMT(set_thread);
|
||||
STMT(get_threads);
|
||||
STMT(get_active_threads);
|
||||
STMT(get_messages_before);
|
||||
STMT(get_pins);
|
||||
STMT(set_emoji_role);
|
||||
STMT(get_emoji_roles);
|
||||
STMT(set_mention);
|
||||
STMT(get_mentions);
|
||||
STMT(set_attachment);
|
||||
STMT(get_attachments);
|
||||
STMT(set_recipient);
|
||||
STMT(get_recipients);
|
||||
STMT(clr_recipient);
|
||||
STMT(add_reaction);
|
||||
STMT(sub_reaction);
|
||||
STMT(get_reactions);
|
||||
#undef STMT
|
||||
};
|
||||
197
src/discord/user.cpp
Normal file
197
src/discord/user.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
#include "user.hpp"
|
||||
#include "abaddon.hpp"
|
||||
|
||||
bool UserData::IsDeleted() const {
|
||||
return Discriminator == "0000";
|
||||
}
|
||||
|
||||
bool UserData::HasAvatar() const {
|
||||
return Avatar.size() > 0;
|
||||
}
|
||||
|
||||
bool UserData::HasAnimatedAvatar() const {
|
||||
return Avatar.size() > 0 && Avatar[0] == 'a' && Avatar[1] == '_';
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(Snowflake guild_id, std::string ext, std::string size) const {
|
||||
const auto member = Abaddon::Get().GetDiscordClient().GetMember(ID, guild_id);
|
||||
if (member.has_value() && member->Avatar.has_value())
|
||||
return "https://cdn.discordapp.com/guilds/" +
|
||||
std::to_string(guild_id) + "/users/" + std::to_string(ID) +
|
||||
"/avatars/" + *member->Avatar + "." +
|
||||
ext + "?" + "size=" + size;
|
||||
else
|
||||
return GetAvatarURL(ext, size);
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext, std::string size) const {
|
||||
if (guild_id.has_value())
|
||||
return GetAvatarURL(*guild_id, ext, size);
|
||||
else
|
||||
return GetAvatarURL(ext, size);
|
||||
}
|
||||
|
||||
std::string UserData::GetAvatarURL(std::string ext, std::string size) const {
|
||||
if (HasAvatar())
|
||||
return "https://cdn.discordapp.com/avatars/" + std::to_string(ID) + "/" + Avatar + "." + ext + "?size=" + size;
|
||||
else
|
||||
return GetDefaultAvatarURL();
|
||||
}
|
||||
|
||||
std::string UserData::GetDefaultAvatarURL() const {
|
||||
return "https://cdn.discordapp.com/embed/avatars/" + std::to_string(std::stoul(Discriminator) % 5) + ".png"; // size isn't respected by the cdn
|
||||
}
|
||||
|
||||
Snowflake UserData::GetHoistedRole(Snowflake guild_id, bool with_color) const {
|
||||
return Abaddon::Get().GetDiscordClient().GetMemberHoistedRole(guild_id, ID, with_color);
|
||||
}
|
||||
|
||||
std::string UserData::GetMention() const {
|
||||
return "<@" + std::to_string(ID) + ">";
|
||||
}
|
||||
|
||||
std::string UserData::GetEscapedName() const {
|
||||
return Glib::Markup::escape_text(Username);
|
||||
}
|
||||
|
||||
std::string UserData::GetEscapedBoldName() const {
|
||||
return "<b>" + Glib::Markup::escape_text(Username) + "</b>";
|
||||
}
|
||||
|
||||
std::string UserData::GetEscapedString() const {
|
||||
return Glib::Markup::escape_text(Username) + "#" + Discriminator;
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("username", m.Username);
|
||||
JS_D("discriminator", m.Discriminator);
|
||||
JS_N("avatar", m.Avatar);
|
||||
JS_O("bot", m.IsBot);
|
||||
JS_O("system", m.IsSystem);
|
||||
JS_O("mfa_enabled", m.IsMFAEnabled);
|
||||
JS_O("locale", m.Locale);
|
||||
JS_O("verified", m.IsVerified);
|
||||
JS_O("email", m.Email);
|
||||
JS_O("flags", m.Flags);
|
||||
JS_ON("premium_type", m.PremiumType);
|
||||
JS_O("public_flags", m.PublicFlags);
|
||||
JS_O("desktop", m.IsDesktop);
|
||||
JS_O("mobile", m.IsMobile);
|
||||
JS_ON("nsfw_allowed", m.IsNSFWAllowed);
|
||||
JS_ON("phone", m.Phone);
|
||||
JS_ON("bio", m.Bio);
|
||||
JS_ON("banner", m.BannerHash);
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, const UserData &m) {
|
||||
j["id"] = m.ID;
|
||||
j["username"] = m.Username;
|
||||
j["discriminator"] = m.Discriminator;
|
||||
if (m.Avatar == "")
|
||||
j["avatar"] = nullptr;
|
||||
else
|
||||
j["avatar"] = m.Avatar;
|
||||
JS_IF("bot", m.IsBot);
|
||||
JS_IF("system", m.IsSystem);
|
||||
JS_IF("mfa_enabled", m.IsMFAEnabled);
|
||||
JS_IF("locale", m.Locale);
|
||||
JS_IF("verified", m.IsVerified);
|
||||
JS_IF("email", m.Email);
|
||||
JS_IF("flags", m.Flags);
|
||||
JS_IF("premium_type", m.PremiumType);
|
||||
JS_IF("public_flags", m.PublicFlags);
|
||||
JS_IF("desktop", m.IsDesktop);
|
||||
JS_IF("mobile", m.IsMobile);
|
||||
JS_IF("nsfw_allowed", m.IsNSFWAllowed);
|
||||
JS_IF("phone", m.Phone);
|
||||
}
|
||||
|
||||
void UserData::update_from_json(const nlohmann::json &j) {
|
||||
JS_RD("username", Username);
|
||||
JS_RD("discriminator", Discriminator);
|
||||
JS_RD("avatar", Avatar);
|
||||
JS_RD("bot", IsBot);
|
||||
JS_RD("system", IsSystem);
|
||||
JS_RD("mfa_enabled", IsMFAEnabled);
|
||||
JS_RD("locale", Locale);
|
||||
JS_RD("verified", IsVerified);
|
||||
JS_RD("email", Email);
|
||||
JS_RD("flags", Flags);
|
||||
JS_RD("premium_type", PremiumType);
|
||||
JS_RD("public_flags", PublicFlags);
|
||||
JS_RD("desktop", IsDesktop);
|
||||
JS_RD("mobile", IsMobile);
|
||||
JS_RD("nsfw_allowed", IsNSFWAllowed);
|
||||
JS_RD("phone", Phone);
|
||||
}
|
||||
|
||||
const char *UserData::GetFlagName(uint64_t flag) {
|
||||
switch (flag) {
|
||||
case DiscordEmployee:
|
||||
return "discordstaff";
|
||||
case PartneredServerOwner:
|
||||
return "partneredowner";
|
||||
case HypeSquadEvents:
|
||||
return "hypesquadevents";
|
||||
case BugHunterLevel1:
|
||||
return "discordbughunter";
|
||||
case HouseBravery:
|
||||
return "hypesquadbravery";
|
||||
case HouseBrilliance:
|
||||
return "hypesquadbrilliance";
|
||||
case HouseBalance:
|
||||
return "hypesquadbalance";
|
||||
case EarlySupporter:
|
||||
return "earlysupporter";
|
||||
case TeamUser:
|
||||
return "teamuser";
|
||||
case System:
|
||||
return "system";
|
||||
case BugHunterLevel2:
|
||||
return "discordbughunter2";
|
||||
case VerifiedBot:
|
||||
return "verifiedbot";
|
||||
case EarlyVerifiedBotDeveloper:
|
||||
return "earlyverifiedbotdeveloper";
|
||||
case CertifiedModerator:
|
||||
return "certifiedmoderator";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
const char *UserData::GetFlagReadableName(uint64_t flag) {
|
||||
switch (flag) {
|
||||
case DiscordEmployee:
|
||||
return "Discord Staff";
|
||||
case PartneredServerOwner:
|
||||
return "Partnered Server Owner";
|
||||
case HypeSquadEvents:
|
||||
return "HypeSquad Events";
|
||||
case BugHunterLevel1:
|
||||
return "Discord Bug Hunter";
|
||||
case HouseBravery:
|
||||
return "HypeSquad Bravery";
|
||||
case HouseBrilliance:
|
||||
return "HypeSquad Brilliance";
|
||||
case HouseBalance:
|
||||
return "HypeSquad Balance";
|
||||
case EarlySupporter:
|
||||
return "Early Supporter";
|
||||
case TeamUser:
|
||||
return "Team User"; // ???
|
||||
case System:
|
||||
return "System";
|
||||
case BugHunterLevel2:
|
||||
return "Discord Bug Hunter Level 2";
|
||||
case VerifiedBot:
|
||||
return "Verified Bot";
|
||||
case EarlyVerifiedBotDeveloper:
|
||||
return "Early Verified Bot Developer";
|
||||
case CertifiedModerator:
|
||||
return "Discord Certified Moderator";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
82
src/discord/user.hpp
Normal file
82
src/discord/user.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#pragma once
|
||||
#include "snowflake.hpp"
|
||||
#include "json.hpp"
|
||||
#include <string>
|
||||
|
||||
enum class EPremiumType {
|
||||
None = 0,
|
||||
NitroClassic = 1,
|
||||
Nitro = 2,
|
||||
};
|
||||
|
||||
struct UserData {
|
||||
enum {
|
||||
DiscordEmployee = 1 << 0,
|
||||
PartneredServerOwner = 1 << 1,
|
||||
HypeSquadEvents = 1 << 2,
|
||||
BugHunterLevel1 = 1 << 3,
|
||||
HouseBravery = 1 << 6,
|
||||
HouseBrilliance = 1 << 7,
|
||||
HouseBalance = 1 << 8,
|
||||
EarlySupporter = 1 << 9,
|
||||
TeamUser = 1 << 10, // no idea what this is
|
||||
System = 1 << 12,
|
||||
BugHunterLevel2 = 1 << 14,
|
||||
VerifiedBot = 1 << 16,
|
||||
EarlyVerifiedBotDeveloper = 1 << 17,
|
||||
CertifiedModerator = 1 << 18,
|
||||
|
||||
MaxFlag_PlusOne,
|
||||
MaxFlag = MaxFlag_PlusOne - 1,
|
||||
};
|
||||
|
||||
static const char *GetFlagName(uint64_t flag);
|
||||
static const char *GetFlagReadableName(uint64_t flag);
|
||||
|
||||
Snowflake ID;
|
||||
std::string Username;
|
||||
std::string Discriminator;
|
||||
std::string Avatar; // null
|
||||
std::optional<bool> IsBot;
|
||||
std::optional<bool> IsSystem;
|
||||
std::optional<bool> IsMFAEnabled;
|
||||
std::optional<std::string> Locale;
|
||||
std::optional<bool> IsVerified;
|
||||
std::optional<std::string> Email; // null
|
||||
std::optional<uint64_t> Flags;
|
||||
std::optional<EPremiumType> PremiumType; // null
|
||||
std::optional<uint64_t> PublicFlags;
|
||||
|
||||
// undocumented (opt)
|
||||
std::optional<bool> IsDesktop;
|
||||
std::optional<bool> IsMobile;
|
||||
std::optional<bool> IsNSFWAllowed; // null
|
||||
std::optional<std::string> Phone; // null?
|
||||
// for now (unserialized)
|
||||
std::optional<std::string> BannerHash; // null
|
||||
std::optional<std::string> Bio; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserData &m);
|
||||
friend void to_json(nlohmann::json &j, const UserData &m);
|
||||
void update_from_json(const nlohmann::json &j);
|
||||
|
||||
bool IsDeleted() const;
|
||||
bool HasAvatar() const;
|
||||
bool HasAnimatedAvatar() const;
|
||||
std::string GetAvatarURL(Snowflake guild_id, std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetAvatarURL(const std::optional<Snowflake> &guild_id, std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetAvatarURL(std::string ext = "png", std::string size = "32") const;
|
||||
std::string GetDefaultAvatarURL() const;
|
||||
Snowflake GetHoistedRole(Snowflake guild_id, bool with_color = false) const;
|
||||
std::string GetMention() const;
|
||||
std::string GetEscapedName() const;
|
||||
std::string GetEscapedBoldName() const;
|
||||
std::string GetEscapedString() const;
|
||||
template<bool with_at>
|
||||
inline std::string GetEscapedBoldString() const {
|
||||
if constexpr (with_at)
|
||||
return "<b>@" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
|
||||
else
|
||||
return "<b>" + Glib::Markup::escape_text(Username) + "</b>#" + Discriminator;
|
||||
}
|
||||
};
|
||||
40
src/discord/usersettings.cpp
Normal file
40
src/discord/usersettings.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "usersettings.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m) {
|
||||
JS_N("color", m.Color);
|
||||
JS_D("guild_ids", m.GuildIDs);
|
||||
JS_N("id", m.ID);
|
||||
JS_N("name", m.Name);
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json &j, UserSettings &m) {
|
||||
JS_D("timezone_offset", m.TimezoneOffset);
|
||||
JS_D("theme", m.Theme);
|
||||
JS_D("stream_notifications_enabled", m.AreStreamNotificationsEnabled);
|
||||
JS_D("status", m.Status);
|
||||
JS_D("show_current_game", m.ShouldShowCurrentGame);
|
||||
// JS_D("restricted_guilds", m.RestrictedGuilds);
|
||||
JS_D("render_reactions", m.ShouldRenderReactions);
|
||||
JS_D("render_embeds", m.ShouldRenderEmbeds);
|
||||
JS_D("native_phone_integration_enabled", m.IsNativePhoneIntegrationEnabled);
|
||||
JS_D("message_display_compact", m.ShouldMessageDisplayCompact);
|
||||
JS_D("locale", m.Locale);
|
||||
JS_D("inline_embed_media", m.ShouldInlineEmbedMedia);
|
||||
JS_D("inline_attachment_media", m.ShouldInlineAttachmentMedia);
|
||||
JS_D("guild_positions", m.GuildPositions);
|
||||
JS_D("guild_folders", m.GuildFolders);
|
||||
JS_D("gif_auto_play", m.ShouldGIFAutoplay);
|
||||
// JS_D("friend_source_flags", m.FriendSourceFlags);
|
||||
JS_D("explicit_content_filter", m.ExplicitContentFilter);
|
||||
JS_D("enable_tts_command", m.IsTTSCommandEnabled);
|
||||
JS_D("disable_games_tab", m.ShouldDisableGamesTab);
|
||||
JS_D("developer_mode", m.DeveloperMode);
|
||||
JS_D("detect_platform_accounts", m.ShouldDetectPlatformAccounts);
|
||||
JS_D("default_guilds_restricted", m.AreDefaultGuildsRestricted);
|
||||
// JS_N("custom_status", m.CustomStatus);
|
||||
JS_D("convert_emoticons", m.ShouldConvertEmoticons);
|
||||
JS_D("contact_sync_enabled", m.IsContactSyncEnabled);
|
||||
JS_D("animate_emoji", m.ShouldAnimateEmojis);
|
||||
JS_D("allow_accessibility_detection", m.IsAccessibilityDetectionAllowed);
|
||||
JS_D("afk_timeout", m.AFKTimeout);
|
||||
}
|
||||
47
src/discord/usersettings.hpp
Normal file
47
src/discord/usersettings.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include <string>
|
||||
|
||||
struct UserSettingsGuildFoldersEntry {
|
||||
int Color = -1; // null
|
||||
std::vector<Snowflake> GuildIDs;
|
||||
Snowflake ID; // null (this can be a snowflake as a string or an int that isnt a snowflake lol)
|
||||
std::string Name; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserSettingsGuildFoldersEntry &m);
|
||||
};
|
||||
|
||||
struct UserSettings {
|
||||
int TimezoneOffset; //
|
||||
std::string Theme; //
|
||||
bool AreStreamNotificationsEnabled; //
|
||||
std::string Status; //
|
||||
bool ShouldShowCurrentGame; //
|
||||
// std::vector<Unknown> RestrictedGuilds; //
|
||||
bool ShouldRenderReactions; //
|
||||
bool ShouldRenderEmbeds; //
|
||||
bool IsNativePhoneIntegrationEnabled; //
|
||||
bool ShouldMessageDisplayCompact; //
|
||||
std::string Locale; //
|
||||
bool ShouldInlineEmbedMedia; //
|
||||
bool ShouldInlineAttachmentMedia; //
|
||||
std::vector<Snowflake> GuildPositions; // deprecated?
|
||||
std::vector<UserSettingsGuildFoldersEntry> GuildFolders; //
|
||||
bool ShouldGIFAutoplay; //
|
||||
// Unknown FriendSourceFlags; //
|
||||
int ExplicitContentFilter; //
|
||||
bool IsTTSCommandEnabled; //
|
||||
bool ShouldDisableGamesTab; //
|
||||
bool DeveloperMode; //
|
||||
bool ShouldDetectPlatformAccounts; //
|
||||
bool AreDefaultGuildsRestricted; //
|
||||
// Unknown CustomStatus; // null
|
||||
bool ShouldConvertEmoticons; //
|
||||
bool IsContactSyncEnabled; //
|
||||
bool ShouldAnimateEmojis; //
|
||||
bool IsAccessibilityDetectionAllowed; //
|
||||
int AFKTimeout;
|
||||
|
||||
friend void from_json(const nlohmann::json &j, UserSettings &m);
|
||||
};
|
||||
13
src/discord/webhook.cpp
Normal file
13
src/discord/webhook.cpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#include "webhook.hpp"
|
||||
|
||||
void from_json(const nlohmann::json &j, WebhookData &m) {
|
||||
JS_D("id", m.ID);
|
||||
JS_D("type", m.Type);
|
||||
JS_O("guild_id", m.GuildID);
|
||||
JS_D("channel_id", m.ChannelID);
|
||||
JS_O("user", m.User);
|
||||
JS_N("name", m.Name);
|
||||
JS_N("avatar", m.Avatar);
|
||||
JS_O("token", m.Token);
|
||||
JS_N("application_id", m.ApplicationID);
|
||||
}
|
||||
24
src/discord/webhook.hpp
Normal file
24
src/discord/webhook.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include "json.hpp"
|
||||
#include "snowflake.hpp"
|
||||
#include "user.hpp"
|
||||
|
||||
enum class WebhookType {
|
||||
Incoming = 1,
|
||||
ChannelFollower = 2,
|
||||
};
|
||||
|
||||
struct WebhookData {
|
||||
Snowflake ID;
|
||||
WebhookType Type;
|
||||
std::optional<Snowflake> GuildID;
|
||||
Snowflake ChannelID;
|
||||
std::optional<UserData> User;
|
||||
std::string Name; // null
|
||||
std::string Avatar; // null
|
||||
std::optional<std::string> Token;
|
||||
Snowflake ApplicationID; // null
|
||||
|
||||
friend void from_json(const nlohmann::json &j, WebhookData &m);
|
||||
};
|
||||
66
src/discord/websocket.cpp
Normal file
66
src/discord/websocket.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include "websocket.hpp"
|
||||
#include <functional>
|
||||
|
||||
Websocket::Websocket() {}
|
||||
|
||||
void Websocket::StartConnection(std::string url) {
|
||||
m_websocket.disableAutomaticReconnection();
|
||||
m_websocket.setUrl(url);
|
||||
m_websocket.setOnMessageCallback(std::bind(&Websocket::OnMessage, this, std::placeholders::_1));
|
||||
m_websocket.setExtraHeaders(ix::WebSocketHttpHeaders { { "User-Agent", m_agent } }); // idk if this actually works
|
||||
m_websocket.start();
|
||||
}
|
||||
|
||||
void Websocket::SetUserAgent(std::string agent) {
|
||||
m_agent = agent;
|
||||
}
|
||||
|
||||
void Websocket::Stop() {
|
||||
Stop(ix::WebSocketCloseConstants::kNormalClosureCode);
|
||||
}
|
||||
|
||||
void Websocket::Stop(uint16_t code) {
|
||||
m_websocket.stop(code);
|
||||
}
|
||||
|
||||
bool Websocket::IsOpen() const {
|
||||
auto state = m_websocket.getReadyState();
|
||||
return state == ix::ReadyState::Open;
|
||||
}
|
||||
|
||||
void Websocket::Send(const std::string &str) {
|
||||
printf("sending %s\n", str.c_str());
|
||||
m_websocket.sendText(str);
|
||||
}
|
||||
|
||||
void Websocket::Send(const nlohmann::json &j) {
|
||||
Send(j.dump());
|
||||
}
|
||||
|
||||
void Websocket::OnMessage(const ix::WebSocketMessagePtr &msg) {
|
||||
switch (msg->type) {
|
||||
case ix::WebSocketMessageType::Open: {
|
||||
m_signal_open.emit();
|
||||
} break;
|
||||
case ix::WebSocketMessageType::Close: {
|
||||
m_signal_close.emit(msg->closeInfo.code);
|
||||
} break;
|
||||
case ix::WebSocketMessageType::Message: {
|
||||
m_signal_message.emit(msg->str);
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Websocket::type_signal_open Websocket::signal_open() {
|
||||
return m_signal_open;
|
||||
}
|
||||
|
||||
Websocket::type_signal_close Websocket::signal_close() {
|
||||
return m_signal_close;
|
||||
}
|
||||
|
||||
Websocket::type_signal_message Websocket::signal_message() {
|
||||
return m_signal_message;
|
||||
}
|
||||
41
src/discord/websocket.hpp
Normal file
41
src/discord/websocket.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
#include <ixwebsocket/IXNetSystem.h>
|
||||
#include <ixwebsocket/IXWebSocket.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
class Websocket {
|
||||
public:
|
||||
Websocket();
|
||||
void StartConnection(std::string url);
|
||||
|
||||
void SetUserAgent(std::string agent);
|
||||
|
||||
void Send(const std::string &str);
|
||||
void Send(const nlohmann::json &j);
|
||||
void Stop();
|
||||
void Stop(uint16_t code);
|
||||
bool IsOpen() const;
|
||||
|
||||
private:
|
||||
void OnMessage(const ix::WebSocketMessagePtr &msg);
|
||||
|
||||
ix::WebSocket m_websocket;
|
||||
std::string m_agent;
|
||||
|
||||
public:
|
||||
typedef sigc::signal<void> type_signal_open;
|
||||
typedef sigc::signal<void, uint16_t> type_signal_close;
|
||||
typedef sigc::signal<void, std::string> type_signal_message;
|
||||
|
||||
type_signal_open signal_open();
|
||||
type_signal_close signal_close();
|
||||
type_signal_message signal_message();
|
||||
|
||||
private:
|
||||
type_signal_open m_signal_open;
|
||||
type_signal_close m_signal_close;
|
||||
type_signal_message m_signal_message;
|
||||
};
|
||||
Reference in New Issue
Block a user