Files
SnowcloakClient/MareSynchronos/Services/ChatService.cs
Eauldane a4c82452be Initial
2025-08-22 02:19:48 +01:00

241 lines
8.7 KiB
C#

using Dalamud.Game.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Game.Text.SeStringHandling.Payloads;
using Dalamud.Plugin.Services;
using MareSynchronos.API.Data;
using MareSynchronos.Interop;
using MareSynchronos.MareConfiguration;
using MareSynchronos.MareConfiguration.Models;
using MareSynchronos.PlayerData.Pairs;
using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.Utils;
using MareSynchronos.WebAPI;
using Microsoft.Extensions.Logging;
namespace MareSynchronos.Services;
public class ChatService : DisposableMediatorSubscriberBase
{
public const int DefaultColor = 710;
public const int CommandMaxNumber = 50;
private readonly ILogger<ChatService> _logger;
private readonly IChatGui _chatGui;
private readonly DalamudUtilService _dalamudUtil;
private readonly MareConfigService _mareConfig;
private readonly ApiController _apiController;
private readonly PairManager _pairManager;
private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly Lazy<GameChatHooks> _gameChatHooks;
public ChatService(ILogger<ChatService> logger, DalamudUtilService dalamudUtil, MareMediator mediator, ApiController apiController,
PairManager pairManager, ILoggerFactory loggerFactory, IGameInteropProvider gameInteropProvider, IChatGui chatGui,
MareConfigService mareConfig, ServerConfigurationManager serverConfigurationManager) : base(logger, mediator)
{
_logger = logger;
_dalamudUtil = dalamudUtil;
_chatGui = chatGui;
_mareConfig = mareConfig;
_apiController = apiController;
_pairManager = pairManager;
_serverConfigurationManager = serverConfigurationManager;
Mediator.Subscribe<UserChatMsgMessage>(this, HandleUserChat);
Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat);
_gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider, SendChatShell));
// Initialize chat hooks in advance
_ = Task.Run(() =>
{
try
{
_ = _gameChatHooks.Value;
}
catch (Exception ex)
{
_logger.LogError(ex, "Failed to initialize chat hooks");
}
});
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (_gameChatHooks.IsValueCreated)
_gameChatHooks.Value!.Dispose();
}
private void HandleUserChat(UserChatMsgMessage message)
{
var chatMsg = message.ChatMsg;
var prefix = new SeStringBuilder();
prefix.AddText("[BnnuyChat] ");
_chatGui.Print(new XivChatEntry{
MessageBytes = [..prefix.Build().Encode(), ..message.ChatMsg.PayloadContent],
Name = chatMsg.SenderName,
Type = XivChatType.TellIncoming
});
}
private ushort ResolveShellColor(int shellColor)
{
if (shellColor != 0)
return (ushort)shellColor;
var globalColor = _mareConfig.Current.ChatColor;
if (globalColor != 0)
return (ushort)globalColor;
return (ushort)DefaultColor;
}
private XivChatType ResolveShellLogKind(int shellLogKind)
{
if (shellLogKind != 0)
return (XivChatType)shellLogKind;
return (XivChatType)_mareConfig.Current.ChatLogKind;
}
private void HandleGroupChat(GroupChatMsgMessage message)
{
if (_mareConfig.Current.DisableSyncshellChat)
return;
var chatMsg = message.ChatMsg;
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(message.GroupInfo.GID);
var shellNumber = shellConfig.ShellNumber;
if (!shellConfig.Enabled)
return;
ushort color = ResolveShellColor(shellConfig.Color);
var extraChatTags = _mareConfig.Current.ExtraChatTags;
var logKind = ResolveShellLogKind(shellConfig.LogKind);
var msg = new SeStringBuilder();
if (extraChatTags)
{
msg.Add(ChatUtils.CreateExtraChatTagPayload(message.GroupInfo.GID));
msg.Add(RawPayload.LinkTerminator);
}
if (color != 0)
msg.AddUiForeground((ushort)color);
msg.AddText($"[SS{shellNumber}]<");
if (message.ChatMsg.Sender.UID.Equals(_apiController.UID, StringComparison.Ordinal))
{
// Don't link to your own character
msg.AddText(chatMsg.SenderName);
}
else
{
msg.Add(new PlayerPayload(chatMsg.SenderName, chatMsg.SenderHomeWorldId));
}
msg.AddText("> ");
msg.Append(SeString.Parse(message.ChatMsg.PayloadContent));
if (color != 0)
msg.AddUiForegroundOff();
_chatGui.Print(new XivChatEntry{
Message = msg.Build(),
Name = chatMsg.SenderName,
Type = logKind
});
}
// Print an example message to the configured global chat channel
public void PrintChannelExample(string message, string gid = "")
{
int chatType = _mareConfig.Current.ChatLogKind;
foreach (var group in _pairManager.Groups)
{
if (group.Key.GID.Equals(gid, StringComparison.Ordinal))
{
int shellChatType = _serverConfigurationManager.GetShellConfigForGid(gid).LogKind;
if (shellChatType != 0)
chatType = shellChatType;
}
}
_chatGui.Print(new XivChatEntry{
Message = message,
Name = "",
Type = (XivChatType)chatType
});
}
// Called to update the active chat shell name if its renamed
public void MaybeUpdateShellName(int shellNumber)
{
if (_mareConfig.Current.DisableSyncshellChat)
return;
foreach (var group in _pairManager.Groups)
{
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
{
if (_gameChatHooks.IsValueCreated && _gameChatHooks.Value.ChatChannelOverride != null)
{
// Very dumb and won't handle re-numbering -- need to identify the active chat channel more reliably later
if (_gameChatHooks.Value.ChatChannelOverride.ChannelName.StartsWith($"SS [{shellNumber}]", StringComparison.Ordinal))
SwitchChatShell(shellNumber);
}
}
}
}
public void SwitchChatShell(int shellNumber)
{
if (_mareConfig.Current.DisableSyncshellChat)
return;
foreach (var group in _pairManager.Groups)
{
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
{
var name = _serverConfigurationManager.GetNoteForGid(group.Key.GID) ?? group.Key.AliasOrGID;
// BUG: This doesn't always update the chat window e.g. when renaming a group
_gameChatHooks.Value.ChatChannelOverride = new()
{
ChannelName = $"SS [{shellNumber}]: {name}",
ChatMessageHandler = chatBytes => SendChatShell(shellNumber, chatBytes)
};
return;
}
}
_chatGui.PrintError($"[ElezenSync] Syncshell number #{shellNumber} not found");
}
public void SendChatShell(int shellNumber, byte[] chatBytes)
{
if (_mareConfig.Current.DisableSyncshellChat)
return;
foreach (var group in _pairManager.Groups)
{
var shellConfig = _serverConfigurationManager.GetShellConfigForGid(group.Key.GID);
if (shellConfig.Enabled && shellConfig.ShellNumber == shellNumber)
{
_ = Task.Run(async () => {
// Should cache the name and home world instead of fetching it every time
var chatMsg = await _dalamudUtil.RunOnFrameworkThread(() => {
return new ChatMessage()
{
SenderName = _dalamudUtil.GetPlayerName(),
SenderHomeWorldId = _dalamudUtil.GetHomeWorldId(),
PayloadContent = chatBytes
};
}).ConfigureAwait(false);
await _apiController.GroupChatSendMsg(new(group.Key), chatMsg).ConfigureAwait(false);
}).ConfigureAwait(false);
return;
}
}
_chatGui.PrintError($"[ElezenSync] Syncshell number #{shellNumber} not found");
}
}