From f8409f328e3b0fd04556d1805ba268ecf285d386 Mon Sep 17 00:00:00 2001 From: Ben Harris Date: Thu, 11 Jan 2018 01:36:30 -0500 Subject: [PATCH] start music player --- Commands/Music.cs | 40 ++++++++++++++++++ Commands/TicTacToe.cs | 2 +- Program.cs | 1 + Services/AudioService.cs | 80 ++++++++++++++++++++++++++++++++++++ Services/TicTacToeService.cs | 2 +- dotbot.csproj | 6 +++ 6 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 Commands/Music.cs create mode 100644 Services/AudioService.cs diff --git a/Commands/Music.cs b/Commands/Music.cs new file mode 100644 index 0000000..73ac3dc --- /dev/null +++ b/Commands/Music.cs @@ -0,0 +1,40 @@ +////////////////////////////////////////////////////////////////////// +// https://gist.github.com/Joe4evr/773d3ce6cc10dbea6924d59bbfa3c62a // +////////////////////////////////////////////////////////////////////// + +using Discord; +using Discord.Commands; +using System.Threading.Tasks; + +public class Music : ModuleBase +{ + private readonly AudioService _service; + + public Music(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); + } +} diff --git a/Commands/TicTacToe.cs b/Commands/TicTacToe.cs index 0828856..0f9e068 100755 --- a/Commands/TicTacToe.cs +++ b/Commands/TicTacToe.cs @@ -88,7 +88,7 @@ namespace dotbot.Commands return true; } - internal async string DoMove(SocketUserMessage msg) + internal async Task DoMove(SocketUserMessage msg) { if (!Active || Players[Turn] != msg.Author.Id) return ""; diff --git a/Program.cs b/Program.cs index 334f43a..2bfe1b7 100644 --- a/Program.cs +++ b/Program.cs @@ -43,6 +43,7 @@ namespace dotbot .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton() .AddSingleton(_config); diff --git a/Services/AudioService.cs b/Services/AudioService.cs new file mode 100644 index 0000000..20d5490 --- /dev/null +++ b/Services/AudioService.cs @@ -0,0 +1,80 @@ +////////////////////////////////////////////////////////////////////// +// https://gist.github.com/Joe4evr/773d3ce6cc10dbea6924d59bbfa3c62a // +////////////////////////////////////////////////////////////////////// + +using System.Collections.Concurrent; +using System.Diagnostics; +using System.IO; +using System.Threading.Tasks; +using Discord; +using Discord.Audio; + +public class AudioService +{ + private readonly ConcurrentDictionary ConnectedChannels = new ConcurrentDictionary(); + + + public async Task JoinAudio(IGuild guild, IVoiceChannel target) + { + if (ConnectedChannels.TryGetValue(guild.Id, out IAudioClient 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) + { + if (ConnectedChannels.TryRemove(guild.Id, out IAudioClient 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; + } + if (ConnectedChannels.TryGetValue(guild.Id, out IAudioClient client)) + { + //await Log(LogSeverity.Debug, $"Starting playback of {path} in {guild.Name}"); + using (var output = CreateStream(path).StandardOutput.BaseStream) + using (var stream = client.CreatePCMStream(AudioApplication.Music)) + { + try { await output.CopyToAsync(stream); } + finally { await stream.FlushAsync(); } + } + } + } + + + private Process CreateStream(string path) + { + return Process.Start(new ProcessStartInfo + { + FileName = "ytdl-bin/ffmpeg.exe", + Arguments = $"-hide_banner -loglevel panic -i \"{path}\" -ac 2 -f s16le -ar 48000 pipe:1", + UseShellExecute = false, + RedirectStandardOutput = true + }); + } +} diff --git a/Services/TicTacToeService.cs b/Services/TicTacToeService.cs index 3a11d73..46fbb99 100755 --- a/Services/TicTacToeService.cs +++ b/Services/TicTacToeService.cs @@ -26,7 +26,7 @@ namespace dotbot.Services if (_activeGames.ContainsKey(id)) { var game = _activeGames[id]; - await context.Channel.SendMessageAsync($"{game.DoMove(msg)}\n\n{game}"); + await context.Channel.SendMessageAsync($"{game.DoMove(msg).Result}\n\n{game}"); } } diff --git a/dotbot.csproj b/dotbot.csproj index 67e3837..46ed354 100644 --- a/dotbot.csproj +++ b/dotbot.csproj @@ -19,6 +19,12 @@ + + Always + + + Always + Always