changing folder structure
This commit is contained in:
		
							
								
								
									
										64
									
								
								Kasbot.APP/Services/CommandHandlingService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Kasbot.APP/Services/CommandHandlingService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.Commands;
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using Kasbot.Extensions;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Kasbot.Services
 | 
			
		||||
{
 | 
			
		||||
    public class CommandHandlingService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly CommandService _commands;
 | 
			
		||||
        private readonly DiscordShardedClient _discord;
 | 
			
		||||
        private readonly IServiceProvider _services;
 | 
			
		||||
 | 
			
		||||
        private readonly string CommandPrefix = "!";
 | 
			
		||||
 | 
			
		||||
        public CommandHandlingService(IServiceProvider services)
 | 
			
		||||
        {
 | 
			
		||||
            _commands = services.GetRequiredService<CommandService>();
 | 
			
		||||
            _discord = services.GetRequiredService<DiscordShardedClient>();
 | 
			
		||||
            _services = services;
 | 
			
		||||
 | 
			
		||||
            _commands.CommandExecuted += CommandExecutedAsync;
 | 
			
		||||
            _discord.MessageReceived += MessageReceivedAsync;
 | 
			
		||||
 | 
			
		||||
            CommandPrefix = Environment.GetEnvironmentVariable("COMMAND_PREFIX") ?? "!";
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task InitializeAsync()
 | 
			
		||||
        {
 | 
			
		||||
            await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task MessageReceivedAsync(SocketMessage rawMessage)
 | 
			
		||||
        {
 | 
			
		||||
            if (!(rawMessage is SocketUserMessage message))
 | 
			
		||||
                return;
 | 
			
		||||
            if (message.Source != MessageSource.User)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            var argPos = 0;
 | 
			
		||||
 | 
			
		||||
            //Check if the message sent has the specified prefix
 | 
			
		||||
            if (!message.HasStringPrefix(CommandPrefix, ref argPos)) return;
 | 
			
		||||
 | 
			
		||||
            var context = new ShardedCommandContext(_discord, message);
 | 
			
		||||
            await _commands.ExecuteAsync(context, argPos, _services);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result)
 | 
			
		||||
        {
 | 
			
		||||
            if (!command.IsSpecified)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (result.IsSuccess)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            await context.Channel.SendTemporaryMessageAsync($"Error: {result}");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								Kasbot.APP/Services/Internal/AudioService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								Kasbot.APP/Services/Internal/AudioService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
using Discord.Audio;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
namespace Kasbot.Services.Internal
 | 
			
		||||
{
 | 
			
		||||
    public class AudioService
 | 
			
		||||
    {
 | 
			
		||||
        public AudioService() { }
 | 
			
		||||
 | 
			
		||||
        public void StartAudioTask(Stream inputStream, IAudioClient outputAudioClient, Action<Stream> onStartAudio, Action<Task> onFinish)
 | 
			
		||||
        {
 | 
			
		||||
            var ffmpeg = CreateFFmpeg();
 | 
			
		||||
 | 
			
		||||
            Task stdin = new Task(() =>
 | 
			
		||||
            {
 | 
			
		||||
                using (var output = inputStream)
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        output.CopyTo(ffmpeg.StandardInput.BaseStream);
 | 
			
		||||
                        ffmpeg.StandardInput.Close();
 | 
			
		||||
                    }
 | 
			
		||||
                    catch { }
 | 
			
		||||
                    finally
 | 
			
		||||
                    {
 | 
			
		||||
                        output.Flush();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            Task stdout = new Task(() =>
 | 
			
		||||
            {
 | 
			
		||||
                using (var output = ffmpeg.StandardOutput.BaseStream)
 | 
			
		||||
                using (var discord = outputAudioClient.CreatePCMStream(AudioApplication.Music))
 | 
			
		||||
                {
 | 
			
		||||
                    try
 | 
			
		||||
                    {
 | 
			
		||||
                        onStartAudio.Invoke(ffmpeg.StandardOutput.BaseStream);
 | 
			
		||||
                        output.CopyTo(discord);
 | 
			
		||||
                    }
 | 
			
		||||
                    catch { }
 | 
			
		||||
                    finally
 | 
			
		||||
                    {
 | 
			
		||||
                        discord.Flush();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            stdin.Start();
 | 
			
		||||
            stdout.Start();
 | 
			
		||||
 | 
			
		||||
            stdin.ContinueWith(onFinish);
 | 
			
		||||
            stdout.ContinueWith(onFinish);
 | 
			
		||||
 | 
			
		||||
            Task.WaitAll(stdin, stdout);
 | 
			
		||||
 | 
			
		||||
            ffmpeg.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Process CreateFFmpeg()
 | 
			
		||||
        {
 | 
			
		||||
            var process = Process.Start(new ProcessStartInfo
 | 
			
		||||
            {
 | 
			
		||||
                FileName = "ffmpeg",
 | 
			
		||||
                Arguments = $"-hide_banner -loglevel panic -i pipe:0 -ac 2 -f s16le -ar 48000 pipe:1",
 | 
			
		||||
                UseShellExecute = false,
 | 
			
		||||
                RedirectStandardInput = true,
 | 
			
		||||
                RedirectStandardOutput = true
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (process == null || process.HasExited)
 | 
			
		||||
            {
 | 
			
		||||
                throw new Exception("Sorry, ffmpeg killed itself in a tragic accident!");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return process;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										144
									
								
								Kasbot.APP/Services/Internal/YoutubeService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								Kasbot.APP/Services/Internal/YoutubeService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,144 @@
 | 
			
		||||
using Discord.WebSocket;
 | 
			
		||||
using YoutubeExplode.Videos;
 | 
			
		||||
using YoutubeExplode;
 | 
			
		||||
using Discord.Rest;
 | 
			
		||||
using YoutubeExplode.Videos.Streams;
 | 
			
		||||
using Kasbot.Models;
 | 
			
		||||
 | 
			
		||||
namespace Kasbot.Services.Internal
 | 
			
		||||
{
 | 
			
		||||
    public class YoutubeService
 | 
			
		||||
    {
 | 
			
		||||
        public YoutubeService()
 | 
			
		||||
        {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<MediaCollection> DownloadPlaylistMetadataFromYoutube(SocketUserMessage message, string search)
 | 
			
		||||
        {
 | 
			
		||||
            var collection = new MediaCollection();
 | 
			
		||||
            var youtube = new YoutubeClient();
 | 
			
		||||
 | 
			
		||||
            var playlistInfo = await youtube.Playlists.GetAsync(search);
 | 
			
		||||
            await youtube.Playlists.GetVideosAsync(search).ForEachAsync(videoId =>
 | 
			
		||||
            {
 | 
			
		||||
                var media = new Media
 | 
			
		||||
                {
 | 
			
		||||
                    Name = videoId.Title,
 | 
			
		||||
                    Length = videoId.Duration ?? new TimeSpan(0),
 | 
			
		||||
                    VideoId = videoId.Id,
 | 
			
		||||
                    Message = message,
 | 
			
		||||
                    Flags = new Flags()
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                collection.Medias.Add(media);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            collection.CollectionName = playlistInfo.Title;
 | 
			
		||||
 | 
			
		||||
            return collection;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<Media> DownloadMetadataFromYoutube(Media media)
 | 
			
		||||
        {
 | 
			
		||||
            var youtube = new YoutubeClient();
 | 
			
		||||
 | 
			
		||||
            IVideo? videoId;
 | 
			
		||||
 | 
			
		||||
            if (media.Search.StartsWith("http://") || media.Search.StartsWith("https://"))
 | 
			
		||||
                videoId = await youtube.Videos.GetAsync(media.Search);
 | 
			
		||||
            else
 | 
			
		||||
                videoId = await youtube.Search.GetVideosAsync(media.Search).FirstOrDefaultAsync();
 | 
			
		||||
 | 
			
		||||
            if (videoId == null)
 | 
			
		||||
            {
 | 
			
		||||
                return media;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            media.Name = videoId.Title;
 | 
			
		||||
            media.Length = videoId.Duration ?? new TimeSpan(0);
 | 
			
		||||
            media.VideoId = videoId.Id;
 | 
			
		||||
 | 
			
		||||
            return media;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<MemoryStream?> DownloadAudioFromYoutube(Media media)
 | 
			
		||||
        {
 | 
			
		||||
            if (media.VideoId == null)
 | 
			
		||||
            {
 | 
			
		||||
                return null;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var memoryStream = new MemoryStream();
 | 
			
		||||
            var youtube = new YoutubeClient();
 | 
			
		||||
 | 
			
		||||
            var streamInfoSet = await youtube.Videos.Streams.GetManifestAsync((VideoId)media.VideoId);
 | 
			
		||||
            var streamInfo = streamInfoSet.GetAudioOnlyStreams().GetWithHighestBitrate();
 | 
			
		||||
            var streamVideo = await youtube.Videos.Streams.GetAsync(streamInfo);
 | 
			
		||||
            streamVideo.Position = 0;
 | 
			
		||||
 | 
			
		||||
            streamVideo.CopyTo(memoryStream);
 | 
			
		||||
            memoryStream.Position = 0;
 | 
			
		||||
 | 
			
		||||
            return memoryStream;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public SearchType GetSearchType(string query)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(query))
 | 
			
		||||
                return SearchType.None;
 | 
			
		||||
 | 
			
		||||
            if (query.StartsWith("http://") || query.StartsWith("https://"))
 | 
			
		||||
            {
 | 
			
		||||
                if (query.Contains("playlist?list="))
 | 
			
		||||
                    return SearchType.PlaylistURL;
 | 
			
		||||
 | 
			
		||||
                // need to add 'else if' for ChannelURL
 | 
			
		||||
 | 
			
		||||
                return SearchType.VideoURL;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return SearchType.StringSearch;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public enum SearchType
 | 
			
		||||
    {
 | 
			
		||||
        None,
 | 
			
		||||
        StringSearch,
 | 
			
		||||
        VideoURL,
 | 
			
		||||
        PlaylistURL,
 | 
			
		||||
        ChannelURL
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class Media
 | 
			
		||||
    {
 | 
			
		||||
        public string Search { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string Name { get; set; }
 | 
			
		||||
        public TimeSpan Length { get; set; }
 | 
			
		||||
        public Flags Flags { get; set; }
 | 
			
		||||
 | 
			
		||||
        public VideoId? VideoId { get; set; }
 | 
			
		||||
        public RestUserMessage PlayMessage { get; set; }
 | 
			
		||||
        public RestUserMessage? QueueMessage { get; set; }
 | 
			
		||||
 | 
			
		||||
        private SocketUserMessage message;
 | 
			
		||||
        public SocketUserMessage Message
 | 
			
		||||
        {
 | 
			
		||||
            get => message;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                message = value;
 | 
			
		||||
                Channel = value.Channel;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        public ISocketMessageChannel Channel { get; private set; }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public class MediaCollection
 | 
			
		||||
    {
 | 
			
		||||
        public string CollectionName { get; set; }
 | 
			
		||||
        public List<Media> Medias { get; set; } = new List<Media>();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										20
									
								
								Kasbot.APP/Services/PictureService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								Kasbot.APP/Services/PictureService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Net.Http;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Kasbot.Services
 | 
			
		||||
{
 | 
			
		||||
    public class PictureService
 | 
			
		||||
    {
 | 
			
		||||
        private readonly HttpClient _http;
 | 
			
		||||
 | 
			
		||||
        public PictureService(HttpClient http)
 | 
			
		||||
            => _http = http;
 | 
			
		||||
 | 
			
		||||
        public async Task<Stream> GetCatPictureAsync()
 | 
			
		||||
        {
 | 
			
		||||
            var resp = await _http.GetAsync("https://cataas.com/cat");
 | 
			
		||||
            return await resp.Content.ReadAsStreamAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										249
									
								
								Kasbot.APP/Services/PlayerService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								Kasbot.APP/Services/PlayerService.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
using Discord;
 | 
			
		||||
using Discord.Audio;
 | 
			
		||||
using Discord.Commands;
 | 
			
		||||
using Kasbot.Extensions;
 | 
			
		||||
using Kasbot.Models;
 | 
			
		||||
using Kasbot.Services.Internal;
 | 
			
		||||
 | 
			
		||||
namespace Kasbot.Services
 | 
			
		||||
{
 | 
			
		||||
    public class PlayerService
 | 
			
		||||
    {
 | 
			
		||||
        public Dictionary<ulong, Connection> Clients { get; set; }
 | 
			
		||||
        public YoutubeService YoutubeService { get; set; }
 | 
			
		||||
        public AudioService AudioService { get; set; }
 | 
			
		||||
 | 
			
		||||
        public PlayerService(YoutubeService youtubeService, AudioService audioService)
 | 
			
		||||
        {
 | 
			
		||||
            YoutubeService = youtubeService;
 | 
			
		||||
            AudioService = audioService;
 | 
			
		||||
 | 
			
		||||
            Clients = new Dictionary<ulong, Connection>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task<Connection> CreateConnection(ulong guildId, IVoiceChannel voiceChannel)
 | 
			
		||||
        {
 | 
			
		||||
            var conn = new Connection();
 | 
			
		||||
            IAudioClient audioClient = await voiceChannel.ConnectAsync(selfDeaf: true);
 | 
			
		||||
 | 
			
		||||
            conn.AudioClient = audioClient;
 | 
			
		||||
            conn.AudioChannel = voiceChannel;
 | 
			
		||||
 | 
			
		||||
            if (Clients.ContainsKey(guildId))
 | 
			
		||||
                Clients.Remove(guildId);
 | 
			
		||||
 | 
			
		||||
            Clients.Add(guildId, conn);
 | 
			
		||||
 | 
			
		||||
            return conn;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Play(ShardedCommandContext Context, string arguments, Flags flags)
 | 
			
		||||
        {
 | 
			
		||||
            var media = new Media()
 | 
			
		||||
            {
 | 
			
		||||
                Message = Context.Message,
 | 
			
		||||
                Search = arguments,
 | 
			
		||||
                Flags = flags,
 | 
			
		||||
                Name = "",
 | 
			
		||||
            };
 | 
			
		||||
            var guildId = Context.Guild.Id;
 | 
			
		||||
            var userVoiceChannel = (Context.User as IVoiceState).VoiceChannel;
 | 
			
		||||
 | 
			
		||||
            if (Clients.TryGetValue(guildId, out var conn))
 | 
			
		||||
            {
 | 
			
		||||
                if (conn.AudioChannel.Id != userVoiceChannel.Id)
 | 
			
		||||
                {
 | 
			
		||||
                    await Stop(guildId);
 | 
			
		||||
                    conn = await CreateConnection(guildId, userVoiceChannel);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                conn = await CreateConnection(guildId, userVoiceChannel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await Enqueue(guildId, conn, media);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task Enqueue(ulong guildId, Connection conn, Media media)
 | 
			
		||||
        {
 | 
			
		||||
            var startPlay = conn.Queue.Count == 0;
 | 
			
		||||
 | 
			
		||||
            media.Search.Trim();
 | 
			
		||||
 | 
			
		||||
            switch (YoutubeService.GetSearchType(media.Search))
 | 
			
		||||
            {
 | 
			
		||||
                case SearchType.StringSearch:
 | 
			
		||||
                case SearchType.VideoURL:
 | 
			
		||||
                    media = await YoutubeService.DownloadMetadataFromYoutube(media);
 | 
			
		||||
 | 
			
		||||
                    if (media.VideoId == null)
 | 
			
		||||
                    {
 | 
			
		||||
                        await media.Channel.SendTemporaryMessageAsync($"No video found for \"{media.Search}\".");
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    conn.Queue.Enqueue(media);
 | 
			
		||||
                    if (startPlay)
 | 
			
		||||
                        await PlayNext(guildId);
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        var message = $"Queued **{media.Name}** *({media.Length.TotalMinutes:00}:{media.Length.Seconds:00})*";
 | 
			
		||||
                        media.QueueMessage = await media.Channel.SendMessageAsync(message);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
                case SearchType.ChannelURL:
 | 
			
		||||
                case SearchType.PlaylistURL:
 | 
			
		||||
                    var collection = await YoutubeService.DownloadPlaylistMetadataFromYoutube(media.Message, media.Search);
 | 
			
		||||
 | 
			
		||||
                    collection.Medias.ForEach(m => conn.Queue.Enqueue(m));
 | 
			
		||||
                    
 | 
			
		||||
                    await media.Channel.SendMessageAsync($"Queued **{collection.Medias.Count}** items from *{collection.CollectionName}* playlist.");
 | 
			
		||||
 | 
			
		||||
                    if (startPlay)
 | 
			
		||||
                        await PlayNext(guildId);
 | 
			
		||||
 | 
			
		||||
                    break;
 | 
			
		||||
                case SearchType.None:
 | 
			
		||||
                default:
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private async Task PlayNext(ulong guildId)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Clients.ContainsKey(guildId) || Clients[guildId].Queue.Count == 0)
 | 
			
		||||
            {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var nextMedia = Clients[guildId].Queue.FirstOrDefault();
 | 
			
		||||
 | 
			
		||||
            if (nextMedia == null)
 | 
			
		||||
            {
 | 
			
		||||
                await Stop(guildId);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // since we can't verify if the bot was disconnected by a websocket error, we do this check which should do enough
 | 
			
		||||
            if (Clients[guildId].AudioClient.ConnectionState == ConnectionState.Disconnected)
 | 
			
		||||
            {
 | 
			
		||||
                var voiceChannel = Clients[guildId].AudioChannel;
 | 
			
		||||
                Clients.Remove(guildId);
 | 
			
		||||
                await CreateConnection(guildId, voiceChannel);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var mp3Stream = await YoutubeService.DownloadAudioFromYoutube(nextMedia);
 | 
			
		||||
 | 
			
		||||
            if (mp3Stream == null)
 | 
			
		||||
            {
 | 
			
		||||
                await Stop(guildId);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var audioClient = Clients[guildId].AudioClient;
 | 
			
		||||
 | 
			
		||||
            if (!nextMedia.Flags.Silent)
 | 
			
		||||
            {
 | 
			
		||||
                var message = $"⏯ Playing: **{nextMedia.Name}** *({nextMedia.Length.TotalMinutes:00}:{nextMedia.Length.Seconds:00})*";
 | 
			
		||||
                nextMedia.PlayMessage = await nextMedia.Channel.SendMessageAsync(message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (nextMedia.QueueMessage != null)
 | 
			
		||||
            {
 | 
			
		||||
                await nextMedia.QueueMessage.TryDeleteAsync();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            AudioService.StartAudioTask(mp3Stream, audioClient,
 | 
			
		||||
                (outAudioStream) =>
 | 
			
		||||
                {
 | 
			
		||||
                    Clients[guildId].CurrentAudioStream = outAudioStream;
 | 
			
		||||
                },
 | 
			
		||||
                async (ac) =>
 | 
			
		||||
                {
 | 
			
		||||
                    if (ac.Exception != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        await nextMedia.Channel.SendTemporaryMessageAsync("Error in stream: " + ac.Exception.ToString());
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            await nextMedia.PlayMessage.TryDeleteAsync();
 | 
			
		||||
 | 
			
		||||
            if (Clients[guildId].Queue.Count > 0 &&
 | 
			
		||||
                !Clients[guildId].Queue.First().Flags.Repeat)
 | 
			
		||||
                Clients[guildId].Queue.Dequeue();
 | 
			
		||||
 | 
			
		||||
            await PlayNext(guildId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public Task Skip(ulong guildId)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Clients.ContainsKey(guildId))
 | 
			
		||||
                throw new Exception("Bot is not connected!");
 | 
			
		||||
 | 
			
		||||
            var media = Clients[guildId];
 | 
			
		||||
 | 
			
		||||
            if (media.CurrentAudioStream == null)
 | 
			
		||||
                throw new Exception("There is no audio playing!");
 | 
			
		||||
 | 
			
		||||
            media.CurrentAudioStream.Close();
 | 
			
		||||
 | 
			
		||||
            return Task.CompletedTask;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Stop(ulong guildId)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Clients.ContainsKey(guildId))
 | 
			
		||||
                throw new Exception("Bot is not connected!");
 | 
			
		||||
 | 
			
		||||
            var media = Clients[guildId];
 | 
			
		||||
 | 
			
		||||
            foreach (var v in media.Queue.Skip(0))
 | 
			
		||||
            {
 | 
			
		||||
                await v.PlayMessage.TryDeleteAsync();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            media.Queue.Clear();
 | 
			
		||||
 | 
			
		||||
            if (media.CurrentAudioStream != null)
 | 
			
		||||
                media.CurrentAudioStream.Close();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Leave(ulong guildId)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Clients.ContainsKey(guildId))
 | 
			
		||||
                throw new Exception("Bot is not connected!");
 | 
			
		||||
 | 
			
		||||
            await Stop(guildId);
 | 
			
		||||
            var media = Clients[guildId];
 | 
			
		||||
 | 
			
		||||
            if (media.AudioClient != null)
 | 
			
		||||
                await media.AudioClient.StopAsync();
 | 
			
		||||
 | 
			
		||||
            Clients.Remove(guildId);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Repeat(ulong guildId)
 | 
			
		||||
        {
 | 
			
		||||
            if (!Clients.ContainsKey(guildId))
 | 
			
		||||
                throw new Exception("Bot is not connected!");
 | 
			
		||||
 | 
			
		||||
            if (Clients[guildId].Queue.Count == 0)
 | 
			
		||||
                throw new Exception("The queue is empty!");
 | 
			
		||||
 | 
			
		||||
            var media = Clients[guildId].Queue.First();
 | 
			
		||||
            media.Flags.Repeat = !media.Flags.Repeat;
 | 
			
		||||
            await media.Channel.SendTemporaryMessageAsync(media.Flags.Repeat ? "Repeat turned on!" : "Repeat turned off!");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task Join(ShardedCommandContext Context)
 | 
			
		||||
        {
 | 
			
		||||
            var guildId = Context.Guild.Id;
 | 
			
		||||
            if (Clients.ContainsKey(guildId))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            await CreateConnection(guildId, (Context.User as IVoiceState).VoiceChannel);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user