Volume, better music performance and other search performance

This commit is contained in:
Master Kwoth 2016-02-04 14:17:15 +01:00
parent a81e924bb5
commit a7fc6e5bda
7 changed files with 255 additions and 228 deletions

View File

@ -29,7 +29,6 @@ namespace NadekoBot.Extensions {
if (letters[i] != ' ') if (letters[i] != ' ')
letters[i] = '_'; letters[i] = '_';
} }
return "`"+string.Join(" ", letters)+"`"; return "`"+string.Join(" ", letters)+"`";
} }
@ -153,7 +152,7 @@ namespace NadekoBot.Extensions {
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="source"></param> /// <param name="source"></param>
/// <param name="action"></param> /// <param name="action"></param>
public static string ShortenUrl(this string str) => Searches.ShortenUrl(str); public static async Task<string> ShortenUrl(this string str) => await Searches.ShortenUrl(str);
/// <summary> /// <summary>
/// Gets the program runtime /// Gets the program runtime

View File

@ -15,6 +15,7 @@ namespace NadekoBot.Classes.Music {
public bool Pause = false; public bool Pause = false;
public List<StreamRequest> SongQueue = new List<StreamRequest>(); public List<StreamRequest> SongQueue = new List<StreamRequest>();
public StreamRequest CurrentSong = null; public StreamRequest CurrentSong = null;
public float Volume { get; set; } = 1.0f;
public bool IsPaused { get; internal set; } = false; public bool IsPaused { get; internal set; } = false;
public bool Stopped { get; private set; } public bool Stopped { get; private set; }
@ -67,7 +68,7 @@ namespace NadekoBot.Classes.Music {
VoiceClient = await NadekoBot.client.Audio().Join(VoiceChannel); VoiceClient = await NadekoBot.client.Audio().Join(VoiceChannel);
Console.WriteLine($"Joined voicechannel [{DateTime.Now.Second}]"); Console.WriteLine($"Joined voicechannel [{DateTime.Now.Second}]");
} }
await CurrentSong.Start(); await Task.Factory.StartNew(async () => await CurrentSong.Start(), TaskCreationOptions.LongRunning).Unwrap();
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine($"Starting failed: {ex}"); Console.WriteLine($"Starting failed: {ex}");
CurrentSong?.Stop(); CurrentSong?.Stop();
@ -77,10 +78,6 @@ namespace NadekoBot.Classes.Music {
internal void Stop() { internal void Stop() {
Stopped = true; Stopped = true;
foreach (var kvp in SongQueue) {
if (kvp != null)
kvp.Stop();
}
SongQueue.Clear(); SongQueue.Clear();
CurrentSong?.Stop(); CurrentSong?.Stop();
CurrentSong = null; CurrentSong = null;
@ -91,6 +88,14 @@ namespace NadekoBot.Classes.Music {
MusicModule.musicPlayers.TryRemove(_e.Server, out throwAwayValue); MusicModule.musicPlayers.TryRemove(_e.Server, out throwAwayValue);
} }
public void SetVolume(int value) {
if (value < 0)
value = 0;
if (value > 150)
value = 150;
this.Volume = value/100f;
}
internal bool TogglePause() => IsPaused = !IsPaused; internal bool TogglePause() => IsPaused = !IsPaused;
} }
} }

View File

