diff --git a/NadekoBot/Classes/Music/PoopyBuffer.cs b/NadekoBot/Classes/Music/PoopyBuffer.cs
new file mode 100644
index 00000000..9d59d35b
--- /dev/null
+++ b/NadekoBot/Classes/Music/PoopyBuffer.cs
@@ -0,0 +1,120 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace NadekoBot.Classes.Music
+{
+
+ ///
+ /// 💩
+ ///
+ 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 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;
+ }
+ }
+ }
+}
diff --git a/NadekoBot/Classes/Music/Song.cs b/NadekoBot/Classes/Music/Song.cs
index 54a001fc..dab40651 100644
--- a/NadekoBot/Classes/Music/Song.cs
+++ b/NadekoBot/Classes/Music/Song.cs
@@ -10,116 +10,17 @@ using System.Threading;
using System.Threading.Tasks;
using VideoLibrary;
-namespace NadekoBot.Classes.Music {
- public class SongInfo {
+namespace NadekoBot.Classes.Music
+{
+ public class SongInfo
+ {
public string Provider { get; internal set; }
public MusicType ProviderType { get; internal set; }
public string Title { get; internal set; }
public string Uri { get; internal set; }
}
- ///
- /// 💩
- ///
- 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 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 class Song
+ {
public StreamState State { get; internal set; }
public string PrettyName =>
$"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`";
@@ -130,22 +31,27 @@ namespace NadekoBot.Classes.Music {
private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; }
- public string PrettyCurrentTime() {
+ public string PrettyCurrentTime()
+ {
var time = TimeSpan.FromSeconds(bytesSent / 3840 / 50);
return $"【{(int)time.TotalMinutes}m {time.Seconds}s】";
}
private ulong bytesSent { get; set; } = 0;
- private Song(SongInfo songInfo) {
+ private Song(SongInfo songInfo)
+ {
this.SongInfo = songInfo;
}
private Task BufferSong(CancellationToken cancelToken) =>
- Task.Run(async () => {
+ Task.Factory.StartNew(async () =>
+ {
Process p = null;
- try {
- p = Process.Start(new ProcessStartInfo {
+ try
+ {
+ p = Process.Start(new ProcessStartInfo
+ {
FileName = "ffmpeg",
Arguments = $"-i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet",
UseShellExecute = false,
@@ -156,11 +62,15 @@ namespace NadekoBot.Classes.Music {
const int blockSize = 3840;
var buffer = new byte[blockSize];
var attempt = 0;
- while (!cancelToken.IsCancellationRequested) {
+ while (!cancelToken.IsCancellationRequested)
+ {
var read = 0;
- try {
+ try
+ {
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken);
- } catch {
+ }
+ catch
+ {
return;
}
if (read == 0)
@@ -168,7 +78,8 @@ namespace NadekoBot.Classes.Music {
break;
else
await Task.Delay(100, cancelToken);
- else {
+ else
+ {
attempt = 0;
await Task.Delay(5, cancelToken);
}
@@ -176,24 +87,35 @@ namespace NadekoBot.Classes.Music {
if (songBuffer.ContentLength > 2.MB())
prebufferingComplete = true;
}
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
Console.WriteLine($"Buffering errored: {ex.Message}");
- } finally {
+ }
+ finally
+ {
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
- if (p != null) {
- try {
+ if (p != null)
+ {
+ try
+ {
p.Kill();
- } catch { }
+ }
+ catch { }
p.Dispose();
}
}
- });
+ }, TaskCreationOptions.LongRunning);
- internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) {
+ internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
+ {
var bufferTask = new ConfiguredTaskAwaitable();
- try {
+ try
+ {
bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
var clr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"ERR BUFFER START : {ex.Message}\n{ex}");
@@ -202,26 +124,31 @@ namespace NadekoBot.Classes.Music {
var bufferAttempts = 0;
const int waitPerAttempt = 500;
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
- while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) {
+ while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes)
+ {
await Task.Delay(waitPerAttempt, cancelToken);
}
cancelToken.ThrowIfCancellationRequested();
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
const int blockSize = 3840;
var attempt = 0;
- while (!cancelToken.IsCancellationRequested) {
+ while (!cancelToken.IsCancellationRequested)
+ {
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize];
var read = songBuffer.Read(buffer, blockSize);
- unchecked {
+ unchecked
+ {
bytesSent += (ulong)read;
}
if (read == 0)
- if (attempt++ == 20) {
+ if (attempt++ == 20)
+ {
voiceClient.Wait();
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
break;
- } else
+ }
+ else
await Task.Delay(100, cancelToken);
else
attempt = 0;
@@ -239,11 +166,13 @@ namespace NadekoBot.Classes.Music {
}
//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)
return audioSamples;
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
short buf1 = audioSamples[i + 1];
@@ -263,35 +192,43 @@ namespace NadekoBot.Classes.Music {
return array;
}
- public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal) {
+ public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal)
+ {
if (string.IsNullOrWhiteSpace(query))
throw new ArgumentNullException(nameof(query));
- if (musicType != MusicType.Local && IsRadioLink(query)) {
+ if (musicType != MusicType.Local && IsRadioLink(query))
+ {
musicType = MusicType.Radio;
query = await HandleStreamContainers(query) ?? query;
}
- try {
- switch (musicType) {
+ try
+ {
+ switch (musicType)
+ {
case MusicType.Local:
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Uri = "\"" + Path.GetFullPath(query) + "\"",
Title = Path.GetFileNameWithoutExtension(query),
Provider = "Local File",
ProviderType = musicType,
});
case MusicType.Radio:
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Uri = query,
Title = $"{query}",
Provider = "Radio Stream",
ProviderType = musicType,
});
}
- if (SoundCloud.Default.IsSoundCloudLink(query)) {
+ if (SoundCloud.Default.IsSoundCloudLink(query))
+ {
var svideo = await SoundCloud.Default.GetVideoAsync(query);
- return new Song(new SongInfo {
+ return new Song(new SongInfo
+ {
Title = svideo.FullName,
Provider = "SoundCloud",
Uri = svideo.StreamLink,
@@ -310,76 +247,99 @@ namespace NadekoBot.Classes.Music {
if (video == null) // do something with this error
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"
Provider = "YouTube",
Uri = video.Uri,
ProviderType = musicType,
});
- } catch (Exception ex) {
+ }
+ catch (Exception ex)
+ {
Console.WriteLine($"Failed resolving the link.{ex.Message}");
return null;
}
}
- private static async Task HandleStreamContainers(string query) {
+ private static async Task HandleStreamContainers(string query)
+ {
string file = null;
- try {
+ try
+ {
file = await SearchHelper.GetResponseStringAsync(query);
- } catch {
+ }
+ catch
+ {
return query;
}
- if (query.Contains(".pls")) {
+ if (query.Contains(".pls"))
+ {
//File1=http://armitunes.com:8000/
//Regex.Match(query)
- try {
+ try
+ {
var m = Regex.Match(file, "File1=(?.*?)\\n");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .pls:\n{file}");
return null;
}
}
- if (query.Contains(".m3u")) {
+ if (query.Contains(".m3u"))
+ {
/*
# This is a comment
C:\xxx4xx\xxxxxx3x\xx2xxxx\xx.mp3
C:\xxx5xx\x6xxxxxx\x7xxxxx\xx.mp3
*/
- try {
+ try
+ {
var m = Regex.Match(file, "(?^[^#].*)", RegexOptions.Multiline);
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .m3u:\n{file}");
return null;
}
}
- if (query.Contains(".asx")) {
+ if (query.Contains(".asx"))
+ {
//
- try {
+ try
+ {
var m = Regex.Match(file, "[.*?)\"");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .asx:\n{file}");
return null;
}
}
- if (query.Contains(".xspf")) {
+ if (query.Contains(".xspf"))
+ {
/*
*/
- try {
+ try
+ {
var m = Regex.Match(file, "(?.*?)");
var res = m.Groups["url"]?.ToString();
return res?.Trim();
- } catch {
+ }
+ catch
+ {
Console.WriteLine($"Failed reading .xspf:\n{file}");
return null;
}
diff --git a/NadekoBot/Commands/TriviaCommand.cs b/NadekoBot/Commands/TriviaCommand.cs
index c2398e81..865361f5 100644
--- a/NadekoBot/Commands/TriviaCommand.cs
+++ b/NadekoBot/Commands/TriviaCommand.cs
@@ -1,35 +1,41 @@
-using System;
-using System.Threading.Tasks;
-using Discord.Commands;
-using System.Collections.Concurrent;
-using Discord;
+using Discord.Commands;
using NadekoBot.Modules;
+using System;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
using TriviaGame = NadekoBot.Classes.Trivia.TriviaGame;
-namespace NadekoBot.Commands {
- internal class Trivia : DiscordCommand {
+namespace NadekoBot.Commands
+{
+ internal class Trivia : DiscordCommand
+ {
public static ConcurrentDictionary RunningTrivias = new ConcurrentDictionary();
- public Func DoFunc() => async e => {
+ public Func DoFunc() => async e =>
+ {
TriviaGame trivia;
- if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
+ if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia))
+ {
var triviaGame = new TriviaGame(e);
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.**");
else
await triviaGame.StopGame();
- } else
+ }
+ else
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")
.Description("Starts a game of trivia.")
.Do(DoFunc());
cgb.CreateCommand(Module.Prefix + "tl")
.Description("Shows a current trivia leaderboard.")
- .Do(async e=> {
+ .Do(async e =>
+ {
TriviaGame trivia;
if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
await e.Channel.SendMessage(trivia.GetLeaderboard());
@@ -39,15 +45,18 @@ namespace NadekoBot.Commands {
cgb.CreateCommand(Module.Prefix + "tq")
.Description("Quits current trivia after current question.")
- .Do(async e=> {
+ .Do(async e =>
+ {
TriviaGame trivia;
- if (RunningTrivias.TryGetValue(e.Server.Id, out trivia)) {
+ if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
+ {
await trivia.StopGame();
- } else
+ }
+ else
await e.Channel.SendMessage("No trivia is running on this server.");
});
}
- public Trivia(DiscordModule module) : base(module) {}
+ public Trivia(DiscordModule module) : base(module) { }
}
}
diff --git a/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/NadekoBot/Modules/Administration/Commands/LogCommand.cs
index 8febca20..6bea42b7 100644
--- a/NadekoBot/Modules/Administration/Commands/LogCommand.cs
+++ b/NadekoBot/Modules/Administration/Commands/LogCommand.cs
@@ -148,8 +148,6 @@ namespace NadekoBot.Modules.Administration.Commands
str += $"`New name:` **{e.After.Name}**";
else if (e.Before.AvatarUrl != e.After.AvatarUrl)
str += $"`New Avatar:` {e.After.AvatarUrl}";
- else if (e.Before.Status != e.After.Status)
- str += $"Status `{e.Before.Status}` -> `{e.After.Status}`";
else
return;
await ch.SendMessage(str);
diff --git a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs
index c742ead5..a511479b 100644
--- a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs
+++ b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs
@@ -20,6 +20,7 @@ namespace NadekoBot.Modules.Administration.Commands
public ulong RepeatingServerId { get; set; }
public ulong RepeatingChannelId { get; set; }
+ public Message lastMessage { get; set; } = null;
public string RepeatingMessage { get; set; }
public int Interval { get; set; }
@@ -34,7 +35,13 @@ namespace NadekoBot.Modules.Administration.Commands
{
try
{
- await ch.SendMessage(msg);
+ if (lastMessage != null)
+ await lastMessage.Delete();
+ }
+ catch { }
+ try
+ {
+ lastMessage = await ch.SendMessage(msg);
}
catch { }
}
diff --git a/NadekoBot/Modules/Administration/Commands/Remind.cs b/NadekoBot/Modules/Administration/Commands/Remind.cs
index be16f374..1c38edcd 100644
--- a/NadekoBot/Modules/Administration/Commands/Remind.cs
+++ b/NadekoBot/Modules/Administration/Commands/Remind.cs
@@ -74,6 +74,10 @@ namespace NadekoBot.Modules.Administration.Commands
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "remind")
+ .Description("Sends a message to you or a channel after certain amount of time. " +
+ "First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. " +
+ "Third argument is a (multiword)message. " +
+ "\n**Usage**: `.remind me 1d5h Do something` or `.remind #general Start now!`")
.Parameter("meorchannel", ParameterType.Required)
.Parameter("time", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
diff --git a/NadekoBot/Modules/Games/Commands/Bomberman.cs b/NadekoBot/Modules/Games/Commands/Bomberman.cs
new file mode 100644
index 00000000..fe6c882d
--- /dev/null
+++ b/NadekoBot/Modules/Games/Commands/Bomberman.cs
@@ -0,0 +1,125 @@
+using Discord;
+using Discord.Commands;
+using NadekoBot.Commands;
+using System.Text;
+using System.Timers;
+using static NadekoBot.Modules.Games.Commands.Bomberman;
+
+namespace NadekoBot.Modules.Games.Commands
+{
+ class Bomberman : DiscordCommand
+ {
+ public Field[,] board = new Field[15, 15];
+
+ public BombermanPlayer[] players = new BombermanPlayer[4];
+
+ public Channel gameChannel = null;
+
+ public Message godMsg = null;
+
+ public int curI = 5;
+ public int curJ = 5;
+
+
+ public Bomberman(DiscordModule module) : base(module)
+ {
+ for (int i = 0; i < 15; i++)
+ {
+ for (int j = 0; j < 15; j++)
+ {
+ board[i, j] = new Field();
+ }
+ }
+ NadekoBot.Client.MessageReceived += (s, e) =>
+ {
+ if (e.Channel != gameChannel ||
+ e.User.Id == NadekoBot.Client.CurrentUser.Id)
+ return;
+
+ if (e.Message.Text == "w")
+ {
+ board[curI - 1, curJ] = board[curI--, curJ];
+ board[curI + 1, curJ].player = null;
+ }
+ else if (e.Message.Text == "s")
+ {
+ board[curI + 1, curJ] = board[curI++, curJ];
+ board[curI - 1, curJ].player = null;
+ }
+ else if (e.Message.Text == "a")
+ {
+ board[curI, curJ - 1] = board[curI, curJ--];
+ board[curI, curJ + 1].player = null;
+ }
+ else if (e.Message.Text == "d")
+ {
+ board[curI, curJ + 1] = board[curI, curJ++];
+ board[curI, curJ - 1].player = null;
+ }
+
+ e.Message.Delete();
+ };
+
+ var t = new Timer();
+ t.Elapsed += async (s, e) =>
+ {
+ if (gameChannel == null)
+ return;
+
+ var boardStr = new StringBuilder();
+
+ for (int i = 0; i < 15; i++)
+ {
+ for (int j = 0; j < 15; j++)
+ {
+ boardStr.Append(board[i, j].ToString());
+ }
+ boardStr.AppendLine();
+ }
+ if (godMsg.Id != 0)
+ await godMsg.Edit(boardStr.ToString());
+
+ };
+ t.Interval = 1000;
+ t.Start();
+
+ }
+
+ internal override void Init(CommandGroupBuilder cgb)
+ {
+ //cgb.CreateCommand(Module.Prefix + "bomb")
+ // .Description("Bomberman start")
+ // .Do(async e =>
+ // {
+ // if (gameChannel != null)
+ // return;
+ // godMsg = await e.Channel.SendMessage("GAME START IN 1 SECOND....");
+ // gameChannel = e.Channel;
+ // players[0] = new BombermanPlayer
+ // {
+ // User = e.User,
+ // };
+
+ // board[5, 5].player = players[0];
+ // });
+ }
+
+ public class BombermanPlayer
+ {
+ public User User = null;
+ public string Icon = "👳";
+
+ internal void MoveLeft()
+ {
+
+ }
+ }
+ }
+
+ internal struct Field
+ {
+ public BombermanPlayer player;
+
+ public override string ToString() => player?.Icon ?? "⬜";
+ }
+}
diff --git a/NadekoBot/Modules/Games.cs b/NadekoBot/Modules/Games/GamesModule.cs
similarity index 96%
rename from NadekoBot/Modules/Games.cs
rename to NadekoBot/Modules/Games/GamesModule.cs
index 6d2e39c7..14c13917 100644
--- a/NadekoBot/Modules/Games.cs
+++ b/NadekoBot/Modules/Games/GamesModule.cs
@@ -2,21 +2,23 @@
using Discord.Modules;
using NadekoBot.Commands;
using NadekoBot.Extensions;
+using NadekoBot.Modules.Games.Commands;
using System;
using System.Linq;
-namespace NadekoBot.Modules
+namespace NadekoBot.Modules.Games
{
- internal class Games : DiscordModule
+ internal class GamesModule : DiscordModule
{
private readonly Random rng = new Random();
- public Games()
+ public GamesModule()
{
commands.Add(new Trivia(this));
commands.Add(new SpeedTyping(this));
commands.Add(new PollCommand(this));
commands.Add(new PlantPick(this));
+ commands.Add(new Bomberman(this));
//commands.Add(new BetrayGame(this));
}
diff --git a/NadekoBot/Modules/Music.cs b/NadekoBot/Modules/Music.cs
index 7b50a3a2..4a974c42 100644
--- a/NadekoBot/Modules/Music.cs
+++ b/NadekoBot/Modules/Music.cs
@@ -71,7 +71,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("n")
.Alias("next")
.Alias("skip")
- .Description("Goes to the next song in the queue.")
+ .Description("Goes to the next song in the queue.**Usage**: `!m n`")
.Do(e =>
{
MusicPlayer musicPlayer;
@@ -81,7 +81,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("s")
.Alias("stop")
- .Description("Stops the music and clears the playlist. Stays in the channel.")
+ .Description("Stops the music and clears the playlist. Stays in the channel.\n**Usage**: `!m s`")
.Do(async e =>
{
await Task.Run(() =>
@@ -94,7 +94,8 @@ namespace NadekoBot.Modules
cgb.CreateCommand("d")
.Alias("destroy")
- .Description("Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour)")
+ .Description("Completely stops the music and unbinds the bot from the channel. " +
+ "(may cause weird behaviour)\n**Usage**: `!m d`")
.Do(async e =>
{
await Task.Run(() =>
@@ -107,7 +108,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("p")
.Alias("pause")
- .Description("Pauses or Unpauses the song.")
+ .Description("Pauses or Unpauses the song.\n**Usage**: `!m p`")
.Do(async e =>
{
MusicPlayer musicPlayer;
@@ -121,7 +122,8 @@ namespace NadekoBot.Modules
cgb.CreateCommand("q")
.Alias("yq")
- .Description("Queue a song using keywords or a link. Bot will join your voice channel. **You must be in a voice channel**.\n**Usage**: `!m q Dream Of Venice`")
+ .Description("Queue a song using keywords or a link. Bot will join your voice channel." +
+ "**You must be in a voice channel**.\n**Usage**: `!m q Dream Of Venice`")
.Parameter("query", ParameterType.Unparsed)
.Do(async e =>
{
@@ -130,7 +132,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("lq")
.Alias("ls").Alias("lp")
- .Description("Lists up to 15 currently queued songs.")
+ .Description("Lists up to 15 currently queued songs.\n**Usage**: `!m lq`")
.Do(async e =>
{
MusicPlayer musicPlayer;
@@ -158,7 +160,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("np")
.Alias("playing")
- .Description("Shows the song currently playing.")
+ .Description("Shows the song currently playing.\n**Usage**: `!m np`")
.Do(async e =>
{
MusicPlayer musicPlayer;
@@ -172,7 +174,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("vol")
- .Description("Sets the music volume 0-150%")
+ .Description("Sets the music volume 0-150%\n**Usage**: `!m vol 50`")
.Parameter("val", ParameterType.Required)
.Do(async e =>
{
@@ -192,7 +194,8 @@ namespace NadekoBot.Modules
cgb.CreateCommand("dv")
.Alias("defvol")
- .Description("Sets the default music volume when music playback is started (0-100). Does not persist through restarts.\n**Usage**: !m dv 80")
+ .Description("Sets the default music volume when music playback is started (0-100)." +
+ " Does not persist through restarts.\n**Usage**: `!m dv 80`")
.Parameter("val", ParameterType.Required)
.Do(async e =>
{
@@ -208,7 +211,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("min").Alias("mute")
- .Description("Sets the music volume to 0%")
+ .Description("Sets the music volume to 0%\n**Usage**: `!m min`")
.Do(e =>
{
MusicPlayer musicPlayer;
@@ -218,7 +221,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("max")
- .Description("Sets the music volume to 100% (real max is actually 150%).")
+ .Description("Sets the music volume to 100% (real max is actually 150%).\n**Usage**: `!m max`")
.Do(e =>
{
MusicPlayer musicPlayer;
@@ -228,7 +231,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("half")
- .Description("Sets the music volume to 50%.")
+ .Description("Sets the music volume to 50%.\n**Usage**: `!m half`")
.Do(e =>
{
MusicPlayer musicPlayer;
@@ -238,7 +241,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("sh")
- .Description("Shuffles the current playlist.")
+ .Description("Shuffles the current playlist.\n**Usage**: `!m sh`")
.Do(async e =>
{
MusicPlayer musicPlayer;
@@ -254,18 +257,8 @@ namespace NadekoBot.Modules
await e.Channel.SendMessage("🎵 `Songs shuffled.`");
});
- cgb.CreateCommand("setgame")
- .Description("Sets the game of the bot to the number of songs playing. **Owner only**")
- .AddCheck(SimpleCheckers.OwnerOnly())
- .Do(async e =>
- {
- await e.Channel.SendMessage("❗This command is deprecated. " +
- "Use:\n `.ropl`\n `.adpl %playing% songs, %queued% queued.` instead.\n " +
- "It even persists through restarts.");
- });
-
cgb.CreateCommand("pl")
- .Description("Queues up to 25 songs from a youtube playlist specified by a link, or keywords.")
+ .Description("Queues up to 25 songs from a youtube playlist specified by a link, or keywords.\n**Usage**: `!m pl playlist link or name`")
.Parameter("playlist", ParameterType.Unparsed)
.Do(async e =>
{
@@ -292,7 +285,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("lopl")
- .Description("Queues up to 50 songs from a directory. **Owner Only!**")
+ .Description("Queues up to 50 songs from a directory. **Owner Only!**\n**Usage**: `!m lopl C:/music/classical`")
.Parameter("directory", ParameterType.Unparsed)
.AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly())
.Do(async e =>
@@ -313,7 +306,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("radio").Alias("ra")
- .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf")
+ .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf\n**Usage**: `!m ra radio link here`")
.Parameter("radio_link", ParameterType.Required)
.Do(async e =>
{
@@ -326,7 +319,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("lo")
- .Description("Queues a local file by specifying a full path. **Owner Only!**")
+ .Description("Queues a local file by specifying a full path. **Owner Only!**\n**Usage**: `!m ra C:/music/mysong.mp3`")
.Parameter("path", ParameterType.Unparsed)
.AddCheck(Classes.Permissions.SimpleCheckers.OwnerOnly())
.Do(async e =>
@@ -338,7 +331,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("mv")
- .Description("Moves the bot to your voice channel. (works only if music is already playing)")
+ .Description("Moves the bot to your voice channel. (works only if music is already playing)\n**Usage**: `!m mv`")
.Do(e =>
{
MusicPlayer musicPlayer;
@@ -349,7 +342,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("rm")
- .Description("Remove a song by its # in the queue, or 'all' to remove whole queue.")
+ .Description("Remove a song by its # in the queue, or 'all' to remove whole queue.\n**Usage**: `!m rm 5`")
.Parameter("num", ParameterType.Required)
.Do(async e =>
{
@@ -378,7 +371,7 @@ namespace NadekoBot.Modules
});
cgb.CreateCommand("cleanup")
- .Description("Cleans up hanging voice connections. **Owner Only!**")
+ .Description("Cleans up hanging voice connections. **Owner Only!**\n**Usage**: `!m cleanup`")
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(e =>
{
@@ -397,7 +390,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("rcs")
.Alias("repeatcurrentsong")
- .Description("Toggles repeat of current song.")
+ .Description("Toggles repeat of current song.\n**Usage**: `!m rcs`")
.Do(async e =>
{
MusicPlayer musicPlayer;
@@ -414,7 +407,7 @@ namespace NadekoBot.Modules
cgb.CreateCommand("rpl")
.Alias("repeatplaylist")
- .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue).")
+ .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue).\n**Usage**: `!m rpl`")
.Do(async e =>
{
MusicPlayer musicPlayer;
diff --git a/NadekoBot/Modules/Search/Commands/ConverterCommand.cs b/NadekoBot/Modules/Search/Commands/ConverterCommand.cs
new file mode 100644
index 00000000..bb08c5af
--- /dev/null
+++ b/NadekoBot/Modules/Search/Commands/ConverterCommand.cs
@@ -0,0 +1,162 @@
+using Discord.Commands;
+using NadekoBot.Commands;
+using ScaredFingers.UnitsConversion;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace NadekoBot.Modules.Search.Commands
+{
+ class ConverterCommand : DiscordCommand
+ {
+
+ public ConverterCommand(DiscordModule module) : base(module)
+ {
+ if (unitTables == null)
+ {
+ CultureInfo ci = new CultureInfo("en-US");
+ Thread.CurrentThread.CurrentCulture = ci;
+ unitTables = new List();
+ unitTables.Add(UnitTable.LengthTable);
+ unitTables.Add(UnitTable.TemperatureTable);
+ unitTables.Add(UnitTable.VolumeTable);
+ unitTables.Add(UnitTable.WeightTable);
+ reInitCurrencyConverterTable();
+ }
+
+ }
+
+
+ internal override void Init(CommandGroupBuilder cgb)
+ {
+ cgb.CreateCommand(Module.Prefix + "convert")
+ .Description("Convert quantities from>to. Like `~convert m>km 1000`")
+ .Parameter("from-to", ParameterType.Required)
+ .Parameter("quantity", ParameterType.Optional)
+ .Do(ConvertFunc());
+ cgb.CreateCommand(Module.Prefix + "convertlist")
+ .Description("List of the convertable dimensions and currencies.")
+ .Do(ConvertListFunc());
+ }
+
+ private Func ConvertListFunc() =>
+ async e =>
+ {
+ reInitCurrencyConverterTable();
+ string msg = "";
+ foreach (var tmpTable in unitTables)
+ {
+ int i = 1;
+ while (tmpTable.IsKnownUnit(i))
+ {
+ msg += tmpTable.GetUnitName(i) + " (" + tmpTable.GetUnitSymbol(i) + "); ";
+ i++;
+ }
+ msg += "\n";
+ }
+ foreach (var curr in exchangeRateProvider.Currencies)
+ {
+ msg += curr + "; ";
+ }
+
+ await e.Channel.SendMessage(msg);
+ };
+
+ private Func ConvertFunc() =>
+ async e =>
+ {
+ try
+ {
+ await e.Channel.SendIsTyping();
+
+ string from = e.GetArg("from-to").ToLowerInvariant().Split('>')[0];
+ string to = e.GetArg("from-to").ToLowerInvariant().Split('>')[1];
+
+ float quantity = 1.0f;
+ if (!float.TryParse(e.GetArg("quantity"), out quantity))
+ {
+ quantity = 1.0f;
+ }
+
+ int fromCode, toCode = 0;
+ UnitTable table = null;
+ ResolveUnitCodes(from, to, out table, out fromCode, out toCode);
+
+ if (table != null)
+ {
+ Unit inUnit = new Unit(fromCode, quantity, table);
+ Unit outUnit = inUnit.Convert(toCode);
+ await e.Channel.SendMessage(inUnit.ToString() + " = " + outUnit.ToString());
+ }
+ else
+ {
+ reInitCurrencyConverterTable();
+ Unit inUnit = currTable.CreateUnit(quantity, from.ToUpperInvariant());
+ Unit outUnit = inUnit.Convert(currTable.CurrencyCode(to.ToUpperInvariant()));
+ await e.Channel.SendMessage(inUnit.ToString() + " = " + outUnit.ToString());
+ }
+ }
+ catch //(Exception ex)
+ {
+ //Console.WriteLine(ex.ToString());
+ await e.Channel.SendMessage("Bad input format, or sth went wrong... Try to list them with `" + Module.Prefix + "`convertlist");
+ }
+ };
+
+ private void reInitCurrencyConverterTable()
+ {
+ if (lastChanged == null || lastChanged.DayOfYear != DateTime.Now.DayOfYear)
+ {
+ exchangeRateProvider = new WebExchangeRatesProvider();
+ currTable = new CurrencyExchangeTable(exchangeRateProvider);
+ lastChanged = DateTime.Now;
+ }
+ }
+
+ private void ResolveUnitCodes(string from, string to, out UnitTable table, out int fromCode, out int toCode)
+ {
+ foreach (var tmpTable in unitTables)
+ {
+ int f = LookupUnit(tmpTable, from);
+ int t = LookupUnit(tmpTable, to);
+ if (f > 0 && t > 0)
+ {
+ table = tmpTable;
+ fromCode = f;
+ toCode = t;
+ return;
+ }
+ }
+ table = null;
+ fromCode = 0;
+ toCode = 0;
+ }
+
+ private int LookupUnit(UnitTable table, string lookup)
+ {
+ string wellformedLookup = lookup.ToLowerInvariant().Replace("°", "");
+ int i = 1;
+ while (table.IsKnownUnit(i))
+ {
+ if (wellformedLookup == table.GetUnitName(i).ToLowerInvariant().Replace("°", "") ||
+ wellformedLookup == table.GetUnitPlural(i).ToLowerInvariant().Replace("°", "") ||
+ wellformedLookup == table.GetUnitSymbol(i).ToLowerInvariant().Replace("°", ""))
+ {
+ return i;
+ }
+ i++;
+ }
+ return 0;
+ }
+
+ private static List unitTables;
+
+ private static CurrencyExchangeRatesProvider exchangeRateProvider;
+
+ private static CurrencyExchangeTable currTable;
+
+ private static DateTime lastChanged;
+ }
+}
diff --git a/NadekoBot/Modules/Searches.cs b/NadekoBot/Modules/Searches.cs
index 0693141e..8a522423 100644
--- a/NadekoBot/Modules/Searches.cs
+++ b/NadekoBot/Modules/Searches.cs
@@ -5,6 +5,7 @@ using NadekoBot.Classes.IMDB;
using NadekoBot.Classes.JSONModels;
using NadekoBot.Commands;
using NadekoBot.Extensions;
+using NadekoBot.Modules.Search.Commands;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@@ -23,6 +24,7 @@ namespace NadekoBot.Modules
{
commands.Add(new LoLCommands(this));
commands.Add(new StreamNotifications(this));
+ commands.Add(new ConverterCommand(this));
rng = new Random();
}
diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs
index 2fbb48f4..6442839f 100644
--- a/NadekoBot/NadekoBot.cs
+++ b/NadekoBot/NadekoBot.cs
@@ -7,6 +7,7 @@ using NadekoBot.Commands;
using NadekoBot.Modules;
using NadekoBot.Modules.Administration;
using NadekoBot.Modules.Gambling;
+using NadekoBot.Modules.Games;
using NadekoBot.Modules.Pokemon;
using NadekoBot.Modules.Translator;
using Newtonsoft.Json;
@@ -163,7 +164,7 @@ namespace NadekoBot
modules.Add(new PermissionModule(), "Permissions", ModuleFilter.None);
modules.Add(new Conversations(), "Conversations", ModuleFilter.None);
modules.Add(new GamblingModule(), "Gambling", ModuleFilter.None);
- modules.Add(new Games(), "Games", ModuleFilter.None);
+ modules.Add(new GamesModule(), "Games", ModuleFilter.None);
modules.Add(new Music(), "Music", ModuleFilter.None);
modules.Add(new Searches(), "Searches", ModuleFilter.None);
modules.Add(new NSFW(), "NSFW", ModuleFilter.None);
diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj
index 81c190bb..4d233b8c 100644
--- a/NadekoBot/NadekoBot.csproj
+++ b/NadekoBot/NadekoBot.csproj
@@ -97,6 +97,10 @@
..\packages\RestSharp.105.2.3\lib\net452\RestSharp.dll
True
+
+ False
+ lib\ScaredFingers.UnitsConversion.dll
+
@@ -130,6 +134,7 @@
+
@@ -186,7 +191,8 @@
-
+
+
@@ -217,6 +223,7 @@
+
@@ -478,6 +485,9 @@
Discord.Net
+
+
+
]