'Unable to resolve service for type 'Victoria.LavaNode
1[TheSwarmManager.Modules.XLavaPlayer]' while attempting to activate 'TheSwarmManager.Services.AudioHandler'.'`
So, I'm creating a discord bot using C#, Discord.NET and Victoria library for playing music. My services are being configured here:
private ServiceProvider ConfigureServices()
{
return new ServiceCollection()
.AddSingleton<DiscordSocketClient>()
.AddSingleton(x => new DiscordSocketClient(new DiscordSocketConfig
{
GatewayIntents = Discord.GatewayIntents.All,
AlwaysDownloadUsers = true,
}))
.AddSingleton<InteractionService>()
.AddSingleton<InteractionHandler>()
.AddSingleton<CommandService>()
.AddSingleton<PrefixHandler>()
.AddLavaNode()
.AddSingleton(new LavaConfig())
.AddSingleton<AudioHandler>()
.AddSingleton(_config)
.BuildServiceProvider();
}
The line that's causing exception is this:
_services = ConfigureServices();
_audioService = _services.GetRequiredService<AudioHandler>(); // <- this one
And here's the XLavaPlayer.cs file:
using Discord;
using Victoria;
namespace TheSwarmManager.Modules {
public class XLavaPlayer : LavaPlayer {
public string ChannelName { get; }
public XLavaPlayer(LavaSocket lavaSocket, IVoiceChannel voiceChannel, ITextChannel textChannel)
: base(lavaSocket, voiceChannel, textChannel) {
ChannelName = textChannel.Name;
}
}
}
I tried to do something with AddScope but I'm kinda new, so I didn't go any further.
EDIT 1: I also think there might be something wrong with my AudioHandler, here's the code:
using System.Collections.Concurrent;
using Microsoft.Extensions.Logging;
using Victoria;
using Victoria.Enums;
using Victoria.EventArgs;
using TheSwarmManager.Modules;
namespace TheSwarmManager.Services {
public sealed class AudioHandler {
private readonly LavaNode<XLavaPlayer> _lavaNode;
private readonly ILogger _logger;
public readonly HashSet<ulong> VoteQueue;
private readonly ConcurrentDictionary<ulong, CancellationTokenSource> _disconnectTokens;
public Func<TrackEndedEventArgs, Task> TrackEnded { get; internal set; }
public AudioHandler(LavaNode<XLavaPlayer> lavaNode, ILoggerFactory loggerFactory) {
_lavaNode = lavaNode;
_logger = loggerFactory.CreateLogger<LavaNode>();
_disconnectTokens = new ConcurrentDictionary<ulong, CancellationTokenSource>();
_lavaNode.OnPlayerUpdated += OnPlayerUpdated;
_lavaNode.OnStatsReceived += OnStatsReceived;
_lavaNode.OnTrackEnded += OnTrackEnded;
_lavaNode.OnTrackStarted += OnTrackStarted;
_lavaNode.OnTrackException += OnTrackException;
_lavaNode.OnTrackStuck += OnTrackStuck;
_lavaNode.OnWebSocketClosed += OnWebSocketClosed;
VoteQueue = new HashSet<ulong>();
}
private Task OnPlayerUpdated(PlayerUpdateEventArgs arg) {
_logger.LogInformation($"Track update received for {arg.Track.Title}: {arg.Position}");
return Task.CompletedTask;
}
private Task OnStatsReceived(StatsEventArgs arg) {
_logger.LogInformation($"Lavalink has been up for {arg.Uptime}.");
return Task.CompletedTask;
}
private async Task OnTrackStarted(TrackStartEventArgs arg) {
await arg.Player.TextChannel.SendMessageAsync($"Now playing: {arg.Track.Title}");
if (!_disconnectTokens.TryGetValue(arg.Player.VoiceChannel.Id, out var value)) {
return;
}
if (value.IsCancellationRequested) {
return;
}
value.Cancel(true);
await arg.Player.TextChannel.SendMessageAsync("Auto disconnect has been cancelled!");
}
private async Task OnTrackEnded(TrackEndedEventArgs args) {
if (args.Reason != TrackEndReason.Finished) {
return;
}
var player = args.Player;
if (!player.Queue.TryDequeue(out var lavaTrack)) {
await player.TextChannel.SendMessageAsync("Queue completed! Please add more tracks to rock n' roll!");
_ = InitiateDisconnectAsync(args.Player, TimeSpan.FromSeconds(10));
return;
}
if (lavaTrack is null) {
await player.TextChannel.SendMessageAsync("Next item in queue is not a track.");
return;
}
await args.Player.PlayAsync(lavaTrack);
await args.Player.TextChannel.SendMessageAsync(
$"{args.Reason}: {args.Track.Title}\nNow playing: {lavaTrack.Title}");
}
private async Task InitiateDisconnectAsync(LavaPlayer player, TimeSpan timeSpan) {
if (!_disconnectTokens.TryGetValue(player.VoiceChannel.Id, out var value)) {
value = new CancellationTokenSource();
_disconnectTokens.TryAdd(player.VoiceChannel.Id, value);
}
else if (value.IsCancellationRequested) {
_disconnectTokens.TryUpdate(player.VoiceChannel.Id, new CancellationTokenSource(), value);
value = _disconnectTokens[player.VoiceChannel.Id];
}
await player.TextChannel.SendMessageAsync($"Auto disconnect initiated! Disconnecting in {timeSpan}...");
var isCancelled = SpinWait.SpinUntil(() => value.IsCancellationRequested, timeSpan);
if (isCancelled) {
return;
}
await _lavaNode.LeaveAsync(player.VoiceChannel);
await player.TextChannel.SendMessageAsync("Invite me again sometime, sugar.");
}
private async Task OnTrackException(TrackExceptionEventArgs arg) {
_logger.LogError($"Track {arg.Track.Title} threw an exception. Please check Lavalink console/logs.");
arg.Player.Queue.Enqueue(arg.Track);
await arg.Player.TextChannel.SendMessageAsync(
$"{arg.Track.Title} has been re-added to queue after throwing an exception.");
}
private async Task OnTrackStuck(TrackStuckEventArgs arg) {
_logger.LogError(
$"Track {arg.Track.Title} got stuck for {arg.Threshold}ms. Please check Lavalink console/logs.");
arg.Player.Queue.Enqueue(arg.Track);
await arg.Player.TextChannel.SendMessageAsync(
$"{arg.Track.Title} has been re-added to queue after getting stuck.");
}
private Task OnWebSocketClosed(WebSocketClosedEventArgs arg) {
_logger.LogCritical($"Discord WebSocket connection closed with following reason: {arg.Reason}");
return Task.CompletedTask;
}
}
}
I'm stupid enough not to update my global variables in my main file:
private readonly LavaNode _lavaNode; // OLD
private readonly LavaNode<XLavaPlayer> _lavaNode; // NEW
_lavaNode = _services.GetRequiredService<LavaNode>(); // OLD
_lavaNode = _services.GetRequiredService<LavaNode<XLavaPlayer>>(); // NEW
And finally ServiceCollection:
.AddSingleton<LavaNode>() // OLD
.AddSingleton<LavaNode<XLavaPlayer>>() // NEW