-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;
/// 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>
/// <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!;
/// <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)
{
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)
{
_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;
}
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();
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
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
--- /dev/null
+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));
+ }
+}