Merge pull request #437 from Kwoth/dev

0.99.9a
This commit is contained in:
Master Kwoth 2016-07-22 21:30:17 +02:00 committed by GitHub
commit 12134d1028
19 changed files with 164 additions and 128 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "discord.net"] [submodule "discord.net"]
path = discord.net path = discord.net
url = git://github.com/rogueexception/discord.net.git url = git://github.com/kwoth/discord.net.git

View File

@ -46,10 +46,10 @@ namespace NadekoBot.Extensions
if (num == 0) if (num == 0)
return string.Empty; return string.Empty;
if (num <= 3) if (num <= 3)
return string.Join("", str.Select(c => '.')); return string.Concat(str.Select(c => '.'));
if (str.Length < num) if (str.Length < num)
return str; return str;
return string.Join("", str.Take(num - 3)) + (hideDots ? "" : "..."); return string.Concat(str.Take(num - 3)) + (hideDots ? "" : "...");
} }
/// <summary> /// <summary>
/// Removes trailing S or ES (if specified) on the given string if the num is 1 /// Removes trailing S or ES (if specified) on the given string if the num is 1
@ -237,7 +237,7 @@ namespace NadekoBot.Extensions
public static string Matrix(this string s) public static string Matrix(this string s)
=> =>
string.Join("", s.Select(c => c.ToString() + " ̵̢̬̜͉̞̭̖̰͋̉̎ͬ̔̇̌̀".TrimTo(rng.Next(0, 12), true))); string.Concat(s.Select(c => c.ToString() + " ̵̢̬̜͉̞̭̖̰͋̉̎ͬ̔̇̌̀".TrimTo(rng.Next(0, 12), true)));
//.Replace("`", ""); //.Replace("`", "");
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)

View File

@ -384,7 +384,7 @@ namespace NadekoBot.Classes
{ {
var i = 0; var i = 0;
return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols) return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols)
.Select(ig => string.Join("", ig.Select(el => howToPrint(el))))) .Select(ig => string.Concat(ig.Select(el => howToPrint(el)))))
+ $"\n```"; + $"\n```";
} }
} }

View File

@ -738,7 +738,7 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(msg)) if (string.IsNullOrWhiteSpace(msg))
return; return;
var ids = e.GetArg("ids").Split('-'); var ids = e.GetArg("ids").Split('|');
if (ids.Length != 2) if (ids.Length != 2)
return; return;
var sid = ulong.Parse(ids[0]); var sid = ulong.Parse(ids[0]);

View File

@ -1,6 +1,8 @@
using Discord.Commands; using Discord.Commands;
using Discord.Net;
using NadekoBot.Classes; using NadekoBot.Classes;
using NadekoBot.Modules.Permissions.Classes; using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -144,7 +146,10 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
await e.User.AddRoles(role).ConfigureAwait(false); await e.User.AddRoles(role).ConfigureAwait(false);
} }
catch catch(HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
}
catch (Exception)
{ {
await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
} }

View File

