Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added handling for relationship presence updates #494

Merged
merged 8 commits into from
Oct 25, 2017
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
*/
package net.dv8tion.jda.core.handle;

import net.dv8tion.jda.client.JDAClient;
import net.dv8tion.jda.client.entities.impl.FriendImpl;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.OnlineStatus;
import net.dv8tion.jda.core.entities.Game;
import net.dv8tion.jda.core.entities.impl.*;
Expand All @@ -37,12 +40,21 @@ public PresenceUpdateHandler(JDAImpl api)
@Override
protected Long handleInternally(JSONObject content)
{
//Do a pre-check to see if this is for a Guild, and if it is, if the guild is currently locked.
if (content.has("guild_id"))
GuildImpl guild = null;
//Do a pre-check to see if this is for a Guild, and if it is, if the guild is currently locked or not cached.
if (!content.isNull("guild_id"))
{
final long guildId = content.getLong("guild_id");
if (api.getGuildLock().isLocked(guildId))
return guildId;
guild = (GuildImpl) api.getGuildById(guildId);
if (guild == null)
{
api.getEventCache().cache(EventCache.Type.GUILD, guildId, () -> handle(responseNumber, allContent));
EventCache.LOG.debug("Received a PRESENCE_UPDATE for a guild that is not yet cached! " +
"GuildId: " + guildId + " UserId: " + content.getJSONObject("user").get("id"));
return null;
}
}

JSONObject jsonUser = content.getJSONObject("user");
Expand Down Expand Up @@ -74,7 +86,7 @@ protected Long handleInternally(JSONObject content)
user, oldUsername, oldDiscriminator));
}
String oldAvatar = user.getAvatarId();
if (!(avatarId == null && oldAvatar == null) && !Objects.equals(avatarId, oldAvatar))
if (!Objects.equals(avatarId, oldAvatar))
{
String oldAvatarId = user.getAvatarId();
user.setAvatarId(avatarId);
Expand All @@ -90,31 +102,29 @@ protected Long handleInternally(JSONObject content)
String gameName = null;
String gameUrl = null;
Game.GameType type = null;
if ( !content.isNull("game") && !content.getJSONObject("game").isNull("name") )
final JSONObject game = content.optJSONObject("game");
if (game != null && !game.isNull("name"))
{
gameName = content.getJSONObject("game").get("name").toString();
gameUrl = ( content.getJSONObject("game").isNull("url") ? null : content.getJSONObject("game").get("url").toString() );
gameName = game.get("name").toString();
gameUrl = game.isNull("url") ? null : game.get("url").toString();
try
{
type = content.getJSONObject("game").isNull("type")
type = game.isNull("type")
? Game.GameType.DEFAULT
: Game.GameType.fromKey(Integer.parseInt(content.getJSONObject("game").get("type").toString()));
: Game.GameType.fromKey(Integer.parseInt(game.get("type").toString()));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't game.getInt("type") be the same here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the past we've encountered numerous issues with game objects due to them not being checked by the API, I didn't touch that logic at all here.

}
catch (NumberFormatException ex)
{
type = Game.GameType.DEFAULT;
}
}
Game nextGame = (gameName == null
? null
: api.getEntityBuilder().createGame(gameName, gameUrl, type));
Game nextGame = gameName == null ? null : api.getEntityBuilder().createGame(gameName, gameUrl, type);
OnlineStatus status = OnlineStatus.fromKey(content.getString("status"));

//If we are in a Guild, then we will use Member.
// If we aren't we'll be dealing with the Relation system.
if (content.has("guild_id"))
if (guild != null)
{
GuildImpl guild = (GuildImpl) api.getGuildById(content.getLong("guild_id"));
MemberImpl member = (MemberImpl) guild.getMember(user);

//If the Member is null, then User isn't in the Guild.
Expand Down Expand Up @@ -144,7 +154,7 @@ protected Long handleInternally(JSONObject content)
api, responseNumber,
user, guild, oldStatus));
}
if(member.getGame() == null ? nextGame != null : !member.getGame().equals(nextGame))
if (!Objects.equals(member.getGame(), nextGame))
{
Game oldGame = member.getGame();
member.setGame(nextGame);
Expand All @@ -158,7 +168,32 @@ protected Long handleInternally(JSONObject content)
else
{
//In this case, this PRESENCE_UPDATE is for a Relation.
if (api.getAccountType() != AccountType.CLIENT)
return null;
JDAClient client = api.asClient();
FriendImpl friend = (FriendImpl) client.getFriendById(userId);

if (friend != null)
{
if (!friend.getOnlineStatus().equals(status))
{
OnlineStatus oldStatus = friend.getOnlineStatus();
friend.setOnlineStatus(status);
api.getEventManager().handle(
new UserOnlineStatusUpdateEvent(
api, responseNumber,
user, null, oldStatus));
}
if (!Objects.equals(friend.getGame(), nextGame))
{
Game oldGame = friend.getGame();
friend.setGame(nextGame);
api.getEventManager().handle(
new UserGameUpdateEvent(
api, responseNumber,
user, null, oldGame));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be firing FriendXEvents, not UserXEvents I think.
In that same vain though, we probably should rename the User events to Member events?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, at a minimum, take a Friend object to these User events so that you can call getFriend like you can call getMember.

Copy link
Member Author

@MinnDevelopment MinnDevelopment Oct 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}
}
}
}
else
Expand All @@ -173,19 +208,10 @@ protected Long handleInternally(JSONObject content)

