Compare commits

...
8 Commits
25 changed files with 334 additions and 36 deletions
+9
View File
@@ -109,6 +109,15 @@ message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
message.no_forward_admin_message false
## 消息转发功能开启时,转发信息是否带上用户clientId和平台类型。
message.forward_with_client_info false
## 机器人回调信息是否带上用户clientId和平台类型。
robot.callback_with_client_info false
## 频道回调信息是否带上用户clientId和平台类型。
channel.callback_with_client_info false
##禁止搜索用户
friend.disable_search false
+1 -1
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.90</version>
<version>0.91</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -142,6 +142,10 @@ public final class BrokerConstants {
public static final String MESSAGE_NO_Forward_Admin_Message = "message.no_forward_admin_message";
public static final String MESSAGE_Forward_With_Client_Info = "message.forward_with_client_info";
public static final String ROBOT_Callback_With_Client_Info = "robot.callback_with_client_info";
public static final String CHANNEL_Callback_With_Client_Info = "channel.callback_with_client_info";
public static final String FRIEND_Disable_Search = "friend.disable_search";
public static final String FRIEND_Disable_NickName_Search = "friend.disable_nick_name_search";
public static final String FRIEND_Disable_Friend_Request = "friend.disable_friend_request";
@@ -21,6 +21,9 @@ import win.liyufan.im.IMTopic;
public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.CreateGroupRequest request, Qos1PublishHandler.IMCallback callback) {
if(request.getGroup().getGroupInfo().getType() < 0 || request.getGroup().getGroupInfo().getType() > 2) {
return ErrorCode.ERROR_CODE_INVALID_DATA;
}
if (!StringUtil.isNullOrEmpty(request.getGroup().getGroupInfo().getTargetId())) {
WFCMessage.GroupInfo existGroupInfo = m_messagesStore.getGroupInfo(request.getGroup().getGroupInfo().getTargetId());
@@ -9,11 +9,13 @@
package io.moquette.imhandler;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.pojos.OutputClient;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.hazelcast.util.StringUtil;
import io.moquette.BrokerConstants;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
@@ -171,12 +173,20 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
OutputClient outputClient = null;
if(m_messagesStore.isForwardMessageWithClientInfo() && requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !StringUtil.isNullOrEmpty(clientID)) {
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
if(session != null && session.getUsername().equals(fromUser)) {
outputClient = new OutputClient(session.getPlatform(), clientID);
}
}
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
publisher.forwardMessage(message, mForwardUrl);
publisher.forwardMessage(message, mForwardUrl, outputClient);
}
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
publisher.forwardMessage(message, mMentionForwardUrl);
publisher.forwardMessage(message, mMentionForwardUrl, outputClient);
}
if (!isAdmin) {
@@ -167,6 +167,10 @@ public class MemoryMessagesStore implements IMessagesStore {
private int mSyncDataPartSize = 0;
private boolean keepDisplayNameWhenDestroyUser = true;
private boolean mForwardMessageWithClientInfo = false;
private boolean mRobotCallbackWithClientInfo = false;
private boolean mChannelCallbackWithClientInfo = false;
private Set<Integer> mUserHideProperties = new HashSet<>();
MemoryMessagesStore(Server server, DatabaseStore databaseStore) {
@@ -446,6 +450,23 @@ public class MemoryMessagesStore implements IMessagesStore {
} catch (NumberFormatException e) {
e.printStackTrace();
}
try {
mForwardMessageWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_With_Client_Info, "false"));
} catch (Exception e) {
}
try {
mRobotCallbackWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.ROBOT_Callback_With_Client_Info, "false"));
} catch (Exception e) {
}
try {
mChannelCallbackWithClientInfo = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.CHANNEL_Callback_With_Client_Info, "false"));
} catch (Exception e) {
}
}
private void printMissConfigLog(String config, String defaultValue) {
@@ -1279,11 +1300,31 @@ public class MemoryMessagesStore implements IMessagesStore {
}
}
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
ArrayList<String> existMemberIds = new ArrayList<>();
for (WFCMessage.GroupMember member : members) {
if (member.getType() != GroupMemberType_Removed) {
existMemberIds.add(member.getMemberId());
}
}
long updateDt = System.currentTimeMillis();
List<WFCMessage.GroupMember> tmp = new ArrayList<>();
ArrayList<String> newInviteUsers = new ArrayList<>();
int newMemberCount = 0;
for (WFCMessage.GroupMember member : memberList) {
if (existMemberIds.contains(member.getMemberId()) && member.getType() != GroupMemberType_Removed) {
if(!isAdmin)
continue;
} else {
newMemberCount++;
}
WFCMessage.GroupMember.Builder builder = member.toBuilder();
builder.setUpdateDt(updateDt).setCreateDt(updateDt);
if (member.getMemberId().equals(groupInfo.getOwner())) {
@@ -1303,29 +1344,23 @@ public class MemoryMessagesStore implements IMessagesStore {
}
member = builder.build();
tmp.add(member);
newInviteUsers.add(member.getMemberId());
updateDt++;
}
memberList = tmp;
MultiMap<String, WFCMessage.GroupMember> groupMembers = hzInstance.getMultiMap(GROUP_MEMBERS);
Collection<WFCMessage.GroupMember> members = groupMembers.get(groupId);
if (members == null || members.size() == 0) {
members = loadGroupMemberFromDB(hzInstance, groupId);
}
if (maxCount != Integer.MAX_VALUE) {
int existCount = 0;
for (WFCMessage.GroupMember member : members) {
if (member.getType() != GroupMemberType_Removed) {
existCount++;
}
if (memberList.size() == 0) {
if (!isAdmin) {
return ErrorCode.ERROR_CODE_ALREADY_IN_GROUP;
} else {
return ErrorCode.ERROR_CODE_SUCCESS;
}
if (existCount + newInviteUsers.size() > maxCount) {
} else {
if (existMemberIds.size() + newMemberCount > maxCount) {
return ErrorCode.ERROR_CODE_GROUP_EXCEED_MAX_MEMBER_COUNT;
}
}
databaseStore.persistGroupMember(groupId, memberList, true);
databaseStore.updateGroupMemberCountDt(groupId);
@@ -4066,6 +4101,20 @@ public class MemoryMessagesStore implements IMessagesStore {
return mGroupVisibleQuitKickoffNotification;
}
@Override
public boolean isForwardMessageWithClientInfo() {
return mForwardMessageWithClientInfo;
}
@Override
public boolean isRobotCallbackWithClientInfo() {
return mRobotCallbackWithClientInfo;
}
@Override
public boolean isChannelCallbackWithClientInfo() {
return mChannelCallbackWithClientInfo;
}
@Override
public List<Integer> getClientForbiddenSendTypes() {
@@ -233,6 +233,10 @@ public interface IMessagesStore {
boolean isAllowRobotCustomGroupNotification();
int getVisibleQuitKickoffNotification();
boolean isForwardMessageWithClientInfo();
boolean isRobotCallbackWithClientInfo();
boolean isChannelCallbackWithClientInfo();
List<Integer> getClientForbiddenSendTypes();
List<Integer> getBlackListExceptionTypes();
List<Integer> getGroupMuteExceptionTypes();
@@ -16,6 +16,7 @@
package io.moquette.spi.impl;
import cn.wildfirechat.pojos.OutputClient;
import cn.wildfirechat.pojos.OutputMessageData;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
@@ -175,10 +176,19 @@ public class MessagesPublisher {
if (message == null) {
message = m_messagesStore.getMessage(messageId);
}
OutputClient outputClient = null;
if(m_messagesStore.isRobotCallbackWithClientInfo() && !StringUtil.isNullOrEmpty(exceptClientId)) {
Session session = m_sessionsStore.getSession(exceptClientId);
if(session != null && session.getUsername().equals(sender)) {
outputClient = new OutputClient(session.getPlatform(), exceptClientId);
}
}
final WFCMessage.Message finalMsg = message;
OutputClient finalOutputClient = outputClient;
Server.getServer().getCallbackScheduler().execute(() -> {
try {
HttpUtils.httpJsonPost(robot.getCallback(), new Gson().toJson(OutputMessageData.fromProtoMessage(finalMsg), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Robot_Message_Callback);
HttpUtils.httpJsonPost(robot.getCallback(), new Gson().toJson(OutputMessageData.fromProtoMessage(finalMsg, finalOutputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Robot_Message_Callback);
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
@@ -595,9 +605,18 @@ public class MessagesPublisher {
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
WFCMessage.ChannelInfo channelInfo = m_messagesStore.getChannelInfo(message.getConversation().getTarget());
if (channelInfo != null && !StringUtil.isNullOrEmpty(channelInfo.getCallback())) {
OutputClient outputClient = null;
if(m_messagesStore.isChannelCallbackWithClientInfo() && !StringUtil.isNullOrEmpty(exceptClientId)) {
Session session = m_sessionsStore.getSession(exceptClientId);
if(session != null && session.getUsername().equals(message.getFromUser())) {
outputClient = new OutputClient(session.getPlatform(), exceptClientId);
}
}
OutputClient finalOutputClient = outputClient;
Server.getServer().getCallbackScheduler().execute(() -> {
try {
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/message", new Gson().toJson(OutputMessageData.fromProtoMessage(message), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Message_Callback);
HttpUtils.httpJsonPost(channelInfo.getCallback() + "/message", new Gson().toJson(OutputMessageData.fromProtoMessage(message, finalOutputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Channel_Message_Callback);
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
@@ -647,10 +666,10 @@ public class MessagesPublisher {
}
public void forwardMessage(final WFCMessage.Message message, String forwardUrl) {
public void forwardMessage(final WFCMessage.Message message, String forwardUrl, OutputClient outputClient) {
Server.getServer().getCallbackScheduler().execute(() -> {
try {
HttpUtils.httpJsonPost(forwardUrl, new Gson().toJson(OutputMessageData.fromProtoMessage(message), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Forward_Message_Callback);
HttpUtils.httpJsonPost(forwardUrl, new Gson().toJson(OutputMessageData.fromProtoMessage(message, outputClient), OutputMessageData.class), HttpUtils.HttpPostType.POST_TYPE_Forward_Message_Callback);
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e, EVENT_CALLBACK_Exception);
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.90</version>
<version>0.91</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -57,6 +57,8 @@ public interface APIPath {
String Msg_Multicast = "/admin/message/multicast";
String Msg_RecallBroadCast = "/admin/message/recall_broadcast";
String Msg_RecallMultiCast = "/admin/message/recall_multicast";
String Msg_ConvRead = "/admin/message/conv_read";
String Msg_Delivery = "/admin/message/delivery";
String Create_Group = "/admin/group/create";
String Group_Dismiss = "/admin/group/del";
@@ -29,6 +29,7 @@ public enum ErrorCode {
//Group error
ERROR_CODE_GROUP_ALREADY_EXIST(11, "group already exist"),
ERROR_CODE_ALREADY_IN_GROUP(12, "member already in group"),
//user error
ERROR_CODE_FRIEND_ALREADY_REQUEST(16, "already send request"),
@@ -13,6 +13,15 @@ public class Conversation {
private String target;
private int line;
public Conversation() {
}
public Conversation(int type, String target, int line) {
this.type = type;
this.target = target;
this.line = line;
}
public int getType() {
return type;
}
@@ -67,16 +67,18 @@ public class InputCreateGroup extends InputGroupBase {
}
groupBuilder.setGroupInfo(groupInfoBuilder);
for (PojoGroupMember pojoGroupMember : group.getMembers()) {
WFCMessage.GroupMember.Builder groupMemberBuilder = WFCMessage.GroupMember.newBuilder().setMemberId(pojoGroupMember.getMember_id());
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getAlias())) {
groupMemberBuilder.setAlias(pojoGroupMember.getAlias());
if(group.getMembers() != null) {
for (PojoGroupMember pojoGroupMember : group.getMembers()) {
WFCMessage.GroupMember.Builder groupMemberBuilder = WFCMessage.GroupMember.newBuilder().setMemberId(pojoGroupMember.getMember_id());
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getAlias())) {
groupMemberBuilder.setAlias(pojoGroupMember.getAlias());
}
groupMemberBuilder.setType(pojoGroupMember.getType());
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getExtra())) {
groupMemberBuilder.setExtra(pojoGroupMember.extra);
}
groupBuilder.addMembers(groupMemberBuilder);
}
groupMemberBuilder.setType(pojoGroupMember.getType());
if(!StringUtil.isNullOrEmpty(pojoGroupMember.getExtra())) {
groupMemberBuilder.setExtra(pojoGroupMember.extra);
}
groupBuilder.addMembers(groupMemberBuilder);
}
WFCMessage.CreateGroupRequest.Builder createGroupReqBuilder = WFCMessage.CreateGroupRequest.newBuilder();
@@ -0,0 +1,58 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
public class InputGetConvReadTime {
private String userId;
private int conversationType;
private String target;
private int line;
public InputGetConvReadTime() {
}
public InputGetConvReadTime(String userId, int conversationType, String target, int line) {
this.userId = userId;
this.conversationType = conversationType;
this.target = target;
this.line = line;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public int getConversationType() {
return conversationType;
}
public void setConversationType(int conversationType) {
this.conversationType = conversationType;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public int getLine() {
return line;
}
public void setLine(int line) {
this.line = line;
}
}
@@ -11,6 +11,13 @@ package cn.wildfirechat.pojos;
public class InputUserId {
private String userId;
public InputUserId() {
}
public InputUserId(String userId) {
this.userId = userId;
}
public String getUserId() {
return userId;
}
@@ -0,0 +1,40 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
import java.util.List;
public class OutputClient {
private int platform;
private String clientId;
public OutputClient() {
}
public OutputClient(int platform, String clientId) {
this.platform = platform;
this.clientId = clientId;
}
public int getPlatform() {
return platform;
}
public void setPlatform(int platform) {
this.platform = platform;
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
}
@@ -20,6 +20,7 @@ public class OutputMessageData {
private MessagePayload payload;
private List<String> toUsers;
private long timestamp;
private OutputClient client;
public String getSender() {
return sender;
@@ -69,7 +70,19 @@ public class OutputMessageData {
this.timestamp = timestamp;
}
public OutputClient getClient() {
return client;
}
public void setClient(OutputClient client) {
this.client = client;
}
public static OutputMessageData fromProtoMessage(WFCMessage.Message protoMessage) {
return fromProtoMessage(protoMessage, null);
}
public static OutputMessageData fromProtoMessage(WFCMessage.Message protoMessage, OutputClient fromClient) {
OutputMessageData data = new OutputMessageData();
data.messageId = protoMessage.getMessageId();
data.sender = protoMessage.getFromUser();
@@ -80,6 +93,7 @@ public class OutputMessageData {
data.payload = MessagePayload.fromProtoMessageContent(protoMessage.getContent());
data.timestamp = protoMessage.getServerTimestamp();
data.toUsers = protoMessage.getToList();
data.client = fromClient;
return data;
}
}
@@ -0,0 +1,28 @@
/*
* This file is part of the Wildfire Chat package.
* (c) Heavyrain2012 <heavyrain.lee@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
package cn.wildfirechat.pojos;
public class OutputTimestamp {
private long timestamp;
public OutputTimestamp() {
}
public OutputTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.90</version>
<version>0.91</version>
</parent>
<artifactId>distribution</artifactId>
@@ -13,7 +13,8 @@ server.ip 0.0.0.0
##原生客户端长链接端口。如果使用4层的反向代理或者类似组件,需要设置长链接保持时间为10分钟。不支持7层代理。
port 1883
##客户端短链接端口,客户端固定使用80端口,不要修改此端口。
##客户端短链接端口,客户端固定使用80端口不能修改。
##如果是国企事业单位等因为政策原因无法使用80端口,可以联系野火解决。
http_port 80
#管理端口
@@ -104,6 +105,15 @@ message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
message.no_forward_admin_message false
## 消息转发功能开启时,转发信息是否带上用户clientId和平台类型。
message.forward_with_client_info false
## 机器人回调信息是否带上用户clientId和平台类型。
robot.callback_with_client_info false
## 频道回调信息是否带上用户clientId和平台类型。
channel.callback_with_client_info false
##禁止搜索用户
friend.disable_search false
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>0.90</version>
<version>0.91</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.90</version>
<version>0.91</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -750,6 +750,20 @@ public class Main {
} else {
System.out.println("failure");
}
IMResult<OutputTimestamp> timestampResult = MessageAdmin.getConversationReadTimestamp("57gqmws2k", new Conversation(0, "admin", 0));
if(timestampResult != null && timestampResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("Get conversation read time success");
} else {
System.out.println("Get conversation read time failure");
}
timestampResult = MessageAdmin.getMessageDelivery("57gqmws2k");
if(timestampResult != null && timestampResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("Get message delivery success");
} else {
System.out.println("Get message delivery failure");
}
}
List<String> multicastReceivers = Arrays.asList("user2", "user3", "user4");
@@ -19,6 +19,9 @@ public class MessageAdmin {
messageData.setConv(conversation);
messageData.setPayload(payload);
messageData.setToUsers(toUsers);
if (payload.getType() == 1 && (payload.getSearchableContent() == null || payload.getSearchableContent().isEmpty())) {
System.out.println("Payload错误,Payload格式应该跟客户端消息encode出来的Payload对齐,这样客户端才能正确识别。比如文本消息,文本需要放到searchableContent属性。请与客户端同事确认Payload的格式,或则去 https://gitee.com/wfchat/android-chat/tree/master/client/src/main/java/cn/wildfirechat/message 找到消息encode的实现方法!");
}
return AdminHttpUtils.httpJsonPost(path, messageData, SendMessageResult.class);
}
@@ -103,4 +106,16 @@ public class MessageAdmin {
messageData.setPayload(payload);
return AdminHttpUtils.httpJsonPost(path, messageData, MultiMessageResult.class);
}
public static IMResult<OutputTimestamp> getConversationReadTimestamp(String userId, Conversation conversation) throws Exception {
String path = APIPath.Msg_ConvRead;
InputGetConvReadTime input = new InputGetConvReadTime(userId, conversation.getType(), conversation.getTarget(), conversation.getLine());
return AdminHttpUtils.httpJsonPost(path, input, OutputTimestamp.class);
}
public static IMResult<OutputTimestamp> getMessageDelivery(String userId) throws Exception {
String path = APIPath.Msg_Delivery;
InputUserId input = new InputUserId(userId);
return AdminHttpUtils.httpJsonPost(path, input, OutputTimestamp.class);
}
}