diff --git a/broker/config/wildfirechat.conf b/broker/config/wildfirechat.conf index f3dcf45d..1698ee87 100755 --- a/broker/config/wildfirechat.conf +++ b/broker/config/wildfirechat.conf @@ -115,6 +115,10 @@ chatroom.rejoin_when_active true ## 是否允许客户端发送操作群的自定义群通知消息 group.allow_client_custom_operation_notification false +## 当群成员退出或者被移除时,是否同时给管理员和相关人员发送显式通知消息。 +## 0 不发送,1 退群时发送,2 踢人时发送,3 退群和踢人时都发送。 +group.visible_quit_or_kickoff_notification 3 + #********************************************************************* # hazelcase configuration #********************************************************************* diff --git a/broker/src/main/java/com/xiaoleilu/loServer/action/admin/ModifyGroupMemberAliasAction.java b/broker/src/main/java/com/xiaoleilu/loServer/action/admin/ModifyGroupMemberAliasAction.java new file mode 100755 index 00000000..36280489 --- /dev/null +++ b/broker/src/main/java/com/xiaoleilu/loServer/action/admin/ModifyGroupMemberAliasAction.java @@ -0,0 +1,85 @@ +/* + * 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.admin; + + +import cn.wildfirechat.common.APIPath; +import cn.wildfirechat.common.ErrorCode; +import cn.wildfirechat.pojos.InputKickoffGroupMember; +import cn.wildfirechat.pojos.InputSetGroupMemberAlias; +import com.google.gson.Gson; +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.moquette.persistence.RPCCenter; +import io.moquette.persistence.TargetEntry; +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 win.liyufan.im.IMTopic; + +import java.util.concurrent.Executor; + +@Route(APIPath.Group_Set_Member_Alias) +@HttpMethod("POST") +public class ModifyGroupMemberAliasAction extends AdminAction { + + @Override + public boolean isTransactionAction() { + return true; + } + + @Override + public boolean action(Request request, Response response) { + if (request.getNettyRequest() instanceof FullHttpRequest) { + InputSetGroupMemberAlias inputSetGroupMemberAlias = getRequestBody(request.getNettyRequest(), InputSetGroupMemberAlias.class); + if (inputSetGroupMemberAlias.isValide()) { + RPCCenter.getInstance().sendRequest(inputSetGroupMemberAlias.getOperator(), null, IMTopic.ModifyGroupMemberAliasTopic, inputSetGroupMemberAlias.toProtoGroupRequest().toByteArray(), inputSetGroupMemberAlias.getOperator(), TargetEntry.Type.TARGET_TYPE_USER, new RPCCenter.Callback() { + @Override + public void onSuccess(byte[] result) { + ByteBuf byteBuf = Unpooled.buffer(); + byteBuf.writeBytes(result); + ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte()); + if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) { + sendResponse(response, null, null); + } else { + sendResponse(response, errorCode, null); + } + } + + @Override + public void onError(ErrorCode errorCode) { + sendResponse(response, errorCode, null); + } + + @Override + public void onTimeout() { + sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null); + } + + @Override + public Executor getResponseExecutor() { + return command -> { + ctx.executor().execute(command); + }; + } + }, true); + return false; + } else { + response.setStatus(HttpResponseStatus.OK); + RestResult result = RestResult.resultOf(ErrorCode.INVALID_PARAMETER); + response.setContent(new Gson().toJson(result)); + } + } + return true; + } +} diff --git a/broker/src/main/java/io/moquette/BrokerConstants.java b/broker/src/main/java/io/moquette/BrokerConstants.java index 3940c510..59da8d3c 100755 --- a/broker/src/main/java/io/moquette/BrokerConstants.java +++ b/broker/src/main/java/io/moquette/BrokerConstants.java @@ -152,6 +152,8 @@ public final class BrokerConstants { public static final String CHATROOM_Rejoin_When_Active = "chatroom.rejoin_when_active"; public static final String GROUP_Allow_Client_Custom_Operation_Notification = "group.allow_client_custom_operation_notification"; + public static final String GROUP_Visible_Quit_Kickoff_Notification = "group.visible_quit_or_kickoff_notification"; + public static final String MESSAGES_FORBIDDEN_CLIENT_SEND_TYPES = "message.forbidden_client_send_types"; diff --git a/broker/src/main/java/io/moquette/imhandler/GroupHandler.java b/broker/src/main/java/io/moquette/imhandler/GroupHandler.java index 2f603b25..c517f04a 100644 --- a/broker/src/main/java/io/moquette/imhandler/GroupHandler.java +++ b/broker/src/main/java/io/moquette/imhandler/GroupHandler.java @@ -13,10 +13,15 @@ import cn.wildfirechat.proto.WFCMessage; import win.liyufan.im.MessageShardingUtil; import java.util.ArrayList; +import java.util.Collection; import java.util.List; abstract public class GroupHandler extends IMHandler { protected void sendGroupNotification(String fromUser, String targetId, List lines, WFCMessage.MessageContent content) { + sendGroupNotification(fromUser, targetId, lines, content, null); + } + + protected void sendGroupNotification(String fromUser, String targetId, List lines, WFCMessage.MessageContent content, Collection toUsers) { if (lines == null) { lines = new ArrayList<>(); } else { @@ -32,6 +37,9 @@ abstract public class GroupHandler extends IMHandler { WFCMessage.Message.Builder builder = WFCMessage.Message.newBuilder().setContent(content).setServerTimestamp(timestamp); builder.setConversation(builder.getConversationBuilder().setType(ProtoConstants.ConversationType.ConversationType_Group).setTarget(targetId).setLine(line)); builder.setFromUser(fromUser); + if(toUsers != null && !toUsers.isEmpty()) { + builder.addAllTo(toUsers); + } try { long messageId = MessageShardingUtil.generateId(); builder.setMessageId(messageId); diff --git a/broker/src/main/java/io/moquette/imhandler/KickoffGroupMember.java b/broker/src/main/java/io/moquette/imhandler/KickoffGroupMember.java index 9ad63d34..e613c0fe 100644 --- a/broker/src/main/java/io/moquette/imhandler/KickoffGroupMember.java +++ b/broker/src/main/java/io/moquette/imhandler/KickoffGroupMember.java @@ -14,6 +14,9 @@ import cn.wildfirechat.pojos.GroupNotificationBinaryContent; import io.moquette.spi.impl.Qos1PublishHandler; import io.netty.buffer.ByteBuf; import cn.wildfirechat.common.ErrorCode; + +import java.util.Set; + import static win.liyufan.im.IMTopic.KickoffGroupMemberTopic; @Handler(value = KickoffGroupMemberTopic) @@ -58,6 +61,13 @@ public class KickoffGroupMember extends GroupHandler 0) { + Set toUsers = m_messagesStore.getGroupManagers(request.getGroupId(), true); + toUsers.addAll(request.getRemovedMemberList()); + WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, request.getRemovedMemberList()).getKickokfMemberVisibleGroupNotifyContent(); + sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content, toUsers); + } errorCode = m_messagesStore.kickoffGroupMembers(fromUser, isAdmin, request.getGroupId(), request.getRemovedMemberList()); } } diff --git a/broker/src/main/java/io/moquette/imhandler/QuitGroupHandler.java b/broker/src/main/java/io/moquette/imhandler/QuitGroupHandler.java index c6c44eb8..a7886dec 100644 --- a/broker/src/main/java/io/moquette/imhandler/QuitGroupHandler.java +++ b/broker/src/main/java/io/moquette/imhandler/QuitGroupHandler.java @@ -14,6 +14,8 @@ import io.moquette.spi.impl.Qos1PublishHandler; import io.netty.buffer.ByteBuf; import cn.wildfirechat.common.ErrorCode; +import java.util.Set; + import static win.liyufan.im.IMTopic.QuitGroupTopic; @Handler(value = QuitGroupTopic) @@ -32,6 +34,12 @@ public class QuitGroupHandler extends GroupHandler WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").getQuitGroupNotifyContent(); sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content); } + + if((m_messagesStore.getVisibleQuitKickoffNotification() & 0x01) > 0) { + Set toUsers = m_messagesStore.getGroupManagers(request.getGroupId(), true); + WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").getQuitVisibleGroupNotifyContent(); + sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content, toUsers); + } } 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 9168d1b6..faa43757 100755 --- a/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java +++ b/broker/src/main/java/io/moquette/persistence/MemoryMessagesStore.java @@ -39,11 +39,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import win.liyufan.im.*; -import java.security.MessageDigest; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -153,6 +148,7 @@ public class MemoryMessagesStore implements IMessagesStore { private String mChatroomInfoUpdateCallback; private String mChatroomMemberUpdateCallback; private boolean mGroupAllowClientCustomOperationNotification; + private int mGroupVisibleQuitKickoffNotification; MemoryMessagesStore(Server server, DatabaseStore databaseStore) { m_Server = server; @@ -324,6 +320,14 @@ public class MemoryMessagesStore implements IMessagesStore { } catch (Exception e) { } + + try { + mGroupVisibleQuitKickoffNotification = Integer.parseInt(server.getConfig().getProperty(GROUP_Visible_Quit_Kickoff_Notification, "0")); + } catch (Exception e) { + + } + + } private void printMissConfigLog(String config, String defaultValue) { @@ -1691,6 +1695,25 @@ public class MemoryMessagesStore implements IMessagesStore { return ErrorCode.ERROR_CODE_SUCCESS; } + @Override + public Set getGroupManagers(String groupId, boolean includeOwner) { + Set ret = new HashSet<>(); + HazelcastInstance hzInstance = m_Server.getHazelcastInstance(); + MultiMap groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS); + + Collection members = groupMembers.get(groupId); + if (members == null || members.size() == 0) { + members = loadGroupMemberFromDB(hzInstance, groupId); + } + for (WFCMessage.GroupMember member : members) { + if(member.getType() == GroupMemberType_Manager || (member.getType() == GroupMemberType_Owner && includeOwner)) { + ret.add(member.getMemberId()); + } + } + + return ret; + } + @Override public ErrorCode recallMessage(long messageUid, String operatorId, String clientId, boolean isAdmin) { HazelcastInstance hzInstance = m_Server.getHazelcastInstance(); @@ -3288,6 +3311,12 @@ public class MemoryMessagesStore implements IMessagesStore { return mGroupAllowClientCustomOperationNotification; } + @Override + public int getVisibleQuitKickoffNotification() { + return mGroupVisibleQuitKickoffNotification; + } + + @Override public List getClientForbiddenSendTypes() { return mForbiddenClientSendTypes; diff --git a/broker/src/main/java/io/moquette/spi/IMessagesStore.java b/broker/src/main/java/io/moquette/spi/IMessagesStore.java index be0d0ece..32e0d479 100755 --- a/broker/src/main/java/io/moquette/spi/IMessagesStore.java +++ b/broker/src/main/java/io/moquette/spi/IMessagesStore.java @@ -122,6 +122,7 @@ public interface IMessagesStore { ErrorCode setGroupManager(String operator, String groupId, int type, List userList, boolean isAdmin); boolean isMemberInGroup(String member, String groupId); ErrorCode canSendMessageInGroup(String member, String groupId); + Set getGroupManagers(String groupId, boolean includeOwner); ErrorCode recallMessage(long messageUid, String operatorId, String clientId, boolean isAdmin); ErrorCode recallCastMessage(long messageUid, String operatorId); @@ -211,6 +212,8 @@ public interface IMessagesStore { WFCMessage.Message getMessage(long messageId); boolean isAllowClientCustomGroupNotification(); + int getVisibleQuitKickoffNotification(); + List getClientForbiddenSendTypes(); long getMessageHead(String user); diff --git a/common/src/main/java/cn/wildfirechat/common/APIPath.java b/common/src/main/java/cn/wildfirechat/common/APIPath.java index 9effaab2..0da5b362 100644 --- a/common/src/main/java/cn/wildfirechat/common/APIPath.java +++ b/common/src/main/java/cn/wildfirechat/common/APIPath.java @@ -60,7 +60,7 @@ public interface APIPath { String Group_Mute_Member = "/admin/group/manager/mute"; String Group_Allow_Member = "/admin/group/manager/allow"; String Get_User_Groups = "/admin/group/of_user"; - + String Group_Set_Member_Alias = "/admin/group/member/set_alias"; String Create_Channel = "/admin/channel/create"; String Get_System_Setting = "/admin/system/get_setting"; diff --git a/common/src/main/java/cn/wildfirechat/pojos/GroupNotificationBinaryContent.java b/common/src/main/java/cn/wildfirechat/pojos/GroupNotificationBinaryContent.java index e2d4d67d..1976ab76 100644 --- a/common/src/main/java/cn/wildfirechat/pojos/GroupNotificationBinaryContent.java +++ b/common/src/main/java/cn/wildfirechat/pojos/GroupNotificationBinaryContent.java @@ -105,10 +105,18 @@ public class GroupNotificationBinaryContent { return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_KICKOF_GROUP_MEMBER); } + public WFCMessage.MessageContent getKickokfMemberVisibleGroupNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_KICKOF_GROUP_MEMBER_VISIBLE); + } + public WFCMessage.MessageContent getQuitGroupNotifyContent() { return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_QUIT_GROUP); } + public WFCMessage.MessageContent getQuitVisibleGroupNotifyContent() { + return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_QUIT_GROUP_VISIBLE); + } + public WFCMessage.MessageContent getTransferGroupNotifyContent() { return getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_TRANSFER_GROUP_OWNER); } diff --git a/common/src/main/java/cn/wildfirechat/pojos/InputSetGroupMemberAlias.java b/common/src/main/java/cn/wildfirechat/pojos/InputSetGroupMemberAlias.java new file mode 100755 index 00000000..ff951a33 --- /dev/null +++ b/common/src/main/java/cn/wildfirechat/pojos/InputSetGroupMemberAlias.java @@ -0,0 +1,67 @@ +/* + * 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 cn.wildfirechat.pojos; + + +import cn.wildfirechat.proto.WFCMessage; +import io.netty.util.internal.StringUtil; + +import java.util.List; + +public class InputSetGroupMemberAlias extends InputGroupBase { + private String group_id; + private String memberId; + private String alias; + + public String getGroup_id() { + return group_id; + } + + public void setGroup_id(String group_id) { + this.group_id = group_id; + } + + public boolean isValide() { + return true; + } + + public WFCMessage.ModifyGroupMemberAlias toProtoGroupRequest() { + WFCMessage.ModifyGroupMemberAlias.Builder modifyAliasBuilder = WFCMessage.ModifyGroupMemberAlias.newBuilder(); + modifyAliasBuilder.setGroupId(group_id); + modifyAliasBuilder.setAlias(StringUtil.isNullOrEmpty(alias)?"":alias); + modifyAliasBuilder.setMemberId(memberId); + + if (to_lines != null) { + for (Integer line : to_lines) { + modifyAliasBuilder.addToLine(line); + } + } + + if (notify_message != null) { + modifyAliasBuilder.setNotifyContent(notify_message.toProtoMessageContent()); + } + return modifyAliasBuilder.build(); + } + + public String getMemberId() { + return memberId; + } + + public void setMemberId(String memberId) { + this.memberId = memberId; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } +} diff --git a/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java b/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java index 4d74b1cd..832af0a5 100644 --- a/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java +++ b/common/src/main/java/cn/wildfirechat/proto/ProtoConstants.java @@ -219,5 +219,7 @@ public class ProtoConstants { public static final int MESSAGE_CONTENT_TYPE_SET_MANAGER = 117; public static final int MESSAGE_CONTENT_TYPE_MUTE_MEMBER = 118; public static final int MESSAGE_CONTENT_TYPE_ALLOW_MEMBER = 119; + public static final int MESSAGE_CONTENT_TYPE_KICKOF_GROUP_MEMBER_VISIBLE = 120; + public static final int MESSAGE_CONTENT_TYPE_QUIT_GROUP_VISIBLE = 121; } diff --git a/distribution/src/main/resources/wildfirechat.conf b/distribution/src/main/resources/wildfirechat.conf index 0c10d839..42f13d18 100755 --- a/distribution/src/main/resources/wildfirechat.conf +++ b/distribution/src/main/resources/wildfirechat.conf @@ -106,6 +106,10 @@ chatroom.rejoin_when_active true ## 是否允许客户端发送操作群的自定义群通知消息 group.allow_client_custom_operation_notification false +## 当群成员退出或者被移除时,是否同时给管理员和相关人员发送显式通知消息。 +## 0 不发送,1 退群时发送,2 踢人时发送,3 退群和踢人时都发送。 +group.visible_quit_or_kickoff_notification 3 + #********************************************************************* # hazelcase configuration #********************************************************************* diff --git a/sdk/src/main/java/cn/wildfirechat/sdk/GroupAdmin.java b/sdk/src/main/java/cn/wildfirechat/sdk/GroupAdmin.java index aec739b8..c0fc6f6c 100644 --- a/sdk/src/main/java/cn/wildfirechat/sdk/GroupAdmin.java +++ b/sdk/src/main/java/cn/wildfirechat/sdk/GroupAdmin.java @@ -140,6 +140,18 @@ public class GroupAdmin { return AdminHttpUtils.httpJsonPost(path, quitGroup, Void.class); } + public static IMResult setGroupMemberAlias(String operator, String groupId, String memberId, String alias, List to_lines, MessagePayload notify_message) throws Exception { + String path = APIPath.Group_Set_Member_Alias; + InputSetGroupMemberAlias input = new InputSetGroupMemberAlias(); + input.setGroup_id(groupId); + input.setOperator(operator); + input.setMemberId(memberId); + input.setAlias(alias); + input.setTo_lines(to_lines); + input.setNotify_message(notify_message); + return AdminHttpUtils.httpJsonPost(path, input, Void.class); + } + public static IMResult getUserGroups(String user) throws Exception { String path = APIPath.Get_User_Groups; InputUserId inputUserId = new InputUserId(); diff --git a/sdk/src/main/java/cn/wildfirechat/sdk/Main.java b/sdk/src/main/java/cn/wildfirechat/sdk/Main.java index 6e3bb7c2..efe0206a 100644 --- a/sdk/src/main/java/cn/wildfirechat/sdk/Main.java +++ b/sdk/src/main/java/cn/wildfirechat/sdk/Main.java @@ -434,7 +434,7 @@ public class Main { PojoGroupMember m = new PojoGroupMember(); m.setMember_id("user1"); m.setAlias("hello user1"); - + voidIMResult = GroupAdmin.addGroupMembers("user1", groupInfo.getTarget_id(), Arrays.asList(m), null, null); if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) { System.out.println("add group member success"); @@ -451,22 +451,31 @@ public class Main { System.exit(-1); } - voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), true, null, null); + voidIMResult = GroupAdmin.setGroupMemberAlias("user1", groupInfo.getTarget_id(), "user3", "test user3", null, null); if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) { - System.out.println("set group manager success"); + System.out.println("set group member alias success"); } else { - System.out.println("set group manager failure"); + System.out.println("set group member alias failure"); System.exit(-1); } - voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), false, null, null); - if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) { - System.out.println("cancel group manager success"); - } else { - System.out.println("cancel group manager failure"); - System.exit(-1); - } + if(commercialServer) { + voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), true, null, null); + if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) { + System.out.println("set group manager success"); + } else { + System.out.println("set group manager failure"); + System.exit(-1); + } + voidIMResult = GroupAdmin.setGroupManager("user1", groupInfo.getTarget_id(), Arrays.asList("user4", "user5"), false, null, null); + if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) { + System.out.println("cancel group manager success"); + } else { + System.out.println("cancel group manager failure"); + System.exit(-1); + } + } voidIMResult = GroupAdmin.quitGroup("user4", groupInfo.getTarget_id(), null, null); if (voidIMResult != null && voidIMResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {