| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343 |
- using System.Text;
- using System.Threading.Tasks;
- using Content.Server.Administration.Managers;
- using Content.Server.Database;
- using Content.Server.EUI;
- using Content.Server.GameTicking;
- using Content.Shared.Administration;
- using Content.Shared.Administration.Notes;
- using Content.Shared.CCVar;
- using Content.Shared.Database;
- using Content.Shared.Players.PlayTimeTracking;
- using Robust.Shared.Configuration;
- using Robust.Shared.Network;
- using Robust.Shared.Player;
- namespace Content.Server.Administration.Notes;
- public sealed class AdminNotesManager : IAdminNotesManager, IPostInjectInit
- {
- [Dependency] private readonly IAdminManager _admins = default!;
- [Dependency] private readonly IServerDbManager _db = default!;
- [Dependency] private readonly ILogManager _logManager = default!;
- [Dependency] private readonly EuiManager _euis = default!;
- [Dependency] private readonly IEntitySystemManager _systems = default!;
- [Dependency] private readonly IConfigurationManager _config = default!;
- public const string SawmillId = "admin.notes";
- public event Action<SharedAdminNote>? NoteAdded;
- public event Action<SharedAdminNote>? NoteModified;
- public event Action<SharedAdminNote>? NoteDeleted;
- private ISawmill _sawmill = default!;
- public bool CanCreate(ICommonSession admin)
- {
- return CanEdit(admin);
- }
- public bool CanDelete(ICommonSession admin)
- {
- return CanEdit(admin);
- }
- public bool CanEdit(ICommonSession admin)
- {
- return _admins.HasAdminFlag(admin, AdminFlags.EditNotes);
- }
- public bool CanView(ICommonSession admin)
- {
- return _admins.HasAdminFlag(admin, AdminFlags.ViewNotes);
- }
- public async Task OpenEui(ICommonSession admin, Guid notedPlayer)
- {
- var ui = new AdminNotesEui();
- _euis.OpenEui(ui, admin);
- await ui.ChangeNotedPlayer(notedPlayer);
- }
- public async Task OpenUserNotesEui(ICommonSession player)
- {
- var ui = new UserNotesEui();
- _euis.OpenEui(ui, player);
- await ui.UpdateNotes();
- }
- public async Task AddAdminRemark(ICommonSession createdBy, Guid player, NoteType type, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
- {
- message = message.Trim();
- // There's a foreign key constraint in place here. If there's no player record, it will fail.
- // Not like there's much use in adding notes on accounts that have never connected.
- // You can still ban them just fine, which is why we should allow admins to view their bans with the notes panel
- if (await _db.GetPlayerRecordByUserId((NetUserId) player) is null)
- return;
- var sb = new StringBuilder($"{createdBy.Name} added a");
- if (secret && type == NoteType.Note)
- {
- sb.Append(" secret");
- }
- sb.Append($" {type} with message {message}");
- switch (type)
- {
- case NoteType.Note:
- sb.Append($" with {severity} severity");
- break;
- case NoteType.Message:
- severity = null;
- secret = false;
- break;
- case NoteType.Watchlist:
- severity = null;
- secret = true;
- break;
- case NoteType.ServerBan:
- case NoteType.RoleBan:
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
- }
- if (expiryTime is not null)
- {
- sb.Append($" which expires on {expiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
- }
- _sawmill.Info(sb.ToString());
- _systems.TryGetEntitySystem(out GameTicker? ticker);
- int? roundId = ticker == null || ticker.RoundId == 0 ? null : ticker.RoundId;
- var serverName = _config.GetCVar(CCVars.AdminLogsServerName); // This could probably be done another way, but this is fine. For displaying only.
- var createdAt = DateTime.UtcNow;
- var playtime = (await _db.GetPlayTimes(player)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall)?.TimeSpent ?? TimeSpan.Zero;
- int noteId;
- bool? seen = null;
- switch (type)
- {
- case NoteType.Note:
- if (severity is null)
- throw new ArgumentException("Severity cannot be null for a note", nameof(severity));
- noteId = await _db.AddAdminNote(roundId, player, playtime, message, severity.Value, secret, createdBy.UserId, createdAt, expiryTime);
- break;
- case NoteType.Watchlist:
- secret = true;
- noteId = await _db.AddAdminWatchlist(roundId, player, playtime, message, createdBy.UserId, createdAt, expiryTime);
- break;
- case NoteType.Message:
- noteId = await _db.AddAdminMessage(roundId, player, playtime, message, createdBy.UserId, createdAt, expiryTime);
- seen = false;
- break;
- case NoteType.ServerBan: // Add bans using the ban panel, not note edit
- case NoteType.RoleBan:
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
- }
- var note = new SharedAdminNote(
- noteId,
- (NetUserId) player,
- roundId,
- serverName,
- playtime,
- type,
- message,
- severity,
- secret,
- createdBy.Name,
- createdBy.Name,
- createdAt,
- createdAt,
- expiryTime,
- null,
- null,
- null,
- seen
- );
- NoteAdded?.Invoke(note);
- }
- private async Task<SharedAdminNote?> GetAdminRemark(int id, NoteType type)
- {
- return type switch
- {
- NoteType.Note => (await _db.GetAdminNote(id))?.ToShared(),
- NoteType.Watchlist => (await _db.GetAdminWatchlist(id))?.ToShared(),
- NoteType.Message => (await _db.GetAdminMessage(id))?.ToShared(),
- NoteType.ServerBan => (await _db.GetServerBanAsNoteAsync(id))?.ToShared(),
- NoteType.RoleBan => (await _db.GetServerRoleBanAsNoteAsync(id))?.ToShared(),
- _ => throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type")
- };
- }
- public async Task DeleteAdminRemark(int noteId, NoteType type, ICommonSession deletedBy)
- {
- var note = await GetAdminRemark(noteId, type);
- if (note == null)
- {
- _sawmill.Warning($"Player {deletedBy.Name} has tried to delete non-existent {type} {noteId}");
- return;
- }
- var deletedAt = DateTime.UtcNow;
- switch (type)
- {
- case NoteType.Note:
- await _db.DeleteAdminNote(noteId, deletedBy.UserId, deletedAt);
- break;
- case NoteType.Watchlist:
- await _db.DeleteAdminWatchlist(noteId, deletedBy.UserId, deletedAt);
- break;
- case NoteType.Message:
- await _db.DeleteAdminMessage(noteId, deletedBy.UserId, deletedAt);
- break;
- case NoteType.ServerBan:
- await _db.HideServerBanFromNotes(noteId, deletedBy.UserId, deletedAt);
- break;
- case NoteType.RoleBan:
- await _db.HideServerRoleBanFromNotes(noteId, deletedBy.UserId, deletedAt);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
- }
- _sawmill.Info($"{deletedBy.Name} has deleted {type} {noteId}");
- NoteDeleted?.Invoke(note);
- }
- public async Task ModifyAdminRemark(int noteId, NoteType type, ICommonSession editedBy, string message, NoteSeverity? severity, bool secret, DateTime? expiryTime)
- {
- message = message.Trim();
- var note = await GetAdminRemark(noteId, type);
- // If the note doesn't exist or is the same, we skip updating it
- if (note == null ||
- note.Message == message &&
- note.NoteSeverity == severity &&
- note.Secret == secret &&
- note.ExpiryTime == expiryTime)
- {
- return;
- }
- var sb = new StringBuilder($"{editedBy.Name} has modified {type} {noteId}");
- if (note.Message != message)
- {
- sb.Append($", modified message from {note.Message} to {message}");
- }
- if (note.Secret != secret)
- {
- sb.Append($", made it {(secret ? "secret" : "visible")}");
- }
- if (note.NoteSeverity != severity)
- {
- sb.Append($", updated the severity from {note.NoteSeverity} to {severity}");
- }
- if (note.ExpiryTime != expiryTime)
- {
- sb.Append(", updated the expiry time from ");
- if (note.ExpiryTime is null)
- sb.Append("never");
- else
- sb.Append($"{note.ExpiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
- sb.Append(" to ");
- if (expiryTime is null)
- sb.Append("never");
- else
- sb.Append($"{expiryTime.Value.ToUniversalTime(): yyyy-MM-dd HH:mm:ss} UTC");
- }
- _sawmill.Info(sb.ToString());
- var editedAt = DateTime.UtcNow;
- switch (type)
- {
- case NoteType.Note:
- if (severity is null)
- throw new ArgumentException("Severity cannot be null for a note", nameof(severity));
- await _db.EditAdminNote(noteId, message, severity.Value, secret, editedBy.UserId, editedAt, expiryTime);
- break;
- case NoteType.Watchlist:
- await _db.EditAdminWatchlist(noteId, message, editedBy.UserId, editedAt, expiryTime);
- break;
- case NoteType.Message:
- await _db.EditAdminMessage(noteId, message, editedBy.UserId, editedAt, expiryTime);
- break;
- case NoteType.ServerBan:
- if (severity is null)
- throw new ArgumentException("Severity cannot be null for a ban", nameof(severity));
- await _db.EditServerBan(noteId, message, severity.Value, expiryTime, editedBy.UserId, editedAt);
- break;
- case NoteType.RoleBan:
- if (severity is null)
- throw new ArgumentException("Severity cannot be null for a role ban", nameof(severity));
- await _db.EditServerRoleBan(noteId, message, severity.Value, expiryTime, editedBy.UserId, editedAt);
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(type), type, "Unknown note type");
- }
- var newNote = note with
- {
- Message = message,
- NoteSeverity = severity,
- Secret = secret,
- LastEditedAt = editedAt,
- EditedByName = editedBy.Name,
- ExpiryTime = expiryTime
- };
- NoteModified?.Invoke(newNote);
- }
- public async Task<List<IAdminRemarksRecord>> GetAllAdminRemarks(Guid player)
- {
- return await _db.GetAllAdminRemarks(player);
- }
- public async Task<List<IAdminRemarksRecord>> GetVisibleRemarks(Guid player)
- {
- if (_config.GetCVar(CCVars.SeeOwnNotes))
- {
- return await _db.GetVisibleAdminNotes(player);
- }
- _sawmill.Warning($"Someone tried to call GetVisibleNotes for {player} when see_own_notes was false");
- return new List<IAdminRemarksRecord>();
- }
- public async Task<List<AdminWatchlistRecord>> GetActiveWatchlists(Guid player)
- {
- return await _db.GetActiveWatchlists(player);
- }
- public async Task<List<AdminMessageRecord>> GetNewMessages(Guid player)
- {
- return await _db.GetMessages(player);
- }
- public async Task MarkMessageAsSeen(int id, bool dismissedToo)
- {
- await _db.MarkMessageAsSeen(id, dismissedToo);
- }
- public void PostInject()
- {
- _sawmill = _logManager.GetSawmill(SawmillId);
- }
- }
|