]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Switch Discord integration to use NetCord instead of Discord.Net (#38400)
authorSimon <63975668+Simyon264@users.noreply.github.com>
Tue, 17 Jun 2025 17:03:24 +0000 (19:03 +0200)
committerGitHub <noreply@github.com>
Tue, 17 Jun 2025 17:03:24 +0000 (19:03 +0200)
Content.Packaging/ServerPackaging.cs
Content.Server/Content.Server.csproj
Content.Server/Discord/DiscordLink/DiscordChatLink.cs
Content.Server/Discord/DiscordLink/DiscordLink.cs
Content.Server/Discord/DiscordLink/DiscordSawmillLogger.cs [new file with mode: 0644]
Directory.Packages.props

index 947a12601c537c2b05383538fa23fc863b19f900..91ebc412261d7e0d117493467f116bc250d2aa2e 100644 (file)
@@ -47,7 +47,7 @@ public static class ServerPackaging
         // Python script had Npgsql. though we want Npgsql.dll as well soooo
         "Npgsql",
         "Microsoft",
-        "Discord",
+        "NetCord",
     };
 
     private static readonly List<string> ServerNotExtraAssemblies = new()
index a0ac12163fc6861c30841bcab644f57fd0425d1c..ffc4df7ed1662f515c7890a0d5a560cb5761bd5e 100644 (file)
@@ -14,8 +14,8 @@
     <ServerGarbageCollection>true</ServerGarbageCollection>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="Discord.Net" />
     <PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
+    <PackageReference Include="NetCord" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\Content.Packaging\Content.Packaging.csproj" />
index 5a2c064e4d8ecf8ae3234aa60c7b36373b10eb28..358bc4ab3ec14145536064ab582e727ec76e3beb 100644 (file)
@@ -1,8 +1,7 @@
-using System.Threading.Tasks;
-using Content.Server.Chat.Managers;
+using Content.Server.Chat.Managers;
 using Content.Shared.CCVar;
 using Content.Shared.Chat;
-using Discord.WebSocket;
+using NetCord.Gateway;
 using Robust.Shared.Asynchronous;
 using Robust.Shared.Configuration;
 
@@ -59,18 +58,18 @@ public sealed class DiscordChatLink : IPostInjectInit
         _adminChannelId = ulong.Parse(channelId);
     }
 
-    private void OnMessageReceived(SocketMessage message)
+    private void OnMessageReceived(Message message)
     {
         if (message.Author.IsBot)
             return;
 
         var contents = message.Content.ReplaceLineEndings(" ");
 
-        if (message.Channel.Id == _oocChannelId)
+        if (message.ChannelId == _oocChannelId)
         {
             _taskManager.RunOnMainThread(() => _chatManager.SendHookOOC(message.Author.Username, contents));
         }
-        else if (message.Channel.Id == _adminChannelId)
+        else if (message.ChannelId == _adminChannelId)
         {
             _taskManager.RunOnMainThread(() => _chatManager.SendHookAdmin(message.Author.Username, contents));
         }
index abf3189a993a0373bbc025508336eef46e185403..cbfe12f180fcc54307a0d7fa3c46de19bc279f0d 100644 (file)
@@ -1,12 +1,9 @@
-using System.Linq;
-using System.Threading.Tasks;
+using System.Threading.Tasks;
 using Content.Shared.CCVar;
-using Discord;
-using Discord.WebSocket;
+using NetCord;
+using NetCord.Gateway;
+using NetCord.Rest;
 using Robust.Shared.Configuration;
-using Robust.Shared.Reflection;
-using Robust.Shared.Utility;
-using LogMessage = Discord.LogMessage;
 
 namespace Content.Server.Discord.DiscordLink;
 
@@ -28,7 +25,7 @@ public sealed class CommandReceivedEventArgs
     /// Information about the message that the command was received from. This includes the message content, author, etc.
     /// Use this to reply to the message, delete it, etc.
     /// </summary>
-    public SocketMessage Message { get; init; } = default!;
+    public Message Message { get; init; } = default!;
 }
 
 /// <summary>
@@ -45,7 +42,7 @@ public sealed class DiscordLink : IPostInjectInit
     /// <remarks>
     ///     This should not be used directly outside of DiscordLink. So please do not make it public. Use the methods in this class instead.
     /// </remarks>
