fish-station/Content.Server/_Sunrise/MentorHelp/MentorHelpSystem.cs

743 lines
32 KiB
C#

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Content.Server.Administration.Managers;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Server.Players.RateLimiting;
using Content.Shared._Sunrise.MentorHelp;
using Content.Shared._Sunrise.SunriseCCVars;
using Content.Shared.Administration;
using Content.Shared.Database;
using Content.Shared.Players.RateLimiting;
using Content.Sunrise.Interfaces.Shared;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server._Sunrise.MentorHelp
{
/// <summary>
/// Server-side mentor help system for managing tickets
/// </summary>
[UsedImplicitly]
public sealed class MentorHelpSystem : SharedMentorHelpSystem
{
private const string RateLimitKey = "MentorHelp";
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly IServerDbManager _dbManager = default!;
[Dependency] private readonly PlayerRateLimitManager _rateLimit = default!;
private ISharedSponsorsManager? _sponsorsManager; // Sunrise-Sponsors
private List<MentorHelpStatisticsData>? _mentorStatsCache;
private DateTimeOffset? _mentorStatsCacheTime;
private readonly float _mentorCacheInterval = 10;
public override void Initialize()
{
base.Initialize();
_rateLimit.Register(
RateLimitKey,
new RateLimitRegistration(SunriseCCVars.MentorHelpRateLimitPeriod, // Reuse ahelp rate limit config
SunriseCCVars.MentorHelpRateLimitCount,
PlayerRateLimitedAction)
);
SubscribeNetworkEvent<MentorHelpClientTypingUpdated>(OnClientTypingUpdated);
_playerManager.PlayerStatusChanged += OnPlayerStatusChanged;
IoCManager.Instance!.TryResolveType(out _sponsorsManager); // Sunrise-Sponsors
}
private void PlayerRateLimitedAction(ICommonSession session)
{
Log.Warning($"Player {session.Name} ({session.UserId}) was rate limited for mentor help");
}
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
{
// Could notify mentors about player connection status for active tickets
// For now, keep it simple
}
protected override async void OnCreateTicketMessage(MentorHelpCreateTicketMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
// Rate limiting
if (_rateLimit.CountAction(session, RateLimitKey) != RateLimitStatus.Allowed)
return;
// Validate input
if (string.IsNullOrWhiteSpace(message.Subject) || string.IsNullOrWhiteSpace(message.Message))
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to create mentor help ticket with empty subject or message");
return;
}
if (message.Subject.Length > 256 || message.Message.Length > 4096)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to create mentor help ticket with too long subject or message");
return;
}
try
{
var now = DateTimeOffset.UtcNow;
var ticket = new MentorHelpTicket
{
PlayerId = session.UserId,
Subject = message.Subject.Trim(),
Status = MentorHelpTicketStatus.Open,
CreatedAt = now,
UpdatedAt = now,
RoundId = _gameTicker.RoundId,
ServerId = await GetServerIdAsync()
};
await _dbManager.AddMentorHelpTicketAsync(ticket);
var ticketMessage = new MentorHelpMessage
{
TicketId = ticket.Id,
SenderUserId = session.UserId.UserId,
Message = message.Message.Trim(),
SentAt = now,
IsStaffOnly = false
};
await _dbManager.AddMentorHelpMessageAsync(ticketMessage);
Log.Info($"Player {session.Name} ({session.UserId}) created mentor help ticket #{ticket.Id}: {ticket.Subject}");
var ticketData = await ConvertToTicketDataAsync(ticket);
await NotifyTicketUpdate(ticketData);
// Instruct the player's client to open the newly created ticket
RaiseNetworkEvent(new MentorHelpOpenTicketMessage(ticket.Id), session.Channel);
var messageData = await ConvertToMessageDataAsync(ticketMessage);
await NotifyTicketMessage(ticketData, messageData);
}
catch (Exception ex)
{
Log.Error($"Error creating mentor help ticket for {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnClaimTicketMessage(MentorHelpClaimTicketMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
// Check permissions
if (!HasMentorPermissions(session))
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to claim mentor help ticket without permissions");
return;
}
try
{
var ticket = await _dbManager.GetMentorHelpTicketAsync(message.TicketId);
if (ticket == null)
{
Log.Warning($"Mentor {session.Name} ({session.UserId}) tried to claim non-existent ticket #{message.TicketId}");
return;
}
if (ticket.Status == MentorHelpTicketStatus.Closed)
{
Log.Warning($"Mentor {session.Name} ({session.UserId}) tried to claim closed ticket #{message.TicketId}");
return;
}
if (ticket.AssignedToUserId.HasValue && ticket.AssignedToUserId.Value != session.UserId.UserId)
{
return;
}
// Claim the ticket
ticket.AssignedToUserId = session.UserId.UserId;
ticket.Status = MentorHelpTicketStatus.Assigned;
ticket.UpdatedAt = DateTimeOffset.UtcNow;
await _dbManager.UpdateMentorHelpTicketAsync(ticket);
Log.Info($"Mentor {session.Name} ({session.UserId}) claimed ticket #{ticket.Id}");
// Notify all relevant parties
var ticketData = await ConvertToTicketDataAsync(ticket);
await NotifyTicketUpdate(ticketData);
}
catch (Exception ex)
{
Log.Error($"Error claiming mentor help ticket #{message.TicketId} by {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnReplyMessage(MentorHelpReplyMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
// Rate limiting
if (_rateLimit.CountAction(session, RateLimitKey) != RateLimitStatus.Allowed)
return;
try
{
var ticket = await _dbManager.GetMentorHelpTicketAsync(message.TicketId);
if (ticket == null)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to reply to non-existent ticket #{message.TicketId}");
return;
}
if (ticket.Status == MentorHelpTicketStatus.Closed)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to reply to closed ticket #{message.TicketId}");
return;
}
// Check permissions - player can reply to their own ticket, mentors/admins can reply to any
var isTicketOwner = ticket.PlayerId == session.UserId.UserId;
var hasMentorPerms = HasMentorPermissions(session);
if (!isTicketOwner && !hasMentorPerms)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to reply to ticket #{message.TicketId} without permissions");
return;
}
// Staff-only messages can only be sent by mentors/admins
if (message.IsStaffOnly && !hasMentorPerms)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to send staff-only message without permissions");
return;
}
// Validate message
if (string.IsNullOrWhiteSpace(message.Message) || message.Message.Length > 4096)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to send invalid message to ticket #{message.TicketId}");
return;
}
// Create the message
var ticketMessage = new MentorHelpMessage
{
TicketId = message.TicketId,
SenderUserId = session.UserId.UserId,
Message = message.Message.Trim(),
SentAt = DateTimeOffset.UtcNow,
IsStaffOnly = message.IsStaffOnly
};
await _dbManager.AddMentorHelpMessageAsync(ticketMessage);
// Update ticket status
if (hasMentorPerms && ticket.Status == MentorHelpTicketStatus.Open)
{
// Mentor replied to open ticket, mark as assigned
ticket.AssignedToUserId = session.UserId.UserId;
ticket.Status = MentorHelpTicketStatus.Assigned;
}
else if (hasMentorPerms)
{
// Mentor replied, awaiting player response
ticket.Status = MentorHelpTicketStatus.AwaitingResponse;
}
else if (isTicketOwner && ticket.Status == MentorHelpTicketStatus.AwaitingResponse)
{
// Player replied, mark as assigned again
ticket.Status = MentorHelpTicketStatus.Assigned;
}
ticket.UpdatedAt = DateTimeOffset.UtcNow;
await _dbManager.UpdateMentorHelpTicketAsync(ticket);
Log.Info($"Player {session.Name} ({session.UserId}) replied to ticket #{message.TicketId}");
// Notify relevant parties
var ticketData = await ConvertToTicketDataAsync(ticket);
var messageData = await ConvertToMessageDataAsync(ticketMessage);
await NotifyTicketUpdate(ticketData);
await NotifyTicketMessage(ticketData, messageData);
if (hasMentorPerms)
{
var userId = new NetUserId(ticket.PlayerId);
if (_playerManager.TryGetSessionById(userId, out var authorSession))
{
RaiseNetworkEvent(new MentorHelpOpenTicketMessage(ticket.Id), authorSession.Channel);
}
}
}
catch (Exception ex)
{
Log.Error($"Error adding reply to mentor help ticket #{message.TicketId} by {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnCloseTicketMessage(MentorHelpCloseTicketMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
try
{
var ticket = await _dbManager.GetMentorHelpTicketAsync(message.TicketId);
if (ticket == null)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to close non-existent ticket #{message.TicketId}");
return;
}
if (ticket.Status == MentorHelpTicketStatus.Closed)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to close already closed ticket #{message.TicketId}");
return;
}
// Check permissions - player can close their own ticket, mentors/admins can close any
var isTicketOwner = ticket.PlayerId == session.UserId.UserId;
var hasMentorPerms = HasMentorPermissions(session);
if (!isTicketOwner && !hasMentorPerms)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to close ticket #{message.TicketId} without permissions");
return;
}
// Close the ticket
ticket.Status = MentorHelpTicketStatus.Closed;
ticket.ClosedAt = DateTimeOffset.UtcNow;
ticket.ClosedByUserId = session.UserId.UserId;
ticket.UpdatedAt = DateTimeOffset.UtcNow;
await _dbManager.UpdateMentorHelpTicketAsync(ticket);
Log.Info($"Player {session.Name} ({session.UserId}) closed ticket #{ticket.Id}");
// Notify relevant parties
var ticketData = await ConvertToTicketDataAsync(ticket);
await NotifyTicketUpdate(ticketData);
}
catch (Exception ex)
{
Log.Error($"Error closing mentor help ticket #{message.TicketId} by {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnRequestTicketsMessage(MentorHelpRequestTicketsMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
try
{
List<MentorHelpTicket> tickets;
if (message.OnlyMine)
{
// Player requesting their own tickets (both open and closed)
tickets = await _dbManager.GetMentorHelpTicketsByPlayerAsync(session.UserId.UserId);
}
else
{
// Mentor/admin requesting all tickets (both open and closed)
if (!HasMentorPermissions(session))
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to request all mentor help tickets without permissions");
return;
}
// Get both open and closed tickets for mentors
var openTickets = await _dbManager.GetOpenMentorHelpTicketsAsync();
var closedTickets = await _dbManager.GetClosedMentorHelpTicketsAsync();
tickets = openTickets.Concat(closedTickets).ToList();
}
// Collect all unique user IDs for batch loading
var userIds = new HashSet<Guid>();
foreach (var ticket in tickets)
{
userIds.Add(ticket.PlayerId);
if (ticket.AssignedToUserId.HasValue)
userIds.Add(ticket.AssignedToUserId.Value);
if (ticket.ClosedByUserId.HasValue)
userIds.Add(ticket.ClosedByUserId.Value);
}
// Load all player names in one batch query
var playerNames = await _dbManager.GetPlayerNamesBatchAsync(userIds);
// Convert tickets to data using cached names
var ticketDataList = new List<MentorHelpTicketData>();
foreach (var ticket in tickets)
{
ticketDataList.Add(ConvertToTicketData(ticket, playerNames));
}
RaiseNetworkEvent(new MentorHelpTicketsListMessage(ticketDataList), session.Channel);
}
catch (Exception ex)
{
Log.Error($"Error requesting mentor help tickets for {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnUnassignTicketMessage(MentorHelpUnassignTicketMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
if (!HasMentorPermissions(session))
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to unassign mentor help ticket without permissions");
return;
}
try
{
var ticket = await _dbManager.GetMentorHelpTicketAsync(message.TicketId);
if (ticket == null)
{
Log.Warning($"Mentor {session.Name} ({session.UserId}) tried to unassign non-existent ticket #{message.TicketId}");
return;
}
if (ticket.Status == MentorHelpTicketStatus.Closed)
{
Log.Warning($"Mentor {session.Name} ({session.UserId}) tried to unassign closed ticket #{message.TicketId}");
return;
}
ticket.AssignedToUserId = null;
ticket.Status = MentorHelpTicketStatus.Open;
ticket.UpdatedAt = DateTimeOffset.UtcNow;
await _dbManager.UpdateMentorHelpTicketAsync(ticket);
Log.Info($"Mentor {session.Name} ({session.UserId}) unassigned ticket #{ticket.Id}");
var ticketData = await ConvertToTicketDataAsync(ticket);
await NotifyTicketUpdate(ticketData);
}
catch (Exception ex)
{
Log.Error($"Error unassigning mentor help ticket #{message.TicketId} by {session.Name} ({session.UserId}): {ex}");
}
}
private bool HasMentorPermissions(ICommonSession session)
{
var adminData = _adminManager.GetAdminData(session);
return adminData?.HasFlag(AdminFlags.Mentor) ?? false;
}
private async Task<int?> GetServerIdAsync()
{
// Implementation would depend on how server ID is tracked
// For now, return null
return null;
}
protected override async void OnRequestStatisticsMessage(MentorHelpRequestStatisticsMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
if (!HasMentorPermissions(session))
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to request mentor help statistics without permissions");
return;
}
try
{
var now = DateTimeOffset.UtcNow;
var cacheValid = _mentorStatsCache != null && _mentorStatsCacheTime != null && (now - _mentorStatsCacheTime.Value).TotalMinutes < _mentorCacheInterval;
List<MentorHelpStatisticsData> statisticsData;
if (cacheValid)
{
statisticsData = _mentorStatsCache!;
}
else
{
var statistics = await _dbManager.GetMentorHelpStatisticsAsync();
statisticsData = new List<MentorHelpStatisticsData>();
foreach (var stat in statistics)
{
var adminData = await _adminManager.LoadAdminData(new NetUserId(stat.MentorUserId));
if (adminData == null)
continue;
if (!adminData.Value.dat.Flags.HasFlag(AdminFlags.Mentor))
continue;
var mentorName = await GetPlayerNameAsync(stat.MentorUserId);
statisticsData.Add(new MentorHelpStatisticsData
{
MentorName = mentorName,
TicketsClaimed = stat.TicketsClaimed,
MessagesCount = stat.MessagesCount
});
}
statisticsData = statisticsData.OrderByDescending((s) => s.TicketsClaimed).ToList();
_mentorStatsCache = statisticsData;
_mentorStatsCacheTime = now;
}
RaiseNetworkEvent(new MentorHelpStatisticsMessage(statisticsData), session.Channel);
}
catch (Exception ex)
{
Log.Error($"Error requesting mentor help statistics for {session.Name} ({session.UserId}): {ex}");
}
}
protected override async void OnRequestTicketMessagesMessage(MentorHelpRequestTicketMessagesMessage message, EntitySessionEventArgs eventArgs)
{
var session = eventArgs.SenderSession;
Log.Info("Received RequestTicketMessages for ticket #{0} from {1} ({2})", message.TicketId, session.Name, session.UserId);
try
{
var ticket = await _dbManager.GetMentorHelpTicketAsync(message.TicketId);
if (ticket == null)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to request messages for non-existent ticket #{message.TicketId}");
return;
}
var hasMentorPerms = HasMentorPermissions(session);
var isTicketOwner = ticket.PlayerId == session.UserId.UserId;
if (!hasMentorPerms && !isTicketOwner)
{
Log.Warning($"Player {session.Name} ({session.UserId}) tried to request messages for ticket #{message.TicketId} without permissions");
return;
}
var allMessages = await _dbManager.GetMentorHelpMessagesByTicketAsync(message.TicketId);
var messageDatas = new List<MentorHelpMessageData>();
foreach (var msg in allMessages.OrderBy(m => m.SentAt))
{
if (!hasMentorPerms && msg.IsStaffOnly)
continue;
messageDatas.Add(await ConvertToMessageDataAsync(msg));
}
RaiseNetworkEvent(new MentorHelpTicketMessagesMessage(message.TicketId, messageDatas), session.Channel);
Log.Info("Sent {0} messages for ticket #{1} to {2} ({3})", messageDatas.Count, message.TicketId, session.Name, session.UserId);
}
catch (Exception ex)
{
Log.Error($"Error requesting mentor help messages for ticket #{message.TicketId} by {session.Name} ({session.UserId}): {ex}");
}
}
private MentorHelpTicketData ConvertToTicketData(MentorHelpTicket ticket, Dictionary<Guid, string> playerNames)
{
playerNames.TryGetValue(ticket.PlayerId, out var playerName);
var assignedToName = ticket.AssignedToUserId.HasValue && playerNames.TryGetValue(ticket.AssignedToUserId.Value, out var assignedName) ? assignedName : null;
var closedByName = ticket.ClosedByUserId.HasValue && playerNames.TryGetValue(ticket.ClosedByUserId.Value, out var closedName) ? closedName : null;
return new MentorHelpTicketData
{
Id = ticket.Id,
PlayerId = new NetUserId(ticket.PlayerId),
PlayerName = playerName ?? "Unknown",
AssignedToUserId = ticket.AssignedToUserId.HasValue ? new NetUserId(ticket.AssignedToUserId.Value) : null,
AssignedToName = assignedToName,
Subject = ticket.Subject,
Status = ticket.Status,
CreatedAt = ticket.CreatedAt.DateTime,
UpdatedAt = ticket.UpdatedAt.DateTime,
ClosedAt = ticket.ClosedAt?.DateTime,
ClosedByUserId = ticket.ClosedByUserId.HasValue ? new NetUserId(ticket.ClosedByUserId.Value) : null,
ClosedByName = closedByName,
RoundId = ticket.RoundId,
HasUnreadMessages = false // Would need to implement read tracking
};
}
private async Task<MentorHelpTicketData> ConvertToTicketDataAsync(MentorHelpTicket ticket)
{
var playerName = await GetPlayerNameAsync(ticket.PlayerId);
var assignedToName = ticket.AssignedToUserId.HasValue ? await GetPlayerNameAsync(ticket.AssignedToUserId.Value) : null;
var closedByName = ticket.ClosedByUserId.HasValue ? await GetPlayerNameAsync(ticket.ClosedByUserId.Value) : null;
return new MentorHelpTicketData
{
Id = ticket.Id,
PlayerId = new NetUserId(ticket.PlayerId),
PlayerName = playerName,
AssignedToUserId = ticket.AssignedToUserId.HasValue ? new NetUserId(ticket.AssignedToUserId.Value) : null,
AssignedToName = assignedToName,
Subject = ticket.Subject,
Status = ticket.Status,
CreatedAt = ticket.CreatedAt.DateTime,
UpdatedAt = ticket.UpdatedAt.DateTime,
ClosedAt = ticket.ClosedAt?.DateTime,
ClosedByUserId = ticket.ClosedByUserId.HasValue ? new NetUserId(ticket.ClosedByUserId.Value) : null,
ClosedByName = closedByName,
RoundId = ticket.RoundId,
HasUnreadMessages = false // Would need to implement read tracking
};
}
private async Task<MentorHelpMessageData> ConvertToMessageDataAsync(MentorHelpMessage message)
{
var senderUserId = new NetUserId(message.SenderUserId);
var senderAdminData = await _adminManager.LoadAdminData(senderUserId);
var senderData = await _dbManager.GetPlayerRecordByUserId(senderUserId);
var username = "";
if (senderData != null)
{
username = senderData.LastSeenUserName;
}
string formatterSender;
var adminPrefix = "";
if (_config.GetCVar(SunriseCCVars.MentorHelpAdminPrefix) && senderAdminData is not null && senderAdminData.Value.dat.Title is not null)
{
adminPrefix = $"[bold]\\[{senderAdminData.Value.dat.Title}\\][/bold] ";
}
if (senderAdminData is not null &&
senderAdminData.Value.dat.Flags ==
AdminFlags.Mentor)
{
formatterSender = $"[color=purple]{adminPrefix}{username}[/color]";
}
else if (senderAdminData is not null && senderAdminData.Value.dat.Flags.HasFlag(AdminFlags.Mentor))
{
formatterSender = $"[color=red]{adminPrefix}{username}[/color]";
}
else if (_sponsorsManager != null)
{
_sponsorsManager.TryGetOocColor(senderUserId, out var oocColor);
_sponsorsManager.TryGetOocTitle(senderUserId, out var oocTitle);
var sponsorTitle = oocTitle is null ? "" : $"\\[{oocTitle}\\]";
if (oocColor != null)
{
formatterSender = $"[color={oocColor.Value.ToHex()}]{sponsorTitle} {username}[/color]";
}
else
{
formatterSender = $"{sponsorTitle} {username}";
}
}
else
{
formatterSender = $"{username}";
}
return new MentorHelpMessageData
{
Id = message.Id,
TicketId = message.TicketId,
SenderUserId = senderUserId,
SenderName = username,
FormattedSender = formatterSender,
Message = message.Message,
SentAt = message.SentAt.DateTime,
IsStaffOnly = message.IsStaffOnly
};
}
private async Task<string> GetPlayerNameAsync(Guid userId)
{
var playerData = await _dbManager.GetPlayerRecordByUserId(new NetUserId(userId));
var name = playerData?.LastSeenUserName;
if (string.IsNullOrWhiteSpace(name))
{
Log.Warning($"GetPlayerNameAsync: No name found for userId {userId}, returning 'Unknown'.");
return "Unknown";
}
return name;
}
private async Task NotifyTicketUpdate(MentorHelpTicketData ticketData)
{
// Notify the player
if (_playerManager.TryGetSessionById(ticketData.PlayerId, out var playerSession))
{
RaiseNetworkEvent(new MentorHelpTicketUpdateMessage(ticketData), playerSession.Channel);
}
// Notify mentors
var mentors = GetTargetMentors();
foreach (var mentor in mentors)
{
RaiseNetworkEvent(new MentorHelpTicketUpdateMessage(ticketData), mentor);
}
}
private async Task NotifyTicketMessage(MentorHelpTicketData ticketData, MentorHelpMessageData messageData)
{
var allMessages = await _dbManager.GetMentorHelpMessagesByTicketAsync(ticketData.Id);
var messageDatas = new List<MentorHelpMessageData>();
foreach (var msg in allMessages.OrderBy(m => m.SentAt)) // сортировка теперь точно по объектам
{
messageDatas.Add(await ConvertToMessageDataAsync(msg));
}
// Notify the player (if not staff-only)
if (!messageData.IsStaffOnly && _playerManager.TryGetSessionById(ticketData.PlayerId, out var playerSession))
{
RaiseNetworkEvent(new MentorHelpTicketMessagesMessage(ticketData.Id, messageDatas), playerSession.Channel);
}
// Notify mentors
var mentors = GetTargetMentors();
foreach (var mentor in mentors)
{
RaiseNetworkEvent(new MentorHelpTicketMessagesMessage(ticketData.Id, messageDatas), mentor);
}
}
private IList<INetChannel> GetTargetMentors()
{
return _adminManager.ActiveAdmins
.Where(p => _adminManager.GetAdminData(p)?.HasFlag(AdminFlags.Mentor) ?? false)
.Select(p => p.Channel)
.ToList();
}
private async void OnClientTypingUpdated(MentorHelpClientTypingUpdated msg, EntitySessionEventArgs args)
{
var session = args.SenderSession;
var ticket = await _dbManager.GetMentorHelpTicketAsync(msg.TicketId);
if (ticket == null)
return;
var update = new MentorHelpPlayerTypingUpdated(msg.TicketId, session.UserId, session.Name, msg.Typing);
var recipients = new HashSet<INetChannel>();
if (_playerManager.TryGetSessionById(new NetUserId(ticket.PlayerId), out var authorSession))
{
if (!authorSession.UserId.Equals(session.UserId))
recipients.Add(authorSession.Channel);
}
if (ticket.AssignedToUserId.HasValue &&
_playerManager.TryGetSessionById(new NetUserId(ticket.AssignedToUserId.Value), out var mentorSession))
{
if (!mentorSession.UserId.Equals(session.UserId))
recipients.Add(mentorSession.Channel);
}
foreach (var recipient in recipients)
RaiseNetworkEvent(update, recipient);
}
}
}