small refactor

This commit is contained in:
Master Kwoth 2016-04-01 22:50:41 +02:00
parent 52923e2680
commit 450ca709b4
3 changed files with 260 additions and 171 deletions

View File

@ -0,0 +1,120 @@
using System;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes.Music
{
/// <summary>
/// 💩
/// </summary>
public class PoopyBuffer
{
private readonly byte[] ringBuffer;
public int WritePosition { get; private set; } = 0;
public int ReadPosition { get; private set; } = 0;
public int ContentLength => (WritePosition >= ReadPosition ?
WritePosition - ReadPosition :
(BufferSize - ReadPosition) + WritePosition);
public int BufferSize { get; }
private readonly object readWriteLock = new object();
public PoopyBuffer(int size)
{
if (size <= 0)
throw new ArgumentException();
BufferSize = size;
ringBuffer = new byte[size];
}
public int Read(byte[] buffer, int count)
{
if (buffer.Length < count)
throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
lock (readWriteLock)
{
//read as much as you can if you're reading too much
if (count > ContentLength)
count = ContentLength;
//if nothing to read, return 0
if (WritePosition == ReadPosition)
return 0;
// if buffer is in the "normal" state, just read
if (WritePosition > ReadPosition)
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
//else ReadPos <Writepos
// buffer is in its inverted state
// A: if i can read as much as possible without hitting the buffer.length, read that
if (count + ReadPosition <= BufferSize)
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
// B: if i can't read as much, read to the end,
var readNormaly = BufferSize - ReadPosition;
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
//then read the remaining amount from the start
var readFromStart = count - readNormaly;
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
ReadPosition = readFromStart;
return count;
}
}
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken)
{
if (count > buffer.Length)
throw new ArgumentException();
while (ContentLength + count > BufferSize)
{
await Task.Delay(20, cancelToken);
if (cancelToken.IsCancellationRequested)
return;
}
//the while above assures that i cannot write past readposition with my write, so i don't have to check
// *unless its multithreaded or task is not awaited
lock (readWriteLock)
{
// if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize)
{
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
WritePosition += count;
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
return;
}
// otherwise, i have to write to the end, then write the rest from the start
var wroteNormaly = BufferSize - WritePosition;
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
var wroteFromStart = count - wroteNormaly;
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
WritePosition = wroteFromStart;
}
}
}
}

View File