@ -9,13 +9,14 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Conversations namespace NadekoBot.Modules.Conversations
{ {
internal class Conversations : DiscordModule internal class Conversations : DiscordModule
{ {
private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥"; private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥";
public Conversations() public Conversations()
{ {
commands.Add(new RipCommand(this)); commands.Add(new RipCommand(this));
@ -153,22 +154,23 @@ namespace NadekoBot.Modules.Conversations
.Parameter("times", ParameterType.Optional) .Parameter("times", ParameterType.Optional)
.Do(async e => .Do(async e =>
{ {
var count = 1; int count;
int.TryParse(e.Args[0], out count); if (string.IsNullOrWhiteSpace(e.Args[0]))
if (count == 0)
count = 1; count = 1;
else
int.TryParse(e.Args[0], out count);
if (count < 1 || count > 12) if (count < 1 || count > 12)
{ {
await e.Channel.SendMessage("Number must be between 0 and 12").ConfigureAwait(false); await e.Channel.SendMessage("Number must be between 1 and 12").ConfigureAwait(false);
return; return;
} }
var str = ""; var str = new StringBuilder();
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
str += firestr; str.Append(firestr);
} }
await e.Channel.SendMessage(str).ConfigureAwait(false); await e.Channel.SendMessage(str.ToString()).ConfigureAwait(false);
}); });
cgb.CreateCommand("dump") cgb.CreateCommand("dump")

View File

@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Gambling.Helpers
var orderedPool = cardPool.OrderBy(x => r.Next()); var orderedPool = cardPool.OrderBy(x => r.Next());
cardPool = cardPool as List<Card> ?? orderedPool.ToList(); cardPool = cardPool as List<Card> ?? orderedPool.ToList();
} }
public override string ToString() => string.Join("", cardPool.Select(c => c.ToString())) + Environment.NewLine; public override string ToString() => string.Concat(cardPool.Select(c => c.ToString())) + Environment.NewLine;
private static void InitHandValues() private static void InitHandValues()
{ {
@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Gambling.Helpers
{ {
return kvp.Key; return kvp.Key;
} }
return "High card " + cards.Max().GetName(); return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetName() ?? cards.Max().GetName());
} }
} }
} }

View File

@ -102,7 +102,6 @@ namespace NadekoBot.Modules.Games.Commands
var file = GetRandomCurrencyImagePath(); var file = GetRandomCurrencyImagePath();
Message msg; Message msg;
//todo send message after, not in lock
if (file == null) if (file == null)
msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult();
else else

View File

@ -190,8 +190,6 @@ namespace NadekoBot.Modules.Games.Commands
await e.Channel.SendMessage("Added new article for typing game.").ConfigureAwait(false); await e.Channel.SendMessage("Added new article for typing game.").ConfigureAwait(false);
}); });
//todo add user submissions
} }
} }
} }

View File

