Compare commits

..
38 Commits
Author SHA1 Message Date
heavyrian2012 ade58efc11 更新版本号和release note内容 2024-12-11 16:28:51 +08:00
heavyrian2012 d45da9674e 默认开启支持支持踢出事件的开关 2024-12-11 16:24:19 +08:00
heavyrian2012 f603a677ea 当禁止私聊时可以配置例外的线路 2024-12-06 16:06:04 +08:00
heavyrian2012 03894aea9a 修改启动成功的提示语 2024-12-06 08:02:30 +08:00
heavyrian2012 8635153e59 解决聊天室定向消息问题 2024-11-20 12:47:11 +08:00
heavyrian2012 3d98d17b0d 机器人接口添加更新朋友圈接口 2024-11-14 12:51:05 +08:00
heavyrian2012 0549bc502f 添加批量获取用户信息的接口 2024-11-14 12:50:38 +08:00
heavyrian2012 1b8cf3e5f9 添加机器人的朋友圈接口 2024-11-03 10:06:10 +08:00
heavyrian2012 f3f08b3026 version & release note 2024-10-30 19:12:31 +08:00
heavyrian2012 78e6efce14 透传消息需要带上时间戳 2024-10-30 19:00:35 +08:00
heavyrian2012 462423a6f3 SDK添加删除广播和组播接口 2024-10-28 22:08:20 +08:00
heavyrian2012 55190a4f76 优化生成测试数据工具 2024-10-26 14:13:56 +08:00
heavyrian2012 4ed21ecb03 添加生成测试工具的工具 2024-10-25 14:43:06 +08:00
heavyrian2012 1ff3b592bd 创建群组时检查操作者参数是不是存在 2024-10-25 14:42:55 +08:00
heavyrian2012 fbc2351d82 用户可以设置禁止添加好友 2024-10-02 20:07:17 +08:00
heavyrian2012 dd1783d4ac 透传消息去掉消息id和时间戳,减少消息体大小 2024-10-02 20:04:47 +08:00
heavyrian2012 845e6ff8aa version and release note 2024-09-29 08:18:17 +08:00
heavyrian2012 6d91f3869e 创建群组或者添加群组成员时,群组成员的Extra会放到通知消息的extra中,这样便于业务处理通知消息 2024-09-25 11:07:12 +08:00
heavyrian2012 d100bd5fb1 update readme 2024-09-18 20:19:15 +08:00
heavyrian2012 bf22b365a8 如果没有配置鸿蒙推送地址,不应该回落到Android,避免对android造成影响 2024-09-18 13:01:49 +08:00
heavyrian2012 f1298a51ae 添加鸿蒙推送回调地址 2024-09-18 12:50:19 +08:00
heavyrian2012 41f62c6354 添加配置,是否允许发送消息给黑名单用户 2024-09-14 09:53:50 +08:00
heavyrian2012 a45de0ecd1 添加好友请求限频功能 2024-09-12 21:34:12 +08:00
heavyrian2012 fc35865478 push message 添加 republish 属性 2024-09-12 21:29:03 +08:00
heavyrian2012 b1f59498e5 version and release note 2024-09-10 10:57:15 +08:00
heavyrian2012 04f5471385 用户id默认使用uuid 2024-09-10 10:53:21 +08:00
heavyrian2012 291fdfb08d 解决server api获取群组信息不全的问题 2024-09-03 14:58:00 +08:00
heavyrian2012 52de13e20d sdk中针对密钥错误给出提醒 2024-08-22 20:42:22 +08:00
heavyrian2012 c1d353bce9 version & release note 2024-08-19 09:28:38 +08:00
heavyrian2012 9d83f8e3fa 解决某些时候http请求返回信息无法读取问题 2024-08-14 08:35:39 +08:00
heavyrian2012 94833780ef update readme 2024-08-12 08:35:32 +08:00
heavyrian2012 d0fdf55db4 群组解散后撤回失败的错误码应该是不存在 2024-07-29 11:45:35 +08:00
heavyrian2012 ea00ef6b7f 添加发送消息的示例 2024-07-19 16:54:15 +08:00
heavyrian2012 a11cf3c564 解决群组标记删除错误 2024-07-19 16:24:36 +08:00
heavyrian2012 f5af956298 更新readme 2024-07-19 09:41:55 +08:00
heavyrian2012 4005a4babe 配置文件注释说明 2024-07-10 11:14:07 +08:00
heavyrian2012 6ef8b78452 server api获取被封禁用户时,过滤掉正常用户 2024-07-09 16:06:10 +08:00
heavyrian2012 100a1bd44c update readme 2024-07-07 11:02:38 +08:00
84 changed files with 3575 additions and 279 deletions
+7 -3
View File
@@ -7,9 +7,9 @@
* 性能强大,专业版IM服务支持百万在线和集群部署,业内顶尖性能水平,详情参考野火性能报告。
* 部署运维简单,依赖服务少,稍加配置一键启动。
* 安全加密。网络连接AES加密。客户端数据库SqlCipher加密。安全无小事。
* 全平台客户端,支持Android、iOS、Web、小程序、UniApp、Flutter、Windows、Mac OS、Linux等所有常见平台。
* 全平台客户端,支持Android、iOS、鸿蒙、Web、小程序、UniApp、Flutter、Windows、Mac OS、Linux等所有常见平台。
* 四端同时在线(移动端,pc端,web端和小程序端),数据和状态多端完美同步。
* 支持国产化。支持国产化操作系统、国产化芯片和国产操作系统。支持国密加密。
* 支持国产化。支持国产化操作系统、国产化芯片和国产数据库。支持国密加密。
* 客户端使用微信[mars](https://github.com/tencent/mars)连接库,野火IM可能是最适应中国网络国情的即时通讯服务。
* 支持加速点加速,即可用于全球应用,也可用于政企内外双网复杂的网络环境。
* 支持阅读回执和在线状态功能,适用于办公环境。
@@ -22,9 +22,10 @@
* API丰富,方便与其它服务系统的对接。
* 拥有机器人和公众号功能,免费的频道(公众号)管理后台。
* 超级群组功能,可以支持万人大群。
* 支持多个IM服务组网互通,打破孤岛,适用于集团公司下多个分公司互联互通。
* 社区版IM服务和移动端免费商用,可以免费构建出完备的移动互联网应用。
* 收费项目非常便宜,野火独有的试用模式,可以先部署到客户环境试用6个月,试用满意后再购买,避免上当受骗。
* 技术支持专业和高效,核心研发人员及时处理issue和论坛回复。
* 技术支持专业高效,***核心研发***及时处理issue和工单,能最快解决用户的技术难题。技术支持不收费,让客户顺利使用是我们最大的愿望。
## 野火开源项目
主要包括以下项目:
@@ -156,6 +157,9 @@ mvn clean package
## 服务对象
因为野火的使用范围比较广,难免被一些不法分子利用,国内反诈反赌力度很大,所以国内多地警方都跟我们都有过联系。***为了预防我们的产品被用于非法目的,试用及购买只能支持境内法人单位,不接受个人或者境外团体。如果我们发现产品被用于非法目的,我们会立即停止技术支持服务并且报警和配合警方调查。请用于非法目的用户绕行!***
## 常见问题
请参考文档[常见问题](https://docs.wildfirechat.cn/faq/server.html)
## 应用截图
登录界面
+29 -11
View File
@@ -68,6 +68,10 @@ token.key testim
##是否使用AES256加密。默认客户端和服务器都是AES128加密,如果需要开启AES256,需要客户端和IM服务同时开通。
#encrypt.use_aes256 true
##当创建一个对象时(用户,群组,频道,机器人等),如果没有传入ID,野火会为这个对象生成一个ID。如果id.use_uuid为true,会使用uuid作为对象ID,否则会使用野火短ID,默认为false。
## 短ID有个很大的风险是容易被人穷举进行攻击。建议用UUID。
id.use_uuid true
##首次登录,是否接收之前的历史消息。
##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉;
##1接收,会接收 message.max_queue 配置的条数的历史消息
@@ -91,9 +95,15 @@ message.disable_stranger_chat false
##当禁止陌生人聊天时,允许聊天的用户id,比如管理员或者文件传输助手等。用户id以英文逗号分割。
message.allow_stranger_chat_list admin,FireRobot,wfc_file_transfer
##当禁止陌生人聊天时,允许聊天的线路。
#message.allow_stranger_line 100,101
##黑名单策略,0 发送失败,返回被拉黑的错误码;1 发送成功但消息被服务器直接丢弃
message.blacklist.strategy 0
##拉黑对方后,对方不能发送消息给你。这个开关确定拉黑对方后你还能不能给对方发送消息。
message.blacklist.allow_send_to_black false
##是否禁止服务器端消息搜索,该功能暂未实现。目前的影响是如果打开,则存储消息时不单独保存_searchable_content字段
message.disable_remote_search false
@@ -173,6 +183,9 @@ friend.reject_request_duration 2592000000
##好友请求过期时间,单位是毫秒,默认是7天,0为无限长期限。
friend.request_expiration_duration 604800000
##好友请求限制频率,一个用户24小时之内允许请求好友的次数,默认不限频。
friend.request_rate_limit 30
##请求添加机器人为好友时,机器人是否自动接受,默认为true
friend.robot_auto_accept true
@@ -254,15 +267,6 @@ client.request_rate_limit 100
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
media.server.use_qiniu 0
##媒体类型分类
#Media_Type_GENERAL = 0,
#Media_Type_IMAGE = 1,
#Media_Type_VOICE = 2,
#Media_Type_VIDEO = 3,
#Media_Type_FILE = 4,
#Media_Type_PORTRAIT = 5,
#Media_Type_FAVORITE = 6
# qiniu media server configuration
qiniu.server_url http://up.qbox.me
qiniu.access_key tU3vdBK5BL5j4N7jI5N5uZgq_HQDo170w5C9Amnn
@@ -286,11 +290,21 @@ qiniu.bucket_portrait_domain http://cdn2.wildfirechat.cn
qiniu.bucket_favorite_name storage
qiniu.bucket_favorite_domain http://cdn2.wildfirechat.cn
##媒体类型分类
#Media_Type_GENERAL = 0,
#Media_Type_IMAGE = 1,
#Media_Type_VOICE = 2,
#Media_Type_VIDEO = 3,
#Media_Type_FILE = 4,
#Media_Type_PORTRAIT = 5,
#Media_Type_FAVORITE = 6,
#Media_Type_STICKER = 7,
#Media_Type_MOMENTS = 8
# local media server configuration
# 本地媒体服务器配置。
local.media.storage.root media
# 如果使用内置文件存储,文件上传后地址默认为 http://server.ip:http_port/fs/5/2021/03/27/08/filename。
# 如果使用内置文件存储,文件上传后地址默认为 http://server.ip:http_port/fs/5/2021/03/27/08/filename。fs目录下按照类型存放文件,媒体类型见上面注释,比如5就是Media_Type_PORTRAIT,是客户端的头像文件。
# 如果需要使用nginx添加https支持,请打开下面配置,这样客户端得到的文件地址为 https://example.com/media/fs/5/2021/03/27/08/filename。
# 需要nginx把请求从https://example.com/media/fs 转到 http://server.ip:http_port/fs。注意需要带上所有header。
# 尽管内置文件存储可以使用,但是我们还是建议使用专业级别的对象存储服务,社区版可以使用七牛,专业版另外支持阿里云和也是私有对象存储。详情请参考 https://docs.wildfirechat.cn/server/oss.html
@@ -316,7 +330,7 @@ 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
server.client_support_kickoff_event true
#*********************************************************************
# Push server configuration
@@ -330,6 +344,10 @@ push.android.server.address http://localhost:8085/android/push
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
push.ios.server.address http://localhost:8085/ios/push
##鸿蒙推送服务器地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
push.harmony.server.address http://localhost:8085/harmony/push
#*********************************************************************
# 监控配置
#*********************************************************************
+1 -1
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.2.7</version>
<version>1.3.2</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -34,6 +34,7 @@ public class PushMessage {
public String voipDeviceToken;
public boolean isHiddenDetail;
public String language;
public boolean republish;
public long messageId;
public PushMessage(String sender, int conversationType, String target, int line, int messageContentType, long serverTime, String senderName, String senderPortrait, String targetName, String targetPortrait, int unReceivedMsg, int mentionedType, boolean isHiddenDetail, String language) {
@@ -65,6 +66,7 @@ public class PushMessage {
this.mentionedType = mentionedType;
this.isHiddenDetail = isHiddenDetail;
this.language = language;
this.republish = messageContentType == 80 || messageContentType == 81;
}
public PushMessage(String sender, String target, long serverTime, String senderName, int unReceivedMsg, String language, int pushMessageType) {
this.sender = sender;
@@ -24,8 +24,7 @@ import win.liyufan.im.Utility;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static io.moquette.BrokerConstants.PUSH_ANDROID_SERVER_ADDRESS;
import static io.moquette.BrokerConstants.PUSH_IOS_SERVER_ADDRESS;
import static io.moquette.BrokerConstants.*;
import static win.liyufan.im.HttpUtils.HttpPostType.POST_TYPE_Push;
public class PushServer {
@@ -47,6 +46,7 @@ public class PushServer {
private String androidPushServerUrl;
private String iOSPushServerUrl;
private String harmonyPushServerUrl;
private PushServer() {
}
@@ -59,6 +59,7 @@ public class PushServer {
this.sessionsStore = sessionsStore;
this.androidPushServerUrl = config.getProperty(PUSH_ANDROID_SERVER_ADDRESS);
this.iOSPushServerUrl = config.getProperty(PUSH_IOS_SERVER_ADDRESS);
this.harmonyPushServerUrl = config.getProperty(PUSH_HARMONY_SERVER_ADDRESS);
}
public void pushMessage(PushMessage pushMessage, String deviceId, String pushContent) {
@@ -106,6 +107,8 @@ public class PushServer {
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS || session.getPlatform() == ProtoConstants.Platform.Platform_iPad) {
url = iOSPushServerUrl;
pushMessage.voipDeviceToken = session.getVoipDeviceToken();
} else if (session.getPlatform() == ProtoConstants.Platform.Platform_Harmony || session.getPlatform() == ProtoConstants.Platform.Platform_HarmonyPad) {
url = harmonyPushServerUrl;
}
HttpUtils.httpJsonPost(url, gson.toJson(pushMessage, pushMessage.getClass()), POST_TYPE_Push);
} else {
@@ -0,0 +1,61 @@
/*
* 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.*;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Route(APIPath.User_Batch_Get_Infos)
@HttpMethod("POST")
public class GetBatchUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputStringList inputUserId = getRequestBody(request.getNettyRequest(), InputStringList.class);
if (inputUserId != null && inputUserId.getList() != null && !inputUserId.getList().isEmpty()) {
OutputUserInfoList res = new OutputUserInfoList();
res.userInfos = new ArrayList<>();
for (String userId : inputUserId.getList()) {
WFCMessage.User user = messagesStore.getUserInfo(userId);
if(user == null || StringUtil.isNullOrEmpty(user.getName())) {
continue;
}
InputOutputUserInfo inputOutputUserInfo = InputOutputUserInfo.fromPbUser(user);
res.userInfos.add(inputOutputUserInfo);
}
RestResult result = RestResult.ok(res);
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -53,6 +53,7 @@ public class GetGroupInfoAction extends AdminAction {
pojoGroupInfo.setPortrait(groupInfo.getPortrait());
pojoGroupInfo.setTarget_id(groupInfo.getTargetId());
pojoGroupInfo.setType(groupInfo.getType());
pojoGroupInfo.setMember_count(groupInfo.getMemberCount());
pojoGroupInfo.setMute(groupInfo.getMute());
pojoGroupInfo.setJoin_type(groupInfo.getJoinType());
pojoGroupInfo.setPrivate_chat(groupInfo.getPrivateChat());
@@ -61,6 +62,8 @@ public class GetGroupInfoAction extends AdminAction {
pojoGroupInfo.setHistory_message(groupInfo.getHistoryMessage());
pojoGroupInfo.setSuper_group(groupInfo.getSuperGroup()>0);
pojoGroupInfo.setDeleted(groupInfo.getDeleted()>0);
pojoGroupInfo.setUpdate_dt(groupInfo.getUpdateDt());
pojoGroupInfo.setMember_update_dt(groupInfo.getMemberUpdateDt());
result = RestResult.ok(pojoGroupInfo);
}
setResponseContent(result, response);
@@ -52,6 +52,7 @@ public class GetGroupInfoAction extends RobotAction {
pojoGroupInfo.setPortrait(groupInfo.getPortrait());
pojoGroupInfo.setTarget_id(groupInfo.getTargetId());
pojoGroupInfo.setType(groupInfo.getType());
pojoGroupInfo.setMember_count(groupInfo.getMemberCount());
pojoGroupInfo.setMute(groupInfo.getMute());
pojoGroupInfo.setJoin_type(groupInfo.getJoinType());
pojoGroupInfo.setPrivate_chat(groupInfo.getPrivateChat());
@@ -60,6 +61,8 @@ public class GetGroupInfoAction extends RobotAction {
pojoGroupInfo.setHistory_message(groupInfo.getHistoryMessage());
pojoGroupInfo.setSuper_group(groupInfo.getSuperGroup()>0);
pojoGroupInfo.setDeleted(groupInfo.getDeleted()>0);
pojoGroupInfo.setUpdate_dt(groupInfo.getUpdateDt());
pojoGroupInfo.setMember_update_dt(groupInfo.getMemberUpdateDt());
result = RestResult.ok(pojoGroupInfo);
}
setResponseContent(result, response);
@@ -90,6 +90,7 @@ public final class BrokerConstants {
public static final String PUSH_ANDROID_SERVER_ADDRESS = "push.android.server.address";
public static final String PUSH_IOS_SERVER_ADDRESS = "push.ios.server.address";
public static final String PUSH_HARMONY_SERVER_ADDRESS = "push.harmony.server.address";
public static final String MONITOR_Exception_Event_Address = "monitor.exception_event_address";
@@ -115,6 +116,8 @@ public final class BrokerConstants {
public static final String CONNECT_CLIENT_SIGNATURE_LIST = "connect.client_signature_list";
public static final String CONNECT_REJECT_EMPTY_SIGNATURE = "connect.reject_empty_signature";
public static final String ID_USE_UUID = "id.use_uuid";
public static final String EMBED_DB_PROPERTY_NAME = "embed.db";
public static final String DB_AUTO_CLEAN_HISTORY_MESSAGES = "db.auto_clean_history_messages";
public static final String H2DB_PATH = "h2db.path";
@@ -149,8 +152,10 @@ public final class BrokerConstants {
public static final String MESSAGE_Disable_Stranger_Chat = "message.disable_stranger_chat";
public static final String MESSAGE_Allow_Stranger_Chat_List = "message.allow_stranger_chat_list";
public static final String MESSAGE_Allow_Stranger_Line = "message.allow_stranger_line";
public static final String MESSAGE_Blacklist_Strategy = "message.blacklist.strategy";
public static final String MESSAGE_Blacklist_Allow_Send = "message.blacklist.allow_send_to_black";
public static final String MESSAGE_NO_Forward_Admin_Message = "message.no_forward_admin_message";
@@ -173,6 +178,7 @@ public final class BrokerConstants {
public static final String FRIEND_Repeat_Request_Duration = "friend.repeat_request_duration";
public static final String FRIEND_Reject_Request_Duration = "friend.reject_request_duration";
public static final String FRIEND_Request_Expiration_Duration = "friend.request_expiration_duration";
public static final String FRIEND_Request_Rate_Limit = "friend.request_rate_limit";
public static final String FRIEND_Request_Robot_Auto_Accept = "friend.robot_auto_accept";
@@ -24,7 +24,8 @@ public class AddFriendHandler extends GroupHandler<WFCMessage.AddFriendRequest>
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.AddFriendRequest request, Qos1PublishHandler.IMCallback callback) {
long[] head = new long[1];
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
ErrorCode errorCode = m_messagesStore.saveAddFriendRequest(fromUser, request, head, isAdmin);
boolean isRobot = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Robot;
ErrorCode errorCode = m_messagesStore.saveAddFriendRequest(fromUser, request, head, isAdmin, isRobot);
if (errorCode == ERROR_CODE_SUCCESS) {
WFCMessage.User user = m_messagesStore.getUserInfo(request.getTargetUid());
if (user != null && user.getType() == ProtoConstants.UserType.UserType_Normal) {
@@ -43,7 +43,7 @@ public class AddGroupMember extends GroupHandler<WFCMessage.AddGroupMemberReques
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, getMemberIdList(request.getAddedMemberList())).getAddGroupNotifyContent();
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, getMemberIdList(request.getAddedMemberList())).setExtra(request.getExtra()).getAddGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
}
@@ -63,7 +63,7 @@ public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupReque
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
} else {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(groupInfo.getTargetId(), fromUser, groupInfo.getName(), "").getCreateGroupNotifyContent();
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(groupInfo.getTargetId(), fromUser, groupInfo.getName(), "").setExtra(request.getMemberExtra()).getCreateGroupNotifyContent();
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), content);
}
}
@@ -37,6 +37,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
private Set<Integer> mForwardExcludeMessageTypes = new HashSet<>();
private String mMentionForwardUrl = null;
private int mBlacklistStrategy = 0; //黑名单中时,0失败,1吞掉。
private boolean mBlacklistAllowSend2Black = true;
private boolean mNoForwardAdminMessage = false;
private String mRemoteSensitiveServerUrl = null;
@@ -69,6 +70,12 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
mBlacklistAllowSend2Black = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Blacklist_Allow_Send, "true"));
} catch (Exception e) {
}
try {
mRemoteSensitiveServerUrl = mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Server_URL);
if(!StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) {
@@ -145,7 +152,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private) {
if(!m_messagesStore.getBlackListExceptionTypes().contains(message.getContent().getType())) {
errorCode = m_messagesStore.isAllowUserMessage(message.getConversation().getTarget(), fromUser);
errorCode = m_messagesStore.isAllowUserMessage(message.getConversation().getTarget(), fromUser, message.getConversation().getLine());
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
if (errorCode == ErrorCode.ERROR_CODE_IN_BLACK_LIST && mBlacklistStrategy != ProtoConstants.BlacklistStrategy.Message_Reject) {
ignoreMsg = true;
@@ -154,6 +161,13 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
return errorCode;
}
}
if(!mBlacklistAllowSend2Black) {
errorCode = m_messagesStore.isBlacked(fromUser, message.getConversation().getTarget());
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
}
}
userStatus = m_messagesStore.getUserStatus(message.getConversation().getTarget());
@@ -2203,6 +2203,7 @@ public class DatabaseStore {
", `_join_type`" +
", `_private_chat`" +
", `_searchable`" +
", _deleted" +
" from t_group where `_gid` = ?";
statement = connection.prepareStatement(sql);
@@ -2259,6 +2260,9 @@ public class DatabaseStore {
intValue = rs.getInt(index++);
builder.setSearchable(intValue);
intValue = rs.getInt(index++);
builder.setDeleted(intValue);
return builder.build();
}
} catch (SQLException e) {
@@ -75,7 +75,7 @@ import static io.moquette.BrokerConstants.*;
import static io.moquette.server.Constants.MAX_CHATROOM_MESSAGE_QUEUE;
import static io.moquette.server.Constants.MAX_MESSAGE_QUEUE;
import static cn.wildfirechat.pojos.MyInfoType.*;
import static win.liyufan.im.UserSettingScope.kUserSettingAddFriendNoVerify;
import static win.liyufan.im.UserSettingScope.kUserSettingAddFriendStrategy;
import static win.liyufan.im.UserSettingScope.kUserSettingPCOnline;
public class MemoryMessagesStore implements IMessagesStore {
@@ -155,6 +155,7 @@ public class MemoryMessagesStore implements IMessagesStore {
private long mFriendRequestDuration = 7 * 24 * 60 * 60 * 1000;
private long mFriendRejectDuration = 30 * 24 * 60 * 60 * 1000;
private long mFriendRequestExpiration = 7 * 24 * 60 * 60 * 1000;
private RateLimiter mFriendRateLimiter;
private boolean mFriendRobotAutoAccept = true;
private long mPushExpiredTimes = 604800000L;
@@ -164,8 +165,11 @@ public class MemoryMessagesStore implements IMessagesStore {
private boolean mMobileDefaultSilentWhenPCOnline = true;
private boolean mDisableStrangerChat = false;
private Set<String> mAllowStrangerChatSet = new HashSet<>();
private Set<Integer> mAllowStrangerLineSet = new HashSet<>();
private boolean mDisableStrangerAddGroup = false;
private boolean mIDUseUUID = true;
private Set<String> mClientSignatureSet = new HashSet<>();
private boolean mRejectEmptySignature = true;
@@ -262,6 +266,18 @@ public class MemoryMessagesStore implements IMessagesStore {
e.printStackTrace();
}
try {
String allowStrangerList = m_Server.getConfig().getProperty(BrokerConstants.MESSAGE_Allow_Stranger_Line);
if(!StringUtil.isNullOrEmptyAfterTrim(allowStrangerList)) {
allowStrangerList = allowStrangerList.replace(",", ",");
for (String s : allowStrangerList.split(",")) {
mAllowStrangerLineSet.add(Integer.parseInt(s.trim()));
}
}
} catch (Exception e) {
e.printStackTrace();
}
try {
String forcePushTypes = m_Server.getConfig().getProperty(BrokerConstants.MESSAGE_Force_Push_Types);
if(!StringUtil.isNullOrEmptyAfterTrim(forcePushTypes)) {
@@ -342,6 +358,13 @@ public class MemoryMessagesStore implements IMessagesStore {
printMissConfigLog(FRIEND_Request_Expiration_Duration, mFriendRequestExpiration + "");
}
try {
int friendRequestRateLimit = Integer.parseInt(m_Server.getConfig().getProperty(FRIEND_Request_Rate_Limit, "0"));
if(friendRequestRateLimit > 0) {
mFriendRateLimiter = new RateLimiter(86400, friendRequestRateLimit);
}
} catch (Exception e) {
}
try {
mFriendRobotAutoAccept = Boolean.parseBoolean(m_Server.getConfig().getProperty(BrokerConstants.FRIEND_Request_Robot_Auto_Accept, "true"));
@@ -614,6 +637,11 @@ public class MemoryMessagesStore implements IMessagesStore {
} catch (Exception e) {
}
try {
mIDUseUUID = Boolean.parseBoolean(server.getConfig().getProperty(BrokerConstants.ID_USE_UUID, "false"));
} catch (Exception e) {
}
}
private void printMissConfigLog(String config, String defaultValue) {
@@ -718,7 +746,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
pullType = ProtoConstants.PullType.Pull_Normal;
} else if (type == ProtoConstants.ConversationType.ConversationType_ChatRoom) {
boolean isDirect = !StringUtil.isNullOrEmpty(message.getToUser());
boolean isDirect = !StringUtil.isNullOrEmpty(message.getToUser()) || !message.getToList().isEmpty();
Collection<UserClientEntry> entries = getChatroomMembers(message.getConversation().getTarget());
for (UserClientEntry entry : entries) {
if (isDirect) {
@@ -726,6 +754,9 @@ public class MemoryMessagesStore implements IMessagesStore {
notifyReceivers.add(message.getToUser());
break;
}
if (message.getToList().contains(entry.userId)) {
notifyReceivers.add(entry.userId);
}
} else {
notifyReceivers.add(entry.userId);
}
@@ -2421,7 +2452,8 @@ public class MemoryMessagesStore implements IMessagesStore {
WFCMessage.GroupInfo groupInfo = groupMap.get(message.getConversation().getTarget());
if (groupInfo == null) {
return ErrorCode.ERROR_CODE_RECALL_TIME_EXPIRED;
LOG.info("recall message failure, group {} not exist", message.getConversation().getTarget());
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
if (operatorId.equals(groupInfo.getOwner())) {
if(operatorId.equals(message.getFromUser())) {
@@ -2858,7 +2890,9 @@ public class MemoryMessagesStore implements IMessagesStore {
IMap<String, Integer> mUserMap = hzInstance.getMap(USER_STATUS);
ArrayList<InputOutputUserBlockStatus> out = new ArrayList<>();
for (Map.Entry<String, Integer> entry : mUserMap.entrySet()) {
out.add(new InputOutputUserBlockStatus(entry.getKey(), entry.getValue()));
if(entry.getValue() > 0) {
out.add(new InputOutputUserBlockStatus(entry.getKey(), entry.getValue()));
}
}
return out;
}
@@ -3175,7 +3209,7 @@ public class MemoryMessagesStore implements IMessagesStore {
}
@Override
public ErrorCode isAllowUserMessage(String targetUser, String fromUser) {
public ErrorCode isAllowUserMessage(String targetUser, String fromUser, int line) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
@@ -3202,7 +3236,7 @@ public class MemoryMessagesStore implements IMessagesStore {
if (mDisableStrangerChat) {
//在禁止私聊时,是否是允许私聊的用户id
if(mAllowStrangerChatSet.contains(targetUser) || mAllowStrangerChatSet.contains(fromUser)) {
if(mAllowStrangerChatSet.contains(targetUser) || mAllowStrangerChatSet.contains(fromUser) || mAllowStrangerLineSet.contains(line)) {
return ErrorCode.ERROR_CODE_SUCCESS;
}
@@ -3226,6 +3260,30 @@ public class MemoryMessagesStore implements IMessagesStore {
return ErrorCode.ERROR_CODE_SUCCESS;
}
@Override
public ErrorCode isBlacked(String fromUser, String userId) {
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, FriendData> friendsMap = hzInstance.getMultiMap(USER_FRIENDS);
Collection<FriendData> friendDatas = friendsMap.get(fromUser);
if (friendDatas == null || friendDatas.size() == 0) {
friendDatas = loadFriend(friendsMap, fromUser);
}
for (FriendData friendData : friendDatas) {
if (friendData.getFriendUid().equals(userId)) {
if (friendData.getBlacked() == 1) {
return ErrorCode.ERROR_CODE_IN_BLACK_LIST;
} else {
break;
}
}
}
return ErrorCode.ERROR_CODE_SUCCESS;
}
synchronized Collection<FriendData> loadFriend(MultiMap<String, FriendData> friendsMap, String userId) {
Collection<FriendData> friends = databaseStore.getPersistFriends(userId);
if (friends != null) {
@@ -3343,16 +3401,28 @@ public class MemoryMessagesStore implements IMessagesStore {
}
@Override
public ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head, boolean isAdmin) {
public ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head, boolean isAdmin, boolean isRobot) {
if (!isAdmin && mDisableFriendRequest) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(!isRobot && !isAdmin && mFriendRateLimiter != null) {
if(!mFriendRateLimiter.isGranted(userId)) {
LOG.error("User {} request add friend over frequency", userId);
return ErrorCode.ERROR_CODE_OVER_FREQUENCY;
}
}
HazelcastInstance hzInstance = m_Server.getHazelcastInstance();
MultiMap<String, WFCMessage.FriendRequest> requestMap = hzInstance.getMultiMap(USER_FRIENDS_REQUEST);
WFCMessage.UserSettingEntry userSettingData = getUserSetting(request.getTargetUid(), kUserSettingAddFriendNoVerify, "");
WFCMessage.UserSettingEntry userSettingData = getUserSetting(request.getTargetUid(), kUserSettingAddFriendStrategy, "");
boolean noVerify = userSettingData != null && "1".equals(userSettingData.getValue());
boolean noAllow = userSettingData != null && "2".equals(userSettingData.getValue());
if(noAllow) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
Collection<WFCMessage.FriendRequest> requests = requestMap.get(userId);
if (requests == null || requests.size() == 0) {
@@ -4788,9 +4858,11 @@ public class MemoryMessagesStore implements IMessagesStore {
@Override
public String getShortUUID(){
int id = databaseStore.getGeneratedId();
if (id > 0) {
return IDUtils.toUid(id);
if(!mIDUseUUID) {
int id = databaseStore.getGeneratedId();
if (id > 0) {
return IDUtils.toUid(id);
}
}
return UUIDGenerator.getUUID();
}
@@ -140,7 +140,7 @@ public class Server {
Runtime.getRuntime().addShutdownHook(new Thread(instance::stopServer));
Runtime.getRuntime().addShutdownHook(new Thread(httpServer::shutdown));
System.out.println("Wildfire IM server start success...");
System.out.println("Wildfire IM server start success.");
}
/**
@@ -179,13 +179,14 @@ public interface IMessagesStore {
void clearUserFriend(String userId);
List<WFCMessage.FriendRequest> getFriendRequestList(String userId, long version);
ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head, boolean isAdmin);
ErrorCode saveAddFriendRequest(String userId, WFCMessage.AddFriendRequest request, long[] head, boolean isAdmin, boolean isRobot);
ErrorCode handleFriendRequest(String userId, WFCMessage.HandleFriendRequest request, WFCMessage.Message.Builder msgBuilder, long[] heads, boolean isAdmin);
ErrorCode deleteFriend(String userId, String friendUid, long[] head);
ErrorCode blackUserRequest(String fromUser, String targetUserId, int status, long[] head);
FriendData getFriendData(String fromUser, String targetUserId);
ErrorCode SyncFriendRequestUnread(String userId, long unreadDt, long[] head);
ErrorCode isAllowUserMessage(String fromUser, String userId);
ErrorCode isAllowUserMessage(String fromUser, String userId, int line);
ErrorCode isBlacked(String fromUser, String userId);
ErrorCode setFriendAliasRequest(String fromUser, String targetUserId, String alias, long[] head);
ErrorCode setFriendExtraRequest(String fromUser, String targetUserId, String extra, long[] head);
@@ -451,6 +451,7 @@ public class MessagesPublisher {
WFCMessage.Message message = m_messagesStore.getMessage(messageHead);
if (message != null) {
message = message.toBuilder().setMessageId(0).build();
for (String user : receivers) {
Collection<Session> sessions = m_sessionsStore.sessionForUser(user);
@@ -102,7 +102,7 @@ public class HttpUtils {
if(httpCallback != null) {
int code = response.code();
if(code == 200) {
if(response.body() != null && response.body().contentLength() > 0) {
if(response.body() != null) {
httpCallback.onSuccess(response.body().string());
} else {
httpCallback.onSuccess(null);
@@ -20,5 +20,5 @@ public interface UserSettingScope {
int kUserSettingPttSilent = 25;
int kUserSettingGroupRemark = 26;
int kUserSettingPrivacySearchable = 27;
int kUserSettingAddFriendNoVerify = 28;
int kUserSettingAddFriendStrategy = 28;
}
+1 -1
View File
@@ -5,7 +5,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.2.7</version>
<version>1.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -71,6 +71,8 @@ public interface APIPath {
String Msg_Multicast = "/admin/message/multicast";
String Msg_RecallBroadCast = "/admin/message/recall_broadcast";
String Msg_RecallMultiCast = "/admin/message/recall_multicast";
String Msg_DeleteBroadCast = "/admin/message/delete_broadcast";
String Msg_DeleteMultiCast = "/admin/message/delete_multicast";
String Msg_ConvRead = "/admin/message/conv_read";
String Msg_Delivery = "/admin/message/delivery";
String Conversation_Delete = "/admin/conversation/delete";
@@ -155,5 +157,17 @@ public interface APIPath {
String Robot_Group_Transfer = "/robot/group/transfer";
String Robot_Group_Set_Manager = "/robot/group/manager/set";
String Robot_Moments_Post_Feed = "/robot/moments/feed/post";
String Robot_Moments_Pull_Feeds = "/robot/moments/feed/pull";
String Robot_Moments_Update_Feed = "/robot/moments/feed/update";
String Robot_Moments_Post_Comment = "/robot/moments/comment/post";
String Robot_Moments_Pull_Comment = "/robot/moments/comment/pull";
String Robot_Moments_Fetch_Feed = "/robot/moments/feed/pull_one";
String Robot_Moments_Fetch_Profiles = "/robot/moments/profiles/pull";
String Robot_Moments_Recall_Comment = "/robot/moments/comment/recall";
String Robot_Moments_Recall_Feed = "/robot/moments/feed/recall";
String Robot_Moments_Update_Profiles_List_Value = "/robot/moments/profiles/list/push";
String Robot_Moments_Update_Profiles_Value = "/robot/moments/profiles/value/push";
String Robot_Conference_Request = "/robot/conference/request";
}
@@ -1,19 +0,0 @@
package cn.wildfirechat.messagecontentbuilder;
import cn.wildfirechat.pojos.MessagePayload;
abstract public class MediaMessageContentBuilder extends MessageContentBuilder{
private String remoteMediaUrl;
public MediaMessageContentBuilder remoteMediaUrl(String remoteMediaUrl) {
this.remoteMediaUrl = remoteMediaUrl;
return this;
}
@Override
protected MessagePayload encodeBase() {
MessagePayload payload = super.encodeBase();
payload.setRemoteMediaUrl(remoteMediaUrl);
return payload;
}
}
@@ -1,33 +0,0 @@
package cn.wildfirechat.messagecontentbuilder;
import cn.wildfirechat.pojos.MessagePayload;
import java.util.List;
public abstract class MessageContentBuilder {
private int mentionedType;
private List<String> mentionedTargets;
private String extra;
public MessageContentBuilder mentionedType(int mentionedType) {
this.mentionedType = mentionedType;
return this;
}
public MessageContentBuilder mentionedTargets(List<String> mentionedTargets) {
this.mentionedTargets = mentionedTargets;
return this;
}
public MessageContentBuilder extra(String extra) {
this.extra = extra;
return this;
}
protected MessagePayload encodeBase() {
MessagePayload payload = new MessagePayload();
payload.setMentionedType(mentionedType);
payload.setMentionedTarget(mentionedTargets);
payload.setExtra(extra);
return payload;
}
abstract public MessagePayload build();
}
@@ -1,25 +0,0 @@
package cn.wildfirechat.messagecontentbuilder;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONObject;
public class SoundMessageContentBuilder extends MediaMessageContentBuilder {
private int duration;
public static SoundMessageContentBuilder newBuilder(int duration) {
SoundMessageContentBuilder builder = new SoundMessageContentBuilder();
builder.duration = duration;
return builder;
}
@Override
public MessagePayload build() {
MessagePayload payload = encodeBase();
payload.setType(ProtoConstants.ContentType.Voice);
payload.setMediaType(ProtoConstants.MessageMediaType.VOICE);
payload.setPersistFlag(ProtoConstants.PersistFlag.Persist_And_Count);
JSONObject jsonObject = new JSONObject();
jsonObject.put("duration", duration);
payload.setContent(jsonObject.toJSONString());
return payload;
}
}
@@ -1,48 +0,0 @@
package cn.wildfirechat.messagecontentbuilder;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import com.google.gson.Gson;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class StreamingTextMessageContentBuilder extends MessageContentBuilder{
private String text;
private String streamId;
private boolean generating;
public static StreamingTextMessageContentBuilder newBuilder(String streamId) {
StreamingTextMessageContentBuilder builder = new StreamingTextMessageContentBuilder();
builder.generating = false;
builder.streamId = streamId;
return builder;
}
public StreamingTextMessageContentBuilder text(String text) {
this.text = text;
return this;
}
public StreamingTextMessageContentBuilder generating(boolean generating) {
this.generating = generating;
return this;
}
@Override
public MessagePayload build() {
MessagePayload payload = encodeBase();
payload.setSearchableContent(text);
payload.setContent(streamId);
if(generating) {
//正在生成消息的类型是14,存储标记位为透传为4。
payload.setType(14);
payload.setPersistFlag(4);
} else {
//已经生成消息的类型是15,存储标记位为透传为3。
payload.setType(15);
payload.setPersistFlag(3);
}
return payload;
}
}
@@ -1,21 +0,0 @@
package cn.wildfirechat.messagecontentbuilder;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class TextMessageContentBuilder extends MessageContentBuilder{
private String text;
public static TextMessageContentBuilder newBuilder(String text) {
TextMessageContentBuilder builder = new TextMessageContentBuilder();
builder.text = text;
return builder;
}
@Override
public MessagePayload build() {
MessagePayload payload = encodeBase();
payload.setType(ProtoConstants.ContentType.Text);
payload.setPersistFlag(ProtoConstants.PersistFlag.Persist_And_Count);
payload.setSearchableContent(text);
return payload;
}
}
@@ -13,6 +13,7 @@ import cn.wildfirechat.proto.WFCMessage;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.protobuf.ByteString;
import io.netty.util.internal.StringUtil;
import java.util.List;
@@ -32,6 +33,8 @@ public class GroupNotificationBinaryContent {
//value2(member or something)
private String m;
private String extra;
public GroupNotificationBinaryContent() {
}
@@ -89,8 +92,18 @@ public class GroupNotificationBinaryContent {
this.ms = ms;
}
public GroupNotificationBinaryContent setExtra(String extra) {
this.extra = extra;
return this;
}
public WFCMessage.MessageContent getGroupNotifyContent(int groupContentType) {
return WFCMessage.MessageContent.newBuilder().setType(groupContentType).setData(ByteString.copyFromUtf8(new GsonBuilder().disableHtmlEscaping().create().toJson(this))).build();
WFCMessage.MessageContent.Builder builder = WFCMessage.MessageContent.newBuilder().setType(groupContentType).setData(ByteString.copyFromUtf8(new GsonBuilder().disableHtmlEscaping().create().toJson(this)));
if(!StringUtil.isNullOrEmpty(extra)) {
builder.setExtra(extra);
}
return builder.build();
}
public WFCMessage.MessageContent getAddGroupNotifyContent() {
@@ -17,6 +17,7 @@ import java.util.List;
public class InputAddGroupMember extends InputGroupBase {
private String group_id;
private List<PojoGroupMember> members;
private String member_extra;
public String getGroup_id() {
return group_id;
@@ -34,6 +35,14 @@ public class InputAddGroupMember extends InputGroupBase {
this.members = members;
}
public String getMember_extra() {
return member_extra;
}
public void setMemberExtra(String extra) {
this.member_extra = extra;
}
public boolean isValide() {
return true;
}
@@ -41,6 +50,9 @@ public class InputAddGroupMember extends InputGroupBase {
public WFCMessage.AddGroupMemberRequest toProtoGroupRequest() {
WFCMessage.AddGroupMemberRequest.Builder addGroupBuilder = WFCMessage.AddGroupMemberRequest.newBuilder();
addGroupBuilder.setGroupId(group_id);
if(!StringUtil.isNullOrEmpty(member_extra)) {
addGroupBuilder.setExtra(member_extra);
}
for (PojoGroupMember pojoGroupMember : getMembers()) {
WFCMessage.GroupMember.Builder groupMemberBuilder = WFCMessage.GroupMember.newBuilder().setMemberId(pojoGroupMember.getMember_id());
if (!StringUtil.isNullOrEmpty(pojoGroupMember.getAlias())) {
@@ -16,9 +16,10 @@ import java.util.List;
public class InputCreateGroup extends InputGroupBase {
private PojoGroup group;
private String member_extra;
public boolean isValide() {
return true;
return !StringUtil.isNullOrEmpty(operator) && group != null;
}
public WFCMessage.CreateGroupRequest toProtoGroupRequest() {
@@ -87,6 +88,9 @@ public class InputCreateGroup extends InputGroupBase {
WFCMessage.CreateGroupRequest.Builder createGroupReqBuilder = WFCMessage.CreateGroupRequest.newBuilder();
createGroupReqBuilder.setGroup(groupBuilder);
if(!StringUtil.isNullOrEmpty(member_extra)) {
createGroupReqBuilder.setMemberExtra(member_extra);
}
if (to_lines != null) {
for (Integer line : to_lines
) {
@@ -107,4 +111,12 @@ public class InputCreateGroup extends InputGroupBase {
public void setGroup(PojoGroup group) {
this.group = group;
}
public String getMember_extra() {
return member_extra;
}
public void setMember_extra(String member_extra) {
this.member_extra = member_extra;
}
}
@@ -0,0 +1,22 @@
package cn.wildfirechat.pojos;
import java.util.List;
public class InputStringList {
private List<String> list;
public InputStringList() {
}
public InputStringList(List<String> list) {
this.list = list;
}
public List<String> getList() {
return list;
}
public void setList(List<String> list) {
this.list = list;
}
}
@@ -0,0 +1,38 @@
package cn.wildfirechat.pojos.moments;
import java.util.List;
public class CommentMsgPojo {
//commentId;
public long commentId;
//sender;
public String sender;
//timestamp
public long serverTime;
//feedId
public long feedId;
//type
public int type;
//content
public String text;
//to replyTo
public String replyTo;
//extra
public String extra;
public int ftype;
public String fcontent;
public String fsender;
public List<MediaEntry> fmedias;
}
@@ -0,0 +1,30 @@
package cn.wildfirechat.pojos.moments;
public class CommentPojo {
//commentId;
public long commentId;
//replyId;
public long replyId;
//sender;
public String sender;
//timestamp
public long timestamp;
//feedId
public long feedId;
//type
public int type;
//content
public String text;
//to replyTo
public String replyTo;
//extra
public String extra;
}
@@ -0,0 +1,38 @@
package cn.wildfirechat.pojos.moments;
import java.util.List;
public class FeedPojo {
//feedid
public long feedId;
//sender
public String sender;
//timestamp
public long timestamp;
//type
public int type;
//content
public String text;
//media urls
public List<MediaEntry> medias;
//to users
public List<String> to;
//excloud users
public List<String> ex;
//mentioned users
public List<String> mu;
//extra
public String extra;
public List<CommentPojo> comments;
}
@@ -0,0 +1,7 @@
package cn.wildfirechat.pojos.moments;
import java.util.List;
public class FeedsPojo {
public List<FeedPojo> feeds;
}
@@ -0,0 +1,9 @@
package cn.wildfirechat.pojos.moments;
public class IdPojo {
//feedid
public long id;
//feedid
public long id2;
}
@@ -0,0 +1,8 @@
package cn.wildfirechat.pojos.moments;
public class MediaEntry {
public String m;
public String t;
public int w;
public int h;
}
@@ -0,0 +1,14 @@
package cn.wildfirechat.pojos.moments;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class MomentProfilePojo {
public String bgUrl;
public List<String> blackList;
public List<String> blockList;
//stranger visiable count
public int svc;
public int visiableScope;
}
@@ -0,0 +1,11 @@
package cn.wildfirechat.pojos.moments;
public class PostFeedResult {
public long id;
public long timestamp;
public PostFeedResult(long id, long timestamp) {
this.id = id;
this.timestamp = timestamp;
}
}
@@ -0,0 +1,11 @@
package cn.wildfirechat.pojos.moments;
import java.util.List;
public class PullCommentsRequestPojo {
public static class IdPair {
public long fid;
public long cid;
}
public List<IdPair> ids;
}
@@ -0,0 +1,8 @@
package cn.wildfirechat.pojos.moments;
public class PullFeedRequestPojo {
//feedid
public long feedId;
public int count;
public String user;
}
@@ -0,0 +1,5 @@
package cn.wildfirechat.pojos.moments;
public class PullOneFeedRequestPojo {
public long feedId;
}
@@ -0,0 +1,9 @@
package cn.wildfirechat.pojos.moments;
public class PullProfileRequestPojo {
//userId
public String u;
//update date
public long d;
}
@@ -0,0 +1,14 @@
package cn.wildfirechat.pojos.moments;
import java.util.List;
public class PushProfileListRequestPojo {
//block or black
public boolean b;
//add user list
public List<String> al;
//remove user list
public List<String> rl;
}
@@ -0,0 +1,16 @@
package cn.wildfirechat.pojos.moments;
public class PushProfileValueRequestPojo {
/*
WFMUpdateUserProfileType_BackgroudUrl,
WFMUpdateUserProfileType_StrangerVisiableCount,
WFMUpdateUserProfileType_VisiableScope
*/
public int t;
//string value depends on t
public String v;
//int value depends on t
public int i;
}
@@ -97,6 +97,10 @@ public class ProtoConstants {
int Composited = 11;
int Rich_Notification = 12;
int Articles = 13;
int StreamingText_Generationg = 14;
int StreamingText_Generated = 15;
int Not_Delivered = 16;
int Ptt_Voice = 23;
int Enter_Channel_Chat = 71;
int Leave_Channel_Chat = 72;
int Recall = 80;
@@ -126,6 +130,12 @@ public class ProtoConstants {
int Quit_Group_Visible_Notification = 121;
int Modify_Group_Extra = 122;
int Modify_Group_Member_Extra = 123;
int Call_Start = 400;
int Call_Accept = 401;
int Call_End = 402;
int Call_Add_Participant = 406;
int Call_Multi_Call_Ongoing = 416;
}
public interface MessagePersistFlag {
@@ -337,6 +347,25 @@ public class ProtoConstants {
int Forbidden_Mute_Group_Member = 0x800;
}
public interface MomentsContentType {
int Moments_Content_Text_Type = 0;
int Moments_Content_Image_Type = 1;
int Moments_Content_Video_Type = 2;
int Moments_Content_Link_Type = 3;
}
public interface MomentsCommentType {
int Moments_Comment_Text_Type = 0;
int Moments_Comment_Thumbup_Type = 1;
}
public interface MomentsVisibleScope {
int Moments_VisibleScope_NoLimit = 0;
int Moments_VisibleScope_3Days = 1;
int Moments_VisibleScope_1Month = 2;
int Moments_VisibleScope_6Months = 3;
};
public static final int MESSAGE_CONTENT_TYPE_CREATE_GROUP = 104;
public static final int MESSAGE_CONTENT_TYPE_ADD_GROUP_MEMBER = 105;
public static final int MESSAGE_CONTENT_TYPE_KICKOF_GROUP_MEMBER = 106;
+1 -1
View File
@@ -6,7 +6,7 @@
<!--<relativePath>../pom.xml</relativePath>-->
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.2.7</version>
<version>1.3.2</version>
</parent>
<artifactId>distribution</artifactId>
@@ -66,6 +66,10 @@ token.key testim
##是否使用AES256加密。默认客户端和服务器都是AES128加密,如果需要开启AES256,需要客户端和IM服务同时开通。
#encrypt.use_aes256 true
##当创建一个对象时(用户,群组,频道,机器人等),如果没有传入ID,野火会为这个对象生成一个ID。如果id.use_uuid为true,会使用uuid作为对象ID,否则会使用野火短ID,默认为false。
## 短ID有个很大的风险是容易被人穷举进行攻击。建议用UUID。
id.use_uuid true
##首次登录,是否接收之前的历史消息。
##0 不接收历史消息,只接收${message.compensate_time_limit}毫秒以内的消息,由于服务器没有保存已经收取记录,所以如果有超过${message.compensate_time_limit}毫秒之前未收取的消息也不会收取下拉;
##1接收,会接收 message.max_queue 配置的条数的历史消息
@@ -89,6 +93,9 @@ message.disable_stranger_chat false
##当禁止陌生人聊天时,允许聊天的用户id,比如管理员或者文件传输助手等。用户id以英文逗号分割。
message.allow_stranger_chat_list admin,FireRobot,wfc_file_transfer
##当禁止陌生人聊天时,允许聊天的线路。
#message.allow_stranger_line 100,101
##黑名单策略,0 发送失败,返回被拉黑的错误码;1 发送成功但消息被服务器直接丢弃
message.blacklist.strategy 0
@@ -171,6 +178,9 @@ friend.reject_request_duration 2592000000
##好友请求过期时间,单位是毫秒,默认是7天,0为无限长期限。
friend.request_expiration_duration 604800000
##好友请求限制频率,一个用户24小时之内允许请求好友的次数,默认不限频。
#friend.request_rate_limit 30
##请求添加机器人为好友时,机器人是否自动接受,默认为true
friend.robot_auto_accept true
@@ -250,15 +260,6 @@ client.request_rate_limit 100
##关于对象存储的详细信息请参考:https://docs.wildfirechat.cn/server/oss.html
media.server.use_qiniu 0
##媒体类型分类
#Media_Type_GENERAL = 0,
#Media_Type_IMAGE = 1,
#Media_Type_VOICE = 2,
#Media_Type_VIDEO = 3,
#Media_Type_FILE = 4,
#Media_Type_PORTRAIT = 5,
#Media_Type_FAVORITE = 6
# qiniu media server configuration
## 上传地址,不同的区域,上传地址也不同,请注意现在正确的地址
qiniu.server_url http://up.qbox.me
@@ -284,11 +285,21 @@ qiniu.bucket_portrait_domain http://cdn2.wildfirechat.cn
qiniu.bucket_favorite_name storage
qiniu.bucket_favorite_domain http://cdn2.wildfirechat.cn
##媒体类型分类
#Media_Type_GENERAL = 0,
#Media_Type_IMAGE = 1,
#Media_Type_VOICE = 2,
#Media_Type_VIDEO = 3,
#Media_Type_FILE = 4,
#Media_Type_PORTRAIT = 5,
#Media_Type_FAVORITE = 6,
#Media_Type_STICKER = 7,
#Media_Type_MOMENTS = 8
# local media server configuration
# 本地媒体服务器配置。
local.media.storage.root ./media
# 如果使用内置文件存储,文件上传后地址默认为 http://server.ip:http_port/fs/5/2021/03/27/08/filename。
local.media.storage.root media
# 如果使用内置文件存储,文件上传后地址默认为 http://server.ip:http_port/fs/5/2021/03/27/08/filename。fs目录下按照类型存放文件,媒体类型见上面注释,比如5就是Media_Type_PORTRAIT,是客户端的头像文件。
# 如果需要使用nginx添加https支持,请打开下面配置,这样客户端得到的文件地址为 https://example.com/media/fs/5/2021/03/27/08/filename。
# 需要nginx把请求从https://example.com/media/fs 转到 http://server.ip:http_port/fs。注意需要带上所有header。
# 尽管内置文件存储可以使用,但是我们还是建议使用专业级别的对象存储服务,社区版可以使用七牛,专业版另外支持阿里云和也是私有对象存储。详情请参考 https://docs.wildfirechat.cn/server/oss.html
@@ -314,7 +325,7 @@ 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
server.client_support_kickoff_event true
#*********************************************************************
# Push server configuration
@@ -328,6 +339,10 @@ push.android.server.address http://localhost:8085/android/push
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
push.ios.server.address http://localhost:8085/ios/push
##鸿蒙推送服务器地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
push.harmony.server.address http://localhost:8085/harmony/push
#*********************************************************************
# 监控配置
#*********************************************************************
+1 -1
View File
@@ -12,7 +12,7 @@
<artifactId>wildfirechat-parent</artifactId>
<packaging>pom</packaging>
<version>1.2.7</version>
<version>1.3.2</version>
<name>Wildfire Chat</name>
<description>Wildfire Chat Server</description>
<inceptionYear>2019</inceptionYear>
+35
View File
@@ -12,6 +12,41 @@
# 更新记录
--------------
Release note 1.3.2:
1. 机器人Server SDK添加朋友圈相关接口。
2. Server API添加批量获取用户接口。
3. 解决聊天室定向消息无效的问题。
4. 添加配置,可以在禁止私聊时添加例外聊天线路。
--------------
Release note 1.3.1:
1. 透传消息去掉消息ID,减少消息传输大小。
2. 修改添加好友策略,可以设置禁止加自己好友。
3. Server API创建群组时,检查是否设置operator。
4. SDK添加删除广播和组播消息接口
--------------
Release note 1.3.0:
1. 推送信息中添加republish参数,用于标识消息是重新发布的。
2. 添加好友请求限频功能,在IM服务配置中可以配置每天限制添加的好友数。
3. 添加配置,是否允许用户发送消息给被拉黑的用户。
4. 添加鸿蒙回调地址。
5. 创建群组或者添加群组成员时,群组成员的Extra会放到通知消息的extra中,这样便于业务处理通知消息。
--------------
Release note 1.2.9:
1. 解决server api获取群组信息不全的问题。
2. 添加配置使用uuid作为用户群组等ID。
--------------
Release note 1.2.8:
1. server api获取被封禁用户时,过滤掉正常用户。
2. 解决群组标记删除错误。
3. SDK中添加发送消息的示例。
4. 群解散后撤回群中的消息应该失败。
5. 解决某些时候http请求返回信息无法读取问题。
--------------
Release note 1.2.7:
1. 解决搜索频道时某些类型的频道无法被搜索的问题。
+1 -1
View File
@@ -3,7 +3,7 @@
<parent>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>1.2.7</version>
<version>1.3.2</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -0,0 +1,361 @@
package cn.wildfirechat.sdk;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.*;
import cn.wildfirechat.sdk.model.IMResult;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 生成测试数据。本应用生成以下数据:
* 1. 创建${totalUserCount}个用户
* 2. 每个用户同其他${friendCount}个用户建立好友关系
* 3. 确保每个人都加入${smallGroupCount}个群组成员数为${smallGroupMemberSize}的小群组
* 4. 确保每个人都加入${middleGroupCount}个群组成员数为${middleGroupMemberSize}的中群组
* 5. 确保每个人都加入${bigGroupCount}个群组成员数为${bigGroupMemberSize}的大群组
* 6. 把数据分片${splitFileCount}个保存在文件中
*
* 好友关系和群组关系都是随机生成,有可能会出现最后几个用户无法满足精确好友数或者群组数的问题,但绝大部分数据都能满足需求。
*/
public class GenerateTestData {
//IM服务Server API
private static String AdminUrl = "http://10.206.0.5:18080";
private static String AdminSecret = "123456";
private static int totalUserCount = 20000;
//每个用户的好友数
private static int friendCount = 100;
//每个用户拥有的小群数,和小群的大小。
private static int smallGroupCount = 100;
private static int smallGroupMemberSize = 20;
//每个用户拥有的中群数,和中群的大小。
private static int middleGroupCount = 100;
private static int middleGroupMemberSize = 100;
//每个用户拥有的大群数,和大群的大小。
private static int bigGroupCount = 20;
private static int bigGroupMemberSize = 1000;
//数据文件分割成几个文件存储。
private static int splitFileCount = 1;
//统计添加好友数和创建群组数。
private static AtomicInteger addFriendCount = new AtomicInteger(0);
private static AtomicInteger createGroupCount = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
AdminConfig.initAdmin(AdminUrl, AdminSecret);
int addFriendRequestCount = friendCount*totalUserCount/2;
int createSmallGroupCount = smallGroupCount * totalUserCount / smallGroupMemberSize;
int createMiddleGroupCount = middleGroupCount * totalUserCount / middleGroupMemberSize;
int createBigGroupCount = bigGroupCount * totalUserCount / bigGroupMemberSize;
System.out.println("根据当前的参数,将创建 " + totalUserCount + " 名用户");
System.out.println("添加大概 " + addFriendRequestCount + " 次好友请求");
System.out.println("创建大概 " + createSmallGroupCount + " 个小型群组");
System.out.println("创建大概 " + createMiddleGroupCount + " 个中等群组");
System.out.println("创建大概 " + createBigGroupCount + " 个大型群组");
System.out.println("等待10秒开始");
Thread.sleep(10*1000);
List<String> userIds = generateUserIds(totalUserCount);
CountDownLatch latch = new CountDownLatch(5);
new Thread(() -> {
createUsers(userIds);
latch.countDown();
}).start();
Map<String, Integer> unfulfilledFriendMap = new HashMap<>();
new Thread(() -> {
addFriends(userIds, friendCount, unfulfilledFriendMap);
latch.countDown();
}).start();
Map<String, Set<String>> userSmallGroupMap = new HashMap<>();
Map<String, Integer> unfulfilledSmallMap = new HashMap<>();
new Thread(() ->{
for (String userId : userIds) {
createGroup(userId, userIds, userSmallGroupMap, smallGroupCount, smallGroupMemberSize, unfulfilledSmallMap);
}
latch.countDown();
}).start();
Map<String, Set<String>> userMiddleGroupMap = new HashMap<>();
Map<String, Integer> unfulfilledMiddleMap = new HashMap<>();
new Thread(() ->{
for (String userId : userIds) {
createGroup(userId, userIds, userMiddleGroupMap, middleGroupCount, middleGroupMemberSize, unfulfilledMiddleMap);
}
latch.countDown();
}).start();
Map<String, Set<String>> userBigGroupMap = new HashMap<>();
Map<String, Integer> unfulfilledBigMap = new HashMap<>();
new Thread(() ->{
for (String userId : userIds) {
createGroup(userId, userIds, userBigGroupMap, bigGroupCount, bigGroupMemberSize, unfulfilledBigMap);
}
latch.countDown();
}).start();
latch.await();
System.out.println("数据初始化结束,实际建立好友关系: " + addFriendCount.get() + " 条。");
if (!unfulfilledFriendMap.isEmpty()) {
System.out.println("有部分用户没有建立" + friendCount + "个好友关系,分别是:");
printUnfulfilledMap(unfulfilledFriendMap);
}
System.out.println("数据初始化结束,创建群组: " + createGroupCount.get() + " 个。");
if (!unfulfilledSmallMap.isEmpty()) {
System.out.println("有部分用户没有建立" + smallGroupMemberSize + "个群组成员的群,分别是:");
printUnfulfilledMap(unfulfilledSmallMap);
}
if (!unfulfilledMiddleMap.isEmpty()) {
System.out.println("有部分用户没有建立" + middleGroupMemberSize + "个群组成员的群,分别是:");
printUnfulfilledMap(unfulfilledMiddleMap);
}
if (!unfulfilledBigMap.isEmpty()) {
System.out.println("有部分用户没有建立" + bigGroupMemberSize + "个群组成员的群,分别是:");
printUnfulfilledMap(unfulfilledBigMap);
}
System.out.println("保存数据到" + splitFileCount + "个文件中。");
for (int i = 0; i < splitFileCount; i++) {
String filePath = "data"+i+".csv"; // 文件路径
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath));
int partSize = totalUserCount/splitFileCount;
for (int j = 0; j < partSize; j++) {
String userId = userIds.get(i*partSize+j);
writeDeviceInfo(writer, userId, 3);
Set<String> smallGroupIds = userSmallGroupMap.get(userId);
writeGroupIds(writer, smallGroupIds);
Set<String> middleGroupIds = userMiddleGroupMap.get(userId);
writeGroupIds(writer, middleGroupIds);
Set<String> bigGroupIds = userBigGroupMap.get(userId);
writeGroupIds(writer, bigGroupIds);
}
writer.close();
}
System.out.println("保存结束。");
}
private static void printUnfulfilledMap(Map<String, Integer> map) {
if(map.isEmpty())
return;
map.forEach((s, integer) -> System.out.print(s + ":" + integer + " "));
System.out.println();
}
private static void writeDeviceInfo(BufferedWriter writer, String userId, int platform) throws IOException {
writer.write(userId);
writer.write(",");
writer.write(UUID.randomUUID().toString());
writer.write(",");
writer.write(""+platform);
writer.write("\n");
}
private static void writeGroupIds(BufferedWriter writer, Set<String> groupIds) throws IOException {
int writeCount = 0;
for (String bigGroupId : groupIds) {
writer.write(bigGroupId);
writeCount++;
if(writeCount < groupIds.size()) {
writer.write(",");
} else {
writer.write("\n");
}
}
}
private static void createGroup(String userId, List<String> userIds, Map<String, Set<String>> userGroupMap, int groupCount, int memberSize, Map<String, Integer> unfulfilledUserMap) {
Set<String> groupIds = userGroupMap.computeIfAbsent(userId, s -> new HashSet<>());
int i = 0;
while (groupIds.size() < groupCount){
PojoGroupInfo groupInfo = new PojoGroupInfo();
groupInfo.setTarget_id(UUID.randomUUID().toString());
groupInfo.setOwner(userId);
groupInfo.setName("Group_"+userId+i++);
groupInfo.setType(2);
groupIds.add(groupInfo.getTarget_id());
List<String> memberCandidates = new ArrayList<>(userIds);
List<String> memberIds = new ArrayList<>();
memberIds.add(userId);
while (!memberCandidates.isEmpty() && memberIds.size() < memberSize) {
String memberId = memberCandidates.get((int)(Math.random()*memberCandidates.size()));
memberCandidates.remove(memberId);
if(memberId.equals(userId)) {
continue;
}
if (memberIds.contains(memberId)) {
continue;
}
Set<String> memberGroupIds = userGroupMap.computeIfAbsent(memberId, s -> new HashSet<>());
if(memberGroupIds.contains(groupInfo.getTarget_id()) || memberGroupIds.size() >= groupCount) {
continue;
}
memberGroupIds.add(groupInfo.getTarget_id());
memberIds.add(memberId);
}
List<PojoGroupMember> members = new ArrayList<>();
for (String memberId : memberIds) {
PojoGroupMember member1 = new PojoGroupMember();
member1.setMember_id(memberId);
members.add(member1);
}
try {
IMResult<OutputCreateGroupResult> resultCreateGroup = GroupAdmin.createGroup(groupInfo.getOwner(), groupInfo, members, null, null, null);
if (resultCreateGroup != null && resultCreateGroup.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("create group success");
} else {
System.out.println("create group failure");
System.exit(-1);
}
} catch (Exception e) {
System.out.println("create group failure:" + e);
System.exit(-1);
}
createGroupCount.incrementAndGet();
if(memberIds.size() < memberSize) {
unfulfilledUserMap.put(userId + "'s group " + groupInfo.getTarget_id(), memberIds.size());
System.out.println("Error, can not find enough user to create group for user:" + userId);
}
}
}
private static void addFriends(List<String> userIds, int count, Map<String, Integer> unfulfilledFriendMap) {
ConcurrentLinkedQueue<Pair<String, String>> toAddSet = new ConcurrentLinkedQueue<>();
Map<String, Set<String>> userFriends = new HashMap<>();
for (String userId : userIds) {
List<String> candidates = new ArrayList<>(userIds);
Set<String> currentUserFriends = userFriends.computeIfAbsent(userId, s -> new HashSet<>());
while (!candidates.isEmpty() && currentUserFriends.size() < count) {
String friendId = candidates.get((int) (Math.random()*candidates.size()));
candidates.remove(friendId);
if(friendId.equals(userId)) {
continue;
}
if(!currentUserFriends.contains(friendId)) {
Set<String> targetUserFriends = userFriends.computeIfAbsent(friendId, s -> new HashSet<>());
if(targetUserFriends.size() < count) {
currentUserFriends.add(friendId);
targetUserFriends.add(userId);
toAddSet.add(new Pair<>(userId, friendId));
}
}
}
if (currentUserFriends.size() < count) {
unfulfilledFriendMap.put(userId, currentUserFriends.size());
System.out.println("Error, can not find enough user to add friend for user:" + userId);
}
}
int threadNum = 5;
CountDownLatch latch = new CountDownLatch(threadNum);
for (int i = 0; i < threadNum; i++) {
new Thread(() -> {
while (true) {
Pair<String, String> pair = toAddSet.poll();
if(pair == null) {
latch.countDown();
break;
}
try {
IMResult<Void> result = RelationAdmin.setUserFriend(pair.first, pair.second, true, null);
if (result != null && (result.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS || result.getErrorCode() == ErrorCode.ERROR_CODE_ALREADY_FRIENDS)) {
System.out.println("add friend success");
addFriendCount.incrementAndGet();
} else {
System.out.println("failure");
System.exit(-1);
}
} catch (Exception e) {
System.out.println("failure:" + e);
System.exit(-1);
}
}
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
System.out.println("failure:" + e);
System.exit(-1);
}
}
private static String generateUserId(int index) {
return UUID.randomUUID().toString().replace("-", "");
}
private static List<String> generateUserIds(int count) {
List<String> userIds = new ArrayList<>();
for (int i = 0; i < count; i++) {
userIds.add(generateUserId(i));
}
return userIds;
}
private static void createUsers(List<String> userIds) {
for (int i = 0; i < userIds.size(); i++) {
InputOutputUserInfo userInfo = generateUserInfo(userIds.get(i), i);
try {
IMResult<OutputCreateUser> resultCreateUser = UserAdmin.createUser(userInfo);
if (resultCreateUser != null && resultCreateUser.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
System.out.println("Create user " + resultCreateUser.getResult().getName() + " success");
} else {
System.out.println("Create user failure");
System.exit(-1);
}
} catch (Exception e) {
System.out.println("Create user failure");
System.exit(-1);
}
}
}
private static InputOutputUserInfo generateUserInfo(String userId, int index) {
InputOutputUserInfo userInfo = new InputOutputUserInfo();
//用户ID,必须保证唯一性
userInfo.setUserId(userId);
//用户名,一般是用户登录帐号,也必须保证唯一性。也就是说所有用户的userId必须不能重复,所有用户的name必须不能重复,但可以同一个用户的userId和name是同一个,一般建议userId使用一个uuid,name是"微信号"且可以修改,
userInfo.setName(userId);
userInfo.setMobile((11000000000L+index) + "");
userInfo.setDisplayName("User"+index);
return userInfo;
}
static class Pair<K, V> {
K first;
V second;
public Pair() {
}
public Pair(K first, V second) {
this.first = first;
this.second = second;
}
}
}
@@ -2,14 +2,13 @@ package cn.wildfirechat.sdk;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.pojos.*;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.sdk.model.IMResult;
import cn.wildfirechat.sdk.utilities.AdminHttpUtils;
import java.util.List;
public class GroupAdmin {
public static IMResult<OutputCreateGroupResult> createGroup(String operator, PojoGroupInfo group_info, List<PojoGroupMember> members, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public static IMResult<OutputCreateGroupResult> createGroup(String operator, PojoGroupInfo group_info, List<PojoGroupMember> members, String member_extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Create_Group;
PojoGroup pojoGroup = new PojoGroup();
pojoGroup.setGroup_info(group_info);
@@ -17,6 +16,7 @@ public class GroupAdmin {
InputCreateGroup createGroup = new InputCreateGroup();
createGroup.setGroup(pojoGroup);
createGroup.setOperator(operator);
createGroup.setMember_extra(member_extra);
createGroup.setTo_lines(to_lines);
createGroup.setNotify_message(notify_message);
@@ -85,12 +85,13 @@ public class GroupAdmin {
return AdminHttpUtils.httpJsonPost(path, input, PojoGroupMember.class);
}
public static IMResult<Void> addGroupMembers(String operator, String groupId, List<PojoGroupMember> groupMembers, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public static IMResult<Void> addGroupMembers(String operator, String groupId, List<PojoGroupMember> groupMembers, String member_extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Group_Member_Add;
InputAddGroupMember addGroupMember = new InputAddGroupMember();
addGroupMember.setGroup_id(groupId);
addGroupMember.setMembers(groupMembers);
addGroupMember.setOperator(operator);
addGroupMember.setMemberExtra(member_extra);
addGroupMember.setTo_lines(to_lines);
addGroupMember.setNotify_message(notify_message);
return AdminHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
File diff suppressed because one or more lines are too long
@@ -81,13 +81,7 @@ public class MessageAdmin {
return AdminHttpUtils.httpJsonPost(path, inputMessageUid, OutputMessageData.class);
}
/**
* 撤回群发或者广播的消息
* @param operator 操作者
* @param messageUid 消息唯一ID
* @return
* @throws Exception
*/
//仅专业版支持
public static IMResult<Void> recallBroadCastMessage(String operator, long messageUid) throws Exception {
String path = APIPath.Msg_RecallBroadCast;
RecallMessageData messageData = new RecallMessageData();
@@ -102,10 +96,28 @@ public class MessageAdmin {
messageData.operator = operator;
messageData.messageUid = messageUid;
messageData.receivers = receivers;
return AdminHttpUtils.httpJsonPost(path, messageData, Void.class);
}
//仅专业版支持
public static IMResult<Void> deleteBroadCastMessage(String operator, long messageUid) throws Exception {
String path = APIPath.Msg_DeleteBroadCast;
RecallMessageData messageData = new RecallMessageData();
messageData.setOperator(operator);
messageData.setMessageUid(messageUid);
return AdminHttpUtils.httpJsonPost(path, messageData, Void.class);
}
public static IMResult<Void> deleteMultiCastMessage(String operator, long messageUid, List<String> receivers) throws Exception {
String path = APIPath.Msg_DeleteMultiCast;
RecallMultiCastMessageData messageData = new RecallMultiCastMessageData();
messageData.operator = operator;
messageData.messageUid = messageUid;
messageData.receivers = receivers;
return AdminHttpUtils.httpJsonPost(path, messageData, Void.class);
}
//仅专业版支持
public static IMResult<BroadMessageResult> broadcastMessage(String sender, int line, MessagePayload payload) throws Exception {
String path = APIPath.Msg_Broadcast;
BroadMessageData messageData = new BroadMessageData();
@@ -1,7 +1,9 @@
package cn.wildfirechat.sdk;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.*;
import cn.wildfirechat.pojos.moments.*;
import cn.wildfirechat.sdk.model.IMResult;
import cn.wildfirechat.sdk.utilities.RobotHttpUtils;
import org.apache.commons.codec.digest.DigestUtils;
@@ -96,13 +98,14 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, pojo, Void.class);
}
public IMResult<OutputCreateGroupResult> createGroup(PojoGroupInfo group_info, List<PojoGroupMember> members, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<OutputCreateGroupResult> createGroup(PojoGroupInfo group_info, List<PojoGroupMember> members, String member_extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Create_Group;
PojoGroup pojoGroup = new PojoGroup();
pojoGroup.setGroup_info(group_info);
pojoGroup.setMembers(members);
InputCreateGroup createGroup = new InputCreateGroup();
createGroup.setGroup(pojoGroup);
createGroup.setMember_extra(member_extra);
createGroup.setTo_lines(to_lines);
createGroup.setNotify_message(notify_message);
@@ -117,7 +120,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, input, PojoGroupInfo.class);
}
public IMResult<Void> dismissGroup(String groupId, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> dismissGroup(String groupId, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Dismiss;
InputDismissGroup dismissGroup = new InputDismissGroup();
dismissGroup.setGroup_id(groupId);
@@ -126,7 +129,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, dismissGroup, Void.class);
}
public IMResult<Void> transferGroup(String groupId, String newOwner, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> transferGroup(String groupId, String newOwner, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Transfer;
InputTransferGroup transferGroup = new InputTransferGroup();
transferGroup.setGroup_id(groupId);
@@ -136,7 +139,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, transferGroup, Void.class);
}
public IMResult<Void> modifyGroupInfo(String groupId, /*ModifyGroupInfoType*/int type, String value, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> modifyGroupInfo(String groupId, /*ModifyGroupInfoType*/int type, String value, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Modify_Info;
InputModifyGroupInfo modifyGroupInfo = new InputModifyGroupInfo();
modifyGroupInfo.setGroup_id(groupId);
@@ -163,17 +166,18 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, input, PojoGroupMember.class);
}
public IMResult<Void> addGroupMembers(String groupId, List<PojoGroupMember> groupMembers, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> addGroupMembers(String groupId, List<PojoGroupMember> groupMembers, String member_extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Member_Add;
InputAddGroupMember addGroupMember = new InputAddGroupMember();
addGroupMember.setGroup_id(groupId);
addGroupMember.setMembers(groupMembers);
addGroupMember.setMemberExtra(member_extra);
addGroupMember.setTo_lines(to_lines);
addGroupMember.setNotify_message(notify_message);
return robotHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
}
public IMResult<Void> setGroupManager(String groupId, List<String> groupMemberIds, boolean isManager, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> setGroupManager(String groupId, List<String> groupMemberIds, boolean isManager, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Set_Manager;
InputSetGroupManager addGroupMember = new InputSetGroupManager();
addGroupMember.setGroup_id(groupId);
@@ -184,7 +188,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
}
public IMResult<Void> muteGroupMember(String groupId, List<String> groupMemberIds, boolean isMute, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> muteGroupMember(String groupId, List<String> groupMemberIds, boolean isMute, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Mute_Member;
InputMuteGroupMember addGroupMember = new InputMuteGroupMember();
addGroupMember.setGroup_id(groupId);
@@ -195,7 +199,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
}
public IMResult<Void> allowGroupMember(String groupId, List<String> groupMemberIds, boolean isAllow, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> allowGroupMember(String groupId, List<String> groupMemberIds, boolean isAllow, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Allow_Member;
InputMuteGroupMember addGroupMember = new InputMuteGroupMember();
addGroupMember.setGroup_id(groupId);
@@ -206,7 +210,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, addGroupMember, Void.class);
}
public IMResult<Void> kickoffGroupMembers(String groupId, List<String> groupMemberIds, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> kickoffGroupMembers(String groupId, List<String> groupMemberIds, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Member_Kickoff;
InputKickoffGroupMember kickoffGroupMember = new InputKickoffGroupMember();
kickoffGroupMember.setGroup_id(groupId);
@@ -216,7 +220,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, kickoffGroupMember, Void.class);
}
public IMResult<Void> quitGroup(String groupId, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> quitGroup(String groupId, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Member_Quit;
InputQuitGroup quitGroup = new InputQuitGroup();
quitGroup.setGroup_id(groupId);
@@ -225,7 +229,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, quitGroup, Void.class);
}
public IMResult<Void> setGroupMemberAlias(String groupId, String memberId, String alias, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> setGroupMemberAlias(String groupId, String memberId, String alias, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Set_Member_Alias;
InputSetGroupMemberAlias input = new InputSetGroupMemberAlias();
input.setGroup_id(groupId);
@@ -236,7 +240,7 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, input, Void.class);
}
public IMResult<Void> setGroupMemberExtra(String groupId, String memberId, String extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
public IMResult<Void> setGroupMemberExtra(String groupId, String memberId, String extra, List<Integer> to_lines, MessagePayload notify_message) throws Exception {
String path = APIPath.Robot_Group_Set_Member_Extra;
InputSetGroupMemberExtra input = new InputSetGroupMemberExtra();
input.setGroup_id(groupId);
@@ -274,4 +278,144 @@ public class RobotService {
return robotHttpUtils.httpJsonPost(path, input, String.class);
}
public IMResult<FeedPojo> postMomentsFeed(int/*MomentsContentType*/ type, String text, List<MediaEntry> medias, List<String> toUsers, List<String> excludeUsers, List<String> mentionedUsers, String extra) throws Exception {
String path = APIPath.Robot_Moments_Post_Feed;
FeedPojo feedPojo = new FeedPojo();
feedPojo.type = type;
feedPojo.text = text;
feedPojo.medias = medias;
feedPojo.to = toUsers;
feedPojo.ex = excludeUsers;
feedPojo.mu = mentionedUsers;
feedPojo.extra = extra;
IMResult<PostFeedResult> imResult = robotHttpUtils.httpJsonPost(path, feedPojo, PostFeedResult.class);
IMResult<FeedPojo> feedResult = new IMResult<>();
if(imResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
feedPojo.feedId = imResult.result.id;
feedPojo.timestamp = imResult.result.timestamp;
feedResult.setCode(ErrorCode.ERROR_CODE_SUCCESS.getCode());
feedResult.setResult(feedPojo);
} else {
feedResult.setCode(imResult.getCode());
feedResult.setMsg(imResult.getMsg());
}
return feedResult;
}
public IMResult<Void> updateMomentsFeed(long feedId, int/*MomentsContentType*/ type, String text, List<MediaEntry> medias, List<String> toUsers, List<String> excludeUsers, List<String> mentionedUsers, String extra) throws Exception {
String path = APIPath.Robot_Moments_Update_Feed;
FeedPojo feedPojo = new FeedPojo();
feedPojo.feedId = feedId;
feedPojo.type = type;
feedPojo.text = text;
feedPojo.medias = medias;
feedPojo.to = toUsers;
feedPojo.ex = excludeUsers;
feedPojo.mu = mentionedUsers;
feedPojo.extra = extra;
return robotHttpUtils.httpJsonPost(path, feedPojo, Void.class);
}
public IMResult<FeedsPojo> getMomentsFeeds(long feedId, int count, String user) throws Exception {
String path = APIPath.Robot_Moments_Pull_Feeds;
PullFeedRequestPojo requestPojo = new PullFeedRequestPojo();
requestPojo.feedId = feedId;
requestPojo.count = count;
requestPojo.user = user;
return robotHttpUtils.httpJsonPost(path, requestPojo, FeedsPojo.class);
}
public IMResult<FeedPojo> getMomentsFeed(long feedId) throws Exception {
String path = APIPath.Robot_Moments_Fetch_Feed;
PullOneFeedRequestPojo requestPojo = new PullOneFeedRequestPojo();
requestPojo.feedId = feedId;
return robotHttpUtils.httpJsonPost(path, requestPojo, FeedPojo.class);
}
public IMResult<Void> deleteMomentsFeed(long feedId) throws Exception {
String path = APIPath.Robot_Moments_Recall_Feed;
IdPojo requestPojo = new IdPojo();
requestPojo.id = feedId;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<CommentPojo> postMomentsComment(long feedId, long replyId, int/*MomentsCommentType*/ type, String text, String replyTo, String extra) throws Exception {
String path = APIPath.Robot_Moments_Post_Comment;
CommentPojo commentPojo = new CommentPojo();
commentPojo.type = type;
commentPojo.text = text;
commentPojo.replyId = replyId;
commentPojo.feedId = feedId;
commentPojo.replyTo = replyTo;
commentPojo.extra = extra;
IMResult<PostFeedResult> imResult = robotHttpUtils.httpJsonPost(path, commentPojo, PostFeedResult.class);
IMResult<CommentPojo> feedResult = new IMResult<>();
if(imResult.getErrorCode() == ErrorCode.ERROR_CODE_SUCCESS) {
commentPojo.commentId = imResult.result.id;
commentPojo.timestamp = imResult.result.timestamp;
feedResult.setCode(ErrorCode.ERROR_CODE_SUCCESS.getCode());
feedResult.setResult(commentPojo);
} else {
feedResult.setCode(imResult.getCode());
feedResult.setMsg(imResult.getMsg());
}
return feedResult;
}
public IMResult<Void> deleteMomentsComment(long feedId, long commentId) throws Exception {
String path = APIPath.Robot_Moments_Recall_Comment;
IdPojo requestPojo = new IdPojo();
requestPojo.id = commentId;
requestPojo.id2 = feedId;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<MomentProfilePojo> getUserMomentsProfile(String userId) throws Exception {
String path = APIPath.Robot_Moments_Fetch_Profiles;
PullProfileRequestPojo requestPojo = new PullProfileRequestPojo();
requestPojo.u = userId;
return robotHttpUtils.httpJsonPost(path, requestPojo, MomentProfilePojo.class);
}
public IMResult<Void> updateMomentsBackgroundUrl(String url) throws Exception {
String path = APIPath.Robot_Moments_Update_Profiles_Value;
PushProfileValueRequestPojo requestPojo = new PushProfileValueRequestPojo();
requestPojo.t = 0;
requestPojo.v = url;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<Void> updateMomentsStrangerVisibleCount(int count) throws Exception {
String path = APIPath.Robot_Moments_Update_Profiles_Value;
PushProfileValueRequestPojo requestPojo = new PushProfileValueRequestPojo();
requestPojo.t = 1;
requestPojo.i = count;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<Void> updateMomentsVisibleScope(int/*MomentsVisibleScope*/ scope) throws Exception {
String path = APIPath.Robot_Moments_Update_Profiles_Value;
PushProfileValueRequestPojo requestPojo = new PushProfileValueRequestPojo();
requestPojo.t = 2;
requestPojo.i = scope;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<Void> updateMomentsBlackList(List<String> addList, List<String> removeList) throws Exception {
String path = APIPath.Robot_Moments_Update_Profiles_List_Value;
PushProfileListRequestPojo requestPojo = new PushProfileListRequestPojo();
requestPojo.b = false;
requestPojo.al = addList;
requestPojo.rl = removeList;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
public IMResult<Void> updateMomentsBlockList(List<String> addList, List<String> removeList) throws Exception {
String path = APIPath.Robot_Moments_Update_Profiles_List_Value;
PushProfileListRequestPojo requestPojo = new PushProfileListRequestPojo();
requestPojo.b = true;
requestPojo.al = addList;
requestPojo.rl = removeList;
return robotHttpUtils.httpJsonPost(path, requestPojo, Void.class);
}
}
@@ -5,6 +5,8 @@ import cn.wildfirechat.pojos.*;
import cn.wildfirechat.sdk.model.IMResult;
import cn.wildfirechat.sdk.utilities.AdminHttpUtils;
import java.util.List;
public class UserAdmin {
public static IMResult<InputOutputUserInfo> getUserByName(String name) throws Exception {
String path = APIPath.User_Get_Info;
@@ -37,6 +39,13 @@ public class UserAdmin {
return AdminHttpUtils.httpJsonPost(path, input, OutputGetUserList.class);
}
public static IMResult<OutputUserInfoList> getBatchUsers(List<String> userIds) throws Exception {
String path = APIPath.User_Batch_Get_Infos;
InputStringList input = new InputStringList();
input.setList(userIds);
return AdminHttpUtils.httpJsonPost(path, input, OutputUserInfoList.class);
}
public static IMResult<OutputCreateUser> createUser(InputOutputUserInfo user) throws Exception {
String path = APIPath.Create_User;
return AdminHttpUtils.httpJsonPost(path, user, OutputCreateUser.class);
@@ -0,0 +1,125 @@
/*
* Copyright (c) 2022 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public class ArticlesMessageContent extends MessageContent {
public Article topArticle;
public ArrayList<Article> subArticles;
@Override
public int getContentType() {
return ProtoConstants.ContentType.Articles;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent(topArticle.title);
JSONObject object = new JSONObject();
object.put("top", topArticle.toJson());
if (subArticles != null) {
JSONArray jsonArray = new JSONArray();
object.put("subArticles", jsonArray);
for (Article article : subArticles) {
jsonArray.add(article.toJson());
}
}
payload.setBase64edData(Base64.getEncoder().encodeToString(object.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
try {
JSONObject object = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
JSONObject topObj = (JSONObject) object.get("top");
this.topArticle = Article.fromJson(topObj);
JSONArray jsonArray = (JSONArray) object.get("subArticles");
if (jsonArray != null && jsonArray.size() > 0) {
this.subArticles = new ArrayList<>();
for (int i = 0; i < jsonArray.size(); i++) {
subArticles.add(Article.fromJson((JSONObject) jsonArray.get(i)));
}
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
public List<LinkMessageContent> toLinkMessageContent() {
List<LinkMessageContent> contents = new ArrayList<>();
contents.add(this.topArticle.toLinkMessageContent());
if (this.subArticles != null) {
for (Article article : subArticles) {
contents.add(article.toLinkMessageContent());
}
}
return contents;
}
public static class Article {
public String articleId;
public String cover;
public String title;
public String digest;
public String url;
boolean readReport;
JSONObject toJson() {
JSONObject obj = new JSONObject();
obj.put("id", articleId);
obj.put("cover", cover);
obj.put("title", title);
obj.put("digest", digest);
obj.put("url", url);
obj.put("rr", readReport);
return obj;
}
static Article fromJson(JSONObject obj) {
Article article = new Article();
article.articleId = (String) obj.get("id");
article.cover = (String) obj.get("cover");
article.title = (String) obj.get("title");
article.digest = (String) obj.get("digest");
article.url = (String) obj.get("url");
article.readReport = (boolean) obj.get("rr");
return article;
}
public LinkMessageContent toLinkMessageContent() {
LinkMessageContent content = new LinkMessageContent(this.title, this.url);
content.setContentDigest(this.digest);
content.setThumbnailUrl(this.cover);
return content;
}
public Article() {
}
}
}
@@ -0,0 +1,205 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import io.netty.util.internal.StringUtil;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
/**
* Created by heavyrain lee on 2017/12/6.
*/
public class CallStartMessageContent extends MessageContent {
private String callId;
// 多人视音频是有效,不包含自己,一对一忽略此参数
private List<String> targetIds;
private long connectTime;
private long endTime;
private boolean audioOnly;
private String pin;
/**
* 0, UnKnown,
* 1, Busy,
* 2, SignalError,
* 3, Hangup,
* 4, MediaError,
* 5, RemoteHangup,
* 6, OpenCameraFailure,
* 7, Timeout,
* 8, AcceptByOtherClient
*/
private int status;
/**
* 0,未知;1,多人版音视频;2,高级版音视频
*/
private int type;
public CallStartMessageContent() {
}
public CallStartMessageContent(String callId, List<String> targetIds, boolean audioOnly) {
this.callId = callId;
this.audioOnly = audioOnly;
this.targetIds = targetIds;
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public long getConnectTime() {
return connectTime;
}
public void setConnectTime(long connectTime) {
this.connectTime = connectTime;
}
public long getEndTime() {
return endTime;
}
public void setEndTime(long endTime) {
this.endTime = endTime;
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public boolean isAudioOnly() {
return audioOnly;
}
public void setAudioOnly(boolean audioOnly) {
this.audioOnly = audioOnly;
}
public List<String> getTargetIds() {
return targetIds;
}
public void setTargetIds(List<String> targetIds) {
this.targetIds = targetIds;
}
public String getPin() {
return pin;
}
public void setPin(String pin) {
this.pin = pin;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Call_Start;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setContent(callId);
payload.setPushContent("音视频通话邀请");
JSONObject objWrite = new JSONObject();
if (connectTime > 0) {
objWrite.put("c", connectTime);
}
if (endTime > 0) {
objWrite.put("e", endTime);
}
if (status > 0) {
objWrite.put("s", status);
}
objWrite.put("t", targetIds.get(0));
JSONArray ts = new JSONArray();
ts.addAll(targetIds);
objWrite.put("ts", ts);
objWrite.put("a", audioOnly ? 1 : 0);
objWrite.put("p", pin);
if (this.type > 0) {
objWrite.put("ty", this.type);
}
payload.setBase64edData(Base64.getEncoder().encodeToString(objWrite.toString().getBytes()));
JSONObject pushDataWrite = new JSONObject();
pushDataWrite.put("callId", callId);
pushDataWrite.put("audioOnly", audioOnly);
if (targetIds != null && targetIds.size() > 0) {
pushDataWrite.put("participants", targetIds);
}
payload.setPushData(pushDataWrite.toString());
return payload;
}
@Override
public void decode(MessagePayload payload) {
callId = payload.getContent();
try {
if (!StringUtil.isNullOrEmpty(payload.getBase64edData())) {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
connectTime = (long) jsonObject.get("c");
endTime = (long) jsonObject.get("e");
status = (int) jsonObject.get("s");
pin = (String) jsonObject.get("p");
type = (int) jsonObject.get("ty");
JSONArray array = (JSONArray) jsonObject.get("ts");
targetIds = new ArrayList<>();
if (array == null) {
targetIds.add((String) jsonObject.get("t"));
} else {
for (int i = 0; i < array.size(); i++) {
if (array.get(i) instanceof String) {
targetIds.add((String) array.get(i));
}
}
}
audioOnly = (int)jsonObject.get("a") > 0;
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,133 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import io.netty.util.internal.StringUtil;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Base64;
public class CardMessageContent extends MessageContent {
/**
* 0,用户;1,群组;2,聊天室;3,频道
*/
private int type;
private String target;
// 用户名,一般是type为用户时使用
private String name;
private String displayName;
private String portrait;
private String from;
public CardMessageContent() {
}
public CardMessageContent(int type, String target, String displayName, String portrait, String from) {
this.type = type;
this.target = target;
this.displayName = displayName;
this.portrait = portrait;
this.from = from;
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDisplayName() {
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public String getPortrait() {
return portrait;
}
public void setPortrait(String portrait) {
this.portrait = portrait;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Name_Card;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setContent(target);
JSONObject objWrite = new JSONObject();
objWrite.put("t", type);
objWrite.put("n", name);
objWrite.put("d", displayName);
objWrite.put("p", portrait);
objWrite.put("f", from);
payload.setBase64edData(Base64.getEncoder().encodeToString(objWrite.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
target = payload.getContent();
try {
if (!StringUtil.isNullOrEmpty(payload.getBase64edData())) {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
type = (int) jsonObject.get("t");
name = (String) jsonObject.get("n");
displayName = (String) jsonObject.get("d");
portrait = (String) jsonObject.get("p");
from = (String) jsonObject.get("f");
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class FileMessageContent extends MediaMessageContent {
private String name;
private int size;
private static final String FILE_NAME_PREFIX = "[文件] ";
@Override
public int getContentType() {
return ProtoConstants.ContentType.File;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent(name);
payload.setContent(size + "");
return payload;
}
public String getName() {
return name;
}
public int getSize() {
return size;
}
public void setName(String name) {
this.name = name;
}
public void setSize(int size) {
this.size = size;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
if (payload.getSearchableContent().startsWith(FILE_NAME_PREFIX)) {
name = payload.getSearchableContent().substring(payload.getSearchableContent().indexOf(FILE_NAME_PREFIX) + FILE_NAME_PREFIX.length());
} else {
name = payload.getSearchableContent();
}
size = Integer.parseInt(payload.getContent());
}
@Override
protected int getMediaType() {
return ProtoConstants.MessageMediaType.FILE;
}
}
@@ -0,0 +1,55 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import java.util.Base64;
public class ImageMessageContent extends MediaMessageContent {
private byte[] thumbnailBytes;
public void setThumbnailBytes(byte[] thumbnailBytes) {
this.thumbnailBytes = thumbnailBytes;
}
public byte[] getThumbnailBytes() {
return thumbnailBytes;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Image;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent("[图片]");
payload.setBase64edData(Base64.getEncoder().encodeToString(thumbnailBytes));
return payload;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
thumbnailBytes = Base64.getDecoder().decode(payload.getBase64edData());
}
@Override
protected int getMediaType() {
return ProtoConstants.MessageMediaType.IMAGE;
}
}
@@ -0,0 +1,105 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import io.netty.util.internal.StringUtil;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Base64;
public class LinkMessageContent extends MessageContent {
private String title;
private String contentDigest;
private String url;
private String thumbnailUrl;
public LinkMessageContent() {
}
public LinkMessageContent(String title, String url) {
this.title = title;
this.url = url;
}
public String getContentDigest() {
return contentDigest;
}
public void setContentDigest(String contentDigest) {
this.contentDigest = contentDigest;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getThumbnailUrl() {
return thumbnailUrl;
}
public void setThumbnailUrl(String thumbnailUrl) {
this.thumbnailUrl = thumbnailUrl;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Link;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent(title);
JSONObject objWrite = new JSONObject();
objWrite.put("d", contentDigest);
objWrite.put("u", url);
objWrite.put("t", thumbnailUrl);
payload.setBase64edData(Base64.getEncoder().encodeToString(objWrite.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
title = payload.getSearchableContent();
try {
if (!StringUtil.isNullOrEmpty(payload.getBase64edData())) {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
contentDigest = (String) jsonObject.get("d");
url = (String) jsonObject.get("u");
thumbnailUrl = (String) jsonObject.get("t");
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import io.netty.util.internal.StringUtil;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Base64;
public class LocationMessageContent extends MessageContent {
private String title;
public byte[] thumbnailByte;
private double longitude;
private double latitude;
public LocationMessageContent() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public byte[] getThumbnailByte() {
return thumbnailByte;
}
public void setThumbnailByte(byte[] thumbnailByte) {
this.thumbnailByte = thumbnailByte;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Location;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent(title);
payload.setBase64edData(Base64.getEncoder().encodeToString(this.thumbnailByte));
JSONObject objWrite = new JSONObject();
objWrite.put("lat", latitude);
objWrite.put("long", longitude);
payload.setContent(objWrite.toString());
return payload;
}
@Override
public void decode(MessagePayload payload) {
if (!StringUtil.isNullOrEmpty(payload.getBase64edData())) {
this.thumbnailByte = Base64.getDecoder().decode(payload.getBase64edData());
}
title = payload.getSearchableContent();
try {
if (payload.getContent() != null) {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(payload.getContent());
latitude = (double) jsonObject.get("lat");
longitude = (double) jsonObject.get("long");
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,25 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
abstract public class MediaMessageContent extends MessageContent {
private String remoteMediaUrl;
public String getRemoteMediaUrl() {
return remoteMediaUrl;
}
public void setRemoteMediaUrl(String remoteMediaUrl) {
this.remoteMediaUrl = remoteMediaUrl;
}
@Override
protected MessagePayload encodeBase() {
MessagePayload payload = super.encodeBase();
payload.setRemoteMediaUrl(remoteMediaUrl);
payload.setMediaType(getMediaType());
return payload;
}
protected abstract int getMediaType();
}
@@ -0,0 +1,44 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import java.util.List;
public abstract class MessageContent {
private int mentionedType;
private List<String> mentionedTargets;
private String extra;
public MessageContent mentionedType(int mentionedType) {
this.mentionedType = mentionedType;
return this;
}
public MessageContent mentionedTargets(List<String> mentionedTargets) {
this.mentionedTargets = mentionedTargets;
return this;
}
public MessageContent extra(String extra) {
this.extra = extra;
return this;
}
protected MessagePayload encodeBase() {
MessagePayload payload = new MessagePayload();
payload.setType(getContentType());
payload.setPersistFlag(getPersistFlag());
payload.setMentionedType(mentionedType);
payload.setMentionedTarget(mentionedTargets);
payload.setExtra(extra);
return payload;
}
protected void decodeBase(MessagePayload payload) {
this.mentionedType = payload.getMentionedType();
this.mentionedTargets = payload.getMentionedTarget();
this.extra = payload.getExtra();
}
abstract public int getContentType();
abstract public int getPersistFlag();
abstract public MessagePayload encode();
abstract public void decode(MessagePayload payload);
}
@@ -0,0 +1,77 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.Conversation;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.WFCMessage;
import cn.wildfirechat.sdk.model.Message;
import cn.wildfirechat.sdk.utilities.ClassUtil;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class MessageContentFactory {
private static final Map<Integer, Class<MessageContent>> contentClassMap = new ConcurrentHashMap<>();
static {
registerAllMessageContent();
}
public static Message decodeMessage(WFCMessage.Message protoMessage) {
Message message = new Message();
message.content = decodeMessageContent(protoMessage.getContent());
WFCMessage.Conversation protoConversation = protoMessage.getConversation();
message.conversation = new Conversation(protoConversation.getType(), protoConversation.getTarget(), protoConversation.getLine());
message.messageUid = protoMessage.getMessageId();
message.sender = protoMessage.getFromUser();
message.serverTime = protoMessage.getServerTimestamp();
message.toUsers = protoMessage.getToList();
return message;
}
public static MessageContent decodeMessageContent(WFCMessage.MessageContent protoMessageContent) {
MessagePayload payload = MessagePayload.fromProtoMessageContent(protoMessageContent);
return decodeMessageContent(payload);
}
public static MessageContent decodeMessageContent(MessagePayload messagePayload) {
Class<MessageContent> cls = contentClassMap.get(messagePayload.getType());
MessageContent messageContent;
if(cls != null) {
try {
messageContent = cls.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
} else {
messageContent = new UnknownMessageContent();
}
messageContent.decodeBase(messagePayload);
messageContent.decode(messagePayload);
return messageContent;
}
private static void registerAllMessageContent() {
try {
for (Class cls : ClassUtil.getAllAssignedClass(MessageContent.class)) {
if(!Modifier.isAbstract(cls.getModifiers())) {
try {
MessageContent content = (MessageContent)cls.newInstance();
contentClassMap.put(content.getContentType(), cls);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,111 @@
/*
* Copyright (c) 2022 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public class MultiCallOngoingMessageContent extends MessageContent {
private String callId;
private String initiator;
private boolean audioOnly;
private List<String> targets;
public MultiCallOngoingMessageContent() {
}
public MultiCallOngoingMessageContent(String callId, String initiator, boolean audioOnly, List<String> targets) {
this.callId = callId;
this.initiator = initiator;
this.audioOnly = audioOnly;
this.targets = targets;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Call_Multi_Call_Ongoing;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Transparent;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setContent(callId);
JSONObject object = new JSONObject();
object.put("initiator", this.initiator);
JSONArray arr = new JSONArray();
for (int i = 0; i < targets.size(); i++) {
arr.add(i, targets.get(i));
}
object.put("targets", arr);
object.put("audioOnly", this.audioOnly ? 1 : 0);
payload.setBase64edData(Base64.getEncoder().encodeToString(object.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
this.callId = payload.getContent();
try {
JSONObject object = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
this.initiator = (String) object.get("initiator");
this.targets = new ArrayList<>();
JSONArray array = (JSONArray) object.get("targets");
if (array != null) {
for (int i = 0; i < array.size(); i++) {
targets.add((String) array.get(i));
}
}
this.audioOnly = (int)object.get("audioOnly") == 1;
} catch (ParseException e) {
e.printStackTrace();
}
}
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public String getInitiator() {
return initiator;
}
public void setInitiator(String initiator) {
this.initiator = initiator;
}
public boolean isAudioOnly() {
return audioOnly;
}
public void setAudioOnly(boolean audioOnly) {
this.audioOnly = audioOnly;
}
public List<String> getTargets() {
return targets;
}
public void setTargets(List<String> targets) {
this.targets = targets;
}
}
@@ -0,0 +1,97 @@
/*
* Copyright (c) 2024 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
public class NotDeliveredMessageContent extends MessageContent {
// 请求的类型,1 发送消息,2 撤回消息,3 删除消息
private int type;
// 发送的消息 uid
private long messageUid;
// 是全部失败,还是部分失败
private boolean allFailure;
// 部分失败时,失败的用户 id 列表
private List<String> userIds;
// 归属IM服务请求桥接服务出现的错误,有可能是桥接服务没有配置,或者不可用。
private int localImErrorCode;
// 归属桥接服务出现的错误
private int localBridgeErrorCode;
// 远端桥接服务出现的错误
private int remoteBridgeErrorCode;
// 远端IM服务出现的错误
private int remoteServerErrorCode;
// 错误提示信息
private String errorMessage;
@Override
public int getContentType() {
return ProtoConstants.ContentType.Not_Delivered;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
JSONObject obj = new JSONObject();
obj.put("mid", this.messageUid);
obj.put("all", this.allFailure);
obj.put("us", this.userIds);
obj.put("lme", this.localImErrorCode);
obj.put("lbe", this.localBridgeErrorCode);
obj.put("rbe", this.remoteBridgeErrorCode);
obj.put("rme", this.remoteServerErrorCode);
obj.put("em", this.errorMessage);
payload.setBase64edData(Base64.getEncoder().encodeToString(obj.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
try {
JSONObject obj = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
this.messageUid = (long) obj.get("mid");
this.allFailure = (boolean) obj.get("all");
this.userIds = new ArrayList<>();
JSONArray arr = (JSONArray) obj.get("us");
if (arr != null) {
for (int i = 0; i < arr.size(); i++) {
this.userIds.add((String) arr.get(i));
}
}
this.localImErrorCode = (int) obj.get("lme");
this.localBridgeErrorCode = (int) obj.get("lbe");
this.remoteBridgeErrorCode = (int) obj.get("rbe");
this.remoteServerErrorCode = (int) obj.get("rme");
this.errorMessage = (String) obj.get("em");
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2021 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONObject;
public class PTTSoundMessageContent extends SoundMessageContent {
public PTTSoundMessageContent() {
super();
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encode();
payload.setSearchableContent("[对讲语音]");
return payload;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Ptt_Voice;
}
}
@@ -1,4 +1,4 @@
package cn.wildfirechat.messagecontentbuilder;
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
@@ -9,7 +9,7 @@ import org.json.simple.JSONObject;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class RichNotificationContentBuilder extends MessageContentBuilder{
public class RichNotificationMessageContent extends MessageContent {
private String title;
private String desc;
private String remark;
@@ -19,34 +19,51 @@ public class RichNotificationContentBuilder extends MessageContentBuilder{
private String exUrl;
private String appId;
public static RichNotificationContentBuilder newBuilder(String title, String desc, String exUrl) {
RichNotificationContentBuilder builder = new RichNotificationContentBuilder();
builder.title = title;
builder.desc = desc;
builder.exUrl = exUrl;
return builder;
//必须有个空的构造函数
public RichNotificationMessageContent() {
}
public RichNotificationContentBuilder remark(String remark) {
public RichNotificationMessageContent(String title, String desc, String exUrl) {
this.title = title;
this.desc = desc;
this.exUrl = exUrl;
}
public RichNotificationMessageContent title(String title) {
this.title = title;
return this;
}
public RichNotificationMessageContent desc(String desc) {
this.desc = desc;
return this;
}
public RichNotificationMessageContent exUrl(String exUrl) {
this.exUrl = exUrl;
return this;
}
public RichNotificationMessageContent remark(String remark) {
this.remark = remark;
return this;
}
public RichNotificationContentBuilder exName(String exName) {
public RichNotificationMessageContent exName(String exName) {
this.exName = exName;
return this;
}
public RichNotificationContentBuilder exPortrait(String exPortrait) {
public RichNotificationMessageContent exPortrait(String exPortrait) {
this.exPortrait = exPortrait;
return this;
}
public RichNotificationContentBuilder appId(String appId) {
public RichNotificationMessageContent appId(String appId) {
this.appId = appId;
return this;
}
public RichNotificationContentBuilder addItem(String key, String value) {
public RichNotificationMessageContent addItem(String key, String value) {
return addItem(key, value, null);
}
public RichNotificationContentBuilder addItem(String key, String value, String color) {
public RichNotificationMessageContent addItem(String key, String value, String color) {
if(this.datas == null) {
this.datas = new JSONArray();
}
@@ -61,10 +78,18 @@ public class RichNotificationContentBuilder extends MessageContentBuilder{
}
@Override
public MessagePayload build() {
public int getContentType() {
return ProtoConstants.ContentType.Rich_Notification;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = encodeBase();
payload.setType(ProtoConstants.ContentType.Rich_Notification);
payload.setPersistFlag(ProtoConstants.PersistFlag.Persist_And_Count);
payload.setPushContent(title);
payload.setContent(desc);
JSONObject jsonObject = new JSONObject();
@@ -85,4 +110,8 @@ public class RichNotificationContentBuilder extends MessageContentBuilder{
return payload;
}
@Override
public void decode(MessagePayload payload) {
}
}
@@ -0,0 +1,53 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONObject;
public class SoundMessageContent extends MediaMessageContent {
private int duration;
//必须有个空的构造函数
public SoundMessageContent() {
}
public SoundMessageContent(int duration) {
this.duration = duration;
}
public int getDuration() {
return duration;
}
public void setDuration(int duration) {
this.duration = duration;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Voice;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public void decode(MessagePayload payload) {
}
@Override
public MessagePayload encode() {
MessagePayload payload = encodeBase();
JSONObject jsonObject = new JSONObject();
jsonObject.put("duration", duration);
payload.setContent(jsonObject.toJSONString());
return payload;
}
@Override
protected int getMediaType() {
return ProtoConstants.MessageMediaType.VOICE;
}
}
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Base64;
public class StickerMessageContent extends MediaMessageContent {
public int width;
public int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent("[动态表情]");
JSONObject objWrite = new JSONObject();
objWrite.put("x", width);
objWrite.put("y", height);
payload.setBase64edData(Base64.getEncoder().encodeToString(objWrite.toString().getBytes()));
return payload;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
try {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(new String(Base64.getDecoder().decode(payload.getBase64edData())));
width = (int) jsonObject.get("x");
height = (int) jsonObject.get("y");
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
@Override
protected int getMediaType() {
return ProtoConstants.MessageMediaType.STICKER;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Sticker;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
}
@@ -0,0 +1,52 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class StreamTextGeneratedMessageContent extends MessageContent {
private String text;
private String streamId;
//必须有个空的构造函数
public StreamTextGeneratedMessageContent() {
}
public StreamTextGeneratedMessageContent(String text, String streamId) {
this.text = text;
this.streamId = streamId;
}
public StreamTextGeneratedMessageContent text(String text) {
this.text = text;
return this;
}
public StreamTextGeneratedMessageContent streamId(String streamId) {
this.streamId = streamId;
return this;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.StreamingText_Generated;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public void decode(MessagePayload payload) {
this.streamId = payload.getContent();
this.text = payload.getSearchableContent();
}
@Override
public MessagePayload encode() {
MessagePayload payload = encodeBase();
payload.setSearchableContent(text);
payload.setContent(streamId);
return payload;
}
}
@@ -0,0 +1,52 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class StreamTextGeneratingMessageContent extends MessageContent {
private String text;
private String streamId;
//必须有个空的构造函数
public StreamTextGeneratingMessageContent() {
}
public StreamTextGeneratingMessageContent(String text, String streamId) {
this.text = text;
this.streamId = streamId;
}
public StreamTextGeneratingMessageContent text(String text) {
this.text = text;
return this;
}
public StreamTextGeneratingMessageContent streamId(String streamId) {
this.streamId = streamId;
return this;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.StreamingText_Generationg;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Transparent;
}
@Override
public void decode(MessagePayload payload) {
streamId = payload.getContent();
text = payload.getSearchableContent();
}
@Override
public MessagePayload encode() {
MessagePayload payload = encodeBase();
payload.setSearchableContent(text);
payload.setContent(streamId);
return payload;
}
}
@@ -0,0 +1,81 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.sdk.model.QuoteInfo;
import org.apache.http.util.TextUtils;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class TextMessageContent extends MessageContent {
private String text;
private QuoteInfo quoteInfo;
//必须有个空的构造函数
public TextMessageContent() {
}
public TextMessageContent(String text) {
this.text = text;
}
public TextMessageContent text(String text) {
this.text = text;
return this;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public QuoteInfo getQuoteInfo() {
return quoteInfo;
}
public void setQuoteInfo(QuoteInfo quoteInfo) {
this.quoteInfo = quoteInfo;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Text;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = encodeBase();
payload.setSearchableContent(text);
if(quoteInfo != null) {
payload.setBase64edData(Base64.getEncoder().encodeToString(quoteInfo.encode().toJSONString().getBytes(StandardCharsets.UTF_8)));
}
return payload;
}
@Override
public void decode(MessagePayload payload) {
text = payload.getSearchableContent();
if(!TextUtils.isEmpty(payload.getBase64edData())) {
String jsonStr = new String(Base64.getDecoder().decode(payload.getBase64edData()), StandardCharsets.UTF_8);
try {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(jsonStr);
quoteInfo = new QuoteInfo();
quoteInfo.decode(jsonObject);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class TipNotificationMessageContent extends MessageContent {
public String tip;
public TipNotificationMessageContent() {
}
public String getTip() {
return tip;
}
public void setTip(String tip) {
this.tip = tip;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setContent(tip);
return payload;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
tip = payload.getContent();
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Tip;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class TypingMessageContent extends MessageContent {
public static final int TYPING_TEXT = 0;
public static final int TYPING_VOICE = 1;
public static final int TYPING_CAMERA = 2;
public static final int TYPING_LOCATION = 3;
public static final int TYPING_FILE = 4;
private int typingType;
public TypingMessageContent() {
}
public TypingMessageContent(int typingType) {
this.typingType = typingType;
}
public int getTypingType() {
return typingType;
}
public void setTypingType(int typingType) {
this.typingType = typingType;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Typing;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Transparent;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setContent(typingType + "");
return payload;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
typingType = Integer.parseInt(payload.getContent());
}
}
@@ -0,0 +1,32 @@
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
public class UnknownMessageContent extends MessageContent {
private MessagePayload orignalPayload;
//必须有个空的构造函数
public UnknownMessageContent() {
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Text;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
return orignalPayload;
}
@Override
public void decode(MessagePayload payload) {
this.orignalPayload = payload;
}
}
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.messagecontent;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import java.util.Base64;
/**
* Created by heavyrain lee on 2017/12/6.
*
* @refactor dhl
* 添加小视频的宽高,时长
*/
public class VideoMessageContent extends MediaMessageContent {
private byte[] thumbnailBytes;
private long duration ;
//必须有个空的构造函数
public VideoMessageContent() {
}
public VideoMessageContent setThumbnailBytes(byte[] thumbnailBytes) {
this.thumbnailBytes = thumbnailBytes;
return this;
}
public byte[] getThumbnailBytes() {
return thumbnailBytes;
}
public void setDuration(long duration) {
this.duration = duration;
}
public long getDuration() {
return duration;
}
@Override
public int getContentType() {
return ProtoConstants.ContentType.Video;
}
@Override
public int getPersistFlag() {
return ProtoConstants.PersistFlag.Persist_And_Count;
}
@Override
public MessagePayload encode() {
MessagePayload payload = super.encodeBase();
payload.setSearchableContent("[视频]");
payload.setBase64edData(Base64.getEncoder().encodeToString(thumbnailBytes));
JSONObject objWrite = new JSONObject();
objWrite.put("d", duration);
objWrite.put("duration", duration);
payload.setContent(objWrite.toJSONString());
return payload;
}
@Override
public void decode(MessagePayload payload) {
super.decodeBase(payload);
thumbnailBytes = Base64.getDecoder().decode(payload.getBase64edData());
try {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(payload.getContent());
if(jsonObject.containsKey("d")) {
duration = (long) jsonObject.get("d");
} else {
duration = (long) jsonObject.get("duration");
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
@Override
protected int getMediaType() {
return ProtoConstants.MessageMediaType.VIDEO;
}
}
@@ -0,0 +1,15 @@
package cn.wildfirechat.sdk.model;
import cn.wildfirechat.pojos.Conversation;
import cn.wildfirechat.sdk.messagecontent.MessageContent;
import java.util.List;
public class Message {
public Conversation conversation;
public MessageContent content;
public long messageUid;
public String sender;
public long serverTime;
public List<String> toUsers;
}
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2020 WildFireChat. All rights reserved.
*/
package cn.wildfirechat.sdk.model;
import org.json.simple.JSONObject;
public class QuoteInfo {
private long messageUid;
private String userId;
private String userDisplayName;
private String messageDigest;
public long getMessageUid() {
return messageUid;
}
public void setMessageUid(long messageUid) {
this.messageUid = messageUid;
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserDisplayName() {
return userDisplayName;
}
public void setUserDisplayName(String userDisplayName) {
this.userDisplayName = userDisplayName;
}
public String getMessageDigest() {
return messageDigest;
}
public void setMessageDigest(String messageDigest) {
this.messageDigest = messageDigest;
}
public JSONObject encode() {
JSONObject object = new JSONObject();
object.put("u", messageUid);
object.put("i", userId);
object.put("n", userDisplayName);
object.put("d", messageDigest);
return object;
}
public void decode(JSONObject object) {
messageUid = (long) object.get("u");
userId = (String) object.get("i");
userDisplayName = (String) object.get("n");
messageDigest = (String) object.get("d");
}
}
@@ -1,5 +1,6 @@
package cn.wildfirechat.sdk.utilities;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.sdk.model.IMResult;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
@@ -157,7 +158,15 @@ public class AdminHttpUtils extends JsonUtils {
LOG.info("Request error: " + statusCode + " error msg:" + content);
throw new Exception("Http request error with code:" + statusCode);
} else {
return fromJsonObject(content, clazz);
IMResult<T> result = fromJsonObject(content, clazz);
if(result != null){
if(result.getErrorCode() == ErrorCode.ERROR_CODE_AUTH_FAILURE) {
LOG.error("鉴权失败,IM服务地址或者密钥配置错误,请检查IM服务{}的配置文件中的http.admin.secret_key是否是{}", adminUrl, adminSecret);
} else if(result.getErrorCode() == ErrorCode.ERROR_CODE_SIGN_EXPIRED) {
LOG.error("时间验证失败,请确保当前服务和IM服务{}的时间是一致的", adminUrl);
}
}
return result;
}
} catch (Exception e) {
e.printStackTrace();
@@ -0,0 +1,234 @@
/*
* 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.sdk.utilities;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassUtil {
/**
* 获取同一路径下所有子类或接口实现类
*
* @param cls
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List<Class<?>> getAllAssignedClass(Class<?> cls) throws IOException,
ClassNotFoundException {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (Class<?> c : getClasses(cls)) {
if (cls.isAssignableFrom(c) && !cls.equals(c)) {
classes.add(c);
}
}
return classes;
}
/**
* 取得当前类路径下的所有类
*
* @param cls
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public static List<Class<?>> getClasses(Class<?> cls) throws IOException,
ClassNotFoundException {
String pk = cls.getPackage().getName();
// String path = pk.replace('.', '/');
// ClassLoader classloader = Thread.currentThread().getContextClassLoader();
// URL url = classloader.getResource(path);
// String type = url.getProtocol();
//
// System.out.println("the type is " + type);
// System.out.println(url.getPath());
//
// return getClasses(new File(url.getFile()), pk);
List<String> classNames = getClassName(pk, true);
List<Class<?>> classes = new ArrayList<>();
for (String className :
classNames) {
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
className = className.substring(className.lastIndexOf("/")+1, className.length());
}
classes.add(Class.forName(className));
}
return classes;
}
/**
* 迭代查找类
*
* @param dir
* @param pk
* @return
* @throws ClassNotFoundException
*/
private static List<Class<?>> getClasses(File dir, String pk) throws ClassNotFoundException {
System.out.println(dir.getAbsolutePath());
List<Class<?>> classes = new ArrayList<Class<?>>();
if (!dir.exists()) {
return classes;
}
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
classes.addAll(getClasses(f, pk + "." + f.getName()));
}
String name = f.getName();
if (name.endsWith(".class")) {
classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6)));
}
}
return classes;
}
/**
* 获取某包下所有类
* @param packageName 包名
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
public static List<String> getClassName(String packageName, boolean childPackage) {
List<String> fileNames = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String packagePath = packageName.replace(".", "/");
URL url = loader.getResource(packagePath);
if (url != null) {
String type = url.getProtocol();
if (type.equals("file")) {
fileNames = getClassNameByFile(url.getPath(), null, childPackage);
} else if (type.equals("jar")) {
fileNames = getClassNameByJar(url.getPath(), childPackage);
}
} else {
fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);
}
return fileNames;
}
/**
* 从项目文件获取某包下所有类
* @param filePath 文件路径
* @param className 类名集合
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
File file = new File(filePath);
File[] childFiles = file.listFiles();
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
if (childPackage) {
myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
}
} else {
String childFilePath = childFile.getPath();
if (childFilePath.endsWith(".class")) {
if (childFilePath.indexOf("\\classes") >= 0 || childFilePath.indexOf("/classes") >= 0) {
int start = childFilePath.indexOf("\\classes");
if (start == -1) {
start = childFilePath.indexOf("/classes");
}
childFilePath = childFilePath.substring(start + 9, childFilePath.lastIndexOf("."));
}
childFilePath = childFilePath.replace("\\", ".");
childFilePath = childFilePath.replace("/", ".");
myClassName.add(childFilePath);
}
}
}
return myClassName;
}
/**
* 从jar获取某包下所有类
* @param jarPath jar文件路径
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByJar(String jarPath, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
String[] jarInfo = jarPath.split("!");
String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
String packagePath = jarInfo[1].substring(1);
try {
JarFile jarFile = new JarFile(jarFilePath);
Enumeration<JarEntry> entrys = jarFile.entries();
while (entrys.hasMoreElements()) {
JarEntry jarEntry = entrys.nextElement();
String entryName = jarEntry.getName();
if (entryName.endsWith(".class")) {
if (childPackage) {
if (entryName.startsWith(packagePath)) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
myClassName.add(entryName);
}
} else {
int index = entryName.lastIndexOf("/");
String myPackagePath;
if (index != -1) {
myPackagePath = entryName.substring(0, index);
} else {
myPackagePath = entryName;
}
if (myPackagePath.equals(packagePath)) {
entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
myClassName.add(entryName);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return myClassName;
}
/**
* 从所有jar中搜索该包,并获取该包下所有类
* @param urls URL集合
* @param packagePath 包路径
* @param childPackage 是否遍历子包
* @return 类的完整名称
*/
private static List<String> getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) {
List<String> myClassName = new ArrayList<String>();
if (urls != null) {
for (int i = 0; i < urls.length; i++) {
URL url = urls[i];
String urlPath = url.getPath();
// 不必搜索classes文件夹
if (urlPath.endsWith("classes/")) {
continue;
}
String jarPath = urlPath + "!/" + packagePath;
myClassName.addAll(getClassNameByJar(jarPath, childPackage));
}
}
return myClassName;
}
}