@ -10,116 +10,17 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using VideoLibrary; using VideoLibrary;
namespace NadekoBot.Classes.Music { namespace NadekoBot.Classes.Music
public class SongInfo { {
public class SongInfo
{
public string Provider { get; internal set; } public string Provider { get; internal set; }
public MusicType ProviderType { get; internal set; } public MusicType ProviderType { get; internal set; }
public string Title { get; internal set; } public string Title { get; internal set; }
public string Uri { get; internal set; } public string Uri { get; internal set; }
} }
/// <summary> public class Song
/// 💩 {
/// </summary>
public class PoopyBuffer {
private readonly byte[] ringBuffer;
public int WritePosition { get; private set; } = 0;
public int ReadPosition { get; private set; } = 0;
public int ContentLength => (WritePosition >= ReadPosition ?
WritePosition - ReadPosition :
(BufferSize - ReadPosition) + WritePosition);
public int BufferSize { get; }
private readonly object readWriteLock = new object();
public PoopyBuffer(int size) {
if (size <= 0)
throw new ArgumentException();
BufferSize = size;
ringBuffer = new byte[size];
}
public int Read(byte[] buffer, int count) {
if (buffer.Length < count)
throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
lock (readWriteLock) {
//read as much as you can if you're reading too much
if (count > ContentLength)
count = ContentLength;
//if nothing to read, return 0
if (WritePosition == ReadPosition)
return 0;
// if buffer is in the "normal" state, just read
if (WritePosition > ReadPosition) {
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
//else ReadPos <Writepos
// buffer is in its inverted state
// A: if i can read as much as possible without hitting the buffer.length, read that
if (count + ReadPosition <= BufferSize) {
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
// B: if i can't read as much, read to the end,
var readNormaly = BufferSize - ReadPosition;
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
//then read the remaining amount from the start
var readFromStart = count - readNormaly;
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
ReadPosition = readFromStart;
return count;
}
}
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken) {
if (count > buffer.Length)
throw new ArgumentException();
while (ContentLength + count > BufferSize) {
await Task.Delay(20, cancelToken);
if (cancelToken.IsCancellationRequested)
return;
}
//the while above assures that i cannot write past readposition with my write, so i don't have to check
// *unless its multithreaded or task is not awaited
lock (readWriteLock) {
// if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize) {
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
WritePosition += count;
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
return;
}
// otherwise, i have to write to the end, then write the rest from the start
var wroteNormaly = BufferSize - WritePosition;
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
var wroteFromStart = count - wroteNormaly;
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
WritePosition = wroteFromStart;
}
}
}
public class Song {
public StreamState State { get; internal set; } public StreamState State { get; internal set; }
public string PrettyName => public string PrettyName =>
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`"; $"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`";
@ -130,22 +31,27 @@ namespace NadekoBot.Classes.Music {
private bool prebufferingComplete { get; set; } = false; private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime() { public string PrettyCurrentTime()
{
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50); var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】"; return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
} }
private ulong bytesSent { get; set; } = 0; private ulong bytesSent { get; set; } = 0;
private Song(SongInfo songInfo) { private Song(SongInfo songInfo)
{
this.SongInfo = songInfo; this.SongInfo = songInfo;
} }
private Task BufferSong(CancellationToken cancelToken) => private Task BufferSong(CancellationToken cancelToken) =>
Task.Run(async () => { Task.Factory.StartNew(async () =>
{
Process p = null; Process p = null;
try { try
p = Process.Start(new ProcessStartInfo { {
p = Process.Start(new ProcessStartInfo
{
FileName = "ffmpeg", FileName = "ffmpeg",
Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet", Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false, UseShellExecute = false,
@ -156,11 +62,15 @@ namespace NadekoBot.Classes.Music {
const int blockSize = 3840; const int blockSize = 3840;
var buffer = new byte[blockSize]; var buffer = new byte[blockSize];
var attempt = 0; var attempt = 0;
while (!cancelToken.IsCancellationRequested) { while (!cancelToken.IsCancellationRequested)
{
var read = 0; var read = 0;
try { try
{
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken); read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken);
} catch { }
catch
{
return; return;
} }
if (read == 0) if (read == 0)
@ -168,7 +78,8 @@ namespace NadekoBot.Classes.Music {
break; break;
else else
await Task.Delay(100, cancelToken); await Task.Delay(100, cancelToken);
else { else
{
attempt = 0; attempt = 0;
await Task.Delay(5, cancelToken); await Task.Delay(5, cancelToken);
} }
@ -176,24 +87,35 @@ namespace NadekoBot.Classes.Music {
if (songBuffer.ContentLength > 2.MB()) if (songBuffer.ContentLength > 2.MB())
prebufferingComplete = true; prebufferingComplete = true;
} }
} catch (Exception ex) { }
catch (Exception ex)
{
Console.WriteLine($"Buffering errored: {ex.Message}"); Console.WriteLine($"Buffering errored: {ex.Message}");
} finally { }
finally
{
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]"); Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
if (p != null) { if (p != null)
try { {
try
{
p.Kill(); p.Kill();
} catch { } }
catch { }
p.Dispose(); p.Dispose();
} }
} }
}); }, TaskCreationOptions.LongRunning);
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{
var bufferTask = new ConfiguredTaskAwaitable(); var bufferTask = new ConfiguredTaskAwaitable();
try { try
{
bufferTask = BufferSong(cancelToken).ConfigureAwait(false); bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
} catch (Exception ex) { }
catch (Exception ex)
{
var clr = Console.ForegroundColor; var clr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}"); Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}");
@ -202,26 +124,31 @@ namespace NadekoBot.Classes.Music {
var bufferAttempts = 0; var bufferAttempts = 0;
const int waitPerAttempt = 500; const int waitPerAttempt = 500;
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9; var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) { while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes)
{
await Task.Delay(waitPerAttempt, cancelToken); await Task.Delay(waitPerAttempt, cancelToken);
} }
cancelToken.ThrowIfCancellationRequested(); 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;
while (!cancelToken.IsCancellationRequested) { while (!cancelToken.IsCancellationRequested)
{
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize]; byte[] buffer = new byte[blockSize];
var read = songBuffer.Read(buffer, blockSize); var read = songBuffer.Read(buffer, blockSize);
unchecked { unchecked
{
bytesSent += (ulong)read; bytesSent += (ulong)read;
} }
if (read == 0) if (read == 0)
if (attempt++ == 20) { if (attempt++ == 20)
{
voiceClient.Wait(); voiceClient.Wait();
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]"); Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
break; break;
} else }
else
await Task.Delay(100, cancelToken); await Task.Delay(100, cancelToken);
else else
attempt = 0; attempt = 0;
@ -239,11 +166,13 @@ namespace NadekoBot.Classes.Music {
} }
//stackoverflow ftw //stackoverflow ftw
private static byte[] AdjustVolume(byte[] audioSamples, float volume) { private static byte[] AdjustVolume(byte[] audioSamples, float volume)
{
if (Math.Abs(volume - 1.0f) < 0.01f) if (Math.Abs(volume - 1.0f) < 0.01f)
return audioSamples; return audioSamples;
var array = new byte[audioSamples.Length]; var array = new byte[audioSamples.Length];
for (var i = 0; i < array.Length; i += 2) { for (var i = 0; i < array.Length; i += 2)
{
// convert byte pair to int // convert byte pair to int
short buf1 = audioSamples[i + 1]; short buf1 = audioSamples[i + 1];
@ -263,35 +192,43 @@ namespace NadekoBot.Classes.Music {
return array; return array;
} }
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal) { public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
{
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query)); throw new ArgumentNullException(nameof(query));
if (musicType != MusicType.Local && IsRadioLink(query)) { if (musicType != MusicType.Local && IsRadioLink(query))
{
musicType = MusicType.Radio; musicType = MusicType.Radio;
query = await HandleStreamContainers(query) ?? query; query = await HandleStreamContainers(query) ?? query;
} }
try { try
switch (musicType) { {
switch (musicType)
{
case MusicType.Local: case MusicType.Local:
return new Song(new SongInfo { return new Song(new SongInfo
{
Uri = "\"" + Path.GetFullPath(query) + "\"", Uri = "\"" + Path.GetFullPath(query) + "\"",
Title = Path.GetFileNameWithoutExtension(query), Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File", Provider = "Local File",
ProviderType = musicType, ProviderType = musicType,
}); });
case MusicType.Radio: case MusicType.Radio:
return new Song(new SongInfo { return new Song(new SongInfo
{
Uri = query, Uri = query,
Title = $"{query}", Title = $"{query}",
Provider = "Radio Stream", Provider = "Radio Stream",
ProviderType = musicType, ProviderType = musicType,
}); });
} }
if (SoundCloud.Default.IsSoundCloudLink(query)) { if (SoundCloud.Default.IsSoundCloudLink(query))
{
var svideo = await SoundCloud.Default.GetVideoAsync(query); var svideo = await SoundCloud.Default.GetVideoAsync(query);
return new Song(new SongInfo { return new Song(new SongInfo
{
Title = svideo.FullName, Title = svideo.FullName,
Provider = "SoundCloud", Provider = "SoundCloud",
Uri = svideo.StreamLink, Uri = svideo.StreamLink,
@ -310,76 +247,99 @@ namespace NadekoBot.Classes.Music {
if (video == null) // do something with this error if (video == null) // do something with this error
throw new Exception("Could not load any video elements based on the query."); throw new Exception("Could not load any video elements based on the query.");
return new Song(new SongInfo { return new Song(new SongInfo
{
Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube" Title = video.Title.Substring(0, video.Title.Length - 10), // removing trailing "- You Tube"
Provider = "YouTube", Provider = "YouTube",
Uri = video.Uri, Uri = video.Uri,
ProviderType = musicType, ProviderType = musicType,
}); });
} catch (Exception ex) { }
catch (Exception ex)
{
Console.WriteLine($"Failed resolving the link.{ex.Message}"); Console.WriteLine($"Failed resolving the link.{ex.Message}");
return null; return null;
} }
} }
private static async Task<string> HandleStreamContainers(string query) { private static async Task<string> HandleStreamContainers(string query)
{
string file = null; string file = null;
try { try
{
file = await SearchHelper.GetResponseStringAsync(query); file = await SearchHelper.GetResponseStringAsync(query);
} catch { }
catch
{
return query; return query;
} }
if (query.Contains(".pls")) { if (query.Contains(".pls"))
{
//File1=http://armitunes.com:8000/ //File1=http://armitunes.com:8000/
//Regex.Match(query) //Regex.Match(query)
try { try
{
var m = Regex.Match(file, "File1=(?<url>.*?)\\n"); var m = Regex.Match(file, "File1=(?<url>.*?)\\n");
var res = m.Groups["url"]?.ToString(); var res = m.Groups["url"]?.ToString();
return res?.Trim(); return res?.Trim();
} catch { }
catch
{
Console.WriteLine($"Failed reading .pls:\n{file}"); Console.WriteLine($"Failed reading .pls:\n{file}");
return null; return null;
} }
} }
if (query.Contains(".m3u")) { if (query.Contains(".m3u"))
{
/* /*
# This is a comment # This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3 C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3 C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/ */
try { try
{
var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline); var m = Regex.Match(file, "(?<url>^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString(); var res = m.Groups["url"]?.ToString();
return res?.Trim(); return res?.Trim();
} catch { }
catch
{
Console.WriteLine($"Failed reading .m3u:\n{file}"); Console.WriteLine($"Failed reading .m3u:\n{file}");
return null; return null;
} }
} }
if (query.Contains(".asx")) { if (query.Contains(".asx"))
{
//<ref href="http://armitunes.com:8000"/> //<ref href="http://armitunes.com:8000"/>
try { try
{
var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\""); var m = Regex.Match(file, "<ref href=\"(?<url>.*?)\"");
var res = m.Groups["url"]?.ToString(); var res = m.Groups["url"]?.ToString();
return res?.Trim(); return res?.Trim();
} catch { }
catch
{
Console.WriteLine($"Failed reading .asx:\n{file}"); Console.WriteLine($"Failed reading .asx:\n{file}");
return null; return null;
} }
} }
if (query.Contains(".xspf")) { if (query.Contains(".xspf"))
{
/* /*
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/"> <playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList> <trackList>
<track><location>file:///mp3s/song_1.mp3</location></track> <track><location>file:///mp3s/song_1.mp3</location></track>
*/ */
try { try
{
var m = Regex.Match(file, "<location>(?<url>.*?)</location>"); var m = Regex.Match(file, "<location>(?<url>.*?)</location>");
var res = m.Groups["url"]?.ToString(); var res = m.Groups["url"]?.ToString();
return res?.Trim(); return res?.Trim();
} catch { }
catch
{
Console.WriteLine($"Failed reading .xspf:\n{file}"); Console.WriteLine($"Failed reading .xspf:\n{file}");
return null; return null;
} }

View File

@ -1,35 +1,41 @@
using System; using Discord.Commands;
using System.Threading.Tasks;
using Discord.Commands;
using System.Collections.Concurrent;
using Discord;
using NadekoBot.Modules; using NadekoBot.Modules;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame; using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
namespace NadekoBot.Commands { namespace NadekoBot.Commands
internal class Trivia : DiscordCommand { {
internal class Trivia : DiscordCommand
{
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>(); public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
public Func<CommandEventArgs, Task> DoFunc() => async e => { public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
TriviaGame trivia; TriviaGame trivia;
if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia)) { if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia))
{
var triviaGame = new TriviaGame(e); var triviaGame = new TriviaGame(e);
if (RunningTrivias.TryAdd(e.Server.Id, triviaGame)) if (RunningTrivias.TryAdd(e.Server.Id, triviaGame))
await e.Channel.SendMessage("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**"); await e.Channel.SendMessage("**Trivia game started!**\nFirst player to get to 10 points wins! You have 30 seconds per question.\nUse command `tq` if game was started by accident.**");
else else
await triviaGame.StopGame(); await triviaGame.StopGame();
} else }
else
await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion); await e.Channel.SendMessage("Trivia game is already running on this server.\n" + trivia.CurrentQuestion);
}; };
internal override void Init(CommandGroupBuilder cgb) { internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "t") cgb.CreateCommand(Module.Prefix + "t")
.Description("Starts a game of trivia.") .Description("Starts a game of trivia.")
.Do(DoFunc()); .Do(DoFunc());
cgb.CreateCommand(Module.Prefix + "tl") cgb.CreateCommand(Module.Prefix + "tl")
.Description("Shows a current trivia leaderboard.") .Description("Shows a current trivia leaderboard.")
.Do(async e=> { .Do(async e =>
{
TriviaGame trivia; TriviaGame trivia;
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia)) if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
await e.Channel.SendMessage(trivia.GetLeaderboard()); await e.Channel.SendMessage(trivia.GetLeaderboard());
@ -39,11 +45,14 @@ namespace NadekoBot.Commands {
cgb.CreateCommand(Module.Prefix + "tq") cgb.CreateCommand(Module.Prefix + "tq")
.Description("Quits current trivia after current question.") .Description("Quits current trivia after current question.")
.Do(async e=> { .Do(async e =>
{
TriviaGame trivia; TriviaGame trivia;
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia)) { if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
{
await trivia.StopGame(); await trivia.StopGame();
} else }
else
await e.Channel.SendMessage("No trivia is running on this server."); await e.Channel.SendMessage("No trivia is running on this server.");
}); });
} }