Skip to content

Commit

Permalink
Add interaction mentions
Browse files Browse the repository at this point in the history
  • Loading branch information
MinnDevelopment committed Apr 17, 2022
1 parent e931305 commit 17185e4
Show file tree
Hide file tree
Showing 4 changed files with 465 additions and 417 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@
package net.dv8tion.jda.api.interactions.commands;

import gnu.trove.map.TLongObjectMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.interactions.Interaction;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.entities.GuildImpl;
import net.dv8tion.jda.internal.entities.InteractionMentions;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.Objects;

/**
* Name/Value pair for a {@link CommandInteraction} option.
Expand All @@ -43,159 +46,35 @@ public class OptionMapping
private final OptionType type;
private final String name;
private final TLongObjectMap<Object> resolved;
private final MessageMentions mentions;

public OptionMapping(DataObject data, TLongObjectMap<Object> resolved)
public OptionMapping(DataObject data, TLongObjectMap<Object> resolved, JDA jda, Guild guild)
{
this.data = data;
this.type = OptionType.fromKey(data.getInt("type", -1));
this.name = data.getString("name");
this.resolved = resolved;
}

private <T, C extends Collection<T>> C parseMentions(C coll, Pattern pattern, boolean duplicates, Function<Matcher, T> resolver)
{
Matcher matcher = pattern.matcher(getAsString());
while (matcher.find())
{
try
{
T obj = resolver.apply(matcher);
if (obj != null && (duplicates || !coll.contains(obj)))
coll.add(obj);
}
catch (NumberFormatException ignored) {}
}

return coll;
if (type == OptionType.STRING)
mentions = new InteractionMentions(getAsString(), resolved, (JDAImpl) jda, (GuildImpl) guild);
else
mentions = new InteractionMentions("", new TLongObjectHashMap<>(0), (JDAImpl) jda, (GuildImpl) guild);
}

/**
* Resolved {@link Member} mentions for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list.
* Resolved mentions for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns empty lists.
* Mentions are sorted by occurrence.
*
* <p>This only contains members of the guild.
* If the user mentions users from other guilds, they will only be provided by {@link #getMentionedUsers()}.
* <p>Mentioned {@link Member members} and {@link Role roles} are always of the same guild.
* If the interaction {@link Interaction#getUser() user}, mentions users from other guilds, they will only be provided by {@link MessageMentions#getUsers()}.
*
* <p>This is not supported for {@link CommandAutoCompleteInteraction}.
*
* @return {@link List} of {@link Member} the resolved guild user mentions in a string option
* @return {@link MessageMentions} for this option
*/
@Nonnull
public List<Member> getMentionedMembers()
public MessageMentions getMentions()
{
if (type != OptionType.STRING)
return Collections.emptyList();

return parseMentions(new ArrayList<>(), Message.MentionType.USER.getPattern(), false, (matcher) -> {
long id = Long.parseUnsignedLong(matcher.group(1));
Object obj = resolved.get(id);
return obj instanceof Member ? (Member) obj : null;
});
}

/**
* Resolved {@link User} mentions for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list.
* Mentions are sorted by occurrence.
*
* <p>This may also contain users which are not members in the guild!
*
* <p>This is not supported for {@link CommandAutoCompleteInteraction}.
*
* @return {@link List} of {@link User} the resolved guild user mentions in a string option
*/
@Nonnull
public List<User> getMentionedUsers()
{
if (type != OptionType.STRING)
return Collections.emptyList();

return parseMentions(new ArrayList<>(), Message.MentionType.USER.getPattern(), false, (matcher) -> {
long id = Long.parseUnsignedLong(matcher.group(1));
Object obj = resolved.get(id);
if (obj instanceof User)
return (User) obj;
if (obj instanceof Member)
return ((Member) obj).getUser();
return null;
});
}

/**
* Resolved {@link Role} mentions for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list.
* Mentions are sorted by occurrence.
*
* <p>This is not supported for {@link CommandAutoCompleteInteraction}.
*
* @return {@link List} of {@link Role} the resolved guild role mentions in a string option
*/
@Nonnull
public List<Role> getMentionedRoles()
{
if (type != OptionType.STRING)
return Collections.emptyList();

return parseMentions(new ArrayList<>(), Message.MentionType.ROLE.getPattern(), false, (matcher) -> {
long id = Long.parseUnsignedLong(matcher.group(1));
Object obj = resolved.get(id);
return obj instanceof Role ? (Role) obj : null;
});
}

