From cac1ade8763605c3cf09859a48358cab0e00027a Mon Sep 17 00:00:00 2001 From: TrueDoctor Date: Sun, 8 Apr 2018 23:06:21 +0200 Subject: Added Audio Support --- DiscoBot/Audio.cs | 27 +++++++++++++++ DiscoBot/AudioService.cs | 88 ++++++++++++++++++++++++++++++++++++++++++++++ DiscoBot/Commands.cs | 90 +++++++++++++++++++++++++++++++++++++++++++----- DiscoBot/DSA.cs | 3 +- DiscoBot/DiscoBot.csproj | 2 ++ DiscoBot/Program.cs | 9 ++++- Voice.cs | 19 +--------- 7 files changed, 209 insertions(+), 29 deletions(-) create mode 100644 DiscoBot/Audio.cs create mode 100644 DiscoBot/AudioService.cs diff --git a/DiscoBot/Audio.cs b/DiscoBot/Audio.cs new file mode 100644 index 0000000..68c860c --- /dev/null +++ b/DiscoBot/Audio.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DiscoBot +{ + using System.Diagnostics; + + class Audio + { + private Process CreateStream(string path) + { + var ffmpeg = new ProcessStartInfo + { + FileName = "ffmpeg", + Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1", + UseShellExecute = false, + RedirectStandardOutput = true, + }; + return Process.Start(ffmpeg); + } + } + + +} diff --git a/DiscoBot/AudioService.cs b/DiscoBot/AudioService.cs new file mode 100644 index 0000000..bb4d21e --- /dev/null +++ b/DiscoBot/AudioService.cs @@ -0,0 +1,88 @@ +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using Discord; +using Discord.Audio; + +namespace DiscoBot +{ + public class AudioService + { + private readonly ConcurrentDictionary ConnectedChannels = + new ConcurrentDictionary(); + + public async Task JoinAudio(IGuild guild, IVoiceChannel target) + { + IAudioClient client; + if (ConnectedChannels.TryGetValue(guild.Id, out client)) + { + return; + } + + if (target.Guild.Id != guild.Id) + { + return; + } + + var audioClient = await target.ConnectAsync(); + + if (ConnectedChannels.TryAdd(guild.Id, audioClient)) + { + // If you add a method to log happenings from this service, + // you can uncomment these commented lines to make use of that. + //await Log(LogSeverity.Info, $"Connected to voice on {guild.Name}."); + } + } + + public async Task LeaveAudio(IGuild guild) + { + IAudioClient client; + if (ConnectedChannels.TryRemove(guild.Id, out client)) + { + await client.StopAsync(); + //await Log(LogSeverity.Info, $"Disconnected from voice on {guild.Name}."); + } + } + + public async Task SendAudioAsync(IGuild guild, IMessageChannel channel, string path) + { + // Your task: Get a full path to the file if the value of 'path' is only a filename. + if (!File.Exists(path)) + { + await channel.SendMessageAsync("File does not exist."); + return; + } + + IAudioClient client; + if (ConnectedChannels.TryGetValue(guild.Id, out client)) + { + //await Log(LogSeverity.Debug, $"Starting playback of {path} in {guild.Name}"); + using (var ffmpeg = Process.Start(path)) + using (var stream = client.CreatePCMStream(AudioApplication.Music)) + { + try + { + await ffmpeg.StandardOutput.BaseStream.CopyToAsync(stream); + } + finally + { + await stream.FlushAsync(); + } + } + } + } + + private Process CreateStream(string path) + { + return Process.Start( + new ProcessStartInfo + { + FileName = "ffmpeg.exe", + Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", + UseShellExecute = false, + RedirectStandardOutput = true + }); + } + } +} diff --git a/DiscoBot/Commands.cs b/DiscoBot/Commands.cs index 55ad530..6ba2b8a 100644 --- a/DiscoBot/Commands.cs +++ b/DiscoBot/Commands.cs @@ -11,9 +11,12 @@ using Discord.WebSocket; namespace DiscoBot { using System.ComponentModel; + using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Threading; + using Discord.Audio; + public class Info : ModuleBase { [Command("say"), Summary("Echos a message.")] @@ -123,24 +126,57 @@ namespace DiscoBot public class Voice : ModuleBase { - [Command("join")] + public static IAudioClient client { get; set; } + [Command("join", RunMode = RunMode.Async)] public async Task JoinChannel(IVoiceChannel channel = null) { + var msg = this.Context.Message; // Get the audio channel - channel = channel ?? (this.Context.User as IGuildUser)?.VoiceChannel; - if (channel == null) - { - await this.Context.Channel.SendMessageAsync( - "User must be in a voice channel, or a voice channel must be passed as an argument."); - return; - } + channel = channel ?? (msg.Author as IGuildUser)?.VoiceChannel; + if (channel == null) { await msg.Channel.SendMessageAsync("User must be in a voice channel, or a voice channel must be passed as an argument."); return; } // For the next step with transmitting audio, you would want to pass this Audio Client in to a service. var audioClient = await channel.ConnectAsync(); - + client = audioClient; + } + + [Command("leave", RunMode = RunMode.Async)] + public async Task LeaveChannel(IVoiceChannel channel = null) + { + // For the next step with transmitting audio, you would want to pass this Audio Client in to a service. + client.StopAsync(); + } + + [Command("play")] + public async Task PlayAudio(string path) + { + SendAsync(client, path); + } + + private Process CreateStream(string path) + { + var ffmpeg = new ProcessStartInfo + { + FileName = "ffmpeg", + Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 -ab 620000 pipe:1", + UseShellExecute = false, + RedirectStandardOutput = true, + }; + return Process.Start(ffmpeg); + } + + private async Task SendAsync(IAudioClient client, string path) + { + // Create FFmpeg using the previous example + var ffmpeg = CreateStream(path); + var output = ffmpeg.StandardOutput.BaseStream; + var discord = client.CreatePCMStream(AudioApplication.Music); + await output.CopyToAsync(discord); + await discord.FlushAsync(); } } + [Group("gmtr")] public class Sample : ModuleBase { @@ -364,6 +400,42 @@ namespace DiscoBot } } + /*public class AudioModule : ModuleBase + { + // Scroll down further for the AudioService. + // Like, way down + private readonly AudioService _service; + + // Remember to add an instance of the AudioService + // to your IServiceCollection when you initialize your bot + public AudioModule(AudioService service) + { + _service = service; + } + + // You *MUST* mark these commands with 'RunMode.Async' + // otherwise the bot will not respond until the Task times out. + [Command("join", RunMode = RunMode.Async)] + public async Task JoinCmd() + { + await _service.JoinAudio(Context.Guild, (Context.User as IVoiceState).VoiceChannel); + } + + // Remember to add preconditions to your commands, + // this is merely the minimal amount necessary. + // Adding more commands of your own is also encouraged. + [Command("leave", RunMode = RunMode.Async)] + public async Task LeaveCmd() + { + await _service.LeaveAudio(Context.Guild); + } + + [Command("play", RunMode = RunMode.Async)] + public async Task PlayCmd([Remainder] string song) + { + await _service.SendAudioAsync(Context.Guild, Context.Channel, song); + } + }*/ public enum Commands { diff --git a/DiscoBot/DSA.cs b/DiscoBot/DSA.cs index 60b25ce..af02023 100644 --- a/DiscoBot/DSA.cs +++ b/DiscoBot/DSA.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; + using System.Threading.Tasks; using Discord.Commands; @@ -14,7 +15,7 @@ public static List Talente { get; set; } = new List(); - public static void Startup() + public static async Task Startup() { relation.Add("The Doctor", "Numeri Illuminus");//Relation relation.Add("Tardis", "Morla");//"Numeri Illuminus"); diff --git a/DiscoBot/DiscoBot.csproj b/DiscoBot/DiscoBot.csproj index 91ce25e..d0fc986 100644 --- a/DiscoBot/DiscoBot.csproj +++ b/DiscoBot/DiscoBot.csproj @@ -140,6 +140,8 @@ + + diff --git a/DiscoBot/Program.cs b/DiscoBot/Program.cs index b90ed54..7ae8244 100644 --- a/DiscoBot/Program.cs +++ b/DiscoBot/Program.cs @@ -24,7 +24,7 @@ namespace DiscoBot public async Task Start() { - DSA.Startup(); + var loading = DSA.Startup(); client = new DiscordSocketClient(); commands = new CommandService(); @@ -32,12 +32,14 @@ namespace DiscoBot services = new ServiceCollection() .BuildServiceProvider(); + AppDomain.CurrentDomain.ProcessExit += new EventHandler(OnProcessExit); await InstallCommands(); await client.LoginAsync(TokenType.Bot, token); await client.StartAsync(); + await loading; await Task.Delay(-1); } @@ -66,6 +68,11 @@ namespace DiscoBot if (!result.IsSuccess) await context.Channel.SendMessageAsync(result.ErrorReason); } + static void OnProcessExit(object sender, EventArgs e) + { + Console.WriteLine("I'm out of here"); + Voice.client.StopAsync(); + } } } diff --git a/Voice.cs b/Voice.cs index ea2f6d6..031c49e 100644 --- a/Voice.cs +++ b/Voice.cs @@ -1,21 +1,4 @@ using System; +using Discord; -public class Voice -{ - public Voice(IAudioclient audi) - { - } - - private Process CreateStream(string path) - { - var ffmpeg = new ProcessStartInfo - { - FileName = "ffmpeg", - Arguments = $"-i {path} -ac 2 -f s16le -ar 48000 pipe:1", - UseShellExecute = false, - RedirectStandardOutput = true, - }; - return Process.Start(ffmpeg); - } -} -- cgit v1.2.3-70-g09d2