diff --git a/src/me/despawningbone/discordbot/command/anime/Sauce.java b/src/me/despawningbone/discordbot/command/anime/Sauce.java index f29106c..3ce28ff 100644 --- a/src/me/despawningbone/discordbot/command/anime/Sauce.java +++ b/src/me/despawningbone/discordbot/command/anime/Sauce.java @@ -1,166 +1,168 @@ package me.despawningbone.discordbot.command.anime; import java.awt.Color; import java.io.IOException; import java.net.MalformedURLException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; 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 Sauce extends Command{ public Sauce() { this.desc = "Get the source of an anime pic!"; - this.usage = ""; + this.usage = "[-d] [imgurl]"; this.alias = Arrays.asList("source", "saucenao", "iqdb"); + this.remarks = Arrays.asList("The URL should be a direct link to an image.", "You can also upload an image as an attachment while calling this command instead of using a url.", + " * Specify the `-d` parameter to do a depth search!", " * It is useful for cropped images and edited images, but makes the search much longer."); } @Override public CommandResult execute(TextChannel channel, User author, Message msg, String[] args) { //allow people to not input url to check sauce of the most recent image like u/2dgt3d? List amend = new ArrayList(Arrays.asList(args)); int temp = amend.indexOf("-d"); if(temp != -1) amend.subList(temp, temp + 1).clear(); String url = null; try { if(amend.size() < 1) { if(msg.getAttachments().size() > 0) url = msg.getAttachments().get(0).getUrl(); else throw new MalformedURLException(); } else { url = amend.get(0).trim(); new URL(url); } } catch(MalformedURLException e) { return new CommandResult(CommandResultType.FAILURE, "Please enter a valid URL!"); } System.out.println(url); try { channel.sendTyping().queueAfter(20, TimeUnit.MILLISECONDS); if(temp != -1) { channel.sendMessage("Performing depth search for the picture... (this can take up to 20 seconds)").queue(); String[] urls = yandexSearch(url); System.out.println(Arrays.asList(urls)); url = urls[2] == null ? urls[0] : urls[1] + ";" + urls[2]; //sync //List collect = Arrays.asList(tempSearchSauce(urls[0]),tempSearchSauce(urls[1])); //async CompletableFuture first = CompletableFuture.supplyAsync(() -> {try {return tempSearchSauce(urls[0]);} catch (IOException e){e.printStackTrace(); return null;}}); CompletableFuture ratio = CompletableFuture.supplyAsync(() -> {try {return tempSearchSauce(urls[1]);} catch (IOException e){e.printStackTrace(); return null;}}); List collect = CompletableFuture.allOf(first, ratio) .thenApply(future -> Arrays.asList(first.join(), ratio.join())) .whenComplete((s, t) -> {if(!(ratio.join() != null && first.join() != null) && t != null) throw new CompletionException(t.getCause());}).get(); channel.sendMessage(collect.stream().filter(e -> e != null).sorted((a, b) -> urls[2] == null ? -1 : Double.compare(Double.parseDouble(b.build().getFooter().getText().replaceAll(".*?([.0-9]*%).*", "$1").replace("%", "")), Double.parseDouble(a.build().getFooter().getText().replaceAll(".*?([.0-9]*%).*", "$1").replace("%", "")))).findFirst().get().build()).queue(); } else { EmbedBuilder eb = tempSearchSauce(url); channel.sendMessage(eb.build()).queue(); } } catch (IOException | InterruptedException | ExecutionException e) { Throwable t = e instanceof ExecutionException ? e.getCause() : e; return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(t)); } catch (NullPointerException | NoSuchElementException e) { if(e instanceof NoSuchElementException) { EmbedBuilder eb = new EmbedBuilder(); String[] urls = url.split(";"); eb.setThumbnail(urls[0]); eb.setTitle("Possibly related image", urls[0]); eb.setDescription("Maybe [this](" + urls[urls.length > 1 ? 1 : 0] + ") will help in your search for the delicious marinara?"); channel.sendMessage(eb.build()).queueAfter(20, TimeUnit.MILLISECONDS); } return new CommandResult(CommandResultType.NORESULT); } return new CommandResult(CommandResultType.SUCCESS); } private String[] yandexSearch(String url) throws IOException { Element yandex = Jsoup.connect("https://yandex.com/images/search?url=" + URLEncoder.encode(url, "UTF-8") + "&rpt=imageview").get().body(); String[] size = new String[]{yandex.selectFirst(".CbirPreview-Placeholder").attr("width"), yandex.selectFirst(".CbirPreview-Placeholder").attr("height")}; //double ratio = Double.parseDouble(size[0]) / Double.parseDouble(size[1]) >= 1 ? 16.0/9 : 9/16.0; double ratio = Double.parseDouble(size[0]) / Double.parseDouble(size[1]); System.out.println(yandex.select(".other-sites__thumb")); System.out.println(size[0] + " " + size[1] + " " + ratio); String other, site = null, similar = URLDecoder.decode(yandex.select(".cbir-similar__thumb .cbir-similar__image").get(yandex.select(".cbir-other-sizes__item").size() > 0 ? 1 : 0).parent().attr("href").split("img_url=")[1].split("&")[0], "UTF-8"); if(yandex.select(".cbir-other-sizes__list").size() > 0) { //TODO merge the sort algorithms? /*other = yandex.select(".cbir-other-sizes__list").first().select("a").stream().sorted((a, b) -> { //selects largest possible section of the other sizes String[] aA = a.select(".cbir-other-sizes__resolution").first().text().split("×"), bA = b.select(".cbir-other-sizes__resolution").first().text().split("×"); return Double.compare(Math.abs(Double.parseDouble(aA[0]) / Double.parseDouble(aA[1]) - ratio), Math.abs(Double.parseDouble(bA[0]) / Double.parseDouble(bA[1]) - ratio)); }).findFirst().get().attr("href");*/ other = yandex.select(".cbir-other-sizes__list a").first().attr("href"); //sorting with ratio seems to perform bad for most crops so dont sort anymore } else { yandex.select(".other-sites__item").stream().limit(12).sorted((a, b) -> { String[] aA = a.select(".other-sites__meta").first().text().split("×"), bA = b.select(".other-sites__meta").first().text().split("×"); System.out.println(Math.abs(Double.parseDouble(aA[0]) / Double.parseDouble(aA[1]) - ratio) + ", " + Math.abs(Double.parseDouble(bA[0]) / Double.parseDouble(bA[1]) - ratio)); return Double.compare(Math.abs(Double.parseDouble(aA[0]) / Double.parseDouble(aA[1]) - ratio), Math.abs(Double.parseDouble(bA[0]) / Double.parseDouble(bA[1]) - ratio));}).forEach(s -> System.out.println(s)); Element eSite = yandex.select(".other-sites__item").stream().limit(12).sorted((a, b) -> { String[] aA = a.select(".other-sites__meta").first().text().split("×"), bA = b.select(".other-sites__meta").first().text().split("×"); return Double.compare(Math.abs(Double.parseDouble(aA[0]) / Double.parseDouble(aA[1]) - ratio), Math.abs(Double.parseDouble(bA[0]) / Double.parseDouble(bA[1]) - ratio));}).findFirst().get(); other = eSite.select(".other-sites__preview-link").first().attr("href"); site = Jsoup.connect(eSite.select(".other-sites__snippet-site-link").first().attr("href")).get().html().split(";URL='")[1].split("'")[0]; } return new String[]{similar, other, site}; //gets second image coz usually the first one is just a sharper identical image, then get the largest image that has the image inside or select an image closest to 16:9 for anime checking //closest to given ratio now, anime checking usually cant be done with depth search anyways since not every scene is screen capped online } private EmbedBuilder tempSearchSauce(String url) throws IOException { EmbedBuilder eb = new EmbedBuilder(); eb.setColor(new Color(29, 29, 29)); try { //search iqdb first for the tags; results usually more organized and formatted Element iqdb = Jsoup.connect("https://iqdb.org/?url=" + url + "&service[]=1&service[]=2&service[]=3&service[]=4&service[]=5&service[]=11&service[]=13").get().body(); //services excluding eshuushuu since it has no tags and will fallback to saucenao anyways if(iqdb.select(".err").size() > 0 && iqdb.select(".err").html().contains("HTTP")) throw new IOException(iqdb.selectFirst(".err").ownText().split("\\.")[0]); Elements tb = iqdb.select("th:contains(Best match)").get(0).parent().siblingElements(); Element img = tb.get(0).selectFirst("img"); eb.setThumbnail("https://iqdb.org/" + img.attr("src")); eb.setTitle("Source: " + tb.get(1).selectFirst("td").ownText(), (img.parent().attr("href").contains("http") ? "" : "https:") + img.parent().attr("href")); String tags = img.attr("alt").split("Tags:")[1]; eb.setDescription(tags.contains(",") ? tags.replaceAll(",", "\n") : tags.replaceAll(" ", "\n").replaceAll("\\_", " ")); eb.setFooter(tb.get(2).select("td").eachText().get(0) + " | " + tb.get(3).select("td").text(), null); } catch (IndexOutOfBoundsException | SocketTimeoutException e) { //fallback to saucenao, usually pixiv source instead of image boards try { Element saucenao = Jsoup.connect("https://saucenao.com/search.php?url=" + url).get().body(); Element result = saucenao.selectFirst(".resulttable"); if(result == null) return null; if(result.parent().attr("class").equals("result hidden")) return null; eb.setThumbnail((result.selectFirst("img").attr("src").contains("http") ? "" : "https:") + result.selectFirst("img").attr("src").replaceAll(" ", "%20")); try { //normal pixiv/deviantart handling Element source = result.selectFirst("strong:contains(ID:)").nextElementSibling(); eb.setAuthor(result.select(".resulttitle strong").text()); eb.setTitle("Source: " + source.previousElementSibling().text().replaceAll("ID:", "#") + source.text(), source.attr("href")); Element member = result.select(".linkify").get(2); eb.setDescription("Author: [" + member.text() + "](" + member.attr("href") + ")"); } catch (NullPointerException e1) { //weird saucenao card formatting (eg episode info) result.select("br").after("\\n"); String[] title = result.selectFirst(".resulttitle") == null ? new String[]{"No title"} : result.selectFirst(".resulttitle").wholeText().replaceAll("\\\\n", "\n").split("\n", 2); //there can be no titles, like 4chan sources eb.setTitle(title[0], result.selectFirst(".resultmiscinfo a") == null ? null : result.selectFirst(".resultmiscinfo a").attr("href")); if(title.length > 1) eb.appendDescription(title[1] + "\n"); eb.appendDescription(result.selectFirst(".resultcontentcolumn").wholeText().replaceAll("\\\\n", "\n")); } eb.setFooter(result.select(".resultsimilarityinfo").text() + " Similarity", null); } catch (IndexOutOfBoundsException e1) { return null; } } return eb; } } diff --git a/src/me/despawningbone/discordbot/command/anime/Waifu.java b/src/me/despawningbone/discordbot/command/anime/Waifu.java index 9a4c79f..61366cd 100644 --- a/src/me/despawningbone/discordbot/command/anime/Waifu.java +++ b/src/me/despawningbone/discordbot/command/anime/Waifu.java @@ -1,190 +1,190 @@ 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.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.usage = " [| index]"; this.examples = Arrays.asList("neptune", "ryuZU"); } @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 { 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") .userAgent("Mozilla/4.0") .header("Cookie", cookie) .header("X-CSRF-Token", csrf) .header("X-Requested-With", "XMLHttpRequest") .followRedirects(true) .requestBody("{\"query\":\"" + stripped[0].trim() + "\"}").post().text(); int index = 0; JSONArray jarr = new JSONArray(new JSONTokener(res)); 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(); } catch (NumberFormatException e) { channel.sendMessage("Invalid index inputted. Defaulting to first result.").queue(); channel.sendTyping().queue(); index = 0; } List arr = new ArrayList<>(); for(Object obj : jarr) arr.add((JSONObject) obj); arr = arr.stream().filter(o -> !o.getString("type").equalsIgnoreCase("series")).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 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) { 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")); 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")); 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("\\\\", "")); 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; } 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); if(!main.getString("original_name").isEmpty()) em.addField("Also known as", main.getString("original_name") + (main.isNull("romaji_name") ? "" : ", " + main.getString("romaji_name")), false); em.addBlankField(false); if (!main.getString("origin").equals("")) { em.addField("Origin", main.getString("origin"), true); } if (main.getDouble("height") != 0.00) { em.addField("Height", String.valueOf(main.getDouble("height")), true); } if (main.getDouble("weight") != 0.00) { em.addField("Weight", String.valueOf(main.getDouble("weight")), true); } if (main.getDouble("bust") != 0.00) { em.addField("Bust", String.valueOf(main.getDouble("bust")), true); } if (main.getDouble("hip") != 0.00) { em.addField("Hip", String.valueOf(main.getDouble("hip")), true); } if (main.getDouble("waist") != 0.00) { em.addField("Waist", String.valueOf(main.getDouble("waist")), true); } if (!main.getString("blood_type").isEmpty()) { em.addField("Blood type", main.getString("blood_type"), true); } if (!main.isNull("birthday_day") && !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); } if (em.getFields().size() == 5) { em.addBlankField(true); } 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); if(em.getFields().size() > 2) em.addBlankField(false); - em.addField("Likes", main.getInt("likes") + " (#" + main.getInt("like_rank") + ")", true); - em.addField("Popularity rank", "#" + main.getInt("popularity_rank"), true); - em.addField("Trash", main.getInt("trash") + " (#" + main.getInt("trash_rank") + ")", true); + 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); channel.sendMessage(em.build()).queue(); return new CommandResult(CommandResultType.SUCCESS); } } diff --git a/src/me/despawningbone/discordbot/command/info/Calculator.java b/src/me/despawningbone/discordbot/command/info/Calculator.java index 1ab2247..e685355 100644 --- a/src/me/despawningbone/discordbot/command/info/Calculator.java +++ b/src/me/despawningbone/discordbot/command/info/Calculator.java @@ -1,227 +1,230 @@ package me.despawningbone.discordbot.command.info; import java.awt.Color; import java.awt.Graphics; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; -import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EmptyStackException; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import javax.imageio.ImageIO; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.math3.special.Gamma; import org.json.JSONObject; import org.json.JSONTokener; import com.neovisionaries.ws.client.WebSocket; import com.neovisionaries.ws.client.WebSocketAdapter; import com.neovisionaries.ws.client.WebSocketException; import com.neovisionaries.ws.client.WebSocketFactory; import me.despawningbone.discordbot.command.Command; import me.despawningbone.discordbot.command.CommandResult; import me.despawningbone.discordbot.command.CommandResult.CommandResultType; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.User; import net.objecthunter.exp4j.Expression; import net.objecthunter.exp4j.ExpressionBuilder; import net.objecthunter.exp4j.function.Function; import net.objecthunter.exp4j.operator.Operator; public class Calculator extends Command { public Calculator() { this.alias = Arrays.asList("calc"); this.desc = "Do some calculations!"; this.usage = "[-w] "; this.remarks = Arrays.asList("This calculator is using exp4j.", "You can get the built in operators and functions at:", "http://projects.congrace.de/exp4j/", "`!` - factorials", "`logx(base, num)` - logarithm (base x)", "are supported too.", "\nYou can also query [Wolfram Alpha](https://www.wolframalpha.com/) using the `-w` switch."); this.examples = Arrays.asList("3*4-2", "4 * (sin(3 - 5)) + 5!", "log(e) + logx(10, 100)", " -w laurent series log(x) about x=0"); } - Function logb = new Function("logx", 2) { + static final double planckConstant = 6.62607004 * Math.pow(10, -34), eulerMascheroni = 0.57721566490153286060651209008240243104215933593992; + + static final Function logb = new Function("logx", 2) { @Override public double apply(double... args) { return Math.log(args[1]) / Math.log(args[0]); } }; - Function digamma = new Function("digamma", 1) { + static final Function digamma = new Function("digamma", 1) { @Override public double apply(double... args) { return Gamma.digamma(args[0]); } }; - Operator factorial = new Operator("!", 2, true, Operator.PRECEDENCE_POWER + 1) { + static final Operator factorial = new Operator("!", 2, true, Operator.PRECEDENCE_POWER + 1) { @Override public double apply(double... args) { final long arg = (long) args[0]; if ((double) arg != args[0]) { throw new IllegalArgumentException("Operand for factorial has to be an integer"); } if (arg < 0) { throw new IllegalArgumentException("The operand of the factorial can not " + "be " + "less than zero"); } double result = 1; for (int i = 1; i <= arg; i++) { result *= i; } return result; } }; @Override public CommandResult execute(TextChannel channel, User author, Message msg, String[] args) { List params = new ArrayList<>(Arrays.asList(args)); if(params.removeAll(Collections.singleton("-w"))) { return queryWolfram(String.join(" ", params), channel); } else { if (args.length < 1) { return new CommandResult(CommandResultType.INVALIDARGS, "Please enter an operation."); } - String splitted = String.join(" ", args); + String splitted = String.join(" ", args); + + //easter eggs if (msg.getContentDisplay().toLowerCase().contains("the meaning of life, the universe, and everything")) { // easter egg channel.sendMessage("The answer is: `42`").queue(); return new CommandResult(CommandResultType.SUCCESS, "Executed easter egg"); } if (splitted.equals("9+10") || splitted.equals("9 + 10")) { // easter egg channel.sendMessage("The answer is: `21`\n\n *i am smart ;)*").queue(); return new CommandResult(CommandResultType.SUCCESS, "Executed easter egg"); } if (splitted.equals("666") || splitted.equals("333")) { // easter egg channel.sendMessage("No you don't").queue(); return new CommandResult(CommandResultType.SUCCESS, "Executed easter egg"); - } else { - String operation = String.join("", args); - if (operation.contains("!")) { - int index = operation.indexOf("!"); - while (index >= 0) { // indexOf returns -1 if no match found - operation = new StringBuilder(operation).insert(index + 1, "(1)").toString(); - index = operation.indexOf("!", index + 1); - } - } - DecimalFormat format = new DecimalFormat(); - format.setDecimalSeparatorAlwaysShown(false); - // System.out.println(operation); - Expression e = null; - String ans = null; - double planckConstant = 6.62607004 * Math.pow(10, -34); - double eulerMascheroni = 0.57721566490153286060651209008240243104215933593992; - try { - e = new ExpressionBuilder(operation).variable("h").variable("γ").function(digamma).function(logb).operator(factorial).build() - .setVariable("h", planckConstant).setVariable("γ", eulerMascheroni); - ans = Double.toString(e.evaluate()); - // String ans = format.format(e.evaluate()); - } catch (EmptyStackException e1) { - return new CommandResult(CommandResultType.INVALIDARGS, "You have imbalanced parentheses."); - } catch (ArithmeticException e1) { - return new CommandResult(CommandResultType.INVALIDARGS, "You cannot divide by zero."); - } catch (IllegalArgumentException e1) { - return new CommandResult(CommandResultType.FAILURE, "An error has occured: " + e1.getMessage()); - } - if (ans.equals("NaN")) { - ans = "Undefined"; + } + + //patch for factorial formatting + String operation = String.join("", args); + if (operation.contains("!")) { + int index = operation.indexOf("!"); + while (index >= 0) { // indexOf returns -1 if no match found + operation = new StringBuilder(operation).insert(index + 1, "(1)").toString(); + index = operation.indexOf("!", index + 1); } - channel.sendMessage("The answer is: `" + ans + "`").queue(); - return new CommandResult(CommandResultType.SUCCESS); - } + } + + // DecimalFormat format = new DecimalFormat(); + // format.setDecimalSeparatorAlwaysShown(false); + // System.out.println(operation); + String ans = null; + try { + Expression e = new ExpressionBuilder(operation).variable("h").variable("γ").function(digamma).function(logb).operator(factorial).build() + .setVariable("h", planckConstant).setVariable("γ", eulerMascheroni); + ans = Double.toString(e.evaluate()); + // String ans = format.format(e.evaluate()); + } catch (EmptyStackException e1) { + return new CommandResult(CommandResultType.INVALIDARGS, "You have imbalanced parentheses."); + } catch (ArithmeticException e1) { + return new CommandResult(CommandResultType.INVALIDARGS, "You cannot divide by zero."); + } catch (IllegalArgumentException e1) { + return new CommandResult(CommandResultType.FAILURE, "An error has occured: " + e1.getMessage()); + } + if (ans.equals("NaN")) { + ans = "Undefined"; + } + channel.sendMessage("The answer is: `" + ans + "`").queue(); + return new CommandResult(CommandResultType.SUCCESS); + } } public CommandResult queryWolfram(String operation, TextChannel channel) { channel.sendTyping().queue(); try { CompletableFuture result = new CompletableFuture<>(); WebSocket socket = new WebSocketFactory().createSocket("wss://www.wolframalpha.com/n/v1/api/fetcher/results"); ArrayList pos = new ArrayList<>(); //prevent duplicate socket.addListener(new WebSocketAdapter() { @Override public void onTextMessage(WebSocket websocket, String message) { try { //System.out.println(message); JSONObject resp = new JSONObject(new JSONTokener(message)); switch(resp.getString("type")) { case "pods": for(Object obj : resp.getJSONArray("pods")) { //pods might have multiple values JSONObject pod = (JSONObject) obj; if(pod.getBoolean("error")) continue; //skip errors if(!pos.contains(pod.getInt("position"))) { //check dupe //build big image for each pods ArrayList images = new ArrayList<>(); int width = 0, height = 0; if(!pod.has("subpods")) continue; //ignore empty pods that doesnt have image, usually fixed somewhere else for(Object subobj : pod.getJSONArray("subpods")) { JSONObject subpodImg = ((JSONObject) subobj).getJSONObject("img"); images.add(ImageIO.read(new URL(subpodImg.getString("src")))); if(subpodImg.getInt("width") > width) width = subpodImg.getInt("width"); //get widest image and use it as final width height += subpodImg.getInt("height"); //add all images } //create final image BufferedImage podImg = new BufferedImage(width + 20, height + 20, BufferedImage.TYPE_INT_RGB); //padding Graphics g = podImg.getGraphics(); g.setColor(new Color(255, 255, 255)); //fill as white first g.fillRect(0, 0, width + 20, height + 20); int y = 10; for(BufferedImage img : images) { g.drawImage(img, 10, y, null); y += img.getHeight(); } //send each pod as an individual message ByteArrayOutputStream os = new ByteArrayOutputStream(); ImageIO.write(podImg, "png", os); channel.sendMessage(pod.getString("title") + ":") .addFile(new ByteArrayInputStream(os.toByteArray()), "result.png").queue(); pos.add(pod.getInt("position")); //update to prevent dupes } } break; case "futureTopic": result.complete(new CommandResult(CommandResultType.FAILURE, "This query was identified under the topic of `" + resp.getJSONObject("futureTopic").getString("topic") + "`; Development of this topic is under investigation...")); break; case "didyoumean": result.complete(new CommandResult(CommandResultType.INVALIDARGS, "No good results found :cry:\nDid you mean `" + resp.getJSONArray("didyoumean").getJSONObject(0).getString("val") + "`?")); break; case "noResult": result.complete(new CommandResult(CommandResultType.NORESULT)); break; case "queryComplete": //might provide no output for certain queries, but theres no practical way to detect with this listener rn websocket.disconnect(); result.complete(new CommandResult(CommandResultType.SUCCESS)); //if its preceded by anything it wouldnt update; thats how complete() works break; } } catch(Exception e) { result.complete(new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e))); } } }); //System.out.println("{\"type\":\"init\",\"lang\":\"en\",\"exp\":" + System.currentTimeMillis() + ",\"displayDebuggingInfo\":false,\"messages\":[{\"type\":\"newQuery\",\"locationId\":\"hipuj\",\"language\":\"en\",\"displayDebuggingInfo\":false,\"yellowIsError\":false,\"input\":\"" + operation + "\",\"assumption\":[],\"file\":null}],\"input\":\"" + operation + "\",\"assumption\":[],\"file\":null}"); socket.connect(); socket.sendText("{\"type\":\"init\",\"lang\":\"en\",\"exp\":" + System.currentTimeMillis() + ",\"displayDebuggingInfo\":false,\"messages\":[{\"type\":\"newQuery\",\"locationId\":\"hipuj\",\"language\":\"en\",\"displayDebuggingInfo\":false,\"yellowIsError\":false,\"input\":\"" + operation + "\",\"assumption\":[],\"file\":null}],\"input\":\"" + operation + "\",\"assumption\":[],\"file\":null}"); return result.get(); } catch (IOException | WebSocketException | InterruptedException | ExecutionException e) { return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e)); } } }