mirror of
https://gitlab.com/niansa/dpplogger.git
synced 2025-03-06 20:48:29 +01:00
Added voice channel and presence logging
This commit is contained in:
parent
c0509932f4
commit
a45ee4f1d9
1 changed files with 84 additions and 19 deletions
103
main.cpp
103
main.cpp
|
@ -44,7 +44,7 @@ public:
|
||||||
StoreResult store(Discord::Snowflake id, const Json::Value& object) {
|
StoreResult store(Discord::Snowflake id, const Json::Value& object) {
|
||||||
logger.log(Loglevel::verbose, "Stored "+id.str()+" in cache");
|
logger.log(Loglevel::verbose, "Stored "+id.str()+" in cache");
|
||||||
auto cache_hit = cache.find(id);
|
auto cache_hit = cache.find(id);
|
||||||
if (cache_hit != cache.end()) {
|
if (cache_hit != cache.end()) [[likely]] {
|
||||||
bool changed = cache_hit->second != object;
|
bool changed = cache_hit->second != object;
|
||||||
if (changed) {
|
if (changed) {
|
||||||
cache_hit->second = object;
|
cache_hit->second = object;
|
||||||
|
@ -57,7 +57,7 @@ public:
|
||||||
StoreResult store(Discord::Snowflake id, Json::Value&& object) {
|
StoreResult store(Discord::Snowflake id, Json::Value&& object) {
|
||||||
logger.log(Loglevel::verbose, "Moved "+id.str()+" into cache");
|
logger.log(Loglevel::verbose, "Moved "+id.str()+" into cache");
|
||||||
auto cache_hit = cache.find(id);
|
auto cache_hit = cache.find(id);
|
||||||
if (cache_hit != cache.end()) {
|
if (cache_hit != cache.end()) [[likely]] {
|
||||||
bool changed = cache_hit->second != object;
|
bool changed = cache_hit->second != object;
|
||||||
if (changed) {
|
if (changed) {
|
||||||
cache_hit->second = std::move(object);
|
cache_hit->second = std::move(object);
|
||||||
|
@ -77,7 +77,7 @@ public:
|
||||||
}
|
}
|
||||||
const Json::Value& fetch(Discord::Snowflake id) const {
|
const Json::Value& fetch(Discord::Snowflake id) const {
|
||||||
const auto& entry = cache.find(id);
|
const auto& entry = cache.find(id);
|
||||||
if (entry == cache.end()) {
|
if (entry == cache.end()) [[unlikely]] {
|
||||||
throw Miss();
|
throw Miss();
|
||||||
}
|
}
|
||||||
return entry->second;
|
return entry->second;
|
||||||
|
@ -97,6 +97,16 @@ static inline std::optional<std::string> GetJSONAsOptionalString(const Json::Val
|
||||||
static inline std::optional<int> GetJSONAsOptionalInt(const Json::Value& data) {
|
static inline std::optional<int> GetJSONAsOptionalInt(const Json::Value& data) {
|
||||||
return data.isString()?data.asInt():std::optional<int>();
|
return data.isString()?data.asInt():std::optional<int>();
|
||||||
}
|
}
|
||||||
|
static inline std::optional<bool> GetJSONAsOptionalBool(const Json::Value& data) {
|
||||||
|
return data.isBool()?data.asBool():std::optional<bool>();
|
||||||
|
}
|
||||||
|
static inline std::string GetJSONAsJSONData(const Json::Value& data) {
|
||||||
|
return Json::writeString(Json::StreamWriterBuilder(), data);
|
||||||
|
}
|
||||||
|
static inline std::optional<std::string> GetJSONAsOptionalJSONData(const Json::Value& data) {
|
||||||
|
return ((!data.empty()&&!data.isNull())?GetJSONAsJSONData(data):std::optional<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
static void reverseString(std::string& str) {
|
static void reverseString(std::string& str) {
|
||||||
int len = str.length();
|
int len = str.length();
|
||||||
for (int i = 0; i < len / 2; i++) {
|
for (int i = 0; i < len / 2; i++) {
|
||||||
|
@ -160,12 +170,12 @@ public:
|
||||||
" has_nitro INTEGER NOT NULL,"
|
" has_nitro INTEGER NOT NULL,"
|
||||||
" is_bot INTEGER NOT NULL"
|
" is_bot INTEGER NOT NULL"
|
||||||
");";
|
");";
|
||||||
db << "CREATE TABLE IF NOT EXISTS user_statuses ("
|
db << "CREATE TABLE IF NOT EXISTS user_presences ("
|
||||||
" user_id TEXT NOT NULL,"
|
" user_id TEXT NOT NULL,"
|
||||||
" is_initial INTEGER DEFAULT 1 NOT NULL,"
|
" is_initial INTEGER DEFAULT 1 NOT NULL,"
|
||||||
" timestamp TEXT NOT NULL,"
|
" timestamp TEXT NOT NULL,"
|
||||||
" status TEXT," // JSON array or null if not updated: [int:online/dnd/afk/offline enum, ?:emoji (null/empty if none), string:text (empty if none)]
|
" status TEXT," // JSON array or null if not updated: [string:online/dnd/afk/offline, string:device]
|
||||||
" presence TEXT" // Discord JSON presence object
|
" activities TEXT" // Discord JSON Activity object list
|
||||||
");";
|
");";
|
||||||
db << "CREATE TABLE IF NOT EXISTS members ("
|
db << "CREATE TABLE IF NOT EXISTS members ("
|
||||||
" user_id TEXT NOT NULL,"
|
" user_id TEXT NOT NULL,"
|
||||||
|
@ -177,15 +187,15 @@ public:
|
||||||
" in_guild INTEGER NOT NULL"
|
" in_guild INTEGER NOT NULL"
|
||||||
");";
|
");";
|
||||||
db << "CREATE TABLE IF NOT EXISTS member_voice_connections ("
|
db << "CREATE TABLE IF NOT EXISTS member_voice_connections ("
|
||||||
" channel_id TEXT NOT NULL,"
|
" channel_id TEXT,"
|
||||||
" user_id TEXT NOT NULL,"
|
" user_id TEXT NOT NULL,"
|
||||||
" is_initial INTEGER DEFAULT 1 NOT NULL,"
|
|
||||||
" timestamp TEXT NOT NULL,"
|
" timestamp TEXT NOT NULL,"
|
||||||
" voice_channel_id TEXT,"
|
" self_mute INTEGER NOT NULL,"
|
||||||
" self_muted INTEGER NOT NULL,"
|
" self_deaf INTEGER NOT NULL,"
|
||||||
" self_deafened INTEGER NOT NULL,"
|
" self_video INTEGER NOT NULL,"
|
||||||
" server_muted INTEGER NOT NULL,"
|
" self_stream INTEGER NOT NULL,"
|
||||||
" server_deafened INTEGER NOT NULL"
|
" server_mute INTEGER NOT NULL,"
|
||||||
|
" server_deaf INTEGER NOT NULL"
|
||||||
");";
|
");";
|
||||||
db << "CREATE TABLE IF NOT EXISTS channels ("
|
db << "CREATE TABLE IF NOT EXISTS channels ("
|
||||||
" id TEXT NOT NULL,"
|
" id TEXT NOT NULL,"
|
||||||
|
@ -333,7 +343,7 @@ protected:
|
||||||
|
|
||||||
// Insert member
|
// Insert member
|
||||||
if (changed) {
|
if (changed) {
|
||||||
insertMemberUpdate(data, user_id, guild_id, !cache.has(data["id"]));
|
insertMemberUpdate(data, user_id, guild_id, changed == Cache::StoreResult::created);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Cache::StoreResult cacheStoreMember(const Json::Value& data, Discord::Snowflake user_id, Discord::Snowflake guild_id) {
|
Cache::StoreResult cacheStoreMember(const Json::Value& data, Discord::Snowflake user_id, Discord::Snowflake guild_id) {
|
||||||
|
@ -359,8 +369,8 @@ protected:
|
||||||
" VALUES (?, ?, ?, ?, ?, ?, ?, ? );"
|
" VALUES (?, ?, ?, ?, ?, ?, ?, ? );"
|
||||||
<< data["id"].asString() << is_initial << std::to_string(time(nullptr))
|
<< data["id"].asString() << is_initial << std::to_string(time(nullptr))
|
||||||
<< data["username"].asString()+'#'+data["discriminator"].asString() << GetJSONAsOptionalString(data["avatar"])
|
<< data["username"].asString()+'#'+data["discriminator"].asString() << GetJSONAsOptionalString(data["avatar"])
|
||||||
<< GetJSONAsOptionalString(data["bio"]) << GetJSONAsOptionalInt(data["has_nitro"]).value_or(0)
|
<< GetJSONAsOptionalString(data["bio"]) << GetJSONAsOptionalInt(data["premium_type"]).value_or(0)
|
||||||
<< GetJSONAsOptionalInt(data["bot"]).value_or(false);
|
<< GetJSONAsOptionalBool(data["bot"]).value_or(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -390,7 +400,7 @@ protected:
|
||||||
db << "INSERT INTO message_contents (message_id, is_initial, timestamp, content, embeds)"
|
db << "INSERT INTO message_contents (message_id, is_initial, timestamp, content, embeds)"
|
||||||
" VALUES (?, ?, ?, ?, ? );"
|
" VALUES (?, ?, ?, ?, ? );"
|
||||||
<< data["id"].asString() << is_initial << std::to_string(time(nullptr)) << data["content"].asString()
|
<< data["id"].asString() << is_initial << std::to_string(time(nullptr)) << data["content"].asString()
|
||||||
<< ((embeds.isArray()&&!embeds.empty())?Json::writeString(Json::StreamWriterBuilder(), embeds):std::optional<std::string>());
|
<< GetJSONAsOptionalJSONData(embeds);
|
||||||
}
|
}
|
||||||
void insertMessageUpdate(const Json::Value& data, bool is_initial) {
|
void insertMessageUpdate(const Json::Value& data, bool is_initial) {
|
||||||
bool has_content = !data["content"].asString().empty() || data["embeds"].isArray();
|
bool has_content = !data["content"].asString().empty() || data["embeds"].isArray();
|
||||||
|
@ -431,11 +441,50 @@ protected:
|
||||||
insertChannelUpdate(cached_data, false, true);
|
insertChannelUpdate(cached_data, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Member voice connections
|
||||||
|
*/
|
||||||
|
void updateMemberVoiceConnection(Json::Value&& data) {
|
||||||
|
// Insert voice connection update
|
||||||
|
insertMemberVoiceConnectionUpdate(data);
|
||||||
|
|
||||||
|
// Update member
|
||||||
|
updateMember(std::move(data["member"]), data["guild_id"]);
|
||||||
|
}
|
||||||
|
void insertMemberVoiceConnectionUpdate(const Json::Value& data) {
|
||||||
|
db << "INSERT INTO member_voice_connections (channel_id, user_id, timestamp, self_mute, self_deaf, self_video, self_stream, server_mute, server_deaf)"
|
||||||
|
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ? );"
|
||||||
|
<< data["channel_id"].asString() << data["user_id"].asString() << std::to_string(time(nullptr)) << data["self_mute"].asBool()
|
||||||
|
<< data["self_deaf"].asBool() << data["self_video"].asBool() << GetJSONAsOptionalBool(data["self_stream"]).value_or(false)
|
||||||
|
<< data["mute"].asBool() << data["deaf"].asBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User presences
|
||||||
|
*/
|
||||||
|
void insertUserPresenceUpdate(const Json::Value& data, bool is_initial) {
|
||||||
|
// Build status
|
||||||
|
std::string status_str;
|
||||||
|
{
|
||||||
|
const auto& client_status = data["client_status"];
|
||||||
|
if (client_status.isArray() && !client_status.empty()) {
|
||||||
|
const auto device = client_status.getMemberNames()[0];
|
||||||
|
status_str = "[\""+client_status[device].asString()+"\", \""+device+"\"]";
|
||||||
|
} else {
|
||||||
|
status_str = R"(["offline", "none"])";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
db << "INSERT INTO user_presences (user_id, is_initial, timestamp, status, activities)"
|
||||||
|
" VALUES (?, ?, ?, ?, ? );"
|
||||||
|
<< data["user"]["id"].asString() << is_initial << std::to_string(time(nullptr)) << status_str << GetJSONAsOptionalJSONData(data["activities"]);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Intent handler
|
* Intent handler
|
||||||
*/
|
*/
|
||||||
virtual boost::asio::awaitable<void> intentHandler(std::string intent, Json::Value data) override {
|
virtual boost::asio::awaitable<void> intentHandler(std::string intent, Json::Value data) override {
|
||||||
std::ofstream(intent+".json") << data;
|
|
||||||
if (intent == "READY") [[unlikely]] {
|
if (intent == "READY") [[unlikely]] {
|
||||||
// Get own user
|
// Get own user
|
||||||
const auto& user = cache.store(std::move(data["user"])).data;
|
const auto& user = cache.store(std::move(data["user"])).data;
|
||||||
|
@ -444,7 +493,7 @@ protected:
|
||||||
co_await fetchAllGuilds(std::move(data["guilds"]));
|
co_await fetchAllGuilds(std::move(data["guilds"]));
|
||||||
// Get users
|
// Get users
|
||||||
auto& users = data["users"];
|
auto& users = data["users"];
|
||||||
if (users.isArray()) {
|
if (users.isArray()) [[likely]] {
|
||||||
for (const auto& user_data : users) {
|
for (const auto& user_data : users) {
|
||||||
insertUserUpdate(user_data, true);
|
insertUserUpdate(user_data, true);
|
||||||
cache.store(std::move(user_data));
|
cache.store(std::move(user_data));
|
||||||
|
@ -487,6 +536,22 @@ protected:
|
||||||
const auto& user_data = cache.store(data["user"]).data;
|
const auto& user_data = cache.store(data["user"]).data;
|
||||||
insertMemberDelete(user_data["id"], data["guild_id"]);
|
insertMemberDelete(user_data["id"], data["guild_id"]);
|
||||||
}
|
}
|
||||||
|
else if (intent == "VOICE_STATE_UPDATE") {
|
||||||
|
updateMemberVoiceConnection(std::move(data));
|
||||||
|
}
|
||||||
|
else if (intent == "PRESENCE_UPDATE") {
|
||||||
|
auto changed = cache.store(Discord::Snowflake(data["user_id"]).uint()|0x04e2e7ce00000000, data).changed;
|
||||||
|
insertUserPresenceUpdate(data, changed==Cache::StoreResult::created);
|
||||||
|
}
|
||||||
|
else if (intent == "GUILD_BAN_ADD") [[unlikely]] {
|
||||||
|
// Should we log bans? Let's just use it as a source of user data
|
||||||
|
const auto& user_data = data["user"];
|
||||||
|
auto changed = cache.store(user_data).changed;
|
||||||
|
if (changed) [[unlikely]] {
|
||||||
|
insertUserUpdate(user_data, (changed&Cache::StoreResult::created)?true:false);
|
||||||
|
}
|
||||||
|
//TODO: Should we do insertMemberDelete here?
|
||||||
|
}
|
||||||
co_return;
|
co_return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue