diff --git a/src/main/java/nl/sbdeveloper/mctpaudio/MCTPAudio.java b/src/main/java/nl/sbdeveloper/mctpaudio/MCTPAudio.java index a4850b3..44fb7c0 100644 --- a/src/main/java/nl/sbdeveloper/mctpaudio/MCTPAudio.java +++ b/src/main/java/nl/sbdeveloper/mctpaudio/MCTPAudio.java @@ -42,7 +42,7 @@ public final class MCTPAudio extends JavaPlugin { saveConfig(); Bukkit.getLogger().info("[MCTPAudio] Connecting with socket..."); - client = new Client("ws://5.9.170.139:25564"); + client = new Client("ws://168.119.82.91:25564"); client.connect(); Bukkit.getLogger().info("[MCTPAudio] Loading commands and events..."); diff --git a/src/main/java/nl/sbdeveloper/mctpaudio/radio/Playlist.java b/src/main/java/nl/sbdeveloper/mctpaudio/radio/Playlist.java index bf20960..888e7d2 100644 --- a/src/main/java/nl/sbdeveloper/mctpaudio/radio/Playlist.java +++ b/src/main/java/nl/sbdeveloper/mctpaudio/radio/Playlist.java @@ -6,11 +6,14 @@ import nl.sbdeveloper.mctpaudio.MCTPAudio; import nl.sbdeveloper.mctpaudio.api.maps.SongList; import nl.sbdeveloper.mctpaudio.managers.PinManager; import nl.sbdeveloper.mctpaudio.utils.HeadUtil; +import nl.sbdeveloper.mctpaudio.utils.MainUtil; +import nl.sbdeveloper.mctpaudio.utils.WebhookMessage; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitTask; import org.json.simple.JSONObject; +import java.awt.*; import java.io.IOException; /* Copyright (C) McThemeParks - All Rights Reserved @@ -21,7 +24,6 @@ import java.io.IOException; public class Playlist { private final SongList playList = new SongList<>(); - private final SongList playedList = new SongList<>(); private boolean running = false; private BukkitTask currentTimer; @@ -78,10 +80,12 @@ public class Playlist { public void nextSong() { if (currentTimer != null) return; //A song is playing? - if (faultCounter > 4) stop(); //FALLBACK! 4 errors occured. + if (faultCounter > 4) { + MCTPAudio.getPlugin(MCTPAudio.class).getLogger().severe("Detected to many IO errors! Stopping the radio..."); + stop(); //FALLBACK! 4 errors occured. + } if (playList.isEmpty()) { - playedList.clear(); start(); } @@ -89,16 +93,27 @@ public class Playlist { String nextURL = playList.getRandom(); if (nextURL == null) return; - //Remove from PlayList and add to PlayedList - playList.remove(nextURL); - playedList.add(nextURL); + //Remove from PlayList + try { + playList.remove(nextURL); + } catch (ArrayIndexOutOfBoundsException ignored) { } //Get ticks of song int ticks; try { ticks = HeadUtil.getTicksOfFile(nextURL); } catch (InvalidDataException | UnsupportedTagException ex) { - ex.printStackTrace(); + WebhookMessage.EmbedObject embed = new WebhookMessage.EmbedObject(); + embed.setTitle("Radio song failure!"); + embed.setColor(Color.RED); + embed.setDescription("A song in the radio does not have MPEG headers. A song without headers cannot be played in the radio because the length cannot be determined. Please fix as soon as possible.\n\nClick on the title to go to the song."); + embed.setUrl(nextURL); + + try { + MainUtil.sendMessageDiscord(embed, "<@&965684395686129724>"); + } catch (IOException e) { + e.printStackTrace(); + } nextSong(); return; } catch (IOException ex) { diff --git a/src/main/java/nl/sbdeveloper/mctpaudio/utils/MainUtil.java b/src/main/java/nl/sbdeveloper/mctpaudio/utils/MainUtil.java new file mode 100644 index 0000000..75f281a --- /dev/null +++ b/src/main/java/nl/sbdeveloper/mctpaudio/utils/MainUtil.java @@ -0,0 +1,22 @@ +package nl.sbdeveloper.mctpaudio.utils; + +import java.io.IOException; + +public class MainUtil { + public static void sendMessageDiscord(WebhookMessage.EmbedObject embed, String mention) throws IOException { + WebhookMessage webhookMessage = new WebhookMessage("https://discord.com/api/webhooks/706629612955762778/MztXWVWupAIoTIHwb0XK8ExfOz6nrygB_FEl9EpbFENGUCGobMib_5Apj2rnUSXuMlbR"); + webhookMessage.setAvatarUrl("https://mcthemeparks.eu/musicupload/downloads/Designs/Logo2.png"); + webhookMessage.setUsername("McThemeParks Audio"); + + embed.setFooter("© McThemeParks 2021/2022", "https://mcthemeparks.eu/musicupload/downloads/Designs/Logo2.png"); + webhookMessage.addEmbed(embed); + + if (mention != null) webhookMessage.setContent(mention); + + webhookMessage.execute(); + } + + public static void sendMessageDiscord(WebhookMessage.EmbedObject embed) throws IOException { + sendMessageDiscord(embed, null); + } +} diff --git a/src/main/java/nl/sbdeveloper/mctpaudio/utils/WebhookMessage.java b/src/main/java/nl/sbdeveloper/mctpaudio/utils/WebhookMessage.java new file mode 100644 index 0000000..6535c07 --- /dev/null +++ b/src/main/java/nl/sbdeveloper/mctpaudio/utils/WebhookMessage.java @@ -0,0 +1,384 @@ +package nl.sbdeveloper.mctpaudio.utils; + +import javax.net.ssl.HttpsURLConnection; +import java.awt.*; +import java.io.IOException; +import java.io.OutputStream; +import java.lang.reflect.Array; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Class used to execute Discord Webhooks with low effort + */ +public class WebhookMessage { + private final String url; + private String content; + private String username; + private String avatarUrl; + private boolean tts; + private final List embeds = new ArrayList<>(); + + /** + * Constructs a new DiscordWebhook instance + * + * @param url The webhook URL obtained in Discord + */ + public WebhookMessage(String url) { + this.url = url; + } + + public void setContent(String content) { + this.content = content; + } + + public void setUsername(String username) { + this.username = username; + } + + public void setAvatarUrl(String avatarUrl) { + this.avatarUrl = avatarUrl; + } + + public void setTts(boolean tts) { + this.tts = tts; + } + + public void addEmbed(EmbedObject embed) { + this.embeds.add(embed); + } + + public void execute() throws IOException { + if (this.content == null && this.embeds.isEmpty()) { + throw new IllegalArgumentException("Set content or add at least one EmbedObject"); + } + + JSONObject json = new JSONObject(); + + json.put("content", this.content); + json.put("username", this.username); + json.put("avatar_url", this.avatarUrl); + json.put("tts", this.tts); + + if (!this.embeds.isEmpty()) { + List embedObjects = new ArrayList<>(); + + for (EmbedObject embed : this.embeds) { + JSONObject jsonEmbed = new JSONObject(); + + jsonEmbed.put("title", embed.getTitle()); + jsonEmbed.put("description", embed.getDescription()); + jsonEmbed.put("url", embed.getUrl()); + + if (embed.getColor() != null) { + Color color = embed.getColor(); + int rgb = color.getRed(); + rgb = (rgb << 8) + color.getGreen(); + rgb = (rgb << 8) + color.getBlue(); + + jsonEmbed.put("color", rgb); + } + + EmbedObject.Footer footer = embed.getFooter(); + EmbedObject.Image image = embed.getImage(); + EmbedObject.Thumbnail thumbnail = embed.getThumbnail(); + EmbedObject.Author author = embed.getAuthor(); + List fields = embed.getFields(); + + if (footer != null) { + JSONObject jsonFooter = new JSONObject(); + + jsonFooter.put("text", footer.getText()); + jsonFooter.put("icon_url", footer.getIconUrl()); + jsonEmbed.put("footer", jsonFooter); + } + + if (image != null) { + JSONObject jsonImage = new JSONObject(); + + jsonImage.put("url", image.getUrl()); + jsonEmbed.put("image", jsonImage); + } + + if (thumbnail != null) { + JSONObject jsonThumbnail = new JSONObject(); + + jsonThumbnail.put("url", thumbnail.getUrl()); + jsonEmbed.put("thumbnail", jsonThumbnail); + } + + if (author != null) { + JSONObject jsonAuthor = new JSONObject(); + + jsonAuthor.put("name", author.getName()); + jsonAuthor.put("url", author.getUrl()); + jsonAuthor.put("icon_url", author.getIconUrl()); + jsonEmbed.put("author", jsonAuthor); + } + + List jsonFields = new ArrayList<>(); + for (EmbedObject.Field field : fields) { + JSONObject jsonField = new JSONObject(); + + jsonField.put("name", field.getName()); + jsonField.put("value", field.getValue()); + jsonField.put("inline", field.isInline()); + + jsonFields.add(jsonField); + } + + jsonEmbed.put("fields", jsonFields.toArray()); + embedObjects.add(jsonEmbed); + } + + json.put("embeds", embedObjects.toArray()); + } + + URL url = new URL(this.url); + HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); + connection.addRequestProperty("Content-Type", "application/json"); + connection.addRequestProperty("User-Agent", "Java-DiscordWebhook-BY-Gelox_"); + connection.setDoOutput(true); + connection.setRequestMethod("POST"); + + OutputStream stream = connection.getOutputStream(); + stream.write(json.toString().getBytes(StandardCharsets.UTF_8)); + stream.flush(); + stream.close(); + + connection.getInputStream().close(); //I'm not sure why but it doesn't work without getting the InputStream + connection.disconnect(); + } + + public static class EmbedObject { + private String title; + private String description; + private String url; + private Color color; + + private Footer footer; + private Thumbnail thumbnail; + private Image image; + private Author author; + private final List fields = new ArrayList<>(); + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public String getUrl() { + return url; + } + + public Color getColor() { + return color; + } + + public Footer getFooter() { + return footer; + } + + public Thumbnail getThumbnail() { + return thumbnail; + } + + public Image getImage() { + return image; + } + + public Author getAuthor() { + return author; + } + + public List getFields() { + return fields; + } + + public EmbedObject setTitle(String title) { + this.title = title; + return this; + } + + public EmbedObject setDescription(String description) { + this.description = description; + return this; + } + + public EmbedObject setUrl(String url) { + this.url = url; + return this; + } + + public EmbedObject setColor(Color color) { + this.color = color; + return this; + } + + public EmbedObject setFooter(String text, String icon) { + this.footer = new Footer(text, icon); + return this; + } + + public EmbedObject setThumbnail(String url) { + this.thumbnail = new Thumbnail(url); + return this; + } + + public EmbedObject setImage(String url) { + this.image = new Image(url); + return this; + } + + public EmbedObject setAuthor(String name, String url, String icon) { + this.author = new Author(name, url, icon); + return this; + } + + public EmbedObject addField(String name, String value, boolean inline) { + this.fields.add(new Field(name, value, inline)); + return this; + } + + private static class Footer { + private final String text; + private final String iconUrl; + + private Footer(String text, String iconUrl) { + this.text = text; + this.iconUrl = iconUrl; + } + + private String getText() { + return text; + } + + private String getIconUrl() { + return iconUrl; + } + } + + private static class Thumbnail { + private final String url; + + private Thumbnail(String url) { + this.url = url; + } + + private String getUrl() { + return url; + } + } + + private static class Image { + private final String url; + + private Image(String url) { + this.url = url; + } + + private String getUrl() { + return url; + } + } + + private static class Author { + private final String name; + private final String url; + private final String iconUrl; + + private Author(String name, String url, String iconUrl) { + this.name = name; + this.url = url; + this.iconUrl = iconUrl; + } + + private String getName() { + return name; + } + + private String getUrl() { + return url; + } + + private String getIconUrl() { + return iconUrl; + } + } + + private static class Field { + private final String name; + private final String value; + private final boolean inline; + + private Field(String name, String value, boolean inline) { + this.name = name; + this.value = value; + this.inline = inline; + } + + private String getName() { + return name; + } + + private String getValue() { + return value; + } + + private boolean isInline() { + return inline; + } + } + } + + private static class JSONObject extends HashMap { + @Override + public Object put(String key, Object value) { + if (value == null) return null; + return super.put(key, value); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("{"); + + int i = 0; + for (Entry entry : entrySet()) { + Object val = entry.getValue(); + builder.append(quote(entry.getKey())).append(":"); + + if (val instanceof String) { + builder.append(quote(String.valueOf(val))); + } else if (val instanceof Integer) { + builder.append((int) val); + } else if (val instanceof Boolean) { + builder.append(val); + } else if (val instanceof JSONObject) { + builder.append(val.toString()); + } else if (val.getClass().isArray()) { + builder.append("["); + int len = Array.getLength(val); + for (int j = 0; j < len; j++) { + builder.append(Array.get(val, j).toString()).append(j != len - 1 ? "," : ""); + } + builder.append("]"); + } + + builder.append(++i == entrySet().size() ? "}" : ","); + } + + return builder.toString(); + } + + private String quote(String string) { + return "\"" + string + "\""; + } + } +} \ No newline at end of file