Java tutorial
/* * Discord4J - Unofficial wrapper for Discord API * Copyright (c) 2015 * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package sx.blah.discord; import org.apache.commons.lang3.StringEscapeUtils; import org.apache.http.entity.StringEntity; import org.apache.http.message.BasicNameValuePair; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.json.simple.parser.ParseException; import sx.blah.discord.handle.IDispatcher; import sx.blah.discord.handle.impl.EventDispatcher; import sx.blah.discord.handle.impl.events.*; import sx.blah.discord.handle.obj.*; import sx.blah.discord.util.HTTP403Exception; import sx.blah.discord.util.Presences; import sx.blah.discord.util.Requests; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; import java.util.List; import java.util.Optional; /** * @author qt * @since 7:00 PM 16 Aug, 2015 * Project: DiscordAPI * <p> * Defines the client. * This class receives and * sends messages, as well * as holds our user data. */ public final class DiscordClient { /** * Used for keep alive. Keeps last time (in ms) * that we sent the keep alive so we can accurately * time our keep alive messages. */ private long timer = System.currentTimeMillis(); /** * User we are logged in as */ private User ourUser; /** * Our token, so we can send XHR to Discord. */ private String token; /** * Time (in ms) between keep alive * messages. */ private long heartbeat; /** * Local copy of all guilds/servers. */ private final List<Guild> guildList = new ArrayList<>(); /** * Re-usable instance of JSONParser. */ private static final JSONParser JSON_PARSER = new JSONParser(); /** * Private copy of the email you logged in with. */ private String email; /** * Private copy of the password you used to log in. */ private String password; /** * Local instance of DiscordClient. */ private static final DiscordClient __INSTANCE = new DiscordClient(); /** * WebSocket over which to communicate with Discord. */ private WebSocketClient ws; /** * Event dispatcher. */ private IDispatcher dispatcher; /** * All of the private message channels that the bot is connected to. */ private final List<PrivateChannel> privateChannels = new ArrayList<>(); /** * A cached json object of the games list to prevent unnecessary slowdown from file i/o */ private JSONArray games; private DiscordClient() { this.dispatcher = new EventDispatcher(); try { games = (JSONArray) JSON_PARSER.parse( new InputStreamReader(this.getClass().getClassLoader().getResourceAsStream("games.json"))); } catch (IOException | ParseException e) { e.printStackTrace(); } } /** * Allows you to use a custom event dispatcher. * * @param dispatcher An instance of IDispatcher. */ public void setDispatcher(IDispatcher dispatcher) { this.dispatcher = dispatcher; } /** * @return Singleton instance */ public static DiscordClient get() { return __INSTANCE; } /** * @return the event dispatcher. This will send events to all listeners */ public IDispatcher getDispatcher() { return dispatcher; } /** * @return Token, used for all XHR requests. */ public String getToken() { return token; } /** * Logs you into Discord using given * email and password. Also starts the bot. * <p> * Very important to use this method. */ public void login(String email, String password) throws IOException, ParseException, URISyntaxException { if (null != ws) { ws.close(); } this.email = email; this.password = password; try { this.token = (String) ((JSONObject) JSON_PARSER.parse(Requests.POST.makeRequest(DiscordEndpoints.LOGIN, new StringEntity("{\"email\":\"" + email + "\",\"password\":\"" + password + "\"}"), new BasicNameValuePair("content-type", "application/json")))).get("token"); } catch (HTTP403Exception e) { e.printStackTrace(); } this.ws = new DiscordWS(new URI(obtainGateway(this.token))); } /** * Logs you out from Discord. * <p> * Should be used on application termination. */ public void logout() throws IOException, ParseException, URISyntaxException { if (null != ws) { ws.close(); try { Requests.POST.makeRequest(DiscordEndpoints.LOGOUT, new BasicNameValuePair("authorization", token)); } catch (HTTP403Exception e) { e.printStackTrace(); } } else { Discord4J.logger.error("Bot has not signed in yet!"); } } /** * Gets the WebSocket gateway * * @param token Our login token * @return the WebSocket URL of which to connect * @throws ParseException */ private String obtainGateway(String token) throws ParseException { String s = null; try { s = ((String) ((JSONObject) JSON_PARSER.parse(Requests.GET.makeRequest( "https://discordapp.com/api/gateway", new BasicNameValuePair("authorization", token)))) .get("url")).replaceAll("wss", "ws"); } catch (HTTP403Exception e) { Discord4J.logger.error("Received 403 error attempting to get gateway; is your login correct?"); } Discord4J.logger.debug("Obtained gateway {}.", s); return s; } /** * Sends a message to the specified channel. * * @param content The actual message to send * @param channelID The channel to send the message to * @return The message that was sent. * @throws IOException * @throws ParseException */ public Message sendMessage(String content, String channelID) throws IOException, ParseException { if (null != ws) { content = StringEscapeUtils.escapeJson(content); try { String response = Requests.POST.makeRequest(DiscordEndpoints.CHANNELS + channelID + "/messages", new StringEntity("{\"content\":\"" + content + "\",\"mentions\":[]}", "UTF-8"), new BasicNameValuePair("authorization", token), new BasicNameValuePair("content-type", "application/json")); JSONObject object1 = (JSONObject) JSON_PARSER.parse(response); String time = (String) object1.get("timestamp"); String messageID = (String) object1.get("id"); Channel channel = getChannelByID(channelID); Message message = new Message(messageID, content, this.ourUser, channel, this.convertFromTimestamp(time)); channel.addMessage(message); //Had to be moved here so that if a message is edited before the MESSAGE_CREATE event, it doesn't error DiscordClient.this.dispatcher.dispatch(new MessageSendEvent(message)); return message; } catch (HTTP403Exception e) { Discord4J.logger.error("Received 403 error attempting to send message; is your login correct?"); return null; } } else { Discord4J.logger.error("Bot has not signed in yet!"); return null; } } /** * Edits a specified message. Currently, Discord only allows you to edit your own message * * @param content The new content of the message * @param messageID The id of the message to edit * @param channelID The channel the message exists in * @throws ParseException */ public void editMessage(String content, String messageID, String channelID) throws ParseException { if (null != ws) { content = StringEscapeUtils.escapeJson(content); Channel channel = getChannelByID(channelID); if (channel == null) { Discord4J.logger.error("Channel id " + channelID + " doesn't exist!"); return; } Message oldMessage = channel.getMessageByID(messageID); try { String response = Requests.PATCH.makeRequest( DiscordEndpoints.CHANNELS + channelID + "/messages/" + messageID, new StringEntity("{\"content\":\"" + content + "\", \"mentions\":[]}", "UTF-8"), new BasicNameValuePair("authorization", token), new BasicNameValuePair("content-type", "application/json")); JSONObject object1 = (JSONObject) JSON_PARSER.parse(response); Message newMessage = new Message((String) object1.get("id"), content, this.ourUser, getChannelByID(channelID), oldMessage.getTimestamp()); //Event dispatched here because otherwise there'll be an NPE as for some reason when the bot edits a message, // the event chain goes like this: //Original message edited to null, then the null message edited to the new content DiscordClient.this.dispatcher.dispatch(new MessageUpdateEvent(oldMessage, newMessage)); oldMessage.setContent(content); } catch (HTTP403Exception e) { Discord4J.logger.error("Received 403 error attempting to send message; is your login correct?"); } } else { Discord4J.logger.error("Bot has not signed in yet!"); } } /** * Deletes a message with given ID from provided channel ID. * * @param messageID Message (ID) to delete. * @param channelID Channel to delete it from. * @throws IOException */ public void deleteMessage(String messageID, String channelID) throws IOException { if (this.isReady()) { try { Requests.DELETE.makeRequest(DiscordEndpoints.CHANNELS + channelID + "/messages/" + messageID, new BasicNameValuePair("authorization", token)); } catch (HTTP403Exception e) { Discord4J.logger.error("Received 403 error attempting to delete message; is your login correct?"); } } else { Discord4J.logger.error("Bot has not signed in yet!"); } } /** * TODO: Fix this because it's fucking stupid. * Allows you to change the info on your bot. * Any fields you don't want to change should be left as an empty string ("") * * @param username Username (if you want to change it). * @param email Email (if you want to change it) * @param password Password (if you want to change it). */ public void changeAccountInfo(String username, String email, String password) throws UnsupportedEncodingException, ParseException { String s = "{\"username\":\"" + (username.isEmpty() ? this.ourUser.getName() : username) + "\",\"email\":\"" + (email.isEmpty() ? this.email : email) + "\",\"password\":\"" + this.password + "\",\"avatar\":\"" + this.ourUser.getAvatar() + "\",\"new_password\":" + (password.isEmpty() ? "null" : "\"" + password + "\"") + "}"; Discord4J.logger.debug("Token: {}", token); Discord4J.logger.debug(s); try { String response = Requests.PATCH.makeRequest(DiscordEndpoints.USERS + "@me", new StringEntity(s), new BasicNameValuePair("Authorization", token), new BasicNameValuePair("content-type", "application/json; charset=UTF-8")); JSONObject object1 = (JSONObject) JSON_PARSER.parse(response); this.token = (String) object1.get("token"); } catch (HTTP403Exception e) { Discord4J.logger .error("Received 403 error attempting to change account details; is your login correct?"); } } /** * Changes the bot's presence * * @param isIdle Set to true to make the bot idle or false for it to be online * @param gameID The (optional) gameID of the game the bot is playing */ public void updatePresence(boolean isIdle, Optional<Long> gameID) { String json = "{\"op\":3,\"d\":{\"idle_since\":" + (isIdle ? System.currentTimeMillis() : "null") + ",\"game_id\":" + (gameID.isPresent() ? gameID.get() : "null") + "}}"; Discord4J.logger.debug(json); ws.send(json); getOurUser().setPresence(isIdle ? Presences.IDLE : Presences.ONLINE); getOurUser().setGameID(gameID.orElse(null)); } /** * Gets the last 50 messages from a given channel ID. * * @param channel The channel to get messages from. * @return Last 50 messages from the channel. * @throws IOException * @throws ParseException */ private void getChannelMessages(Channel channel) throws Exception { String response = Requests.GET.makeRequest( DiscordEndpoints.CHANNELS + channel.getID() + "/messages?limit=50", new BasicNameValuePair("authorization", token)); JSONArray messageArray = (JSONArray) JSON_PARSER.parse(response); for (Object o : messageArray) { JSONObject object1 = (JSONObject) o; JSONObject author = (JSONObject) object1.get("author"); JSONArray mentions = (JSONArray) object1.get("mentions"); String[] mentionsArray = new String[mentions.size()]; for (int i = 0; i < mentions.size(); i++) { JSONObject mention = (JSONObject) mentions.get(i); mentionsArray[i] = (String) mention.get(mention.get("id")); } channel.addMessage(new Message((String) object1.get("id"), (String) object1.get("content"), this.getUserByID((String) author.get("id")), channel, this.convertFromTimestamp((String) object1.get("timestamp")))); } } public LocalDateTime convertFromTimestamp(String time) { return LocalDateTime.parse(time.split("\\+")[0]).atZone(ZoneId.of("UTC+00:00")) .withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime(); } /** * @return Whether or not the bot is ready to be used (is it logged in?) */ public boolean isReady() { return null != ws; } /** * @return The user the bot has signed in as. */ public User getOurUser() { return ourUser; } /** * Finds channel by given ID. * * @param id ID of the channel to find. * @return The channel. */ public Channel getChannelByID(String id) { for (Guild guild : guildList) { for (Channel channel : guild.getChannels()) { if (channel.getID().equalsIgnoreCase(id)) return channel; } } for (PrivateChannel channel : privateChannels) { if (channel.getID().equalsIgnoreCase(id)) return channel; } return null; } /** * Gets a guild object from a given guild ID. * @param guildID * @return */ public Guild getGuildByID(String guildID) { for (Guild guild : guildList) { if (guild.getID().equalsIgnoreCase(guildID)) return guild; } return null; } /** * Gets all the guilds this client is connected to. * * @return The guilds. */ public List<Guild> getGuilds() { return guildList; } @Deprecated public User getUserByID(String userID) { User u = null; for (Guild guild : guildList) { if (null == u) { u = guild.getUserByID(userID); } } return u; } /** * Gets a PM channel for a user; if one doesn't exist, it will be created. * @param user * @return */ public PrivateChannel getOrCreatePMChannel(User user) throws Exception { for (PrivateChannel channel : privateChannels) { if (channel.getRecipient().getID().equalsIgnoreCase(user.getID())) { return channel; } } try { String response = Requests.POST.makeRequest(DiscordEndpoints.USERS + this.ourUser.getID() + "/channels", new StringEntity("{\"recipient_id\":\"" + user.getID() + "\"}"), new BasicNameValuePair("authorization", this.token), new BasicNameValuePair("content-type", "application/json")); JSONObject object = (JSONObject) JSON_PARSER.parse(response); PrivateChannel channel = new PrivateChannel(user, (String) object.get("id")); privateChannels.add(channel); return channel; } catch (HTTP403Exception e) { e.printStackTrace(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return null; } /** * Attempts to get the name of the game based on its id * * @param gameId The game id (nullable!) * @return The game name, the Optional will be empty if the game couldn't be found */ public Optional<String> getGameByID(Long gameId) { if (games == null || gameId == null) return Optional.empty(); for (Object object : games) { if (!(object instanceof JSONObject)) continue; JSONObject jsonObject = (JSONObject) object; Long id = (Long) jsonObject.get("id"); if (id != null) { if (id.equals(gameId)) return Optional.ofNullable((String) jsonObject.get("name")); } } return Optional.empty(); } /** * Attempts to get the id of a game based on its name * * @param gameName The game name (nullable!) * @return The game id, the Optional will be empty if the game couldn't be found */ public Optional<Long> getGameIDByGame(String gameName) { if (gameName == null || gameName.isEmpty() || gameName.equals("null")) return Optional.empty(); for (Object object : games) { if (!(object instanceof JSONObject)) continue; JSONObject jsonObject = (JSONObject) object; String name = (String) jsonObject.get("name"); if (name != null) { if (name.equalsIgnoreCase(gameName)) return Optional.ofNullable((Long) jsonObject.get("id")); } } return Optional.empty(); } /** * Returns a user from raw JSON data. */ private User constructUserFromJSON(JSONObject user) { String id = (String) user.get("id"); String username = (String) user.get("username"); String avatar = (String) user.get("avatar"); User ourUser = new User(username, id, avatar); ourUser.setPresence(Presences.ONLINE); return ourUser; } class DiscordWS extends WebSocketClient { public DiscordWS(URI serverURI) { super(serverURI); this.connect(); } @Override public void onOpen(ServerHandshake serverHandshake) { if (!token.isEmpty()) { send("{\"op\":2,\"d\":{\"token\":\"" + token + "\",\"properties\":{\"$os\":\"Linux\",\"$browser\":\"DesuBot\",\"$device\":\"DesuBot\",\"$referrer\":\"\",\"$referring_domain\":\"\"},\"v\":2}}"); Discord4J.logger.debug("Connected to the Discord websocket."); } else System.err.println("Use the login() method to set your token first!"); } /** * Called when the websocket receives a message. * This method is parses from raw JSON to objects, * then dispatches them in the form of events. * * @param frame raw JSON data from Discord servers */ @Override public final void onMessage(String frame) { try { JSONObject object = ((JSONObject) JSON_PARSER.parse(frame)); try { String message = (String) object.get("message"); if (message.isEmpty()) { Discord4J.logger.error( "Received unknown error from Discord. Complain to the Discord devs, not me!"); } else Discord4J.logger.debug("Received error from Discord: {}", message); } catch (Exception e) { } String s = (String) object.get("t"); JSONObject d = (JSONObject) object.get("d"); switch (s) { case "READY": DiscordClient.this.ourUser = DiscordClient.this .constructUserFromJSON((JSONObject) d.get("user")); DiscordClient.this.heartbeat = (long) d.get("heartbeat_interval"); Discord4J.logger.debug("Received heartbeat interval of {}.", DiscordClient.this.heartbeat); JSONArray guilds = (JSONArray) d.get("guilds"); // I hope you like loops. for (Object o : guilds) { JSONObject guild = (JSONObject) o; JSONArray members = (JSONArray) guild.get("members"); JSONArray channels = (JSONArray) guild.get("channels"); JSONArray presences = (JSONArray) guild.get("presences"); String name = (String) guild.get("name"); String guildID = (String) guild.get("id"); String icon = (String) guild.get("icon"); String owner = (String) guild.get("owner_id"); Guild g; guildList.add(g = new Guild(name, guildID, icon, owner)); for (Object o1 : members) { JSONObject member = (JSONObject) ((JSONObject) o1).get("user"); g.addUser(new User((String) member.get("username"), (String) member.get("id"), (String) member.get("avatar"))); } for (Object o1 : presences) { JSONObject presence = (JSONObject) o1; User user = g.getUserByID((String) ((JSONObject) presence.get("user")).get("id")); user.setPresence(Presences.valueOf(((String) presence.get("status")).toUpperCase())); user.setGameID((Long) presence.get("game_id")); } for (Object o1 : channels) { JSONObject channel = (JSONObject) o1; String type = (String) channel.get("type"); if ("text".equalsIgnoreCase(type)) { String channelID = (String) channel.get("id"); String chName = (String) channel.get("name"); Channel c; g.addChannel(c = new Channel(chName, channelID, g)); try { DiscordClient.this.getChannelMessages(c); } catch (HTTP403Exception e) { Discord4J.logger.error( "No permission for channel \"{}\" in guild \"{}\". Are you logged in properly?", chName, name); } catch (Exception e) { Discord4J.logger.error( "Unable to get messages for channel \"{}\" in guild \"{}\" (Cause: {}).", chName, name, e.getClass().getSimpleName()); } } } } JSONArray privateChannelsArray = (JSONArray) d.get("private_channels"); for (Object o : privateChannelsArray) { JSONObject privateChannel = (JSONObject) o; String id = (String) privateChannel.get("id"); JSONObject user = (JSONObject) privateChannel.get("recipient"); User recipient = new User((String) user.get("username"), (String) user.get("id"), (String) user.get("avatar")); PrivateChannel channel = new PrivateChannel(recipient, id); try { DiscordClient.this.getChannelMessages(channel); } catch (HTTP403Exception e) { Discord4J.logger.error( "No permission for the private channel for \"{}\". Are you logged in properly?", channel.getRecipient().getName()); } catch (Exception e) { Discord4J.logger.error( "Unable to get messages for the private channel for \"{}\" (Cause: {}).", channel.getRecipient().getName(), e.getClass().getSimpleName()); } privateChannels.add(channel); } Discord4J.logger.debug("Logged in as {} (ID {}).", DiscordClient.this.ourUser.getName(), DiscordClient.this.ourUser.getID()); new Thread(() -> { // Keep alive while (null != ws) { long l; if ((l = (System.currentTimeMillis() - timer)) >= heartbeat) { Discord4J.logger.debug("Sending keep alive... ({}). Took {} ms.", System.currentTimeMillis(), l); send("{\"op\":1,\"d\":" + System.currentTimeMillis() + "}"); timer = System.currentTimeMillis(); } } }).start(); DiscordClient.this.dispatcher.dispatch(new ReadyEvent()); break; case "MESSAGE_CREATE": JSONObject author = (JSONObject) d.get("author"); String username = (String) author.get("username"); String id = (String) author.get("id"); String channelID = (String) d.get("channel_id"); String content = (String) d.get("content"); String messageID = (String) d.get("id"); JSONArray array = (JSONArray) d.get("mentions"); String time = (String) d.get("timestamp"); String[] mentionedIDs = new String[array.size()]; boolean mentioned = false; for (int i = 0; i < array.size(); i++) { JSONObject userInfo = (JSONObject) array.get(i); String userID = (String) userInfo.get("id"); if (userID.equalsIgnoreCase(DiscordClient.get().getOurUser().getID())) { mentioned = true; } mentionedIDs[i] = userID; } Channel channel = DiscordClient.get().getChannelByID(channelID); if (null != channel) { Message message1 = new Message(messageID, content, DiscordClient.get().getUserByID(id), channel, DiscordClient.get().convertFromTimestamp(time)); if (!id.equalsIgnoreCase(DiscordClient.get().getOurUser().getID())) { channel.addMessage(message1); Discord4J.logger.debug("Message from: {} ({}) in channel ID {}: {}", username, id, channelID, content); if (content.contains("discord.gg/")) { String inviteCode = content.split("discord\\.gg/")[1].split(" ")[0]; Discord4J.logger.debug("Received invite code \"{}\"", inviteCode); DiscordClient.get().dispatcher .dispatch(new InviteReceivedEvent(new Invite(inviteCode), message1)); } if (mentioned) { DiscordClient.this.dispatcher.dispatch(new MentionEvent(message1)); } DiscordClient.this.dispatcher.dispatch(new MessageReceivedEvent(message1)); } } break; case "TYPING_START": id = (String) d.get("user_id"); channelID = (String) d.get("channel_id"); User user; channel = getChannelByID(channelID); if (channel.isPrivate()) { user = ((PrivateChannel) channel).getRecipient(); } else { user = channel.getParent().getUserByID(id); } if (null != channel && null != user) { dispatcher.dispatch(new TypingEvent(user, channel)); } break; case "GUILD_CREATE": String name = (String) d.get("name"); id = (String) d.get("id"); JSONArray members = (JSONArray) d.get("members"); JSONArray channels = (JSONArray) d.get("channels"); String icon = (String) d.get("icon"); String owner = (String) d.get("owner_id"); Guild guild = new Guild(name, id, icon, owner); DiscordClient.this.guildList.add(guild); for (Object o : members) { JSONObject object1 = (JSONObject) o; guild.addUser(new User((String) object1.get("username"), (String) object1.get("id"), (String) object1.get("avatar"))); } for (Object o : channels) { JSONObject channelData = (JSONObject) o; if (((String) channelData.get("type")).equalsIgnoreCase("text")) { guild.addChannel(channel = new Channel((String) channelData.get("name"), (String) channelData.get("id"), guild)); try { getChannelMessages(channel); } catch (Exception e) { e.printStackTrace(); } } } DiscordClient.get().dispatcher.dispatch(new GuildCreateEvent(guild)); Discord4J.logger.debug("New guild has been joined/created! \"{}\" with ID {}.", name, id); break; case "GUILD_MEMBER_ADD": user = constructUserFromJSON((JSONObject) d.get("user")); String guildID = (String) d.get("guild_id"); guild = getGuildByID(guildID); if (null != guild) { guild.addUser(user); Discord4J.logger.debug("User \"{}\" joined guild \"{}\".", user.getName(), guild.getName()); dispatcher.dispatch( new UserJoinEvent(guild, user, convertFromTimestamp((String) d.get("joined_at")))); } break; case "GUILD_MEMBER_REMOVE": user = constructUserFromJSON((JSONObject) d.get("user")); guildID = (String) d.get("guild_id"); guild = getGuildByID(guildID); if (null != guild && guild.getUsers().contains(user)) { guild.getUsers().remove(user); Discord4J.logger.debug("User \"{}\" has been removed from or left guild \"{}\".", user.getName(), guild.getName()); dispatcher.dispatch(new UserLeaveEvent(guild, user)); } break; case "MESSAGE_UPDATE": id = (String) d.get("id"); channelID = (String) d.get("channel_id"); content = (String) d.get("content"); channel = DiscordClient.this.getChannelByID(channelID); Message m = channel.getMessageByID(id); if (null != m && !m.getAuthor().getID().equals(getOurUser().getID()) && !m.getContent().equals(content)) { Message newMessage; int index = channel.getMessages().indexOf(m); channel.getMessages().remove(m); channel.getMessages().add(index, newMessage = new Message(id, content, m.getAuthor(), channel, m.getTimestamp())); dispatcher.dispatch(new MessageUpdateEvent(m, newMessage)); } break; case "MESSAGE_DELETE": id = (String) d.get("id"); channelID = (String) d.get("channel_id"); channel = DiscordClient.this.getChannelByID(channelID); if (null != channel) { Message message = channel.getMessageByID(id); if (null != message && !message.getAuthor().getID() .equalsIgnoreCase(DiscordClient.this.ourUser.getID())) { channel.getMessages().remove(message); DiscordClient.get().dispatcher.dispatch(new MessageDeleteEvent(message)); } } break; case "PRESENCE_UPDATE": Presences presences = Presences.valueOf(((String) d.get("status")).toUpperCase()); Long gameId = (Long) d.get("game_id"); guild = getGuildByID((String) d.get("guild_id")); if (null != guild && null != presences) { user = guild.getUserByID((String) ((JSONObject) d.get("user")).get("id")); if (null != user) { if (!user.getPresence().equals(presences)) { dispatcher.dispatch( new PresenceUpdateEvent(guild, user, user.getPresence(), presences)); user.setPresence(presences); Discord4J.logger.debug("User \"{}\" changed presence to {}", user.getName(), user.getPresence()); } if (!user.getGameID().equals(Optional.ofNullable(gameId))) { dispatcher.dispatch(new GameChangeEvent(guild, user, user.getGameID().isPresent() ? user.getGameID().get() : null, gameId)); user.setGameID(gameId); Discord4J.logger.debug("User \"{}\" changed game to {}.", user.getName(), getGameByID(gameId).isPresent() ? getGameByID(gameId).get() : "null"); } } } break; case "GUILD_DELETE": id = (String) d.get("id"); guild = getGuildByID(id); getGuilds().remove(guild); Discord4J.logger.debug("You have been kicked from or left \"{}\"! :O", guild.getName()); dispatcher.dispatch(new GuildLeaveEvent(guild)); break; case "CHANNEL_CREATE": boolean isPrivate = (boolean) d.get("is_private"); id = (String) d.get("id"); if (isPrivate) { // PM channel. boolean b = false; for (PrivateChannel privateChannel : privateChannels) { if (privateChannel.getID().equalsIgnoreCase(id)) b = true; } if (b) break; // we already have this PM channel; no need to create another. user = constructUserFromJSON((JSONObject) d.get("recipient")); PrivateChannel privateChannel; privateChannels.add(privateChannel = new PrivateChannel(user, id)); try { getChannelMessages(privateChannel); } catch (Exception e) { e.printStackTrace(); } } else { // Regular channel. String type = (String) d.get("type"); if ("text".equalsIgnoreCase(type)) { name = (String) d.get("name"); id = (String) d.get("id"); guildID = (String) d.get("guild_id"); guild = getGuildByID(guildID); if (null != guild) { channel = new Channel(name, id, guild); try { getChannelMessages(channel); } catch (Exception e) { e.printStackTrace(); } dispatcher.dispatch(new ChannelCreateEvent(channel)); } } } break; case "CHANNEL_DELETE": if ("text".equalsIgnoreCase((String) d.get("type"))) { channel = getChannelByID((String) d.get("id")); channel.getParent().getChannels().remove(channel); } break; default: Discord4J.logger.warn("Unknown message received: {} (ignoring): {}", s, frame); } } catch (ParseException e) { e.printStackTrace(); } } @Override public void onClose(int i, String s, boolean b) { } @Override public void onError(Exception e) { e.printStackTrace(); } } }