@ -36,6 +36,8 @@ namespace NadekoBot.Classes.Music {
public bool IsPaused => MusicControls.IsPaused; public bool IsPaused => MusicControls.IsPaused;
public float Volume => MusicControls?.Volume ?? 1.0f;
public MusicControls MusicControls; public MusicControls MusicControls;
public StreamRequest(CommandEventArgs e, string query, MusicControls mc) { public StreamRequest(CommandEventArgs e, string query, MusicControls mc) {
@ -50,17 +52,17 @@ namespace NadekoBot.Classes.Music {
mc.SongQueue.Add(this); mc.SongQueue.Add(this);
} }
private void ResolveStreamLink() { private async void ResolveStreamLink() {
Video video = null; Video video = null;
try { try {
if (OnResolving != null) if (OnResolving != null)
OnResolving(); OnResolving();
Console.WriteLine("Resolving video link"); var links = await Searches.FindYoutubeUrlByKeywords(Query);
var videos = await YouTube.Default.GetAllVideosAsync(links);
video = YouTube.Default.GetAllVideos(Searches.FindYoutubeUrlByKeywords(Query)) video = videos
.Where(v => v.AdaptiveKind == AdaptiveKind.Audio) .Where(v => v.AdaptiveKind == AdaptiveKind.Audio)
.OrderByDescending(v => v.AudioBitrate) .OrderByDescending(v => v.AudioBitrate)
.FirstOrDefault(); .FirstOrDefault();
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.");
@ -77,7 +79,6 @@ namespace NadekoBot.Classes.Music {
musicStreamer = new MusicStreamer(this, video.Uri); musicStreamer = new MusicStreamer(this, video.Uri);
if (OnQueued != null) if (OnQueued != null)
OnQueued(); OnQueued();
return;
} }
internal string PrintStats() => musicStreamer?.Stats(); internal string PrintStats() => musicStreamer?.Stats();
@ -98,14 +99,11 @@ namespace NadekoBot.Classes.Music {
} }
internal async Task Start() { internal async Task Start() {
Console.WriteLine("Start called.");
int attemptsLeft = 4; int attemptsLeft = 4;
//wait for up to 4 seconds to resolve a link //wait for up to 4 seconds to resolve a link
try { try {
while (State == StreamState.Resolving) { while (State == StreamState.Resolving) {
await Task.Delay(1000); await Task.Delay(1000);
Console.WriteLine("Resolving...");
if (--attemptsLeft == 0) { if (--attemptsLeft == 0) {
throw new TimeoutException("Resolving timed out."); throw new TimeoutException("Resolving timed out.");
} }
@ -137,7 +135,6 @@ namespace NadekoBot.Classes.Music {
this.parent = parent; this.parent = parent;
this.buffer = new DualStream(); this.buffer = new DualStream();
this.Url = directUrl; this.Url = directUrl;
Console.WriteLine("Created new streamer");
State = StreamState.Queued; State = StreamState.Queued;
} }
@ -149,7 +146,6 @@ namespace NadekoBot.Classes.Music {
"--------------------------------\n"; "--------------------------------\n";
private async Task BufferSong() { private async Task BufferSong() {
Console.WriteLine("Buffering...");
//start feeding the buffer //start feeding the buffer
var p = Process.Start(new ProcessStartInfo { var p = Process.Start(new ProcessStartInfo {
FileName = "ffmpeg", FileName = "ffmpeg",
@ -162,11 +158,11 @@ namespace NadekoBot.Classes.Music {
}); });
int attempt = 0; int attempt = 0;
while (true) { while (true) {
int magickBuffer = 1;
//wait for the read pos to catch up with write pos //wait for the read pos to catch up with write pos
while (buffer.writePos - buffer.readPos > 5.MB() && State != StreamState.Completed) { while (buffer.writePos - buffer.readPos > 1.MB() && State != StreamState.Completed) {
prebufferingComplete = true; prebufferingComplete = true;
await Task.Delay(200); await Task.Delay(150);
} }
if (State == StreamState.Completed) { if (State == StreamState.Completed) {
@ -178,10 +174,8 @@ namespace NadekoBot.Classes.Music {
return; return;
} }
if (buffer.readPos > 5.MiB()) { // if buffer is over 5 MiB, create new one if (buffer.readPos > 1.MiB() && buffer.writePos > 1.MiB()) { // if buffer is over 5 MiB, create new one
Console.WriteLine("Buffer over 5 megs, clearing."); var skip = 1.MB(); //remove only 5 MB, just in case
var skip = 5.MB(); //remove only 5 MB, just in case
var newBuffer = new DualStream(); var newBuffer = new DualStream();
lock (_bufferLock) { lock (_bufferLock) {
@ -225,7 +219,10 @@ namespace NadekoBot.Classes.Music {
State = StreamState.Playing; State = StreamState.Playing;
if (parent.OnBuffering != null) if (parent.OnBuffering != null)
parent.OnBuffering(); parent.OnBuffering();
BufferSong();
Task.Factory.StartNew(async () => {
await BufferSong();
}, TaskCreationOptions.LongRunning).ConfigureAwait(false);
// prebuffering wait stuff start // prebuffering wait stuff start
int bufferAttempts = 0; int bufferAttempts = 0;
@ -253,6 +250,8 @@ namespace NadekoBot.Classes.Music {
int attempt = 0; int attempt = 0;
while (!IsCanceled) { while (!IsCanceled) {
int readCount = 0; int readCount = 0;
//adjust volume
lock (_bufferLock) { lock (_bufferLock) {
readCount = buffer.Read(voiceBuffer, 0, voiceBuffer.Length); readCount = buffer.Read(voiceBuffer, 0, voiceBuffer.Length);
} }
@ -272,7 +271,7 @@ namespace NadekoBot.Classes.Music {
Console.WriteLine("Canceled"); Console.WriteLine("Canceled");
break; break;
} }
voiceBuffer = adjustVolume(voiceBuffer, parent.Volume);
parent.MusicControls.VoiceClient.Send(voiceBuffer, 0, voiceBuffer.Length); parent.MusicControls.VoiceClient.Send(voiceBuffer, 0, voiceBuffer.Length);
while (IsPaused) { while (IsPaused) {
@ -288,11 +287,36 @@ namespace NadekoBot.Classes.Music {
} }
internal void Stop() { internal void Stop() {
Console.WriteLine($"Stopping playback [{DateTime.Now.Second}]"); if (State == StreamState.Completed) return;
if (State != StreamState.Completed) { var oldState = State;
State = StreamState.Completed; State = StreamState.Completed;
parent.OnCompleted(); if (oldState == StreamState.Playing)
if (parent.OnCompleted != null)
parent.OnCompleted();
}
//stackoverflow ftw
private byte[] adjustVolume(byte[] audioSamples, float volume) {
if (volume == 1.0f)
return audioSamples;
byte[] array = new byte[audioSamples.Length];
for (int i = 0; i < array.Length; i += 2) {
// convert byte pair to int
short buf1 = audioSamples[i + 1];
short buf2 = audioSamples[i];
buf1 = (short)((buf1 & 0xff) << 8);
buf2 = (short)(buf2 & 0xff);
short res = (short)(buf1 | buf2);
res = (short)(res * volume);
// convert back
array[i] = (byte)res;
array[i + 1] = (byte)(res >> 8);
} }
return array;
} }
} }

View File

@ -266,44 +266,6 @@ namespace NadekoBot.Modules {
await e.Send("Invalid code."); await e.Send("Invalid code.");
} }
}); });
cgb.CreateCommand("save")
.Description("Saves something for the owner in a file.")
.Parameter("all", ParameterType.Unparsed)
.Do(async e => {
if (e.User.Id == NadekoBot.OwnerID) {
string m = "";
try {
FileStream f = File.OpenWrite("saves.txt");
m = e.Args[0];
byte[] b = Encoding.ASCII.GetBytes(m + "\n");
f.Seek(f.Length, SeekOrigin.Begin);
f.Write(b, 0, b.Length);
f.Close();
} catch (Exception) {
await e.Send("Error saving. Sorry :(");
}
if (m.Length > 0)
await e.Send("I saved this for you: " + Environment.NewLine + "```" + m + "```");
else
await e.Send("No point in saving empty message...");
} else await e.Send("Not for you, only my Master <3");
});
cgb.CreateCommand("ls")
.Description("Shows all saved items.")
.Do(async e => {
FileStream f = File.OpenRead("saves.txt");
if (f.Length == 0) {
await e.Send("Saves are empty.");
return;
}
byte[] b = new byte[f.Length / sizeof(byte)];
f.Read(b, 0, b.Length);
f.Close();
string str = Encoding.ASCII.GetString(b);
await e.User.Send("```" + (str.Length < 1950 ? str : str.Substring(0, 1950)) + "```");
});
cgb.CreateCommand("slm") cgb.CreateCommand("slm")
.Description("Shows the message where you were last mentioned in this channel (checks last 10k messages)") .Description("Shows the message where you were last mentioned in this channel (checks last 10k messages)")
.Do(async e => { .Do(async e => {
@ -331,12 +293,6 @@ namespace NadekoBot.Modules {
else else
await e.Send("I can't find a message mentioning you."); await e.Send("I can't find a message mentioning you.");
}); });
cgb.CreateCommand("cs")
.Description("Deletes all saves")
.Do(async e => {
File.Delete("saves.txt");
await e.Send("Cleared all saves.");
});
cgb.CreateCommand("bb") cgb.CreateCommand("bb")
.Description("Says bye to someone. **Usage**: @NadekoBot bb @X") .Description("Says bye to someone. **Usage**: @NadekoBot bb @X")
@ -416,8 +372,7 @@ namespace NadekoBot.Modules {
await e.Send("Invalid user specified."); await e.Send("Invalid user specified.");
return; return;
} }
string av = usr.AvatarUrl; await e.Send(await usr.AvatarUrl.ShortenUrl());
await e.Send(Searches.ShortenUrl(av));
}); });
//TODO add eval //TODO add eval

View File

@ -20,10 +20,9 @@ namespace NadekoBot.Modules
commands.ForEach(com => com.Init(cgb)); commands.ForEach(com => com.Init(cgb));
cgb.CreateCommand("$raffle") cgb.CreateCommand("$raffle")
.Description("Mentions a user from the online list from the (optional) role.") .Description("Prints a name and ID of a random user from the online list from the (optional) role.")
.Parameter("role", ParameterType.Optional) .Parameter("role", ParameterType.Optional)
.Do(async e => { .Do(async e => {
if (!e.User.ServerPermissions.MentionEveryone) return;
var arg = string.IsNullOrWhiteSpace(e.GetArg("role")) ? "@everyone" : e.GetArg("role"); var arg = string.IsNullOrWhiteSpace(e.GetArg("role")) ? "@everyone" : e.GetArg("role");
var role = e.Server.FindRoles(arg).FirstOrDefault(); var role = e.Server.FindRoles(arg).FirstOrDefault();
if (role == null) { if (role == null) {

View File

@ -7,6 +7,7 @@ using NadekoBot.Extensions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NadekoBot.Classes.Music; using NadekoBot.Classes.Music;
using Timer = System.Timers.Timer; using Timer = System.Timers.Timer;
using System.Threading.Tasks;
namespace NadekoBot.Modules { namespace NadekoBot.Modules {
class Music : DiscordModule { class Music : DiscordModule {
@ -20,19 +21,6 @@ namespace NadekoBot.Modules {
} }
public Music() : base() { public Music() : base() {
/*Timer cleaner = new Timer();
cleaner.Elapsed += (s, e) => System.Threading.Tasks.Task.Run(() => CleanMusicPlayers());
cleaner.Interval = 10000;
cleaner.Start();
Timer statPrinter = new Timer();
NadekoBot.client.Connected += (s, e) => {
if (statPrinter.Enabled) return;
statPrinter.Elapsed += (se, ev) => { Console.WriteLine($"<<--Music-->> {musicPlayers.Count} songs playing."); musicPlayers.ForEach(kvp => Console.WriteLine(kvp.Value?.CurrentSong?.PrintStats())); Console.WriteLine("<<--Music END-->>"); };
statPrinter.Interval = 5000;
statPrinter.Start();
};
*/
} }
public override void Install(ModuleManager manager) { public override void Install(ModuleManager manager) {
@ -45,9 +33,9 @@ namespace NadekoBot.Modules {
cgb.CreateCommand("n") cgb.CreateCommand("n")
.Alias("next") .Alias("next")
.Description("Goes to the next song in the queue.") .Description("Goes to the next song in the queue.")
.Do(e => { .Do(async e => {
if (musicPlayers.ContainsKey(e.Server) == false) return; if (musicPlayers.ContainsKey(e.Server) == false) return;
musicPlayers[e.Server].LoadNextSong(); await musicPlayers[e.Server].LoadNextSong();
}); });
cgb.CreateCommand("s") cgb.CreateCommand("s")
@ -73,67 +61,7 @@ namespace NadekoBot.Modules {
.Alias("yq") .Alias("yq")
.Description("Queue a song using keywords or link. **You must be in a voice channel**.\n**Usage**: `!m q Dream Of Venice`") .Description("Queue a song using keywords or link. **You must be in a voice channel**.\n**Usage**: `!m q Dream Of Venice`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => { .Do(async e => await QueueSong(e,e.GetArg("query")));
if (e.User.VoiceChannel?.Server != e.Server) {
await e.Send(":anger: You need to be in the voice channel on this server.");
return;
}
if (musicPlayers.ContainsKey(e.Server) == false)
if (!musicPlayers.TryAdd(e.Server, new MusicControls(e.User.VoiceChannel, e))) {
await e.Send("Failed to create a music player for this server");
return;
}
if (e.GetArg("query") == null || e.GetArg("query").Length < 4)
return;
var player = musicPlayers[e.Server];
if (player.SongQueue.Count > 25) {
await e.Send("Music player supports up to 25 songs atm. Contant the owner if you think this is not enough :warning:");
}
try {
Message qmsg = await e.Channel.SendMessage(":musical_note: **Searching...**");
var sr = new StreamRequest(e, e.GetArg("query"), player);
if (sr == null)
throw new NullReferenceException("StreamRequest is null.");
Message msg = null;
sr.OnResolving += async () => {
await qmsg.Edit($":musical_note: **Resolving**... \"{e.GetArg("query")}\"");
};
sr.OnResolvingFailed += async (err) => {
await qmsg.Edit($":anger: :musical_note: **Resolving failed** for `{e.GetArg("query")}`");
};
sr.OnQueued += async () => {
await qmsg.Edit($":musical_note:**Queued** {sr.Title.TrimTo(55)}");
};
sr.OnCompleted += async () => {
MusicControls mc;
if (musicPlayers.TryGetValue(e.Server, out mc)) {
if (mc.SongQueue.Count == 0)
mc.Stop();
}
await e.Send($":musical_note:**Finished playing** {sr.Title.TrimTo(55)}");
};
sr.OnStarted += async () => {
if (msg == null)
await e.Send($":musical_note:**Playing ** {sr.Title.TrimTo(55)}");
else
await msg.Edit($":musical_note:**Playing ** {sr.Title.TrimTo(55)}");
qmsg?.Delete();
};
sr.OnBuffering += async () => {
msg = await e.Send($":musical_note:**Buffering...** {sr.Title.TrimTo(55)}");
};
} catch (Exception ex) {
Console.WriteLine();
await e.Send($":anger: {ex.Message}");
return;
}
});
cgb.CreateCommand("lq") cgb.CreateCommand("lq")
.Alias("ls").Alias("lp") .Alias("ls").Alias("lp")
@ -141,8 +69,10 @@ namespace NadekoBot.Modules {
.Do(async e => { .Do(async e => {
if (musicPlayers.ContainsKey(e.Server) == false) await e.Send(":musical_note: No active music player."); if (musicPlayers.ContainsKey(e.Server) == false) await e.Send(":musical_note: No active music player.");
var player = musicPlayers[e.Server]; var player = musicPlayers[e.Server];
string toSend = ":musical_note: " + player.SongQueue.Count + " videos currently queued. ";
await e.Send(":musical_note: " + player.SongQueue.Count + " videos currently queued."); if (player.SongQueue.Count >= 25)
toSend += "**Song queue is full!**\n";
await e.Send(toSend);
int number = 1; int number = 1;
await e.Send(string.Join("\n", player.SongQueue.Select(v => $"**#{number++}** {v.Title.TrimTo(60)}").Take(10))); await e.Send(string.Join("\n", player.SongQueue.Select(v => $"**#{number++}** {v.Title.TrimTo(60)}").Take(10)));
}); });
@ -156,6 +86,45 @@ namespace NadekoBot.Modules {
await e.Send($"Now Playing **{player.CurrentSong.Title}**"); await e.Send($"Now Playing **{player.CurrentSong.Title}**");
}); });
cgb.CreateCommand("vol")
.Description("Sets the music volume 0-150%")
.Parameter("val", ParameterType.Required)
.Do(async e => {
if (musicPlayers.ContainsKey(e.Server) == false) return;
var player = musicPlayers[e.Server];
var arg = e.GetArg("val");
int volume;
if (!int.TryParse(arg, out volume)) {
await e.Send("Volume number invalid.");
return;
}
player.SetVolume(volume);
});
cgb.CreateCommand("min").Alias("mute")
.Description("Sets the music volume to 0%")
.Do(e => {
if (musicPlayers.ContainsKey(e.Server) == false) return;
var player = musicPlayers[e.Server];
player.SetVolume(0);
});
cgb.CreateCommand("max")
.Description("Sets the music volume to 100% (real max is actually 150%).")
.Do(e => {
if (musicPlayers.ContainsKey(e.Server) == false) return;
var player = musicPlayers[e.Server];
player.SetVolume(100);
});
cgb.CreateCommand("half")
.Description("Sets the music volume to 50%.")
.Do(e => {
if (musicPlayers.ContainsKey(e.Server) == false) return;
var player = musicPlayers[e.Server];
player.SetVolume(50);
});
cgb.CreateCommand("sh") cgb.CreateCommand("sh")
.Description("Shuffles the current playlist.") .Description("Shuffles the current playlist.")
.Do(async e => { .Do(async e => {
@ -191,6 +160,19 @@ namespace NadekoBot.Modules {
await e.Send("Music status " + (setgameEnabled ? "enabled" : "disabled")); await e.Send("Music status " + (setgameEnabled ? "enabled" : "disabled"));
}); });
cgb.CreateCommand("pl")
.Description("Queues up to 25 songs from a youtube playlist")
.Parameter("playlist", ParameterType.Unparsed)
.Do(async e => {
var ids = await Searches.GetVideoIDs(await Searches.GetPlaylistIdByKeyword(e.GetArg("playlist")));
//todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE
await e.Send($"Attempting to queue {ids.Count} songs".SnPl(ids.Count));
foreach (var id in ids) {
Task.Run(async () => await QueueSong(e, id, true)).ConfigureAwait(false);
await Task.Delay(150);
}
});
cgb.CreateCommand("debug") cgb.CreateCommand("debug")
.Description("Writes some music data to console. **BOT OWNER ONLY**") .Description("Writes some music data to console. **BOT OWNER ONLY**")
.Do(e => { .Do(e => {
@ -200,5 +182,67 @@ namespace NadekoBot.Modules {
}); });
}); });
} }
private async Task QueueSong(CommandEventArgs e, string query, bool silent = false) {
if (e.User.VoiceChannel?.Server != e.Server) {
await e.Send(":anger: You need to be in the voice channel on this server.");
return;
}
if (musicPlayers.ContainsKey(e.Server) == false)
if (!musicPlayers.TryAdd(e.Server, new MusicControls(e.User.VoiceChannel, e))) {
await e.Send("Failed to create a music player for this server");
return;
}
if (query == null || query.Length < 4)
return;
var player = musicPlayers[e.Server];
if (player.SongQueue.Count >= 25) return;
try {
var sr = await Task.Run(() => new StreamRequest(e, query, player));
if (sr == null)
throw new NullReferenceException("StreamRequest is null.");
Message qmsg = null;
Message msg = null;
if (!silent) {
qmsg = await e.Channel.SendMessage(":musical_note: **Searching...**");
sr.OnResolving += async () => {
await qmsg.Edit($":musical_note: **Resolving**... \"{query}\"");
};
sr.OnResolvingFailed += async (err) => {
await qmsg.Edit($":anger: :musical_note: **Resolving failed** for `{query}`");
};
sr.OnQueued += async () => {
await qmsg.Edit($":musical_note:**Queued** {sr.Title.TrimTo(55)}");
};
}
sr.OnCompleted += async () => {
MusicControls mc;
if (musicPlayers.TryGetValue(e.Server, out mc)) {
if (mc.SongQueue.Count == 0)
mc.Stop();
}
await e.Send($":musical_note:**Finished playing** {sr.Title.TrimTo(55)}");
};
sr.OnStarted += async () => {
if (msg == null)
await e.Send($":musical_note:**Playing ** {sr.Title.TrimTo(55)} **Volume:** {(int)(player.Volume * 100)}%");
else
await msg.Edit($":musical_note:**Playing ** {sr.Title.TrimTo(55)} **Volume:** {(int)(player.Volume * 100)}%");
qmsg?.Delete();
};
sr.OnBuffering += async () => {
msg = await e.Send($":musical_note:**Buffering...** {sr.Title.TrimTo(55)}");
};
} catch (Exception ex) {
Console.WriteLine();
await e.Send($":anger: {ex.Message}");
return;
}
}
} }
} }

View File

@ -24,12 +24,12 @@ namespace NadekoBot.Modules {
commands.ForEach(cmd => cmd.Init(cgb)); commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand("~yt") cgb.CreateCommand("~yt")
.Parameter("query", Discord.Commands.ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Queries youtubes and embeds the first result") .Description("Searches youtubes and shows the first result")
.Do(async e => { .Do(async e => {
if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return;
var str = ShortenUrl(FindYoutubeUrlByKeywords(e.GetArg("query"))); var str = await ShortenUrl(await FindYoutubeUrlByKeywords(e.GetArg("query")));
if (string.IsNullOrEmpty(str.Trim())) { if (string.IsNullOrEmpty(str.Trim())) {
await e.Send("Query failed"); await e.Send("Query failed");
return; return;
@ -39,12 +39,12 @@ namespace NadekoBot.Modules {
cgb.CreateCommand("~ani") cgb.CreateCommand("~ani")
.Alias("~anime").Alias("~aq") .Alias("~anime").Alias("~aq")
.Parameter("query", Discord.Commands.ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Queries anilist for an anime and shows the first result.") .Description("Queries anilist for an anime and shows the first result.")
.Do(async e => { .Do(async e => {
if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return;
var result = GetAnimeQueryResultLink(e.GetArg("query")); var result = await GetAnimeQueryResultLink(e.GetArg("query"));
if (result == null) { if (result == null) {
await e.Send("Failed to find that anime."); await e.Send("Failed to find that anime.");
return; return;
@ -55,12 +55,12 @@ namespace NadekoBot.Modules {
cgb.CreateCommand("~mang") cgb.CreateCommand("~mang")
.Alias("~manga").Alias("~mq") .Alias("~manga").Alias("~mq")
.Parameter("query", Discord.Commands.ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Queries anilist for a manga and shows the first result.") .Description("Queries anilist for a manga and shows the first result.")
.Do(async e => { .Do(async e => {
if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return; if (!(await ValidateQuery(e.Channel, e.GetArg("query")))) return;
var result = GetMangaQueryResultLink(e.GetArg("query")); var result = await GetMangaQueryResultLink(e.GetArg("query"));
if (result == null) { if (result == null) {
await e.Send("Failed to find that anime."); await e.Send("Failed to find that anime.");
return; return;
@ -105,8 +105,8 @@ namespace NadekoBot.Modules {
string tag = e.GetArg("tag"); string tag = e.GetArg("tag");
if (tag == null) if (tag == null)
tag = ""; tag = "";
await e.Send(":heart: Gelbooru: " + GetGelbooruImageLink(tag)); await e.Send(":heart: Gelbooru: " + await GetGelbooruImageLink(tag));
await e.Send(":heart: Danbooru: " + GetDanbooruImageLink(tag)); await e.Send(":heart: Danbooru: " + await GetDanbooruImageLink(tag));
}); });
cgb.CreateCommand("~danbooru") cgb.CreateCommand("~danbooru")
.Description("Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered.\n**Usage**: ~hentai yuri") .Description("Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered.\n**Usage**: ~hentai yuri")
@ -115,7 +115,7 @@ namespace NadekoBot.Modules {
string tag = e.GetArg("tag"); string tag = e.GetArg("tag");
if (tag == null) if (tag == null)
tag = ""; tag = "";
await e.Send(GetDanbooruImageLink(tag)); await e.Send(await GetDanbooruImageLink(tag));
}); });
cgb.CreateCommand("~gelbooru") cgb.CreateCommand("~gelbooru")
.Description("Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered.\n**Usage**: ~hentai yuri") .Description("Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered.\n**Usage**: ~hentai yuri")
@ -124,7 +124,7 @@ namespace NadekoBot.Modules {
string tag = e.GetArg("tag"); string tag = e.GetArg("tag");
if (tag == null) if (tag == null)
tag = ""; tag = "";
await e.Send(GetGelbooruImageLink(tag)); await e.Send(await GetGelbooruImageLink(tag));
}); });
cgb.CreateCommand("~cp") cgb.CreateCommand("~cp")
.Description("We all know where this will lead you to.") .Description("We all know where this will lead you to.")
@ -137,21 +137,21 @@ namespace NadekoBot.Modules {
.Parameter("ffs", ParameterType.Unparsed) .Parameter("ffs", ParameterType.Unparsed)
.Do(async e => { .Do(async e => {
if (e.GetArg("ffs") == null || e.GetArg("ffs").Length < 1) return; if (e.GetArg("ffs") == null || e.GetArg("ffs").Length < 1) return;
await e.Send($"http://lmgtfy.com/?q={ Uri.EscapeUriString(e.GetArg("ffs").ToString()) }".ShortenUrl()); await e.Send(await $"http://lmgtfy.com/?q={ Uri.EscapeUriString(e.GetArg("ffs").ToString()) }".ShortenUrl());
}); });
}); });
} }
public static string MakeRequestAndGetResponse(string v) => public static async Task<string> GetResponseAsync(string v) =>
new StreamReader(((HttpWebRequest)WebRequest.Create(v)).GetResponse().GetResponseStream()).ReadToEnd(); await new StreamReader((await ((HttpWebRequest)WebRequest.Create(v)).GetResponseAsync()).GetResponseStream()).ReadToEndAsync();
private string token = ""; private string token = "";
private AnimeResult GetAnimeQueryResultLink(string query) { private async Task<AnimeResult> GetAnimeQueryResultLink(string query) {
try { try {
var cl = new RestSharp.RestClient("http://anilist.co/api"); var cl = new RestSharp.RestClient("http://anilist.co/api");
var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST);
RefreshToken(); RefreshAnilistToken();
rq = new RestSharp.RestRequest("/anime/search/" + Uri.EscapeUriString(query)); rq = new RestSharp.RestRequest("/anime/search/" + Uri.EscapeUriString(query));
rq.AddParameter("access_token", token); rq.AddParameter("access_token", token);
@ -160,15 +160,15 @@ namespace NadekoBot.Modules {
rq = new RestSharp.RestRequest("anime/" + smallObj["id"]); rq = new RestSharp.RestRequest("anime/" + smallObj["id"]);
rq.AddParameter("access_token", token); rq.AddParameter("access_token", token);
return JsonConvert.DeserializeObject<AnimeResult>(cl.Execute(rq).Content); return await Task.Run(() => JsonConvert.DeserializeObject<AnimeResult>(cl.Execute(rq).Content));
} catch (Exception) { } catch (Exception) {
return null; return null;
} }
} }
//todo kick out RestSharp and make it truly async
private MangaResult GetMangaQueryResultLink(string query) { private async Task<MangaResult> GetMangaQueryResultLink(string query) {
try { try {
RefreshToken(); RefreshAnilistToken();
var cl = new RestSharp.RestClient("http://anilist.co/api"); var cl = new RestSharp.RestClient("http://anilist.co/api");
var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST);
@ -179,27 +179,31 @@ namespace NadekoBot.Modules {
rq = new RestSharp.RestRequest("manga/" + smallObj["id"]); rq = new RestSharp.RestRequest("manga/" + smallObj["id"]);
rq.AddParameter("access_token", token); rq.AddParameter("access_token", token);
return JsonConvert.DeserializeObject<MangaResult>(cl.Execute(rq).Content); return await Task.Run(() => JsonConvert.DeserializeObject<MangaResult>(cl.Execute(rq).Content));
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
return null; return null;
} }
} }
private void RefreshToken() { private void RefreshAnilistToken() {
var cl = new RestSharp.RestClient("http://anilist.co/api"); try {
var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST); var cl = new RestSharp.RestClient("http://anilist.co/api");
rq.AddParameter("grant_type", "client_credentials"); var rq = new RestSharp.RestRequest("/auth/access_token", RestSharp.Method.POST);
rq.AddParameter("client_id", "kwoth-w0ki9"); rq.AddParameter("grant_type", "client_credentials");
rq.AddParameter("client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"); rq.AddParameter("client_id", "kwoth-w0ki9");
var exec = cl.Execute(rq); rq.AddParameter("client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z");
/* var exec = cl.Execute(rq);
Console.WriteLine($"Server gave me content: { exec.Content }\n{ exec.ResponseStatus } -> {exec.ErrorMessage} "); /*
Console.WriteLine($"Err exception: {exec.ErrorException}"); Console.WriteLine($"Server gave me content: { exec.Content }\n{ exec.ResponseStatus } -> {exec.ErrorMessage} ");
Console.WriteLine($"Inner: {exec.ErrorException.InnerException}"); Console.WriteLine($"Err exception: {exec.ErrorException}");
*/ Console.WriteLine($"Inner: {exec.ErrorException.InnerException}");
*/
token = JObject.Parse(exec.Content)["access_token"].ToString(); token = JObject.Parse(exec.Content)["access_token"].ToString();
} catch (Exception ex) {
Console.WriteLine($"Failed refreshing anilist token:\n {ex}");
}
} }
private static async Task<bool> ValidateQuery(Discord.Channel ch, string query) { private static async Task<bool> ValidateQuery(Discord.Channel ch, string query) {
@ -210,7 +214,7 @@ namespace NadekoBot.Modules {
return true; return true;
} }
public static string FindYoutubeUrlByKeywords(string v) { public static async Task<string> FindYoutubeUrlByKeywords(string v) {
if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) { if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) {
Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`."); Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`.");
return @"https://www.youtube.com/watch?v=dQw4w9WgXcQ"; return @"https://www.youtube.com/watch?v=dQw4w9WgXcQ";
@ -223,21 +227,19 @@ namespace NadekoBot.Modules {
return str; return str;
} }
WebRequest wr = WebRequest.Create("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + Uri.EscapeDataString(v) + "&key=" + NadekoBot.GoogleAPIKey); WebRequest wr = WebRequest.Create("https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=" + Uri.EscapeDataString(v) + "&key=" + NadekoBot.GoogleAPIKey);
var sr = new StreamReader(wr.GetResponse().GetResponseStream()); var sr = new StreamReader((await wr.GetResponseAsync()).GetResponseStream());
dynamic obj = JObject.Parse(sr.ReadToEnd()); dynamic obj = JObject.Parse(await sr.ReadToEndAsync());
string toReturn = "http://www.youtube.com/watch?v=" + obj.items[0].id.videoId.ToString(); return "http://www.youtube.com/watch?v=" + obj.items[0].id.videoId.ToString();
return toReturn;
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine($"Error in findyoutubeurl: {ex.Message}"); Console.WriteLine($"Error in findyoutubeurl: {ex.Message}");
return string.Empty; return string.Empty;
} }
} }
public static string GetPlaylistIdByKeyword(string v) { public static async Task<string> GetPlaylistIdByKeyword(string v) {
if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) { if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) {
Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`."); Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`.");
return string.Empty; return string.Empty;
@ -245,18 +247,17 @@ namespace NadekoBot.Modules {
try { try {
WebRequest wr = WebRequest.Create($"https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q={Uri.EscapeDataString(v)}&type=playlist&key={NadekoBot.creds.GoogleAPIKey}"); WebRequest wr = WebRequest.Create($"https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q={Uri.EscapeDataString(v)}&type=playlist&key={NadekoBot.creds.GoogleAPIKey}");
var sr = new StreamReader(wr.GetResponse().GetResponseStream()); var sr = new StreamReader((await wr.GetResponseAsync()).GetResponseStream());
dynamic obj = JObject.Parse(sr.ReadToEnd()); dynamic obj = JObject.Parse(await sr.ReadToEndAsync());
string toReturn = obj.items[0].id.playlistId.ToString(); return obj.items[0].id.playlistId.ToString();
return toReturn;
} catch (Exception ex) { } catch (Exception ex) {
Console.WriteLine($"Error in GetPlaylistId: {ex.Message}"); Console.WriteLine($"Error in GetPlaylistId: {ex.Message}");
return string.Empty; return string.Empty;
} }
} }
public static List<string> GetVideoIDs(string v) { public static async Task<List<string>> GetVideoIDs(string v) {
List<string> toReturn = new List<string>(); List<string> toReturn = new List<string>();
if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) { if (NadekoBot.GoogleAPIKey == "" || NadekoBot.GoogleAPIKey == null) {
Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`."); Console.WriteLine("ERROR: No google api key found. Playing `Never gonna give you up`.");
@ -264,14 +265,14 @@ namespace NadekoBot.Modules {
} }
try { try {
WebRequest wr = WebRequest.Create($"https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails&maxResults={25}&playlistId=PL8lZieNFgOdmrNGTqwjqYJpJ_2nw_O_M2&key={ NadekoBot.creds.GoogleAPIKey }"); WebRequest wr = WebRequest.Create($"https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails&maxResults={25}&playlistId={v}&key={ NadekoBot.creds.GoogleAPIKey }");
var sr = new StreamReader(wr.GetResponse().GetResponseStream()); var sr = new StreamReader((await wr.GetResponseAsync()).GetResponseStream());
dynamic obj = JObject.Parse(sr.ReadToEnd()); dynamic obj = JObject.Parse(await sr.ReadToEndAsync());
foreach (var item in obj.items) { foreach (var item in obj.items) {
toReturn.Add("http://www.youtube.com/watch?v=" + item.id.contentDetails.videoId); toReturn.Add("http://www.youtube.com/watch?v=" + item.contentDetails.videoId);
} }
return toReturn; return toReturn;
} catch (Exception ex) { } catch (Exception ex) {
@ -281,30 +282,30 @@ namespace NadekoBot.Modules {
} }
public string GetDanbooruImageLink(string tag) { public async Task<string> GetDanbooruImageLink(string tag) {
try { try {
var rng = new Random(); var rng = new Random();
if (tag == "loli") //loli doesn't work for some reason atm if (tag == "loli") //loli doesn't work for some reason atm
tag = "flat_chest"; tag = "flat_chest";
var webpage = MakeRequestAndGetResponse($"http://danbooru.donmai.us/posts?page={ rng.Next(0, 30) }&tags={ tag.Replace(" ", "_") }"); var webpage = await GetResponseAsync($"http://danbooru.donmai.us/posts?page={ rng.Next(0, 30) }&tags={ tag.Replace(" ", "_") }");
var matches = Regex.Matches(webpage, "data-large-file-url=\"(?<id>.*?)\""); var matches = Regex.Matches(webpage, "data-large-file-url=\"(?<id>.*?)\"");
return $"http://danbooru.donmai.us{ matches[rng.Next(0, matches.Count)].Groups["id"].Value }".ShortenUrl(); return await $"http://danbooru.donmai.us{ matches[rng.Next(0, matches.Count)].Groups["id"].Value }".ShortenUrl();
} catch (Exception) { } catch (Exception) {
return null; return null;
} }
} }
public string GetGelbooruImageLink(string tag) { public async Task<string> GetGelbooruImageLink(string tag) {
try { try {
var rng = new Random(); var rng = new Random();
var url = $"http://gelbooru.com/index.php?page=post&s=list&pid={ rng.Next(0, 15) * 42 }&tags={ tag.Replace(" ", "_") }"; var url = $"http://gelbooru.com/index.php?page=post&s=list&pid={ rng.Next(0, 15) * 42 }&tags={ tag.Replace(" ", "_") }";
var webpage = MakeRequestAndGetResponse(url); // first extract the post id and go to that posts page var webpage = await GetResponseAsync(url); // first extract the post id and go to that posts page
var matches = Regex.Matches(webpage, "span id=\"s(?<id>\\d*)\""); var matches = Regex.Matches(webpage, "span id=\"s(?<id>\\d*)\"");
var postLink = $"http://gelbooru.com/index.php?page=post&s=view&id={ matches[rng.Next(0, matches.Count)].Groups["id"].Value }"; var postLink = $"http://gelbooru.com/index.php?page=post&s=view&id={ matches[rng.Next(0, matches.Count)].Groups["id"].Value }";
webpage = MakeRequestAndGetResponse(postLink); webpage = await GetResponseAsync(postLink);
//now extract the image from post page //now extract the image from post page
var match = Regex.Match(webpage, "\"(?<url>http://simg4.gelbooru.com//images.*?)\""); var match = Regex.Match(webpage, "\"(?<url>http://simg4.gelbooru.com//images.*?)\"");
return match.Groups["url"].Value; return match.Groups["url"].Value;
@ -313,21 +314,21 @@ namespace NadekoBot.Modules {
} }
} }
public static string ShortenUrl(string url) { public static async Task<string> ShortenUrl(string url) {
if (NadekoBot.GoogleAPIKey == null || NadekoBot.GoogleAPIKey == "") return url; if (NadekoBot.GoogleAPIKey == null || NadekoBot.GoogleAPIKey == "") return url;
var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/urlshortener/v1/url?key=" + NadekoBot.GoogleAPIKey);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream())) {
string json = "{\"longUrl\":\"" + url + "\"}";
streamWriter.Write(json);
}
try { try {
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse(); var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://www.googleapis.com/urlshortener/v1/url?key=" + NadekoBot.GoogleAPIKey);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(await httpWebRequest.GetRequestStreamAsync())) {
string json = "{\"longUrl\":\"" + url + "\"}";
streamWriter.Write(json);
}
var httpResponse = (await httpWebRequest.GetResponseAsync()) as HttpWebResponse;
using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) {
var responseText = streamReader.ReadToEnd(); string responseText = await streamReader.ReadToEndAsync();
string MATCH_PATTERN = @"""id"": ?""(?<id>.+)"""; string MATCH_PATTERN = @"""id"": ?""(?<id>.+)""";
return Regex.Match(responseText, MATCH_PATTERN).Groups["id"].Value; return Regex.Match(responseText, MATCH_PATTERN).Groups["id"].Value;
} }