diff --git a/src/me/despawningbone/discordbot/command/anime/Waifu.java b/src/me/despawningbone/discordbot/command/anime/Waifu.java index c24d46d..0718c7c 100644 --- a/src/me/despawningbone/discordbot/command/anime/Waifu.java +++ b/src/me/despawningbone/discordbot/command/anime/Waifu.java @@ -1,192 +1,184 @@ package me.despawningbone.discordbot.command.anime; import java.awt.Color; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.exception.ExceptionUtils; import org.json.JSONArray; import org.json.JSONObject; import org.json.JSONTokener; import org.jsoup.Connection.Response; import org.jsoup.Jsoup; import me.despawningbone.discordbot.command.Command; import me.despawningbone.discordbot.command.CommandResult; import me.despawningbone.discordbot.command.CommandResult.CommandResultType; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.User; public class Waifu extends Command { public Waifu() { - this.desc = "Find information about your waifu!"; //, or leave it blank for a random one!"; + this.desc = "Find information about your waifu/husbando!"; //, or leave it blank for a random one!"; this.usage = " [| index]"; this.examples = Arrays.asList("neptune", "ryuZU"); + this.alias = Arrays.asList("husbando"); } @Override public CommandResult execute(TextChannel channel, User author, Message msg, String[] args) { channel.sendTyping().queue(); String[] stripped = String.join(" ", args).split("\\|"); - /* , url = ""; - if (args.length < 1) { - url = "https://mywaifulist.moe/random/"; - } else { - url = "https://mywaifulist.moe/waifu/" + stripped.replaceAll(" ", "-"); - } - JSONObject main = getWaifuJSON(url); - if (main == null) { - String search; - int index; - if (stripped.contains("|")) { - String[] split = stripped.split(" \\|"); - search = split[0]; - try { - index = Integer.parseInt(split[1].trim()) - 1; - } catch (NumberFormatException | ArrayIndexOutOfBoundsException e) { - channel.sendMessage("Invalid index inputted. Defaulting to first result.").queue(); - channel.sendTyping().queue(); - index = 0; - } - } else { - search = stripped; - index = 0; - } - try { - url = GoogleSearch - .search(URLEncoder.encode("site:https://mywaifulist.moe/waifu " + search, "UTF-8"), 20) - .get(index).getValue(); - // System.out.println(url); //debug - } catch (IOException | IndexOutOfBoundsException e) { - if (e instanceof IndexOutOfBoundsException) { - return new CommandResult(CommandResultType.NORESULT); - } else { - return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e)); - } - } - main = getWaifuJSON(url); - }*/ if(args.length < 1) return new CommandResult(CommandResultType.FAILURE, "Please enter something to search for!"); + JSONObject main; try { + //get required cookies Response con = Jsoup.connect("https://mywaifulist.moe/dash/").execute(); String cookie = String.join(";", con.header("Set-Cookie").split("; expires.*?,.*?,")).split("; expires")[0]; String csrf = con.parse().selectFirst("meta[name=\"csrf-token\"]").attr("content"); - String res = Jsoup.connect("https://mywaifulist.moe/api/waifu/search") + String res = Jsoup.connect("https://mywaifulist.moe/api/waifu/advancedsearch") //has problems with short names - names are sorted by ascending and only first 25 returns, so my own sorting cant work well with many pages of results .userAgent("Mozilla/4.0") .header("Cookie", cookie) .header("X-CSRF-Token", csrf) .header("X-Requested-With", "XMLHttpRequest") .header("Accept", "application/json, text/plain, */*") .header("Content-Type", "application/json;charset=utf-8") .followRedirects(true).ignoreContentType(true) - .requestBody("{\"query\":\"" + stripped[0].trim() + "\"}").post().text(); + .requestBody("{\"name\":\"" + stripped[0].trim() + + "\",\"starts_with\":\"\",\"tags\":[],\"nameSort\":\"ASC\",\"height\":\"\",\"weight\":\"\"," + + "\"bust\":\"\",\"hip\":\"\",\"waist\":\"\",\"blood_type\":\"\"}").post().text(); + + //check index int index = 0; - JSONArray jarr = new JSONArray(new JSONTokener(res)); + JSONArray jarr = new JSONObject(new JSONTokener(res)).getJSONArray("data"); try { //System.out.println(arr.length()); if(stripped.length > 1) index = Integer.parseInt(stripped[1].trim()) - 1; - if(jarr.length() <= index && index != 0) throw new NumberFormatException(); + if(jarr.length() <= index && index != 0) throw new NumberFormatException(); //if set index is out of bounds } catch (NumberFormatException e) { channel.sendMessage("Invalid index inputted. Defaulting to first result.").queue(); channel.sendTyping().queue(); index = 0; } + + //sort search results for fetching with specified index + Pattern wholeWord = Pattern.compile("(?i).*\\b" + stripped[0].trim() + "\\b.*"); List arr = new ArrayList<>(); for(Object obj : jarr) arr.add((JSONObject) obj); - arr = arr.stream().filter(o -> !o.isNull("type") && (o.getString("type").equalsIgnoreCase("waifu") || o.getString("type").equalsIgnoreCase("husbando"))).sorted((a, b) -> Integer.compare((b.has("likes") ? b.getInt("likes") : 0) + (b.has("trash") ? b.getInt("trash") : 0), (a.has("likes") ? a.getInt("likes") : 0) + (a.has("trash") ? a.getInt("trash") : 0))).collect(Collectors.toList()); //sort by popularity since the search result sucks ass + arr = arr.stream().filter(o -> !o.isNull("type") && (o.getString("type").equalsIgnoreCase("waifu") || o.getString("type").equalsIgnoreCase("husbando"))) //filter only characters + .sorted((a, b) -> Integer.compare( //combine likes and trash to get popularity - sort by popularity since the search result sucks ass + (b.has("likes") ? b.getInt("likes") : 0) + (b.has("trash") ? b.getInt("trash") : 0), + (a.has("likes") ? a.getInt("likes") : 0) + (a.has("trash") ? a.getInt("trash") : 0))) + .sorted((a, b) -> wholeWord.matcher(a.getString("name")).matches() && !wholeWord.matcher(b.getString("name")).matches() ? -1 : 0) //move whole word matches up only if last one was not matched + .collect(Collectors.toList()); + + //fetch main = new JSONObject(new JSONTokener(Jsoup.connect("https://mywaifulist.moe/api/waifu/" + arr.get(index).getInt("id")) .userAgent("Mozilla/4.0") .header("Cookie", cookie) .header("X-CSRF-Token", csrf) .header("X-Requested-With", "XMLHttpRequest") .followRedirects(true).ignoreContentType(true).get().text())).getJSONObject("data"); - System.out.println(main); } catch (IndexOutOfBoundsException e) { //e.printStackTrace(); return new CommandResult(CommandResultType.NORESULT); } catch (IOException e) { + if(e.toString().contains("Status=422")) return new CommandResult(CommandResultType.NORESULT); //search scope too broad(?) + return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e)); } + EmbedBuilder em = new EmbedBuilder(); em.setColor(new Color(44, 62, 80)); em.setTitle((main.getBoolean("husbando") ? "Husbando" : "Waifu") + " info of " + main.getString("name"), "https://mywaifulist.moe/waifu/" + main.getString("slug")); + //series JSONObject series = main.getJSONObject("series"); em.setAuthor("Series: " + series.getString("name"), "https://mywaifulist.moe/series/" + series.getString("slug")); - if(!main.getJSONObject("series").getString("display_picture").isEmpty()) em.setThumbnail(main.getJSONObject("series").getString("display_picture")); + if(!main.getJSONObject("series").isNull("display_picture") && !main.getJSONObject("series").getString("display_picture").isEmpty()) + em.setThumbnail(main.getJSONObject("series").getString("display_picture")); try { em.setDescription(main.getString("description").substring(0, Math.min(main.getString("description").length(), 2040)) + (main.getString("description").length() > 2040 ? "..." : "")); } catch (IllegalArgumentException e) { return new CommandResult(CommandResultType.TOOLONG); } em.setImage(main.getString("display_picture").replaceAll("\\\\", "")); + //series appearance + series description ArrayList appearances = new ArrayList<>(); int totalName = 0; for(Object obj : main.getJSONArray("appearances")) { JSONObject jobj = ((JSONObject) obj); appearances.add(jobj); - totalName += jobj.getString("name").length() + jobj.getString("slug").length() + 43; + totalName += jobj.getString("name").length() + jobj.getString("slug").length() + 43; //magic number for giving enough leeway } - final int avg = (1024 - totalName) / appearances.size(); - System.out.println(appearances.stream().map(jobj -> "[" + jobj.getString("name") + "](https://mywaifulist.moe/series/" + jobj.getString("slug") + " \"" + jobj.getString("description").substring(0, Math.min(jobj.getString("description").length(), avg)) + (jobj.getString("description").length() > avg ? "..." : "").replaceAll("\"", "”") + "\")").collect(Collectors.joining(", ")).length()); - em.addField("Appearances", appearances.stream().map(jobj -> "[" + jobj.getString("name") + "](https://mywaifulist.moe/series/" + jobj.getString("slug") + " \"" + jobj.getString("description").substring(0, Math.min(jobj.getString("description").length(), avg)) + (jobj.getString("description").length() > avg ? "..." : "").replaceAll("\"", "”") + "\")").collect(Collectors.joining(", ")), false); + final int avg = (1024 - totalName) / appearances.size(); //get max description average length + em.addField("Appearances", appearances.stream().map(jobj -> "[" + jobj.getString("name") + "](https://mywaifulist.moe/series/" + jobj.getString("slug") + + (!jobj.isNull("description") ? (" \"" + jobj.getString("description").substring(0, Math.min(jobj.getString("description").length(), avg)).replaceAll("\"", "”")) //trim desc to max length, replacing double quotes since it will interfere with markdown + + (jobj.getString("description").length() > avg ? "..." : "") + "\")" : ")")) //append ... if its not finished (only if desc is non null will desc be printed) + .collect(Collectors.joining(", ")), false); - if(!main.getString("original_name").isEmpty()) em.addField("Also known as", main.getString("original_name") + (main.isNull("romaji_name") ? "" : ", " + main.getString("romaji_name")), false); + //aliases + if(!main.isNull("original_name") && !main.getString("original_name").isEmpty()) + em.addField("Also known as", main.getString("original_name") + + (main.isNull("romaji_name") || main.getString("romaji_name").isEmpty() ? "" + : ", " + main.getString("romaji_name")), false); em.addBlankField(false); - if (!main.isNull("origin")) { + //optionally existing info + if (!main.isNull("origin")) em.addField("Origin", main.getString("origin"), true); - } - if (!main.isNull("height")) { + + if (!main.isNull("height")) em.addField("Height", String.valueOf(main.getDouble("height")), true); - } - if (!main.isNull("weight")) { + + if (!main.isNull("weight")) em.addField("Weight", String.valueOf(main.getDouble("weight")), true); - } - if (!main.isNull("bust")) { + + if (!main.isNull("bust")) em.addField("Bust", String.valueOf(main.getDouble("bust")), true); - } - if (!main.isNull("hip")) { + + if (!main.isNull("hip")) em.addField("Hip", String.valueOf(main.getDouble("hip")), true); - } - if (!main.isNull("waist")) { + + if (!main.isNull("waist")) em.addField("Waist", String.valueOf(main.getDouble("waist")), true); - } - if (!main.isNull("blood_type")) { + + if (!main.isNull("blood_type")) em.addField("Blood type", main.getString("blood_type"), true); - } - if (!main.isNull("birthday_day") && !main.isNull("birthday_month")) { - em.addField("Birthday", - main.getString("birthday_month") + " " + String.valueOf(main.getInt("birthday_day")) - + ( main.isNull("birthday_year") || main.getString("birthday_year").isEmpty() ? "" : ", " + main.getString("birthday_year")), - true); - } + + if (!main.isNull("birthday_day") && main.getInt("birthday_day") != 0 && !main.isNull("birthday_month") && !main.getString("birthday_month").isEmpty()) + em.addField("Birthday", main.getString("birthday_month") + " " + String.valueOf(main.getInt("birthday_day")) + + (main.isNull("birthday_year") || main.getString("birthday_year").isEmpty() ? "" : ", " + main.getString("birthday_year")), true); + + //only add blank on 5 fields for formatting if (em.getFields().size() == 5) { em.addBlankField(true); } + //tags, usually not used anymore and returns empty list String tags = main.getJSONArray("tags").toList().stream().map(o -> o.toString().replaceAll("\\{name=", "").replaceAll(", id=.*\\}", "")).collect(Collectors.joining(", ")); if(!tags.isEmpty()) em.addField("Tags", tags, false); + //popularity stats if(em.getFields().size() > 2) em.addBlankField(false); em.addField("Likes", main.getInt("likes") + " (#" + (main.isNull("like_rank") ? "N/A" : main.getInt("like_rank")) + ")", true); em.addField("Popularity rank", "#" + (main.isNull("popularity_rank") ? "N/A" : main.getInt("popularity_rank")), true); em.addField("Trash", main.getInt("trash") + " (#" + (main.isNull("trash_rank") ? "N/A" : main.getInt("trash_rank")) + ")", true); - em.setFooter("Created by " + main.getJSONObject("creator").getString("name") + /*", Last updated: " - + main.getString("updated_at") +*/ " | MyWaifuList.moe", null); + em.setFooter("Created by " + main.getJSONObject("creator").getString("name") + " | MyWaifuList.moe", null); channel.sendMessage(em.build()).queue(); return new CommandResult(CommandResultType.SUCCESS); } }