Switch Discord integration to use NetCord instead of Discord.Net (#38400)
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
34
Content.Server/Discord/DiscordLink/DiscordSawmillLogger.cs
Normal file
34
Content.Server/Discord/DiscordLink/DiscordSawmillLogger.cs
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user