Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c02e9066be | ||
|
|
ffd1492ea2 | ||
|
|
5e9bbcb1e9 | ||
|
|
63770c75fa | ||
|
|
bf0eb5b8aa | ||
|
|
8cbf66930f | ||
|
|
ce48f67b04 | ||
|
|
2fdf11006d | ||
|
|
4a2d7a4cc5 | ||
|
|
0513da8993 | ||
|
|
d400b74bff | ||
|
|
3d7220d79c | ||
|
|
4bcf784a6d | ||
|
|
6d021568d3 | ||
|
|
44f54ecd17 | ||
|
|
4cd0349911 | ||
|
|
9ad78f2fb8 |
@@ -111,7 +111,7 @@ friend.disable_search false
|
||||
##禁止按照昵称搜索用户。
|
||||
friend.disable_nick_name_search false
|
||||
|
||||
##禁止发送好友邀请,通过server api添加好友不受此限制
|
||||
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
|
||||
friend.disable_friend_request false
|
||||
|
||||
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
|
||||
@@ -154,6 +154,20 @@ group.visible_quit_or_kickoff_notification 0
|
||||
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
|
||||
#sync.data_part_size 1000
|
||||
|
||||
#*********************************************************************
|
||||
# 限频配置
|
||||
#*********************************************************************
|
||||
##API接口限频设置,时间是10秒内允许的请求次数。
|
||||
##管理api频率限制,默认是10秒10000次。
|
||||
http.admin.rate_limit 10000
|
||||
##机器人api频率限制,默认是10秒1000次,按照机器人ID区分。
|
||||
http.robot.rate_limit 1000
|
||||
##频道api频率限制,默认是10秒1000次,按照频道ID区分。
|
||||
http.channel.rate_limit 1000
|
||||
|
||||
##客户端请求频率限制,默认是5秒100次。通常情况下不要修改这个值。
|
||||
client.request_rate_limit 100
|
||||
|
||||
#*********************************************************************
|
||||
# hazelcase configuration
|
||||
#*********************************************************************
|
||||
@@ -163,7 +177,8 @@ hazelcast.configuration config/hazelcast.xml
|
||||
# Media server configuration
|
||||
#*********************************************************************
|
||||
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
|
||||
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
|
||||
##专业版另外支持阿里云存储和野火私有存储,关于野火私有存储请参考:https://github.com/wildfirechat/WF-minio
|
||||
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
|
||||
media.server.use_qiniu 0
|
||||
|
||||
##媒体类型分类
|
||||
@@ -264,6 +279,11 @@ push.ios.server.address http://localhost:8085/ios/push
|
||||
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
|
||||
#message.forward.types 1,2,3
|
||||
|
||||
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
|
||||
|
||||
|
||||
##设备信息转发地址
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#things_message.forward.url http://localhost:8888/im_event/things/message
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<relativePath>../</relativePath>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.71</version>
|
||||
<version>0.74</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>moquette-broker</artifactId>
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.xiaoleilu.loServer.RestResult;
|
||||
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
|
||||
import com.xiaoleilu.loServer.handler.Request;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.moquette.server.config.IConfig;
|
||||
import io.moquette.spi.IMessagesStore;
|
||||
import io.moquette.spi.ISessionsStore;
|
||||
import io.moquette.spi.impl.Utils;
|
||||
@@ -21,10 +22,12 @@ import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import win.liyufan.im.RateLimiter;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||
import static io.moquette.BrokerConstants.*;
|
||||
|
||||
/**
|
||||
* 请求处理接口<br>
|
||||
@@ -38,6 +41,36 @@ abstract public class Action {
|
||||
public static ISessionsStore sessionsStore = null;
|
||||
|
||||
public ChannelHandlerContext ctx;
|
||||
|
||||
protected static RateLimiter adminLimiter = null;
|
||||
protected static RateLimiter robotLimiter = null;
|
||||
protected static RateLimiter channelLimiter = null;
|
||||
|
||||
public static void init(IConfig config) {
|
||||
int adminRate = 10000;
|
||||
int robotRate = 1000;
|
||||
int channelRate = 1000;
|
||||
|
||||
try {
|
||||
adminRate = Integer.parseInt(config.getProperty(HTTP_ADMIN_RATE_LIMIT, "10000"));
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
}
|
||||
try {
|
||||
robotRate = Integer.parseInt(config.getProperty(HTTP_ROBOT_RATE_LIMIT, "1000"));
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
}
|
||||
try {
|
||||
channelRate = Integer.parseInt(config.getProperty(HTTP_CHANNEL_RATE_LIMIT, "1000"));
|
||||
} catch (NumberFormatException e) {
|
||||
|
||||
}
|
||||
adminLimiter = new RateLimiter(10, adminRate);
|
||||
robotLimiter = new RateLimiter(10, robotRate);
|
||||
channelLimiter = new RateLimiter(10, channelRate);
|
||||
}
|
||||
|
||||
protected class Result {
|
||||
Object data;
|
||||
ErrorCode errorCode;
|
||||
|
||||
@@ -29,7 +29,6 @@ abstract public class AdminAction extends Action {
|
||||
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(AdminAction.class);
|
||||
private static String SECRET_KEY = "123456";
|
||||
private static boolean NO_CHECK_TIME = false;
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 500);
|
||||
public static void setSecretKey(String secretKey) {
|
||||
SECRET_KEY = secretKey;
|
||||
}
|
||||
@@ -44,7 +43,7 @@ abstract public class AdminAction extends Action {
|
||||
|
||||
@Override
|
||||
public ErrorCode preAction(Request request, Response response) {
|
||||
if (!mLimitCounter.isGranted("admin")) {
|
||||
if (!adminLimiter.isGranted("admin")) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
@@ -88,14 +87,16 @@ abstract public class AdminAction extends Action {
|
||||
}
|
||||
|
||||
protected void sendResponse(Response response, ErrorCode errorCode, Object data) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
if(response != null) {
|
||||
response.setStatus(HttpResponseStatus.OK);
|
||||
if (errorCode == null) {
|
||||
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
RestResult result = RestResult.resultOf(errorCode, errorCode.getMsg(), data);
|
||||
response.setContent(new Gson().toJson(result));
|
||||
response.send();
|
||||
}
|
||||
}
|
||||
protected void sendApiMessage(Response response, String fromUser, String topic, byte[] message, ApiCallback callback) {
|
||||
sendApiMessage(response, fromUser, null, topic, message, callback, false);
|
||||
|
||||
@@ -12,16 +12,22 @@ import cn.wildfirechat.common.APIPath;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import cn.wildfirechat.pojos.InputGetFriendList;
|
||||
import cn.wildfirechat.pojos.InputUpdateAlias;
|
||||
import cn.wildfirechat.pojos.OutputCreateChannel;
|
||||
import cn.wildfirechat.pojos.OutputStringList;
|
||||
import cn.wildfirechat.proto.WFCMessage;
|
||||
import com.google.gson.Gson;
|
||||
import com.hazelcast.util.StringUtil;
|
||||
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 com.xiaoleilu.loServer.model.FriendData;
|
||||
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.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -39,8 +45,26 @@ public class AliasPutAction extends AdminAction {
|
||||
public boolean action(Request request, Response response) {
|
||||
if (request.getNettyRequest() instanceof FullHttpRequest) {
|
||||
InputUpdateAlias input = getRequestBody(request.getNettyRequest(), InputUpdateAlias.class);
|
||||
ErrorCode errorCode = messagesStore.setFriendAliasRequest(input.getOperator(), input.getTargetId(), input.getAlias(), new long[1]);
|
||||
setResponseContent(RestResult.resultOf(errorCode), response);
|
||||
if(!StringUtil.isNullOrEmpty(input.getOperator()) && !StringUtil.isNullOrEmpty(input.getTargetId())) {
|
||||
WFCMessage.AddFriendRequest addFriendRequest = WFCMessage.AddFriendRequest.newBuilder().setTargetUid(input.getTargetId()).setReason(input.getAlias()==null?"":input.getAlias()).build();
|
||||
sendApiMessage(response, input.getOperator(), IMTopic.SetFriendAliasTopic, addFriendRequest.toByteArray(), result -> {
|
||||
ByteBuf byteBuf = Unpooled.buffer();
|
||||
byteBuf.writeBytes(result);
|
||||
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
|
||||
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
byte[] data = new byte[byteBuf.readableBytes()];
|
||||
byteBuf.readBytes(data);
|
||||
String channelId = new String(data);
|
||||
return new Result(ErrorCode.ERROR_CODE_SUCCESS, new OutputCreateChannel(channelId));
|
||||
} else {
|
||||
return new Result(errorCode);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
ErrorCode errorCode = messagesStore.setFriendAliasRequest(input.getOperator(), input.getTargetId(), input.getAlias(), new long[1]);
|
||||
setResponseContent(RestResult.resultOf(errorCode), response);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public class BlockUserAction extends AdminAction {
|
||||
response.setContent(new Gson().toJson(result));
|
||||
|
||||
if (inputUserBlock.getStatus() == 2) {
|
||||
sendApiMessage(response, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
|
||||
sendApiMessage(null, null, ServerAPIHelper.KICKOFF_USER_REQUEST, inputUserBlock.getUserId().getBytes(), null);
|
||||
}
|
||||
|
||||
sendResponse(response, ErrorCode.ERROR_CODE_SUCCESS, null);
|
||||
|
||||
@@ -18,6 +18,8 @@ import cn.wildfirechat.pojos.OutputGetIMTokenData;
|
||||
import com.xiaoleilu.loServer.handler.Response;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import cn.wildfirechat.common.ErrorCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.Base64;
|
||||
@@ -25,7 +27,7 @@ import java.util.Base64;
|
||||
@Route(APIPath.User_Get_Token)
|
||||
@HttpMethod("POST")
|
||||
public class GetIMTokenAction extends AdminAction {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GetIMTokenAction.class);
|
||||
@Override
|
||||
public boolean isTransactionAction() {
|
||||
return false;
|
||||
@@ -49,6 +51,8 @@ public class GetIMTokenAction extends AdminAction {
|
||||
}
|
||||
String token = Base64.getEncoder().encodeToString(data);
|
||||
|
||||
LOG.info("get im token success {},{},{}", userId, input.getClientId(), token.substring(0, Math.min(10, token.length())));
|
||||
|
||||
return new Result(errorCode1, new OutputGetIMTokenData(userId, token));
|
||||
} else {
|
||||
return new Result(errorCode1);
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.concurrent.Executor;
|
||||
|
||||
abstract public class ChannelAction extends Action {
|
||||
private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(ChannelAction.class);
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 200);
|
||||
protected WFCMessage.ChannelInfo channelInfo;
|
||||
|
||||
protected interface Callback {
|
||||
@@ -61,7 +60,7 @@ abstract public class ChannelAction extends Action {
|
||||
return ErrorCode.ERROR_CODE_API_NOT_SIGNED;
|
||||
}
|
||||
|
||||
if (!mLimitCounter.isGranted(cid)) {
|
||||
if (!channelLimiter.isGranted(cid)) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ import java.util.concurrent.Executor;
|
||||
|
||||
abstract public class RobotAction extends Action {
|
||||
protected static final Logger LOG = LoggerFactory.getLogger(RobotAction.class);
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(10, 1000);
|
||||
|
||||
protected WFCMessage.Robot robot;
|
||||
|
||||
@@ -64,7 +63,7 @@ abstract public class RobotAction extends Action {
|
||||
return ErrorCode.ERROR_CODE_API_NOT_SIGNED;
|
||||
}
|
||||
|
||||
if (!mLimitCounter.isGranted(rid)) {
|
||||
if (!robotLimiter.isGranted(rid)) {
|
||||
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,8 @@ public final class BrokerConstants {
|
||||
|
||||
public static final String MESSAGE_Forward_Url = "message.forward.url";
|
||||
public static final String MESSAGE_Forward_Types = "message.forward.types";
|
||||
public static final String MESSAGE_MentionMsg_Forward_Url = "message.mentionmsg.forward.url";
|
||||
|
||||
|
||||
public static final String SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
|
||||
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
|
||||
@@ -176,6 +178,10 @@ public final class BrokerConstants {
|
||||
public static final String MESSAGES_RECALL_TIME_LIMIT = "message.recall_time_limit";
|
||||
public static final String MESSAGES_DISABLE_GROUP_MANAGER_RECALL = "message.disable_group_manager_recall";
|
||||
|
||||
public static final String HTTP_ADMIN_RATE_LIMIT = "http.admin.rate_limit";
|
||||
public static final String HTTP_ROBOT_RATE_LIMIT = "http.robot.rate_limit";
|
||||
public static final String HTTP_CHANNEL_RATE_LIMIT = "http.channel.rate_limit";
|
||||
public static final String CLIENT_REQUEST_RATE_LIMIT = "client.request_rate_limit";
|
||||
|
||||
private BrokerConstants() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the Wildfire Chat package.
|
||||
* (c) Heavyrain2012 <[email protected]>
|
||||
*
|
||||
* 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.proto.WFCMessage;
|
||||
import com.hazelcast.util.StringUtil;
|
||||
import com.xiaoleilu.loServer.model.FriendData;
|
||||
import io.moquette.spi.impl.Qos1PublishHandler;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import win.liyufan.im.IMTopic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Handler(value = IMTopic.ClearSessionTopic)
|
||||
public class DisconnectHandler extends IMHandler<Byte> {
|
||||
@Override
|
||||
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, Byte request, Qos1PublishHandler.IMCallback callback) {
|
||||
if (request == 8) {
|
||||
m_sessionsStore.cleanSession(fromUser, clientID);
|
||||
} else if(request == 1) {
|
||||
m_sessionsStore.disableSession(fromUser, clientID);
|
||||
}
|
||||
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import win.liyufan.im.I18n;
|
||||
import win.liyufan.im.IMTopic;
|
||||
import win.liyufan.im.MessageShardingUtil;
|
||||
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||
|
||||
@Handler(IMTopic.HandleFriendRequestTopic)
|
||||
@@ -86,6 +87,10 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
|
||||
publisher.publishNotification(IMTopic.NotifyFriendTopic, fromUser, heads[1]);
|
||||
}
|
||||
}
|
||||
if(errorCode == ERROR_CODE_ALREADY_FRIENDS) {
|
||||
errorCode = ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ import java.util.*;
|
||||
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_OVER_FREQUENCY;
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||
import static io.moquette.BrokerConstants.CLIENT_REQUEST_RATE_LIMIT;
|
||||
|
||||
/**
|
||||
* 请求处理接口<br>
|
||||
@@ -52,7 +53,7 @@ abstract public class IMHandler<T> {
|
||||
protected static Server mServer = null;
|
||||
protected static MessagesPublisher publisher;
|
||||
private static ThreadPoolExecutorWrapper m_imBusinessExecutor;
|
||||
private static final RateLimiter mLimitCounter = new RateLimiter(5, 100);
|
||||
private static RateLimiter mLimitCounter;
|
||||
private Method parseDataMethod;
|
||||
private Class dataCls;
|
||||
|
||||
@@ -147,12 +148,24 @@ abstract public class IMHandler<T> {
|
||||
publisher = p;
|
||||
m_imBusinessExecutor = businessExecutor;
|
||||
mServer = server;
|
||||
int clientRateLimit = 100;
|
||||
try {
|
||||
clientRateLimit = Integer.parseInt(server.getConfig().getProperty(CLIENT_REQUEST_RATE_LIMIT, "100"));
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
if(clientRateLimit == 0) {
|
||||
clientRateLimit = 100;
|
||||
}
|
||||
|
||||
mLimitCounter = new RateLimiter(5, clientRateLimit);
|
||||
}
|
||||
|
||||
|
||||
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback) {
|
||||
public ErrorCode preAction(String clientID, String fromUser, String topic, Qos1PublishHandler.IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
|
||||
LOG.info("imHandler fromUser={}, clientId={}, topic={}", fromUser, clientID, topic);
|
||||
if(!mLimitCounter.isGranted(clientID + fromUser + topic)) {
|
||||
if(!isAdmin && !isRobotOrChannel && !mLimitCounter.isGranted(clientID + fromUser + topic)) {
|
||||
ByteBuf ackPayload = Unpooled.buffer();
|
||||
ackPayload.ensureWritable(1).writeByte(ERROR_CODE_OVER_FREQUENCY.getCode());
|
||||
try {
|
||||
@@ -166,7 +179,7 @@ abstract public class IMHandler<T> {
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
|
||||
public void doHandler(String clientID, String fromUser, String topic, byte[] payloadContent, Qos1PublishHandler.IMCallback callback, boolean isAdmin) {
|
||||
public void doHandler(String clientID, String fromUser, String topic, byte[] payloadContent, Qos1PublishHandler.IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
|
||||
m_imBusinessExecutor.execute(() -> {
|
||||
Qos1PublishHandler.IMCallback callbackWrapper = new Qos1PublishHandler.IMCallback() {
|
||||
@Override
|
||||
@@ -177,7 +190,7 @@ abstract public class IMHandler<T> {
|
||||
}
|
||||
};
|
||||
|
||||
ErrorCode preActionCode = preAction(clientID, fromUser, topic, callbackWrapper);
|
||||
ErrorCode preActionCode = preAction(clientID, fromUser, topic, callbackWrapper, isAdmin, isRobotOrChannel);
|
||||
|
||||
if (preActionCode == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
ByteBuf ackPayload = Unpooled.buffer(1);
|
||||
|
||||
@@ -33,6 +33,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
||||
private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。
|
||||
private String mForwardUrl = null;
|
||||
private Set<Integer> mForwardMessageTypes = new HashSet<>();
|
||||
private String mMentionForwardUrl = null;
|
||||
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
|
||||
private boolean mNoForwardAdminMessage = false;
|
||||
|
||||
@@ -43,10 +44,8 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
||||
public SendMessageHandler() {
|
||||
super();
|
||||
|
||||
String forwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
|
||||
if (!StringUtil.isNullOrEmpty(forwardUrl)) {
|
||||
mForwardUrl = forwardUrl;
|
||||
|
||||
mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
|
||||
if (!StringUtil.isNullOrEmpty(mForwardUrl)) {
|
||||
String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types);
|
||||
if (!StringUtil.isNullOrEmpty(forwardTypes)) {
|
||||
String[] tss = forwardTypes.split(",");
|
||||
@@ -56,6 +55,8 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
||||
}
|
||||
}
|
||||
|
||||
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
|
||||
|
||||
try {
|
||||
mSensitiveType = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Filter_Type));
|
||||
} catch (Exception e) {
|
||||
@@ -169,10 +170,14 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
|
||||
}
|
||||
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
|
||||
|
||||
if (mForwardUrl != null && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
|
||||
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
|
||||
publisher.forwardMessage(message, mForwardUrl);
|
||||
}
|
||||
|
||||
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
|
||||
publisher.forwardMessage(message, mMentionForwardUrl);
|
||||
}
|
||||
|
||||
if (!isAdmin) {
|
||||
if(StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) {
|
||||
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
|
||||
|
||||
@@ -640,6 +640,17 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
}
|
||||
|
||||
if(fromMessageId == 1) { //lite mode
|
||||
if(maps == null || maps.isEmpty()) {
|
||||
head = 0;
|
||||
} else {
|
||||
head = maps.lastKey();
|
||||
}
|
||||
builder.setCurrent(head);
|
||||
builder.setHead(head);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
mReadLock.lock();
|
||||
int size = 0;
|
||||
try {
|
||||
@@ -2808,6 +2819,10 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
return ErrorCode.ERROR_CODE_IN_BLACK_LIST;
|
||||
}
|
||||
|
||||
if (friendData1 != null && friendData1.getState() == 0) {
|
||||
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
|
||||
}
|
||||
|
||||
WFCMessage.FriendRequest newRequest = WFCMessage.FriendRequest
|
||||
.newBuilder()
|
||||
.setFromUid(userId)
|
||||
@@ -2845,7 +2860,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
public ErrorCode handleFriendRequest(String userId, WFCMessage.HandleFriendRequest request, WFCMessage.Message.Builder msgBuilder, long[] heads, boolean isAdmin) {
|
||||
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
|
||||
|
||||
|
||||
boolean alreadyFriend = false;
|
||||
if(!isAdmin && request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted) {
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
|
||||
@@ -2857,10 +2872,9 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
for (FriendData fd : friends) {
|
||||
if (fd.getFriendUid().equals(request.getTargetUid())) {
|
||||
if (fd.getState() == 0) {
|
||||
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
|
||||
} else {
|
||||
break;
|
||||
alreadyFriend = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2939,7 +2953,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
|
||||
if (existRequest != null) {
|
||||
if (mFriendRequestExpiration > 0 && System.currentTimeMillis() - existRequest.getUpdateDt() > mFriendRequestExpiration) {
|
||||
if (!alreadyFriend && mFriendRequestExpiration > 0 && System.currentTimeMillis() - existRequest.getUpdateDt() > mFriendRequestExpiration) {
|
||||
return ErrorCode.ERROR_CODE_FRIEND_REQUEST_EXPIRED;
|
||||
} else {
|
||||
existRequest = existRequest.toBuilder().setStatus(request.getStatus()).setUpdateDt(System.currentTimeMillis()).build();
|
||||
@@ -2965,7 +2979,7 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
}
|
||||
}
|
||||
|
||||
if(request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted){
|
||||
if(!alreadyFriend && request.getStatus() == ProtoConstants.FriendRequestStatus.RequestStatus_Accepted){
|
||||
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
|
||||
FriendData friendData1 = new FriendData(userId, request.getTargetUid(), "", request.getExtra(), 0, 0, System.currentTimeMillis());
|
||||
databaseStore.persistOrUpdateFriendData(friendData1);
|
||||
@@ -2986,6 +3000,9 @@ public class MemoryMessagesStore implements IMessagesStore {
|
||||
|
||||
callbackRelationEvent(userId, request.getTargetUid(), 0, "1");
|
||||
}
|
||||
if(alreadyFriend) {
|
||||
return ErrorCode.ERROR_CODE_ALREADY_FRIENDS;
|
||||
}
|
||||
return ErrorCode.ERROR_CODE_SUCCESS;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -531,7 +531,7 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
if (session.getUsername().equals(username)) {
|
||||
return session;
|
||||
} else {
|
||||
cleanSession(clientID);
|
||||
cleanSession(null, clientID);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -739,9 +739,9 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
|
||||
|
||||
@Override
|
||||
public void disableSession(String clientId) {
|
||||
public void disableSession(String userId, String clientId) {
|
||||
Session session = sessions.get(clientId);
|
||||
if (session != null && session.getDeleted() == 0) {
|
||||
if (session != null && session.getDeleted() == 0 && (userId == null || session.getUsername().equals(userId))) {
|
||||
databaseStore.updateSessionDeleted(session.getUsername(), clientId, 1);
|
||||
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
||||
sessionSet.remove(clientId);
|
||||
@@ -750,7 +750,7 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanSession(String clientID) {
|
||||
public void cleanSession(String userId, String clientID) {
|
||||
LOG.info("Fooooooooo <{}>", clientID);
|
||||
|
||||
Session session = sessions.get(clientID);
|
||||
@@ -758,6 +758,11 @@ public class MemorySessionStore implements ISessionsStore {
|
||||
LOG.error("Can't find the session for client <{}>", clientID);
|
||||
return;
|
||||
}
|
||||
|
||||
if(userId != null && !session.getUsername().equals(userId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(session.username);
|
||||
sessionSet.remove(clientID);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.hazelcast.config.FileSystemXmlConfig;
|
||||
import com.hazelcast.core.*;
|
||||
import com.xiaoleilu.loServer.LoServer;
|
||||
import com.xiaoleilu.loServer.ServerSetting;
|
||||
import com.xiaoleilu.loServer.action.Action;
|
||||
import com.xiaoleilu.loServer.action.admin.AdminAction;
|
||||
import io.moquette.BrokerConstants;
|
||||
import io.moquette.persistence.*;
|
||||
@@ -257,6 +258,7 @@ public class Server {
|
||||
|
||||
initMediaServerConfig(config);
|
||||
|
||||
Action.init(config);
|
||||
String monitorEventAddress = config.getProperty(MONITOR_Exception_Event_Address);
|
||||
Utility.setMonitorEventAddress(monitorEventAddress);
|
||||
|
||||
|
||||
@@ -109,19 +109,19 @@ public class ClientSession {
|
||||
}
|
||||
|
||||
|
||||
public void disconnect(boolean cleanSession, boolean disableSession) {
|
||||
public void disconnect(String userId, boolean cleanSession, boolean disableSession) {
|
||||
if (cleanSession) {
|
||||
LOG.info("Client disconnected. Removing its subscriptions. ClientId={}", clientID);
|
||||
// cleanup topic subscriptions
|
||||
cleanSession();
|
||||
cleanSession(userId);
|
||||
} else if(disableSession) {
|
||||
LOG.info("Client disconnected. disable session. ClientId={}", clientID);
|
||||
m_sessionsStore.disableSession(this.clientID);
|
||||
m_sessionsStore.disableSession(userId, this.clientID);
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanSession() {
|
||||
m_sessionsStore.cleanSession(this.clientID);
|
||||
public void cleanSession(String userId) {
|
||||
m_sessionsStore.cleanSession(userId, this.clientID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -150,8 +150,8 @@ public interface ISessionsStore {
|
||||
*/
|
||||
int getSecondPhaseAckPendingMessages(String clientID);
|
||||
|
||||
void disableSession(String clientId);
|
||||
void cleanSession(String clientID);
|
||||
void disableSession(String userId, String clientId);
|
||||
void cleanSession(String userId, String clientID);
|
||||
|
||||
boolean isMultiEndpointSupported();
|
||||
|
||||
|
||||
@@ -44,12 +44,14 @@ import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import win.liyufan.im.HttpUtils;
|
||||
import win.liyufan.im.IMTopic;
|
||||
import win.liyufan.im.Utility;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
|
||||
import static io.moquette.spi.impl.InternalRepublisher.createPublishForQos;
|
||||
import static cn.wildfirechat.common.IMExceptionEvent.EventType.EVENT_CALLBACK_Exception;
|
||||
import static io.moquette.spi.impl.Utils.readBytesAndRewind;
|
||||
@@ -582,7 +584,7 @@ public class ProtocolProcessor {
|
||||
|
||||
//disconnect the session
|
||||
|
||||
m_sessionsStore.sessionForClient(clientID).disconnect(isDup, isRetain);
|
||||
m_sessionsStore.sessionForClient(clientID).disconnect(session.getUsername(), isDup, isRetain);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
||||
private final MessagesPublisher publisher;
|
||||
private final ISessionsStore m_sessionStore;
|
||||
private final ThreadPoolExecutorWrapper m_imBusinessExecutor;
|
||||
private final RateLimiter mLimitCounter = new RateLimiter(5, 100);
|
||||
|
||||
private HashMap<String, IMHandler> m_imHandlers = new HashMap<>();
|
||||
|
||||
@@ -148,19 +147,6 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
||||
|
||||
void imHandler(String clientID, String fromUser, String topic, byte[] payloadContent, IMCallback callback, boolean isAdmin, boolean isRobotOrChannel) {
|
||||
LOG.info("imHandler fromUser={}, topic={}", fromUser, topic);
|
||||
if(!isAdmin && !isRobotOrChannel && !mLimitCounter.isGranted(clientID + fromUser + topic)) {
|
||||
ByteBuf ackPayload = Unpooled.buffer();
|
||||
ackPayload.ensureWritable(1).writeByte(ERROR_CODE_OVER_FREQUENCY.getCode());
|
||||
try {
|
||||
callback.onIMHandled(ERROR_CODE_OVER_FREQUENCY, ackPayload);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Utility.printExecption(LOG, e);
|
||||
}
|
||||
LOG.warn("user {} request over frequency", fromUser);
|
||||
return;
|
||||
}
|
||||
|
||||
IMCallback wrapper = (errorcode, ackPayload) -> {
|
||||
ackPayload.resetReaderIndex();
|
||||
byte code = ackPayload.readByte();
|
||||
@@ -215,7 +201,7 @@ public class Qos1PublishHandler extends QosPublishHandler {
|
||||
|
||||
IMHandler handler = m_imHandlers.get(topic);
|
||||
if (handler != null) {
|
||||
handler.doHandler(clientID, fromUser, topic, payloadContent, wrapper, isAdmin);
|
||||
handler.doHandler(clientID, fromUser, topic, payloadContent, wrapper, isAdmin, isRobotOrChannel);
|
||||
} else {
|
||||
LOG.error("imHandler unknown topic={}", topic);
|
||||
ByteBuf ackPayload = Unpooled.buffer();
|
||||
|
||||
@@ -107,8 +107,12 @@ public class DBUtil {
|
||||
}
|
||||
|
||||
Flyway flyway = Flyway.configure().dataSource(comboPooledDataSource).locations(migrateLocation).baselineOnMigrate(true).load();
|
||||
if(!IsEmbedDB && (flyway.info().current() == null || flyway.info().current().getVersion().getMajor().intValue() < 43)) {
|
||||
System.out.println("数据库执行初始化或升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
|
||||
if(!IsEmbedDB) {
|
||||
if(flyway.info().current() == null) {
|
||||
System.out.println("数据库执行初始化需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
|
||||
} else if(flyway.info().current().getVersion().getMajor().intValue() < 43) {
|
||||
System.out.println("数据库执行升级需要较长时间,可能长达数分钟或更长,请耐心等待,不要中断。。。");
|
||||
}
|
||||
}
|
||||
flyway.migrate();
|
||||
|
||||
|
||||
@@ -80,4 +80,6 @@ public interface IMTopic {
|
||||
|
||||
String LoadRemoteMessagesTopic = "LRM";
|
||||
String KickoffPCClientTopic = "KPCC";
|
||||
|
||||
String ClearSessionTopic = "CST";
|
||||
}
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.71</version>
|
||||
<version>0.74</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<!--<relativePath>../pom.xml</relativePath>-->
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.71</version>
|
||||
<version>0.74</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>distribution</artifactId>
|
||||
|
||||
@@ -106,7 +106,7 @@ friend.disable_search false
|
||||
##禁止按照昵称搜索用户。
|
||||
friend.disable_nick_name_search false
|
||||
|
||||
##禁止发送好友邀请,通过server api添加好友不受此限制
|
||||
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
|
||||
friend.disable_friend_request false
|
||||
|
||||
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
|
||||
@@ -150,6 +150,20 @@ group.visible_quit_or_kickoff_notification 0
|
||||
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
|
||||
#sync.data_part_size 1000
|
||||
|
||||
#*********************************************************************
|
||||
# 限频配置
|
||||
#*********************************************************************
|
||||
##API接口限频设置,时间是10秒内允许的请求次数。
|
||||
##管理api频率限制,默认是10秒10000次。
|
||||
http.admin.rate_limit 10000
|
||||
##机器人api频率限制,默认是10秒1000次,按照机器人ID区分。
|
||||
http.robot.rate_limit 1000
|
||||
##频道api频率限制,默认是10秒1000次,按照频道ID区分。
|
||||
http.channel.rate_limit 1000
|
||||
|
||||
##客户端请求频率限制,默认是5秒100次。通常情况下不要修改这个值。
|
||||
client.request_rate_limit 100
|
||||
|
||||
#*********************************************************************
|
||||
# hazelcase configuration
|
||||
#*********************************************************************
|
||||
@@ -159,7 +173,8 @@ hazelcast.configuration config/hazelcast.xml
|
||||
# Media server configuration
|
||||
#*********************************************************************
|
||||
##是否使用七牛云存储。1使用七牛;0使用内存文件服务器。默认的七牛账户信息不可用,请在七牛官网申请账户并配置
|
||||
##配置使用七牛请参考:https://docs.wildfirechat.cn/server/media_server.html
|
||||
##专业版另外支持阿里云存储和野火私有存储,关于野火私有存储请参考:https://github.com/wildfirechat/WF-minio
|
||||
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
|
||||
media.server.use_qiniu 0
|
||||
|
||||
##媒体类型分类
|
||||
@@ -262,6 +277,11 @@ push.ios.server.address http://localhost:8085/ios/push
|
||||
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
|
||||
#message.forward.types 1,2,3
|
||||
|
||||
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
|
||||
|
||||
|
||||
##设备信息转发地址
|
||||
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
|
||||
#things_message.forward.url http://localhost:8087/im_event/things/message
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<version>0.71</version>
|
||||
<version>0.74</version>
|
||||
<name>Wildfire Chat</name>
|
||||
<description>Wildfire Chat Server</description>
|
||||
<inceptionYear>2019</inceptionYear>
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>wildfirechat-parent</artifactId>
|
||||
<groupId>cn.wildfirechat</groupId>
|
||||
<version>0.71</version>
|
||||
<version>0.74</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public class Main {
|
||||
}
|
||||
|
||||
IMResult<Void> result = RelationAdmin.sendFriendRequest("ff1", "ff2", "hello", true);
|
||||
if (result != null && result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
if (result != null && (result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS || result.getErrorCode() == ErrorCode.ERROR_CODE_ALREADY_FRIENDS)) {
|
||||
System.out.println("send friend request success");
|
||||
} else {
|
||||
System.out.println("failure");
|
||||
@@ -308,7 +308,7 @@ public class Main {
|
||||
}
|
||||
|
||||
result = RelationAdmin.sendFriendRequest("ff1", "ff2", "hello3", true);
|
||||
if (result != null && result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
|
||||
if (result != null && (result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS || result.getErrorCode() == ErrorCode.ERROR_CODE_ALREADY_FRIENDS)) {
|
||||
System.out.println("success");
|
||||
} else {
|
||||
System.out.println("send friend request success");
|
||||
|
||||
Reference in New Issue
Block a user