Compare commits

..
36 Commits
Author SHA1 Message Date
heavyrain2012 c74cddc387 version 0.75 2021-11-17 16:30:25 +08:00
heavyrain2012 b2e4fd9546 update 2021-11-16 16:50:48 +08:00
heavyrain2012 9e8c364642 机器人不允许获取token和登录 2021-11-16 16:49:09 +08:00
heavyrain2012 8127ec03ad 好友欢迎语排序问题 2021-11-10 11:30:06 +08:00
heavyrain2012 c02e9066be update version 0.74 2021-11-04 15:00:43 +08:00
heavyrain2012 ffd1492ea2 添加限频配置项 2021-10-26 10:11:05 +08:00
heavyrain2012 5e9bbcb1e9 修改注释 2021-10-16 22:20:01 +08:00
heavyrain2012 63770c75fa 修正server api设置好友昵称的问题 2021-10-16 19:43:41 +08:00
heavyrain2012 bf0eb5b8aa 更新注释 2021-10-16 18:12:48 +08:00
heavyrain2012 8cbf66930f 修改注释 2021-10-16 18:11:46 +08:00
heavyrain2012 ce48f67b04 发送好友请求时,如果已经是好友了,返回已经是好友的错误码 2021-10-16 18:03:13 +08:00
heavyrain2012 2fdf11006d 解决双方同时加好友后,有一方接受后,另外一方无法接受问题 2021-10-12 07:59:39 +08:00
heavyrain2012 4a2d7a4cc5 version to 0.73 2021-10-12 07:30:18 +08:00
heavyrain2012 0513da8993 客户端断开链接优化 2021-10-12 07:28:47 +08:00
heavyrain2012 d400b74bff version to 0.72 2021-10-08 12:05:14 +08:00
heavyrain2012 3d7220d79c 优化disconnect功能 2021-10-08 11:07:08 +08:00
heavyrain2012 4bcf784a6d 优化数据库表结构升级日志提示 2021-09-29 19:50:20 +08:00
heavyrain2012 6d021568d3 add log 2021-09-29 19:39:01 +08:00
heavyrain2012 44f54ecd17 提醒消息判断是不是server api发送 2021-09-29 10:19:24 +08:00
heavyrain2012 4cd0349911 提醒消息转发 2021-09-29 10:17:32 +08:00
heavyrain2012 9ad78f2fb8 支持客户端lite mode 2021-09-27 10:28:58 +08:00
heavyrain2012 b284d10d0f version to 0.71 2021-09-24 21:20:40 +08:00
heavyrain2012 4fd87eb605 add run time in version info 2021-09-20 21:01:40 +08:00
heavyrain2012 f39fd3abd5 version info add run time 2021-09-20 20:44:04 +08:00
heavyrain2012 db25a6ae04 解决只有pushdata没有pushcontent的推送问题 2021-09-20 08:42:47 +08:00
heavyrain2012 edbed585ce 撤回消息推送 2021-09-20 08:21:16 +08:00
heavyrain2012 9ca69d7318 添加voip免打扰开关 2021-09-20 08:12:26 +08:00
heavyrain2012 eeaa9eefdf 添加开关
1,是否不转发管理员消息
2,客户端是否支持kickoff事件
2021-09-07 22:07:54 +08:00
heavyrain2012 92c9d761ed add kicked off error code 2021-09-07 08:29:29 +08:00
heavyrain2012 1f9824b283 version to 0.70 2021-09-04 11:23:40 +08:00
heavyrain2012 c7e20632a8 解决单个敏感词无法过滤的问题 2021-09-02 11:55:09 +08:00
heavyrain2012 81a2ab8239 更新命名 2021-09-01 14:48:11 +08:00
heavyrain2012 f62e3f3cef 添加设置用户关系中的附加信息 2021-09-01 14:39:59 +08:00
heavyrain2012 5ce8165514 fix word spell 2021-09-01 13:46:57 +08:00
heavyrain2012 106756ded4 修改敏感词替换错误问题 2021-09-01 13:44:49 +08:00
imndx 3f93be0ece update readme 2021-08-15 10:48:05 +08:00
44 changed files with 555 additions and 98 deletions
-1
View File
@@ -90,4 +90,3 @@
## License
1. Under the Creative Commons Attribution-NoDerivs 3.0 Unported license. See the [LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE) file for details.
2. Under the Anti 996 License. See the [Anti 996 LICENSE](https://github.com/wildfirechat/server/blob/wildfirechat/LICENSE_996) file for details.
+31 -2
View File
@@ -100,13 +100,18 @@ message.group_mute_exception_types 401,402,403,404,405,407,408,410,411
## 用户被全局禁言时允许发送的消息类型
message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
## 消息转发功能开启时,开关是否不转发server api接口消息。
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
message.no_forward_admin_message false
##禁止搜索用户
friend.disable_search false
##禁止按照昵称搜索用户。
friend.disable_nick_name_search false
##禁止发送好友邀请,通过server api添加好友不受此限制
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
friend.disable_friend_request false
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
@@ -149,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
#*********************************************************************
@@ -158,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
##媒体类型分类
@@ -215,6 +235,10 @@ server.multi_platform_notification true
# 如果这里改为false,客户端那边需要同步修改pc在线默认通知状态,方法是client的setDefaultSilentWhenPcOnline函数。
server.mobile_default_silent_when_pc_online true
## 客户端是否支持kickoff事件。当客户端被其他端登录踢出时,如果此开关为false时,客户端协议栈上报secret_mismatch。如果此开关为ture时,客户端协议栈上报kicked_off。
## 客户端被踢时上报kicked_off是在2021.9.15之后才加上的。如果客户端协议栈全部为此日期之后的版本才可以打开此开关。
server.client_support_kickoff_event false
#*********************************************************************
# Push server configuration
#*********************************************************************
@@ -255,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
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.69</version>
<version>0.75</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -32,6 +32,7 @@ public class PushMessage {
public String voipDeviceToken;
public boolean isHiddenDetail;
public String language;
public long messageId;
public PushMessage(String sender, int conversationType, String target, int line, int messageContentType, long serverTime, String senderName, String targetName, int unReceivedMsg, int mentionedType, boolean isHiddenDetail, String language) {
this.sender = sender;
@@ -49,6 +50,10 @@ public class PushMessage {
this.pushMessageType = PushServer.PushMessageType.PUSH_MESSAGE_TYPE_VOIP_BYE;
} else if(cntType == 401) {
this.pushMessageType = PushServer.PushMessageType.PUSH_MESSAGE_TYPE_VOIP_ANSWER;
} else if(cntType == 80) {
this.pushMessageType = PushServer.PushMessageType.PUSH_MESSAGE_TYPE_RECALLED;
} else if(cntType == 81) {
this.pushMessageType = PushServer.PushMessageType.PUSH_MESSAGE_TYPE_DELETED;
} else {
this.pushMessageType = PushServer.PushMessageType.PUSH_MESSAGE_TYPE_NORMAL;
}
@@ -35,6 +35,8 @@ public class PushServer {
int PUSH_MESSAGE_TYPE_VOIP_BYE = 2;
int PUSH_MESSAGE_TYPE_FRIEND_REQUEST = 3;
int PUSH_MESSAGE_TYPE_VOIP_ANSWER = 4;
int PUSH_MESSAGE_TYPE_RECALLED = 5;
int PUSH_MESSAGE_TYPE_DELETED = 6;
}
private static PushServer INSTANCE = new PushServer();
@@ -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);
@@ -0,0 +1,39 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputUpdateFriendExtra;
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.handler.codec.http.FullHttpRequest;
@Route(APIPath.Friend_Set_Extra)
@HttpMethod("POST")
public class FriendExtraPutAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputUpdateFriendExtra input = getRequestBody(request.getNettyRequest(), InputUpdateFriendExtra.class);
ErrorCode errorCode = messagesStore.setFriendExtraRequest(input.getOperator(), input.getTargetId(), input.getExtra(), new long[1]);
setResponseContent(RestResult.resultOf(errorCode), response);
}
return true;
}
}
@@ -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,10 +128,13 @@ 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";
public static final String SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE = "server.mobile_default_silent_when_pc_online";
public static final String SERVER_CLIENT_SUPPORT_KICKOFF_EVENT = "server.client_support_kickoff_event";
public static final String MESSAGE_ROAMING = "message.roaming";
public static final String MESSAGE_Remote_History_Message = "message.remote_history_message";
@@ -142,6 +145,8 @@ public final class BrokerConstants {
public static final String MESSAGE_Blacklist_Strategy = "message.blacklist.strategy";
public static final String MESSAGE_NO_Forward_Admin_Message = "message.no_forward_admin_message";
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";
@@ -173,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;
}
}
@@ -9,6 +9,7 @@
package io.moquette.imhandler;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.hutool.system.UserInfo;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.moquette.spi.impl.security.TokenAuthenticator;
@@ -21,6 +22,11 @@ public class GetTokenHandler extends IMHandler<WFCMessage.GetTokenRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, boolean isAdmin, WFCMessage.GetTokenRequest request, Qos1PublishHandler.IMCallback callback) {
MemorySessionStore.Session session = m_sessionsStore.updateOrCreateUserSession(fromUser, clientID, request.getPlatform());
WFCMessage.User userInfo = m_messagesStore.getUserInfo(fromUser);
if(userInfo != null && userInfo.getType() == 1) {
return ErrorCode.ERROR_CODE_ROBOT_NO_TOKEN;
}
TokenAuthenticator authenticator = new TokenAuthenticator();
String strToken = authenticator.generateToken(fromUser);
String result = strToken + "|" + session.getSecret() + "|" + session.getDbSecret();
@@ -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)
@@ -57,8 +58,7 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
builder.setFromUser(request.getTargetUid());
builder.setConversation(WFCMessage.Conversation.newBuilder().setTarget(fromUser).setLine(0).setType(ProtoConstants.ConversationType.ConversationType_Private).build());
builder.setContent(contentBuilder);
timestamp = System.currentTimeMillis();
builder.setServerTimestamp(timestamp);
builder.setServerTimestamp(++timestamp);
messageId = MessageShardingUtil.generateId();
builder.setMessageId(messageId);
@@ -73,10 +73,8 @@ public class HandleFriendRequestHandler extends IMHandler<WFCMessage.HandleFrien
builder.setContent(contentBuilder);
messageId = MessageShardingUtil.generateId();
builder.setMessageId(messageId);
timestamp = System.currentTimeMillis();
builder.setServerTimestamp(timestamp);
builder.setServerTimestamp(++timestamp);
saveAndPublish(request.getTargetUid(), null, builder.build());
} catch (Exception e) {
e.printStackTrace();
}
@@ -86,6 +84,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,7 +33,9 @@ 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;
private String mRemoteSensitiveServerUrl = null;
private Set<Integer> mRemoteSensitiveMessageTypes;
@@ -42,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(",");
@@ -55,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) {
@@ -86,6 +88,13 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
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);
}
}
@Override
@@ -161,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()))) {
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());
@@ -121,6 +121,7 @@ public class MemoryMessagesStore implements IMessagesStore {
private volatile long lastUpdateSensitiveTime = 0;
private ConcurrentHashMap<String, Boolean> userGlobalSlientMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Boolean> userVoipSlientMap = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Boolean> userPushHiddenDetail = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Boolean> userConvSlientMap = new ConcurrentHashMap<>();
@@ -639,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 {
@@ -2041,7 +2053,20 @@ public class MemoryMessagesStore implements IMessagesStore {
String recalledContent = json.toJSONString();
message = message.toBuilder().setContent(WFCMessage.MessageContent.newBuilder().setContent(operatorId).setType(80).setData(ByteString.copyFrom(String.valueOf(messageUid).getBytes())).setExtra(recalledContent)).build();
JSONObject pushData = new JSONObject();
pushData.put("messageUid", messageUid);
message = message.toBuilder().setContent(WFCMessage.MessageContent.newBuilder()
.setContent(operatorId)
.clearSearchableContent()
.clearPushContent()
.setPersistFlag(1)
.setExpireDuration(0)
.setMentionedType(0)
.setType(80)
.setData(ByteString.copyFrom(String.valueOf(messageUid).getBytes()))
.setPushData(pushData.toJSONString())
.setExtra(recalledContent)).build();
messageBundle.setMessage(message);
messageBundle.setFromClientId(clientId);
@@ -2794,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)
@@ -2831,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);
@@ -2843,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;
}
}
}
@@ -2925,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();
@@ -2951,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);
@@ -2972,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 {
@@ -3079,6 +3110,43 @@ public class MemoryMessagesStore implements IMessagesStore {
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
public ErrorCode setFriendExtraRequest(String fromUser, String targetUserId, String extra, long[] heads){
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
FriendData friendData = null;
Collection<FriendData> friends = friendsMap.get(fromUser);
if (friends == null || friends.size() == 0) {
friends = loadFriend(friendsMap, fromUser);
}
for (FriendData fd:friends) {
if (fd.getFriendUid().equals(targetUserId)) {
friendData = fd;
break;
}
}
if (friendData == null) {
friendData = new FriendData();
friendData.setUserId(fromUser);
friendData.setFriendUid(targetUserId);
}
friendData.setExtra(extra);
friendData.setTimestamp(System.currentTimeMillis());
databaseStore.persistOrUpdateFriendData(friendData);
heads[0] = friendData.getTimestamp();
friendsMap.remove(fromUser);
callbackRelationEvent(fromUser, targetUserId, 3, extra);
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
public ErrorCode handleJoinChatroom(String userId, String clientId, String chatroomId) {
IMap<String, WFCMessage.ChatroomInfo> chatroomInfoMap = m_Server.getHazelcastInstance().getMap(CHATROOMS);
@@ -3341,6 +3409,8 @@ public class MemoryMessagesStore implements IMessagesStore {
userConvSlientMap.remove(key);
} else if(request.getScope() == UserSettingScope.kUserSettingGlobalSilent) {
userGlobalSlientMap.remove(userId);
} else if(request.getScope() == UserSettingScope.kUserSettingVoipSilent) {
userVoipSlientMap.remove(userId);
} else if(request.getScope() == UserSettingScope.kUserSettingHiddenNotificationDetail) {
userPushHiddenDetail.remove(userId);
}
@@ -3371,6 +3441,21 @@ public class MemoryMessagesStore implements IMessagesStore {
return slient;
}
@Override
public boolean getUserVoipSilent(String userId) {
Boolean slient = userVoipSlientMap.get(userId);
if (slient == null) {
WFCMessage.UserSettingEntry entry = getUserSetting(userId, UserSettingScope.kUserSettingVoipSilent, null);
if (entry == null || !entry.getValue().equals("1")) {
slient = false;
} else {
slient = true;
}
userVoipSlientMap.put(userId, slient);
}
return slient;
}
@Override
public boolean getUserPushHiddenDetail(String userId) {
Boolean hidden = userPushHiddenDetail.get(userId);
@@ -39,6 +39,7 @@ public class MemorySessionStore implements ISessionsStore {
private static final Logger LOG = LoggerFactory.getLogger(MemorySessionStore.class);
private boolean supportMultiEndpoint = false;
private boolean clientSupportKickoff = false;
public static class Session implements Comparable<Session>{
final String clientID;
@@ -280,7 +281,14 @@ public class MemorySessionStore implements ISessionsStore {
this.databaseStore = databaseStore;
try {
supportMultiEndpoint = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_MULTI_ENDPOINT));
supportMultiEndpoint = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_MULTI_ENDPOINT, "false"));
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
clientSupportKickoff = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.SERVER_CLIENT_SUPPORT_KICKOFF_EVENT, "false"));
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
@@ -456,20 +464,32 @@ public class MemorySessionStore implements ISessionsStore {
if (session != null && session.getDeleted() == 0) {
LOG.error("already exists a session for client <{}>, bad condition", clientID);
throw new IllegalArgumentException("Can't create a session with the ID of an already existing" + clientID);
return ErrorCode.ERROR_CODE_SUCCESS;
}
if (session != null && session.getDeleted() > 0) {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
if(clientSupportKickoff) {
return ErrorCode.ERROR_CODE_KICKED_OFF;
} else {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
}
ClientSession clientSession = new ClientSession(clientID, this);
session = databaseStore.getSession(username, clientID, clientSession);
if (session == null || session.getDeleted() > 0) {
if (session == null) {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
if(session.getDeleted() > 0) {
if(clientSupportKickoff) {
return ErrorCode.ERROR_CODE_KICKED_OFF;
} else {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
}
sessions.put(clientID, session);
ConcurrentSkipListSet<String> sessionSet = getUserSessionSet(username);
sessionSet.add(clientID);
@@ -511,7 +531,7 @@ public class MemorySessionStore implements ISessionsStore {
if (session.getUsername().equals(username)) {
return session;
} else {
cleanSession(clientID);
cleanSession(null, clientID);
}
}
return null;
@@ -719,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);
@@ -730,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);
@@ -738,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.*;
@@ -80,6 +81,7 @@ public class Server {
}
private ServerAcceptor m_acceptor;
private long startTime;
private boolean m_shutdowning = false;
@@ -102,6 +104,10 @@ public class Server {
System.out.println(BANNER);
}
public Server() {
startTime = System.currentTimeMillis();
}
public static void start(String[] args) throws IOException {
instance = new Server();
final IConfig config = defaultConfig();
@@ -252,6 +258,7 @@ public class Server {
initMediaServerConfig(config);
Action.init(config);
String monitorEventAddress = config.getProperty(MONITOR_Exception_Event_Address);
Utility.setMonitorEventAddress(monitorEventAddress);
@@ -513,4 +520,7 @@ public class Server {
public ThreadPoolExecutorWrapper getCallbackScheduler() {
return callbackScheduler;
}
public long getRunTime() {
return (System.currentTimeMillis() - startTime)/1000;
}
}
@@ -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);
}
@@ -182,6 +182,7 @@ public interface IMessagesStore {
ErrorCode SyncFriendRequestUnread(String userId, long unreadDt, long[] head);
ErrorCode isAllowUserMessage(String fromUser, String userId);
ErrorCode setFriendAliasRequest(String fromUser, String targetUserId, String alias, long[] head);
ErrorCode setFriendExtraRequest(String fromUser, String targetUserId, String extra, long[] head);
ErrorCode handleJoinChatroom(String userId, String clientId, String chatroomId);
ErrorCode handleQuitChatroom(String userId, String clientId, String chatroomId);
@@ -194,6 +195,7 @@ public interface IMessagesStore {
long updateUserSettings(String userId, WFCMessage.ModifyUserSettingReq request, String clientId);
void clearUserSettings(String userId);
boolean getUserGlobalSilent(String userId);
boolean getUserVoipSilent(String userId);
boolean getUserPushHiddenDetail(String userId);
boolean getUserConversationSilent(String userId, WFCMessage.Conversation conversation);
@@ -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();
@@ -160,9 +160,9 @@ public class MessagesPublisher {
}
}
private void publish2Receivers(String sender, int conversationType, String target, int line, long messageHead, Collection<String> receivers, String pushContent, String pushData, String exceptClientId, int pullType, int messageContentType, long serverTime, int mentionType, List<String> mentionTargets, int persistFlag) {
private void publish2Receivers(String sender, int conversationType, String target, int line, long messageId, Collection<String> receivers, String pushContent, String pushData, String exceptClientId, int pullType, int messageContentType, long serverTime, int mentionType, List<String> mentionTargets, int persistFlag) {
if (persistFlag == Transparent) {
publishTransparentMessage2Receivers(messageHead, receivers, pullType, exceptClientId);
publishTransparentMessage2Receivers(messageId, receivers, pullType, exceptClientId);
return;
}
@@ -174,7 +174,7 @@ public class MessagesPublisher {
WFCMessage.Robot robot = m_messagesStore.getRobot(user);
if (robot != null && !StringUtil.isNullOrEmpty(robot.getCallback())) {
if (message == null) {
message = m_messagesStore.getMessage(messageHead);
message = m_messagesStore.getMessage(messageId);
}
final WFCMessage.Message finalMsg = message;
Server.getServer().getCallbackScheduler().execute(() -> {
@@ -191,9 +191,9 @@ public class MessagesPublisher {
}
long messageSeq;
if (pullType != ProtoConstants.PullType.Pull_ChatRoom) {
messageSeq = m_messagesStore.insertUserMessages(sender, conversationType, target, line, messageContentType, user, messageHead);
messageSeq = m_messagesStore.insertUserMessages(sender, conversationType, target, line, messageContentType, user, messageId);
} else {
messageSeq = m_messagesStore.insertChatroomMessages(user, line, messageHead);
messageSeq = m_messagesStore.insertChatroomMessages(user, line, messageId);
}
Collection<Session> sessions = m_sessionsStore.sessionForUser(user);
@@ -253,12 +253,18 @@ public class MessagesPublisher {
}
boolean isSilent;
boolean isVoipSilent;
boolean isNoDisturb;
boolean isConvSilent = false;
if (pullType == ProtoConstants.PullType.Pull_ChatRoom) {
isSilent = true;
isConvSilent = true;
isVoipSilent = true;
isNoDisturb = true;
} else {
isSilent = false;
isVoipSilent = false;
isNoDisturb = false;
if (!user.equals(sender)) {
WFCMessage.Conversation conversation;
@@ -278,14 +284,19 @@ public class MessagesPublisher {
isSilent = true;
}
if (m_messagesStore.getUserVoipSilent(user)) {
LOG.info("The user {} is voip sliented", user);
isVoipSilent = true;
}
if (!isSilent && isPcOnline && m_messagesStore.getSilentWhenPcOnline(user)) {
LOG.info("The user {} pc online and silent when pc online", user);
isSilent = true;
}
if (!isSilent && m_messagesStore.isUserNoDisturbing(user)) {
if (m_messagesStore.isUserNoDisturbing(user)) {
LOG.info("The user {} is no disturbing", user);
isSilent = true;
isNoDisturb = true;
}
}
@@ -331,8 +342,9 @@ public class MessagesPublisher {
isConvSilent = false;
}
}
boolean isVoip = messageContentType == 402 || messageContentType == 400 || messageContentType == 401 || messageContentType == 406;
if ((StringUtil.isNullOrEmpty(pushContent) && messageContentType != 402 && messageContentType != 400 && messageContentType != 401 && messageContentType != 406)) {
if (StringUtil.isNullOrEmpty(pushContent) && StringUtil.isNullOrEmpty(pushData) && !isVoip) {
LOG.info("push content is empty and contenttype is {}", messageContentType);
continue;
}
@@ -342,16 +354,26 @@ public class MessagesPublisher {
continue;
}
if (isConvSilent) {
if (isConvSilent && !isVoip) {
LOG.info("Silent of user or conversation");
continue;
}
if (isSilent) {
if (isSilent && !isVoip) {
LOG.info("Silent of user");
continue;
}
if(isVoip && isVoipSilent) {
LOG.info("voip silent of user");
continue;
}
if(isNoDisturb) {
LOG.info("no disturb of user");
continue;
}
boolean isHiddenDetail = m_messagesStore.getUserPushHiddenDetail(user);
if(!nameLoaded) {
@@ -367,7 +389,7 @@ public class MessagesPublisher {
name = fd.getAlias();
}
}
this.messageSender.sendPush(sender, conversationType, target, line, messageHead, targetSession.getClientID(), pushContent, pushData, messageContentType, serverTime, name, targetName, targetSession.getUnReceivedMsgs(), curMentionType, isHiddenDetail, targetSession.getLanguage());
this.messageSender.sendPush(sender, conversationType, target, line, messageId, targetSession.getClientID(), pushContent, pushData, messageContentType, serverTime, name, targetName, targetSession.getUnReceivedMsgs(), curMentionType, isHiddenDetail, targetSession.getLanguage());
}
}
@@ -37,11 +37,12 @@ class PersistentQueueMessageSender {
this.connectionDescriptorStore = connectionDescriptorStore;
}
void sendPush(String sender, int conversationType, String target, int line, long messageHead, String deviceId, String pushContent, String pushData, int messageContentType, long serverTime, String senderName, String targetName, int unReceivedMsg, int mentionType, boolean isHiddenDetail, String language) {
void sendPush(String sender, int conversationType, String target, int line, long messageId, String deviceId, String pushContent, String pushData, int messageContentType, long serverTime, String senderName, String targetName, int unReceivedMsg, int mentionType, boolean isHiddenDetail, String language) {
LOG.info("Send push to {}, message from {}", deviceId, sender);
PushMessage pushMessage = new PushMessage(sender, conversationType, target, line, messageContentType, serverTime, senderName, targetName, unReceivedMsg, mentionType, isHiddenDetail, language);
pushMessage.pushContent = pushContent;
pushMessage.pushData = pushData;
pushMessage.messageId = messageId;
PushServer.getServer().pushMessage(pushMessage, deviceId, pushContent);
}
@@ -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();
@@ -8,6 +8,8 @@
package win.liyufan.im;
import io.moquette.server.Server;
import java.io.*;
import java.util.Properties;
@@ -34,6 +36,8 @@ public class GitRepositoryState {
public final String buildTime;
public final String buildHost;
public final String buildVersion;
public final long runTime;
private GitRepositoryState(Properties properties) {
this.tags = String.valueOf(properties.get("git.tags"));
@@ -58,6 +62,7 @@ public class GitRepositoryState {
this.buildTime = String.valueOf(properties.get("git.build.time"));
this.buildHost = String.valueOf(properties.get("git.build.host"));
this.buildVersion = String.valueOf(properties.get("git.build.version"));
this.runTime = Server.getServer().getRunTime();
}
public static GitRepositoryState getGitRepositoryState() throws IOException {
@@ -80,4 +80,6 @@ public interface IMTopic {
String LoadRemoteMessagesTopic = "LRM";
String KickoffPCClientTopic = "KPCC";
String ClearSessionTopic = "CST";
}
@@ -31,11 +31,12 @@ public class SensitiveFilter {
return sensitiveWords;
}
String originalText = text;
text = text.toLowerCase();
for(int i=0;i<text.length();i++){
int sensitiveWordLength = getSensitiveWordLength(text, i, matchType);
if(sensitiveWordLength>0){
String sensitiveWord = text.substring(i, i + sensitiveWordLength);
String sensitiveWord = originalText.substring(i, i + sensitiveWordLength);
sensitiveWords.add(sensitiveWord);
if(matchType==MatchType.MIN_MATCH){
break;
@@ -93,7 +94,7 @@ public class SensitiveFilter {
Iterator<String> iterator = sensitiveWords.iterator();
while (iterator.hasNext()){
currentWord=iterator.next();
if(currentWord==null||currentWord.trim().length()<2){ //敏感词长度必须大于等于2
if(currentWord==null||currentWord.trim().length()<1){ //敏感词长度必须大于等于1
continue;
}
currentMap=sensitiveWordsMap;
@@ -10,4 +10,9 @@ public interface UserSettingScope {
int kUserSettingPCOnline = 10;
int kUserSettingMuteWhenPCOnline = 15;
int kUserSettingNoDisturbing = 17;
int kUserSettingConversationClearMessage = 18;
int kUserSettingConversationDraft = 19;
int kUserSettingDisableSyncDraft = 20;
int kUserSettingVoipSilent = 21;
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.69</version>
<version>0.75</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -39,6 +39,7 @@ public interface APIPath {
String Blacklist_Get_List = "/admin/blacklist/list";
String Friend_Get_Alias = "/admin/friend/get_alias";
String Friend_Set_Alias = "/admin/friend/set_alias";
String Friend_Set_Extra = "/admin/friend/set_extra";
String Friend_Send_Request = "/admin/friend/send_request";
String Friend_Get_Requests = "/admin/friend/get_requests";
String Relation_Get = "/admin/relation/get";
@@ -20,6 +20,7 @@ public enum ErrorCode {
//Auth error
ERROR_CODE_TOKEN_ERROR(6, "token error"),
ERROR_CODE_KICKED_OFF(7, "kicked off"),
ERROR_CODE_USER_FORBIDDEN(8, "user forbidden"),
//Message error
@@ -42,6 +43,9 @@ public enum ErrorCode {
ERROR_CODE_ALREADY_FRIENDS(23, "already friends"),
ERROR_CODE_RECALL_TIME_EXPIRED(24, "time expired"),
ERROR_CODE_LOCK_ALREADY_LOCKED(25, "already locked"),
ERROR_CODE_NOT_YOUR_LOCKED(26, "unload failure, not your lock"),
ERROR_CODE_ROBOT_NO_TOKEN(27, "robot no token"),
ERROR_CODE_CONFERENCE_ROOM_NOT_EXIST(60, "conference room not exist"),
ERROR_CODE_USER_NOT_PREPARED(238, "user not prepared"),
@@ -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;
public class InputUpdateFriendExtra {
private String operator;
private String targetId;
private String extra;
public String getTargetId() {
return targetId;
}
public void setTargetId(String targetId) {
this.targetId = targetId;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getExtra() {
return extra;
}
public void setExtra(String extra) {
this.extra = extra;
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.69</version>
<version>0.75</version>
</parent>
<artifactId>distribution</artifactId>
@@ -94,13 +94,19 @@ message.blacklist_exception_types 401,402,403,404,405,407,408,410,411
message.group_mute_exception_types 401,402,403,404,405,407,408,410,411
## 用户被全局禁言时允许发送的消息类型
message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
## 消息转发功能开启时,开关是否不转发server api接口消息。
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
message.no_forward_admin_message false
##禁止搜索用户
friend.disable_search false
##禁止按照昵称搜索用户。
friend.disable_nick_name_search false
##禁止发送好友邀请,通过server api添加好友不受此限制
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
friend.disable_friend_request false
##好友请求重复发送的间隔,单位是毫秒,默认是7天,0为不限制
@@ -144,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
#*********************************************************************
@@ -153,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
##媒体类型分类
@@ -212,6 +233,10 @@ server.multi_platform_notification true
# 如果这里改为false,客户端那边需要同步修改pc在线默认通知状态,方法是client的setDefaultSilentWhenPcOnline函数。
server.mobile_default_silent_when_pc_online true
## 客户端是否支持kickoff事件。当客户端被其他端登录踢出时,如果此开关为false时,客户端协议栈上报secret_mismatch。如果此开关为ture时,客户端协议栈上报kicked_off。
## 客户端被踢时上报kicked_off是在2021.9.15之后才加上的。如果客户端协议栈全部为此日期之后的版本才可以打开此开关。
server.client_support_kickoff_event false
#*********************************************************************
# Push server configuration
#*********************************************************************
@@ -252,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
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>0.69</version>
<version>0.75</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.69</version>
<version>0.75</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");
@@ -381,6 +381,15 @@ public class Main {
System.exit(-1);
}
String friendExtra = "hello friend extra";
IMResult<Void> setExtraResult = RelationAdmin.updateFriendExtra("ff1", "ff2", friendExtra);
if (setExtraResult != null && setExtraResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("set friend extra success");
} else {
System.out.println("set friend extra failure");
System.exit(-1);
}
IMResult<RelationPojo> getRelation = RelationAdmin.getRelation("ff1", "ff2");
if (getRelation != null && getRelation.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("get friend relation success");
@@ -388,6 +397,11 @@ public class Main {
System.out.println("get friend relation failure");
System.exit(-1);
}
if(!friendExtra.equals(getRelation.getResult().extra)) {
System.out.println("set friend extra failure");
System.exit(-1);
}
}
//***********************************************
//**** 群组相关功能
@@ -56,6 +56,14 @@ public class RelationAdmin {
return AdminHttpUtils.httpJsonPost(path, input, OutputGetAlias.class);
}
public static IMResult<Void> updateFriendExtra(String operator, String targetId, String extra) throws Exception {
String path = APIPath.Friend_Set_Extra;
InputUpdateFriendExtra input = new InputUpdateFriendExtra();
input.setOperator(operator);
input.setTargetId(targetId);
input.setExtra(extra);
return AdminHttpUtils.httpJsonPost(path, input, Void.class);
}
public static IMResult<Void> sendFriendRequest(String userId, String targetId, String reason, boolean force) throws Exception {
String path = APIPath.Friend_Send_Request;
InputAddFriendRequest input = new InputAddFriendRequest();