//If the OnlineStatus is OFFLINE, ignore the event and return.
OnlineStatus status = OnlineStatus.fromKey(content.getString("status"));
if (status == OnlineStatus.OFFLINE)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We lost the ability to return in an OFFLINE non-guild situation.
Is that correct handling?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate? Not sure what you mean

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind. I see what has changed here. The fast escape logic is no longer needed.

return null;

//If this was for a Guild, cache it in the Guild for later use in GUILD_MEMBER_ADD
if (content.has("guild_id"))
{
GuildImpl guild = (GuildImpl) api.getGuildById(content.getLong("guild_id"));
if (status != OnlineStatus.OFFLINE && guild != null)
guild.getCachedPresenceMap().put(userId, content);
}
else
{
//cache in relationship stuff
}
}
return null;
}
Expand Down
45 changes: 26 additions & 19 deletions src/main/java/net/dv8tion/jda/core/requests/WebSocketClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -939,7 +939,7 @@ protected void handleEvent(JSONObject raw)
{
type = "GUILD_CREATE";
raw.put("t", "GUILD_CREATE")
.put("jda-field","This event was originally a GUILD_DELETE but was converted to GUILD_CREATE for WS init Guild streaming");
.put("jda-field","This event was originally a GUILD_DELETE but was converted to GUILD_CREATE for WS init Guild streaming");
}
else
{
Expand All @@ -948,20 +948,24 @@ protected void handleEvent(JSONObject raw)
return;
}
}
//
// // Needs special handling due to content of "d" being an array
// if(type.equals("PRESENCE_REPLACE"))
// {
// JSONArray presences = raw.getJSONArray("d");
// LOG.trace(String.format("%s -> %s", type, presences.toString()));
// PresenceUpdateHandler handler = new PresenceUpdateHandler(api, responseTotal);
// for (int i = 0; i < presences.length(); i++)
// {
// JSONObject presence = presences.getJSONObject(i);
// handler.handle(presence);
// }
// return;
// }

// Needs special handling due to content of "d" being an array
if (type.equals("PRESENCES_REPLACE"))
{
JSONArray presences = raw.getJSONArray("d");
LOG.trace(String.format("%s -> %s", type, presences.toString()));
PresenceUpdateHandler handler = getHandler("PRESENCE_UPDATE");
for (int i = 0; i < presences.length(); i++)
{
JSONObject presence = presences.getJSONObject(i);
final JSONObject obj = new JSONObject();
obj.put("jda-field", "This was constructed from a PRESENCES_REPLACE payload")
.put("d", presence)
.put("t", "PRESENCE_UPDATE");
handler.handle(responseTotal, obj);
}
return;
}

JSONObject content = raw.getJSONObject("d");
LOG.trace(String.format("%s -> %s", type, content.toString()));
Expand All @@ -973,7 +977,6 @@ protected void handleEvent(JSONObject raw)
//INIT types
case "READY":
api.setStatus(JDA.Status.LOADING_SUBSYSTEMS);
//LOG.debug(String.format("%s -> %s", type, content.toString())); already logged on trace level
processingReady = true;
handleIdentifyRateLimit = false;
sessionId = content.getString("session_id");
Expand Down Expand Up @@ -1349,6 +1352,7 @@ public <T> T getHandler(String type)

private void setupHandlers()
{
final SocketHandler.NOPHandler nopHandler = new SocketHandler.NOPHandler(api);
handlers.put("CHANNEL_CREATE", new ChannelCreateHandler(api));
handlers.put("CHANNEL_DELETE", new ChannelDeleteHandler(api));
handlers.put("CHANNEL_UPDATE", new ChannelUpdateHandler(api));
Expand Down Expand Up @@ -1381,8 +1385,11 @@ private void setupHandlers()
handlers.put("VOICE_STATE_UPDATE", new VoiceStateUpdateHandler(api));

// Unused events
handlers.put("CHANNEL_PINS_UPDATE", new SocketHandler.NOPHandler(api));
handlers.put("WEBHOOKS_UPDATE", new SocketHandler.NOPHandler(api));
handlers.put("CHANNEL_PINS_ACK", nopHandler);
handlers.put("CHANNEL_PINS_UPDATE", nopHandler);
handlers.put("GUILD_INTEGRATIONS_UPDATE", nopHandler);
handlers.put("PRESENCES_REPLACE", nopHandler);
handlers.put("WEBHOOKS_UPDATE", nopHandler);

if (api.getAccountType() == AccountType.CLIENT)
{
Expand All @@ -1395,7 +1402,7 @@ private void setupHandlers()
handlers.put("RELATIONSHIP_REMOVE", new RelationshipRemoveHandler(api));

// Unused client events
handlers.put("MESSAGE_ACK", new SocketHandler.NOPHandler(api));
handlers.put("MESSAGE_ACK", nopHandler);
}
}

Expand Down