mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-27 23:17:20 +01:00
Fix chat for IRC at least
This commit is contained in:
@@ -29,7 +29,7 @@ namespace Tgstation.Server.Api.Rights
|
||||
/// </summary>
|
||||
WriteConnectionString = 8,
|
||||
/// <summary>
|
||||
/// User can read <see cref="Models.Internal.ChatSettings.ConnectionString"/>
|
||||
/// User can read <see cref="Models.Internal.ChatSettings.ConnectionString"/> requires <see cref="Read"/>
|
||||
/// </summary>
|
||||
ReadConnectionString = 16,
|
||||
/// <summary>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -132,8 +133,8 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
Task task;
|
||||
lock (mappedChannels)
|
||||
{
|
||||
foreach (var kvp in mappedChannels.Where(x => x.Value.ProviderId == connectionId))
|
||||
mappedChannels.Remove(kvp.Key);
|
||||
foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList())
|
||||
mappedChannels.Remove(I);
|
||||
|
||||
if (updateTrackings)
|
||||
lock (trackingContexts)
|
||||
@@ -156,6 +157,32 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
{
|
||||
logger.LogTrace("Chat message: {0}. User (Note unconverted provider Id): {1}", message.Content, JsonConvert.SerializeObject(message.User));
|
||||
|
||||
//map the channel if it's private and we haven't seen it
|
||||
if (message.User.Channel.IsPrivate)
|
||||
lock (providers)
|
||||
lock (mappedChannels)
|
||||
{
|
||||
if (!provider.Connected)
|
||||
return;
|
||||
var enumerable = mappedChannels.Where(x => x.Value.ProviderChannelId == message.User.Channel.RealId);
|
||||
if (!enumerable.Any())
|
||||
{
|
||||
ulong newId;
|
||||
lock (this)
|
||||
newId = channelIdCounter++;
|
||||
mappedChannels.Add(newId, new ChannelMapping
|
||||
{
|
||||
IsWatchdogChannel = false,
|
||||
ProviderChannelId = message.User.Channel.RealId,
|
||||
ProviderId = providers.Where(x => x.Value == provider).Select(x => x.Key).First(),
|
||||
Channel = message.User.Channel
|
||||
});
|
||||
message.User.Channel.RealId = newId;
|
||||
}
|
||||
else
|
||||
message.User.Channel.RealId = enumerable.First().Key;
|
||||
}
|
||||
|
||||
var splits = new List<string>(message.Content.TrimEnd().Split(' '));
|
||||
var address = splits[0];
|
||||
if (address.Length > 1 && (address[address.Length - 1] == ':' || address[address.Length - 1] == ','))
|
||||
@@ -163,18 +190,21 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
|
||||
address = address.ToUpperInvariant();
|
||||
|
||||
if (address != CommonMention.ToUpperInvariant() && address != provider.BotMention.ToUpperInvariant())
|
||||
var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant();
|
||||
|
||||
if (!addressed && !message.User.Channel.IsPrivate)
|
||||
//no mention
|
||||
return;
|
||||
|
||||
if (splits.Count == 1)
|
||||
if ((splits.Count == 1 && !message.User.Channel.IsPrivate) || splits.Count == 0)
|
||||
{
|
||||
//just a mention
|
||||
await SendMessage("Hi!", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
splits.RemoveAt(0);
|
||||
if (addressed)
|
||||
splits.RemoveAt(0);
|
||||
|
||||
var command = splits[0].ToUpperInvariant();
|
||||
splits.RemoveAt(0);
|
||||
@@ -182,16 +212,46 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
|
||||
try
|
||||
{
|
||||
if (!builtinCommands.TryGetValue(command, out ICommand commandHandler))
|
||||
async Task<ICommand> GetCommand(string commandName)
|
||||
{
|
||||
var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
commandHandler = tasks.SelectMany(x => x.Result).Where(x => x.Name.ToUpperInvariant() == command).FirstOrDefault();
|
||||
if (!builtinCommands.TryGetValue(commandName, out var handler))
|
||||
{
|
||||
var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
handler = tasks.SelectMany(x => x.Result).Where(x => x.Name.ToUpperInvariant() == commandName).FirstOrDefault();
|
||||
}
|
||||
return handler;
|
||||
};
|
||||
|
||||
const string UnknownCommandMessage = "Unknown command! Type '?' or 'help' for available commands.";
|
||||
|
||||
if (command == "HELP" || command == "?")
|
||||
{
|
||||
string helpText;
|
||||
if (splits.Count == 0)
|
||||
{
|
||||
var allCommands = builtinCommands.Select(x => x.Value).ToList();
|
||||
var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken));
|
||||
await Task.WhenAll(tasks).ConfigureAwait(false);
|
||||
allCommands.AddRange(tasks.SelectMany(x => x.Result));
|
||||
helpText = String.Format(CultureInfo.InvariantCulture, "Available commands: Type 'help' and then a command name for more details: {0}", String.Join(", ", allCommands.Select(x => x.Name)));
|
||||
}
|
||||
else
|
||||
{
|
||||
var helpHandler = await GetCommand(splits[0].ToUpperInvariant()).ConfigureAwait(false);
|
||||
if (helpHandler != default)
|
||||
helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}", helpHandler.Name, helpHandler.HelpText);
|
||||
else
|
||||
helpText = UnknownCommandMessage;
|
||||
}
|
||||
await SendMessage(helpText, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command == default)
|
||||
var commandHandler = await GetCommand(command).ConfigureAwait(false);
|
||||
if (commandHandler == default)
|
||||
{
|
||||
await SendMessage("Invalid command! Type '?' or 'help' for available commands.", new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
|
||||
await SendMessage(UnknownCommandMessage, new List<ulong> { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -220,9 +280,8 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
//prune disconnected providers
|
||||
foreach (var I in messageTasks)
|
||||
if (!I.Key.Connected)
|
||||
messageTasks.Remove(I.Key);
|
||||
foreach (var I in messageTasks.Where(x => !x.Key.Connected).ToList())
|
||||
messageTasks.Remove(I.Key);
|
||||
|
||||
//add new ones
|
||||
foreach (var I in providers)
|
||||
@@ -237,19 +296,16 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
|
||||
//wait for a message
|
||||
await Task.WhenAny(messageTasks.Select(x => x.Value)).ConfigureAwait(false);
|
||||
|
||||
var toRemove = new List<IProvider>();
|
||||
//process completed ones
|
||||
foreach (var I in messageTasks.Where(x => x.Value.IsCompleted))
|
||||
|
||||
//process completed ones
|
||||
foreach (var I in messageTasks.Where(x => x.Value.IsCompleted).ToList())
|
||||
{
|
||||
var message = await I.Value.ConfigureAwait(false);
|
||||
|
||||
await ProcessMessage(I.Key, message, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
toRemove.Add(I.Key);
|
||||
messageTasks.Remove(I.Key);
|
||||
}
|
||||
foreach (var I in toRemove)
|
||||
messageTasks.Remove(I);
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
@@ -310,23 +366,43 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
if (newSettings == null)
|
||||
throw new ArgumentNullException(nameof(newSettings));
|
||||
IProvider provider;
|
||||
|
||||
async Task DisconnectProvider(IProvider p)
|
||||
{
|
||||
try
|
||||
{
|
||||
await p.Disconnect(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
finally
|
||||
{
|
||||
p.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Task disconnectTask;
|
||||
lock (providers)
|
||||
{
|
||||
//raw settings changes forces a rebuild of the provider
|
||||
if (providers.TryGetValue(newSettings.Id, out provider))
|
||||
{
|
||||
providers.Remove(newSettings.Id);
|
||||
provider.Dispose();
|
||||
disconnectTask = DisconnectProvider(provider);
|
||||
}
|
||||
else
|
||||
disconnectTask = Task.CompletedTask;
|
||||
if (newSettings.Enabled.Value)
|
||||
{
|
||||
provider = providerFactory.CreateProvider(newSettings);
|
||||
providers.Add(newSettings.Id, provider);
|
||||
}
|
||||
}
|
||||
|
||||
lock (mappedChannels)
|
||||
foreach (var channelId in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key))
|
||||
mappedChannels.Remove(channelId);
|
||||
foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList())
|
||||
mappedChannels.Remove(I);
|
||||
|
||||
await disconnectTask.ConfigureAwait(false);
|
||||
|
||||
if (newSettings.Enabled.Value && started)
|
||||
await provider.Connect(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
@@ -133,7 +133,11 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose() => client.Disconnect(); //just closes the socket
|
||||
public override void Dispose()
|
||||
{
|
||||
if(Connected)
|
||||
client.Disconnect(); //just closes the socket
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle an IRC message
|
||||
@@ -142,10 +146,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <param name="isPrivate">If this is a query message</param>
|
||||
void HandleMessage(IrcEventArgs e, bool isPrivate)
|
||||
{
|
||||
if (e.Data.From.ToUpperInvariant() == client.Nickname.ToUpperInvariant())
|
||||
if (e.Data.Nick.ToUpperInvariant() == client.Nickname.ToUpperInvariant())
|
||||
return;
|
||||
|
||||
var username = e.Data.From;
|
||||
var username = e.Data.Nick;
|
||||
var channelName = isPrivate ? username : e.Data.Channel;
|
||||
ulong channelId = 0;
|
||||
lock (this)
|
||||
@@ -279,19 +283,26 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
{
|
||||
if (!Connected)
|
||||
return;
|
||||
await Task.Factory.StartNew(() =>
|
||||
try
|
||||
{
|
||||
try
|
||||
await Task.Factory.StartNew(() =>
|
||||
{
|
||||
client.RfcQuit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning("Error quitting IRC: {0}", e);
|
||||
}
|
||||
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
|
||||
Dispose();
|
||||
await listenTask.ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); //priocritical otherwise Disconnect will hard block
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning("Error quitting IRC: {0}", e);
|
||||
}
|
||||
}, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
|
||||
Dispose();
|
||||
await listenTask.ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.LogWarning("Error disconnecting from IRC! Exception: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -315,7 +326,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
client.RfcJoin(I);
|
||||
|
||||
return (IReadOnlyList<Channel>)channels.Select(x => {
|
||||
ulong id = channelIdCounter;
|
||||
var id = channelIdCounter;
|
||||
if (!channelIdMap.Any(y =>
|
||||
{
|
||||
if (y.Value != x.IrcChannel)
|
||||
@@ -323,9 +334,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
id = y.Key;
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
channelIdMap.Add(id, x.IrcChannel);
|
||||
else
|
||||
++channelIdCounter;
|
||||
}
|
||||
return new Channel
|
||||
{
|
||||
RealId = id,
|
||||
@@ -341,11 +353,18 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// <inheritdoc />
|
||||
public override Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
|
||||
{
|
||||
var channelName = channelIdMap[channelId] ?? queryChannelIdMap[channelId];
|
||||
var channelName = channelIdMap[channelId];
|
||||
SendType sendType;
|
||||
if (channelName == null)
|
||||
{
|
||||
channelName = queryChannelIdMap[channelId];
|
||||
sendType = SendType.Notice;
|
||||
}
|
||||
else
|
||||
sendType = SendType.Message;
|
||||
try
|
||||
{
|
||||
if (client.JoinedChannels.Contains(channelName))
|
||||
client.SendMessage(SendType.Message, channelName, message);
|
||||
client.SendMessage(sendType, channelName, message);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
|
||||
@@ -219,11 +219,15 @@ namespace Tgstation.Server.Host.Controllers
|
||||
//have to rebuild the thing first
|
||||
await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (model.Channels != null)
|
||||
if (model.Channels != null || anySettingsModified)
|
||||
await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if(userRights.HasFlag(ChatSettingsRights.Read))
|
||||
return Json(current);
|
||||
if (userRights.HasFlag(ChatSettingsRights.Read))
|
||||
{
|
||||
if (!userRights.HasFlag(ChatSettingsRights.ReadConnectionString))
|
||||
current.ConnectionString = null;
|
||||
return Json(current.ToApi());
|
||||
}
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Byond.TopicSender" Version="1.1.1" />
|
||||
<PackageReference Include="Cyberboss.AspNetCore.AsyncInitializer" Version="1.1.0" />
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.5" />
|
||||
<PackageReference Include="Cyberboss.SmartIrc4net.Standard" Version="0.4.6" />
|
||||
<PackageReference Include="Discord.Net.WebSocket" Version="1.0.2" />
|
||||
<PackageReference Include="LibGit2Sharp" Version="0.26.0-preview-0027" />
|
||||
<PackageReference Include="Microsoft.AspNetCore" Version="2.1.2" />
|
||||
|
||||
Reference in New Issue
Block a user