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

Implement per-guild avatars #1649

Merged
merged 12 commits into from
Oct 23, 2021
39 changes: 39 additions & 0 deletions src/main/java/net/dv8tion/jda/api/entities/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@
*/
public interface Member extends IMentionable, IPermissionHolder, IFakeable
{
/** Template for {@link #getAvatarUrl()}. */
String AVATAR_URL = "https://cdn.discordapp.com/guilds/%s/users/%s/avatars/%s.%s";

/**
* The user wrapped by this Entity.
*
Expand Down Expand Up @@ -207,6 +210,42 @@ public interface Member extends IMentionable, IPermissionHolder, IFakeable
@Nonnull
String getEffectiveName();

/**
* The Discord Id for this member's per guild avatar image.
* If the member has not set a per guild avatar, this will return null.
*
* @return Possibly-null String containing the {@link net.dv8tion.jda.api.entities.Member} per guild avatar id.
*/
@Nullable
String getAvatarId();

/**
* The URL for the member's per guild avatar image.
* If the member has not set a per guild avatar, this will return null.
*
* @return Possibly-null String containing the {@link net.dv8tion.jda.api.entities.Member} per guild avatar url.
*/
@Nullable
default String getAvatarUrl()
{
String avatarId = getAvatarId();
return avatarId == null ? null : String.format(AVATAR_URL, getGuild().getId(), getId(), avatarId, avatarId.startsWith("a_") ? "gif" : "png");
}

/**
* The URL for the member's effective avatar image.
* If they do not have a per guild avatar set, this will return the URL of
* their effective {@link User} avatar.
*
* @return Never-null String containing the {@link net.dv8tion.jda.api.entities.Member} avatar url.
*/
@Nonnull
default String getEffectiveAvatarUrl()
{
String avatarUrl = getAvatarUrl();
return avatarUrl == null ? getUser().getEffectiveAvatarUrl() : avatarUrl;
}

/**
* The roles applied to this Member.
* <br>The roles are ordered based on their position. The highest role being at index 0
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.dv8tion.jda.api.events.guild.member.update;
RedDaedalus marked this conversation as resolved.
Show resolved Hide resolved

import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Member;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* Indicates that a {@link net.dv8tion.jda.api.entities.Member Member} updated their {@link net.dv8tion.jda.api.entities.Guild Guild} avatar.
*
* <p>Can be used to retrieve members who change their per guild avatar, triggering guild, the old avatar id and the new avatar id.
*
* <p>Identifier: {@code avatar}
*
* <h2>Requirements</h2>
*
* <p>This event requires the {@link net.dv8tion.jda.api.requests.GatewayIntent#GUILD_MEMBERS GUILD_MEMBERS} intent to be enabled.
* <br>{@link net.dv8tion.jda.api.JDABuilder#createDefault(String) createDefault(String)} and
* {@link net.dv8tion.jda.api.JDABuilder#createLight(String) createLight(String)} disable this by default!
*
* <p>Additionally, this event requires the {@link net.dv8tion.jda.api.utils.MemberCachePolicy MemberCachePolicy}
* to cache the updated members. Discord does not specifically tell us about the updates, but merely tells us the
* member was updated and gives us the updated member object. In order to fire a specific event like this we
* need to have the old member cached to compare against.
*/
public class GuildMemberUpdateAvatarEvent extends GenericGuildMemberUpdateEvent<String>
{
public static final String IDENTIFIER = "avatar";
RedDaedalus marked this conversation as resolved.
Show resolved Hide resolved

public GuildMemberUpdateAvatarEvent(@NotNull JDA api, long responseNumber, @NotNull Member member, @Nullable String oldAvatarId)
{
super(api, responseNumber, member, oldAvatarId, member.getAvatarId(), IDENTIFIER);
}

/**
* The old avatar id
*
* @return The old avatar id
*/
public String getOldAvatarId()
{
return getOldValue();
}

/**
* The new avatar id
*
* @return The new avatar id
*/
public String getNewAvatarId()
RedDaedalus marked this conversation as resolved.
Show resolved Hide resolved
{
return getNewValue();
}
}
6 changes: 2 additions & 4 deletions src/main/java/net/dv8tion/jda/api/hooks/ListenerAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@
import net.dv8tion.jda.api.events.guild.invite.GuildInviteCreateEvent;
import net.dv8tion.jda.api.events.guild.invite.GuildInviteDeleteEvent;
import net.dv8tion.jda.api.events.guild.member.*;
import net.dv8tion.jda.api.events.guild.member.update.GenericGuildMemberUpdateEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateBoostTimeEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdatePendingEvent;
import net.dv8tion.jda.api.events.guild.member.update.*;
import net.dv8tion.jda.api.events.guild.override.GenericPermissionOverrideEvent;
import net.dv8tion.jda.api.events.guild.override.PermissionOverrideCreateEvent;
import net.dv8tion.jda.api.events.guild.override.PermissionOverrideDeleteEvent;
Expand Down Expand Up @@ -348,6 +345,7 @@ public void onGuildMemberRoleRemove(@Nonnull GuildMemberRoleRemoveEvent event) {
//Guild Member Update Events
public void onGuildMemberUpdate(@Nonnull GuildMemberUpdateEvent event) {}
public void onGuildMemberUpdateNickname(@Nonnull GuildMemberUpdateNicknameEvent event) {}
public void onGuildMemberUpdateAvatar(@Nonnull GuildMemberUpdateAvatarEvent event) {}
public void onGuildMemberUpdateBoostTime(@Nonnull GuildMemberUpdateBoostTimeEvent event) {}
public void onGuildMemberUpdatePending(@Nonnull GuildMemberUpdatePendingEvent event) {}

Expand Down
16 changes: 16 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/entities/EntityBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import net.dv8tion.jda.api.entities.MessageEmbed.*;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleAddEvent;
import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateAvatarEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateBoostTimeEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent;
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdatePendingEvent;
Expand Down Expand Up @@ -468,6 +469,8 @@ public MemberImpl createMember(GuildImpl guild, DataObject memberJson, DataObjec
// Create a brand new member
member = new MemberImpl(guild, user);
member.setNickname(memberJson.getString("nick", null));
member.setAvatarId(memberJson.getString("avatar", null));

long epoch = 0;
if (!memberJson.isNull("premium_since"))
{
Expand Down Expand Up @@ -562,6 +565,19 @@ public void updateMember(GuildImpl guild, MemberImpl member, DataObject content,
member, oldNick));
}
}
if (content.hasKey("avatar"))
{
String oldAvatarId = member.getAvatarId();
String newAvatarId = content.getString("avatar", null);
if (!Objects.equals(oldAvatarId, newAvatarId))
{
member.setAvatarId(newAvatarId);
getJDA().handleEvent(
new GuildMemberUpdateAvatarEvent(
getJDA(), responseNumber,
member, oldAvatarId));
}
}
if (content.hasKey("premium_since"))
{
long epoch = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@ public AuditableRestAction<Void> modifyNickname(@Nonnull Member member, String n

Route.CompiledRoute route;
if (member.equals(getSelfMember()))
route = Route.Guilds.MODIFY_SELF_NICK.compile(getId());
route = Route.Guilds.MODIFY_SELF.compile(getId());
RedDaedalus marked this conversation as resolved.
Show resolved Hide resolved
else
route = Route.Guilds.MODIFY_MEMBER.compile(getId(), member.getUser().getId());

Expand Down
13 changes: 13 additions & 0 deletions src/main/java/net/dv8tion/jda/internal/entities/MemberImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public class MemberImpl implements Member
private GuildImpl guild;
private User user;
private String nickname;
private String avatarId;
private long joinDate, boostDate;
private boolean pending = false;

Expand Down Expand Up @@ -165,6 +166,12 @@ public String getNickname()
return nickname;
}

@Override
public String getAvatarId()
{
return avatarId;
}

@Nonnull
@Override
public String getEffectiveName()
Expand Down Expand Up @@ -359,6 +366,12 @@ public MemberImpl setNickname(String nickname)
return this;
}

public MemberImpl setAvatarId(String avatarId)
{
this.avatarId = avatarId;
return this;
}

public MemberImpl setJoinDate(long joinDate)
{
this.joinDate = joinDate;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/net/dv8tion/jda/internal/requests/Route.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package net.dv8tion.jda.internal.requests;

import net.dv8tion.jda.annotations.ForRemoval;
import net.dv8tion.jda.internal.utils.Checks;
import net.dv8tion.jda.internal.utils.Helpers;

Expand Down Expand Up @@ -136,7 +137,7 @@ public static class Guilds
public static final Route MODIFY_MEMBER = new Route(PATCH, "guilds/{guild_id}/members/{user_id}");
public static final Route ADD_MEMBER = new Route(PUT, "guilds/{guild_id}/members/{user_id}");
public static final Route GET_MEMBER = new Route(GET, "guilds/{guild_id}/members/{user_id}");
public static final Route MODIFY_SELF_NICK = new Route(PATCH, "guilds/{guild_id}/members/@me/nick");
public static final Route MODIFY_SELF = new Route(PATCH, "guilds/{guild_id}/members/@me");
RedDaedalus marked this conversation as resolved.
Show resolved Hide resolved
public static final Route PRUNABLE_COUNT = new Route(GET, "guilds/{guild_id}/prune");
public static final Route PRUNE_MEMBERS = new Route(POST, "guilds/{guild_id}/prune");
public static final Route GET_WEBHOOKS = new Route(GET, "guilds/{guild_id}/webhooks");
Expand Down