mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-26 22:48:20 +01:00
Merge pull request #1427 from tgstation/dev [APIDeploy][NugetDeploy][TGSDeploy]
v5.3.0
This commit is contained in:
@@ -51,8 +51,8 @@ jobs:
|
||||
dmapi-build:
|
||||
name: Build DMAPI
|
||||
env:
|
||||
BYOND_MAJOR: 513
|
||||
BYOND_MINOR: 1536
|
||||
BYOND_MAJOR: 515
|
||||
BYOND_MINOR: 1592
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install x86 libc Dependencies
|
||||
|
||||
+5
-5
@@ -3,12 +3,12 @@
|
||||
<!-- Integration tests will ensure they match across the board -->
|
||||
<Import Project="ControlPanelVersion.props" />
|
||||
<PropertyGroup>
|
||||
<TgsCoreVersion>5.2.4</TgsCoreVersion>
|
||||
<TgsCoreVersion>5.3.0</TgsCoreVersion>
|
||||
<TgsConfigVersion>4.4.0</TgsConfigVersion>
|
||||
<TgsApiVersion>9.7.0</TgsApiVersion>
|
||||
<TgsApiLibraryVersion>10.1.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>11.1.0</TgsClientVersion>
|
||||
<TgsDmapiVersion>6.0.5</TgsDmapiVersion>
|
||||
<TgsApiVersion>9.8.0</TgsApiVersion>
|
||||
<TgsApiLibraryVersion>10.2.0</TgsApiLibraryVersion>
|
||||
<TgsClientVersion>11.2.0</TgsClientVersion>
|
||||
<TgsDmapiVersion>6.0.6</TgsDmapiVersion>
|
||||
<TgsInteropVersion>5.3.0</TgsInteropVersion>
|
||||
<TgsHostWatchdogVersion>1.2.0</TgsHostWatchdogVersion>
|
||||
<TgsContainerScriptVersion>1.2.0</TgsContainerScriptVersion>
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// tgstation-server DMAPI
|
||||
|
||||
#define TGS_DMAPI_VERSION "6.0.5"
|
||||
#define TGS_DMAPI_VERSION "6.0.6"
|
||||
|
||||
// All functions and datums outside this document are subject to change with any version and should not be relied on.
|
||||
|
||||
|
||||
@@ -5,4 +5,5 @@ This folder contains all DMAPI code not directly involved in an API.
|
||||
- [_definitions.dm](./definitions.dm) contains defines needed across DMAPI internals.
|
||||
- [core.dm](./core.dm) contains the implementations of the `/world/proc/TgsXXX()` procs. Many map directly to the `/datum/tgs_api` functions. It also contains the /datum selection and setup code.
|
||||
- [datum.dm](./datum.dm) contains the `/datum/tgs_api` declarations that all APIs must implement.
|
||||
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
|
||||
- [tgs_version.dm](./tgs_version.dm) contains the `/datum/tgs_version` definition
|
||||
-
|
||||
|
||||
@@ -99,7 +99,11 @@
|
||||
if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL))
|
||||
TGS_ERROR_LOG("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.")
|
||||
return
|
||||
#if DM_VERSION >= 515
|
||||
call_ext(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
|
||||
#else
|
||||
call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(instance_name, command) //trust no retval
|
||||
#endif
|
||||
return TRUE
|
||||
|
||||
/datum/tgs_api/v3210/OnTopic(T)
|
||||
|
||||
@@ -1,49 +1,38 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Api.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates a chat channel.
|
||||
/// </summary>
|
||||
public class ChatChannel
|
||||
public class ChatChannel : ChatChannelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The channel identifier. Supercedes <see cref="IrcChannel"/> and <see cref="DiscordChannelId"/>.
|
||||
/// For <see cref="ChatProvider.Irc"/>, it's the IRC channel name and optional password colon separated.
|
||||
/// For <see cref="ChatProvider.Discord"/>, it's the stringified Discord channel snowflake.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
|
||||
public string? ChannelData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The IRC channel name. Also potentially contains the channel passsword (if separated by a colon).
|
||||
/// If multiple copies of the same channel with different keys are added to the server, the one that will be used is undefined.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
|
||||
[Obsolete($"Use {nameof(ChannelData)}")]
|
||||
public string? IrcChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Discord channel ID.
|
||||
/// </summary>
|
||||
[Obsolete($"Use {nameof(ChannelData)}")]
|
||||
[ResponseOptions]
|
||||
public ulong? DiscordChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is an admin channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsAdminChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is a watchdog channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsWatchdogChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is an updates channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsUpdatesChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A custom tag users can define to group channels together.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
[StringLength(Limits.MaximumStringLength)]
|
||||
public string? Tag { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace Tgstation.Server.Api.Models.Internal
|
||||
return true;
|
||||
return Provider.Value switch
|
||||
{
|
||||
ChatProvider.Discord => Channels?.Select(x => x.DiscordChannelId.HasValue && x.IrcChannel == null).All(x => x) ?? true,
|
||||
ChatProvider.Irc => Channels?.Select(x => !x.DiscordChannelId.HasValue && x.IrcChannel != null).All(x => x) ?? true,
|
||||
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,
|
||||
_ => throw new InvalidOperationException("Invalid provider type!"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Tgstation.Server.Api.Models.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Base chat channel class.
|
||||
/// </summary>
|
||||
public abstract class ChatChannelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is an admin channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsAdminChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is a watchdog channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsWatchdogChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="ChatChannel"/> is an updates channel.
|
||||
/// </summary>
|
||||
[Required]
|
||||
public bool? IsUpdatesChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A custom tag users can define to group channels together.
|
||||
/// </summary>
|
||||
[ResponseOptions]
|
||||
[StringLength(Limits.MaximumStringLength)]
|
||||
public string? Tag { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<RepositoryUrl>https://github.com/tgstation/tgstation-server</RepositoryUrl>
|
||||
<Copyright>2018-2022</Copyright>
|
||||
<PackageTags>json web api tgstation-server tgstation ss13 byond</PackageTags>
|
||||
<PackageReleaseNotes>Retargeted to netstandard2.0 to support migrator.</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>Added ChannelData field to ChatChannels model.</PackageReleaseNotes>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<CodeAnalysisRuleSet>../../build/analyzers.ruleset</CodeAnalysisRuleSet>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<RepositoryUrl>https://github.com/tgstation/tgstation-server</RepositoryUrl>
|
||||
<Copyright>2018-2022</Copyright>
|
||||
<PackageTags>json web api tgstation-server tgstation ss13 byond client</PackageTags>
|
||||
<PackageReleaseNotes>Retargeted to netstandard2.0 to support migrator. Fixed nullablity of IByondClient.SetActiveVersion's Stream parameter.</PackageReleaseNotes>
|
||||
<PackageReleaseNotes>Updated to API library 10.2.0.</PackageReleaseNotes>
|
||||
<IncludeSymbols>true</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<CodeAnalysisRuleSet>../../build/analyzers.ruleset</CodeAnalysisRuleSet>
|
||||
|
||||
@@ -118,6 +118,32 @@ namespace Tgstation.Server.Host.Service
|
||||
{
|
||||
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())
|
||||
{
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
readonly object synchronizationLock;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ICustomCommandHandler"/> for the <see cref="ChangeChannels(long, IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>.
|
||||
/// The <see cref="ICustomCommandHandler"/> for the <see cref="ChangeChannels(long, IEnumerable{Models.ChatChannel}, CancellationToken)"/>.
|
||||
/// </summary>
|
||||
ICustomCommandHandler customCommandHandler;
|
||||
|
||||
@@ -175,7 +175,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken)
|
||||
public async Task ChangeChannels(long connectionId, IEnumerable<Models.ChatChannel> newChannels, CancellationToken cancellationToken)
|
||||
{
|
||||
if (newChannels == null)
|
||||
throw new ArgumentNullException(nameof(newChannels));
|
||||
@@ -554,7 +554,7 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
async Task RemapProvider(IProvider provider, CancellationToken cancellationToken)
|
||||
{
|
||||
logger.LogTrace("Remapping channels for provider reconnection...");
|
||||
IEnumerable<Api.Models.ChatChannel> channelsToMap;
|
||||
IEnumerable<Models.ChatChannel> channelsToMap;
|
||||
long providerId;
|
||||
lock (providers)
|
||||
providerId = providers.Where(x => x.Value == provider).Select(x => x.Key).First();
|
||||
|
||||
@@ -40,10 +40,10 @@ namespace Tgstation.Server.Host.Components.Chat
|
||||
/// Change chat channels.
|
||||
/// </summary>
|
||||
/// <param name="connectionId">The <see cref="Api.Models.EntityId.Id"/> of the connection.</param>
|
||||
/// <param name="newChannels">An <see cref="IEnumerable{T}"/> of the new list of <see cref="Api.Models.ChatChannel"/>s.</param>
|
||||
/// <param name="newChannels">An <see cref="IEnumerable{T}"/> of the new list of <see cref="Models.ChatChannel"/>s.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task"/> representing the running operation.</returns>
|
||||
Task ChangeChannels(long connectionId, IEnumerable<Api.Models.ChatChannel> newChannels, CancellationToken cancellationToken);
|
||||
Task ChangeChannels(long connectionId, IEnumerable<Models.ChatChannel> newChannels, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Queue a chat <paramref name="message"/> to a given set of <paramref name="channelIds"/>.
|
||||
|
||||
@@ -608,14 +608,14 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
|
||||
protected override async Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(IEnumerable<Models.ChatChannel> channels, CancellationToken cancellationToken)
|
||||
{
|
||||
if (channels == null)
|
||||
throw new ArgumentNullException(nameof(channels));
|
||||
|
||||
var remapRequired = false;
|
||||
|
||||
async Task<Tuple<Api.Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Api.Models.ChatChannel channelFromDB)
|
||||
async Task<Tuple<Models.ChatChannel, ChannelRepresentation>> GetModelChannelFromDBChannel(Models.ChatChannel channelFromDB)
|
||||
{
|
||||
if (!channelFromDB.DiscordChannelId.HasValue)
|
||||
throw new InvalidOperationException("ChatChannel missing DiscordChannelId!");
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
Task InitialConnectionJob { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicate to the provider that at least one <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/> call has successfully completed.
|
||||
/// Indicate to the provider that at least one <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/> call has successfully completed.
|
||||
/// </summary>
|
||||
void InitialMappingComplete();
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in the next available <see cref="Message"/> or <see langword="null"/> if the <see cref="IProvider"/> needed to reconnect.</returns>
|
||||
/// <remarks>Note that private messages will come in the form of <see cref="ChannelRepresentation"/>s not returned in <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>. Do not <see cref="IDisposable.Dispose"/> the <see cref="IProvider"/> on continuations run from the returned <see cref="Task"/>.</remarks>
|
||||
/// <remarks>Note that private messages will come in the form of <see cref="ChannelRepresentation"/>s not returned in <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/>. Do not <see cref="IDisposable.Dispose"/> the <see cref="IProvider"/> on continuations run from the returned <see cref="Task"/>.</remarks>
|
||||
Task<Message> NextMessage(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
@@ -57,8 +57,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
/// </summary>
|
||||
/// <param name="channels">The <see cref="Api.Models.ChatChannel"/>s to map.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="Api.Models.ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken);
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to the <see cref="IProvider"/>.
|
||||
|
||||
@@ -252,8 +252,8 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<ChatChannel> channels,
|
||||
protected override Task<IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<Models.ChatChannel> channels,
|
||||
CancellationToken cancellationToken)
|
||||
=> Task.Factory.StartNew(
|
||||
() =>
|
||||
@@ -285,10 +285,10 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
else
|
||||
client.RfcJoin(channelToJoin);
|
||||
|
||||
return (IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>)channels
|
||||
.Select(apiChannel =>
|
||||
return (IReadOnlyCollection<Tuple<Models.ChatChannel, ChannelRepresentation>>)channels
|
||||
.Select(dbChannel =>
|
||||
{
|
||||
var channelName = apiChannel.GetIrcChannelName();
|
||||
var channelName = dbChannel.GetIrcChannelName();
|
||||
ulong? id = null;
|
||||
if (!channelIdMap.Any(y =>
|
||||
{
|
||||
@@ -303,15 +303,15 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
}
|
||||
|
||||
return Tuple.Create(
|
||||
apiChannel,
|
||||
dbChannel,
|
||||
new ChannelRepresentation
|
||||
{
|
||||
RealId = id.Value,
|
||||
IsAdminChannel = apiChannel.IsAdminChannel == true,
|
||||
IsAdminChannel = dbChannel.IsAdminChannel == true,
|
||||
ConnectionName = address,
|
||||
FriendlyName = channelIdMap[id.Value],
|
||||
IsPrivateChannel = false,
|
||||
Tag = apiChannel.Tag,
|
||||
Tag = dbChannel.Tag,
|
||||
});
|
||||
})
|
||||
.ToList();
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
public void InitialMappingComplete() => initialConnectionTcs.TrySetResult(null);
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<Api.Models.ChatChannel> channels, CancellationToken cancellationToken)
|
||||
public async Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannels(IEnumerable<ChatChannel> channels, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -193,13 +193,13 @@ namespace Tgstation.Server.Host.Components.Chat.Providers
|
||||
protected abstract Task DisconnectImpl(CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of <see cref="MapChannels(IEnumerable{Api.Models.ChatChannel}, CancellationToken)"/>.
|
||||
/// Implementation of <see cref="MapChannels(IEnumerable{ChatChannel}, CancellationToken)"/>.
|
||||
/// </summary>
|
||||
/// <param name="channels">The <see cref="Api.Models.ChatChannel"/>s to map.</param>
|
||||
/// <param name="channels">The <see cref="ChatChannel"/>s to map.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation.</param>
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="Api.Models.ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
protected abstract Task<IReadOnlyCollection<Tuple<Api.Models.ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<Api.Models.ChatChannel> channels,
|
||||
/// <returns>A <see cref="Task{TResult}"/> resulting in a <see cref="IReadOnlyCollection{T}"/> of the <see cref="ChatChannel"/>'s <see cref="ChannelRepresentation"/>s representing <paramref name="channels"/>.</returns>
|
||||
protected abstract Task<IReadOnlyCollection<Tuple<ChatChannel, ChannelRepresentation>>> MapChannelsImpl(
|
||||
IEnumerable<ChatChannel> channels,
|
||||
CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
using Tgstation.Server.Host.Components.Deployment.Remote;
|
||||
using Tgstation.Server.Host.Components.Events;
|
||||
using Tgstation.Server.Host.Database;
|
||||
using Tgstation.Server.Host.IO;
|
||||
using Tgstation.Server.Host.Models;
|
||||
@@ -53,6 +54,11 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// </summary>
|
||||
readonly ILogger<DmbFactory> logger;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="IEventConsumer"/> for <see cref="DmbFactory"/>.
|
||||
/// </summary>
|
||||
readonly IEventConsumer eventConsumer;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Api.Models.Instance"/> for the <see cref="DmbFactory"/>.
|
||||
/// </summary>
|
||||
@@ -69,7 +75,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
readonly IDictionary<long, int> jobLockCounts;
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="Task"/> representing calls to <see cref="CleanJob(CompileJob)"/>.
|
||||
/// <see cref="Task"/> representing calls to <see cref="CleanRegisteredCompileJob(CompileJob)"/>.
|
||||
/// </summary>
|
||||
Task cleanupTask;
|
||||
|
||||
@@ -94,18 +100,21 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// <param name="databaseContextFactory">The value of <see cref="databaseContextFactory"/>.</param>
|
||||
/// <param name="ioManager">The value of <see cref="ioManager"/>.</param>
|
||||
/// <param name="remoteDeploymentManagerFactory">The value of <see cref="remoteDeploymentManagerFactory"/>.</param>
|
||||
/// <param name="eventConsumer">The value of <see cref="eventConsumer"/>.</param>
|
||||
/// <param name="logger">The value of <see cref="logger"/>.</param>
|
||||
/// <param name="metadata">The value of <see cref="metadata"/>.</param>
|
||||
public DmbFactory(
|
||||
IDatabaseContextFactory databaseContextFactory,
|
||||
IIOManager ioManager,
|
||||
IRemoteDeploymentManagerFactory remoteDeploymentManagerFactory,
|
||||
IEventConsumer eventConsumer,
|
||||
ILogger<DmbFactory> logger,
|
||||
Api.Models.Instance metadata)
|
||||
{
|
||||
this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory));
|
||||
this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager));
|
||||
this.remoteDeploymentManagerFactory = remoteDeploymentManagerFactory ?? throw new ArgumentNullException(nameof(remoteDeploymentManagerFactory));
|
||||
this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer));
|
||||
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
|
||||
|
||||
@@ -245,7 +254,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
void CleanupAction()
|
||||
{
|
||||
if (providerSubmitted)
|
||||
CleanJob(compileJob);
|
||||
CleanRegisteredCompileJob(compileJob);
|
||||
}
|
||||
|
||||
var newProvider = new DmbProvider(compileJob, ioManager, CleanupAction);
|
||||
@@ -358,7 +367,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
try
|
||||
{
|
||||
++deleting;
|
||||
await ioManager.DeleteDirectory(x, cancellationToken);
|
||||
await DeleteCompileJobContent(x, cancellationToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -386,19 +395,19 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
/// Delete the <see cref="Api.Models.Internal.CompileJob.DirectoryName"/> of <paramref name="job"/>.
|
||||
/// </summary>
|
||||
/// <param name="job">The <see cref="CompileJob"/> to clean.</param>
|
||||
void CleanJob(CompileJob job)
|
||||
void CleanRegisteredCompileJob(CompileJob job)
|
||||
{
|
||||
async Task HandleCleanup()
|
||||
{
|
||||
var deleteJob = ioManager.DeleteDirectory(job.DirectoryName.ToString(), cleanupCts.Token);
|
||||
var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager(
|
||||
metadata,
|
||||
job);
|
||||
// First kill the GitHub deployment
|
||||
var remoteDeploymentManager = remoteDeploymentManagerFactory.CreateRemoteDeploymentManager(metadata, job);
|
||||
|
||||
// DCT: None available
|
||||
var deploymentJob = remoteDeploymentManager.MarkInactive(job, default);
|
||||
|
||||
var deleteTask = DeleteCompileJobContent(job.DirectoryName.ToString(), cleanupCts.Token);
|
||||
var otherTask = cleanupTask;
|
||||
await Task.WhenAll(otherTask, deleteJob, deploymentJob);
|
||||
await Task.WhenAll(otherTask, deleteTask, deploymentJob);
|
||||
}
|
||||
|
||||
lock (jobLockCounts)
|
||||
@@ -414,5 +423,18 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
logger.LogTrace("Compile job {0} lock count now: {1}", job.Id, decremented);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles cleaning the resources of a <see cref="CompileJob"/>.
|
||||
/// </summary>
|
||||
/// <param name="directory">The directory to cleanup.</param>
|
||||
/// <param name="cancellationToken">The <see cref="CancellationToken"/> for this <see cref="Task"/>.</param>
|
||||
/// <returns>The deletion <see cref="Task"/>.</returns>
|
||||
async Task DeleteCompileJobContent(string directory, CancellationToken cancellationToken)
|
||||
{
|
||||
// Then call the cleanup event, waiting here first
|
||||
await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { ioManager.ResolvePath(directory) }, cancellationToken);
|
||||
await ioManager.DeleteDirectory(directory, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -929,6 +929,7 @@ namespace Tgstation.Server.Host.Components.Deployment
|
||||
try
|
||||
{
|
||||
// DCT: None available
|
||||
await eventConsumer.HandleEvent(EventType.DeploymentCleanup, new List<string> { jobPath }, default);
|
||||
await ioManager.DeleteDirectory(jobPath, default);
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
+1
-1
@@ -283,7 +283,7 @@ namespace Tgstation.Server.Host.Components.Deployment.Remote
|
||||
string remoteRepositoryName,
|
||||
bool updated) => String.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"#### Test Merge {4}{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}{8}",
|
||||
"#### Test Merge {4}{0}{0}<details><summary>Details</summary>{0}{0}##### Server Instance{0}{5}{1}{0}{0}##### Revision{0}Origin: {6}{0}Pull Request: {2}{0}Server: {7}{3}{8}{0}</details>",
|
||||
Environment.NewLine,
|
||||
repositorySettings.ShowTestMergeCommitters.Value
|
||||
? String.Format(
|
||||
|
||||
@@ -155,5 +155,11 @@
|
||||
/// </summary>
|
||||
[EventScript("PreDreamMaker")]
|
||||
PreDreamMaker,
|
||||
|
||||
/// <summary>
|
||||
/// Whenever a deployment folder is deleted from disk. Parameters: Game directory path
|
||||
/// </summary>
|
||||
[EventScript("DeploymentCleanup")]
|
||||
DeploymentCleanup,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ namespace Tgstation.Server.Host.Components
|
||||
databaseContextFactory,
|
||||
gameIoManager,
|
||||
remoteDeploymentManagerFactory,
|
||||
eventConsumer,
|
||||
loggerFactory.CreateLogger<DmbFactory>(),
|
||||
metadata);
|
||||
try
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
@@ -57,16 +58,39 @@ namespace Tgstation.Server.Host.Controllers
|
||||
/// Converts <paramref name="api"/> to a <see cref="ChatChannel"/>.
|
||||
/// </summary>
|
||||
/// <param name="api">The <see cref="Api.Models.ChatChannel"/>. </param>
|
||||
/// <param name="chatProvider">The channel's <see cref="ChatProvider"/>.</param>
|
||||
/// <returns>A <see cref="ChatChannel"/> based on <paramref name="api"/>.</returns>
|
||||
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api) => new ()
|
||||
static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api, ChatProvider chatProvider)
|
||||
{
|
||||
DiscordChannelId = api.DiscordChannelId,
|
||||
IrcChannel = api.IrcChannel,
|
||||
IsAdminChannel = api.IsAdminChannel ?? false,
|
||||
IsWatchdogChannel = api.IsWatchdogChannel ?? false,
|
||||
IsUpdatesChannel = api.IsUpdatesChannel ?? false,
|
||||
Tag = api.Tag,
|
||||
};
|
||||
var result = new Models.ChatChannel
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
DiscordChannelId = api.DiscordChannelId,
|
||||
IrcChannel = api.IrcChannel,
|
||||
#pragma warning restore CS0618
|
||||
IsAdminChannel = api.IsAdminChannel ?? false,
|
||||
IsWatchdogChannel = api.IsWatchdogChannel ?? false,
|
||||
IsUpdatesChannel = api.IsUpdatesChannel ?? false,
|
||||
Tag = api.Tag,
|
||||
};
|
||||
|
||||
if (api.ChannelData != null)
|
||||
{
|
||||
switch (chatProvider)
|
||||
{
|
||||
case ChatProvider.Discord:
|
||||
result.DiscordChannelId = ulong.Parse(api.ChannelData, CultureInfo.InvariantCulture);
|
||||
break;
|
||||
case ChatProvider.Irc:
|
||||
result.IrcChannel = api.ChannelData;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Invalid chat provider: {chatProvider}");
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new chat bot <paramref name="model"/>.
|
||||
@@ -106,7 +130,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
Name = model.Name,
|
||||
ConnectionString = model.ConnectionString,
|
||||
Enabled = model.Enabled,
|
||||
Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List<Models.ChatChannel>(), // important that this isn't null
|
||||
Channels = model.Channels?.Select(x => ConvertApiChatChannel(x, model.Provider.Value)).ToList() ?? new List<Models.ChatChannel>(), // important that this isn't null
|
||||
InstanceId = Instance.Id.Value,
|
||||
Provider = model.Provider,
|
||||
ReconnectionInterval = model.ReconnectionInterval,
|
||||
@@ -319,7 +343,7 @@ namespace Tgstation.Server.Host.Controllers
|
||||
DatabaseContext.ChatChannels.RemoveRange(current.Channels);
|
||||
if (hasChannels)
|
||||
{
|
||||
var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList();
|
||||
var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x, model.Provider ?? current.Provider.Value)).ToList();
|
||||
DatabaseContext.ChatChannels.AddRange(dbChannels);
|
||||
current.Channels = dbChannels;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Extensions
|
||||
{
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Tgstation.Server.Host.Models
|
||||
/// <inheritdoc />
|
||||
public ChatBotResponse ToApi() => new ChatBotResponse
|
||||
{
|
||||
Channels = Channels.Select(x => x.ToApi()).ToList(),
|
||||
Channels = Channels.Select(x => x.ToApi(Provider.Value)).ToList(),
|
||||
ConnectionString = ConnectionString,
|
||||
Enabled = Enabled,
|
||||
Provider = Provider,
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
namespace Tgstation.Server.Host.Models
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using Tgstation.Server.Api.Models;
|
||||
using Tgstation.Server.Api.Models.Internal;
|
||||
|
||||
namespace Tgstation.Server.Host.Models
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public sealed class ChatChannel : Api.Models.ChatChannel, IApiTransformable<Api.Models.ChatChannel>
|
||||
public sealed class ChatChannel : ChatChannelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// The row Id.
|
||||
@@ -9,23 +14,41 @@
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Api.Models.EntityId.Id"/>.
|
||||
/// The <see cref="EntityId.Id"/>.
|
||||
/// </summary>
|
||||
public long ChatSettingsId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Api.Models.ChatChannel.IrcChannel"/>.
|
||||
/// </summary>
|
||||
[StringLength(Limits.MaximumIndexableStringLength, MinimumLength = 1)]
|
||||
public string IrcChannel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// See <see cref="Api.Models.ChatChannel.DiscordChannelId"/>.
|
||||
/// </summary>
|
||||
public ulong? DiscordChannelId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ChatBot"/>.
|
||||
/// </summary>
|
||||
public ChatBot ChatSettings { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel
|
||||
/// <summary>
|
||||
/// Convert to a <see cref="Api.Models.ChatChannel"/>.
|
||||
/// </summary>
|
||||
/// <param name="chatProvider">The channel's <see cref="ChatProvider"/>.</param>
|
||||
/// <returns>The converted <see cref="Api.Models.ChatChannel"/>.</returns>
|
||||
public Api.Models.ChatChannel ToApi(ChatProvider chatProvider) => new Api.Models.ChatChannel
|
||||
{
|
||||
ChannelData = chatProvider == ChatProvider.Discord ? DiscordChannelId.ToString() : IrcChannel,
|
||||
#pragma warning disable CS0618
|
||||
IrcChannel = IrcChannel,
|
||||
DiscordChannelId = DiscordChannelId,
|
||||
#pragma warning restore CS0618
|
||||
IsAdminChannel = IsAdminChannel,
|
||||
IsWatchdogChannel = IsWatchdogChannel,
|
||||
IsUpdatesChannel = IsUpdatesChannel,
|
||||
IrcChannel = IrcChannel,
|
||||
Tag = Tag,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
{
|
||||
sealed class ByondTest : JobsRequiredTest
|
||||
{
|
||||
public static readonly Version TestVersion = new (513, 1536);
|
||||
public static readonly Version TestVersion = new (515, 1592);
|
||||
|
||||
readonly IByondClient byondClient;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -81,7 +81,10 @@ namespace Tgstation.Server.Tests.Instance
|
||||
IsUpdatesChannel = true,
|
||||
IsWatchdogChannel = true,
|
||||
Tag = "butt2",
|
||||
IrcChannel = channelId
|
||||
ChannelData = channelId,
|
||||
#pragma warning disable CS0618
|
||||
IrcChannel = "should_not_be_this!!!JHF*WW(#*(*$&(#*@))("
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
}, cancellationToken);
|
||||
@@ -93,8 +96,11 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Assert.AreEqual(true, updatedBot.Channels.First().IsUpdatesChannel);
|
||||
Assert.AreEqual(true, updatedBot.Channels.First().IsWatchdogChannel);
|
||||
Assert.AreEqual("butt2", updatedBot.Channels.First().Tag);
|
||||
#pragma warning disable CS0618
|
||||
Assert.AreEqual(channelId, updatedBot.Channels.First().IrcChannel);
|
||||
Assert.IsNull(updatedBot.Channels.First().DiscordChannelId);
|
||||
#pragma warning restore CS0618
|
||||
Assert.AreEqual(channelId, updatedBot.Channels.First().ChannelData);
|
||||
}
|
||||
|
||||
async Task RunDiscord(CancellationToken cancellationToken)
|
||||
@@ -138,17 +144,6 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Assert.AreEqual(true, updatedBot.Enabled);
|
||||
|
||||
var channelId = UInt64.Parse(Environment.GetEnvironmentVariable("TGS_TEST_DISCORD_CHANNEL"));
|
||||
firstBot.Channels = new List<ChatChannel>
|
||||
{
|
||||
new ChatChannel
|
||||
{
|
||||
IsAdminChannel = true,
|
||||
IsUpdatesChannel = true,
|
||||
IsWatchdogChannel = true,
|
||||
Tag = "butt",
|
||||
DiscordChannelId = channelId
|
||||
}
|
||||
};
|
||||
|
||||
updatedBot = await chatClient.Update(new ChatBotUpdateRequest
|
||||
{
|
||||
@@ -161,7 +156,10 @@ namespace Tgstation.Server.Tests.Instance
|
||||
IsUpdatesChannel = true,
|
||||
IsWatchdogChannel = true,
|
||||
Tag = "butt",
|
||||
DiscordChannelId = channelId
|
||||
ChannelData = channelId.ToString(),
|
||||
#pragma warning disable CS0618
|
||||
DiscordChannelId = 1234,
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
}
|
||||
}, cancellationToken);
|
||||
@@ -173,8 +171,11 @@ namespace Tgstation.Server.Tests.Instance
|
||||
Assert.AreEqual(true, updatedBot.Channels.First().IsUpdatesChannel);
|
||||
Assert.AreEqual(true, updatedBot.Channels.First().IsWatchdogChannel);
|
||||
Assert.AreEqual("butt", updatedBot.Channels.First().Tag);
|
||||
#pragma warning disable CS0618
|
||||
Assert.AreEqual(channelId, updatedBot.Channels.First().DiscordChannelId);
|
||||
Assert.IsNull(updatedBot.Channels.First().IrcChannel);
|
||||
#pragma warning restore CS0618
|
||||
Assert.AreEqual(channelId.ToString(), updatedBot.Channels.First().ChannelData);
|
||||
}
|
||||
|
||||
public async Task RunPostTest(CancellationToken cancellationToken)
|
||||
@@ -215,7 +216,7 @@ namespace Tgstation.Server.Tests.Instance
|
||||
IsUpdatesChannel = false,
|
||||
IsWatchdogChannel = true,
|
||||
Tag = "butt",
|
||||
DiscordChannelId = discordBotReq.Channels.First().DiscordChannelId
|
||||
ChannelData = discordBotReq.Channels.First().ChannelData
|
||||
});
|
||||
|
||||
await ApiAssert.ThrowsException<ApiConflictException>(() => chatClient.Update(discordBotReq, cancellationToken), ErrorCode.ChatBotMaxChannels);
|
||||
|
||||
@@ -204,13 +204,9 @@ static class Program
|
||||
IsWatchdogChannel = providerInfo.WatchdogChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
|
||||
IsAdminChannel = providerInfo.AdminChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
|
||||
IsUpdatesChannel = providerInfo.DevChannels.Any(x => NormalizeChannelId(x) == channelIdentifier),
|
||||
ChannelData = channelIdentifier,
|
||||
};
|
||||
|
||||
if (isDiscordProvider)
|
||||
newChatChannel.DiscordChannelId = ulong.Parse(channelIdentifier);
|
||||
else
|
||||
newChatChannel.IrcChannel = channelIdentifier;
|
||||
|
||||
createRequest.Channels.Add(newChatChannel);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user