-    private DiscordSocketClient? _client;
+    private GatewayClient? _client;
     private ISawmill _sawmill = default!;
     private ISawmill _sawmillLog = default!;
 
@@ -67,7 +64,7 @@ public sealed class DiscordLink : IPostInjectInit
     /// <summary>
     ///     Event that is raised when a message is received from Discord. This is raised for every message, including commands.
     /// </summary>
-    public event Action<SocketMessage>? OnMessageReceived;
+    public event Action<Message>? OnMessageReceived;
 
     public void RegisterCommandCallback(Action<CommandReceivedEventArgs> callback, string command)
     {
@@ -101,33 +98,34 @@ public sealed class DiscordLink : IPostInjectInit
             return;
         }
 
-        _client = new DiscordSocketClient(new DiscordSocketConfig()
+        _client = new GatewayClient(new BotToken(token), new GatewayClientConfiguration()
         {
-            GatewayIntents = GatewayIntents.Guilds
-                             | GatewayIntents.GuildMembers
+            Intents = GatewayIntents.Guilds
+                             | GatewayIntents.GuildUsers
                              | GatewayIntents.GuildMessages
                              | GatewayIntents.MessageContent
                              | GatewayIntents.DirectMessages,
+            Logger = new DiscordSawmillLogger(_sawmillLog),
         });
-        _client.Log += Log;
-        _client.MessageReceived += OnCommandReceivedInternal;
-        _client.MessageReceived += OnMessageReceivedInternal;
+        _client.MessageCreate += OnCommandReceivedInternal;
+        _client.MessageCreate += OnMessageReceivedInternal;
 
         _botToken = token;
         // Since you cannot change the token while the server is running / the DiscordLink is initialized,
         // we can just set the token without updating it every time the cvar changes.
 
-        _client.Ready += () =>
+        _client.Ready += _ =>
         {
             _sawmill.Info("Discord client ready.");
-            return Task.CompletedTask;
+            return default;
         };
 
         Task.Run(async () =>
         {
             try
             {
-                await LoginAsync(token);
+                await _client.StartAsync();
+                _sawmill.Info("Connected to Discord.");
             }
             catch (Exception e)
             {
@@ -143,12 +141,11 @@ public sealed class DiscordLink : IPostInjectInit
             _sawmill.Info("Disconnecting from Discord.");
 
             // Unsubscribe from the events.
-            _client.MessageReceived -= OnCommandReceivedInternal;
-            _client.MessageReceived -= OnMessageReceivedInternal;
+            _client.MessageCreate -= OnCommandReceivedInternal;
+            _client.MessageCreate -= OnMessageReceivedInternal;
 
-            await _client.LogoutAsync();
-            await _client.StopAsync();
-            await _client.DisposeAsync();
+            await _client.CloseAsync();
+            _client.Dispose();
             _client = null;
         }
 
@@ -172,45 +169,12 @@ public sealed class DiscordLink : IPostInjectInit
         BotPrefix = prefix;
     }
 
-    private async Task LoginAsync(string token)
-    {
-        DebugTools.Assert(_client != null);
-        DebugTools.Assert(_client.LoginState == LoginState.LoggedOut);
-
-        await _client.LoginAsync(TokenType.Bot, token);
-        await _client.StartAsync();
-
-
-        _sawmill.Info("Connected to Discord.");
-    }
-
-    private string FormatLog(LogMessage msg)
-    {
-        return msg.Exception is null
-            ? $"{msg.Source}: {msg.Message}"
-            : $"{msg.Source}: {msg.Message}\n{msg.Exception}";
-    }
-
-    private Task Log(LogMessage msg)
-    {
-        var logLevel = msg.Severity switch
-        {
-            LogSeverity.Critical => LogLevel.Fatal,
-            LogSeverity.Error => LogLevel.Error,
-            LogSeverity.Warning => LogLevel.Warning,
-            _ => LogLevel.Debug
-        };
-
-        _sawmillLog.Log(logLevel, FormatLog(msg));
-        return Task.CompletedTask;
-    }
-
-    private Task OnCommandReceivedInternal(SocketMessage message)
+    private ValueTask OnCommandReceivedInternal(Message message)
     {
         var content = message.Content;
         // If the message doesn't start with the bot prefix, ignore it.
         if (!content.StartsWith(BotPrefix))
-            return Task.CompletedTask;
+            return ValueTask.CompletedTask;
 
         // Split the message into the command and the arguments.
         var trimmedInput = content[BotPrefix.Length..].Trim();
@@ -236,13 +200,13 @@ public sealed class DiscordLink : IPostInjectInit
             Arguments = arguments,
             Message = message,
         });
-        return Task.CompletedTask;
+        return ValueTask.CompletedTask;
     }
 