/**
* Resolved {@link GuildChannel} mentions for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list.
* Mentions are sorted by occurrence.
*
* <p>This is not supported for {@link CommandAutoCompleteInteraction}.
*
* @return {@link List} of {@link GuildChannel} the resolved guild channel mentions in a string option
*/
@Nonnull
public List<GuildChannel> getMentionedChannels()
{
if (type != OptionType.STRING)
return Collections.emptyList();

return parseMentions(new ArrayList<>(), Message.MentionType.CHANNEL.getPattern(), false, (matcher) -> {
long id = Long.parseUnsignedLong(matcher.group(1));
Object obj = resolved.get(id);
return obj instanceof GuildChannel ? (GuildChannel) obj : null;
});
}

/**
* All resolved {@link IMentionable mentions} for a {@link OptionType#STRING STRING} option.
* <br>If this option is not of type {@link OptionType#STRING STRING}, this always returns an empty list.
* Mentions are sorted by occurrence.
*
* <p>This is not supported for {@link CommandAutoCompleteInteraction}.
*
* This merges {@link #getMentionedUsers()}, {@link #getMentionedMembers()}, {@link #getMentionedRoles()}, and {@link #getMentionedChannels()}.
*
* @return {@link List} of {@link IMentionable} the resolved mentions in a string option
*/
@Nonnull
public List<IMentionable> getMentions()
{
if (type != OptionType.STRING)
return Collections.emptyList();

List<User> users = getMentionedUsers();
List<Member> members = getMentionedMembers();
List<Role> roles = getMentionedRoles();
List<GuildChannel> channels = getMentionedChannels();
users.removeIf(user -> members.stream().anyMatch(m -> m.getIdLong() == user.getIdLong()));

List<IMentionable> mentions = new ArrayList<>(users.size() + members.size() + roles.size() + channels.size());
mentions.addAll(users);
mentions.addAll(members);
mentions.addAll(roles);
mentions.addAll(channels);
mentions.sort(Comparator.comparingInt(mention -> getAsString().indexOf(mention.getId())));

return mentions;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* Copyright 2015 Austin Keener, Michael Ritter, Florian Spieß, and the JDA contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.dv8tion.jda.internal.entities;

import gnu.trove.map.TLongObjectMap;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.utils.MiscUtil;
import net.dv8tion.jda.internal.JDAImpl;
import net.dv8tion.jda.internal.entities.mentions.AbstractMentions;
import org.apache.commons.collections4.CollectionUtils;

import java.util.regex.Matcher;

public class InteractionMentions extends AbstractMentions
{
protected final TLongObjectMap<Object> resolved;

public InteractionMentions(String content, TLongObjectMap<Object> resolved, JDAImpl jda, GuildImpl guild)
{
super(content, jda, guild, false);
this.resolved = resolved;
}

protected User matchUser(Matcher matcher)
{
long userId = MiscUtil.parseSnowflake(matcher.group(1));
Object it = resolved.get(userId);
return it instanceof User
? (User) it
: it instanceof Member
? ((Member) it).getUser()
: null;
}

protected Member matchMember(Matcher matcher)
{
long userId = MiscUtil.parseSnowflake(matcher.group(1));
Object it = resolved.get(userId);
return it instanceof Member ? (Member) it : null;
}

protected GuildChannel matchChannel(Matcher matcher)
{
long channelId = MiscUtil.parseSnowflake(matcher.group(1));
Object it = resolved.get(channelId);
return it instanceof GuildChannel ? (GuildChannel) it : null;
}

protected Role matchRole(Matcher matcher)
{
long roleId = MiscUtil.parseSnowflake(matcher.group(1));
Object it = resolved.get(roleId);
return it instanceof Role ? (Role) it : null;
}

protected Emote matchEmote(Matcher m)
{
long emoteId = MiscUtil.parseSnowflake(m.group(2));
String name = m.group(1);
boolean animated = m.group(0).startsWith("<a:");
Emote emote = getJDA().getEmoteById(emoteId);
if (emote == null)
emote = new EmoteImpl(emoteId, jda).setName(name).setAnimated(animated);
return emote;
}

protected boolean isUserMentioned(IMentionable mentionable)
{
return resolved.containsKey(mentionable.getIdLong());
}

protected boolean isRoleMentioned(IMentionable mentionable)
{
if (mentionable instanceof Role)
{
return getRoles().contains(mentionable);
}
else if (mentionable instanceof Member)
{
final Member member = (Member) mentionable;
return CollectionUtils.containsAny(getRoles(), member.getRoles());
}
else if (guild != null && mentionable instanceof User)
{
final Member member = guild.getMember((User) mentionable);
return member != null && CollectionUtils.containsAny(getRoles(), member.getRoles());
}
return false;
}
}
Loading

0 comments on commit 17185e4

Please sign in to comment.