Merge pull request #1435 from tgstation/FeatureBranch

Discord Replies and some other stuff
This commit is contained in:
Jordan Dominion
2023-03-25 17:37:41 -04:00
committed by GitHub
32 changed files with 268 additions and 244 deletions
+1 -1
View File
@@ -218,7 +218,7 @@ For the docker version run `docker stop <your container name>`
## Integrating
tgstation-server 4 provides the DMAPI which can be be integrated into any BYOND codebase for heavily enhanced functionality. The integration process is a fairly simple set of code changes.
tgstation-server provides the DMAPI which can be be integrated into any BYOND codebase for heavily enhanced functionality. The integration process is a fairly simple set of code changes.
1. Copy the [latest release of the DMAPI](https://github.com/tgstation/tgstation-server/releases) anywhere in your code base. `tgs.dm` can be seperated from the `tgs` folder, but do not modify or move the contents of the `tgs` folder
2. Modify your `.dme`(s) to include the `tgs.dm` and `tgs/includes.dm` files (ORDER OF APPEARANCE IS MANDATORY)
+1 -1
View File
@@ -3,7 +3,7 @@
<!-- Integration tests will ensure they match across the board -->
<Import Project="ControlPanelVersion.props" />
<PropertyGroup>
<TgsCoreVersion>5.3.3</TgsCoreVersion>
<TgsCoreVersion>5.4.0</TgsCoreVersion>
<TgsConfigVersion>4.4.0</TgsConfigVersion>
<TgsApiVersion>9.8.1</TgsApiVersion>
<TgsApiLibraryVersion>10.2.0</TgsApiLibraryVersion>
@@ -22,8 +22,10 @@ namespace Tgstation.Server.Api.Models.Internal
return true;
return Provider.Value switch
{
#pragma warning disable CS0618
ChatProvider.Discord => Channels?.Select(x => (x.DiscordChannelId.HasValue || ulong.TryParse(x.ChannelData, out _)) && x.IrcChannel == null).All(x => x) ?? true,
ChatProvider.Irc => Channels?.Select(x => !x.DiscordChannelId.HasValue && (x.IrcChannel != null || x.ChannelData != null)).All(x => x) ?? true,
#pragma warning restore CS0618
_ => throw new InvalidOperationException("Invalid provider type!"),
};
}
@@ -47,7 +47,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+1 -5
View File
@@ -26,15 +26,11 @@ namespace Tgstation.Server.Host.Console
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
internal static async Task Main(string[] args)
{
using var loggerFactory = new LoggerFactory();
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var arguments = new List<string>(args);
var trace = arguments.Remove("--trace-host-watchdog");
var debug = arguments.Remove("--debug-host-watchdog");
#pragma warning disable CS0618 // Type or member is obsolete
loggerFactory.AddConsole();
#pragma warning restore CS0618 // Type or member is obsolete
if (trace && debug)
{
loggerFactory.CreateLogger(nameof(Program)).LogCritical("Please specify only 1 of --trace-host-watchdog or --debug-host-watchdog!");
@@ -25,7 +25,7 @@
<ItemGroup>
<!-- DO NOT UPDATE UNLESS YOU WANT TO DEAL WITH THE LOGGERFACTORY REFACTOR -->
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
+72 -69
View File
@@ -77,6 +77,54 @@ namespace Tgstation.Server.Host.Service
/// <returns>A <see cref="Task"/> resulting in the <see cref="Program"/>'s exit code.</returns>
static Task<int> Main(string[] args) => CommandLineApplication.ExecuteAsync<Program>(args);
/// <summary>
/// Attempt to install the TGS Service.
/// </summary>
static void RunServiceInstall()
{
// First check if the service already exists
if (Environment.UserInteractive)
foreach (ServiceController sc in ServiceController.GetServices())
if (sc.ServiceName == "tgstation-server" || sc.ServiceName == "tgstation-server-4")
{
DialogResult result = MessageBox.Show($"You already have another TGS service installed ({sc.ServiceName}). Would you like to uninstall it now? Pressing \"No\" will cancel this install.", "TGS Service", MessageBoxButtons.YesNo);
if (result != DialogResult.Yes)
return; // is this needed after exit?
// Stop it first to give it some cleanup time
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
// And remove it
using (ServiceInstaller si = new ServiceInstaller())
{
si.Context = new InstallContext($"old-{sc.ServiceName}-uninstall.log", null);
si.ServiceName = sc.ServiceName;
si.Uninstall(null);
}
}
using (var processInstaller = new ServiceProcessInstaller())
using (var installer = new ServiceInstaller())
{
processInstaller.Account = ServiceAccount.LocalSystem;
installer.Context = new InstallContext("tgs-install.log", new string[] { String.Format(CultureInfo.InvariantCulture, "/assemblypath={0}", Assembly.GetEntryAssembly().Location) });
installer.Description = "/tg/station 13 server running as a windows service";
installer.DisplayName = "/tg/station server";
installer.StartType = ServiceStartMode.Automatic;
installer.ServicesDependedOn = new string[] { "Tcpip", "Dhcp", "Dnscache" };
installer.ServiceName = ServerService.Name;
installer.Parent = processInstaller;
var state = new ListDictionary();
installer.Install(state);
}
}
/// <summary>
/// Command line handler, always runs.
/// </summary>
@@ -112,82 +160,37 @@ namespace Tgstation.Server.Host.Service
}
}
using (var loggerFactory = new LoggerFactory())
if (Install)
{
if (Install)
{
if (Uninstall)
return; // oh no, it's retarded...
if (Uninstall)
return; // oh no, it's retarded...
// First check if the service already exists
if (Environment.UserInteractive)
foreach (ServiceController sc in ServiceController.GetServices())
if (sc.ServiceName == "tgstation-server" || sc.ServiceName == "tgstation-server-4")
{
DialogResult result = MessageBox.Show($"You already have another TGS service installed ({sc.ServiceName}). Would you like to uninstall it now? Pressing \"No\" will cancel this install.", "TGS Service", MessageBoxButtons.YesNo);
if (result != DialogResult.Yes)
return; // is this needed after exit?
// Stop it first to give it some cleanup time
if (sc.Status == ServiceControllerStatus.Running)
{
sc.Stop();
sc.WaitForStatus(ServiceControllerStatus.Stopped);
}
// And remove it
using (ServiceInstaller si = new ServiceInstaller())
{
si.Context = new InstallContext($"old-{sc.ServiceName}-uninstall.log", null);
si.ServiceName = sc.ServiceName;
si.Uninstall(null);
}
}
using (var processInstaller = new ServiceProcessInstaller())
using (var installer = new ServiceInstaller())
{
processInstaller.Account = ServiceAccount.LocalSystem;
installer.Context = new InstallContext("tgs-install.log", new string[] { String.Format(CultureInfo.InvariantCulture, "/assemblypath={0}", Assembly.GetEntryAssembly().Location) });
installer.Description = "/tg/station 13 server running as a windows service";
installer.DisplayName = "/tg/station server";
installer.StartType = ServiceStartMode.Automatic;
installer.ServicesDependedOn = new string[] { "Tcpip", "Dhcp", "Dnscache" };
installer.ServiceName = ServerService.Name;
installer.Parent = processInstaller;
var state = new ListDictionary();
installer.Install(state);
}
if (Configure)
{
Console.WriteLine("For this first run we'll launch the console runner so you may use the setup wizard.");
Console.WriteLine("If it starts successfully, feel free to close it and then start the service from the Windows control panel.");
}
}
else if (Uninstall)
using (var installer = new ServiceInstaller())
{
installer.Context = new InstallContext("tgs-uninstall.log", null);
installer.ServiceName = ServerService.Name;
installer.Uninstall(null);
}
else if (!Configure)
using (var service = new ServerService(WatchdogFactory, loggerFactory, Trace ? LogLevel.Trace : Debug ? LogLevel.Debug : LogLevel.Information))
ServiceBase.Run(service);
RunServiceInstall();
if (Configure)
{
#pragma warning disable CS0618 // Type or member is obsolete
loggerFactory.AddConsole();
#pragma warning restore CS0618 // Type or member is obsolete
// DCT: None available
await WatchdogFactory.CreateWatchdog(loggerFactory).RunAsync(true, Array.Empty<string>(), default);
Console.WriteLine("For this first run we'll launch the console runner so you may use the setup wizard.");
Console.WriteLine("If it starts successfully, feel free to close it and then start the service from the Windows control panel.");
}
}
else if (Uninstall)
using (var installer = new ServiceInstaller())
{
installer.Context = new InstallContext("tgs-uninstall.log", null);
installer.ServiceName = ServerService.Name;
installer.Uninstall(null);
}
else if (!Configure)
{
using (var service = new ServerService(WatchdogFactory, Trace ? LogLevel.Trace : Debug ? LogLevel.Debug : LogLevel.Information))
ServiceBase.Run(service);
}
if (Configure)
{
using (var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()))
await WatchdogFactory.CreateWatchdog(loggerFactory).RunAsync(true, Array.Empty<string>(), default); // DCT: None available
}
}
}
}
@@ -25,10 +25,20 @@ namespace Tgstation.Server.Host.Service
/// <summary>
/// The <see cref="IWatchdog"/> for the <see cref="ServerService"/>.
/// </summary>
readonly IWatchdog watchdog;
readonly IWatchdogFactory watchdogFactory;
/// <summary>
/// The <see cref="Task"/> recieved from <see cref="IWatchdog.RunAsync(bool, string[], CancellationToken)"/> of <see cref="watchdog"/>.
/// The minimum <see cref="Microsoft.Extensions.Logging.LogLevel"/> for the <see cref="EventLog"/>.
/// </summary>
readonly LogLevel minimumLogLevel;
/// <summary>
/// The <see cref="ILoggerFactory"/> used by the service.
/// </summary>
ILoggerFactory loggerFactory;
/// <summary>
/// The <see cref="Task"/> that represents the running service.
/// </summary>
Task watchdogTask;
@@ -40,33 +50,19 @@ namespace Tgstation.Server.Host.Service
/// <summary>
/// Initializes a new instance of the <see cref="ServerService"/> class.
/// </summary>
/// <param name="watchdogFactory">The <see cref="IWatchdogFactory"/> to create <see cref="watchdog"/> with.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/> for <paramref name="watchdogFactory"/>.</param>
/// <param name="minumumLogLevel">The minimum <see cref="Microsoft.Extensions.Logging.LogLevel"/> to record in the event log.</param>
public ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory, LogLevel minumumLogLevel)
/// <param name="watchdogFactory">The value of <see cref="watchdogFactory"/>.</param>
/// <param name="minimumLogLevel">The minimum <see cref="Microsoft.Extensions.Logging.LogLevel"/> to record in the event log.</param>
public ServerService(IWatchdogFactory watchdogFactory, LogLevel minimumLogLevel)
{
if (watchdogFactory == null)
throw new ArgumentNullException(nameof(watchdogFactory));
if (loggerFactory == null)
throw new ArgumentNullException(nameof(loggerFactory));
#pragma warning disable CS0618 // Type or member is obsolete
loggerFactory.AddEventLog(new EventLogSettings
{
LogName = EventLog.Log,
MachineName = EventLog.MachineName,
SourceName = EventLog.Source,
Filter = (message, logLevel) => logLevel >= minumumLogLevel,
});
#pragma warning restore CS0618 // Type or member is obsolete
this.watchdogFactory = watchdogFactory ?? throw new ArgumentNullException(nameof(watchdogFactory));
this.minimumLogLevel = minimumLogLevel;
ServiceName = Name;
watchdog = watchdogFactory.CreateWatchdog(loggerFactory);
}
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
loggerFactory?.Dispose();
cancellationTokenSource?.Dispose();
base.Dispose(disposing);
}
@@ -74,9 +70,23 @@ namespace Tgstation.Server.Host.Service
/// <inheritdoc />
protected override void OnStart(string[] args)
{
if (loggerFactory == null)
{
loggerFactory = LoggerFactory.Create(builder => builder.AddEventLog(new EventLogSettings
{
LogName = EventLog.Log,
MachineName = EventLog.MachineName,
SourceName = EventLog.Source,
Filter = (message, logLevel) => logLevel >= minimumLogLevel,
}));
}
var watchdog = watchdogFactory.CreateWatchdog(loggerFactory);
cancellationTokenSource?.Dispose();
cancellationTokenSource = new CancellationTokenSource();
watchdogTask = RunWatchdog(args, cancellationTokenSource.Token);
watchdogTask = RunWatchdog(watchdog, args, cancellationTokenSource.Token);
}
/// <inheritdoc />
@@ -87,12 +97,13 @@ namespace Tgstation.Server.Host.Service
}
/// <summary>
/// Executes the <see cref="watchdog"/>, stopping the service if it exits.
/// Executes the <paramref name="watchdog"/>, stopping the service if it exits.
/// </summary>
/// <param name="args">The arguments for the <see cref="watchdog"/>.</param>
/// <param name="watchdog">The <see cref="IWatchdog"/> to run.</param>
/// <param name="args">The arguments for the <paramref name="watchdog"/>.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
async Task RunWatchdog(string[] args, CancellationToken cancellationToken)
async Task RunWatchdog(IWatchdog watchdog, string[] args, CancellationToken cancellationToken)
{
await watchdog.RunAsync(false, args, cancellationTokenSource.Token);
@@ -23,13 +23,13 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.1.0" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.2" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.EventLog" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "6.0.8",
"version": "6.0.15",
"commands": [
"dotnet-ef"
]
@@ -329,7 +329,7 @@ namespace Tgstation.Server.Host.Components.Chat
if (channelIds == null)
throw new ArgumentNullException(nameof(channelIds));
var task = SendMessage(message, channelIds, handlerCts.Token);
var task = SendMessage(channelIds, null, message, handlerCts.Token);
AddMessageTask(task);
}
@@ -499,7 +499,7 @@ namespace Tgstation.Server.Host.Components.Chat
.Select(x => x.Key)
.ToList();
return SendMessage(message, wdChannels, cancellationToken);
return SendMessage(wdChannels, null, message, cancellationToken);
}
/// <summary>
@@ -641,11 +641,12 @@ namespace Tgstation.Server.Host.Components.Chat
providerId,
message.User.Channel.RealId);
await SendMessage(
"Processing error, check logs!",
new List<ulong>
{
message.User.Channel.RealId,
},
null,
"Processing error, check logs!",
cancellationToken)
;
return;
@@ -685,7 +686,7 @@ namespace Tgstation.Server.Host.Components.Chat
if (splits.Count == 0)
{
// just a mention
await SendMessage("Hi!", new List<ulong> { message.User.Channel.RealId }, cancellationToken);
await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, "Hi!", cancellationToken);
return;
}
@@ -731,7 +732,7 @@ namespace Tgstation.Server.Host.Components.Chat
helpText = UnknownCommandMessage;
}
await SendMessage(helpText, new List<ulong> { message.User.Channel.RealId }, cancellationToken);
await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, helpText, cancellationToken);
return;
}
@@ -739,19 +740,19 @@ namespace Tgstation.Server.Host.Components.Chat
if (commandHandler == default)
{
await SendMessage(UnknownCommandMessage, new List<ulong> { message.User.Channel.RealId }, cancellationToken);
await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, UnknownCommandMessage, cancellationToken);
return;
}
if (commandHandler.AdminOnly && !message.User.Channel.IsAdminChannel)
{
await SendMessage("Use this command in an admin channel!", new List<ulong> { message.User.Channel.RealId }, cancellationToken);
await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, "Use this command in an admin channel!", cancellationToken);
return;
}
var result = await commandHandler.Invoke(arguments, message.User, cancellationToken);
if (result != null)
await SendMessage(result, new List<ulong> { message.User.Channel.RealId }, cancellationToken);
await SendMessage(new List<ulong> { message.User.Channel.RealId }, message, result, cancellationToken);
}
catch (OperationCanceledException ex)
{
@@ -763,8 +764,9 @@ namespace Tgstation.Server.Host.Components.Chat
// error bc custom commands should reply about why it failed
logger.LogError(e, "Error processing chat command");
await SendMessage(
"TGS: Internal error processing command! Check server logs!",
new List<ulong> { message.User.Channel.RealId },
message,
"TGS: Internal error processing command! Check server logs!",
cancellationToken)
;
}
@@ -861,11 +863,12 @@ namespace Tgstation.Server.Host.Components.Chat
/// <summary>
/// Asynchronously send a given <paramref name="message"/> to a set of <paramref name="channelIds"/>.
/// </summary>
/// <param name="message">The message to send.</param>
/// <param name="channelIds">The <see cref="Models.ChatChannel.Id"/>s of the <see cref="Models.ChatChannel"/>s to send to.</param>
/// <param name="replyTo">The <see cref="Message"/> to reply to.</param>
/// <param name="message">The message to send.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task SendMessage(string message, IEnumerable<ulong> channelIds, CancellationToken cancellationToken)
Task SendMessage(IEnumerable<ulong> channelIds, Message replyTo, string message, CancellationToken cancellationToken)
{
logger.LogTrace("Chat send \"{message}\" to channels: {channelIdsCommaSeperated}", message, String.Join(", ", channelIds));
@@ -880,7 +883,7 @@ namespace Tgstation.Server.Host.Components.Chat
lock (providers)
if (!providers.TryGetValue(channelMapping.ProviderId, out provider))
return Task.CompletedTask;
return provider.SendMessage(channelMapping.ProviderChannelId, message, cancellationToken);
return provider.SendMessage(replyTo, message, channelMapping.ProviderChannelId, cancellationToken);
}));
}
@@ -3,7 +3,7 @@
/// <summary>
/// Represents a message recieved by a <see cref="IProvider"/>.
/// </summary>
sealed class Message
class Message
{
/// <summary>
/// The text of the message.
@@ -0,0 +1,16 @@
using Remora.Discord.API.Abstractions.Objects;
using Remora.Rest.Core;
namespace Tgstation.Server.Host.Components.Chat.Providers
{
/// <summary>
/// A <see cref="Message"/> containing the source <see cref="IMessageReference"/>.
/// </summary>
sealed class DiscordMessage : Message
{
/// <summary>
/// The <see cref="IMessageReference"/> of the source <see cref="Message"/>.
/// </summary>
public Optional<IMessageReference> MessageReference { get; set; }
}
}
@@ -219,14 +219,21 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
/// <inheritdoc />
public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken)
public override async Task SendMessage(Message replyTo, string message, ulong channelId, CancellationToken cancellationToken)
{
Optional<IMessageReference> replyToReference = default;
if (replyTo != null && replyTo is DiscordMessage discordMessage)
{
replyToReference = discordMessage.MessageReference;
}
var channelsClient = serviceProvider.GetRequiredService<IDiscordRestChannelAPI>();
async Task SendToChannel(Snowflake channelId)
{
var result = await channelsClient.CreateMessageAsync(
channelId,
message,
messageReference: replyToReference,
ct: cancellationToken);
if (!result.IsSuccess)
@@ -273,8 +280,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
Logger.LogTrace("Dispatching to {0} unmapped channels...", unmappedTextChannels.Count());
await Task.WhenAll(
unmappedTextChannels.Select(
x => SendToChannel(x.ID)))
;
x => SendToChannel(x.ID)));
}
return;
@@ -430,14 +436,25 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|| messageCreateEvent.Author.ID == currentUserId)
return Result.FromSuccess();
var messageReference = new MessageReference
{
ChannelID = messageCreateEvent.ChannelID,
GuildID = messageCreateEvent.GuildID,
MessageID = messageCreateEvent.ID,
FailIfNotExists = false,
};
if (basedMeme && messageCreateEvent.Content.Equals("Based on what?", StringComparison.OrdinalIgnoreCase))
{
// DCT: None available
await SendMessage(
messageCreateEvent.ChannelID.Value,
new DiscordMessage
{
MessageReference = messageReference,
},
"https://youtu.be/LrNu-SuFF_o",
default)
;
messageCreateEvent.ChannelID.Value,
default);
return Result.FromSuccess();
}
@@ -491,8 +508,9 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
messageCreateEvent.ID);
}
var result = new Message
var result = new DiscordMessage
{
MessageReference = messageReference,
Content = content,
User = new ChatUser
{
@@ -63,11 +63,12 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
/// <summary>
/// Send a message to the <see cref="IProvider"/>.
/// </summary>
/// <param name="channelId">The <see cref="ChannelRepresentation.RealId"/> to send to.</param>
/// <param name="replyTo">The <see cref="Message"/> to reply to.</param>
/// <param name="message">The message contents.</param>
/// <param name="channelId">The <see cref="ChannelRepresentation.RealId"/> to send to.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
Task SendMessage(Message replyTo, string message, ulong channelId, CancellationToken cancellationToken);
/// <summary>
/// Set the interval at which the provider starts jobs to try to reconnect.
@@ -159,7 +159,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
/// <inheritdoc />
public override Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) => Task.Factory.StartNew(
public override Task SendMessage(Message replyTo, string message, ulong channelId, CancellationToken cancellationToken) => Task.Factory.StartNew(
() =>
{
// IRC doesn't allow newlines
@@ -230,7 +230,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
})));
await SendMessage(
channelId,
null,
String.Format(
CultureInfo.InvariantCulture,
"DM: Deploying revision: {0}{1}{2} BYOND Version: {3}{4}",
@@ -243,11 +243,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
estimatedCompletionTime.HasValue
? $" ETA: {estimatedCompletionTime - DateTimeOffset.UtcNow}"
: String.Empty),
channelId,
cancellationToken);
return (errorMessage, dreamMakerOutput) => SendMessage(
channelId,
null,
$"DM: Deployment {(errorMessage == null ? "complete" : "failed")}!",
channelId,
cancellationToken);
}
@@ -165,7 +165,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
}
/// <inheritdoc />
public abstract Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken);
public abstract Task SendMessage(Message replyTo, string message, ulong channelId, CancellationToken cancellationToken);
/// <inheritdoc />
public abstract Task<Func<string, string, Task>> SendUpdateMessage(
@@ -6,6 +6,7 @@ using Elastic.CommonSchema.Serilog;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Configuration;
using Serilog.Sinks.Elasticsearch;
@@ -82,10 +83,10 @@ namespace Tgstation.Server.Host.Extensions
.WriteTo
.Async(sinkConfiguration =>
{
sinkConfiguration.Console(
outputTemplate: "[{Timestamp:HH:mm:ss}] {Level:w3}: {SourceContext:l} "
var template = "[{Timestamp:HH:mm:ss}] {Level:w3}: {SourceContext:l} "
+ SerilogContextTemplate
+ "|IR:{InstanceReference}){NewLine} {Message:lj}{NewLine}{Exception}");
+ "|IR:{InstanceReference}){NewLine} {Message:lj}{NewLine}{Exception}";
sinkConfiguration.Console(outputTemplate: template, formatProvider: CultureInfo.InvariantCulture);
sinkConfigurationAction?.Invoke(sinkConfiguration);
});
@@ -276,9 +276,12 @@ namespace Tgstation.Server.Host.Setup
}
if (isSqliteDB && !dbExists)
await Task.WhenAll(
console.WriteAsync("Deleting test database file...", true, cancellationToken),
ioManager.DeleteFile(databaseName, cancellationToken));
{
await console.WriteAsync("Deleting test database file...", true, cancellationToken);
if (platformIdentifier.IsWindows)
SqliteConnection.ClearAllPools();
await ioManager.DeleteFile(databaseName, cancellationToken);
}
}
/// <summary>
@@ -68,39 +68,39 @@
<PackageReference Include="Elastic.CommonSchema.Serilog" Version="1.5.3" />
<PackageReference Include="GitLabApiClient" Version="1.8.0" />
<PackageReference Include="LibGit2Sharp" Version="0.27.0-preview-0034" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.15" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.15">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.15" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.15" />
<!-- Required for https://github.com/coverlet-coverage/coverlet/issues/1381 -->
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
<PackageReference Include="Mono.Posix.NETStandard" Version="1.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.2.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
<PackageReference Include="Octokit" Version="3.0.0" />
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="3.0.0" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.8" />
<PackageReference Include="Octokit" Version="5.0.2" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.2" />
<PackageReference Include="Remora.Discord" Version="2022.48.0" />
<PackageReference Include="Remora.Discord" Version="2022.49.0" />
<PackageReference Include="Serilog.Extensions.Logging" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="8.4.1" />
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="6.5.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="6.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.23.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
<PackageReference Include="System.Management" Version="6.0.0" />
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="6.15.1" />
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="6.20.1" />
</ItemGroup>
<ItemGroup>
@@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
@@ -19,11 +19,9 @@ namespace Tgstation.Server.Host.Service.Tests
[TestMethod]
public void TestConstructionAndDisposal()
{
Assert.ThrowsException<ArgumentNullException>(() => new ServerService(null, null, default));
Assert.ThrowsException<ArgumentNullException>(() => new ServerService(null, default));
var mockWatchdogFactory = new Mock<IWatchdogFactory>();
Assert.ThrowsException<ArgumentNullException>(() => new ServerService(mockWatchdogFactory.Object, null, default));
var mockLoggerFactory = new LoggerFactory();
new ServerService(mockWatchdogFactory.Object, mockLoggerFactory, default).Dispose();
new ServerService(mockWatchdogFactory.Object, default).Dispose();
}
[TestMethod]
@@ -38,10 +36,9 @@ namespace Tgstation.Server.Host.Service.Tests
CancellationToken cancellationToken;
mockWatchdog.Setup(x => x.RunAsync(false, args, It.IsAny<CancellationToken>())).Callback((bool x, string[] _, CancellationToken token) => cancellationToken = token).Returns(Task.CompletedTask).Verifiable();
var mockWatchdogFactory = new Mock<IWatchdogFactory>();
var mockLoggerFactory = new LoggerFactory();
mockWatchdogFactory.Setup(x => x.CreateWatchdog(mockLoggerFactory)).Returns(mockWatchdog.Object).Verifiable();
mockWatchdogFactory.Setup(x => x.CreateWatchdog(It.IsNotNull<ILoggerFactory>())).Returns(mockWatchdog.Object).Verifiable();
using (var service = new ServerService(mockWatchdogFactory.Object, mockLoggerFactory, default))
using (var service = new ServerService(mockWatchdogFactory.Object, default))
{
onStart.Invoke(service, new object[] { args });
onStop.Invoke(service, Array.Empty<object>());
@@ -13,13 +13,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -9,8 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -14,14 +14,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
@@ -152,7 +152,7 @@ namespace Tgstation.Server.Tests
Assert.AreEqual(1, serverInformation.SwarmServers.Count);
var controller = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "controller");
Assert.IsNotNull(controller);
Assert.AreEqual(controller.Address, "http://localhost:5011");
Assert.AreEqual(controller.Address, new Uri("http://localhost:5011"));
Assert.IsTrue(controller.Controller);
}
@@ -270,17 +270,17 @@ namespace Tgstation.Server.Tests
var node1 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node1");
Assert.IsNotNull(node1);
Assert.AreEqual(node1.Address, "http://localhost:5012");
Assert.AreEqual(node1.Address, new Uri("http://localhost:5012"));
Assert.IsFalse(node1.Controller);
var node2 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node2");
Assert.IsNotNull(node2);
Assert.AreEqual(node2.Address, "http://localhost:5013");
Assert.AreEqual(node2.Address, new Uri("http://localhost:5013"));
Assert.IsFalse(node2.Controller);
var controller = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "controller");
Assert.IsNotNull(controller);
Assert.AreEqual(controller.Address, "http://localhost:5011");
Assert.AreEqual(controller.Address, new Uri("http://localhost:5011"));
Assert.IsTrue(controller.Controller);
}
@@ -483,17 +483,17 @@ namespace Tgstation.Server.Tests
var node1 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node1");
Assert.IsNotNull(node1);
Assert.AreEqual(node1.Address, "http://localhost:5012");
Assert.AreEqual(node1.Address, new Uri("http://localhost:5012"));
Assert.IsFalse(node1.Controller);
var node2 = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "node2");
Assert.IsNotNull(node2);
Assert.AreEqual(node2.Address, "http://localhost:5013");
Assert.AreEqual(node2.Address, new Uri("http://localhost:5013"));
Assert.IsFalse(node2.Controller);
var controller = serverInformation.SwarmServers.SingleOrDefault(x => x.Identifier == "controller");
Assert.IsNotNull(controller);
Assert.AreEqual(controller.Address, "http://localhost:5011");
Assert.AreEqual(controller.Address, new Uri("http://localhost:5011"));
Assert.IsTrue(controller.Controller);
}
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../../build/Version.props" />
<PropertyGroup>
@@ -8,14 +8,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.1" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="Moq" Version="4.18.4" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="MSTest.TestFramework" Version="3.0.2" />
</ItemGroup>
<ItemGroup>
+6 -36
View File
@@ -75,7 +75,6 @@ namespace ReleaseNotes
Task<Milestone> milestoneTask = null;
var milestoneTaskLock = new object();
var releaseDictionary = new Dictionary<string, List<Tuple<string, int, string>>>(StringComparer.OrdinalIgnoreCase);
var authorizedUsers = new Dictionary<long, Task<bool>>();
bool postControlPanelMessage = false;
@@ -111,44 +110,13 @@ namespace ReleaseNotes
// if (!fullPR.Merged)
//return;
async Task BuildNotesFromComment(string comment, User user)
void BuildNotesFromComment(string comment, User user)
{
if (comment == null)
return;
async Task CommitNotes(string component, List<string> notes)
void CommitNotes(string component, List<string> notes)
{
Task<bool> authTask;
TaskCompletionSource<bool> ourTcs = null;
lock (authorizedUsers)
{
if (!authorizedUsers.TryGetValue(user.Id, out authTask))
{
ourTcs = new TaskCompletionSource<bool>();
authTask = ourTcs.Task;
authorizedUsers.Add(user.Id, authTask);
}
}
if (ourTcs != null)
try
{
//check if the user has access
var perm = String.IsNullOrWhiteSpace(githubToken)
? PermissionLevel.Write
: (await client.Repository.Collaborator.ReviewPermission(RepoOwner, RepoName, user.Login).ConfigureAwait(false)).Permission;
ourTcs.SetResult(perm == PermissionLevel.Write || perm == PermissionLevel.Admin);
}
catch
{
ourTcs.SetResult(false);
throw;
}
var authorized = await authTask.ConfigureAwait(false);
if (!authorized)
return;
lock (releaseDictionary)
{
foreach (var I in notes)
@@ -180,7 +148,7 @@ namespace ReleaseNotes
}
if (trimmedLine.StartsWith("/:cl:", StringComparison.Ordinal))
{
await CommitNotes(targetComponent, notes);
CommitNotes(targetComponent, notes);
targetComponent = null;
notes.Clear();
continue;
@@ -193,7 +161,9 @@ namespace ReleaseNotes
}
var comments = await client.Issue.Comment.GetAllForIssue(RepoOwner, RepoName, fullPR.Number).ConfigureAwait(false);
await Task.WhenAll(BuildNotesFromComment(fullPR.Body, fullPR.User), Task.WhenAll(comments.Select(x => BuildNotesFromComment(x.Body, x.User)))).ConfigureAwait(false);
BuildNotesFromComment(fullPR.Body, fullPR.User);
foreach(var x in comments)
BuildNotesFromComment(x.Body, x.User);
}
var tasks = new List<Task>();
+1 -1
View File
@@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="octokit" Version="3.0.0" />
<PackageReference Include="octokit" Version="5.0.2" />
<PackageReference Include="Octokit.GraphQL" Version="0.2.0-beta" />
</ItemGroup>