Page MenuHomedesp's stash

Osu.java
No OneTemporary

Osu.java

package me.despawningbone.discordbot.command.games;
import java.awt.Color;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.xml.bind.DatatypeConverter;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.XYChart;
import org.knowm.xchart.XYChartBuilder;
import org.knowm.xchart.BitmapEncoder.BitmapFormat;
import org.knowm.xchart.XYSeries.XYSeriesRenderStyle;
import org.knowm.xchart.style.Styler.LegendPosition;
import me.despawningbone.discordbot.DiscordBot;
import me.despawningbone.discordbot.command.Command;
import me.despawningbone.discordbot.command.CommandResult;
import me.despawningbone.discordbot.command.CommandResult.CommandResultType;
import me.despawningbone.discordbot.command.games.Koohii.Accuracy;
import me.despawningbone.discordbot.command.games.Koohii.DiffCalc;
import me.despawningbone.discordbot.command.games.Koohii.Map;
import me.despawningbone.discordbot.command.games.Koohii.PPv2;
import me.despawningbone.discordbot.command.games.Koohii.PPv2Parameters;
import me.despawningbone.discordbot.command.games.Koohii.MapStats;
import me.despawningbone.discordbot.command.games.Koohii.TaikoPP;
import me.despawningbone.discordbot.utils.MiscUtils;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
public class Osu extends Command {
private final static String osuAPI = DiscordBot.tokens.getProperty("osu");
private static DecimalFormat df = new DecimalFormat("#.##");
private final static HashMap<Integer, String> modes = new HashMap<>();
static {
modes.put(0, "osu!");
modes.put(1, "osu!taiko");
modes.put(2, "osu!catch");
modes.put(3, "osu!mania");
}
public Osu() { //TODO add back the todos to respective sub command, automate desc for subcmds, add back typing; CLOSE ALL STREAMS
this.desc = "All the info you need with osu!";
this.usage = "<subcommands>";
//TODO add a command to parse replays?
registerSubCommand("pp", Arrays.asList("map"), (channel, user, msg, words) -> {
//OffsetDateTime timesent = OffsetDateTime.now();
List<String> amend = new ArrayList<String>(Arrays.asList(words));
int temp = amend.indexOf("-w");
String uid = null;
boolean weight = temp != -1;
if(weight) {
try {
uid = amend.get(temp + 1);
if(!uid.contains("osu.ppy.sh/u") && (uid.startsWith("http") && uid.contains("://"))) {
uid = getPlayer(user);
}
amend.subList(temp, temp + 1).clear();
} catch (IndexOutOfBoundsException e) {
uid = getPlayer(user);
}
}
String initmap;
try {
initmap = amend.get(0);
} catch (IndexOutOfBoundsException e) {
initmap = "null";
}
List<String> mid = new ArrayList<String>();
if (!initmap.startsWith("https://") && !initmap.startsWith("http://")) { //check if no map input, use discord rich presence
String details = null;
try (Connection con = DiscordBot.db.getConnection()) {
ResultSet rs = con.createStatement().executeQuery("SELECT game FROM users WHERE id = " + user.getId() + ";");
if(rs.next()) {
details = rs.getString(1).substring(rs.getString(1).indexOf("||"));
}
rs.close();
} catch (SQLException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
channel.sendMessage("Trying to retrieve map from discord status...").queue(m -> mid.add(m.getId())); //DONE custom status masks presence now, will not be as effective; any way to get manually? //JDA 4 API change fixed this
channel.sendTyping().queue();
/*OffsetDateTime timeReceived = OffsetDateTime.now();
long ms = Math.abs(timesent.until(timeReceived, ChronoUnit.MILLIS));
System.out.println("Time taken: " + ms + "ms");*/
if (details != null) { //TODO if name is sth like `Feryquitous - (S).0ngs//---::compilation.[TQR-f3] [-[//mission:#FC.0011-excindell.defer.abferibus]-]` it breaks (but reasonable break tbh)
//if(game.getName().equals("osu!") && game.isRich()) {
try {
String title = URLEncoder.encode(details.substring(details.indexOf(" - ") + 3, details.lastIndexOf("[")).trim(), "UTF-8");
String diff = URLEncoder.encode(details.substring(details.lastIndexOf("[") + 1, details.lastIndexOf("]")).trim(), "UTF-8");
String url = "https://osusearch.com/query/?title=" + title + "&diff_name=" + diff + "&query_order=play_count&offset=0";
URLConnection stream = new URL(url).openConnection();
stream.addRequestProperty("User-Agent", "Mozilla/4.0");
JSONTokener tokener = new JSONTokener(stream.getInputStream());
initmap = "https://osu.ppy.sh/beatmaps/" + new JSONObject(tokener).getJSONArray("beatmaps").getJSONObject(0).getInt("beatmap_id");
} catch (IOException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
} catch (JSONException e) {
e.printStackTrace();
System.out.println(details);
return new CommandResult(CommandResultType.NORESULT); //returns if osusearch isnt updated fast enough
}
/*SELENIUM DEPRECATED*/
/*String url = "http://osusearch.com/search/?title=" + title + "&diff_name=" + diff
+ "&query_order=play_count";
System.out.println(url);
//MsgListener.driver.manage().timeouts().implicitlyWait(2000, TimeUnit.MILLISECONDS);
DiscordBot.driver.get(url);
WebDriverWait wait = new WebDriverWait(DiscordBot.driver, 10);
wait.until(ExpectedConditions
.elementToBeClickable(By.cssSelector("div[class~=beatmap-list]")));
Document document = Jsoup.parse(DiscordBot.driver.getPageSource());
//System.out.println(document.toString());
initmap = document.body()
.select("div[class~=beatmap-list]")
.first()
.child(0)
.select("div.truncate.beatmap-title a").get(0).attr("href");*/
} else {
return new CommandResult(CommandResultType.FAILURE, "There is no account of your rich presence, therefore I cannot get the beatmap from your status.");
}
/*
* } else { throw new
* IllegalArgumentException("Rich presence is not an instance of osu!, therefore I cannot get the beatmap from your status."
* ); }
*/
}
if(initmap.equals("null")) { //shouldnt throw at all
return new CommandResult(CommandResultType.FAILURE, "You haven't played any maps I can recognize yet!");
}
List<String> params = Arrays.asList(words);
double dacc = 100;
int combo = -1, mods = 0, miss = 0;
PPv2Parameters p = new Koohii.PPv2Parameters();
try {
p.beatmap = new Koohii.Parser()
.map(new BufferedReader(new InputStreamReader(getMap(initmap), "UTF-8")));
} catch (IOException e1) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e1));
} catch (UnsupportedOperationException e1) {
return new CommandResult(CommandResultType.FAILURE, "This gamemode is not yet supported.");
} catch (IllegalArgumentException e1) {
return new CommandResult(CommandResultType.INVALIDARGS, e1.getMessage());
}
if (p.beatmap.title.isEmpty()) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid beatmap.");
}
try {
for (int i = 0; i < params.size(); i++) {
String param = params.get(i);
if (param.startsWith("+")) {
mods = Koohii.mods_from_str(param.substring(1).toUpperCase());
} else if (param.toLowerCase().endsWith("m")) {
miss = Integer.parseInt(param.substring(0, param.length() - 1));
} else if (param.endsWith("%")) {
dacc = Double.parseDouble(param.substring(0, param.length() - 1));
} else if (param.toLowerCase().endsWith("x")) {
combo = Integer.parseInt(param.substring(0, param.length() - 1));
}
}
} catch (NumberFormatException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid value inputted.");
}
DiffCalc stars = new Koohii.DiffCalc().calc(p.beatmap, mods);
Accuracy acc = new Accuracy(dacc, p.beatmap.objects.size(), miss);
p.n50 = acc.n50;
p.n100 = acc.n100;
p.n300 = acc.n300;
p.nmiss = miss;
p.aim_stars = stars.aim;
p.speed_stars = stars.speed;
p.mods = mods;
if (combo > 0) {
p.combo = combo;
} else {
p.combo = p.beatmap.max_combo();
}
EmbedBuilder eb = new EmbedBuilder();
JSONTokener result = null;
InputStream stream = null;
URL url = null;
try {
String addparam = "";
if(p.beatmap.mode == 1) {
if((mods & 1<<8) != 0) addparam = "&mods=256";
if((mods & 1<<6) != 0) if(addparam.isEmpty()) addparam = "&mods=64";
else addparam = "";
}
url = new URL("https://osu.ppy.sh/api/get_beatmaps?k=" + osuAPI + "&b=" + initmap.substring(initmap.lastIndexOf("/") + 1).split("&")[0] + addparam);
stream = url.openStream();
} catch (IOException ex) {
ex.printStackTrace();
}
result = new JSONTokener(stream);
JSONObject jbm = new JSONArray(result).getJSONObject(0);
String setid = jbm.getString("beatmapset_id");
double accVal, ppVal, starsVal;
MapStats stats = new MapStats(p.beatmap);
try {
PPv2 pp = new PPv2(p);
ppVal = pp.total;
accVal = acc.value(); //somehow real_acc aint correct, pretty sure i screwed sth up
starsVal = stars.total;
Koohii.mods_apply(mods, stats, 1|2|4|8);
} catch (UnsupportedOperationException e) { //should always only be taiko
starsVal = jbm.getDouble("difficultyrating");
TaikoPP pp = new TaikoPP(starsVal, dacc, p);
ppVal = pp.pp;
accVal = pp.acc;
Koohii.mods_apply(mods, stats, 0); //want nothing but speed to be changed
}
String totaldur = MiscUtils.convertMillis(TimeUnit.SECONDS.toMillis((int)(jbm.getInt("total_length") / stats.speed)));
String draindur = MiscUtils.convertMillis(TimeUnit.SECONDS.toMillis((int)(jbm.getInt("hit_length") / stats.speed)));
if(jbm.has("source") && !jbm.getString("source").isEmpty()) eb.setTitle("Source: " + jbm.getString("source"));
eb.setDescription("Mode: " + modes.get(p.beatmap.mode));
eb.setAuthor("PP information for " + (p.beatmap.title_unicode.isEmpty() ? p.beatmap.title : p.beatmap.title_unicode),
initmap, "https://b.ppy.sh/thumb/" + setid + ".jpg");
eb.addField("Artist", (p.beatmap.artist_unicode.isEmpty() ? p.beatmap.artist : p.beatmap.artist_unicode),
true);
eb.addField("Created by", p.beatmap.creator, true);
eb.addField("Duration", totaldur + " | Drain " + draindur, true);
eb.addField("Difficulty", p.beatmap.version, true);
eb.addField("Mods", (p.mods == 0 ? "None" : Koohii.mods_str(p.mods)), true);
eb.addField("BPM", df.format(jbm.getDouble("bpm") * stats.speed), true);
eb.addField("Accuracy", df.format(accVal * 100), true);
eb.addField("Combo", String.valueOf(p.combo), true);
eb.addField("Misses", String.valueOf(p.nmiss), true);
eb.addField("300", String.valueOf(p.n300), true);
eb.addField("100", String.valueOf(p.n100), true);
eb.addField("50", String.valueOf(p.n50), true);
eb.addField("PP", df.format(ppVal), true);
if(weight) {
try {
String gain = df.format(weightForPP(ppVal, jbm.getString("beatmap_id"), getUserBest(uid, p.beatmap.mode)));
eb.addField("Actual pp gain for " + uid, (gain.equals("-1") ? "User has a better score than this" : gain + "pp"), true);
} catch (IllegalArgumentException e1) {
return new CommandResult(CommandResultType.INVALIDARGS, e1.getMessage());
}
}
eb.setFooter("Stars: " + df.format(starsVal) + " | CS: " + df.format(stats.cs) + " | HP: " + df.format(stats.hp)
+ " | AR: " + df.format(stats.ar) + " | OD: " + df.format(stats.od), null);
//e.setColor(new Color(248, 124, 248));
eb.setColor(new Color(239, 109, 167));
if(!mid.isEmpty()) {
channel.deleteMessageById(mid.get(0)).queue();
}
//channel.sendMessage(e.build()).queue();
channel.sendMessage(eb.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
}, "[beatmap URL] [+|%|x|m] [-w [username]]", Arrays.asList("-w", "+DT", "https://osu.ppy.sh/b/1817768 93.43% -w", "https://osu.ppy.sh/beatmaps/1154509 +HDDT 96.05% 147x 2m -w despawningbone"),
"Check PP information about that beatmap!", Arrays.asList(
" * Available parameters: +[mod alias], [accuracy]%, [combo]x, [misses]m",
" * specify the `-w` parameter with a username (or none for your own) to check how many actual pp the play will get them!",
" * You can also not input the URL, if you are currently playing osu! and discord sensed it."));
registerSubCommand("weight", Arrays.asList("w"), (channel, user, msg, words) -> {
//System.out.println(weightForPP(95.91, null, null));
//String debugMax = "", debugMin = "", debugData = "", debugAll = "";
List<String> params = new ArrayList<>(Arrays.asList(words));
int modeId = getMode(params);
final double precision = 1e-4, iterations = 500;
String uid;
int index = params.size() < 2 ? 0 : 1;
try {
uid = params.get(index - 1);
if(index == 0 || (!uid.contains("osu.ppy.sh/u") && (uid.startsWith("http") && uid.contains("://")))) {
uid = getPlayer(user);
}
} catch (IndexOutOfBoundsException e) {
uid = getPlayer(user);
}
JSONArray arr = null;
try {
arr = getUserBest(uid, modeId);
} catch (IllegalArgumentException e) {
return new CommandResult(CommandResultType.INVALIDARGS, e.getMessage());
}
double maxPP = arr.getJSONObject(0).getDouble("pp") + 100; //fricc those who wanna get pp over 100pp more than they can lmao
try {
//double basePP = new JSONArray(new JSONTokener(new URL("https://osu.ppy.sh/api/get_user?k=" + osuAPI + "&u=" + uid).openStream())).getJSONObject(0).getDouble("pp_raw");
double targetPP = params.size() > index ? Double.parseDouble(params.get(index)) : 1;
int i = 0; double minPP = 0, mid = 0;
/*for(i = 0; i < 600; i++) {
//debugData += weightForPP(i, null, arr) + "\n";
debugData += i + " " + weightForPP(i, null, arr) + "\n";
}*/
//System.out.println(debugData);
i = 0;
while(i < iterations && Math.abs(maxPP - minPP) > precision) {
mid = (maxPP + minPP) / 2;
double temp = weightForPP(mid, null, arr);
//System.out.println(i + ", " + mid + ", " + temp);
if(temp > targetPP) {
maxPP = mid;
} else {
minPP = mid;
}
i++;
//System.out.println(maxPP + " " + minPP);
//debugAll += maxPP + " " + minPP + "\n";
//debugMax += maxPP + "";
//debugMin += minPP + " ";
}
//System.out.println(debugAll);
//System.out.println(debugMax);
//System.out.println(debugMin);
if(!df.format(mid).equals(df.format(arr.getJSONObject(0).getDouble("pp") + 100))) {
channel.sendMessage("For " + uid + " to actually gain " + df.format(targetPP) + "pp in " + modes.get(modeId) + ", they have to play a map worth approximately **" + df.format(mid) + "pp** raw.").queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
return new CommandResult(CommandResultType.INVALIDARGS, "You can't really achieve such large pp jumps without people thinking you are hacking :P");
}
} catch (NumberFormatException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid target pp specified.");
}
//return "";
}, "[username] [wished pp gain]", Arrays.asList("", "10", "despawningbone 20"),
"Estimate how much raw pp a map needs to have in order to gain you pp!",
Arrays.asList(" * Supports `-t` for taiko, `-c` for catch, and `-m` for mania (Defaults to standard)."));
//TODO NEED EXTREME TIDYING UP
//TODO merge parts of the code with getRecent() and getPP();
registerSubCommand("recent", Arrays.asList("r"), (channel, user, msg, words) -> {
List<String> params = new ArrayList<>(Arrays.asList(words));
int modeId = getMode(params);
if(modeId > 1) modeId = 0;
boolean passOnly = params.removeAll(Collections.singleton("-p")); //no need space, as spaced ones are split (if a player has a name with spaced -p it might be a problem)
String[] split = String.join(" ", params).split("\\|");
String search = split[0].trim();
int nrecent = 0;
try {
nrecent = Integer.parseInt(split[1].trim()) - 1;
} catch (NumberFormatException e) {
//print to channel?
} catch (ArrayIndexOutOfBoundsException e) {
; //nothing special about this
}
if(nrecent > 50) {
return new CommandResult(CommandResultType.FAILURE, "Only the 50 most recent plays can be recalled!");
}
String id = "";
boolean noturl = false;
try {
new URL(search);
id = search.substring(search.lastIndexOf("/") + 1);
} catch (MalformedURLException e) {
noturl = true;
id = search;
}
if(id.isEmpty()) {
id = getPlayer(user);
}
JSONTokener result = null;
InputStream stream = null;
URL url = null;
try {
String addparam = "";
if (noturl) {
addparam = "&type=string";
}
if (modeId != 0) {
addparam += ("&m=" + modeId);
}
url = new URL(
"https://osu.ppy.sh/api/get_user_recent?k=" + osuAPI + "&u=" + URLEncoder.encode(id, "UTF-8") + addparam + "&limit=50");
stream = url.openStream();
if (stream.available() < 4) {
return new CommandResult(CommandResultType.INVALIDARGS, "Unknown player `" + id + "` or the player has not been playing in the last 24h.");
}
} catch (IOException ex) {
ex.printStackTrace();
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(ex));
}
result = new JSONTokener(stream);
JSONArray array = new JSONArray(result);
//System.out.println(array);
if(array.length() > 0) {
JSONObject mostRecent = null;
if(passOnly) {
int times = 0;
for(int i = 0; i < array.length(); i++) {
if(!array.getJSONObject(i).getString("rank").equals("F")) {
times++;
if(times - 1 >= nrecent) {
mostRecent = array.getJSONObject(i);
nrecent = i;
break;
}
}
}
if(mostRecent == null) {
return new CommandResult(CommandResultType.INVALIDARGS, "You did not have this much passed plays in the recent 50 plays!");
}
} else {
try {
mostRecent = array.getJSONObject(nrecent);
} catch (JSONException e) {
return new CommandResult(CommandResultType.FAILURE, "You only played " + array.length() + " times recently!");
}
}
String beatmap = mostRecent.getString("beatmap_id");
String name;
if (noturl) {
name = id;
id = mostRecent.getString("user_id");
} else {
try {
Long.parseLong(id);
//get more info from here?
name = new JSONObject(new JSONTokener(new URL("https://osu.ppy.sh/api/get_user?k=" + osuAPI + "&u=" + id).openStream())).getString("username");
} catch (NumberFormatException e) {
name = id;
} catch (IOException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
}
int i;
String more = "";
for(i = nrecent + 1; i < array.length(); i++) {
if(!array.getJSONObject(i).getString("beatmap_id").equals(beatmap)) {
break;
}
if(i == array.length() - 1) {
more = " (or more)";
}
}
if(i == array.length()) {
more = " (or more)";
}
PPv2Parameters p = new Koohii.PPv2Parameters();
try {
p.beatmap = new Koohii.Parser()
.map(new BufferedReader(new InputStreamReader(getMap(beatmap), "UTF-8")));
} catch (IOException e1) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e1));
} catch (UnsupportedOperationException e1) {
return new CommandResult(CommandResultType.FAILURE, "This gamemode is not yet supported.");
} catch (IllegalArgumentException e1) {
return new CommandResult(CommandResultType.INVALIDARGS, e1.getMessage());
}
int mods = mostRecent.getInt("enabled_mods");
if (p.beatmap.title.isEmpty()) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid beatmap.");
}
DiffCalc stars = new Koohii.DiffCalc().calc(p.beatmap, mods);
p.n50 = mostRecent.getInt("count50");
p.n100 = mostRecent.getInt("count100");
p.n300 = mostRecent.getInt("count300");
p.nmiss = mostRecent.getInt("countmiss");
//System.out.println(p.n300);
p.nobjects = p.n300 + p.n100 + p.n50 + p.nmiss;
//System.out.println(p.nobjects);
p.aim_stars = stars.aim;
p.speed_stars = stars.speed;
p.mods = mods;
p.combo = mostRecent.getInt("maxcombo");
EmbedBuilder eb = new EmbedBuilder();
//System.out.println(mods);
String modStr = Koohii.mods_str(mods);
eb.setColor(new Color(0, 0, 0));
double ppVal, ppMax, starVal, accVal;
JSONObject obj;
try {
String addparam = "";
if(p.beatmap.mode == 1) {
if((mods & 1<<8) != 0) addparam = "&mods=256";
if((mods & 1<<6) != 0) if(addparam.isEmpty()) addparam = "&mods=64";
else addparam = "";
}
obj = new JSONArray(new JSONTokener(new URL("https://osu.ppy.sh/api/get_beatmaps?k=" + osuAPI + "&b=" + mostRecent.getInt("beatmap_id") + addparam).openStream())).getJSONObject(0);
} catch (JSONException | IOException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
try {
PPv2 pp = new PPv2(p);
starVal = stars.total;
accVal = pp.computed_accuracy.value();
ppVal = pp.total;
ppMax = constructPP(stars.aim, stars.speed, p.beatmap, mods).total;
} catch (UnsupportedOperationException e) {
starVal = obj.getDouble("difficultyrating");
TaikoPP pp = new TaikoPP(starVal, p);
ppVal = pp.pp;
accVal = pp.acc;
ppMax = new TaikoPP(starVal, p.max_combo, mods, 1, 0, p.beatmap).pp;
}
eb.setTitle((p.beatmap.artist_unicode.isEmpty() ? p.beatmap.artist : p.beatmap.artist_unicode) + " - " + (p.beatmap.title_unicode.isEmpty() ? p.beatmap.title : p.beatmap.title_unicode) + " [" + p.beatmap.version + "]" + (modStr.isEmpty() ? "" : " +" + modStr) + " (" + df.format(starVal) + "*)", "https://osu.ppy.sh/beatmaps/" + beatmap);
eb.setAuthor(MiscUtils.ordinal(nrecent + 1) + " most recent " + modes.get(modeId) + " play by " + name, "https://osu.ppy.sh/users/" + id, "https://a.ppy.sh/" + id);
eb.setDescription(MiscUtils.ordinal(i - nrecent) + more + " consecutive try\nRanking: " + mostRecent.getString("rank").replaceAll("H", "+").replaceAll("X", "SS") + (mostRecent.getString("rank").equals("F") ? " (Estimated completion percentage: " + df.format(p.nobjects * 100.0 / p.beatmap.objects.size()) + "%)" : ""));
eb.setThumbnail("https://b.ppy.sh/thumb/" + obj.getString("beatmapset_id") + ".jpg");
eb.addField("Score", String.valueOf(mostRecent.getInt("score")), true);
eb.addField("Accuracy", df.format(accVal * 100) + " (" + p.n300 + "/" + p.n100 + "/" + p.n50 + "/" + p.nmiss + ")", true);
eb.addField("Combo", (mostRecent.getInt("perfect") == 0 ? p.combo + "x / " + p.beatmap.max_combo() + "x" : "FC (" + p.combo + "x)"), true);
eb.addField("PP", df.format(ppVal) + "pp / " + df.format(ppMax) + "pp", true);
eb.setFooter("Score submitted", null);
eb.setTimestamp(OffsetDateTime.parse(mostRecent.getString("date").replace(" ", "T") + "+00:00"));
channel.sendMessage(eb.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
return new CommandResult(CommandResultType.FAILURE, "You have no recent plays in this 24h!");
}
}, "[-p] [username] [| index]", Arrays.asList("", "-p | 2", "-p despawningbone"),
"Check a user's recent play!", Arrays.asList(
" * Specifying the `-p` parameter will only search for plays that did not fail.",
" * Supports `-t` for taiko (Defaults to standard)."));
//TODO tidy up first part, merge with getTopPlays()?
registerSubCommand("ppchart", Arrays.asList("ppc"), (channel, user, msg, words) -> {
if(words.length < 30) {
List<String> params = new ArrayList<>(Arrays.asList(words));
int modeId = getMode(params);
if(params.size() < 1) {
params.add(getPlayer(user));
}
String[] split = String.join(" ", params).split("\\|");
String concName = String.join(" | ", split);
XYChart chart = new XYChartBuilder().width(800).height(600).title("Top PP plays for " + concName + " (" + modes.get(modeId) + ")").yAxisTitle("PP").xAxisTitle("Plays (100 = top)").build();
chart.getStyler().setDefaultSeriesRenderStyle(XYSeriesRenderStyle.Scatter);
chart.getStyler().setLegendPosition(LegendPosition.InsideSE);
chart.getStyler().setPlotContentSize(0.91);
chart.getStyler().setMarkerSize(7);
//chart.getStyler().setAxisTitlePadding(24);
chart.getStyler().setChartTitlePadding(12);
chart.getStyler().setChartPadding(12);
chart.getStyler().setChartBackgroundColor(new Color(200, 220, 230));
//ThreadLocalRandom ran = ThreadLocalRandom.current();
for(String name : split) {
List<Double> pps = new ArrayList<>();
try {
JSONArray array = getUserBest(name.trim(), modeId);
for(int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
pps.add(obj.getDouble("pp"));
}
} catch (IllegalArgumentException e) {
return new CommandResult(CommandResultType.INVALIDARGS, e.getMessage());
}
Collections.reverse(pps);
/*XYSeries series =*/ chart.addSeries(name.trim() + "'s PP", pps);
//use series.setXYSeriesRenderStyle() for average line
//series.setMarkerColor(new Color(ran.nextInt(1, 255), ran.nextInt(1, 255), ran.nextInt(1, 255)));
}
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
BitmapEncoder.saveBitmap(chart, os, BitmapFormat.PNG);
} catch (IOException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
channel.sendFile(new ByteArrayInputStream(os.toByteArray()), "ppchart.png").queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
return new CommandResult(CommandResultType.INVALIDARGS, "Please do not include so many players at once.");
}
}, "[mode] [username] [| usernames]...", Arrays.asList("", "taiko", "FlyingTuna | Rafis | despawningbone"),
"Get a graph with the users' top plays!", Arrays.asList(
" * You can specify up to 30 players at once, seperated by `|`."
+ " * Supports `-t` for taiko, `-c` for catch, and `-m` for mania (Defaults to standard)."));
registerSubCommand("set", Arrays.asList("s"), (channel, user, msg, words) -> {
int page = 1;
if (words.length < 1) {
return new CommandResult(CommandResultType.INVALIDARGS, "Please enter search words or an URL.");
}/* else if (words.length > 2) {
try {
page = Integer.parseInt(words[2]);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Please enter a valid page number.");
}
}*/
String id = "", set = "";
try {
new URL(words[0]);
} catch (MalformedURLException e) {
//throw new IllgalArgumentException("Invalid URL."); //NOFIX: for some reason, discord seems to continue the typing after this has been sent //its because of queue()
try {
String[] split = String.join(" ", words).split("\\|");
String search = URLEncoder.encode(split[0].trim(), "UTF-8");
try {
if(split.length > 1) page = Integer.parseInt(split[1].trim());
} catch (NumberFormatException e1) {
return new CommandResult(CommandResultType.INVALIDARGS, "Please enter a valid page number.");
}
String url = "https://osusearch.com/query/?title=" + search + "&query_order=play_count&offset=0";
URLConnection stream;
stream = new URL(url).openConnection();
stream.addRequestProperty("User-Agent", "Mozilla/4.0");
JSONTokener tokener = new JSONTokener(stream.getInputStream());
id = String.valueOf(new JSONObject(tokener).getJSONArray("beatmaps").getJSONObject(0).getInt("beatmapset_id"));
set = "https://osu.ppy.sh/beatmapset/" + id;
} catch (IOException | JSONException e1) {
if(e1 instanceof JSONException) {
return new CommandResult(CommandResultType.NORESULT);
} else {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e1));
}
}
}
if(id.equals("")) {
set = words[0];
id = set.substring(set.lastIndexOf("/") + 1);
if (set.contains("/beatmapsets/")) {
id = set.substring(set.lastIndexOf("/beatmapsets/"));
id = id.substring(id.lastIndexOf("/") + 1);
} else {
if (set.contains("/b/") || set.contains("#")) {
return new CommandResult(CommandResultType.INVALIDARGS, "This is a specific beatmap, not a beatmap set.");
}
id = id.split("&")[0];
}
}
JSONTokener result = null;
InputStream stream = null;
URL url = null;
try {
url = new URL("https://osu.ppy.sh/api/get_beatmaps?k=" + osuAPI + "&s=" + id);
stream = url.openStream();
} catch (IOException ex) {
ex.printStackTrace();
}
result = new JSONTokener(stream);
JSONArray arr = new JSONArray(result);
List<JSONObject> obj = new ArrayList<JSONObject>();
for (int i = 0; i < arr.length(); i++) {
obj.add(arr.getJSONObject(i));
}
List<JSONObject> fobj = obj.stream().sorted(Comparator.comparing(e -> e.getDouble("difficultyrating")))
.collect(Collectors.groupingBy(e -> e.getInt("mode"))).values().stream().flatMap(List::stream)
.collect(Collectors.toList());
EmbedBuilder em = new EmbedBuilder();
JSONObject fo;
try {
fo = fobj.get(0);
} catch (IndexOutOfBoundsException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid beatmap URL.");
}
if(page > Math.ceil((double) fobj.size() / 3)) {
return new CommandResult(CommandResultType.INVALIDARGS, "The beatmap set does not have this many difficulties!");
}
em.setAuthor("Beatmap set information of " + fo.getString("title"), set,
"https://b.ppy.sh/thumb/" + id + ".jpg");
em.setDescription("Author: " + fo.getString("artist"));
em.addField("BPM", fo.getString("bpm"), true);
em.addField("Creator", fo.getString("creator"), true);
em.addField("Approved/Ranked", (fo.getInt("approved") >= 1 ? "Yes" : "No"), true);
em.addBlankField(false);
int n = ((page - 1) * 3);
//String desc = "";
for (int i = n; (n + 3 <= fobj.size() ? i < n + 3 : i < fobj.size()); i++) {
JSONObject json = fobj.get(i);
String mode = modes.get(json.getInt("mode"));
/*desc += "**[" + json.getString("version") + "](https://osu.ppy.sh/b/" + json.getString("beatmap_id") + ")** (" + mode + ")\n";
desc += df.format(json.getDouble("difficultyrating")) + "* " + (json.get("max_combo").toString() + "*").replaceAll("null*", "N/A") + "";*/
em.addField("Difficulty", "[" + json.getString("version") + "](https://osu.ppy.sh/b/" + json.getString("beatmap_id") + ")" + " ([Preview](http://bloodcat.com/osu/preview.html#" + json.getString("beatmap_id") + "))", false);
/*em.addField("URL", "https://osu.ppy.sh/b/" + json.getString("beatmap_id"), true);
em.addBlankField(true);*/
em.addField("Max combo", json.get("max_combo").toString().replaceAll("null", "N/A"), true);
em.addField("Stars", df.format(json.getDouble("difficultyrating")), true);
em.addField("Mode", mode, true);
}
em.setFooter("Page: " + String.valueOf(page) + "/" + String.valueOf((int) Math.ceil((double) fobj.size() / 3)),
null);
channel.sendMessage(em.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
}, "<beatmap set URL/search words> [| page]\n", Arrays.asList("big black", "high free spirits | 2"),
"Check info about a beatmap set!", Arrays.asList(
" * Useful to get the specific difficulty URL for !desp osu pp."));
//TODO add userset for setting username to db so getPlayer wont be wrong?
registerSubCommand("user", Arrays.asList("u"), (channel, user, msg, words) -> {
List<String> params = new ArrayList<>(Arrays.asList(words));
int modeId = getMode(params);
String search = String.join(" ", params);
String id = "";
boolean noturl = false;
try {
new URL(search);
id = search.substring(search.lastIndexOf("/") + 1);
} catch (MalformedURLException e) {
noturl = true;
id = search;
}
if(id.isEmpty()) {
id = getPlayer(user);
}
JSONTokener result = null;
InputStream stream = null;
URL url = null;
try {
String addparam = "";
if (noturl) {
addparam = "&type=string";
}
if (modeId != 0) {
addparam += ("&m=" + modeId);
}
url = new URL(
"https://osu.ppy.sh/api/get_user?k=" + osuAPI + "&u=" + URLEncoder.encode(id, "UTF-8") + addparam);
stream = url.openStream();
if (stream.available() < 4) {
return new CommandResult(CommandResultType.INVALIDARGS, "Unknown user `" + id + "`.");
}
} catch (IOException ex) {
ex.printStackTrace();
}
result = new JSONTokener(stream);
JSONObject usr = new JSONArray(result).getJSONObject(0);
id = usr.getString("user_id"); //no matter if its url or not user_id is still gonna be the most accurate one
EmbedBuilder em = new EmbedBuilder();
em.setAuthor("User info of " + usr.getString("username") + " (" + modes.get(modeId) + ")", "https://osu.ppy.sh/users/" + id);
String img = "https://a.ppy.sh/" + id;
try {
new URL(img).openStream();
} catch (IOException e) {
img = "https://s.ppy.sh/images/blank.jpg";
}
em.setThumbnail(img);
try {
em.addField("PP", usr.getDouble("pp_raw") == 0 ? "No Recent Plays" : usr.getString("pp_raw"), true);
em.addField("Accuracy", df.format(usr.getDouble("accuracy")), true);
} catch (JSONException e) {
return new CommandResult(CommandResultType.FAILURE, "This user has not played any map in " + modes.get(modeId) + " before.");
}
StringBuffer unibuff = new StringBuffer();
char[] ch = usr.getString("country").toLowerCase().toCharArray();
for (char c : ch) {
int temp = (int) c;
int temp_integer = 96; //for lower case
if (temp <= 122 & temp >= 97)
unibuff.append(Character.toChars(127461 + (temp - temp_integer)));
}
em.addField("Rank", (usr.getInt("pp_rank") == 0 ? "N/A" : "#" + usr.getString("pp_rank")) + " | Country #" + usr.getString("pp_country_rank"), true);
em.addField("Country", unibuff.toString(), true);
em.addField("Level", df.format(usr.getDouble("level")), true);
em.addField("Play count", usr.getString("playcount"), true);
em.addField("SS+", usr.getString("count_rank_ssh"), true);
em.addField("SS", usr.getString("count_rank_ss"), true);
double total = usr.getDouble("total_score");
em.addField("Total score", String.valueOf((long) total), true);
em.addField("S+", usr.getString("count_rank_sh"), true);
em.addField("S", usr.getString("count_rank_s"), true);
double ranked = usr.getDouble("ranked_score");
em.addField("Ranked score", String.valueOf((long) ranked) + " (" + df.format((ranked / total) * 100) + "%)", true);
em.setColor(new Color(66, 134, 244));
em.setFooter("300: " + usr.getString("count300") + " | 100: " + usr.getString("count100") + " | 50: "
+ usr.getString("count50"), null); //TODO display percentage?
channel.sendMessage(em.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
}, "[gamemode] [user URL|keyword]", Arrays.asList("", "despawningbone", "taiko despawningbone"),
"Check info about a user!", Arrays.asList(
" * Supports `-t` for taiko, `-c` for catch, and `-m` for mania (Defaults to standard)."));
//DONE api demanding; need to add cooldown //nvm i changed it to use XHR now
//parts mergeable with getRecent()
registerSubCommand("topplays", Arrays.asList("top", "t"), (channel, user, msg, words) -> {
List<String> params = new ArrayList<>(Arrays.asList(words));
int modeId = getMode(params), page;
String[] split = String.join(" ", params).split("\\|");
String search = split[0];
try {
page = Integer.parseInt(split[1].trim());
if(page > 10) {
return new CommandResult(CommandResultType.INVALIDARGS, "You can only request your top 100 plays!");
}
} catch (NumberFormatException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid page number!");
} catch (ArrayIndexOutOfBoundsException e) {
page = 1; //its normal for people to not input index
}
String id = "";
boolean noturl = false;
try {
new URL(search);
id = search.substring(search.lastIndexOf("/") + 1);
} catch (MalformedURLException e) {
noturl = true;
id = search;
}
if(id.isEmpty()) {
id = getPlayer(user);
}
JSONTokener result = null;
InputStream stream = null;
String name, addparam = "";
try {
if (noturl) {
addparam = "&type=string";
}
JSONObject userObj = new JSONArray(new JSONTokener(new URL("https://osu.ppy.sh/api/get_user?k=" + osuAPI + "&u=" + id + addparam).openStream())).getJSONObject(0);
name = userObj.getString("username");
id = userObj.getString("user_id");
URLConnection con = new URL("https://osu.ppy.sh/users/" + id + "/scores/best?mode=" + (modeId == 0 ? "osu" : modeId == 1 ? "taiko" : modeId == 2 ? "fruits" : modeId == 3 ? "mania" : "osu") + "&offset=" + (page - 1) * 10 + "&limit=10").openConnection();
stream = con.getInputStream();
if (stream.available() < 4) {
return new CommandResult(CommandResultType.FAILURE, "This player does not have this many plays in this gamemode!");
}
} catch (IOException ex) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(ex));
} catch (JSONException ex) {
return new CommandResult(CommandResultType.INVALIDARGS, "Unknown user `" + id + "`.");
}
result = new JSONTokener(stream);
JSONArray array = new JSONArray(result);
EmbedBuilder eb = new EmbedBuilder();
eb.setAuthor(name + "'s top plays (" + modes.get(modeId) + ")", "https://osu.ppy.sh/users/" + id, "https://a.ppy.sh/" + id);
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
for(int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
String mods = obj.getJSONArray("mods").join("").replaceAll("\"", "");
/*bInfo = new JSONArray(new JSONTokener(new URL("https://osu.ppy.sh/api/get_beatmaps?k=" + osuAPI + "&b=" + bId).openStream())).getJSONObject(0);
String info = "**" + obj.getString("rank").replaceAll("H", "+").replaceAll("X", "SS") + "** "
+ df.format(bInfo.getDouble("difficultyrating")) + "*"
+ (!mods.isEmpty() ? " **" + mods + "** " : " ")
+ " ([link](https://osu.ppy.sh/beatmap/" + bId + "))"
+ "\n" + (obj.getDouble("pp") + "pp (" + obj.getInt("count300") + "/" + obj.getInt("count100") + "/" + obj.getInt("count50") + "/" + obj.getInt("countmiss") + ")")
+ " " + (obj.getInt("perfect") == 1 ? "FC " + obj.getInt("maxcombo") + "x" : obj.getInt("maxcombo") + (!bInfo.isNull("max_combo") ? "/" + bInfo.getInt("max_combo") + "x" : "x"))
+ "\nPlayed on " + obj.getString("date");
eb.addField((i + 1) + ". " + bInfo.getString("artist") + " - " + bInfo.getString("title") + " [" + bInfo.getString("version") + "]", info, false); //no unicode unfortunately*/
JSONObject stats = obj.getJSONObject("statistics");
JSONObject bInfo = obj.getJSONObject("beatmap");
JSONObject bsInfo = obj.getJSONObject("beatmapset");
int bId = bInfo.getInt("id");
String info = "**" + obj.getString("rank").replaceAll("H", "+").replaceAll("X", "SS") + "**"
+ (!mods.isEmpty() ? " **" + mods + "** " : " ")
+ df.format(bInfo.getDouble("difficulty_rating")) + "*"
+ " ([map link](https://osu.ppy.sh/beatmaps/" + bId + "))"
+ "\nScore: " + obj.getInt("score")
+ "\n**" + (obj.getDouble("pp") + "pp** | Weighted " + df.format(obj.getJSONObject("weight").getDouble("pp")) + "pp (" + df.format(obj.getJSONObject("weight").getDouble("percentage")) + "%)"
+ "\n" + df.format(obj.getDouble("accuracy") * 100) + "% " + (obj.getBoolean("perfect") ? "FC " + obj.getInt("max_combo") + "x" : obj.getInt("max_combo") + "x") + " (" + stats.getInt("count_300") + "/" + stats.getInt("count_100") + "/" + stats.getInt("count_50") + "/" + stats.getInt("count_miss") + ")")
+ "\nPlayed on " + format.format(DatatypeConverter.parseDateTime(obj.getString("created_at")).getTime());
eb.addField(((page - 1) * 10 + (i + 1)) + ". " + bsInfo.getString("artist") + " - " + bsInfo.getString("title") + " [" + bInfo.getString("version") + "]", info, false); //no unicode unfortunately*/
}
eb.setFooter("Page: " + page, null);
eb.setColor(new Color(5, 255, 162));
} catch (JSONException e1) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e1));
}
channel.sendMessage(eb.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
}, "[username] [|page]", Arrays.asList("", "| 2", "despawningbone", "-m despawningbone"),
"Get the top plays of a user!", Arrays.asList(
" * Supports `-t` for taiko, `-c` for catch, and `-m` for mania (Defaults to standard)."));
/*registerSubCommand("search", Arrays.asList("s"), (channel, user, msg, words) -> { //need to deprecate google search //no longer works at all
String search = String.join(" ", Arrays.asList(words));
List<Entry<String, String>> url = new ArrayList<>();
try {
url = GoogleSearch
.search(URLEncoder.encode("site:https://osu.ppy.sh/ " + search, "UTF-8"), 10);
} catch (IOException e1) {
e1.printStackTrace();
}
if (!url.isEmpty()) {
String list = "";
Stream<Entry<String, String>> stream = url.stream();
if (words[0].equals("mapsearch")) {
stream = stream.filter(n -> n.getValue().contains("/b/") || n.getValue().contains("/s/")
|| n.getValue().contains("/beatmapsets/"));
} else if (words[0].equals("usersearch")) {
stream = stream.filter(n -> n.getValue().contains("/u/") || n.getValue().contains("/users/"));
}
list = "Search results for " + search + ":\n\n";
list += stream
.map(e -> (e.getKey() + "\n" + e.getValue() + "\n").replaceAll("<b>", "").replaceAll("</b>", ""))
.collect(Collectors.joining("\n"));
if (!list.isEmpty()) {
channel.sendMessage(list).queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
return new CommandResult(CommandResultType.NORESULT);
}
} else {
return new CommandResult(CommandResultType.NORESULT);
}
}, "search|usersearch|mapsearch [keywords]", Arrays.asList("usersearch cookiezi", "search big black"),
"Search the osu website!", null);*/
registerSubCommand("compare", Arrays.asList("c", "comp"), (channel, user, msg, words) -> {
String map = "", name = "", img = "", mode = "";
int index = 1;
String[] param = String.join(" ", words).split("\\|");
try {
if(param.length > 1) index = Integer.parseInt(param[1].trim());
} catch(NumberFormatException e) {
channel.sendMessage("Invalid index specified. Defaulting to the most recent scorecard...").queue();
}
for(Message card : channel.getHistory().retrievePast(100).complete()) {
List<MessageEmbed> embeds = card.getEmbeds();
if(embeds.size() > 0) {
MessageEmbed embed = embeds.get(0);
if(embed.getAuthor() == null) continue;
String author = embed.getAuthor().getName();
if(card.getAuthor().getId().equals(DiscordBot.BotID)) {
if(author.contains("most recent osu!")) {
map = embed.getUrl();
name = embed.getTitle().lastIndexOf("+") == -1 ? embed.getTitle().substring(0, embed.getTitle().lastIndexOf("(")) : embed.getTitle().substring(0, embed.getTitle().lastIndexOf("+"));
img = embed.getThumbnail().getUrl();
mode = author.split("most recent osu!")[1].split(" play")[0];
} else if(author.startsWith("PP information for")) {
map = embed.getAuthor().getUrl();
name = author.split("PP information for ")[1] + " [" +embed.getFields().get(3).getValue() + "]";
img = embed.getAuthor().getIconUrl();
mode = embed.getDescription().replace("Mode: osu!", "");
}
} else if(card.getAuthor().getId().equals("289066747443675143")) { //owobot support
if(author.startsWith("New") && author.contains("in osu!")) {
String markdown = embed.getDescription().substring(7, embed.getDescription().indexOf("\n")).trim();
map = markdown.substring(markdown.lastIndexOf("(") + 1, markdown.length() - 2);
name = markdown.substring(0, markdown.indexOf("__**]"));
img = embed.getThumbnail().getUrl();
mode = author.substring(author.lastIndexOf("in osu! "), author.length()).trim();
} else if(card.getContentDisplay().contains("Most Recent ")) {
map = embed.getAuthor().getUrl();
name = author.substring(0, author.lastIndexOf("+"));
img = embed.getThumbnail().getUrl();
mode = card.getContentDisplay().contains("Mania") ? "mania" : card.getContentDisplay().split("Most Recent ")[1].split(" ")[0]; //ye fucking blame owobot for being so inconsistent
} else if(card.getContentDisplay().contains("map(s).")) {
map = embed.getAuthor().getUrl();
map = map.substring(0, map.length() - (map.endsWith("/") ? 1 : 0)); //fucking hell the url actually changes according to how ppl trigger it
name = author.split(" – ")[1];
String[] split = embed.getFields().get(0).getName().split("__", 3);
name = name.substring(0, name.lastIndexOf(" by ")) + " [" + split[1] + "]";
img = "https://b.ppy.sh/thumb/" + embed.getDescription().split("\\[map\\]\\(https:\\/\\/osu\\.ppy\\.sh\\/d\\/", 2)[1].split("\\)", 2)[0] + ".jpg";
mode = split[2].isEmpty() ? "std" : split[2].substring(2, split[2].lastIndexOf("]") + 1);
}
}
if(index > 1) {
index--;
map = "";
}
if(!map.isEmpty()) break;
}
}
int modeId = 0;
if(mode.equalsIgnoreCase("taiko")) modeId = 1;
else if(mode.equalsIgnoreCase("catch") || mode.equals("ctb")) modeId = 2;
else if(mode.equalsIgnoreCase("mania")) modeId = 3;
if(map.isEmpty()) return new CommandResult(CommandResultType.FAILURE, "There are no recent map/score cards to compare with in the past 100 messages!");
String uid = param[0].isEmpty() ? getPlayer(user) : param[0].replaceAll(" ", "%20");
try {
URL url = new URL("https://osu.ppy.sh/api/get_scores?k=" + osuAPI + "&b=" + map.substring(map.lastIndexOf("/") + 1) + "&u=" + uid + "&m=" + modeId);
JSONArray arr = new JSONArray(new JSONTokener(url.openStream()));
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Top plays for " + arr.getJSONObject(0).getString("username") + " on " + name);
eb.setColor(new Color(237, 154, 70));
eb.setThumbnail(img);
for(int i = 0; i < arr.length(); i++) {
JSONObject score = arr.getJSONObject(i);
String mods = Koohii.mods_str(score.getInt("enabled_mods"));
Accuracy acc = new Accuracy(score.getInt("count300"), score.getInt("count100"), score.getInt("count50"), score.getInt("countmiss"));
String info = "**`" + (mods.isEmpty() ? "No mods" : mods) + "` Score:\n"
+ score.getString("rank").replaceAll("H", "+").replaceAll("X", "SS") + " " + (score.isNull("pp") ? "No " : score.getDouble("pp")) + "pp** | Score **" + score.getInt("score") + "**\n"
+ df.format(acc.value() * 100) + "% " + (score.getInt("perfect") == 1 ? "FC " + score.getInt("maxcombo") + "x" : score.getInt("maxcombo") + "x") + " (" + acc.n300 + "/" + acc.n100 + "/" + acc.n50 + "/" + acc.nmisses + ")\n"
+ "Played on " + score.getString("date") + (score.getInt("replay_available") == 1 ? " (Replay available)" : "");
eb.appendDescription(info + "\n\n");
}
channel.sendMessage(eb.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
} catch (IOException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
} catch (JSONException e) {
e.printStackTrace();
return new CommandResult(CommandResultType.INVALIDARGS, "Unknown player `" + uid + "` or the player has no scores on this map.");
}
}, "[username] [| index]", null, "Compare your score with the most recent map/score card in the channel!", Arrays.asList(" * Specify the index to skip to the nth recent score card in the channel."));
}
private static double weightForPP(double pp, String ibid, JSONArray userBest) {
//String[] dataset = test.split(" ");
//List<Double> datas = Arrays.asList(dataset).stream().map(s -> Double.parseDouble(s)).collect(Collectors.toList());
double net = 0, change = -1;
List<Double> pps = new ArrayList<>();
for(int i = 0; i < userBest.length(); i++) { //something like a treemap might work more elegantly, but i need to add pp to the list anyways so its fine i guess
//for(int i = 0; i < datas.size(); i++) {
JSONObject obj = userBest.getJSONObject(i);
double v = obj.getDouble("pp");
//double v = datas.get(i);
//System.out.println("raw" + v);
String bid = obj.getString("beatmap_id");
if(bid.equals(ibid)) {
if(v >= pp) {
return -1;
} else {
change = v;
}
}
if(v <= pp && !pps.contains(pp)) {
pps.add(pp);
}
pps.add(v);
}
if(pps.indexOf(pp) == -1) {
return 0;
}
//System.out.println(pps.indexOf(pp));
if(change == -1) {
double last = pps.size() > 100 ? pps.remove(100) * (Math.pow(0.95, 99)): 0;
for(int i = pps.indexOf(pp); i < pps.size(); i++) {
double c = pps.get(i);
double w = c*(Math.pow(0.95, i));
//System.out.println("pp" + w);
if(i == pps.indexOf(pp)) {
net += w;
} else {
net += c*(Math.pow(0.95, i - 1)) * (0.95 - 1);
}
//System.out.println("t1" + net);
}
//System.out.println("last" + last);
net -= last; //because the last one is completely not counted, not just shifted to a lower value
} else {
int old = pps.indexOf(change) - 1;
pps.remove(change);
for(int i = pps.indexOf(pp); i <= old; i++) {
double c = pps.get(i);
double w = c*(Math.pow(0.95, i));
if(c == pp) {
net += w - change*(Math.pow(0.95, old));
} else {
net += w * (0.95 - 1);
}
}
}
return net;
}
private static JSONArray getUserBest(String id, int mode) {
boolean noturl = false;
try {
new URL(id);
id = id.substring(id.lastIndexOf("/") + 1);
} catch (MalformedURLException e) {
noturl = true;
}
JSONTokener result = null;
InputStream stream = null;
URL url = null;
try {
String addparam = "";
if (noturl) {
addparam = "&type=string";
}
//System.out.println("https://osu.ppy.sh/api/get_user_best?k=" + osuAPI + "&m=" + mode + "&u=" + URLEncoder.encode(id, "UTF-8") + addparam + "&limit=100");
url = new URL(
"https://osu.ppy.sh/api/get_user_best?k=" + osuAPI + "&m=" + mode + "&u=" + URLEncoder.encode(id, "UTF-8") + addparam + "&limit=100");
stream = url.openStream();
if (stream.available() < 4) {
throw new IllegalArgumentException("Unknown player `" + id + "`.");
}
} catch (IOException ex) {
ex.printStackTrace();
}
result = new JSONTokener(stream);
return new JSONArray(result);
}
private static PPv2 constructPP(double aim_stars, double speed_stars, Map b, int mods) {
PPv2Parameters p = new PPv2Parameters();
p.aim_stars = aim_stars;
p.speed_stars = speed_stars;
p.beatmap = b;
p.nobjects = b.objects.size();
p.mods = mods;
return new PPv2(p);
}
private static String getPlayer(User user) { //say im getting from presence/name?
try (Connection con = DiscordBot.db.getConnection()){
ResultSet rs = con.createStatement().executeQuery("SELECT game FROM users WHERE id = " + user.getId() + ";");
String player = rs.next() ? player = rs.getString(1).substring(0, rs.getString(1).indexOf("||")) : user.getName();
rs.close();
return player;
} catch (SQLException e) {
return user.getName();
}
}
private static int getMode(List<String> params) {
int modeId = 0; String mode = "";
params.replaceAll(String::toLowerCase);
if(params.contains("-t")) {
mode = "-t";
modeId = 1;
}
if(params.contains("-c")) {
mode = "-c";
modeId = 2;
}
if(params.contains("-m")) {
mode = "-m";
modeId = 3;
}
params.removeAll(Collections.singleton(mode)); //pass by ref so it removes from the list
return modeId;
}
private static InputStream getMap(String origURL) throws IOException {
String id = origURL.substring(origURL.lastIndexOf("/") + 1);
if (origURL.contains("/beatmapsets/")) {
if (!origURL.contains("#")) throw new IllegalArgumentException("Please specify a difficulty, instead of giving a set URL.");
} else {
if (origURL.contains("/s/")) throw new IllegalArgumentException("Please specify a difficulty, instead of giving a set URL.");
id = id.split("&")[0]; //remove things like &m=0, which is no need
}
URLConnection mapURL;
try {
mapURL = new URL("https://osu.ppy.sh/osu/" + id).openConnection();
mapURL.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0)");
} catch (IOException e) {
//e.printStackTrace();
throw new IllegalArgumentException("Invalid beatmap URL.");
}
try {
return mapURL.getInputStream();
} catch (IOException e) {
if(e.getMessage().contains("503")) { //most likely cloudflare, unless they change the request method which is very unlikely
mapURL = new URL("https://bloodcat.com/osu/b/" + id).openConnection();
mapURL.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0)");
return mapURL.getInputStream(); //not catching this, if even this doesnt work just throw something went wrong.
} else {
throw e;
}
}
}
}

File Metadata

Mime Type
text/x-java
Expires
Fri, Aug 1, 9:28 AM (11 h, 52 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d6/2c/ee3a1c8cea17c8dba9bbfbd3ffda

Event Timeline