From 8e440d4928df10d51042c742f2382dc340cae833 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 29 Sep 2026 18:22:32 -0500 Subject: [PATCH] feat(dashboard): reading whispers is its own permission chat_dms (GM 8) reads whispers; chat_private is now team and guild chat only. Adds chat_flag and chat_flag_review (GM 3). Both can be granted per account or character with the permission grants. Co-Authored-By: Claude Opus 5.5 --- dCommon/Permissions.cpp | 5 ++++- dDashboardServer/routes/CharacterTools.cpp | 1 + dDashboardServer/routes/ModerationTools.cpp | 1 + dDashboardServer/routes/ModeratorHelper.cpp | 4 ++++ dDashboardServer/routes/PrometheusMetrics.cpp | 1 + 5 files changed, 11 insertions(+), 1 deletion(-) diff --git a/dCommon/Permissions.cpp b/dCommon/Permissions.cpp index d994e8062..657ca2652 100644 --- a/dCommon/Permissions.cpp +++ b/dCommon/Permissions.cpp @@ -57,7 +57,10 @@ namespace { { "moderate_pet_names", "Moderation", "Moderate pet names", "Approve and reject pet names", 5 }, { "guilds_manage", "Moderation", "Guilds", "See guilds, their members and history; approve, reject and change guild names, remove members and disband guilds", 5 }, { "chat_view", "Moderation", "Read chat", "The chat log: what players said in zones, and what the chat filter stopped", 3 }, - { "chat_private", "Moderation", "Read private chat", "Whispers, team and guild chat in the chat log", 8 }, + { "chat_private", "Moderation", "Read team and guild chat", "Team and guild chat in the chat log, and the team and guild chat histories", 8 }, + { "chat_dms", "Moderation", "Read whispers", "Whispers between players: in the chat log and as conversations on a character (every conversation opened is audited)", 8 }, + { "chat_flag", "Moderation", "Flag chat", "Flag chat messages for review and see the Chat Flags queue (a flag only shows chat the viewer may read)", 3 }, + { "chat_flag_review", "Moderation", "Review chat flags", "Mark chat flags actioned or dismissed, and add notes to them", 3 }, { "chat_filter_manage", "Moderation", "Chat filter", "See and change the words the chat filter allows and stops, and apply them in running worlds", 5 }, { "player_reports_view", "Moderation", "View player reports", "Read what players reported from the game's Report Abuse window", 2 }, { "player_reports_manage", "Moderation", "Handle player reports", "Act on or dismiss player reports (a strike also needs strikes_give)", 3 }, diff --git a/dDashboardServer/routes/CharacterTools.cpp b/dDashboardServer/routes/CharacterTools.cpp index 85f352537..53225189c 100644 --- a/dDashboardServer/routes/CharacterTools.cpp +++ b/dDashboardServer/routes/CharacterTools.cpp @@ -130,6 +130,7 @@ namespace { q.characterId = characterId; q.accountId = accountId; q.includePrivate = Can(context, "chat_private"); + q.includeWhispers = Can(context, "chat_dms"); q.newestFirst = true; q.limit = 100; nlohmann::json rows = nlohmann::json::array(); diff --git a/dDashboardServer/routes/ModerationTools.cpp b/dDashboardServer/routes/ModerationTools.cpp index 09ce2496a..359f4e802 100644 --- a/dDashboardServer/routes/ModerationTools.cpp +++ b/dDashboardServer/routes/ModerationTools.cpp @@ -383,6 +383,7 @@ namespace { IChatLog::ChatQuery query; query.search = *word; query.includePrivate = Can(context, "chat_private"); + query.includeWhispers = Can(context, "chat_dms"); query.blockedOnly = allowed; query.newestFirst = true; query.limit = CHECK_MESSAGES; diff --git a/dDashboardServer/routes/ModeratorHelper.cpp b/dDashboardServer/routes/ModeratorHelper.cpp index 231d4443f..32e99e983 100644 --- a/dDashboardServer/routes/ModeratorHelper.cpp +++ b/dDashboardServer/routes/ModeratorHelper.cpp @@ -216,6 +216,8 @@ namespace { query.characterId = character; query.since = center - window; query.includePrivate = Can(context, "chat_private"); + query.includeWhispers = Can(context, "chat_dms"); + query.includeWhispers = Can(context, "chat_dms"); query.limit = 200; for (auto& m : Database::Get()->GetChatMessages(query)) { if (m.time <= center + window) found[m.id] = std::move(m); @@ -317,6 +319,7 @@ namespace { query.afterId = id - 1; query.limit = 1; query.includePrivate = Can(context, "chat_private"); + query.includeWhispers = Can(context, "chat_dms"); const auto found = Database::Get()->GetChatMessages(query); if (found.empty() || found.front().id != id) return std::nullopt; return found.front(); @@ -354,6 +357,7 @@ namespace { IChatLog::ChatQuery query; query.characterId = characterId; query.includePrivate = Can(context, "chat_private"); + query.includeWhispers = Can(context, "chat_dms"); query.newestFirst = true; query.limit = MAX_CHAT_LINES; auto messages = Database::Get()->GetChatMessages(query); diff --git a/dDashboardServer/routes/PrometheusMetrics.cpp b/dDashboardServer/routes/PrometheusMetrics.cpp index 47a9f73cd..e1c1b372f 100644 --- a/dDashboardServer/routes/PrometheusMetrics.cpp +++ b/dDashboardServer/routes/PrometheusMetrics.cpp @@ -157,6 +157,7 @@ namespace { } IChatLog::ChatQuery q; q.includePrivate = true; + q.includeWhispers = true; q.limit = CHAT_PAGE; for (uint32_t page = 0; page < CHAT_PAGES && g_ChatSeen < newest; page++) { q.afterId = g_ChatSeen;