From 06d168935dba06349a65eb817ddd1a8dfe838126 Mon Sep 17 00:00:00 2001 From: heavyrian2012 Date: Sat, 6 Sep 2025 21:36:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81@=E4=B8=8D=E5=9C=A8=E7=BE=A4?= =?UTF-8?q?=E7=BB=84=E4=B8=AD=E7=9A=84=E6=9C=BA=E5=99=A8=E4=BA=BA=E5=92=8C?= =?UTF-8?q?=E5=8D=95=E4=BA=BA=E4=BC=9A=E8=AF=9D=E6=94=AF=E6=8C=81@?= =?UTF-8?q?=E6=9C=BA=E5=99=A8=E4=BA=BA=EF=BC=8C=E6=94=AF=E6=8C=81=E6=9C=BA?= =?UTF-8?q?=E5=99=A8=E4=BA=BA=E5=9B=9E=E5=A4=8D=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action/robot/ReplyMessageAction.java | 103 ++++++ .../imhandler/ReplyMessageHandler.java | 348 ++++++++++++++++++ .../persistence/MemoryMessagesStore.java | 41 ++- .../src/main/java/win/liyufan/im/IMTopic.java | 1 + 4 files changed, 488 insertions(+), 5 deletions(-) create mode 100755 broker/src/main/java/com/xiaoleilu/loServer/action/robot/ReplyMessageAction.java create mode 100644 broker/src/main/java/io/moquette/imhandler/ReplyMessageHandler.java diff --git a/broker/src/main/java/com/xiaoleilu/loServer/action/robot/ReplyMessageAction.java b/broker/src/main/java/com/xiaoleilu/loServer/action/robot/ReplyMessageAction.java new file mode 100755 index 00000000..a6ff64bb --- /dev/null +++ b/broker/src/main/java/com/xiaoleilu/loServer/action/robot/ReplyMessageAction.java @@ -0,0 +1,103 @@ +/* + * This file is part of the Wildfire Chat package. + * (c) Heavyrain2012 + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +package com.xiaoleilu.loServer.action.robot; + + +import cn.wildfirechat.common.APIPath; +import cn.wildfirechat.common.ErrorCode; +import cn.wildfirechat.pojos.Conversation; +import cn.wildfirechat.pojos.ReplyMessageData; +import cn.wildfirechat.pojos.SendMessageData; +import cn.wildfirechat.pojos.SendMessageResult; +import cn.wildfirechat.proto.ProtoConstants; +import cn.wildfirechat.proto.WFCMessage; +import com.xiaoleilu.loServer.RestResult; +import com.xiaoleilu.loServer.annotation.HttpMethod; +import com.xiaoleilu.loServer.annotation.Route; +import com.xiaoleilu.loServer.handler.Request; +import com.xiaoleilu.loServer.handler.Response; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpResponseStatus; +import org.jetbrains.annotations.NotNull; +import win.liyufan.im.IMTopic; + +import java.util.Arrays; + +@Route(APIPath.Robot_Message_Reply) +@HttpMethod("POST") +public class ReplyMessageAction extends RobotAction { + + @Override + public boolean isTransactionAction() { + return true; + } + + @Override + public boolean action(Request request, Response response) { + if (request.getNettyRequest() instanceof FullHttpRequest) { + ReplyMessageData replyMessageData = getRequestBody(request.getNettyRequest(), ReplyMessageData.class); + if (ReplyMessageData.isValide(replyMessageData)) { + WFCMessage.Message message = messagesStore.getMessage(replyMessageData.getMessageUid()); + ErrorCode errorCode = null; + if(message == null) { + errorCode = ErrorCode.ERROR_CODE_NOT_EXIST; + } else if(!message.getContent().getMentionedTargetList().contains(robot.getUid())) { + errorCode = ErrorCode.ERROR_CODE_INVALID_MESSAGE; + } else if(message.getConversation().getType() != ProtoConstants.ConversationType.ConversationType_Private && message.getConversation().getType() != ProtoConstants.ConversationType.ConversationType_Group) { + errorCode = ErrorCode.ERROR_CODE_NOT_IMPLEMENT; + } else if(System.currentTimeMillis() - message.getServerTimestamp() > 15*60*1000) { + errorCode = ErrorCode.ERROR_CODE_RECALL_TIME_EXPIRED; + } + + if(errorCode != null) { + response.setStatus(HttpResponseStatus.OK); + RestResult result = RestResult.resultOf(errorCode); + response.setContent(gson.toJson(result)); + return true; + } + + SendMessageData sendMessageData = getSendMessageData(message, replyMessageData); + WFCMessage.Message newMsg = sendMessageData.toProtoMessage().toBuilder().setMessageId(message.getMessageId()).build(); + sendApiRequest(response, IMTopic.RobotReplyMessageTopic, newMsg.toByteArray(), result -> { + ByteBuf byteBuf = Unpooled.buffer(); + byteBuf.writeBytes(result); + ErrorCode ec = ErrorCode.fromCode(byteBuf.readByte()); + if (ec == ErrorCode.ERROR_CODE_SUCCESS) { + long messageId = byteBuf.readLong(); + long timestamp = byteBuf.readLong(); + sendResponse(response, null, new SendMessageResult(messageId, timestamp)); + } else { + sendResponse(response, ec, null); + } + }); + return false; + } else { + setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response); + } + } + return true; + } + + private @NotNull SendMessageData getSendMessageData(WFCMessage.Message message, ReplyMessageData replyMessageData) { + SendMessageData sendMessageData = new SendMessageData(); + sendMessageData.setSender(robot.getUid()); + Conversation conversation = new Conversation(); + conversation.setType(message.getConversation().getType()); + conversation.setLine(message.getConversation().getLine()); + conversation.setTarget(message.getConversation().getTarget()); + sendMessageData.setConv(conversation); + sendMessageData.setPayload(replyMessageData.getPayload()); + if(replyMessageData.isOnly2Sender()) { + sendMessageData.setToUsers(Arrays.asList(message.getFromUser())); + } + return sendMessageData; + } +} diff --git a/broker/src/main/java/io/moquette/imhandler/ReplyMessageHandler.java b/broker/src/main/java/io/moquette/imhandler/ReplyMessageHandler.java new file mode 100644 index 00000000..e5697dc2 --- /dev/null +++ b/broker/src/main/java/io/moquette/imhandler/ReplyMessageHandler.java @@ -0,0 +1,348 @@ +/* + * This file is part of the Wildfire Chat package. + * (c) Heavyrain2012 + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +package io.moquette.imhandler; + +import cn.wildfirechat.common.ErrorCode; +import cn.wildfirechat.pojos.MessagePayload; +import cn.wildfirechat.proto.ProtoConstants; +import cn.wildfirechat.proto.WFCMessage; +import com.hazelcast.util.StringUtil; +import io.moquette.BrokerConstants; +import io.moquette.spi.impl.Qos1PublishHandler; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import win.liyufan.im.*; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_NOT_IN_GROUP; +import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS; + +@Handler(value = IMTopic.RobotReplyMessageTopic) +public class ReplyMessageHandler extends IMHandler { + private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。 + private String mForwardUrl = null; + private String mSensitiveMessageForwardUrl = null; + private Set mForwardMessageTypes = new HashSet<>(); + private Set mForwardExcludeMessageTypes = new HashSet<>(); + private String mMentionForwardUrl = null; + private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。 + private boolean mBlacklistAllowSend2Black = true; + private boolean mNoForwardAdminMessage = false; + private boolean mAllowSend2ForbiddenUser = false; + + private String mRemoteSensitiveServerUrl = null; + private Set mRemoteSensitiveMessageTypes; + private boolean mWaitRemoteSensitiveServerResponse = false; + + public ReplyMessageHandler() { + super(); + + mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url); + if (!StringUtil.isNullOrEmpty(mForwardUrl)) { + String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types); + parseTypes(mForwardMessageTypes, forwardTypes); + String excludeTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Exclude_Types); + parseTypes(mForwardExcludeMessageTypes, excludeTypes); + } + mSensitiveMessageForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Sensitive_Forward_Url); + mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url); + + try { + mSensitiveType = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Filter_Type)); + } catch (Exception e) { + e.printStackTrace(); + Utility.printExecption(LOG, e); + } + + try { + mBlacklistStrategy = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Blacklist_Strategy)); + } catch (Exception e) { + e.printStackTrace(); + Utility.printExecption(LOG, e); + } + + try { + mBlacklistAllowSend2Black = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Blacklist_Allow_Send, "true")); + } catch (Exception e) { + } + + try { + mRemoteSensitiveServerUrl = mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Server_URL); + if(!StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) { + mWaitRemoteSensitiveServerResponse = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Fail_When_Matched, "false")); + String types = mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Message_Type, ""); + mRemoteSensitiveMessageTypes = new HashSet<>(); + if(!StringUtil.isNullOrEmpty(types)) { + String[] ts = types.split(","); + for (String t:ts) { + mRemoteSensitiveMessageTypes.add(Integer.parseInt(t.trim())); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + Utility.printExecption(LOG, e); + } + + try { + mNoForwardAdminMessage = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_NO_Forward_Admin_Message, "false")); + } catch (Exception e) { + e.printStackTrace(); + Utility.printExecption(LOG, e); + } + + try { + mAllowSend2ForbiddenUser = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGES_ALLOW_SEND_TO_FORBIDDEN_USER, "false")); + } catch (Exception e) {} + } + + private void parseTypes(Collection collection, String types) { + try { + if (!StringUtil.isNullOrEmpty(types)) { + String[] tss = types.split(","); + for (String ts:tss) { + if(ts.contains("-")) { + String[] ss = ts.split("-"); + if(ss.length == 2) { + int begin = Integer.parseInt(ss[0]); + int end = Integer.parseInt(ss[1]); + for (int i = begin; i <= end ; i++) { + collection.add(i); + } + } + } else { + collection.add(Integer.parseInt(ts.trim())); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.Message message, Qos1PublishHandler.IMCallback callback) { + if(requestSourceType != ProtoConstants.RequestSourceType.Request_From_Robot) { + return ErrorCode.ERROR_CODE_NOT_IMPLEMENT; + } + + ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS; + + if (message != null) { + if(message.getContent().getContent().length() + + message.getContent().getSearchableContent().length() + + message.getContent().getData().size() + + message.getContent().getExtra().length() + + message.getContent().getPushContent().length() + + message.getContent().getPushData().length() > 64 * 1024) { + return ErrorCode.ERROR_CODE_MESSAGE_TOO_LARGE; + } + + boolean ignoreMsg = false; + + // 不能在端上直接发送撤回和群通知 + if (message.getContent().getType() == 80 || message.getContent().getType() == 81 || (message.getContent().getType() >= 100 && message.getContent().getType() < 200)) { + return ErrorCode.INVALID_PARAMETER; + } + + if(m_messagesStore.getClientForbiddenSendTypes().contains(message.getContent().getType())) { + return ErrorCode.ERROR_CODE_NOT_RIGHT; + } + + int userStatus = m_messagesStore.getUserStatus(fromUser); + if (userStatus == ProtoConstants.UserStatus.Muted || userStatus == ProtoConstants.UserStatus.Forbidden) { + if(!m_messagesStore.getGlobalMuteExceptionTypes().contains(message.getContent().getType())) { + return ErrorCode.ERROR_CODE_FORBIDDEN_SEND_MSG; + } + } + + if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private) { + if(!m_messagesStore.getBlackListExceptionTypes().contains(message.getContent().getType())) { + errorCode = m_messagesStore.isAllowUserMessage(message.getConversation().getTarget(), fromUser, message.getConversation().getLine()); + if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) { + if (errorCode == ErrorCode.ERROR_CODE_IN_BLACK_LIST && mBlacklistStrategy != ProtoConstants.BlacklistStrategy.Message_Reject) { + ignoreMsg = true; + errorCode = ErrorCode.ERROR_CODE_SUCCESS; + } else { + return errorCode; + } + } + + if(!mBlacklistAllowSend2Black) { + errorCode = m_messagesStore.isBlacked(fromUser, message.getConversation().getTarget()); + if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) { + return errorCode; + } + } + } + + if (!mAllowSend2ForbiddenUser) { + userStatus = m_messagesStore.getUserStatus(message.getConversation().getTarget()); + if (userStatus == ProtoConstants.UserStatus.Forbidden) { + return ErrorCode.ERROR_CODE_USER_FORBIDDEN; + } + } + } + + if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group ) { + if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) { + errorCode = m_messagesStore.canSendMessageInGroup(fromUser, message.getConversation().getTarget()); + if(errorCode == ERROR_CODE_NOT_IN_GROUP) { + errorCode = ERROR_CODE_SUCCESS; + } + if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) { + return errorCode; + } + } + } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom) { + if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) { + if (!m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) { + return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM; + } + } + } else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) { + if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) { + if (!m_messagesStore.canSendMessageInChannel(fromUser, message.getConversation().getTarget())) { + return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL; + } + } + } + + if(message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private) { + long beforeMessageId = message.getMessageId(); + WFCMessage.Message replyMsg = m_messagesStore.getMessage(beforeMessageId); + message = message.toBuilder().setConversation(message.getConversation().toBuilder().setTarget(replyMsg.getFromUser() + "|" + replyMsg.getConversation().getTarget())).build(); + } + + long timestamp = System.currentTimeMillis(); + final long messageId; + try { + messageId = MessageShardingUtil.generateId(); + } catch (Exception e) { + e.printStackTrace(); + return ErrorCode.ERROR_CODE_SERVER_ERROR; + } + message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build(); + + + if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !mForwardExcludeMessageTypes.contains(message.getContent().getType())) { + publisher.forwardMessage(message, mForwardUrl, clientID); + } + + if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0) { + publisher.forwardMessage(message, mMentionForwardUrl, clientID); + } + + + if(StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) { + Set matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent()); + if (matched != null && !matched.isEmpty()) { + m_messagesStore.storeSensitiveMessage(message); + if (!StringUtil.isNullOrEmpty(mSensitiveMessageForwardUrl)) { + publisher.forwardMessage(message, mSensitiveMessageForwardUrl, clientID); + } + if (mSensitiveType == 0) { + errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED; + } else if (mSensitiveType == 1) { + ignoreMsg = true; + } else if (mSensitiveType == 2) { + String text = message.getContent().getSearchableContent(); + for (String word : matched) { + text = text.replace(word, "***"); + } + message = message.toBuilder().setContent(message.getContent().toBuilder().setSearchableContent(text).build()).build(); + } + } + } else { + if(mRemoteSensitiveMessageTypes.contains(message.getContent().getType())) { + final WFCMessage.Message finalMsg = message; + publisher.forwardMessageWithCallback(message, clientID, mRemoteSensitiveServerUrl, new HttpUtils.HttpCallback() { + @Override + public void onSuccess(String content) { + if(StringUtil.isNullOrEmpty(content)) { + saveAndPublish(fromUser, clientID, finalMsg, requestSourceType); + } else { + MessagePayload payload = GsonUtil.gson.fromJson(content, MessagePayload.class); + if (payload != null && payload.getType() > 0) { + WFCMessage.Message newMsg = finalMsg.toBuilder().setContent(payload.toProtoMessageContent()).build(); + saveAndPublish(fromUser, clientID, newMsg, requestSourceType); + } else { + LOG.error("Response content {} from censor is invalid payload, ignore response and send original message", content); + saveAndPublish(fromUser, clientID, finalMsg, requestSourceType); + } + } + if(mWaitRemoteSensitiveServerResponse) { + ByteBuf ackPayload = Unpooled.buffer(21); + ErrorCode errorCode = ERROR_CODE_SUCCESS; + ackPayload.writeByte(errorCode.getCode()); + ackPayload.writeLong(messageId); + ackPayload.writeLong(timestamp); + callback.onIMHandled(ErrorCode.ERROR_CODE_SUCCESS, ackPayload); + } + } + + @Override + public void onFailure(int statusCode, String errorMessage) { + ByteBuf ackPayload = null; + ErrorCode errorCode = ERROR_CODE_SUCCESS; + if(statusCode == 403) { + if(mWaitRemoteSensitiveServerResponse) { + errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED; + ackPayload = Unpooled.buffer(1); + ackPayload.writeByte(errorCode.getCode()); + } + } else { + LOG.warn("Failed to censor message with status {}, errorMessage {}. Send the original message", statusCode, errorMessage); + saveAndPublish(fromUser, clientID, finalMsg, requestSourceType); + + if(mWaitRemoteSensitiveServerResponse) { + errorCode = ERROR_CODE_SUCCESS; + ackPayload = Unpooled.buffer(21); + ackPayload.writeByte(errorCode.getCode()); + ackPayload.writeLong(messageId); + ackPayload.writeLong(timestamp); + } + } + + if(mWaitRemoteSensitiveServerResponse) { + callback.onIMHandled(errorCode, ackPayload); + } + } + }); + + if(mWaitRemoteSensitiveServerResponse) { + ackPayload.clear(); + return ErrorCode.INVALID_ASYNC_HANDLING; + } else { + ackPayload = ackPayload.capacity(20); + ackPayload.writeLong(messageId); + ackPayload.writeLong(timestamp); + return ErrorCode.ERROR_CODE_SUCCESS; + } + } + } + + if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) { + if(!ignoreMsg) { + saveAndPublish(fromUser, clientID, message, requestSourceType); + } + ackPayload = ackPayload.capacity(20); + ackPayload.writeLong(messageId); + ackPayload.writeLong(timestamp); + } + } else { + errorCode = ErrorCode.ERROR_CODE_INVALID_MESSAGE; + } + return errorCode; + } + +} diff --git a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java index a990ec3e..47ce864f 100755 --- a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java +++ b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java @@ -742,13 +742,34 @@ public class MemoryMessagesStore implements IMessagesStore { int pullType = ProtoConstants.PullType.Pull_Normal; if (type == ProtoConstants.ConversationType.ConversationType_Private) { - notifyReceivers.add(fromUser); - if(message.getToCount() > 0) { - if(message.getToList().contains(message.getConversation().getTarget())) { - notifyReceivers.add(message.getConversation().getTarget()); + if(message.getConversation().getTarget().contains("|")) { + String[] ss = message.getConversation().getTarget().split("\\|"); + notifyReceivers.add(ss[0]); + notifyReceivers.add(ss[1]); + if(!message.getToList().isEmpty()) { + notifyReceivers.addAll(message.getToList()); + } else if(!StringUtil.isNullOrEmpty(message.getToUser())) { + notifyReceivers.add(message.getToUser()); } } else { - notifyReceivers.add(message.getConversation().getTarget()); + notifyReceivers.add(fromUser); + if (message.getToCount() > 0) { + if (message.getToList().contains(message.getConversation().getTarget())) { + notifyReceivers.add(message.getConversation().getTarget()); + } + } else { + notifyReceivers.add(message.getConversation().getTarget()); + } + } + if (message.getContent().getMentionedTargetCount() > 0) { + for (String mentioned : message.getContent().getMentionedTargetList()) { + if (!notifyReceivers.contains(mentioned)) { + WFCMessage.User user = getUserInfo(mentioned); + if(user != null && user.getType() == ProtoConstants.UserType.UserType_Robot) { + notifyReceivers.add(mentioned); + } + } + } } pullType = ProtoConstants.PullType.Pull_Normal; } else if (type == ProtoConstants.ConversationType.ConversationType_Group) { @@ -778,6 +799,16 @@ public class MemoryMessagesStore implements IMessagesStore { } } } + if (message.getContent().getMentionedTargetCount() > 0) { + for (String mentioned : message.getContent().getMentionedTargetList()) { + if (!notifyReceivers.contains(mentioned)) { + WFCMessage.User user = getUserInfo(mentioned); + if(user != null && user.getType() == ProtoConstants.UserType.UserType_Robot) { + notifyReceivers.add(mentioned); + } + } + } + } pullType = ProtoConstants.PullType.Pull_Normal; } else if (type == ProtoConstants.ConversationType.ConversationType_ChatRoom) { boolean isDirect = !StringUtil.isNullOrEmpty(message.getToUser()) || !message.getToList().isEmpty(); diff --git a/broker/src/main/java/win/liyufan/im/IMTopic.java b/broker/src/main/java/win/liyufan/im/IMTopic.java index 4ff22e17..7df75e37 100755 --- a/broker/src/main/java/win/liyufan/im/IMTopic.java +++ b/broker/src/main/java/win/liyufan/im/IMTopic.java @@ -10,6 +10,7 @@ package win.liyufan.im; public interface IMTopic { String SendMessageTopic = "MS"; + String RobotReplyMessageTopic = "RRM"; String MultiCastMessageTopic = "MMC"; String RecallMessageTopic = "MR"; String RecallMultiCastMessageTopic = "MRMC";