如何使用 JDA 添加多个反应以嵌入消息

Posted

技术标签:

【中文标题】如何使用 JDA 添加多个反应以嵌入消息【英文标题】:How to add multiple reactions to embed message with JDA 【发布时间】:2020-04-12 10:13:26 【问题描述】:

我正在尝试使机器人响应带有嵌入消息的命令,该消息下有多个反应。 我已经让它添加了 1 个反应,但我还需要添加不同的反应,就像这样:(http://prntscr.com/qd8da4) 网上有很多教程添加了 1 个反应,但没有一个添加多个反应。

我正在使用最新版本的 Discord JDA。

我目前拥有的代码是:

public void onGuildMessageReceived(GuildMessageReceivedEvent event) 
    String[] args = event.getMessage().getContentRaw().split("\\s+");

    if (args[0].equalsIgnoreCase(DiscordBot.prefix + "info")) 
        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss z");
        Date date = new Date(System.currentTimeMillis());

        MessageChannel channel = event.getChannel(); // Channel the command was sent in.

        EmbedBuilder info = new EmbedBuilder();
        info.setTitle("Server Info");
        info.setDescription("Info About the bot.");
        info.addField("Creator", "Name", false);
        info.setColor(0xf45642);
        info.setTimestamp(Instant.now());
        channel.sendMessage(info.build()).queue(message -> message.addReaction("✔️").queue());
    

【问题讨论】:

这能回答你的问题吗? How to add reaction to an embed message JDA 【参考方案1】:

我自己也遇到了同样的问题,因为没有真正的本地方式来做我想做的事情,即在点击反应时执行某些机器人操作。

我最终创建了几个帮助程序类,允许程序在点击反应时进行回调:

以下是会监听响应点击的响应:

public class ReactionListener<T> 

  private final Map<String, Consumer<Message>> reactions;
  private final long userId;
  private volatile T data;
  private Long expiresIn, lastAction;
  private boolean active;

  public ReactionListener(long userId, T data) 
    this.data = data;
    this.userId = userId;
    reactions = new LinkedHashMap<>();
    active = true;
    lastAction = System.currentTimeMillis();
    expiresIn = TimeUnit.MINUTES.toMillis(5);
  

  public boolean isActive() 
    return active;
  

  public void disable() 
    this.active = false;
  

  /**
   * The time after which this listener expires which is now + specified time
   * Defaults to now+5min
   *
   * @param timeUnit time units
   * @param time     amount of time units
   */
  public void setExpiresIn(TimeUnit timeUnit, long time) 
    expiresIn = timeUnit.toMillis(time);
  

  /**
   * Check if this listener has specified emote
   *
   * @param emote the emote to check for
   * @return does this listener do anything with this emote?
   */
  public boolean hasReaction(String emote) 
    return reactions.containsKey(emote);
  

  /**
   * React to the reaction :')
   *
   * @param emote   the emote used
   * @param message the message bound to the reaction
   */
  public void react(String emote, Message message) 
    if (hasReaction(emote)) reactions.get(emote).accept(message);
  

  public T getData() 
    return data;
  

  public void setData(T data) 
    this.data = data;
  

  /**
   * Register a consumer for a specified emote
   * Multiple emote's will result in overriding the old one
   *
   * @param emote    the emote to respond to
   * @param consumer the behaviour when emote is used
   */
  public void registerReaction(String emote, Consumer<Message> consumer) 
    reactions.put(emote, consumer);
  

  /**
   * @return list of all emotes used in this reaction listener
   */
  public Set<String> getEmotes() 
    return reactions.keySet();
  

  /**
   * updates the timestamp when the reaction was last accessed
   */
  public void updateLastAction() 
    lastAction = System.currentTimeMillis();
  

  /**
   * When does this reaction listener expire?
   *
   * @return timestamp in millis
   */
  public Long getExpiresInTimestamp() 
    return lastAction + expiresIn;
  

  public long getUserId() 
    return userId;
  

接下来我们要处理点击时的反应。此方法需要当前登录的 userId 以确保仅记录用户的操作而不会记录其他人。如果其他人也可以反应,它可以修改为全局。:

public class ReactionHandler 

  private final ConcurrentHashMap<Long, ConcurrentHashMap<Long, ReactionListener<?>>> reactions;

  private ReactionHandler() 
    reactions = new ConcurrentHashMap<>();
  

  public synchronized void addReactionListener(long guildId, Message message, ReactionListener<?> handler) 
    addReactionListener(guildId, message, handler, true);
  

  public synchronized void addReactionListener(long guildId, Message message, ReactionListener<?> handler, boolean queue) 
    if (handler == null) 
      return;
    
    if (message.getChannelType().equals(ChannelType.TEXT)) 
      if (!PermissionUtil.checkPermission(message.getTextChannel(), message.getGuild().getSelfMember(), Permission.MESSAGE_ADD_REACTION)) 
        return;
      
    
    if (!reactions.containsKey(guildId)) 
      reactions.put(guildId, new ConcurrentHashMap<>());
    
    if (!reactions.get(guildId).containsKey(message.getIdLong())) 
      for (String emote : handler.getEmotes()) 
        RestAction<Void> action = message.addReaction(emote);
        if (queue) action.queue(); else action.complete();
      
    
    reactions.get(guildId).put(message.getIdLong(), handler);
  

  public synchronized void removeReactionListener(long guildId, long messageId) 
    if (!reactions.containsKey(guildId)) return;
    reactions.get(guildId).remove(messageId);
  

  /**
   * Handles the reaction
   *
   * @param channel   TextChannel of the message
   * @param messageId id of the message
   * @param userId    id of the user reacting
   * @param reaction  the reaction
   */
  public void handle(TextChannel channel, long messageId, long userId, MessageReaction reaction) 
    ReactionListener<?> listener = reactions.get(channel.getGuild().getIdLong()).get(messageId);
    if (!listener.isActive() || listener.getExpiresInTimestamp() < System.currentTimeMillis()) 
      reactions.get(channel.getGuild().getIdLong()).remove(messageId);
     else if ((listener.hasReaction(reaction.getReactionEmote().getName())) && listener.getUserId() == userId) 
      reactions.get(channel.getGuild().getIdLong()).get(messageId).updateLastAction();
      Message message = channel.retrieveMessageById(messageId).complete();
      listener.react(reaction.getReactionEmote().getName(), message);
    
  

  /**
   * Do we have an event for a message?
   *
   * @param guildId   discord guild-id of the message
   * @param messageId id of the message
   * @return do we have an handler?
   */
  public boolean canHandle(long guildId, long messageId) 
    return reactions.containsKey(guildId) && reactions.get(guildId).containsKey(messageId);
  

  public synchronized void removeGuild(long guildId) 
    reactions.remove(guildId);
  

  /**
   * Delete expired handlers
   */
  public synchronized void cleanCache() 
    long now = System.currentTimeMillis();
    for (Iterator<Map.Entry<Long, ConcurrentHashMap<Long, ReactionListener<?>>>> iterator = reactions.entrySet().iterator(); iterator.hasNext(); ) 
      Map.Entry<Long, ConcurrentHashMap<Long, ReactionListener<?>>> mapEntry = iterator.next();
      mapEntry.getValue().values().removeIf(listener -> !listener.isActive() || listener.getExpiresInTimestamp() < now);
      if (mapEntry.getValue().values().isEmpty()) 
        reactions.remove(mapEntry.getKey());
      
    
  

然后,有了这两个库,我们就可以在我们的消息中实现它们。

channel.sendMessage(info.build()).queue((msg) -> 

  ReactionListener<String> handler = new ReactionListener<>(userId, msg.getId());
  handler.setExpiresIn(TimeUnit.MINUTES, 1);
  handler.registerReaction("✔️", (ret) -> foo());
  handler.registerReaction("X",  (ret) -> bar());

  reactionHandler.addReactionListener(guild.getIdLong(), msg, handler);
);

【讨论】:

以上是关于如何使用 JDA 添加多个反应以嵌入消息的主要内容,如果未能解决你的问题,请参考以下文章

如何添加对嵌入消息 JDA 的反应

如何根据反应发送消息,然后根据用户消息发送另一个消息?使用 JDA

如何在 Discord JDA 嵌入式消息中使用本地文件作为缩略图?

JDA Discord 向消息添加反应

不和谐 Java JDA |哪个反应是addet

Discord JDA 在嵌入中使用本地图像?