mirror of
https://github.com/DorkSquadRR/DorkNet.git
synced 2026-10-25 15:08:47 -07:00
Refactor moderation notifications: replace NotifyAsync with KickPlayerAsync for player kicks in InGameModerationController and AdminController; update NotificationService to streamline moderation kick payloads.
This commit is contained in:
@@ -136,12 +136,9 @@ public class InGameModerationController(
|
|||||||
foreach (var target in ids)
|
foreach (var target in ids)
|
||||||
{
|
{
|
||||||
logger.LogInformation("[mod] instant-kick by {Caller}: target={Target}", caller, target);
|
logger.LogInformation("[mod] instant-kick by {Caller}: target={Target}", caller, target);
|
||||||
// Push a SignalR ModerationKick notification — the watch's
|
// Push the ModerationKick shape the 2020.12 client expects:
|
||||||
// PlayerReporting.OnModerationKick handler boots the player
|
// Msg.Reason must be a ModerationBlockDetail object.
|
||||||
// back to their dorm.
|
await notifications.KickPlayerAsync(target, "Instant kick by moderator.");
|
||||||
await notifications.NotifyAsync(
|
|
||||||
target,
|
|
||||||
Models.Notification.PushNotificationId.ModerationKick);
|
|
||||||
}
|
}
|
||||||
return Ok(new { success = true, error = "" });
|
return Ok(new { success = true, error = "" });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ public class AdminController(
|
|||||||
await LogAsync("ban_player", "player", id, body.Reason ?? "");
|
await LogAsync("ban_player", "player", id, body.Reason ?? "");
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
|
|
||||||
await notifications.NotifyAsync(id, PushNotificationId.ModerationKick,
|
await notifications.KickPlayerAsync(id, body.Reason ?? "You have been banned.");
|
||||||
new { Reason = body.Reason ?? "", Until = player.BannedUntil });
|
playerPresence.Clear(id);
|
||||||
|
|
||||||
return Ok(new { player.Id, player.BannedUntil });
|
return Ok(new { player.Id, player.BannedUntil });
|
||||||
}
|
}
|
||||||
@@ -1269,7 +1269,7 @@ public class AdminController(
|
|||||||
{
|
{
|
||||||
var r = playerPresence.GetRoom(pid);
|
var r = playerPresence.GetRoom(pid);
|
||||||
if (r is null || r.RoomId != id || r.RoomInstanceId != instanceId) continue;
|
if (r is null || r.RoomId != id || r.RoomInstanceId != instanceId) continue;
|
||||||
await notifications.NotifyAsync(pid, PushNotificationId.ModerationKick, new { Reason = reason });
|
await notifications.KickPlayerAsync(pid, reason);
|
||||||
playerPresence.Clear(pid);
|
playerPresence.Clear(pid);
|
||||||
kicked.Add(pid);
|
kicked.Add(pid);
|
||||||
}
|
}
|
||||||
@@ -1616,8 +1616,11 @@ public class AdminController(
|
|||||||
[HttpPost("players/{id:long}/kick")]
|
[HttpPost("players/{id:long}/kick")]
|
||||||
public async Task<ActionResult> KickPlayer(long id, [FromBody] KickRequest? body)
|
public async Task<ActionResult> KickPlayer(long id, [FromBody] KickRequest? body)
|
||||||
{
|
{
|
||||||
await notifications.NotifyAsync(id, PushNotificationId.ModerationKick,
|
var reason = string.IsNullOrWhiteSpace(body?.Reason)
|
||||||
new { Reason = body?.Reason ?? "" });
|
? "Kicked by an admin."
|
||||||
|
: body!.Reason!.Trim();
|
||||||
|
await notifications.KickPlayerAsync(id, reason);
|
||||||
|
playerPresence.Clear(id);
|
||||||
await LogAsync("kick_player", "player", id, body?.Reason ?? "");
|
await LogAsync("kick_player", "player", id, body?.Reason ?? "");
|
||||||
await db.SaveChangesAsync();
|
await db.SaveChangesAsync();
|
||||||
return Ok(new { kicked = id });
|
return Ok(new { kicked = id });
|
||||||
|
|||||||
@@ -329,14 +329,10 @@ public class NotificationService(
|
|||||||
/// <paramref name="playerId"/> to disconnect via the watch's
|
/// <paramref name="playerId"/> to disconnect via the watch's
|
||||||
/// ModerationKick flow. Wire path verified against decompilation:
|
/// ModerationKick flow. Wire path verified against decompilation:
|
||||||
/// <c>PushNotificationId.ModerationKick = 22</c> is registered
|
/// <c>PushNotificationId.ModerationKick = 22</c> is registered
|
||||||
/// at <c>PlayerReporting.txt:244</c> via the enum-overload, so
|
/// by <c>LCCEEFHOBEN.OFFLOPLJBBG</c> via the enum overload.
|
||||||
/// the wire name resolves to <c>"ModerationKick"</c>.
|
/// <c>LCCEEFHOBEN.KEIAFMLIMFA(Dictionary)</c> reads
|
||||||
/// <c>OnModerationKick(Dictionary)</c> at
|
/// <c>Msg.Reason</c> as a ModerationBlockDetail object, then
|
||||||
/// <c>PlayerReporting.txt:1857</c> shows a confirmation dialog
|
/// calls <c>PODFHPNOHGB</c> / <c>BootLocalPlayerToDormRoom</c>.
|
||||||
/// then calls <c>LocalModerationKickSelf</c>
|
|
||||||
/// (<c>PlayerReporting.txt:2647</c>), which queues the
|
|
||||||
/// disconnect through <c>Core.ShutdownForLogout</c> — the same
|
|
||||||
/// graceful path the watch uses for real moderator kicks.
|
|
||||||
///
|
///
|
||||||
/// Used by <see cref="Controllers.Match.MatchPlayerController.PlayerLogin"/>
|
/// Used by <see cref="Controllers.Match.MatchPlayerController.PlayerLogin"/>
|
||||||
/// to enforce single-session-per-account: when a second login on
|
/// to enforce single-session-per-account: when a second login on
|
||||||
@@ -347,18 +343,11 @@ public class NotificationService(
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Task KickStaleSession(long playerId, string reason) =>
|
public Task KickStaleSession(long playerId, string reason) =>
|
||||||
NotifyAsync(playerId, PushNotificationId.ModerationKick,
|
NotifyAsync(playerId, PushNotificationId.ModerationKick,
|
||||||
new
|
ModerationKickPayload(reason, isBan: false, source: "session_replaced"));
|
||||||
{
|
|
||||||
// Field names follow PlayerReporting's wire convention
|
public Task KickPlayerAsync(long playerId, string reason, bool isBan = false) =>
|
||||||
// for moderation-related dialogs; the watch's
|
NotifyAsync(playerId, PushNotificationId.ModerationKick,
|
||||||
// OnModerationKick deserializes via Util.GetKeyOrDefault
|
ModerationKickPayload(reason, isBan, source: "admin_kick"));
|
||||||
// so missing fields are tolerated, but populating them
|
|
||||||
// makes the dialog read sensibly.
|
|
||||||
Reason = reason,
|
|
||||||
DisplayReason = reason,
|
|
||||||
BannedByPlayerId = 1, // Coach
|
|
||||||
Source = "session_replaced",
|
|
||||||
});
|
|
||||||
|
|
||||||
/// <summary>Push a ModerationKick to a specific set of SignalR
|
/// <summary>Push a ModerationKick to a specific set of SignalR
|
||||||
/// connection ids — used by <see cref="DorkNet.Server.Hubs.NotifyHub.OnConnectedAsync"/>
|
/// connection ids — used by <see cref="DorkNet.Server.Hubs.NotifyHub.OnConnectedAsync"/>
|
||||||
@@ -371,16 +360,38 @@ public class NotificationService(
|
|||||||
public Task KickConnectionsAsync(IReadOnlyCollection<string> connectionIds, string reason)
|
public Task KickConnectionsAsync(IReadOnlyCollection<string> connectionIds, string reason)
|
||||||
{
|
{
|
||||||
if (connectionIds.Count == 0) return Task.CompletedTask;
|
if (connectionIds.Count == 0) return Task.CompletedTask;
|
||||||
var payload = Serialize(PushNotificationId.ModerationKick, new
|
var payload = Serialize(PushNotificationId.ModerationKick,
|
||||||
{
|
ModerationKickPayload(reason, isBan: false, source: "session_replaced"));
|
||||||
Reason = reason,
|
|
||||||
DisplayReason = reason,
|
|
||||||
BannedByPlayerId = 1,
|
|
||||||
Source = "session_replaced",
|
|
||||||
});
|
|
||||||
return hub.Clients.Clients(connectionIds).SendAsync(HubMethod, payload);
|
return hub.Clients.Clients(connectionIds).SendAsync(HubMethod, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static object ModerationKickPayload(string reason, bool isBan, string source)
|
||||||
|
{
|
||||||
|
var message = string.IsNullOrWhiteSpace(reason)
|
||||||
|
? "Kicked to Dormroom"
|
||||||
|
: reason.Trim();
|
||||||
|
|
||||||
|
var blockDetail = new
|
||||||
|
{
|
||||||
|
ReportCategory = 10, // PlayerReporting.ReportCategory.VoteKick
|
||||||
|
Duration = 0,
|
||||||
|
GameSessionId = 0L,
|
||||||
|
Message = message,
|
||||||
|
IsHostKick = true,
|
||||||
|
PlayerIdReporter = 1,
|
||||||
|
IsBan = isBan,
|
||||||
|
VoteKickReason = message,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
Reason = blockDetail,
|
||||||
|
DisplayReason = message,
|
||||||
|
BannedByPlayerId = 1,
|
||||||
|
Source = source,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Notify a private-instance owner that someone tried to
|
/// <summary>Notify a private-instance owner that someone tried to
|
||||||
/// follow them in via "Go to Player" but was rejected for not
|
/// follow them in via "Go to Player" but was rejected for not
|
||||||
/// being on the invite list. Reuses the MessageReceived channel
|
/// being on the invite list. Reuses the MessageReceived channel
|
||||||
|
|||||||
Reference in New Issue
Block a user