mirror of
https://github.com/uowuo/abaddon.git
synced 2025-08-12 08:40:16 +00:00
initial sqlite, store user
This commit is contained in:
parent
1463d8da9e
commit
deb482a8db
@ -69,6 +69,9 @@ if(NOT USE_PKGCONFIG)
|
||||
|
||||
find_package(nlohmann_json 3.2.0 REQUIRED)
|
||||
|
||||
find_package(unofficial-sqlite3 CONFIG REQUIRED)
|
||||
link_libraries(unofficial::sqlite3::sqlite3)
|
||||
|
||||
find_path(IXWEBSOCKET_INCLUDE_DIRS ixwebsocket/ixwebsocket.h)
|
||||
find_library(IXWEBSOCKET_LIBRARY ixwebsocket)
|
||||
link_libraries(${IXWEBSOCKET_LIBRARY})
|
||||
|
16
abaddon.cpp
16
abaddon.cpp
@ -54,7 +54,6 @@ int Abaddon::StartGTK() {
|
||||
|
||||
m_main_window = std::make_unique<MainWindow>();
|
||||
m_main_window->set_title(APP_TITLE);
|
||||
m_main_window->show();
|
||||
m_main_window->UpdateComponents();
|
||||
|
||||
// crashes for some stupid reason if i put it somewhere else
|
||||
@ -111,6 +110,13 @@ int Abaddon::StartGTK() {
|
||||
dlg.run();
|
||||
}
|
||||
|
||||
if (!m_discord.IsStoreValid()) {
|
||||
Gtk::MessageDialog dlg(*m_main_window, "The Discord cache could not be created!", false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
||||
dlg.run();
|
||||
return 1;
|
||||
}
|
||||
|
||||
m_main_window->show();
|
||||
return m_gtk_app->run(*m_main_window);
|
||||
}
|
||||
|
||||
@ -380,8 +386,8 @@ void Abaddon::ActionLeaveGuild(Snowflake id) {
|
||||
|
||||
void Abaddon::ActionKickMember(Snowflake user_id, Snowflake guild_id) {
|
||||
ConfirmDialog dlg(*m_main_window);
|
||||
const auto *user = m_discord.GetUser(user_id);
|
||||
if (user != nullptr)
|
||||
const auto user = m_discord.GetUser(user_id);
|
||||
if (user.has_value())
|
||||
dlg.SetConfirmText("Are you sure you want to kick " + user->Username + "#" + user->Discriminator + "?");
|
||||
auto response = dlg.run();
|
||||
if (response == Gtk::RESPONSE_OK)
|
||||
@ -390,8 +396,8 @@ void Abaddon::ActionKickMember(Snowflake user_id, Snowflake guild_id) {
|
||||
|
||||
void Abaddon::ActionBanMember(Snowflake user_id, Snowflake guild_id) {
|
||||
ConfirmDialog dlg(*m_main_window);
|
||||
const auto *user = m_discord.GetUser(user_id);
|
||||
if (user != nullptr)
|
||||
const auto user = m_discord.GetUser(user_id);
|
||||
if (user.has_value())
|
||||
dlg.SetConfirmText("Are you sure you want to ban " + user->Username + "#" + user->Discriminator + "?");
|
||||
auto response = dlg.run();
|
||||
if (response == Gtk::RESPONSE_OK)
|
||||
|
@ -416,9 +416,9 @@ void ChatMessageItemContainer::HandleUserMentions(Gtk::TextView *tv) {
|
||||
int mstart, mend;
|
||||
if (!match.fetch_pos(0, mstart, mend)) break;
|
||||
const Glib::ustring user_id = match.fetch(1);
|
||||
const auto *user = discord.GetUser(user_id);
|
||||
const auto user = discord.GetUser(user_id);
|
||||
const auto *channel = discord.GetChannel(ChannelID);
|
||||
if (user == nullptr || channel == nullptr) {
|
||||
if (!user.has_value() || channel == nullptr) {
|
||||
startpos = mend;
|
||||
continue;
|
||||
}
|
||||
@ -814,8 +814,8 @@ void ChatMessageHeader::UpdateNameColor() {
|
||||
const auto &discord = Abaddon::Get().GetDiscordClient();
|
||||
const auto guild_id = discord.GetChannel(ChannelID)->GuildID;
|
||||
const auto role_id = discord.GetMemberHoistedRole(guild_id, UserID, true);
|
||||
const auto *user = discord.GetUser(UserID);
|
||||
if (user == nullptr) return;
|
||||
const auto user = discord.GetUser(UserID);
|
||||
if (!user.has_value()) return;
|
||||
const auto *role = discord.GetRole(role_id);
|
||||
|
||||
std::string md;
|
||||
|
@ -179,8 +179,8 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
|
||||
} else {
|
||||
const auto guild_id = client.GetChannel(m_active_channel)->GuildID;
|
||||
const auto user_id = data->Author.ID;
|
||||
const auto *user = client.GetUser(user_id);
|
||||
if (user == nullptr) return;
|
||||
const auto user = client.GetUser(user_id);
|
||||
if (!user.has_value()) return;
|
||||
|
||||
header = Gtk::manage(new ChatMessageHeader(data));
|
||||
header->signal_action_insert_mention().connect([this, user_id]() {
|
||||
|
@ -112,13 +112,13 @@ void MemberList::UpdateMemberListInternal() {
|
||||
|
||||
// process all the shit first so its in proper order
|
||||
std::map<int, const Role *> pos_to_role;
|
||||
std::map<int, std::vector<const User *>> pos_to_users;
|
||||
std::map<int, std::vector<User>> pos_to_users;
|
||||
std::unordered_map<Snowflake, int> user_to_color;
|
||||
std::vector<Snowflake> roleless_users;
|
||||
|
||||
for (const auto &id : ids) {
|
||||
auto *user = discord.GetUser(id);
|
||||
if (user == nullptr) {
|
||||
auto user = discord.GetUser(id);
|
||||
if (!user.has_value()) {
|
||||
roleless_users.push_back(id);
|
||||
continue;
|
||||
}
|
||||
@ -134,7 +134,7 @@ void MemberList::UpdateMemberListInternal() {
|
||||
};
|
||||
|
||||
pos_to_role[pos_role->Position] = pos_role;
|
||||
pos_to_users[pos_role->Position].push_back(user);
|
||||
pos_to_users[pos_role->Position].push_back(std::move(*user));
|
||||
if (col_role != nullptr) {
|
||||
if (ColorDistance(col_role->Color, 0xFFFFFF) < 15)
|
||||
user_to_color[id] = 0x000000;
|
||||
@ -198,10 +198,10 @@ void MemberList::UpdateMemberListInternal() {
|
||||
if (pos_to_users.find(pos) == pos_to_users.end()) continue;
|
||||
|
||||
auto &users = pos_to_users.at(pos);
|
||||
AlphabeticalSort(users.begin(), users.end(), [](auto e) { return e->Username; });
|
||||
AlphabeticalSort(users.begin(), users.end(), [](const auto &e) { return e.Username; });
|
||||
|
||||
for (const auto data : users)
|
||||
add_user(data);
|
||||
add_user(&data);
|
||||
}
|
||||
|
||||
if (chan->Type == ChannelType::DM || chan->Type == ChannelType::GROUP_DM)
|
||||
@ -209,9 +209,9 @@ void MemberList::UpdateMemberListInternal() {
|
||||
else
|
||||
add_role("@everyone");
|
||||
for (const auto &id : roleless_users) {
|
||||
const auto *user = discord.GetUser(id);
|
||||
if (user != nullptr)
|
||||
add_user(user);
|
||||
const auto user = discord.GetUser(id);
|
||||
if (user.has_value())
|
||||
add_user(&*user);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,10 @@ bool DiscordClient::IsStarted() const {
|
||||
return m_client_connected;
|
||||
}
|
||||
|
||||
bool DiscordClient::IsStoreValid() const {
|
||||
return m_store.IsValid();
|
||||
}
|
||||
|
||||
std::unordered_set<Snowflake> DiscordClient::GetGuildsID() const {
|
||||
const auto &guilds = m_store.GetGuilds();
|
||||
std::unordered_set<Snowflake> ret;
|
||||
@ -121,6 +125,8 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(cons
|
||||
std::vector<Snowflake> ids;
|
||||
|
||||
nlohmann::json::parse(r.text).get_to(msgs);
|
||||
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &msg : msgs) {
|
||||
m_store.SetMessage(msg.ID, msg);
|
||||
AddMessageToChannel(msg.ID, id);
|
||||
@ -128,6 +134,7 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(cons
|
||||
AddUserToGuild(msg.Author.ID, *msg.GuildID);
|
||||
ids.push_back(msg.ID);
|
||||
}
|
||||
m_store.EndTransaction();
|
||||
|
||||
cb(ids);
|
||||
});
|
||||
@ -142,6 +149,8 @@ void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake
|
||||
std::vector<Snowflake> ids;
|
||||
|
||||
nlohmann::json::parse(r.text).get_to(msgs);
|
||||
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &msg : msgs) {
|
||||
m_store.SetMessage(msg.ID, msg);
|
||||
AddMessageToChannel(msg.ID, channel_id);
|
||||
@ -149,6 +158,7 @@ void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake
|
||||
AddUserToGuild(msg.Author.ID, *msg.GuildID);
|
||||
ids.push_back(msg.ID);
|
||||
}
|
||||
m_store.EndTransaction();
|
||||
|
||||
cb(ids);
|
||||
});
|
||||
@ -162,7 +172,7 @@ const Channel *DiscordClient::GetChannel(Snowflake id) const {
|
||||
return m_store.GetChannel(id);
|
||||
}
|
||||
|
||||
const User *DiscordClient::GetUser(Snowflake id) const {
|
||||
std::optional<User> DiscordClient::GetUser(Snowflake id) const {
|
||||
return m_store.GetUser(id);
|
||||
}
|
||||
|
||||
@ -594,11 +604,13 @@ void DiscordClient::HandleGatewayReady(const GatewayMessage &msg) {
|
||||
for (auto &g : data.Guilds)
|
||||
ProcessNewGuild(g);
|
||||
|
||||
m_store.BeginTransaction();
|
||||
for (const auto &dm : data.PrivateChannels) {
|
||||
m_store.SetChannel(dm.ID, dm);
|
||||
for (const auto &recipient : dm.Recipients)
|
||||
m_store.SetUser(recipient.ID, recipient);
|
||||
}
|
||||
m_store.EndTransaction();
|
||||
|
||||
m_session_id = data.SessionID;
|
||||
m_user_data = data.User;
|
||||
@ -642,8 +654,10 @@ void DiscordClient::HandleGatewayGuildMemberUpdate(const GatewayMessage &msg) {
|
||||
void DiscordClient::HandleGatewayPresenceUpdate(const GatewayMessage &msg) {
|
||||
PresenceUpdateMessage data = msg.Data;
|
||||
auto cur = m_store.GetUser(data.User.at("id").get<Snowflake>());
|
||||
if (cur != nullptr)
|
||||
if (cur.has_value()) {
|
||||
User::update_from_json(data.User, *cur);
|
||||
m_store.SetUser(cur->ID, *cur);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscordClient::HandleGatewayChannelDelete(const GatewayMessage &msg) {
|
||||
@ -715,6 +729,8 @@ void DiscordClient::HandleGatewayMessageUpdate(const GatewayMessage &msg) {
|
||||
void DiscordClient::HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg) {
|
||||
GuildMemberListUpdateMessage data = msg.Data;
|
||||
|
||||
m_store.BeginTransaction();
|
||||
|
||||
bool has_sync = false;
|
||||
for (const auto &op : data.Ops) {
|
||||
if (op.Op == "SYNC") {
|
||||
@ -730,6 +746,8 @@ void DiscordClient::HandleGatewayGuildMemberListUpdate(const GatewayMessage &msg
|
||||
}
|
||||
}
|
||||
|
||||
m_store.EndTransaction();
|
||||
|
||||
// todo: manage this event a little better
|
||||
if (has_sync)
|
||||
m_signal_guild_member_list_update.emit(data.GuildID);
|
||||
|
@ -59,6 +59,7 @@ public:
|
||||
void Start();
|
||||
void Stop();
|
||||
bool IsStarted() const;
|
||||
bool IsStoreValid() const;
|
||||
|
||||
using guilds_type = Store::guilds_type;
|
||||
using channels_type = Store::channels_type;
|
||||
@ -81,7 +82,7 @@ public:
|
||||
void FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function<void(const std::vector<Snowflake> &)> cb);
|
||||
const Message *GetMessage(Snowflake id) const;
|
||||
const Channel *GetChannel(Snowflake id) const;
|
||||
const User *GetUser(Snowflake id) const;
|
||||
std::optional<User> GetUser(Snowflake id) const;
|
||||
const Role *GetRole(Snowflake id) const;
|
||||
const Guild *GetGuild(Snowflake id) const;
|
||||
const GuildMember *GetMember(Snowflake user_id, Snowflake guild_id) const;
|
||||
|
@ -1,17 +1,12 @@
|
||||
#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>
|
||||
struct is_optional : ::std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_optional<::std::optional<T>> : ::std::true_type {};
|
||||
|
||||
template<typename T>
|
||||
inline void json_direct(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (is_optional<T>::value)
|
||||
if constexpr (::util::is_optional<T>::value)
|
||||
val = j.at(key).get<typename T::value_type>();
|
||||
else
|
||||
j.at(key).get_to(val);
|
||||
@ -19,7 +14,7 @@ inline void json_direct(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
|
||||
template<typename T>
|
||||
inline void json_optional(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (is_optional<T>::value) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key))
|
||||
val = j.at(key).get<typename T::value_type>();
|
||||
else
|
||||
@ -32,7 +27,7 @@ inline void json_optional(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
|
||||
template<typename T>
|
||||
inline void json_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (is_optional<T>::value) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
val = at.get<typename T::value_type>();
|
||||
@ -47,7 +42,7 @@ inline void json_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
|
||||
template<typename T>
|
||||
inline void json_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (is_optional<T>::value) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
@ -68,7 +63,7 @@ inline void json_optional_nullable(const ::nlohmann::json &j, const char *key, T
|
||||
|
||||
template<typename T>
|
||||
inline void json_update_optional_nullable(const ::nlohmann::json &j, const char *key, T &val) {
|
||||
if constexpr (is_optional<T>::value) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (!at.is_null())
|
||||
@ -89,7 +84,7 @@ inline void json_update_optional_nullable(const ::nlohmann::json &j, const char
|
||||
|
||||
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 (is_optional<T>::value) {
|
||||
if constexpr (::util::is_optional<T>::value) {
|
||||
if (j.contains(key)) {
|
||||
const auto &at = j.at(key);
|
||||
if (at.is_null())
|
||||
|
@ -1,7 +1,71 @@
|
||||
#include "store.hpp"
|
||||
|
||||
// hopefully the casting between signed and unsigned int64 doesnt cause issues
|
||||
|
||||
Store::Store() {
|
||||
m_db_path = std::filesystem::temp_directory_path() / "abaddon-store.db";
|
||||
m_db_err = sqlite3_open(m_db_path.string().c_str(), &m_db);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error opening database: %s\n", sqlite3_errstr(m_db_err));
|
||||
return;
|
||||
}
|
||||
|
||||
m_db_err = sqlite3_exec(m_db, "PRAGMA journal_mode = WAL", nullptr, nullptr, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "enabling write-ahead-log failed: %s\n", sqlite3_errstr(m_db_err));
|
||||
return;
|
||||
}
|
||||
|
||||
m_db_err = sqlite3_exec(m_db, "PRAGMA synchronous = NORMAL", nullptr, nullptr, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "setting synchronous failed: %s\n", sqlite3_errstr(m_db_err));
|
||||
return;
|
||||
}
|
||||
|
||||
CreateTables();
|
||||
CreateStatements();
|
||||
}
|
||||
|
||||
Store::~Store() {
|
||||
Cleanup();
|
||||
|
||||
m_db_err = sqlite3_close(m_db);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error closing database: %s\n", sqlite3_errstr(m_db_err));
|
||||
return;
|
||||
}
|
||||
|
||||
std::filesystem::remove(m_db_path);
|
||||
}
|
||||
|
||||
bool Store::IsValid() const {
|
||||
return m_db_err == SQLITE_OK;
|
||||
}
|
||||
|
||||
void Store::SetUser(Snowflake id, const User &user) {
|
||||
m_users[id] = user;
|
||||
if ((uint64_t)id == 0) {
|
||||
printf("???: %s\n", user.Username.c_str());
|
||||
}
|
||||
|
||||
Bind(m_set_user_stmt, 1, id);
|
||||
Bind(m_set_user_stmt, 2, user.Username);
|
||||
Bind(m_set_user_stmt, 3, user.Discriminator);
|
||||
Bind(m_set_user_stmt, 4, user.Avatar);
|
||||
Bind(m_set_user_stmt, 5, user.IsBot);
|
||||
Bind(m_set_user_stmt, 6, user.IsSystem);
|
||||
Bind(m_set_user_stmt, 7, user.IsMFAEnabled);
|
||||
Bind(m_set_user_stmt, 8, user.Locale);
|
||||
Bind(m_set_user_stmt, 9, user.IsVerified);
|
||||
Bind(m_set_user_stmt, 10, user.Email);
|
||||
Bind(m_set_user_stmt, 11, user.Flags);
|
||||
Bind(m_set_user_stmt, 12, user.PremiumType);
|
||||
Bind(m_set_user_stmt, 13, user.PublicFlags);
|
||||
|
||||
if (!RunInsert(m_set_user_stmt)) {
|
||||
fprintf(stderr, "user insert failed: %s\n", sqlite3_errstr(m_db_err));
|
||||
}
|
||||
|
||||
// m_users[id] = user;
|
||||
}
|
||||
|
||||
void Store::SetChannel(Snowflake id, const Channel &channel) {
|
||||
@ -32,18 +96,33 @@ void Store::SetEmoji(Snowflake id, const Emoji &emoji) {
|
||||
m_emojis[id] = emoji;
|
||||
}
|
||||
|
||||
User *Store::GetUser(Snowflake id) {
|
||||
auto it = m_users.find(id);
|
||||
if (it == m_users.end())
|
||||
return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
std::optional<User> Store::GetUser(Snowflake id) const {
|
||||
Bind(m_get_user_stmt, 1, id);
|
||||
if (!FetchOne(m_get_user_stmt)) {
|
||||
if (m_db_err != SQLITE_DONE) // not an error, just means user isnt found
|
||||
fprintf(stderr, "error while fetching user info: %s\n", sqlite3_errstr(m_db_err));
|
||||
Reset(m_get_user_stmt);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const User *Store::GetUser(Snowflake id) const {
|
||||
auto it = m_users.find(id);
|
||||
if (it == m_users.end())
|
||||
return nullptr;
|
||||
return &it->second;
|
||||
User ret;
|
||||
Get(m_get_user_stmt, 0, ret.ID);
|
||||
Get(m_get_user_stmt, 1, ret.Username);
|
||||
Get(m_get_user_stmt, 2, ret.Discriminator);
|
||||
Get(m_get_user_stmt, 3, ret.Avatar);
|
||||
Get(m_get_user_stmt, 4, ret.IsBot);
|
||||
Get(m_get_user_stmt, 5, ret.IsSystem);
|
||||
Get(m_get_user_stmt, 6, ret.IsMFAEnabled);
|
||||
Get(m_get_user_stmt, 7, ret.Locale);
|
||||
Get(m_get_user_stmt, 8, ret.IsVerified);
|
||||
Get(m_get_user_stmt, 9, ret.Email);
|
||||
Get(m_get_user_stmt, 10, ret.Flags);
|
||||
Get(m_get_user_stmt, 11, ret.PremiumType);
|
||||
Get(m_get_user_stmt, 12, ret.PublicFlags);
|
||||
|
||||
Reset(m_get_user_stmt);
|
||||
|
||||
return std::optional<User>(std::move(ret));
|
||||
}
|
||||
|
||||
Channel *Store::GetChannel(Snowflake id) {
|
||||
@ -186,3 +265,136 @@ void Store::ClearAll() {
|
||||
m_roles.clear();
|
||||
m_users.clear();
|
||||
}
|
||||
|
||||
void Store::BeginTransaction() {
|
||||
m_db_err = sqlite3_exec(m_db, "BEGIN TRANSACTION", nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void Store::EndTransaction() {
|
||||
m_db_err = sqlite3_exec(m_db, "COMMIT", nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool Store::CreateTables() {
|
||||
constexpr const char *create_users = R"(
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT NOT NULL,
|
||||
discriminator TEXT NOT NULL,
|
||||
avatar TEXT,
|
||||
bot BOOL,
|
||||
system BOOL,
|
||||
mfa BOOL,
|
||||
locale TEXT,
|
||||
verified BOOl,
|
||||
email TEXT,
|
||||
flags INTEGER,
|
||||
premium INTEGER,
|
||||
pubflags INTEGER
|
||||
)
|
||||
)";
|
||||
|
||||
m_db_err = sqlite3_exec(m_db, create_users, nullptr, nullptr, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "failed to create user table\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Store::CreateStatements() {
|
||||
constexpr const char *set_user = R"(
|
||||
REPLACE INTO users VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)
|
||||
)";
|
||||
|
||||
constexpr const char *get_user = R"(
|
||||
SELECT * FROM users WHERE id = ?
|
||||
)";
|
||||
|
||||
m_db_err = sqlite3_prepare_v2(m_db, set_user, -1, &m_set_user_stmt, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "failed to prepare set user statement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_db_err = sqlite3_prepare_v2(m_db, get_user, -1, &m_get_user_stmt, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "failed to prepare get user statement\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Store::Cleanup() {
|
||||
sqlite3_finalize(m_set_user_stmt);
|
||||
sqlite3_finalize(m_get_user_stmt);
|
||||
}
|
||||
|
||||
void Store::Bind(sqlite3_stmt *stmt, int index, int num) const {
|
||||
m_db_err = sqlite3_bind_int(stmt, index, num);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
|
||||
}
|
||||
}
|
||||
|
||||
void Store::Bind(sqlite3_stmt *stmt, int index, uint64_t num) const {
|
||||
m_db_err = sqlite3_bind_int64(stmt, index, num);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
|
||||
}
|
||||
}
|
||||
|
||||
void Store::Bind(sqlite3_stmt *stmt, int index, const std::string &str) const {
|
||||
m_db_err = sqlite3_bind_text(stmt, index, str.c_str(), -1, nullptr);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
|
||||
}
|
||||
}
|
||||
|
||||
void Store::Bind(sqlite3_stmt *stmt, int index, bool val) const {
|
||||
m_db_err = sqlite3_bind_int(stmt, index, val ? 1 : 0);
|
||||
if (m_db_err != SQLITE_OK) {
|
||||
fprintf(stderr, "error binding index %d: %s\n", index, sqlite3_errstr(m_db_err));
|
||||
}
|
||||
}
|
||||
|
||||
bool Store::RunInsert(sqlite3_stmt *stmt) {
|
||||
m_db_err = sqlite3_step(stmt);
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_clear_bindings(stmt);
|
||||
return m_db_err == SQLITE_DONE;
|
||||
}
|
||||
|
||||
bool Store::FetchOne(sqlite3_stmt *stmt) const {
|
||||
m_db_err = sqlite3_step(stmt);
|
||||
return m_db_err == SQLITE_ROW;
|
||||
}
|
||||
|
||||
void Store::Get(sqlite3_stmt *stmt, int index, int &out) const {
|
||||
out = sqlite3_column_int(stmt, index);
|
||||
}
|
||||
|
||||
void Store::Get(sqlite3_stmt *stmt, int index, std::string &out) const {
|
||||
const unsigned char *ptr = sqlite3_column_text(stmt, index);
|
||||
if (ptr == nullptr)
|
||||
out = "";
|
||||
else
|
||||
out = reinterpret_cast<const char *>(ptr);
|
||||
}
|
||||
|
||||
void Store::Get(sqlite3_stmt *stmt, int index, bool &out) const {
|
||||
out = sqlite3_column_int(stmt, index) != 0;
|
||||
}
|
||||
|
||||
void Store::Get(sqlite3_stmt *stmt, int index, Snowflake &out) const {
|
||||
const int64_t num = sqlite3_column_int64(stmt, index);
|
||||
out = static_cast<uint64_t>(num);
|
||||
}
|
||||
|
||||
void Store::Reset(sqlite3_stmt *stmt) const {
|
||||
sqlite3_reset(stmt);
|
||||
sqlite3_clear_bindings(stmt);
|
||||
}
|
||||
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
#include "../util.hpp"
|
||||
#include "objects.hpp"
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <filesystem>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#ifdef GetMessage // fuck you windows.h
|
||||
#undef GetMessage
|
||||
@ -9,6 +12,11 @@
|
||||
|
||||
class Store {
|
||||
public:
|
||||
Store();
|
||||
~Store();
|
||||
|
||||
bool IsValid() const;
|
||||
|
||||
void SetUser(Snowflake id, const User &user);
|
||||
void SetChannel(Snowflake id, const Channel &channel);
|
||||
void SetGuild(Snowflake id, const Guild &guild);
|
||||
@ -18,7 +26,8 @@ public:
|
||||
void SetPermissionOverwrite(Snowflake channel_id, Snowflake id, const PermissionOverwrite &perm);
|
||||
void SetEmoji(Snowflake id, const Emoji &emoji);
|
||||
|
||||
User *GetUser(Snowflake id);
|
||||
// slap const on everything even tho its not *really* const
|
||||
|
||||
Channel *GetChannel(Snowflake id);
|
||||
Guild *GetGuild(Snowflake id);
|
||||
Role *GetRole(Snowflake id);
|
||||
@ -26,7 +35,7 @@ public:
|
||||
GuildMember *GetGuildMemberData(Snowflake guild_id, Snowflake user_id);
|
||||
PermissionOverwrite *GetPermissionOverwrite(Snowflake channel_id, Snowflake id);
|
||||
Emoji *GetEmoji(Snowflake id);
|
||||
const User *GetUser(Snowflake id) const;
|
||||
std::optional<User> GetUser(Snowflake id) const;
|
||||
const Channel *GetChannel(Snowflake id) const;
|
||||
const Guild *GetGuild(Snowflake id) const;
|
||||
const Role *GetRole(Snowflake id) const;
|
||||
@ -53,6 +62,9 @@ public:
|
||||
|
||||
void ClearAll();
|
||||
|
||||
void BeginTransaction();
|
||||
void EndTransaction();
|
||||
|
||||
private:
|
||||
users_type m_users;
|
||||
channels_type m_channels;
|
||||
@ -62,4 +74,50 @@ private:
|
||||
members_type m_members;
|
||||
permission_overwrites_type m_permissions;
|
||||
emojis_type m_emojis;
|
||||
|
||||
bool CreateTables();
|
||||
bool CreateStatements();
|
||||
void Cleanup();
|
||||
|
||||
template<typename T>
|
||||
void Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, int num) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, uint64_t num) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, const std::string &str) const;
|
||||
void Bind(sqlite3_stmt *stmt, int index, bool val) const;
|
||||
bool RunInsert(sqlite3_stmt *stmt);
|
||||
bool FetchOne(sqlite3_stmt *stmt) const;
|
||||
template<typename T>
|
||||
void Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, int &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, uint64_t &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, std::string &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, bool &out) const;
|
||||
void Get(sqlite3_stmt *stmt, int index, Snowflake &out) const;
|
||||
void Reset(sqlite3_stmt *stmt) const;
|
||||
|
||||
std::filesystem::path m_db_path;
|
||||
mutable sqlite3 *m_db;
|
||||
mutable int m_db_err;
|
||||
mutable sqlite3_stmt *m_set_user_stmt;
|
||||
mutable sqlite3_stmt *m_get_user_stmt;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
inline void Store::Bind(sqlite3_stmt *stmt, int index, const std::optional<T> &opt) const {
|
||||
if (opt.has_value())
|
||||
Bind(stmt, index, *opt);
|
||||
else
|
||||
sqlite3_bind_null(stmt, index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void Store::Get(sqlite3_stmt *stmt, int index, std::optional<T> &out) const {
|
||||
if (sqlite3_column_type(stmt, index) == SQLITE_NULL)
|
||||
out = std::nullopt;
|
||||
else {
|
||||
T v;
|
||||
Get(stmt, index, v);
|
||||
out = std::optional<T>(v);
|
||||
}
|
||||
}
|
||||
|
@ -34,20 +34,20 @@ void from_json(const nlohmann::json &j, User &m) {
|
||||
}
|
||||
|
||||
void User::update_from_json(const nlohmann::json &j, User &m) {
|
||||
JS_ON("username", m.Username);
|
||||
JS_ON("discriminator", m.Discriminator);
|
||||
JS_ON("avatar", m.Avatar);
|
||||
JS_ON("bot", m.IsBot);
|
||||
JS_ON("system", m.IsSystem);
|
||||
JS_ON("mfa_enabled", m.IsMFAEnabled);
|
||||
JS_ON("locale", m.Locale);
|
||||
JS_ON("verified", m.IsVerified);
|
||||
JS_ON("email", m.Email);
|
||||
JS_ON("flags", m.Flags);
|
||||
JS_ON("premium_type", m.PremiumType);
|
||||
JS_ON("public_flags", m.PublicFlags);
|
||||
JS_ON("desktop", m.IsDesktop);
|
||||
JS_ON("mobile", m.IsMobile);
|
||||
JS_ON("nsfw_allowed", m.IsNSFWAllowed);
|
||||
JS_ON("phone", m.Phone);
|
||||
JS_RD("username", m.Username);
|
||||
JS_RD("discriminator", m.Discriminator);
|
||||
JS_RD("avatar", m.Avatar);
|
||||
JS_RD("bot", m.IsBot);
|
||||
JS_RD("system", m.IsSystem);
|
||||
JS_RD("mfa_enabled", m.IsMFAEnabled);
|
||||
JS_RD("locale", m.Locale);
|
||||
JS_RD("verified", m.IsVerified);
|
||||
JS_RD("email", m.Email);
|
||||
JS_RD("flags", m.Flags);
|
||||
JS_RD("premium_type", m.PremiumType);
|
||||
JS_RD("public_flags", m.PublicFlags);
|
||||
JS_RD("desktop", m.IsDesktop);
|
||||
JS_RD("mobile", m.IsMobile);
|
||||
JS_RD("nsfw_allowed", m.IsNSFWAllowed);
|
||||
JS_RD("phone", m.Phone);
|
||||
}
|
||||
|
@ -4,19 +4,19 @@
|
||||
#include <string>
|
||||
|
||||
struct User {
|
||||
Snowflake ID; //
|
||||
std::string Username; //
|
||||
std::string Discriminator; //
|
||||
std::string Avatar; // null
|
||||
bool IsBot = false; // opt
|
||||
bool IsSystem = false; // opt
|
||||
bool IsMFAEnabled = false; // opt
|
||||
std::string Locale; // opt
|
||||
bool IsVerified = false; // opt
|
||||
std::string Email; // opt, null
|
||||
int Flags = 0; // opt
|
||||
int PremiumType = 0; // opt, null (docs wrong)
|
||||
int PublicFlags = 0; // opt
|
||||
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<int> Flags;
|
||||
std::optional<int> PremiumType; // null
|
||||
std::optional<int> PublicFlags;
|
||||
|
||||
// undocumented (opt)
|
||||
bool IsDesktop = false; //
|
||||
|
10
util.hpp
10
util.hpp
@ -11,6 +11,16 @@
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace util {
|
||||
template<typename T>
|
||||
struct is_optional : ::std::false_type {};
|
||||
|
||||
template<typename T>
|
||||
struct is_optional<::std::optional<T>> : ::std::true_type {};
|
||||
}
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
|
Loading…
Reference in New Issue
Block a user