NadekoBot/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs

277 lines
13 KiB
C#
Raw Normal View History

using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Globalization;
using System.IO;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
2016-06-21 15:57:24 +00:00
namespace NadekoBot.Modules.Searches
{
public partial class Searches
{
[Group]
public class OsuCommands
{
2016-12-08 17:35:34 +00:00
private static Logger _log;
2016-06-21 15:57:24 +00:00
2016-12-08 17:35:34 +00:00
static OsuCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-09-30 02:45:23 +00:00
public async Task Osu(IUserMessage umsg, string usr, [Remainder] string mode = null)
{
var channel = (ITextChannel)umsg.Channel;
2016-06-21 15:57:24 +00:00
if (string.IsNullOrWhiteSpace(usr))
return;
2016-06-21 15:57:24 +00:00
using (HttpClient http = new HttpClient())
{
try
{
var m = 0;
if (!string.IsNullOrWhiteSpace(mode))
{
m = ResolveGameMode(mode);
}
http.AddFakeHeaders();
var res = await http.GetStreamAsync(new Uri($"http://lemmmy.pw/osusig/sig.php?uname={ usr }&flagshadow&xpbar&xpbarhex&pp=2&mode={m}")).ConfigureAwait(false);
2016-06-21 15:57:24 +00:00
2016-10-13 01:18:32 +00:00
MemoryStream ms = new MemoryStream();
res.CopyTo(ms);
ms.Position = 0;
2016-11-21 20:57:42 +00:00
await channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link: **https://osu.ppy.sh/u/{Uri.EscapeDataString(usr)}\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync("💢 Failed retrieving osu signature :\\").ConfigureAwait(false);
_log.Warn(ex, "Osu command failed");
}
}
}
2016-06-21 15:57:24 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Osub(IUserMessage umsg, [Remainder] string map)
{
var channel = (ITextChannel)umsg.Channel;
2016-06-21 15:57:24 +00:00
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
{
await channel.SendMessageAsync("💢 An osu! API key is required.").ConfigureAwait(false);
return;
}
2016-06-21 15:57:24 +00:00
if (string.IsNullOrWhiteSpace(map))
return;
2016-06-21 15:57:24 +00:00
try
{
using (var http = new HttpClient())
{
var mapId = ResolveMap(map);
var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}";
var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0];
var sb = new System.Text.StringBuilder();
var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
var time = TimeSpan.FromSeconds(Double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss");
sb.AppendLine($"{obj["artist"]} - {obj["title"]}, mapped by {obj["creator"]}. https://osu.ppy.sh/s/{obj["beatmapset_id"]}");
sb.AppendLine($"{starRating} stars, {obj["bpm"]} BPM | AR{obj["diff_approach"]}, CS{obj["diff_size"]}, OD{obj["diff_overall"]} | Length: {time}");
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
}
}
catch (Exception ex)
{
await channel.SendMessageAsync("Something went wrong.");
_log.Warn(ex, "Osub command failed");
}
}
2016-06-21 15:57:24 +00:00
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
2016-10-13 01:18:32 +00:00
public async Task Osu5(IUserMessage umsg, string user, [Remainder] string mode = null)
{
var channel = (ITextChannel)umsg.Channel;
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey))
{
await channel.SendMessageAsync("💢 An osu! API key is required.").ConfigureAwait(false);
return;
}
2016-06-21 15:57:24 +00:00
if (string.IsNullOrWhiteSpace(user))
{
await channel.SendMessageAsync("💢 Please provide a username.").ConfigureAwait(false);
return;
}
using (var http = new HttpClient())
{
try
{
var m = 0;
if (!string.IsNullOrWhiteSpace(mode))
{
m = ResolveGameMode(mode);
}
2016-06-21 15:57:24 +00:00
var reqString = $"https://osu.ppy.sh/api/get_user_best?k={NadekoBot.Credentials.OsuApiKey}&u={Uri.EscapeDataString(user)}&type=string&limit=5&m={m}";
var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false));
var sb = new System.Text.StringBuilder($"`Top 5 plays for {user}:`\n```xl" + Environment.NewLine);
foreach (var item in obj)
{
var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&b={item["beatmap_id"]}";
var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0];
var pp = Math.Round(Double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2);
var acc = CalculateAcc(item, m);
var mods = ResolveMods(Int32.Parse($"{item["enabled_mods"]}"));
if (mods != "+")
sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | **{mods,-10}** | /b/{item["beatmap_id"]}");
else
sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}");
}
sb.Append("```");
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
}
catch (Exception ex)
{
await channel.SendMessageAsync("Something went wrong.");
_log.Warn(ex, "Osu5 command failed");
}
2016-06-21 15:57:24 +00:00
}
}
2016-06-21 15:57:24 +00:00
//https://osu.ppy.sh/wiki/Accuracy
private static Double CalculateAcc(JToken play, int mode)
{
if (mode == 0)
{
var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["count300"]}") * 300;
var totalHits = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countmiss"]}");
totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2);
}
else if (mode == 1)
{
var hitPoints = Double.Parse($"{play["countmiss"]}") * 0 + Double.Parse($"{play["count100"]}") * 0.5 + Double.Parse($"{play["count300"]}") * 1;
var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}");
hitPoints *= 300;
totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2);
}
else if (mode == 2)
{
var fruitsCaught = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}");
var totalFruits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countkatu"]}");
return Math.Round(fruitsCaught / totalFruits * 100, 2);
}
else
{
var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["countkatu"]}") * 200 + (Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}")) * 300;
var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["countkatu"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}");
totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2);
}
}
2016-06-21 15:57:24 +00:00
private static string ResolveMap(string mapLink)
{
Match s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink);
Match b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink);
Match p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink);
Match m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink);
if (s.Success)
{
var mapId = mapLink.Substring(mapLink.IndexOf("/s/") + 3);
return $"s={mapId}";
}
else if (b.Success)
{
if (m.Success)
return $"b={mapLink.Substring(mapLink.IndexOf("/b/") + 3, mapLink.IndexOf("&m") - (mapLink.IndexOf("/b/") + 3))}";
else
return $"b={mapLink.Substring(mapLink.IndexOf("/b/") + 3)}";
}
else if (p.Success)
{
if (m.Success)
return $"b={mapLink.Substring(mapLink.IndexOf("?b=") + 3, mapLink.IndexOf("&m") - (mapLink.IndexOf("?b=") + 3))}";
else
return $"b={mapLink.Substring(mapLink.IndexOf("?b=") + 3)}";
}
else
{
return $"s={mapLink}"; //just a default incase an ID number was provided by itself (non-url)?
}
}
2016-06-21 15:57:24 +00:00
private static int ResolveGameMode(string mode)
{
switch (mode.ToLower())
{
case "std":
case "standard":
return 0;
case "taiko":
return 1;
case "ctb":
case "catchthebeat":
return 2;
case "mania":
case "osu!mania":
return 3;
default:
return 0;
}
}
2016-06-21 15:57:24 +00:00
//https://github.com/ppy/osu-api/wiki#mods
private static string ResolveMods(int mods)
{
var modString = $"+";
2016-06-21 15:57:24 +00:00
if (IsBitSet(mods, 0))
modString += "NF";
if (IsBitSet(mods, 1))
modString += "EZ";
if (IsBitSet(mods, 8))
modString += "HT";
2016-06-21 15:57:24 +00:00
if (IsBitSet(mods, 3))
modString += "HD";
if (IsBitSet(mods, 4))
modString += "HR";
if (IsBitSet(mods, 6) && !IsBitSet(mods, 9))
modString += "DT";
if (IsBitSet(mods, 9))
modString += "NC";
if (IsBitSet(mods, 10))
modString += "FL";
2016-06-21 15:57:24 +00:00
if (IsBitSet(mods, 5))
modString += "SD";
if (IsBitSet(mods, 14))
modString += "PF";
2016-06-21 15:57:24 +00:00
if (IsBitSet(mods, 7))
modString += "RX";
if (IsBitSet(mods, 11))
modString += "AT";
if (IsBitSet(mods, 12))
modString += "SO";
return modString;
}
private static bool IsBitSet(int mods, int pos) =>
(mods & (1 << pos)) != 0;
}
}
2016-11-21 16:23:17 +00:00
}