@ -5,6 +5,7 @@ using NadekoBot.Modules.Permissions.Classes;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Classes.Help.Commands namespace NadekoBot.Classes.Help.Commands
@ -24,8 +25,13 @@ namespace NadekoBot.Classes.Help.Commands
var com = NadekoBot.Client.GetService<CommandService>().AllCommands var com = NadekoBot.Client.GetService<CommandService>().AllCommands
.FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) || .FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) ||
c.Aliases.Select(a => a.ToLowerInvariant()).Contains(comToFind)); c.Aliases.Select(a => a.ToLowerInvariant()).Contains(comToFind));
var str = "";
var alias = com.Aliases.FirstOrDefault();
if (alias != null)
str = $" / `{ com.Aliases.FirstOrDefault()}`";
if (com != null) if (com != null)
await e.Channel.SendMessage($"**__Help for `{com.Text}`__ / __`{("" + com.Aliases.FirstOrDefault() + "" ?? "")}`__**\n**Desc:** {com.Description.Replace("|", "\n**Usage:**")}").ConfigureAwait(false); await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**",1)}").ConfigureAwait(false);
}).ConfigureAwait(false); }).ConfigureAwait(false);
}; };
public static string HelpString { public static string HelpString {

View File

@ -2,7 +2,9 @@
using Discord.Audio; using Discord.Audio;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes namespace NadekoBot.Modules.Music.Classes
@ -20,22 +22,18 @@ namespace NadekoBot.Modules.Music.Classes
{ {
Resolving, Resolving,
Queued, Queued,
Buffering, //not using it atm
Playing, Playing,
Completed Completed
} }
public class MusicPlayer public class MusicPlayer
{ {
public static int MaximumPlaylistSize => 50;
private IAudioClient audioClient { get; set; } private IAudioClient audioClient { get; set; }
private readonly List<Song> playlist = new List<Song>(); private readonly List<Song> playlist = new List<Song>();
public IReadOnlyCollection<Song> Playlist => playlist; public IReadOnlyCollection<Song> Playlist => playlist;
private readonly object playlistLock = new object();
public Song CurrentSong { get; set; } = default(Song); public Song CurrentSong { get; private set; }
private CancellationTokenSource SongCancelSource { get; set; } private CancellationTokenSource SongCancelSource { get; set; }
private CancellationToken cancelToken { get; set; } private CancellationToken cancelToken { get; set; }
@ -54,6 +52,8 @@ namespace NadekoBot.Modules.Music.Classes
public bool Autoplay { get; set; } = false; public bool Autoplay { get; set; } = false;
public uint MaxQueueSize { get; set; } = 0; public uint MaxQueueSize { get; set; } = 0;
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>();
public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume)
{ {
if (startingVoiceChannel == null) if (startingVoiceChannel == null)
@ -68,85 +68,110 @@ namespace NadekoBot.Modules.Music.Classes
Task.Run(async () => Task.Run(async () =>
{ {
while (!Destroyed) try
{ {
try while (!Destroyed)
{
if (audioClient?.State != ConnectionState.Connected)
audioClient = await PlaybackVoiceChannel.JoinAudio().ConfigureAwait(false);
}
catch
{
await Task.Delay(1000).ConfigureAwait(false);
continue;
}
CurrentSong = GetNextSong();
var curSong = CurrentSong;
if (curSong != null)
{ {
try try
{ {
OnStarted(this, curSong); Action action;
await curSong.Play(audioClient, cancelToken).ConfigureAwait(false); if (actionQueue.TryDequeue(out action))
{
action();
}
} }
catch (OperationCanceledException) finally
{ {
Console.WriteLine("Song canceled"); await Task.Delay(100).ConfigureAwait(false);
} }
catch (Exception ex)
{
Console.WriteLine($"Exception in PlaySong: {ex}");
}
OnCompleted(this, curSong);
curSong = CurrentSong; //to check if its null now
if (curSong != null)
if (RepeatSong)
playlist.Insert(0, curSong);
else if (RepeatPlaylist)
playlist.Insert(playlist.Count, curSong);
SongCancelSource = new CancellationTokenSource();
cancelToken = SongCancelSource.Token;
} }
await Task.Delay(1000).ConfigureAwait(false);
} }
}); catch (Exception ex)
{
Console.WriteLine("Action queue crashed");
Console.WriteLine(ex);
}
}).ConfigureAwait(false);
var t = new Thread(new ThreadStart(async () =>
{
try
{
while (!Destroyed)
{
try
{
if (audioClient?.State != ConnectionState.Connected)
{
audioClient = await PlaybackVoiceChannel.JoinAudio();
continue;
}
CurrentSong = GetNextSong();
RemoveSongAt(0);
if (CurrentSong == null)
continue;
try
{
OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken);
}
catch (OperationCanceledException)
{
Console.WriteLine("Song canceled");
SongCancelSource = new CancellationTokenSource();
cancelToken = SongCancelSource.Token;
}
OnCompleted(this, CurrentSong);
if (RepeatPlaylist)
AddSong(CurrentSong, CurrentSong.QueuerName);
if (RepeatSong)
AddSong(CurrentSong, 0);
}
finally
{
await Task.Delay(300).ConfigureAwait(false);
CurrentSong = null;
}
}
}
catch (Exception ex) {
Console.WriteLine("Music thread crashed.");
Console.WriteLine(ex);
}
}));
t.Start();
} }
public void Next() public void Next()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (!SongCancelSource.IsCancellationRequested) Paused = false;
{ SongCancelSource.Cancel();
Paused = false; });
SongCancelSource.Cancel();
}
}
} }
public void Stop() public void Stop()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Clear();
CurrentSong = null;
RepeatPlaylist = false; RepeatPlaylist = false;
RepeatSong = false; RepeatSong = false;
playlist.Clear();
if (!SongCancelSource.IsCancellationRequested) if (!SongCancelSource.IsCancellationRequested)
SongCancelSource.Cancel(); SongCancelSource.Cancel();
} });
} }
public void TogglePause() => Paused = !Paused; public void TogglePause() => Paused = !Paused;
public void Shuffle()
{
lock (playlistLock)
{
playlist.Shuffle();
}
}
public int SetVolume(int volume) public int SetVolume(int volume)
{ {
if (volume < 0) if (volume < 0)
@ -158,16 +183,15 @@ namespace NadekoBot.Modules.Music.Classes
return volume; return volume;
} }
private Song GetNextSong() private Song GetNextSong() =>
playlist.FirstOrDefault();
public void Shuffle()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (playlist.Count == 0) playlist.Shuffle();
return null; });
var toReturn = playlist[0];
playlist.RemoveAt(0);
return toReturn;
}
} }
public void AddSong(Song s, string username) public void AddSong(Song s, string username)
@ -175,42 +199,64 @@ namespace NadekoBot.Modules.Music.Classes
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
ThrowIfQueueFull(); ThrowIfQueueFull();
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
s.MusicPlayer = this; s.MusicPlayer = this;
s.QueuerName = username.TrimTo(10); s.QueuerName = username.TrimTo(10);
playlist.Add(s); playlist.Add(s);
} });
} }
public void AddSong(Song s, int index) public void AddSong(Song s, int index)
{ {
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Insert(index, s); playlist.Insert(index, s);
} });
} }
public void RemoveSong(Song s) public void RemoveSong(Song s)
{ {
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Remove(s); playlist.Remove(s);
} });
} }
public void RemoveSongAt(int index) public void RemoveSongAt(int index)
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (index < 0 || index >= playlist.Count) if (index < 0 || index >= playlist.Count)
throw new ArgumentException("Invalid index"); return;
playlist.RemoveAt(index); playlist.RemoveAt(index);
} });
}
internal void ClearQueue()
{
actionQueue.Enqueue(() =>
{
playlist.Clear();
});
}
public void Destroy()
{
actionQueue.Enqueue(() =>
{
RepeatPlaylist = false;
RepeatSong = false;
Destroyed = true;
playlist.Clear();
if (!SongCancelSource.IsCancellationRequested)
SongCancelSource.Cancel();
audioClient.Disconnect();
});
} }
internal Task MoveToVoiceChannel(Channel voiceChannel) internal Task MoveToVoiceChannel(Channel voiceChannel)
@ -221,27 +267,6 @@ namespace NadekoBot.Modules.Music.Classes
return PlaybackVoiceChannel.JoinAudio(); return PlaybackVoiceChannel.JoinAudio();
} }
internal void ClearQueue()
{
lock (playlistLock)
{
playlist.Clear();
}
}
public void Destroy()
{
lock (playlistLock)
{
playlist.Clear();
Destroyed = true;
CurrentSong = null;
if (!SongCancelSource.IsCancellationRequested)
SongCancelSource.Cancel();
audioClient.Disconnect();
}
}
internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong;
internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist;

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; } public SongInfo SongInfo { get; }
public string QueuerName { get; set; } public string QueuerName { get; set; }
private PoopyBuffer songBuffer { get; } = new PoopyBuffer(NadekoBot.Config.BufferSize); private PoopyBuffer songBuffer { get; set; }
private bool prebufferingComplete { get; set; } = false; private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
@ -137,6 +137,9 @@ namespace NadekoBot.Modules.Music.Classes
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{ {
// initialize the buffer here because if this song was playing before (requeued), we must delete old buffer data
songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize);
var bufferTask = BufferSong(cancelToken).ConfigureAwait(false); var bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
var bufferAttempts = 0; var bufferAttempts = 0;
const int waitPerAttempt = 500; const int waitPerAttempt = 500;
@ -145,7 +148,6 @@ namespace NadekoBot.Modules.Music.Classes
{ {
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false); await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false);
} }
cancelToken.ThrowIfCancellationRequested();
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
const int blockSize = 3840; const int blockSize = 3840;
var attempt = 0; var attempt = 0;

