Files
abaddon/src/discord/store.hpp
2023-01-29 21:33:34 -05:00

316 lines
9.9 KiB
C++

#pragma once
#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;
Snowflake GetGuildOwner(Snowflake guild_id) const;
std::vector<Snowflake> GetMemberRoles(Snowflake guild_id, Snowflake user_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
std::vector<Snowflake> GetChannelIDsWithParentID(Snowflake channel_id) const;
std::unordered_set<Snowflake> GetMembersInGuild(Snowflake guild_id) const;
// ^ not the same as GetUsersInGuild since users in a guild may include users who do not have retrieved member data
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);
void ClearRole(Snowflake 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;
[[nodiscard]] 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>();
}
[[nodiscard]] 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;
static RoleData GetRoleBound(std::unique_ptr<Statement> &stmt);
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(get_guild_roles);
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(clr_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);
STMT(get_chan_ids_parent);
STMT(get_guild_member_ids);
STMT(clr_role);
STMT(get_guild_owner);
#undef STMT
};