-    private Task OnMessageReceivedInternal(SocketMessage message)
+    private ValueTask OnMessageReceivedInternal(Message message)
     {
         OnMessageReceived?.Invoke(message);
-        return Task.CompletedTask;
+        return ValueTask.CompletedTask;
     }
 
     #region Proxy methods
@@ -257,14 +221,18 @@ public sealed class DiscordLink : IPostInjectInit
             return;
         }
 
-        var channel = _client.GetChannel(channelId) as IMessageChannel;
+        var channel = await _client.Rest.GetChannelAsync(channelId) as TextChannel;
         if (channel == null)
         {
             _sawmill.Error("Tried to send a message to Discord but the channel {Channel} was not found.", channel);
             return;
         }
 
-        await channel.SendMessageAsync(message, allowedMentions: AllowedMentions.None);
+        await channel.SendMessageAsync(new MessageProperties()
+        {
+            AllowedMentions = AllowedMentionsProperties.None,
+            Content = message,
+        });
     }
 
     #endregion
diff --git a/Content.Server/Discord/DiscordLink/DiscordSawmillLogger.cs b/Content.Server/Discord/DiscordLink/DiscordSawmillLogger.cs
new file mode 100644 (file)
index 0000000..c6ca6ac
--- /dev/null
@@ -0,0 +1,34 @@
+using NetCord.Logging;
+using NLogLevel = NetCord.Logging.LogLevel;
+using LogLevel = Robust.Shared.Log.LogLevel;
+
+namespace Content.Server.Discord.DiscordLink;
+
+public sealed class DiscordSawmillLogger(ISawmill sawmill) : IGatewayLogger, IRestLogger, IVoiceLogger
+{
+    private static LogLevel GetLogLevel(NLogLevel logLevel)
+    {
+        return logLevel switch
+        {
+            NLogLevel.Critical => LogLevel.Fatal,
+            NLogLevel.Error => LogLevel.Error,
+            NLogLevel.Warning => LogLevel.Warning,
+            _ => LogLevel.Debug,
+        };
+    }
+
+    void IGatewayLogger.Log<TState>(NetCord.Logging.LogLevel logLevel, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
+    {
+        sawmill.Log(GetLogLevel(logLevel), exception, formatter(state, exception));
+    }
+
+    void IRestLogger.Log<TState>(NetCord.Logging.LogLevel logLevel, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
+    {
+        sawmill.Log(GetLogLevel(logLevel), exception, formatter(state, exception));
+    }
+
+    void IVoiceLogger.Log<TState>(NetCord.Logging.LogLevel logLevel, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
+    {
+        sawmill.Log(GetLogLevel(logLevel), exception, formatter(state, exception));
+    }
+}
index d18f894bf760165258eebada5c5efb33493639fb..b2f8d0d8444a690c7d126b486319feaafb411209 100644 (file)
@@ -7,16 +7,16 @@
     <PackageVersion Remove="Npgsql.EntityFrameworkCore.PostgreSQL" />
     <PackageVersion Remove="Microsoft.EntityFrameworkCore.Design" />
     <PackageVersion Include="CsvHelper" Version="33.0.1" />
-    <PackageVersion Include="Discord.Net" Version="3.16.0" />
     <PackageVersion Include="ImGui.NET" Version="1.87.3" />
     <PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
       <PrivateAssets>all</PrivateAssets>
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageVersion>
     <PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="9.0.1" />
+    <PackageVersion Include="NetCord" Version="1.0.0-alpha.388" />
     <PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
     <PackageVersion Include="OpenTK" Version="4.7.2" />
     <PackageVersion Include="Veldrid" Version="4.8.0" />
     <PackageVersion Include="Veldrid.SPIRV" Version="1.0.15" />
   </ItemGroup>
-</Project>
+</Project>
\ No newline at end of file