View File

@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Music
else if (musicPlayer.RepeatPlaylist) else if (musicPlayer.RepeatPlaylist)
toSend += "🔁"; toSend += "🔁";
toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` "; toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` ";
if (musicPlayer.Playlist.Count >= MusicPlayer.MaximumPlaylistSize) if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
toSend += "**Song queue is full!**\n"; toSend += "**Song queue is full!**\n";
else else
toSend += "\n"; toSend += "\n";
@ -300,7 +300,6 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false); await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false);
return; return;
} }
//todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE
var idArray = ids as string[] ?? ids.ToArray(); var idArray = ids as string[] ?? ids.ToArray();
var count = idArray.Length; var count = idArray.Length;
var msg = var msg =

View File

@ -26,8 +26,8 @@ namespace NadekoBot.Modules.Permissions.Classes
{ {
while (true) while (true)
{ {
//blacklist is cleared every 1.75 seconds. That is the most time anyone will be blocked //blacklist is cleared every 1.00 seconds. That is the most time anyone will be blocked
await Task.Delay(1750).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
timeBlackList.Clear(); timeBlackList.Clear();
} }
}); });

View File

@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches.Commands
string.Join("\n", JsonConvert.DeserializeObject<Dictionary<string, string>>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/")) string.Join("\n", JsonConvert.DeserializeObject<Dictionary<string, string>>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/"))
.Select(kvp => Path.GetFileName(kvp.Value)) .Select(kvp => Path.GetFileName(kvp.Value))
.GroupBy(item => (i++) / 4) .GroupBy(item => (i++) / 4)
.Select(ig => string.Join("", ig.Select(el => $"{el,-17}")))) .Select(ig => string.Concat(ig.Select(el => $"{el,-17}"))))
+ $"\n```").ConfigureAwait(false); + $"\n```").ConfigureAwait(false);
}); });

