diff --git a/src/main/java/net/tylermurphy/ken/api/JsonRequest.java b/src/main/java/net/tylermurphy/ken/api/JsonRequest.java index 59b9991..591ef4e 100644 --- a/src/main/java/net/tylermurphy/ken/api/JsonRequest.java +++ b/src/main/java/net/tylermurphy/ken/api/JsonRequest.java @@ -9,12 +9,13 @@ import java.nio.charset.StandardCharsets; public class JsonRequest extends Request { - public Object request() { + public String request() { super.addHeaders("Content-type","application/json"); HttpURLConnection connection = super.getConnection(); try { return parseJson(connection.getInputStream()); - } catch (Exception ignored) {} finally { + } catch (IOException ignored){ + } finally { if (connection != null) { connection.disconnect(); } @@ -22,7 +23,7 @@ public class JsonRequest extends Request { return null; } - public static JSONObject parseJson(InputStream is) throws JSONException { + public static String parseJson(InputStream is) throws JSONException { char[] buffer = new char[1024 * 4]; int n; try (BufferedInputStream stream = new BufferedInputStream(is); InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) { @@ -30,9 +31,9 @@ public class JsonRequest extends Request { while (-1 != (n = reader.read(buffer))) { writer.write(buffer, 0, n); } - return new JSONObject(writer.toString()); + return writer.toString(); } catch (IOException ignored) {} - return new JSONObject(""); + return "{}"; } } diff --git a/src/main/java/net/tylermurphy/ken/api/Request.java b/src/main/java/net/tylermurphy/ken/api/Request.java index 9da2061..c33e6fd 100644 --- a/src/main/java/net/tylermurphy/ken/api/Request.java +++ b/src/main/java/net/tylermurphy/ken/api/Request.java @@ -1,5 +1,6 @@ package net.tylermurphy.ken.api; +import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; @@ -61,13 +62,17 @@ public abstract class Request { byte[] input = body.getBytes("utf-8"); os.write(input, 0, input.length); } catch (Exception e) { - throw new Exception("Error writing body to HTTP connection.\n"+e.getMessage()); + throw new RuntimeException("Error writing body to HTTP connection.\n"+e.getMessage()); } } + if(connection.getResponseCode() > 299){ + throw new RuntimeException(connection.getResponseCode() + " " + connection.getResponseMessage()); + } + return connection; - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); } return null; diff --git a/src/main/java/net/tylermurphy/ken/api/XmlRequest.java b/src/main/java/net/tylermurphy/ken/api/XmlRequest.java index e82603d..4d4519f 100644 --- a/src/main/java/net/tylermurphy/ken/api/XmlRequest.java +++ b/src/main/java/net/tylermurphy/ken/api/XmlRequest.java @@ -13,12 +13,13 @@ import java.net.HttpURLConnection; public class XmlRequest extends Request { - public Document request() { + public Document request() throws RuntimeException { super.addHeaders("Content-type","text/xml"); HttpURLConnection connection = super.getConnection(); try { return parseXML(connection.getInputStream()); - } catch (Exception ignored) {} finally { + } catch (IOException | ParserConfigurationException | SAXException ignored) { + } finally { if (connection != null) { connection.disconnect(); } diff --git a/src/main/java/net/tylermurphy/ken/command/Responder.java b/src/main/java/net/tylermurphy/ken/command/Responder.java index edd7317..e7843ee 100644 --- a/src/main/java/net/tylermurphy/ken/command/Responder.java +++ b/src/main/java/net/tylermurphy/ken/command/Responder.java @@ -175,9 +175,8 @@ public class Responder extends ListenerAdapter { EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() .setColor(Color.RED) .setTitle(":x: **Error**") - .setDescription(e.getMessage()); + .setDescription(e.getCause().getMessage()); event.getHook().sendMessageEmbeds(builder.build()).queue(); - e.printStackTrace(); } } @@ -236,8 +235,11 @@ public class Responder extends ListenerAdapter { } } } catch (Exception e) { - event.getHook().sendMessage(e.getMessage()).queue(); - e.printStackTrace(); + EmbedBuilder builder = Ken.getInstance().getDefaultEmbed() + .setColor(Color.RED) + .setTitle(":x: **Error**") + .setDescription(e.getCause().getMessage()); + event.getHook().editOriginalEmbeds(builder.build()).queue(); } } diff --git a/src/main/java/net/tylermurphy/ken/command/main/Help.java b/src/main/java/net/tylermurphy/ken/command/main/Help.java index d9c64ce..477c85c 100644 --- a/src/main/java/net/tylermurphy/ken/command/main/Help.java +++ b/src/main/java/net/tylermurphy/ken/command/main/Help.java @@ -80,9 +80,12 @@ public class Help { Ken.getInstance().getDefaultEmbed() .setAuthor(name + " Command List", sender.getUser().getAvatarUrl()) .setTitle(":underage: **NSFW Commands**") - .appendDescription("**/rule34 ** Search on rule34\n") - .appendDescription("**/e612 ** Search on e621\n") - .appendDescription("**/akaneko ** Search on akaneko\n") + .appendDescription("**/hentai ** Retrieves a random hentai image\n") + .appendDescription("**/rule34 ** Searches on rule34\n") + .appendDescription("**/gelbooru ** Searches on gelbooru\n") + .appendDescription("**/danbooru ** Searches on danbooru\n") + .appendDescription("**/e621 ** Searches on e621\n") + .appendDescription("**/nhentai ** Searches on nhentai\n") .setFooter("Page "+page+"/"+PAGES) }; return embeds[page-1].build(); diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/Danbooru.java b/src/main/java/net/tylermurphy/ken/command/nsfw/Danbooru.java new file mode 100644 index 0000000..a91e7c5 --- /dev/null +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/Danbooru.java @@ -0,0 +1,57 @@ +package net.tylermurphy.ken.command.nsfw; + +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.tylermurphy.ken.Ken; +import net.tylermurphy.ken.api.HTTPMethod; +import net.tylermurphy.ken.api.JsonRequest; +import net.tylermurphy.ken.command.Response; +import net.tylermurphy.ken.util.Command; +import net.tylermurphy.ken.util.Option; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.List; + +public class Danbooru { + + @Command(name="danbooru",description="Searches for an image off of danbooru") + @Option(name="query",description="Search query for danbooru",type= OptionType.STRING,required=true) + @Option(name="page",description="Page number for danbooru",type= OptionType.INTEGER) + public Response execute(GuildMessageChannel channel, List args){ + if(!(channel instanceof TextChannel)) { + return Response.error("This command can only be used in a text channel"); + } + TextChannel textChannel = (TextChannel) channel; + if(!textChannel.isNSFW()){ + return Response.error("This command can only be used in an NSFW channel"); + } + int page = args.size() > 1 ? (int)args.get(1) : 1; + String url = request((String)args.get(0), page); + if(url == null){ + return Response.error("Unable to find post with search: "+args.get(0)); + } else { + if(url.endsWith(".mp4") || url.endsWith(".webm")) + return Response.success(url); + return Response.success(Ken.getInstance().getDefaultEmbed().setImage(url).build()); + } + } + + private String request(String query, int page){ + String response = (String) new JsonRequest() + .setURL("https://danbooru.donmai.us/posts.json?limit=100?tags="+query+"?page="+page) + .setType(HTTPMethod.GET) + .request(); + try { + JSONArray results = new JSONArray(response); + int choice = (int) (Math.random()*results.length()-1); + JSONObject post = (JSONObject) results.get(choice); + return post.getString("file_url"); + } catch (JSONException e) { + return null; + } + } + +} diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/E621.java b/src/main/java/net/tylermurphy/ken/command/nsfw/E621.java index b62dec4..a454f13 100644 --- a/src/main/java/net/tylermurphy/ken/command/nsfw/E621.java +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/E621.java @@ -17,9 +17,10 @@ import java.util.List; public class E621 { - @Command(name="e621",description=":underage: Searches for an image off of e621") - @Option(name="query",description="Search query for rule34",type= OptionType.STRING,required=true) - public Response execute(GuildMessageChannel channel, List args){ + @Command(name="e621",description="Searches for an image off of e621") + @Option(name="query",description="Search query for e621",type= OptionType.STRING,required=true) + @Option(name="page",description="Page number for e621",type= OptionType.INTEGER) + public Response execute(GuildMessageChannel channel, List args){ if(!(channel instanceof TextChannel)) { return Response.error("This command can only be used in a text channel"); } @@ -27,7 +28,8 @@ public class E621 { if(!textChannel.isNSFW()){ return Response.error("This command can only be used in an NSFW channel"); } - String url = request(args.get(0)); + int page = args.size() > 1 ? (int)args.get(1) : 1; + String url = request((String)args.get(0), page); if(url == null){ return Response.error("Unable to find post with search: "+args.get(0)); } else { @@ -37,13 +39,13 @@ public class E621 { } } - private String request(String query){ - JSONObject json = (JSONObject) new JsonRequest() - .setURL("https://e621.net/posts.json?tags="+query) + private String request(String query, int page){ + String response = (String) new JsonRequest() + .setURL("https://e621.net/posts.json?limit=100?tags="+query+"?page="+page) .setType(HTTPMethod.GET) .request(); try { - JSONArray results = json.getJSONArray("posts"); + JSONArray results = new JSONObject(response).getJSONArray("posts"); int choice = (int) (Math.random()*results.length()-1); JSONObject post = (JSONObject) results.get(choice); JSONObject file = post.getJSONObject("file"); diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/Gelbooru.java b/src/main/java/net/tylermurphy/ken/command/nsfw/Gelbooru.java new file mode 100644 index 0000000..04062dd --- /dev/null +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/Gelbooru.java @@ -0,0 +1,63 @@ +package net.tylermurphy.ken.command.nsfw; + +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.tylermurphy.ken.Ken; +import net.tylermurphy.ken.api.HTTPMethod; +import net.tylermurphy.ken.api.XmlRequest; +import net.tylermurphy.ken.command.Response; +import net.tylermurphy.ken.util.Command; +import net.tylermurphy.ken.util.Option; +import org.json.JSONException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.List; + +public class Gelbooru { + + @Command(name="gelbooru",description="Searches for an image off of gelbooru") + @Option(name="query",description="Search query for gelbooru",type=OptionType.STRING,required=true) + @Option(name="page",description="Page number for gelbooru",type= OptionType.INTEGER) + public Response execute(GuildMessageChannel channel, List args){ + if(!(channel instanceof TextChannel)) { + return Response.error("This command can only be used in a text channel"); + } + TextChannel textChannel = (TextChannel) channel; + if(!textChannel.isNSFW()){ + return Response.error("This command can only be used in an NSFW channel"); + } + int page = args.size() > 1 ? (int)args.get(1) : 1; + String url = request((String)args.get(0), page); + System.out.println(url); + if(url == null){ + return Response.error("Unable to find post with search: "+args.get(0)); + } else { + if(url.endsWith(".mp4") || url.endsWith(".webm")) + return Response.success(url); + return Response.success(Ken.getInstance().getDefaultEmbed().setImage(url).build()); + } + } + + private String request(String query, int page) { + Document doc = (Document) new XmlRequest() + .setURL("https://gelbooru.com/index.php?page=dapi&s=post&q=index&limit=100&tags="+query+"&pid="+page) + .setType(HTTPMethod.GET) + .request(); + try { + NodeList nList = doc.getElementsByTagName("post"); + int choice = (int) (Math.random() * nList.getLength() - 1); + if (choice < 0) return null; + Node node = nList.item(choice); + Element element = (Element) node; + Node file = element.getElementsByTagName("file_url").item(0); + return file.getTextContent(); + } catch (JSONException e) { + return null; + } + } + +} diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/Akaneko.java b/src/main/java/net/tylermurphy/ken/command/nsfw/Hentai.java similarity index 90% rename from src/main/java/net/tylermurphy/ken/command/nsfw/Akaneko.java rename to src/main/java/net/tylermurphy/ken/command/nsfw/Hentai.java index 71a3922..bfb63c0 100644 --- a/src/main/java/net/tylermurphy/ken/command/nsfw/Akaneko.java +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/Hentai.java @@ -14,9 +14,9 @@ import org.json.JSONObject; import java.util.List; -public class Akaneko { +public class Hentai { - @Command(name="akaneko", description=":underage: Searches for an image off of akaneko") + @Command(name="hentai", description="Retrieves a random hentai image") @Selection(name="query", description="Type of post you want to get", type=OptionType.STRING, required=true, choices={"ass","bdsm","cum","hentai","femdom","doujin","maid","orgy","nsfwwallpapers","nsfwmobilewallpapers","gif","blowjob","feet","pussy","uglybastard","gangbang","cumslut","glasses","thighs","tentacles","masturbation","school","yuri","succubus"}) public Response execute(GuildMessageChannel channel, List args){ if(!(channel instanceof TextChannel)) { @@ -37,12 +37,12 @@ public class Akaneko { } private String request(String query){ - JSONObject json = (JSONObject) new JsonRequest() + String response = (String) new JsonRequest() .setURL("https://akaneko-api.herokuapp.com/api/"+query) .setType(HTTPMethod.GET) .request(); try { - return json.getString("url"); + return new JSONObject(response).getString("url"); } catch (JSONException e) { return null; } diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/Rule34.java b/src/main/java/net/tylermurphy/ken/command/nsfw/Rule34.java index e1d9146..a62c301 100644 --- a/src/main/java/net/tylermurphy/ken/command/nsfw/Rule34.java +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/Rule34.java @@ -19,8 +19,9 @@ import java.util.List; public class Rule34 { - @Command(name="rule34",description=":underage: Searches for an image off of rule34") + @Command(name="rule34",description="Searches for an image off of rule34") @Option(name="query",description="Search query for rule34",type=OptionType.STRING,required=true) + @Option(name="page",description="Page number for rule34",type= OptionType.INTEGER) public Response execute(GuildMessageChannel channel, List args){ if(!(channel instanceof TextChannel)) { return Response.error("This command can only be used in a text channel"); @@ -29,7 +30,8 @@ public class Rule34 { if(!textChannel.isNSFW()){ return Response.error("This command can only be used in an NSFW channel"); } - String url = request((String)args.get(0)); + int page = args.size() > 1 ? (int)args.get(1) : 1; + String url = request((String)args.get(0), page); if(url == null){ return Response.error("Unable to find post with search: "+args.get(0)); } else { @@ -39,9 +41,9 @@ public class Rule34 { } } - private String request(String query) { + private String request(String query, int page) { Document doc = (Document) new XmlRequest() - .setURL("https://rule34.xxx/index.php?page=dapi&s=post&q=index&tags=" + query) + .setURL("https://rule34.xxx/index.php?page=dapi&s=post&q=index&limit=100&tags="+query+"&pid="+page) .setType(HTTPMethod.GET) .request(); try { diff --git a/src/main/java/net/tylermurphy/ken/command/nsfw/nHentai.java b/src/main/java/net/tylermurphy/ken/command/nsfw/nHentai.java new file mode 100644 index 0000000..347eb82 --- /dev/null +++ b/src/main/java/net/tylermurphy/ken/command/nsfw/nHentai.java @@ -0,0 +1,132 @@ +package net.tylermurphy.ken.command.nsfw; + +import net.dv8tion.jda.api.entities.GuildMessageChannel; +import net.dv8tion.jda.api.entities.Message; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.interactions.commands.OptionType; +import net.tylermurphy.ken.Ken; +import net.tylermurphy.ken.api.HTTPMethod; +import net.tylermurphy.ken.api.JsonRequest; +import net.tylermurphy.ken.command.Response; +import net.tylermurphy.ken.util.ButtonCallback; +import net.tylermurphy.ken.util.Command; +import net.tylermurphy.ken.util.Option; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class nHentai { + + private static class Comic{ + public int pages; + public int id; + public String[] page_file_type; + public String cover_file_type; + public String media_id; + public String title; + } + + private final Map cache = new HashMap<>(); + + @Command(name="nhentai", description="Search for a post off of nhentai") + @Option(name="query", description="Post id or search for nhentai", type=OptionType.STRING, required=true) + public Response execute(GuildMessageChannel channel, List args) { + if (!(channel instanceof TextChannel)) { + return Response.error("This command can only be used in a text channel"); + } + TextChannel textChannel = (TextChannel) channel; + if (!textChannel.isNSFW()) { + return Response.error("This command can only be used in an NSFW channel"); + } + Comic comic = request((String)args.get(0)); + if(comic == null){ + return Response.error("Unable to find post with that query"); + } + return Response.success(createEmbed(comic,0)).addSecondaryButton("nhentai", "previous", "Previous").addSecondaryButton("nhentai", "next", "Next"); + } + + @ButtonCallback(name="nhentai") + public Response onButton(String button_id, Message message){ + int page, id; + if (message != null && message.getEmbeds().size() > 0 && message.getEmbeds().get(0) != null) { + page = Integer.parseInt(message.getEmbeds().get(0).getFooter().getText().split("/")[0].substring(5)); + id = Integer.parseInt(message.getEmbeds().get(0).getDescription().substring(4)); + } else { + return Response.error("Embed is missing, please resend command"); + } + Comic comic = cache.get(id); + if(comic == null) comic = request(id+""); + if(comic == null) return Response.error("Failed to fetch comic data"); + if(button_id.equals("previous")) + page = page - 1 < 0 ? comic.pages : page - 1; + else + page = page + 1 > comic.pages ? 0 : page + 1; + return Response.success(createEmbed(comic,page)); + } + + private MessageEmbed createEmbed(Comic comic, int page){ + String imageLink = page == 0 ? + "https://t.nhentai.net/galleries/"+comic.id+"/cover."+comic.cover_file_type : + "https://i.nhentai.net/galleries/"+comic.id+"/"+page+"."+comic.page_file_type[page-1]; + return Ken.getInstance().getDefaultEmbed() + .setTitle(comic.title, "https://nhentai.net/g/"+comic.id) + .setImage(imageLink) + .setDescription("id: "+comic.id) + .setFooter("Page "+page+"/"+comic.pages) + .build(); + } + + private Comic request(String search) { + int id; + JSONObject info; + if(search.length() == 6 && isNumber(search)){ + id = Integer.parseInt(search); + if(cache.containsKey(id)) return cache.get(id); + String response = (String) new JsonRequest() + .setURL(String.format("https://nhentai.net/api/gallery/%s", id)) + .setType(HTTPMethod.GET) + .request(); + info = new JSONObject(response); + } else { + String response = (String) new JsonRequest() + .setURL("https://nhentai.net/api/galleries/search?query="+search+"%20language:english") + .setType(HTTPMethod.GET) + .request(); + JSONArray results = new JSONObject(response).getJSONArray("result"); + if(results.length() == 0) return null; + info = (JSONObject) results.get((int) (Math.random()*results.length())); + id = info.getInt("id"); + } + Comic c = new Comic(); + c.pages = info.getJSONObject("images").getJSONArray("pages").length(); + c.page_file_type = new String[c.pages]; + Iterator itr = info.getJSONObject("images").getJSONArray("pages").iterator(); + int i = 0; + while(itr.hasNext()) { + JSONObject o = (JSONObject) itr.next(); + c.page_file_type[i] = o.getString("t").equals("j") ? "jpg" : "png"; + i++; + } + c.cover_file_type = info.getJSONObject("images").getJSONObject("cover").getString("t").equals("j") ? "jpg" : "png"; + c.media_id = info.getString("media_id"); + c.title = info.getJSONObject("title").getString("english"); + c.id = id; + cache.put(id,c); + return c; + } + + private static boolean isNumber(String test) { + try { + Integer.parseInt(test); + return true; + } catch (Exception e){ + return false; + } + } + +} \ No newline at end of file