Merge pull request #10 from Kwoth/dev

Dev
This commit is contained in:
miraai 2016-08-01 21:40:26 +02:00 committed by GitHub
commit a1da64e4ff
15 changed files with 310 additions and 112 deletions

47
DockerGuide.md Normal file
View File

@ -0,0 +1,47 @@
## Docker guide with digitalocean
#####Prerequisites:
- Digital ocean account (you can use my reflink to support the project and get 10$ after you register http://m.do.co/c/46b4d3d44795/ )
- Putty (get it here http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html)
- A bot account - (follow http://discord.kongslien.net/guide.html)
- Common sense
Click on the create droplet button
![img](http://i.imgur.com/g2ayOcC.png)
pick one click apps and select docker on 14.04
![img](http://imgur.com/065Xkme.png)
- pick any droplet size you want (5$ will work ok-ish on a few servers)
- pick location closest to your discord server's location
- Pick a hostname
![img](http://imgur.com/ifPKB6p.png)
- click create
You will get an email from digitalocean with your creds now.
Open putty and type ip adress **you got in your email** with port 22
![img](http://imgur.com/Mh5ehsh.png)
console will open and you will be prompted for a username, type `root`
type in the password you got in the email
confirm the password you just typed in
type in the new password
confirm new password
when you are successfully logged in, type
`docker run --name nadeko -v /nadeko:/config uirel/nadeko`
wait for it to download and at one point it is going to start throwing errors due to credentials.json being empty
CTRL+C to exit that
type `docker stop nadeko`
type `nano /nadeko/credentials.json` and type in your credentials
CTRL+X then CTRL+Y to save
type `docker start nadeko`
Your bot is running, enjoy
*When you want to update the bot, just type `docker restart nadeko` as it always downloads latest prerelease*

View File

@ -175,7 +175,7 @@ namespace NadekoBot.Classes
return null; return null;
} }
public static async Task<string> GetRelatedVideoId(string id) public static async Task<IEnumerable<string>> GetRelatedVideoIds(string id, int count = 1)
{ {
if (string.IsNullOrWhiteSpace(id)) if (string.IsNullOrWhiteSpace(id))
throw new ArgumentNullException(nameof(id)); throw new ArgumentNullException(nameof(id));
@ -186,20 +186,14 @@ namespace NadekoBot.Classes
} }
var response = await GetResponseStringAsync( var response = await GetResponseStringAsync(
$"https://www.googleapis.com/youtube/v3/search?" + $"https://www.googleapis.com/youtube/v3/search?" +
$"part=snippet&maxResults=1&type=video" + $"part=snippet&maxResults={count}&type=video" +
$"&relatedToVideoId={id}" + $"&relatedToVideoId={id}" +
$"&key={NadekoBot.Creds.GoogleAPIKey}").ConfigureAwait(false); $"&key={NadekoBot.Creds.GoogleAPIKey}").ConfigureAwait(false);
JObject obj = JObject.Parse(response); JObject obj = JObject.Parse(response);
var data = JsonConvert.DeserializeObject<YoutubeVideoSearch>(response); var data = JsonConvert.DeserializeObject<YoutubeVideoSearch>(response);
if (data.items.Length > 0) return data.items.Select(v => "http://www.youtube.com/watch?v=" + v.id.videoId);
{
var toReturn = "http://www.youtube.com/watch?v=" + data.items[0].id.videoId.ToString();
return toReturn;
}
else
return null;
} }
public static async Task<string> GetPlaylistIdByKeyword(string query) public static async Task<string> GetPlaylistIdByKeyword(string query)

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
cgb.CreateCommand(Module.Prefix + "autoassignrole") cgb.CreateCommand(Module.Prefix + "autoassignrole")
.Alias(Module.Prefix + "aar") .Alias(Module.Prefix + "aar")
.Description($"Automaticaly assigns a specified role to every user who joins the server. Type `{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable") .Description($"Automaticaly assigns a specified role to every user who joins the server. |`{Prefix}aar` to disable, `{Prefix}aar Role Name` to enable")
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.AddCheck(new SimpleCheckers.ManageRoles()) .AddCheck(new SimpleCheckers.ManageRoles())
.Do(async e => .Do(async e =>

View File

@ -4,7 +4,10 @@ using NadekoBot.Classes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Classes; using NadekoBot.Modules.Permissions.Classes;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Commands namespace NadekoBot.Modules.Administration.Commands
{ {
@ -12,6 +15,8 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; private string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
private ConcurrentBag<KeyValuePair<Channel, string>> voicePresenceUpdates = new ConcurrentBag<KeyValuePair<Channel, string>>();
public LogCommand(DiscordModule module) : base(module) public LogCommand(DiscordModule module) : base(module)
{ {
NadekoBot.Client.MessageReceived += MsgRecivd; NadekoBot.Client.MessageReceived += MsgRecivd;
@ -45,6 +50,36 @@ namespace NadekoBot.Modules.Administration.Commands
} }
catch { } catch { }
}; };
// start the userpresence queue
NadekoBot.OnReady += () => Task.Run(async () =>
{
while (true)
{
var toSend = new Dictionary<Channel, string>();
//take everything from the queue and merge the messages which are going to the same channel
KeyValuePair<Channel, string> item;
while (voicePresenceUpdates.TryTake(out item))
{
if (toSend.ContainsKey(item.Key))
{
toSend[item.Key] = toSend[item.Key] + Environment.NewLine + item.Value;
}
else
{
toSend.Add(item.Key, item.Value);
}
}
//send merged messages to each channel
foreach (var k in toSend)
{
try { await k.Key.SendMessage(Environment.NewLine + k.Value).ConfigureAwait(false); } catch { }
}
await Task.Delay(5000);
}
});
} }
private async void ChannelUpdated(object sender, ChannelUpdatedEventArgs e) private async void ChannelUpdated(object sender, ChannelUpdatedEventArgs e)
@ -252,7 +287,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}`
{ {
if (e.Before.Status != e.After.Status) if (e.Before.Status != e.After.Status)
{ {
await ch.SendMessage($"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**.").ConfigureAwait(false); voicePresenceUpdates.Add(new KeyValuePair<Channel, string>(ch, $"`{prettyCurrentTime}`**{e.Before.Name}** is now **{e.After.Status}**."));
} }
} }
} }

View File

@ -57,7 +57,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "repeatinvoke") cgb.CreateCommand(Module.Prefix + "repeatinvoke")
.Alias(Module.Prefix + "repinv") .Alias(Module.Prefix + "repinv")
.Description("Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`") .Description($"Immediately shows the repeat message and restarts the timer. | `{Prefix}repinv`")
.AddCheck(SimpleCheckers.ManageMessages()) .AddCheck(SimpleCheckers.ManageMessages())
.Do(async e => .Do(async e =>
{ {

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "betray") cgb.CreateCommand(Module.Prefix + "betray")
.Description("BETRAY GAME. Betray nadeko next turn." + .Description("BETRAY GAME. Betray nadeko next turn." +
"If Nadeko cooperates - you get extra points, nadeko loses a LOT." + "If Nadeko cooperates - you get extra points, nadeko loses a LOT." +
"If Nadeko betrays - you both lose some points. | `{Prefix}betray`") $"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
.Do(async e => .Do(async e =>
{ {
await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false); await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false);
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games.Commands
cgb.CreateCommand(Module.Prefix + "cooperate") cgb.CreateCommand(Module.Prefix + "cooperate")
.Description("BETRAY GAME. Cooperate with nadeko next turn." + .Description("BETRAY GAME. Cooperate with nadeko next turn." +
"If Nadeko cooperates - you both get bonus points." + "If Nadeko cooperates - you both get bonus points." +
"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`") $"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
.Do(async e => .Do(async e =>
{ {

View File

@ -48,12 +48,11 @@ namespace NadekoBot.Classes.Help.Commands
public Action<CommandEventArgs> DoGitFunc() => e => public Action<CommandEventArgs> DoGitFunc() => e =>
{ {
string helpstr = string helpstr =
$@"######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/** $@"######For more information and how to setup your own NadekoBot, go to: <http://github.com/Kwoth/NadekoBot/wiki>
######You can donate on patreon: `https://patreon.com/nadekobot` ######You can donate on patreon: <https://patreon.com/nadekobot>
######or paypal: `nadekodiscordbot@gmail.com` ######or paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands #NadekoBot List Of Commands ";
Version: `{NadekoStats.Instance.BotVersion}`";
string lastCategory = ""; string lastCategory = "";
@ -81,7 +80,7 @@ Version: `{NadekoStats.Instance.BotVersion}`";
{ {
cgb.CreateCommand(Module.Prefix + "h") cgb.CreateCommand(Module.Prefix + "h")
.Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h") .Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h")
.Description("Either shows a help for a single command, or PMs you help link if no arguments are specified. | `-h !m q` or just `-h` ") .Description($"Either shows a help for a single command, or PMs you help link if no arguments are specified. | `{Prefix}h !m q` or just `{Prefix}h` ")
.Parameter("command", ParameterType.Unparsed) .Parameter("command", ParameterType.Unparsed)
.Do(HelpFunc()); .Do(HelpFunc());
cgb.CreateCommand(Module.Prefix + "hgit") cgb.CreateCommand(Module.Prefix + "hgit")
@ -103,7 +102,7 @@ Version: `{NadekoStats.Instance.BotVersion}`";
cgb.CreateCommand(Module.Prefix + "donate") cgb.CreateCommand(Module.Prefix + "donate")
.Alias("~donate") .Alias("~donate")
.Description("Instructions for helping the project! | `{Prefix}donate` or `~donate`") .Description($"Instructions for helping the project! | `{Prefix}donate` or `~donate`")
.Do(async e => .Do(async e =>
{ {
await e.Channel.SendMessage( await e.Channel.SendMessage(

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "modules") cgb.CreateCommand(Prefix + "modules")
.Alias(".modules") .Alias(".modules")
.Description("List all bot modules. | `{Prefix}modules` or `.modules`") .Description($"List all bot modules. | `{Prefix}modules` or `.modules`")
.Do(async e => .Do(async e =>
{ {
await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`") await e.Channel.SendMessage("`List of modules:` \n• " + string.Join("\n• ", NadekoBot.Client.GetService<ModuleService>().Modules.Select(m => m.Name)) + $"\n`Type \"{Prefix}commands module_name\" to get a list of commands in that module.`")
@ -38,7 +38,7 @@ namespace NadekoBot.Modules.Help
cgb.CreateCommand(Prefix + "commands") cgb.CreateCommand(Prefix + "commands")
.Alias(".commands") .Alias(".commands")
.Description("List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`") .Description($"List all of the bot's commands from a certain module. | `{Prefix}commands` or `.commands`")
.Parameter("module", ParameterType.Unparsed) .Parameter("module", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {

View File

@ -34,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes
public IReadOnlyCollection<Song> Playlist => playlist; public IReadOnlyCollection<Song> Playlist => playlist;
public Song CurrentSong { get; private set; } public Song CurrentSong { get; private set; }
private CancellationTokenSource SongCancelSource { get; set; } public CancellationTokenSource SongCancelSource { get; private set; }
private CancellationToken cancelToken { get; set; } private CancellationToken cancelToken { get; set; }
public bool Paused { get; set; } public bool Paused { get; set; }

View File

@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; } public SongInfo SongInfo { get; }
public string QueuerName { get; set; } public string QueuerName { get; set; }
private bool bufferingCompleted { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime() public string PrettyCurrentTime()
@ -73,78 +72,22 @@ namespace NadekoBot.Modules.Music.Classes
return this; return this;
} }
private Task BufferSong(string filename, CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {skipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
var prebufferSize = 100ul.MiB();
using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
{
byte[] buffer = new byte[81920];
int bytesRead;
while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0)
{
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while ((ulong)outStream.Length - bytesSent > prebufferSize)
await Task.Delay(100, cancelToken);
}
}
bufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception) {
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{ {
var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false); SongBuffer sb = new SongBuffer(filename, SongInfo, skipTo);
var bufferTask = sb.BufferSong(cancelToken).ConfigureAwait(false);
var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); var inStream = new FileStream(sb.GetNextFile(), FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write); ;
bytesSent = 0; bytesSent = 0;
try try
{ {
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken); var attempt = 0;
var prebufferingTask = CheckPrebufferingAsync(inStream, sb, cancelToken);
var sw = new Stopwatch(); var sw = new Stopwatch();
sw.Start(); sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken)); var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
@ -162,7 +105,6 @@ Check the guides for your platform on how to setup ffmpeg correctly:
Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed); Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed);
const int blockSize = 3840; const int blockSize = 3840;
var attempt = 0;
byte[] buffer = new byte[blockSize]; byte[] buffer = new byte[blockSize];
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
@ -173,14 +115,31 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{ {
bytesSent += (ulong)read; bytesSent += (ulong)read;
} }
if (read < blockSize)
{
if (sb.IsNextFileReady())
{
inStream.Dispose();
inStream = new FileStream(sb.GetNextFile(), FileMode.Open, FileAccess.Read, FileShare.Write);
read += inStream.Read(buffer, read, buffer.Length - read);
attempt = 0;
}
if (read == 0) if (read == 0)
{
if (sb.BufferingCompleted)
break;
if (attempt++ == 20) if (attempt++ == 20)
{ {
voiceClient.Wait(); voiceClient.Wait();
MusicPlayer.SongCancelSource.Cancel();
break; break;
} }
else else
await Task.Delay(100, cancelToken).ConfigureAwait(false); await Task.Delay(100, cancelToken).ConfigureAwait(false);
}
else
attempt = 0;
}
else else
attempt = 0; attempt = 0;
@ -195,14 +154,16 @@ Check the guides for your platform on how to setup ffmpeg correctly:
{ {
await bufferTask; await bufferTask;
await Task.Run(() => voiceClient.Clear()); await Task.Run(() => voiceClient.Clear());
if(inStream != null)
inStream.Dispose(); inStream.Dispose();
try { File.Delete(filename); } catch { } Console.WriteLine("l");
sb.CleanFiles();
} }
} }
private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken) private async Task CheckPrebufferingAsync(Stream inStream, SongBuffer sb, CancellationToken cancelToken)
{ {
while (!bufferingCompleted && inStream.Length < 2.MiB()) while (!sb.BufferingCompleted && inStream.Length < 2.MiB())
{ {
await Task.Delay(100, cancelToken); await Task.Delay(100, cancelToken);
} }

View File

@ -0,0 +1,159 @@
using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes
{
/// <summary>
/// Create a buffer for a song file. It will create multiples files to ensure, that radio don't fill up disk space.
/// It also help for large music by deleting files that are already seen.
/// </summary>
class SongBuffer
{
public SongBuffer(string basename, SongInfo songInfo, int skipTo)
{
Basename = basename;
SongInfo = songInfo;
SkipTo = skipTo;
}
private string Basename;
private SongInfo SongInfo;
private int SkipTo;
private static int MAX_FILE_SIZE = 20.MiB();
private long FileNumber = -1;
private long NextFileToRead = 0;
public bool BufferingCompleted { get; private set;} = false;
private ulong CurrentBufferSize = 0;
public Task BufferSong(CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
FileStream outStream = null;
try
{
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = false,
CreateNoWindow = true,
});
byte[] buffer = new byte[81920];
int currentFileSize = 0;
ulong prebufferSize = 100ul.MiB();
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
while (!p.HasExited) //Also fix low bandwidth
{
int bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false);
if (currentFileSize >= MAX_FILE_SIZE)
{
try
{
outStream.Dispose();
}catch { }
outStream = new FileStream(Basename + "-" + ++FileNumber, FileMode.Append, FileAccess.Write, FileShare.Read);
currentFileSize = bytesRead;
}
else
{
currentFileSize += bytesRead;
}
CurrentBufferSize += Convert.ToUInt64(bytesRead);
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while (CurrentBufferSize > prebufferSize)
await Task.Delay(100, cancelToken);
}
BufferingCompleted = true;
}
catch (System.ComponentModel.Win32Exception)
{
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
}
catch (Exception ex)
{
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
if(outStream != null)
outStream.Dispose();
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
{
p.Kill();
}
catch { }
p.Dispose();
}
}
}, TaskCreationOptions.LongRunning);
/// <summary>
/// Return the next file to read, and delete the old one
/// </summary>
/// <returns>Name of the file to read</returns>
public string GetNextFile()
{
string filename = Basename + "-" + NextFileToRead;
if (NextFileToRead != 0)
{
try
{
CurrentBufferSize -= Convert.ToUInt64(new FileInfo(Basename + "-" + (NextFileToRead - 1)).Length);
File.Delete(Basename + "-" + (NextFileToRead - 1));
}
catch { }
}
NextFileToRead++;
return filename;
}
public bool IsNextFileReady()
{
return NextFileToRead <= FileNumber;
}
public void CleanFiles()
{
for (long i = NextFileToRead - 1 ; i <= FileNumber; i++)
{
try
{
File.Delete(Basename + "-" + i);
}
catch { }
}
}
}
}

View File

@ -773,11 +773,13 @@ namespace NadekoBot.Modules.Music
{ {
await e.Channel.SendMessage("Could not select song, likely wrong index"); await e.Channel.SendMessage("Could not select song, likely wrong index");
} else }
else
{ {
await e.Channel.SendMessage($"🎶`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false); await e.Channel.SendMessage($"🎶`Selected song {selSong.SongInfo.Title}:` <{selSong.SongInfo.Query}>").ConfigureAwait(false);
} }
} else }
else
{ {
var curSong = musicPlayer.CurrentSong; var curSong = musicPlayer.CurrentSong;
if (curSong == null) if (curSong == null)
@ -837,7 +839,7 @@ namespace NadekoBot.Modules.Music
lastFinishedMessage = await textCh.SendMessage($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false); lastFinishedMessage = await textCh.SendMessage($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false);
if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube") if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube")
{ {
await QueueSong(queuer, textCh, voiceCh, await SearchHelper.GetRelatedVideoId(song.SongInfo.Query), silent, musicType).ConfigureAwait(false); await QueueSong(queuer.Server.CurrentUser, textCh, voiceCh, (await SearchHelper.GetRelatedVideoIds(song.SongInfo.Query, 4)).ToList().Shuffle().FirstOrDefault(), silent, musicType).ConfigureAwait(false);
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Translator
internal override void Init(CommandGroupBuilder cgb) internal override void Init(CommandGroupBuilder cgb)
{ {
cgb.CreateCommand(Module.Prefix + "translangs") cgb.CreateCommand(Module.Prefix + "translangs")
.Description("List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`") .Description($"List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
.Parameter("search", ParameterType.Optional) .Parameter("search", ParameterType.Optional)
.Do(ListLanguagesFunc()); .Do(ListLanguagesFunc());
} }

View File

@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Trello
}); });
cgb.CreateCommand(Prefix + "unbind") cgb.CreateCommand(Prefix + "unbind")
.Description("Unbinds a bot from the channel and board.") .Description($"Unbinds a bot from the channel and board. | `{Prefix}unbind`")
.Do(async e => .Do(async e =>
{ {
if (!NadekoBot.IsOwner(e.User.Id)) return; if (!NadekoBot.IsOwner(e.User.Id)) return;
@ -106,7 +106,7 @@ namespace NadekoBot.Modules.Trello
cgb.CreateCommand(Prefix + "lists") cgb.CreateCommand(Prefix + "lists")
.Alias(Prefix + "list") .Alias(Prefix + "list")
.Description("Lists all lists yo ;)") .Description($"Lists all lists yo ;) | {Prefix}list")
.Do(async e => .Do(async e =>
{ {
if (!NadekoBot.IsOwner(e.User.Id)) return; if (!NadekoBot.IsOwner(e.User.Id)) return;

View File

@ -231,6 +231,7 @@
<Compile Include="Modules\Music\Classes\MusicControls.cs" /> <Compile Include="Modules\Music\Classes\MusicControls.cs" />
<Compile Include="Modules\Music\Classes\PoopyBuffer.cs" /> <Compile Include="Modules\Music\Classes\PoopyBuffer.cs" />
<Compile Include="Modules\Music\Classes\Song.cs" /> <Compile Include="Modules\Music\Classes\Song.cs" />
<Compile Include="Modules\Music\Classes\SongBuffer.cs" />
<Compile Include="Modules\Music\Classes\SoundCloud.cs" /> <Compile Include="Modules\Music\Classes\SoundCloud.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" /> <Compile Include="Modules\Permissions\Classes\PermissionChecker.cs" />
<Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" /> <Compile Include="Modules\Permissions\Classes\PermissionHelper.cs" />