View File

@ -226,10 +226,10 @@ $@"🌍 **Weather for** 【{obj["target"]}】
.Parameter("terms", ParameterType.Unparsed) .Parameter("terms", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
var terms = e.GetArg("terms")?.Trim().Replace(' ', '+'); var terms = e.GetArg("terms")?.Trim();
if (string.IsNullOrWhiteSpace(terms)) if (string.IsNullOrWhiteSpace(terms))
return; return;
await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms) }") await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms).Replace(' ', '+') }")
.ConfigureAwait(false); .ConfigureAwait(false);
}); });

View File

@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Translator.Helpers
text = await http.GetStringAsync(url).ConfigureAwait(false); text = await http.GetStringAsync(url).ConfigureAwait(false);
} }
return JArray.Parse(text)[0][0][0].ToString(); return (string.Concat(JArray.Parse(text)[0].Select(x => x[0])));
} }
#endregion #endregion

View File

@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Utility
if (arr.Length == 0) if (arr.Length == 0)
await e.Channel.SendMessage("Nobody. (not 100% sure)").ConfigureAwait(false); await e.Channel.SendMessage("Nobody. (not 100% sure)").ConfigureAwait(false);
else else
await e.Channel.SendMessage("```xl\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Join("", ig.Select(el => $"• {el,-35}")))) + "\n```").ConfigureAwait(false); await e.Channel.SendMessage("```xl\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Concat(ig.Select(el => $"• {el,-35}")))) + "\n```").ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "inrole") cgb.CreateCommand(Prefix + "inrole")

@ -1 +1 @@
Subproject commit 6bfeaaddf0cbc83fe0ca44e6164f61c6f8fdaf27 Subproject commit 3e519b5e0b33175e5a5ca247322b7082de484e15