Compare commits

...
280 Commits
Author SHA1 Message Date
heavyrian2012 a5f64df2de update version and release note 2025-11-09 11:42:48 +08:00
heavyrian2012 c7f1ea57dc 解决某些平台SDK内置消息没有注册的问题 2025-11-06 18:31:12 +08:00
heavyrian2012 c9b2de076d 默认用户ID搜索用户功能开启 2025-10-31 07:31:57 +08:00
heavyrian2012 552fadaa87 记录客户端来源IP地址 2025-10-26 16:57:33 +08:00
heavyrian2012 765572db36 移除掉无用的编译plugin,解决java8编译错误问题 2025-10-13 09:57:32 +08:00
heavyrian2012 a5f194b385 server api测试代码默认为社区版 2025-10-13 09:56:56 +08:00
heavyrian2012 c74cd7e290 解决server sdk解析图片消息不带缩略图解析错误问题 2025-09-29 20:39:12 +08:00
heavyrian2012 70cf65f0c0 解决Server SDK发送引用消息客户端无法正确解析的问题 2025-09-20 09:41:29 +08:00
heavyrian2012 3bb23f92c4 update version and release note 2025-09-07 09:44:42 +08:00
heavyrian2012 804c12845e fix error 2025-09-06 23:33:35 +08:00
heavyrian2012 fe15a32499 添加开关是否开启允许@不在会话中的机器人 2025-09-06 23:29:09 +08:00
heavyrian2012 06d168935d 支持@不在群组中的机器人和单人会话支持@机器人,支持机器人回复。 2025-09-06 21:36:29 +08:00
heavyrian2012 c1984d27b3 同步专业版SDK 2025-09-05 20:53:32 +08:00
heavyrian2012 29ef46b7a0 解决机器人和频道sdk检查端口错误提示的问题 2025-09-05 20:53:12 +08:00
heavyrian2012 0bdff3fd89 同步专业版server sdk 2025-08-30 10:18:34 +08:00
heavyrian2012 cd03583dab 解决server sdk反复初始化时内存泄漏问题 2025-08-25 11:11:12 +08:00
heavyrian2012 3f8fd7e5e0 update version and release note 2025-08-17 14:46:20 +08:00
heavyrian2012 fdfe71f700 server api添加群备注和收藏群组接口 2025-08-17 14:45:38 +08:00
heavyrian2012 35ecf7ccc6 发送消息的server api添加参数,可以以用户权限发送消息 2025-08-17 10:51:39 +08:00
heavyrian2012 8f798742c3 同步专业版sdk 2025-08-17 10:51:08 +08:00
heavyrian2012 f4ea8b184c 如果server sdk没有初始化,打印出初始化的代码和提示 2025-08-12 12:19:52 +08:00
heavyrian2012 2d7f344b1a 可以系统设置里设置那么名字不能被使用 2025-08-05 17:11:00 +08:00
heavyrian2012 bee03cd2f8 添加配置敏感词检查用户昵称/群组名/群备注等信息 2025-08-05 15:32:20 +08:00
heavyrian2012 59b1c3180f 现在发送消息大小不能超过64KB 2025-08-03 18:04:12 +08:00
heavyrian2012 88d764fb6d update version and release note 2025-08-02 10:45:59 +08:00
heavyrian2012 08b3886238 解决机器人回调配置带上发送人信息功能无效问题 2025-08-02 10:38:09 +08:00
heavyrian2012 7e73c2880b 支持客户端上传本地角标,当需要推送时会把角标数字带上 2025-07-29 11:51:11 +08:00
heavyrian2012 1359ce5250 解决上传文件可能内存泄漏的问题 2025-07-18 21:17:13 +08:00
heavyrian2012 56eaf938a8 添加注释 2025-07-16 17:50:18 +08:00
heavyrian2012 6ef6ed2906 添加群组成员时支持部分成员失败 2025-07-05 16:47:15 +08:00
heavyrian2012 63f37e8984 更新sdk接口测试的说明 2025-07-04 14:03:06 +08:00
heavyrian2012 337302c551 update readme 2025-06-29 11:32:04 +08:00
heavyrian2012 721f27a58a 搜索用户可以指定搜索用户或者机器人 2025-06-22 14:59:34 +08:00
heavyrian2012 ced74280d8 version and release note 2025-06-19 19:19:21 +08:00
heavyrian2012 18617c67f4 api/version和gettoken都添加节点信息 2025-06-19 19:15:35 +08:00
heavyrian2012 883866ce39 配置文件添加注释 2025-06-17 16:02:40 +08:00
heavyrian2012 2e135d4794 群组成员表添加一条索引 2025-06-14 12:38:16 +08:00
heavyrian2012 1d6faa5796 server api支持修改群组类型 2025-05-29 14:18:29 +08:00
heavyrian2012 731fa00cc9 创建/更新频道接口添加频道菜单属性 2025-05-12 14:27:07 +08:00
heavyrian2012 4da9173c5f 修改撤回时间配置,可以配置为不允许撤回 2025-05-10 08:12:20 +08:00
heavyrian2012 94e430cc83 解决外置消息审核回调没有回调成功的问题 2025-05-05 16:52:19 +08:00
heavyrian2012 7e92d1e0c5 Server API创建频道时,需要同时返回频道ID和频道密钥 2025-04-25 23:10:43 +08:00
heavyrian2012 4fa79bfeac 频道和机器人API添加对端口的检查提示 2025-04-22 12:21:45 +08:00
heavyrian2012 98388a5129 update version and release note 2025-04-18 20:03:38 +08:00
heavyrian2012 1a89714df9 配置文件中群组标记删除默认开启 2025-04-18 19:59:37 +08:00
heavyrian2012 a855652c26 音视频通话挂断时推送数据包含开始通话的消息UID 2025-04-18 11:12:15 +08:00
heavyrian2012 21aadd9c0b 去掉空的echo 2025-04-14 10:42:26 +08:00
heavyrian2012 5522a8689d server api添加日志 2025-04-13 18:26:07 +08:00
heavyrian2012 bf52a00304 添加配置是否允许发送消息给被封禁用户 2025-04-12 14:00:55 +08:00
heavyrian2012 3349e2a4e5 添加server api发送朋友圈的api 2025-04-12 12:54:34 +08:00
heavyrian2012 70f3f96f7a server sdk添加撤回和删除消息 2025-04-12 09:57:10 +08:00
heavyrian2012 5e9d411f1b 内置存储支持在线播放 2025-04-09 16:40:37 +08:00
heavyrian2012 e74fd0c660 添加配置,转发消息时是否代码发送者信息和目标信息 2025-03-27 20:52:12 +08:00
heavyrian2012 d1232abe39 version and release note 2025-03-05 19:20:54 +08:00
heavyrian2012 1df192f730 解决分段拉取消息时可能丢消息的问题 2025-03-05 19:13:29 +08:00
heavyrian2012 f897e640fc SDK添加超时配置 2025-03-03 12:11:19 +08:00
heavyrian2012 ee6e4f96fb 配置允许撤回消息时间小于等于0时可以允许用户任意时间撤回消息 2025-02-21 19:28:57 +08:00
heavyrian2012 d14dc74566 sdk添加注册消息的接口,可以允许sdk注册自定义消息 2025-02-19 19:27:42 +08:00
heavyrian2012 ff75379779 优化server sdk 2025-02-18 09:53:34 +08:00
heavyrian2012 b95a6b7785 更新版本号和Release note 2025-02-13 19:00:39 +08:00
heavyrian2012 5acb3044ac Revert "serveri api获取用户信息时返回deleted字段"
This reverts commit ae084063f4.
2025-01-24 10:34:53 +08:00
heavyrian2012 ae084063f4 serveri api获取用户信息时返回deleted字段 2025-01-23 16:27:36 +08:00
heavyrian2012 cb3a82559d 机器人API添加定向消息参数 2025-01-17 20:17:18 +08:00
heavyrian2012 9191204bb0 nginx配置注释 2025-01-15 10:07:37 +08:00
heavyrian2012 6587a88ca9 修改注释 2025-01-14 11:07:39 +08:00
heavyrian2012 86c1a2e140 转移群主时检查目标用户是否在群组中 2025-01-03 21:33:46 +08:00
heavyrian2012 83e9679339 SDK的地址和密钥添加trim,避免空格换行等问题 2025-01-02 14:23:23 +08:00
heavyrian2012 dafb0cd4d9 修改nginx配置示例中上传文件的大小为200MB 2024-12-26 08:42:24 +08:00
heavyrian2012 cbcc8310c4 敏感词过滤类型默认改成2 2024-12-15 10:47:45 +08:00
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
heavyrian2012 5a213704c5 version and release note 2024-06-30 12:17:37 +08:00
heavyrian2012 5a52a7d553 同步专业版IM服务 2024-06-30 12:13:21 +08:00
heavyrian2012 d751a7080b 添加Mesh相关SDK 2024-06-21 16:45:10 +08:00
heavyrian2012 68ae7dbd58 SDK中关于频道的api放到ChannelAdmin方法中 2024-06-11 09:10:17 +08:00
heavyrian2012 10fce4baad 解决机器人API修改机器人用户信息客户端没有更新的问题 2024-06-11 08:47:42 +08:00
heavyrian2012 c69906e249 添加频道新的回调特性 2024-05-29 16:30:54 +08:00
heavyrian2012 1aed4b1d37 搜索频道时,解决某些频道不能搜索出来的问题 2024-05-29 16:08:37 +08:00
heavyrian2012 f2601fc65d 针对非x86_64平台关于epoll的说明 2024-05-07 12:32:37 +08:00
heavyrian2012 4895f03a68 version and release note 2024-05-05 15:36:06 +08:00
heavyrian2012 1d234a3575 获取被删除群组时总是同步 2024-04-28 17:11:36 +08:00
heavyrian2012 386d0b9c57 删除无用代码 2024-04-27 08:35:55 +08:00
heavyrian2012 3ab1c7553d 如果用户不在群中,不再进行群组信息更新 2024-04-25 10:45:21 +08:00
heavyrian2012 85a4c806c1 退群时添加是否保留消息的参加 2024-04-25 10:44:50 +08:00
heavyrian2012 9aac87b7a1 拉取消息时,处理重复消息 2024-04-23 14:17:40 +08:00
heavyrian2012 ce21441772 群主强制被退群时(销毁账号/server api强制退群),调整新群主选择,群管理优先,先加入群组优先。 2024-04-23 13:28:53 +08:00
heavyrian2012 0a3516cdf2 version and release note 2024-04-19 11:28:17 +08:00
heavyrian2012 135a9ed0a8 添加用户设置添加好友验证功能 2024-04-19 11:19:43 +08:00
heavyrian2012 73e3447b39 强制推送配置默认为关闭 2024-04-03 10:51:06 +08:00
heavyrian2012 394b9119fa 添加配置,可以在会话静音/全局静音/PC在线时静音时强制推送。
(cherry picked from commit 1f4cf54990831d4bcc4fcec2fa5479036a581e58)
2024-04-03 10:49:47 +08:00
heavyrian2012 ebb05351a8 群组标记删除
(cherry picked from commit cdf4a0c011b879819c0263b84bddd2da5cb8bc7d)
2024-04-03 10:49:46 +08:00
heavyrian2012 c4421fc9ed server api添加通过邮箱获取用户列表的接口 2024-03-25 19:00:42 +08:00
heavyrian2012 a906bf352d version and release note 2024-03-25 09:04:28 +08:00
heavyrian2012 37f1772705 在线状态回调添加其他端的状态 2024-03-23 12:32:45 +08:00
heavyrian2012 0a05503c78 创建频道和机器人时,不再设置默认头像 2024-03-23 11:13:37 +08:00
heavyrian2012 a702602758 去掉用户信息中的密码处理 2024-03-23 11:13:09 +08:00
heavyrian2012 c01e5b634d version and release note 2024-03-07 16:33:50 +08:00
heavyrian2012 6c2848cbf7 升级依赖 2024-02-27 20:45:14 +08:00
heavyrian2012 ae3c8e0d25 升级部分依赖版本 2024-02-27 11:51:48 +08:00
heavyrian2012 2ef0059395 remove useless test code 2024-02-27 11:28:28 +08:00
heavyrian2012 d91a2d9fd5 升级部分依赖版本 2024-02-27 11:27:56 +08:00
heavyrian2012 a4bcd5e29e 同步专业版IM服务 2024-02-22 14:28:49 +08:00
heavyrian2012 fe4612c801 解决server端音视频SDK信令透传问题 2024-02-22 14:26:04 +08:00
heavyrian2012 58ade23b23 添加鸿蒙平台的支持 2024-02-04 17:02:20 +08:00
heavyrian2012 f1e46fca75 同步专业版IM服务 2024-02-02 13:44:47 +08:00
heavyrian2012 1e17fa1db4 同步专业版IM SDK 2024-02-02 12:50:23 +08:00
heavyrian2012 7094114ea3 同步专业版IM服务SDK 2024-01-27 14:13:14 +08:00
heavyrian2012 c805bddca1 version and release note 2024-01-14 10:47:49 +08:00
heavyrian2012 cb5754fe07 群组信息变更回调中加上群组信息 2024-01-14 09:51:46 +08:00
heavyrian2012 c014b78d95 1,添加配置,可以禁止拉陌生人进群
2,拉人进群时检查是否被拉黑
2024-01-14 09:42:42 +08:00
heavyrian2012 8a13a32615 sdk支持流式文本 2023-12-30 19:09:30 +08:00
heavyrian2012 bc409042b0 解决搜索时特殊字符的转义问题 2023-12-27 20:07:48 +08:00
heavyrian2012 49ad4f5eb6 机器人sdk添加撤回和更新接口 2023-12-26 09:20:34 +08:00
heavyrian2012 8c93e2ee42 update readme 2023-12-11 10:55:30 +08:00
heavyrian2012 54286b4a55 release note and version 2023-12-03 11:43:41 +08:00
heavyrian2012 594103e06b 解决广播没有发送消息给未登录用户的问题 2023-12-02 23:03:00 +08:00
heavyrian2012 90e89978aa 消息表的消息id索引改成唯一索引,只对新库有效,已有数据库不影响。 2023-12-02 17:01:03 +08:00
heavyrian2012 93b6e70804 优化用户id搜索逻辑 2023-11-30 19:44:06 +08:00
heavyrian2012 b774e0d423 添加获取在线用户信息和获取用户session信息的接口 2023-11-28 11:04:12 +08:00
heavyrian2012 08b813cd9f version 1.2.0 and release note 2023-11-18 20:28:35 +08:00
heavyrian2012 24c6538084 添加配置,处理多端同时加入聊天室的问题 2023-11-17 09:06:49 +08:00
heavyrian2012 724be0b273 添加获取用户聊天室状态的API 2023-11-17 09:06:00 +08:00
heavyrian2012 63f0fc66ce 支持按照用户ID来搜索用户 2023-11-15 15:38:22 +08:00
heavyrian2012 327b8d43a8 解决某些特殊情况下获取网卡信息失败导致启动失败的问题 2023-11-15 10:21:57 +08:00
imndx df67b8e0c8 禁止私聊时,只要发送者或接受者一方在允许私聊的列表里面,就允许私聊 fix #663 2023-11-10 11:00:45 +08:00
heavyrian2012 b4fcef1ced 会议事件添加时间戳 2023-11-05 10:51:46 +08:00
heavyrian2012 c43dfe26f4 更新SDK的依赖说明 2023-10-21 10:22:24 +08:00
heavyrian2012 aa1161131d 更新readme 2023-10-14 09:11:25 +08:00
heavyrian2012 a1188f5d52 添加配置开关,可以关掉api/version检查接口 2023-10-12 15:44:00 +08:00
heavyrian2012 6a10412940 version 1.1.9 and release note 2023-10-12 10:17:12 +08:00
heavyrian2012 8e3100e5b7 添加配置,是否允许群主和群管理员不受限制撤回自己的发言 2023-10-10 21:53:03 +08:00
heavyrian2012 c17a5be171 支持获取用户群组指定群成员类型 2023-10-10 12:51:40 +08:00
heavyrian2012 7727fad4dc 避免无限制重复好友请求,好友请求在5分钟之内只允许重复请求一次 2023-09-29 11:26:16 +08:00
heavyrian2012 dd5b9b1847 解决好友请求被拒绝后可能还能再次请求的问题 2023-09-29 10:45:41 +08:00
heavyrian2012 92c3c98553 解决重复发送好友请求时,请求时间没有更新的问题 2023-09-29 10:32:52 +08:00
heavyrian2012 d13ed6f989 sdk打印出请求的完整路径,方便问题排查 2023-09-26 09:58:12 +08:00
heavyrian2012 4e6188ee32 添加推送过期配置 2023-09-13 11:16:54 +08:00
heavyrian2012 a183d30f89 解决服务器时间过大的问题 https://github.com/wildfirechat/android-chat/issues/795 2023-09-11 11:12:50 +08:00
heavyrian2012 3f4c2b0cea version 1.1.8 & release note 2023-09-07 22:09:08 +08:00
heavyrian2012 932ee9235f 解决撤回消息中原消息中二进制消息数据格式错误问题 2023-09-07 22:01:37 +08:00
heavyrian2012 1e5ac9b787 修改配置文件注释 2023-09-07 09:56:41 +08:00
heavyrian2012 ad5d4991c8 发送好友请求之后5分钟之内可以再次发送 2023-09-06 19:47:24 +08:00
heavyrian2012 a2ee6c87eb 不能把群主设置为管理员 2023-09-06 15:20:38 +08:00
heavyrian2012 30e01a7040 修改docker说明 2023-08-26 10:40:07 +08:00
heavyrian2012 082f42af77 1.1.7 version and release note 2023-08-07 08:54:14 +08:00
heavyrian2012 2bec610552 优化撤回消息 2023-08-05 10:58:22 +08:00
heavyrian2012 f3a2603309 推送信息中,目标名称考虑到群备注,另外添加发送者和目标的头像 2023-08-04 11:20:09 +08:00
heavyrian2012 7d10251bd9 优化用户搜索 2023-08-04 09:47:07 +08:00
heavyrian2012 5c7b320b04 同步专业版IM服务代码 2023-08-01 14:24:19 +08:00
heavyrian2012 509048da04 1.1.6 version and release node 2023-07-20 21:22:57 +08:00
heavyrian2012 15f7777d78 添加获取订阅频道的功能 2023-07-20 21:07:58 +08:00
heavyrian2012 45b1b9cf96 解决搜索用户时返回用户类型问题 2023-07-08 13:34:05 +08:00
heavyrian2012 57314e673a 用户设置查找自己的方法 2023-06-28 16:14:15 +08:00
heavyrian2012 0a7b8f9587 version and release note 2023-06-20 17:59:11 +08:00
heavyrian2012 c8ff39c2ab 添加获取所有用户的Server API 2023-06-20 15:14:57 +08:00
heavyrian2012 252204a760 添加好友时,只检查对方的黑名单状态 2023-05-27 10:15:07 +08:00
heavyrian2012 26fbde676e stop脚本等待时间延长到30秒 2023-05-17 17:58:03 +08:00
heavyrian2012 81585ec146 docker file 为启动脚本添加执行属性 2023-05-17 17:57:47 +08:00
heavyrian2012 4ad0437730 更新版本号和release note 2023-05-10 20:19:45 +08:00
heavyrian2012 6245fe37fc 添加配置,请求机器人好友时,是否自动接受 2023-05-04 21:39:45 +08:00
heavyrian2012 ca50872913 server api添加获取单个群成员信息的接口 2023-05-04 19:00:08 +08:00
heavyrian2012 3c11c368a3 update readme 2023-04-28 11:32:47 +08:00
heavyrian2012 145709fd76 支持AES256配置 2023-04-24 11:51:13 +08:00
heavyrian2012 758c5de2c7 解决pc多端登录互踢时pc在线状态没有更新的问题 2023-04-24 11:26:55 +08:00
heavyrian2012 c159ff90e3 去掉windows脚本中的中文,解决windows平台下启动失败的问题 2023-04-19 09:28:30 +08:00
heavyrian2012 083b07fdba 修改版本号和release note 2023-04-13 09:36:49 +08:00
heavyrian2012 c6a8462ece 修改启动注释 2023-04-13 09:30:42 +08:00
heavyrian2012 afee0466e7 用户状态和频道订阅状态修改改成同步模式,防止高并发时出现问题 2023-04-13 09:18:43 +08:00
heavyrian2012 62b36cd2b4 超级群组协议 2023-04-13 09:04:55 +08:00
heavyrian2012 99c5210935 同步专业版IM服务超级群组的数据库和SDK 2023-04-13 08:50:07 +08:00
heavyrian2012 75cb7f8b98 修正当server api设置好友关系时,回调状态错误问题 #604 2023-04-07 22:25:01 +08:00
heavyrian2012 c89259e892 修改群昵称时检查是否在群中 2023-04-04 13:57:22 +08:00
heavyrian2012 d40835aa18 解决grouptype为0时被删除用户还能修改群名称的问题 2023-04-03 16:54:36 +08:00
heavyrian2012 b93c76c6c7 优化群组撤回逻辑。 2023-04-03 13:33:07 +08:00
heavyrian2012 c29370ac61 添加撤回消息回调 2023-03-24 15:55:11 +08:00
heavyrian2012 68abeed907 添加一些异常数据的处理 2023-03-20 13:49:29 +08:00
imndx 11b7851b9f 启动脚本里面,打印 JAVA 版本信息 2023-03-20 12:25:07 +08:00
heavyrian2012 329791e70e 解决用户被block后,session失效问题 2023-03-14 14:54:22 +08:00
heavyrian2012 b7b262be00 服务检查时间是否一致 2023-03-12 18:55:14 +08:00
heavyrian2012 6a806aa10b fix build error 2023-03-10 11:02:52 +08:00
heavyrian2012 6542b8ba55 version and release note 2023-03-10 10:55:32 +08:00
heavyrian2012 c3bc62f1e1 单聊会话发送定向消息时,分发目标需要包括自己。 2023-03-10 10:48:39 +08:00
heavyrian2012 8b649200d2 添加检查会议是否存在的接口 2023-03-06 15:17:46 +08:00
heavyrian2012 5f95b18e77 release note 2023-03-06 12:25:11 +08:00
imndx 4d45aedc73 禁止客户端群操作flag 解析错误 2023-03-06 11:41:23 +08:00
heavyrian2012 a038bedb12 version and release note 2023-03-06 10:54:26 +08:00
heavyrian2012 43892dbe41 解决关闭roaming,且message.compensate_time_limit配置为-1时,首次消息无法同步的问题 2023-03-02 08:52:43 +08:00
heavyrian2012 ad33abc424 用户消息表中的target优化 2023-02-28 21:01:20 +08:00
heavyrian2012 290a3cea0e 支持单聊会话的定向消息 2023-02-28 20:45:41 +08:00
imndx ca3bbb6435 添加日志 2023-02-23 10:41:11 +08:00
heavyrian2012 704e6e0ec2 搜索用户时,过滤掉被删除的用户 2023-02-20 19:30:08 +08:00
heavyrian2012 23871487e4 当用户获取群组信息时,如果不在群中返回-1,如果已经离开群了返回离开时间的负数 2023-02-20 19:27:30 +08:00
heavyrian2012 6e1ba9440e 更新版本号和release note 2023-02-15 15:25:33 +08:00
heavyrian2012 c284272b06 文件存储路径中加上分钟和秒 2023-02-15 15:20:57 +08:00
heavyrian2012 6746a54659 优化内置存储功能 2023-02-14 21:35:58 +08:00
heavyrian2012 93be667e17 日志文件精确到分钟,避免出现问题时被复写 2023-02-14 08:45:34 +08:00
heavyrian2012 f11da6225d 添加修改频道菜单接口 2023-02-12 11:06:35 +08:00
heavyrian2012 e28a4c6cbf server api发送消息检查对象是否存在 2023-02-11 13:51:05 +08:00
heavyrian2012 fa8a2bef32 channel api添加检查是否是订阅者的接口 2023-02-08 11:13:32 +08:00
heavyrian2012 1c9d238734 update readme 2023-01-19 16:14:17 +08:00
heavyrian2012 2f7f4e6456 update readme 2023-01-10 11:57:35 +08:00
heavyrian2012 6e83ec85c7 server sdk http request add log 2023-01-04 09:20:12 +08:00
heavyrian2012 cc561b846e 更新版本号和release note 2022-12-28 08:49:40 +08:00
heavyrian2012 4628b3b0b6 sdk add conference rtp forward api 2022-12-22 11:53:38 +08:00
heavyrian2012 9c69fbc7d4 sdk add conference rtp forward api 2022-12-13 17:38:21 +08:00
heavyrian2012 9047020d69 添加停止命令 2022-12-12 12:37:31 +08:00
heavyrian2012 1c18d862a1 会议离开加上原因 2022-12-07 16:25:18 +08:00
heavyrian2012 b0ddca985a route时可能不能携带正确的推送类型 2022-12-06 14:35:16 +08:00
heavyrian2012 996e06c451 添加禁止客户端群操作配置 2022-11-26 22:21:43 +08:00
imndx 612747203a 创建群组时,支持组织群 2022-11-25 10:48:29 +08:00
heavyrian2012 f7853ae299 解决短连接端口被扫描时打印出异常日志的问题 2022-11-24 19:06:27 +08:00
heavyrian2012 7443db3078 检查工具添加help命令 2022-11-22 10:12:09 +08:00
heavyrian2012 ad5f6cb228 添加注释 2022-11-21 08:34:08 +08:00
heavyrian2012 1486b19ce1 更新版本和release note 2022-11-19 09:44:09 +08:00
heavyrian2012 3e1095f745 移动release note文件的位置 2022-11-19 09:38:26 +08:00
heavyrian2012 17039bd3c1 http请求错误时不打印数据内容,防止内容泄露 2022-11-19 09:31:22 +08:00
heavyrian2012 b5d8feab62 客户端连接时检查是否跟获取token的平台一致 2022-11-18 22:10:53 +08:00
heavyrian2012 95b08ff7a3 Revert "mqtt proto version error"
This reverts commit ab8ae9d0c9.
2022-11-18 21:55:49 +08:00
heavyrian2012 ab8ae9d0c9 mqtt proto version error 2022-11-18 21:27:18 +08:00
heavyrian2012 b4d163e144 添加android客户端签名验证功能 2022-11-18 16:01:17 +08:00
heavyrian2012 3ccfd9a29b 解决user setting并发问题,fix #566 2022-11-15 17:12:51 +08:00
heavyrian2012 90504d1305 打包dockerfile 2022-11-14 15:47:42 +08:00
heavyrian2012 88199189a2 当禁止陌生人聊天时,添加例外允许用户id,用于比如管理员、文件助手等场景 2022-11-14 15:23:03 +08:00
heavyrian2012 0b6612eacc 内置对象存储支持绝对路径和相对路径 2022-11-14 15:13:06 +08:00
heavyrian2012 d706f545d2 解决启动脚本中的小错误 2022-11-14 15:04:51 +08:00
heavyrian2012 ae8d63b162 update docker readme 2022-11-14 14:42:04 +08:00
heavyrian2012 77e742e812 添加server sdk检查server api的工具 2022-11-12 19:25:24 +08:00
heavyrian2012 b2ee8b8d94 linux安装包创建日志目录 2022-11-12 19:24:57 +08:00
heavyrian2012 7080d85605 只有server api才能创建组织群 2022-11-12 15:01:57 +08:00
heavyrian2012 1275b2109f fix build error 2022-11-12 14:18:26 +08:00
heavyrian2012 b2eab28cb9 组织群客户端不能自主退出 2022-11-12 14:17:15 +08:00
heavyrian2012 67234ceaa6 添加新的群组类型:组织群 2022-11-12 14:00:24 +08:00
heavyrian2012 45f9e20e97 支持转发消息include/exclude类型,支持配置消息区间 2022-11-12 12:51:25 +08:00
heavyrian2012 6b7edc713a API获取用户信息时,过滤掉被删除的用户 2022-11-09 10:36:52 +08:00
heavyrian2012 1ef8f94d67 忽略掉无效的请求和日志 2022-11-05 17:05:31 +08:00
heavyrian2012 9b0b644cd1 update version and release note 2022-11-01 08:47:34 +08:00
heavyrian2012 b233c49d73 解决字符编码错误问题 2022-11-01 08:40:07 +08:00
heavyrian2012 b8d8d2f8ed 群组成员表添加成员索引和获取2个人共同群组的接口 2022-10-28 21:56:20 +08:00
heavyrian2012 731020ba0f update readme 2022-10-27 08:40:47 +08:00
heavyrian2012 4c00955639 更新readme 2022-10-27 08:33:09 +08:00
heavyrian2012 cbc150905f update screenshots 2022-10-26 10:32:48 +08:00
heavyrian2012 a71b8cef73 add more screenshots 2022-10-26 10:27:03 +08:00
heavyrian2012 66c67fa8d1 pc 截图大小 2022-10-26 09:29:36 +08:00
heavyrian2012 3ca7c70faa add screenshots 2022-10-26 09:26:43 +08:00
299 changed files with 14860 additions and 1596 deletions
+150 -39
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可能是最适应中国网络国情的即时通讯服务。
* 支持加速点加速,即可用于全球应用,也可用于政企内外双网复杂的网络环境。
* 支持阅读回执和在线状态功能,适用于办公环境。
@@ -20,15 +20,17 @@
* 结构设计合理,客户端分功能库、UI库和应用三层。应用成熟完善,开箱即用,也可把SDK嵌入其它应用。
* 拥有应用开放平台,可以开发和创建自建应用,扩展您的工作台。
* API丰富,方便与其它服务系统的对接。
* 拥有机器人和公众号功能。
* 拥有机器人和公众号功能,免费的频道(公众号)管理后台。
* 超级群组功能,可以支持万人大群。
* 支持多个IM服务组网互通,打破孤岛,适用于集团公司下多个分公司互联互通。
* 社区版IM服务和移动端免费商用,可以免费构建出完备的移动互联网应用。
* 收费项目非常便宜,野火独有的试用模式,可以先部署到客户环境试用6个月,试用满意后再购买,避免上当受骗。
* 技术支持专业和高效,核心研发人员及时处理issue和论坛回复。
* 技术支持专业高效,***核心研发***及时处理issue和工单,能最快解决用户的技术难题。技术支持不收费,让客户顺利使用是我们最大的愿望。
## 野火开源项目
主要包括以下项目:
| [GitHub仓库地址](https://github.com/wildfirechat) | [码云仓库地址](https://gitee.com/wfchat) | 说明
| [GitHub仓库](https://github.com/wildfirechat) | [码云仓库](https://gitee.com/wfchat) | 说明
| ------------------------------------------------------------ | ----------------------------------------------------- | --------------------------------------------------------------------------
| [im-server](https://github.com/wildfirechat/im-server) | [im-server](https://gitee.com/wfchat/im-server) | 野火社区版IM服务,野火IM的核心服务,处理所有IM相关业务。 |
| [app-server](https://github.com/wildfirechat/app-server) | [app-server](https://gitee.com/wfchat/app-server) | Demo应用服务,模拟客户的应用服登陆处理逻辑及部分二次开发示例。 |
@@ -39,19 +41,24 @@
| [open-platform](https://github.com/wildfirechat/open-platform) | [open-platform](https://gitee.com/wfchat/open-platform) | 野火开放平台服务。 |
| [daily-report](https://github.com/wildfirechat/daily-report) | [daily-report](https://github.com/wildfirechat/daily-report) | 野火开放平台日报 demo服务。 |
| [channel-platform](https://github.com/wildfirechat/channel-platform) | [channel-platform](https://gitee.com/wfchat/channel-platform) | 野火频道(公众号)平台服务。 |
| [organization-platform](https://github.com/wildfirechat/organization-platform) | [organization-platform](https://gitee.com/wfchat/organization-platform) | 野火组织通讯录服务。 |
| [android-chat](https://github.com/wildfirechat/android-chat) | [android-chat](https://gitee.com/wfchat/android-chat) | 野火IM Android SDK源码和App源码。 |
| [ios-chat](https://github.com/wildfirechat/ios-chat) | [ios-chat](https://gitee.com/wfchat/ios-chat) | 野火IM iOS SDK源码和App源码。|
| [pc-chat](https://github.com/wildfirechat/vue-pc-chat) | [pc-chat](https://gitee.com/wfchat/vue-pc-chat) | 基于[Electron](https://electronjs.org/)的PC 端,支持Windows、Mac、Linux(包括国产化linux系统和CPU)。 |
| [pc-chat(electron)](https://github.com/wildfirechat/vue-pc-chat) | [pc-chat(electron)](https://gitee.com/wfchat/vue-pc-chat) | 基于[Electron](https://electronjs.org/)开发的PC 端 | |
| [pc-chat(Qt)](https://github.com/wildfirechat/qt-pc-chat) | [pc-chat(Qt)](https://gitee.com/wfchat/qt-pc-chat) | 基于Qt开发的PC 端 | |
| [web-chat](https://github.com/wildfirechat/vue-chat) | [web-chat](https://gitee.com/wfchat/vue-chat) | 野火IM Web 端, [体验地址](https://web.wildfirechat.cn)。 |
| [wx-chat](https://github.com/wildfirechat/wx-chat) | [wx-chat](https://gitee.com/wfchat/wx-chat) | 小程序平台的Demo(支持微信、百度、阿里、字节、QQ 等小程序平台)。 |
| [uni-chat](https://github.com/wildfirechat/uni-chat) | [uni-chat](https://gitee.com/wfchat/uni-chat) | UniApp平台移动端应用(使用原生插件)。 |
| [uni-wfc-client](https://github.com/wildfirechat/uni-wfc-client) | [uni-wfc-client](https://gitee.com/wfchat/uni-wfc-client) | UniApp平台原生插件。 |
| [flutter_imclient](https://github.com/wildfirechat/flutter_imclient) | [flutter_imclient](https://gitee.com/wfchat/flutter_imclient) | Flutter平台原生插件。 |
| [uni-wfc-client](https://github.com/wildfirechat/uni-wfc-client) | [uni-wfc-client](https://gitee.com/wfchat/uni-wfc-client) | UniApp平台原生插件。已[商店](https://ext.dcloud.net.cn/plugin?id=7895)上架 |
| [wfc_flutter_plugins](https://github.com/wildfirechat/wfc_flutter_plugins) | [flutter_imclient](https://gitee.com/wfchat/wfc_flutter_plugins) | Flutter平台原生插件,支持IM和音视频通话。 |
| [docs](https://github.com/wildfirechat/docs) | [docs](https://gitee.com/wfchat/docs) | 野火IM相关文档,包含设计、概念、开发、使用说明,[在线查看](https://docs.wildfirechat.cn/)。 |
## 野火开发文档
[在线文档](https://docs.wildfirechat.cn/)
## 野火IM论坛
[野火IM论坛](https://bbs.wildfirechat.cn)
## 野火性能测试报告及测试方法
[Github](https://github.com/wildfirechat/Performance_Test), [码云](https://gitee.com/wfchat/Performance_Test)。
@@ -68,39 +75,29 @@ PC客户端点[这里](https://github.com/wildfirechat/vue-pc-chat/releases)下
![野火IM](http://static.wildfirechat.net/wx.jpg)
## 快速使用
## 快速开始
可以按照[快速开始](https://docs.wildfirechat.cn/quick_start/)来部署应用服务和IM服务,然后打包移动端,就可以完成聊天和通话功能。
之后再部署开放平台服务、频道(公众号)服务、机器人服务、推送服务、Turn服务(音视频服务)来实现完整的应用功能,详情可以详细阅读文档。
## 应用截图
![ios-demo](http://static.wildfirechat.cn/ios-demo.gif)
## 联系我们
商务合作请微信联系:
<img src="http://static.wildfirechat.cn/ios-message-view.png" width = 50% height = 50% />
* 微信1:wildfirechat
* 微信2:wfchat
<img src="http://static.wildfirechat.cn/ios-contact-view.png" width = 50% height = 50% />
## 问题交流
<img src="http://static.wildfirechat.cn/ios-discover-view.png" width = 50% height = 50% />
1. 如果大家发现bug,请在GitHub或码云提issue;如果有需求也请给我们提issue。
2. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
3. 关注我们的公众号。我们有新版本发布或者有重大更新会通过公众号通知大家,另外我们也会不定期的发布一些关于野火IM的技术介绍。
<img src="http://static.wildfirechat.cn/ios-settings-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-messagelist-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-chat-setting-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-takephoto-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-record-voice-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-location-view.png" width = 50% height = 50% />
<img src="http://static.wildfirechat.cn/ios-voip-view.png" width = 50% height = 50% />
我们有核心研发工程师轮流值班处理issue和论坛,会及时处理的,疑难Bug的修改和新需求的开发我们也会尽快解决。
## 编译
由于```distribution/pom.xml```中使用了生成RPM包的plugin,所以需要本地安装有rpm。如果不需要RPM包,可以删除掉```distribution/pom.xml```文件中的RPM plugin。
在安装RPM后或者删除```distribution/pom.xml```文件中的RPM plugin后,执行下面命令:
```
mvn clean package
```
@@ -119,6 +116,24 @@ mvn clean package
1. 在浏览器中输入地址 ```http://${ip}/api/version```可以看到返回一个json文件。
2. 部署[应用服务](https://github.com/wildfirechat/app-server),配置和编译[Android客户端](https://github.com/wildfirechat/android-chat)和[iOS客户端](https://github.com/wildfirechat/ios-chat)进行验证。详情可参考[快速开始](https://docs.wildfirechat.cn/quick_start/)。
## 打包RPM格式
打包会生成Java包和deb安装包,如果需要rpm安装包,请在```distribution/pom.xml```中取消注释生成rpm包的plugin。另外还需要本地安装有rpm,在linux或者mac系统中很容易安装,在windows系统需要安装cygwin并安装rpm,具体信息请百度查询。
修改之后运行编译命令```mvn clean package```,rpm包生成在```distribution/target```目录下。
## Epoll
在linux系统中打开Epoll开关可以提高性能,默认软件包内打包的是x86_64的epoll native sdk。如果是arm64的机器,可以把[broker pom](./broker/pom.xml)文件中修改如下:
```xml
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<!-- <classifier>linux-x86_64</classifier>-->
<classifier>linux-aarch_64</classifier>
</dependency>
```
然后重新打包。专业版IM服务软件包中有arm64架构的sdk,可以直接替换。epoll只支持x86_64和arm64两个架构,其他架构不要把epoll开关打开。
## 升级说明
1. 从0.42 版本增加了群成员数限制,默认为2000。如果您从之前的版本升级到这个版本或以后,需要注意到群成员数的限制。升级之后超出限制的群不受影响,但不能继续加人,如果您想修改默认值,可以在升级版本之后,修改t_setting表,把默认的大小改为您期望的人数。另外修改t_group表,把已经存在的群组max_member_count改成您期望的,然后重启。
2. 0.50版本添加了是否允许客户端发送群操作通知的配置。如果您在客户端自定义群通知,需要在服务器端配置允许,没有使用自定义群操作通知的不受影响。***
@@ -131,23 +146,119 @@ mvn clean package
9. 从0.79 版本起把log4j升级到log4j2,因为log4j已经不再维护而且还有已知的漏洞,建议所有客户都升级,升级时注意更新log4j2的配置文件
10. 0.97版本更改了启动脚本```wildfirechat.sh```,如果是升级服务,请注意更新启动脚本。
## 维护说明
野火设计理念当中,IM服务和客户端协议栈构成个管道工具,数据在人和人或者人和服务或者服务和服务之间传递,可以通过Server API和自定义消息来对接和开发业务。业务系统把IM系统当作一个工具来使用,是不需要把任何业务逻辑写在IM服务中的。
## 联系我们
如果修改了IM服务可能会引起跟客户端协议栈的不兼容,以后也没有可能从社区版IM服务迁移到专业版IM服务,还有一旦修改就跟我们代码不一样了,如果出了问题我们就无法分析和解决。所以 ***我们强烈不推荐在IM服务修改代码,如果修改过就不再提供任何技术支持和服务了***。除了IM服务以外的所有代码可以任意修改。
商务合作请微信联系:
现有接口足够对接和二开任何业务了,如果发现无法实现你们的业务需求,可以在[论坛](https://bbs.wildfirechat.cn)来咨询,我们会分析如何实现,如果需要添加新接口,我们会及时免费添加。请切记:***不要修改IM服务的代码!*** ***不要修改IM服务的代码!*** ***不要修改IM服务的代码!***
* 微信1:wildfirechat
* 微信2:wfchat
详细维护说明请参考 [野火维护说明](https://docs.wildfirechat.cn/base_knowledge/maintain.html)。
## 问题交流
## 服务对象
因为野火的使用范围比较广,难免被一些不法分子利用,国内反诈反赌力度很大,所以国内多地警方都跟我们都有过联系。***为了预防我们的产品被用于非法目的,试用及购买只能支持境内法人单位,不接受个人或者境外团体。如果我们发现产品被用于非法目的,我们会立即停止技术支持服务并且报警和配合警方调查。请用于非法目的用户绕行!***
1. 如果大家发现bug,请在GitHub或码云提issue;如果有需求也请给我们提issue。
2. 其他问题,请到[野火IM论坛](http://bbs.wildfirechat.cn/)进行交流学习
3. 关注我们的公众号。我们有新版本发布或者有重大更新会通过公众号通知大家,另外我们也会不定期的发布一些关于野火IM的技术介绍。
## 常见问题
请参考文档[常见问题](https://docs.wildfirechat.cn/faq/server.html)
<img src="http://static.wildfirechat.cn/wx_wfc_qrcode.jpg" width = 50% height = 50% />
## 应用截图
登录界面
我们有核心研发工程师轮流值班处理issue和论坛,会及时处理的,疑难Bug的修改和新需求的开发我们也会尽快解决。
<img src="./screenshots/1_login.png" width = 50% height = 50% />
会话列表
<img src="./screenshots/2_conversation_list.png" width = 50% height = 50% />
好友列表
<img src="./screenshots/3_contacts_list.png" width = 50% height = 50% />
工作台
<img src="./screenshots/4_workplatform.png" width = 50% height = 50% />
发现
<img src="./screenshots/5_discover.png" width = 50% height = 50% />
我的页面
<img src="./screenshots/6_settings.png" width = 50% height = 50% />
消息聊天
<img src="./screenshots/7_conversation.png" width = 50% height = 50% />
动态表情
<img src="./screenshots/8_conversation_sticker.png" width = 50% height = 50% />
语音消息
<img src="./screenshots/9_conversation_voice_msg.png" width = 50% height = 50% />
位置消息
<img src="./screenshots/10_conversation_location.png" width = 50% height = 50% />
拍摄图片或者小视频
<img src="./screenshots/11_conversation_capture.jpeg" width = 50% height = 50% />
会话设置
<img src="./screenshots/13_conversation_settings.png" width = 50% height = 50% />
朋友圈
<img src="./screenshots/14_moments.png" width = 50% height = 50% />
新好友
<img src="./screenshots/15_new_friends.png" width = 50% height = 50% />
多人视频通话
<img src="./screenshots/16_videocall.png" width = 50% height = 50% />
会议主页
<img src="./screenshots/17_conference_home.png" width = 50% height = 50% />
会议详情
<img src="./screenshots/18_conference_info.png" width = 50% height = 50% />
会议界面
<img src="./screenshots/19_conference.png" width = 50% height = 50% />
PC客户端界面
<img src="./screenshots/20_pc.png"/>
移动客户端、PC客户端和Web客户端三端同框
<img src="./screenshots/21_three_platform.png" />
小程序客户端
<img src="./screenshots/22_micro_app.jpeg" width = 50% height = 50% />
公众号管理后台
<img src="./screenshots/23_channel.png"/>
开放平台管理后台
<img src="./screenshots/24_open_platform.png" />
IM服务管理后台
<img src="./screenshots/25_im_admin.png" />
> 更多详细信息请下载demo进行体验。
## 特别感谢
1. [moquette](https://github.com/moquette-io/moquette) 本项目是基于此项目二次开发而来,处理MQTT相关业务。
+144 -20
View File
@@ -12,7 +12,7 @@
#服务器的接入IP。给客户端提供是${server.ip}和${http_port}。
#客户端会从${http_port}端口获取到长链接端口。这个地址一定要改成客户端可以访问到的IP地址
#(如果您部署云服务器上或者具有独立公网出口的服务器上,请改为对应的公网IP;如果您部署在在内网环境下,在内网使用,这个地方改成内网地址)
server.ip 192.168.3.124
server.ip 192.168.1.81
##原生客户端长链接端口
port 1883
@@ -59,6 +59,19 @@ token.key testim
##token的过期时间,单位为毫秒,默认为无限期。如果需要设置无限期,客户端上一定需要加上token过期的处理。token过期的处理请参考文档的常见问题
##token.expire_time 2592000000
##客户端(目前只有android)的应用签名(一个应用最取第一个签名),如果有多个签名(多个客户端),用英文逗号分开。获取签名方法请参考文档:https://docs.wildfirechat.cn/blogs/签名验证.html
#connect.client_signature_list eNkezQ1g9OsnhfLSFUY1vzKDzhs=,xykezQ1g9OsnhfLSFUY1vzKDzhs=
##是否拒绝空签名的android客户端, 默认为true。当有旧的客户端需要兼容时改成false,当所有都是新客户端时,改成true。
#connect.reject_empty_signature true
##是否使用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 配置的条数的历史消息
@@ -79,9 +92,18 @@ message.chatroom_remote_history_message 1
##是否禁止陌生人聊天
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
@@ -89,7 +111,7 @@ message.disable_remote_search false
##如果要避免数据库中明文存储消息内容,请打开这个开关和message.disable_remote_search开关
message.encrypt_message_content false
##允许客户端撤回用户自己发送的消息时限,单位是秒。
##允许客户端撤回用户自己发送的消息时限,单位是秒。0是不允许撤回;-1是无限制撤回。
##Server API不受此限制,可以撤回任意时限内的消息
##群组管理员或群主撤回群成员消息不受此限制,可以撤回任意时限内的消息。但管理员或群主撤回自己的消息还是需要准守此时间限制。
##修改此参数需要同步修改客户端UI,客户端UI代码有判断撤回是否显示的逻辑。
@@ -111,6 +133,18 @@ message.group_mute_exception_types 401,402,403,404,405,407,408,410,411
## 用户被全局禁言时允许发送的消息类型
message.global_mute_exception_types 401,402,403,404,405,407,408,410,411
## 离线用户推送过期天数,0是永不过期,建议配置为7天。
message.push_expired_days 7
## 当会话静音/全局静音/PC在线静音时,普通消息都不会有推送,如果有某些消息类型需要此时保持推送,可以配置到这里。此配置对消息免打扰时段无效。
## 此配置只影响远程推送,需要客户端处理客户端在线且在后台收到此消息的处理,需要针对这些消息类型加上本地通知。
## 强制推送消息的MessagePayload中pushContent和pushData至少要存在一个才可以推送。
## 消息类型以英文逗号分割。
#message.force_push_types 401,402
## 群发消息(包括server api群发和全局频道群发)时,目标用户是否来自于t_user表。如果使用野火托管用户信息,请设置为true,否则设置为false。
message.broadcast.target_from_user_table true
## 消息转发功能开启时,开关是否不转发server api接口消息。
## 当为true时,不转发server api发送的消息;当为false时,转发server api发送的消息。
message.no_forward_admin_message false
@@ -118,18 +152,50 @@ message.no_forward_admin_message false
## 消息转发功能开启时,转发信息是否带上用户clientId和平台类型。
message.forward_with_client_info false
## 消息转发功能开启时,转发信息是否带上发送者的用户信息
message.forward_with_sender_info false
## 消息转发功能开启时,转发信息是否带上目标的信息,单聊时是对方用户信息,群聊时是群组信息,频道时是频道信息
message.forward_with_target_info false
## 允许发送单聊消息给被封禁用户
message.allow_send_to_forbidden_user false
## 机器人回调信息是否带上用户clientId和平台类型。
robot.callback_with_client_info false
## 机器人回调信息是否带上发送者的用户信息
robot.callback_with_sender_info false
## 机器人回调信息是否带上目标的信息,单聊时是对方用户信息,群聊时是群组信息,频道时是频道信息
robot.callback_with_target_info false
## 是否允许@会话外的机器人。如果为true,单聊或者群聊时,可以@不在会话中的机器人。当机器人收到消息后,可以回复消息。
## 如果为false,不能@不在会话中的机器人。机器人不在会话中时,也不能回复消息。
robot.mention_external_robot true
## 频道回调信息是否带上用户clientId和平台类型。
channel.callback_with_client_info false
## 频道回调信息是否带上发送者的用户信息。
channel.callback_with_sender_info false
## 频道回调信息是否带上频道信息
channel.callback_with_target_info false
## 频道回调新方式,在新方式下,如果有callback地址就会总是回调客户端发送的消息(包括owner和订阅者)。automatic属性控制订阅者发送的消息是否发给owner, 0发,1不发。
## 旧的方式是,automatic为0时,订阅者的消息发给owner,不转发消息到callback地址。为1时,把订阅者的消息转发给callback地址,不发给owner。
channel.new_callback_feature true
##禁止搜索用户
friend.disable_search false
##禁止按照昵称搜索用户。
friend.disable_nick_name_search false
##禁止按照用户id搜索用户,默认为false。
friend.disable_user_id_search false
##禁止发送好友邀请,通过server api添加好友不受此限制(force参数需要为true)
friend.disable_friend_request false
@@ -142,6 +208,12 @@ friend.reject_request_duration 2592000000
##好友请求过期时间,单位是毫秒,默认是7天,0为无限长期限。
friend.request_expiration_duration 604800000
##好友请求限制频率,一个用户24小时之内允许请求好友的次数,默认不限频。
friend.request_rate_limit 30
##请求添加机器人为好友时,机器人是否自动接受,默认为true
friend.robot_auto_accept true
## 聊天室观众空闲退出时间,单位为毫秒,默认为15分钟,0为永远不退出
chatroom.participant_idle_time 900000
@@ -151,6 +223,18 @@ chatroom.rejoin_when_active true
## 当一个聊天室不存在时,如果有用户加入就自动创建
chatroom.create_when_not_exist true
## 一个用户同一时刻只能加入一个聊天室。
## 当一个客户端加入一个聊天室时,如果这个客户端已经加入另外一个聊天室了,会自动退出之前的聊天室。
## 当一个客户端加入一个聊天室时,如果这个用户的其他端已经加入一个聊天室了,kickoff_other_platform为true时会自动退出之前的聊天室,为false时会返回225的错误码。
## 当自动退出聊天室时,客户端不会收到任何通知,只是不会再收到消息。
chatroom.kickoff_other_platform true
## 是否允许群主不受限制地撤回自己发送的消息。false可以${message.recall_time_limit}撤回自己的消息;true可以撤回任意时间自己的消息。默认为true。
group.allow_owner_recall_self_msg true
## 是否允许群管理员不受限制地撤回自己发送的消息。false可以${message.recall_time_limit}撤回自己的消息;true可以撤回任意时间自己的消息。默认为false。
group.allow_manager_recall_self_msg false
## 是否允许客户端发送操作群的自定义群通知消息
group.allow_client_custom_operation_notification true
@@ -161,6 +245,20 @@ group.allow_robot_custom_operation_notification true
## 0 不发送,1 退群时发送,2 踢人时发送,3 退群和踢人时都发送。
group.visible_quit_or_kickoff_notification 0
## 禁止客户端群操作。第1位是禁止创建群组,第2位是禁止销毁群组,3位禁止加入群,4位禁止退出群,5位禁止邀请群成员,6位禁止移出群成员,7位禁止转移群,8位禁止设置群管理员,9位禁止白名单处理,10位禁止群禁言,11位禁止修改群组信息,12位禁止群成员禁言。
## Server API不受此限制。
#group.forbidden_client_operation 0xFFF
## 是否禁止陌生人拉人入群,默认为false
group.disable_stranger_invite false
## 创建群组或者加人时。可能会有成员加入失败(黑名单,或者不允许陌生人拉入群)。当出现有成员失败时,如果此开关为关,操作会失败。
## 如果此开关打开,操作会返回成功,另外在群里发送一条通知消息 XXX 拒绝加入群组。默认为开。
group.add_member_allow_part_success true
## 群组信息是否标记删除
group.enable_mark_deletion true
## 隐藏用户信息属性类型,2是性别,3是电话号码,4是邮箱,5是地址,6是公司信息,7是社交账号,8是Extra信息。当需要隐藏多个时以英文逗号分开;
## 本配置是为了提高安全性,避免把对应信息同步到客户端,这样就能防止程序被破解从而拿到用户关键信息。仅对客户端有效,server api不受影响。
## 注意,修改本配置不会对已经同步的信息产生影响。
@@ -173,6 +271,9 @@ user.keep_display_name_when_destroy true
# 因此需要分段多次同步,此开关定义了每次同步的数据的条数。此需要客户端支持,客户端SDK在2021.5.25日后支持此功能。如果确认所有客户端都在这个日志之后才可以打开此设置。
#sync.data_part_size 1000
## 是否关闭api/version接口。api/version方法可以用来检查IM服务版本,上线确认无问题后,可以关掉这个接口。
#http.close_api_version false
#*********************************************************************
# 限频配置
#*********************************************************************
@@ -195,15 +296,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
@@ -227,11 +319,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
@@ -245,7 +347,10 @@ server.multi_endpoint false
## 是否支持PC多端登陆,当server.multi_endpoint为true时,此开关无意义,当为false时,可以单独打开PC端多端登录。
server.multi_pc_endpoint false
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或web端登录的通知。
## 是否支持PAD多端登陆,当server.multi_endpoint为true时,此开关无意义,当为false时,可以单独打开PAD端多端登录。
server.multi_pad_endpoint false
# 多平台连接状态通知(仅当multi_endpoint为false时有效),true时移动端可以收到pc或pad或web端登录的通知。
server.multi_platform_notification true
# 当pc或者web在线时,手机是否默认静音,默认为true。注意仅影响默认值,用户可以手动切换是否静音。
@@ -254,7 +359,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
@@ -268,6 +373,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
#*********************************************************************
# 监控配置
#*********************************************************************
@@ -293,17 +402,23 @@ push.ios.server.address http://localhost:8085/ios/push
##消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.forward.url http://localhost:8888/im_event/message
##需要转发的消息类型,当有多个时以英文逗号分割。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。如果转发所有消息,请注释掉配置或者设置为空
#message.forward.types 1,2,3,5-10
##需要转发的消息类型,当有多个时以英文逗号分割,可以用-来指定区间,注意区间不能太大,因为要把区间内的所有数字都放入到Set中。
#message.forward.exclude_types 1,2,3,5-10
##敏感消息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.sensitive.forward.url http://localhost:8888/im_event/message
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。此转发不受${message.forward.types}限制。
##提醒消息转发地址,@全体用户和@某个用户消息会回调此地址。
##此转发不受${message.forward.types}和${message.forward.exclude_types}限制。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.mentionmsg.forward.url http://localhost:8888/im_event/message
##撤回消息转发地址,当用户撤回消息会回调此地址。
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
#message.recallmsg.forward.url http://localhost:8888/im_event/recall_message
##设备信息转发地址
##注意回调是单线程回调,接收服务必须在同一内网,且异步处理快速返回,否则会有延迟问题!!
@@ -332,10 +447,19 @@ push.ios.server.address http://localhost:8085/ios/push
#*********************************************************************
# Sensitive configuration
#*********************************************************************
## 内置文本敏感词过滤处理方法,0 发送失败;1 发送成功但消息被服务器直接丢弃;2 命中的敏感词被替换成***;3 记录并继续发送
sensitive.filter.type 0
## 内置文本敏感词过滤处理方法,当命中敏感词后,消息会被记录到t_sensitive_messages表中,然后根据type类型做不同的处理,处理方式如下:
## 0 发送失败;1 发送成功但消息被服务器直接丢弃;2 命中的敏感词被替换成***然后正常发送;3 正常发送。
## 如果开启 message.sensitive.forward.url 配置,命中敏感词的消息还会被回调到这个地址
sensitive.filter.type 2
## 如果内置敏感词库无法满足您的需求,可以自己开发部署敏感词审核服务。配置如下地址和消息类型,IM服务会把指定消息类型的消息回调到指定地址
## 敏感词是否只处理消息。如果为true,只过滤消息;如果为false,会同时过滤用户昵称、群组名和群昵称。
## 除了消息之外的信息只支持内部敏感词处理,不支持外置审核。
sensitive.only_message false
## 敏感词添加或者删除有2种方式,一种是通过Server API进行,这种方式会立即生效。另外一种方法是修改数据库,直接中数据库的t_sensitive_word表中处理
## 修改数据库的方法不会立即生效,IM服务会2个小时重新加载一次敏感词。所以如果直接修改敏感词表,需要等2个小时,或者重启才能生效。
## 如果内置敏感词库无法满足您的需求,可以开发部署独立敏感词审核服务。配置如下地址和消息类型,IM服务会把指定消息类型的消息回调到指定地址,内置的敏感词审核和敏感词转发功能就不再起作用了。
## 审核服务返回状态码200表示继续发送,如果需要替换内容,返回状态码200并且内容为替换后的内容。返回403表示不允许发送。其它错误继续发送。
## 独立审核服务地址,IM服务会post SendMessageData。如果需要修改消息内容,返回 MessagePayload。请参考应用服务中 "/message/censor" Mapping.
#sensitive.remote_server_url http://192.168.3.202:8888/message/censor
+1
View File
@@ -0,0 +1 @@
alter table `t_group_member` add INDEX `group_member_mid_index` (`_mid`);
+19
View File
@@ -0,0 +1,19 @@
alter table `t_group` add column `_super_group` tinyint NOT NULL DEFAULT 0;
DROP TABLE IF EXISTS `t_group_messages`;
CREATE TABLE `t_group_messages` (
`_mid` bigint(20) NOT NULL PRIMARY KEY,
`_sender` varchar(64) NOT NULL,
`_gid` varchar(64) NOT NULL,
`_line` int(11) NOT NULL,
`_client_id` varchar(64) NULL,
`_seq` bigint(20) NOT NULL,
`_persist_flag` int(11) NOT NULL,
`_mentioned_type` int(11) NOT NULL,
`_mentioned_targets` BLOB NULL,
`_to` BLOB NULL,
`_cont_type` int(11) NOT NULL DEFAULT 0,
`_duration` int(11) NOT NULL DEFAULT 0,
`_dt` bigint(20) NOT NULL DEFAULT 0,
INDEX `group_message_seq_uid_index` (`_gid` DESC, `_line`, `_seq` DESC)
);
+2
View File
@@ -0,0 +1,2 @@
alter table `t_group` add column `_deleted` tinyint NOT NULL DEFAULT 0;
+13
View File
@@ -0,0 +1,13 @@
DROP TABLE IF EXISTS `t_domain`;
CREATE TABLE `t_domain` (
`_domain_id` varchar(64) NOT NULL PRIMARY KEY,
`_name` varchar(64) NOT NULL,
`_desc` varchar(256) DEFAULT '',
`_email` varchar(64) DEFAULT '',
`_tel` varchar(64) DEFAULT '',
`_address` varchar(64) DEFAULT '',
`_extra` varchar(1024) DEFAULT '',
`_dt` bigint(20) NOT NULL
);
alter table `t_user` add column `_external` tinyint DEFAULT 0;
+1
View File
@@ -0,0 +1 @@
CREATE INDEX `user_gid_dt_index` ON `t_group_member` (`_gid`, `_dt`);
+1
View File
@@ -0,0 +1 @@
insert into t_settings(`id`, `_value`,`_desc`) values (2, '文件传输助手,客服,官方', '禁止使用名称,如果有多个,以英文逗号隔开。直接修改数据库,不用重启IM服务');
+1
View File
@@ -0,0 +1 @@
alter table `t_messages` modify column `_target` varchar(129) NOT NULL;
+1
View File
@@ -0,0 +1 @@
ALTER TABLE `t_user_session` ADD COLUMN `_ip` varchar(40) DEFAULT '';
@@ -10,7 +10,7 @@ CREATE TABLE `t_messages_0` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -28,7 +28,7 @@ CREATE TABLE `t_messages_1` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -46,7 +46,7 @@ CREATE TABLE `t_messages_2` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -63,7 +63,7 @@ CREATE TABLE `t_messages_3` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -81,7 +81,7 @@ CREATE TABLE `t_messages_4` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -99,7 +99,7 @@ CREATE TABLE `t_messages_5` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -116,7 +116,7 @@ CREATE TABLE `t_messages_6` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -134,7 +134,7 @@ CREATE TABLE `t_messages_7` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -152,7 +152,7 @@ CREATE TABLE `t_messages_8` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -169,7 +169,7 @@ CREATE TABLE `t_messages_9` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -187,7 +187,7 @@ CREATE TABLE `t_messages_10` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -205,7 +205,7 @@ CREATE TABLE `t_messages_11` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -222,7 +222,7 @@ CREATE TABLE `t_messages_12` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -240,7 +240,7 @@ CREATE TABLE `t_messages_13` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -258,7 +258,7 @@ CREATE TABLE `t_messages_14` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -275,7 +275,7 @@ CREATE TABLE `t_messages_15` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -293,7 +293,7 @@ CREATE TABLE `t_messages_16` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -311,7 +311,7 @@ CREATE TABLE `t_messages_17` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -328,7 +328,7 @@ CREATE TABLE `t_messages_18` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -346,7 +346,7 @@ CREATE TABLE `t_messages_19` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -364,7 +364,7 @@ CREATE TABLE `t_messages_20` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -381,7 +381,7 @@ CREATE TABLE `t_messages_21` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -399,7 +399,7 @@ CREATE TABLE `t_messages_22` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -417,7 +417,7 @@ CREATE TABLE `t_messages_23` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -434,7 +434,7 @@ CREATE TABLE `t_messages_24` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -452,7 +452,7 @@ CREATE TABLE `t_messages_25` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -470,7 +470,7 @@ CREATE TABLE `t_messages_26` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -487,7 +487,7 @@ CREATE TABLE `t_messages_27` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -505,7 +505,7 @@ CREATE TABLE `t_messages_28` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -523,7 +523,7 @@ CREATE TABLE `t_messages_29` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -540,7 +540,7 @@ CREATE TABLE `t_messages_30` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -558,7 +558,7 @@ CREATE TABLE `t_messages_31` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -576,7 +576,7 @@ CREATE TABLE `t_messages_32` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -593,7 +593,7 @@ CREATE TABLE `t_messages_33` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -611,7 +611,7 @@ CREATE TABLE `t_messages_34` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
@@ -629,7 +629,7 @@ CREATE TABLE `t_messages_35` (
`_data` BLOB NOT NULL,
`_searchable_key` TEXT DEFAULT NULL,
`_dt` DATETIME NOT NULL,
INDEX `message_uid_index` (`_mid` DESC)
UNIQUE INDEX `message_uid_index` (`_mid` DESC)
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
+1
View File
@@ -0,0 +1 @@
alter table `t_group_member` add INDEX `group_member_mid_index` (`_mid`);
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
alter table `t_group` add column `_deleted` tinyint NOT NULL DEFAULT 0;
+16
View File
@@ -0,0 +1,16 @@
DROP TABLE IF EXISTS `t_domain`;
CREATE TABLE `t_domain` (
`_domain_id` varchar(128) NOT NULL PRIMARY KEY,
`_name` varchar(64) NOT NULL,
`_desc` varchar(256) DEFAULT '',
`_email` varchar(64) DEFAULT '',
`_tel` varchar(64) DEFAULT '',
`_address` varchar(64) DEFAULT '',
`_extra` varchar(1024) DEFAULT '',
`_dt` bigint(20) NOT NULL
)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8mb4
COLLATE = utf8mb4_unicode_ci;
alter table `t_user` add column `_external` tinyint DEFAULT 0;
+1
View File
@@ -0,0 +1 @@
CREATE INDEX `user_gid_mid_dt_index` ON `t_group_member` (`_gid`, `_mid`, `_dt`);
@@ -0,0 +1,2 @@
DROP INDEX `user_gid_mid_dt_index` ON `t_group_member`;
CREATE INDEX `user_gid_dt_index` ON `t_group_member` (`_gid`, `_dt`);
+1
View File
@@ -0,0 +1 @@
insert into t_settings(`id`, `_value`,`_desc`) values (2, '文件传输助手,客服,官方', '禁止使用名称,如果有多个,以英文逗号隔开。直接修改数据库,不用重启IM服务');
+37
View File
@@ -0,0 +1,37 @@
alter table `t_messages` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_0` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_1` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_2` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_3` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_4` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_5` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_6` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_7` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_8` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_9` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_10` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_11` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_12` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_13` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_14` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_15` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_16` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_17` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_18` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_19` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_20` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_21` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_22` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_23` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_24` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_25` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_26` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_27` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_28` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_29` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_30` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_31` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_32` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_33` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_34` modify column `_target` varchar(129) NOT NULL;
alter table `t_messages_35` modify column `_target` varchar(129) NOT NULL;
+1
View File
@@ -0,0 +1 @@
ALTER TABLE `t_user_session` ADD COLUMN `_ip` varchar(40) DEFAULT '';
+10 -1
View File
@@ -9,6 +9,7 @@ server {
root html;
index index.html index.htm index.php;
#同步服务设置
location /route {
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
@@ -16,7 +17,7 @@ server {
proxy_pass http://imserver;
}
#社区版需要启用下面一段,专业版不能用
#客户端短连接业务
location /im {
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
@@ -24,14 +25,21 @@ server {
proxy_pass http://imserver;
}
#版本检查/api/version,可以删掉
location /api {
proxy_pass http://imserver;
}
#内置文件上传和下载
location /fs {
client_max_body_size 200M;
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://imserver;
}
#机器人API
location /robot {
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
@@ -39,6 +47,7 @@ server {
proxy_pass http://imserver;
}
#频道API
location /channel {
proxy_set_header Host $host;
proxy_set_header X-real-ip $remote_addr;
+22 -109
View File
@@ -5,7 +5,7 @@
<relativePath>../</relativePath>
<artifactId>wildfirechat-parent</artifactId>
<groupId>cn.wildfirechat</groupId>
<version>0.97</version>
<version>1.4.0</version>
</parent>
<artifactId>moquette-broker</artifactId>
@@ -37,14 +37,14 @@
<!--用于与slf4j保持桥接-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.2</version>
<artifactId>log4j-slf4j2-impl</artifactId>
<version>2.22.1</version>
</dependency>
<!-- slf4j核心包-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
<version>2.0.12</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
@@ -65,6 +65,18 @@
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.25.3</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-common</artifactId>
@@ -112,9 +124,7 @@
<artifactId>netty-transport-native-epoll</artifactId>
<version>${netty.version}</version>
<classifier>linux-x86_64</classifier>
<!--
<classifier>${os.detected.name}-${os.detected.arch}</classifier>
-->
<!-- <classifier>linux-aarch_64</classifier>-->
</dependency>
<dependency>
@@ -129,37 +139,18 @@
<version>3.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.xiaoleilu/hutool -->
<dependency>
<groupId>com.xiaoleilu</groupId>
<artifactId>hutool</artifactId>
<version>2.15.11</version>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.26</version>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.4</version>
</dependency>
<!-- needs extra dependencies: objenesis & hamcrest -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.fusesource.mqtt-client</groupId>
<artifactId>mqtt-client</artifactId>
@@ -169,29 +160,7 @@
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>websocket-client</artifactId>
<version>9.2.0.M1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<!-- use 2.6.0 for Java 7 projects -->
<version>2.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.191</version>
<scope>test</scope>
<version>1.2.2</version>
</dependency>
<dependency>
@@ -214,14 +183,12 @@
<scope>runtime</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.13.1</version>
<version>4.12.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.flywaydb/flyway-core -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
@@ -244,60 +211,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Test.java</include>
</includes>
</configuration>
</plugin>
<!-- Generate the test-jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Includes to generate OSGi headers -->
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>
</archive>
</configuration>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<executions>
<execution>
<id>bundle-manifest</id>
<phase>process-classes</phase>
<goals>
<goal>manifest</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>pl.project13.maven</groupId>
<artifactId>git-commit-id-plugin</artifactId>
@@ -12,9 +12,11 @@ package cn.wildfirechat.push;
public class PushMessage {
public String sender;
public String senderName;
public String senderPortrait;
public int convType;
public String target;
public String targetName;
public String targetPortrait;
public String userId;
public int line;
public int cntType;
@@ -32,14 +34,19 @@ public class PushMessage {
public String voipDeviceToken;
public boolean isHiddenDetail;
public String language;
public boolean republish;
public long messageId;
public long callStartUid;
public int existBadgeNumber;
public PushMessage(String sender, int conversationType, String target, int line, int messageContentType, long serverTime, String senderName, String targetName, int unReceivedMsg, int mentionedType, boolean isHiddenDetail, String language) {
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) {
this.sender = sender;
this.convType = conversationType;
this.target = target;
this.senderName = senderName;
this.senderPortrait = senderPortrait;
this.targetName = targetName;
this.targetPortrait = targetPortrait;
this.line = line;
this.cntType = messageContentType;
this.serverTime = serverTime;
@@ -61,6 +68,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) {
@@ -80,10 +81,6 @@ public class PushServer {
}
MemorySessionStore.Session session = sessionsStore.getSession(deviceId);
int badge = session.getUnReceivedMsgs();
if (badge <= 0) {
badge = 1;
}
if (StringUtil.isNullOrEmpty(session.getDeviceToken())) {
LOG.warn("Device token is empty for device {}", deviceId);
return;
@@ -93,13 +90,20 @@ public class PushServer {
pushMessage.pushType = session.getPushType();
pushMessage.pushContent = pushContent;
pushMessage.deviceToken = session.getDeviceToken();
pushMessage.unReceivedMsg = badge;
pushMessage.userId = session.getUsername();
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS || session.getPlatform() == ProtoConstants.Platform.Platform_Android) {
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS ||
session.getPlatform() == ProtoConstants.Platform.Platform_iPad ||
session.getPlatform() == ProtoConstants.Platform.Platform_Android ||
session.getPlatform() == ProtoConstants.Platform.Platform_APad ||
session.getPlatform() == ProtoConstants.Platform.Platform_Harmony ||
session.getPlatform() == ProtoConstants.Platform.Platform_HarmonyPad
) {
String url = androidPushServerUrl;
if (session.getPlatform() == ProtoConstants.Platform.Platform_iOS) {
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 {
@@ -1,8 +1,6 @@
package com.xiaoleilu.loServer;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.DateUtil;
import cn.hutool.core.date.DateUtil;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.ClassUtil;
import com.xiaoleilu.loServer.annotation.Route;
@@ -1,6 +1,6 @@
package com.xiaoleilu.loServer;
import com.xiaoleilu.hutool.util.DateUtil;
import cn.hutool.core.date.DateUtil;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.AdminActionHandler;
@@ -5,11 +5,9 @@ import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.xiaoleilu.hutool.lang.Singleton;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.FileUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Singleton;
import cn.hutool.core.util.StrUtil;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.DefaultIndexAction;
import com.xiaoleilu.loServer.action.UnknownErrorAction;
@@ -189,7 +187,7 @@ public class ServerSetting {
* @param filterClass Filter类
*/
public static void setFilter(String path, Class<? extends Filter> filterClass) {
setFilter(path, (Filter)Singleton.get(filterClass));
setFilter(path, (Filter) Singleton.get(filterClass));
}
//----------------------------------------------------------------------------------------------- Filter end
@@ -8,9 +8,13 @@
package com.xiaoleilu.loServer.action;
import cn.wildfirechat.common.IMExceptionEvent;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.action.channel.ChannelAction;
import com.xiaoleilu.loServer.action.robot.RobotAction;
import com.xiaoleilu.loServer.annotation.RequireAuthentication;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
@@ -23,7 +27,10 @@ import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import cn.wildfirechat.common.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.RateLimiter;
import win.liyufan.im.Utility;
import java.nio.charset.StandardCharsets;
@@ -38,6 +45,7 @@ import static io.moquette.BrokerConstants.*;
*/
abstract public class Action {
private static final Logger LOG = LoggerFactory.getLogger(Action.class);
public static IMessagesStore messagesStore = null;
public static ISessionsStore sessionsStore = null;
protected static final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
@@ -47,6 +55,8 @@ abstract public class Action {
protected static RateLimiter robotLimiter = null;
protected static RateLimiter channelLimiter = null;
protected static boolean closeApiVersion = false;
public static void init(IConfig config) {
int adminRate = 10000;
int robotRate = 1000;
@@ -70,6 +80,11 @@ abstract public class Action {
adminLimiter = new RateLimiter(10, adminRate);
robotLimiter = new RateLimiter(10, robotRate);
channelLimiter = new RateLimiter(10, channelRate);
try {
closeApiVersion = Boolean.parseBoolean(config.getProperty(HTTP_CLOSE_API_VERSION, "false"));
} catch (Exception e) {
}
}
protected class Result {
@@ -146,9 +161,22 @@ abstract public class Action {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes, StandardCharsets.UTF_8);//contribute by JiaRG from github
T t = gson.fromJson(content, cls);
return t;
try {
T t = gson.fromJson(content, cls);
return t;
} catch (JsonSyntaxException e) {
e.printStackTrace();
LOG.error("Object {} from json {} failure", cls.getName(), content);
int exception = IMExceptionEvent.EventType.ADMIN_API_Exception;
if (this instanceof RobotAction) {
exception = IMExceptionEvent.EventType.ROBOT_API_Exception;
} else if (this instanceof ChannelAction) {
exception = IMExceptionEvent.EventType.CHANNEL_API_Exception;
}
Utility.printExecption(LOG, e, exception);
throw new RuntimeException(e);
}
}
return null;
}
@@ -31,6 +31,11 @@ public class CheckTokenAction extends Action {
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
if(closeApiVersion) {
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Not found!");
return true;
}
response.setStatus(HttpResponseStatus.OK);
String userId = request.getParam("userId");
String clientId = request.getParam("clientId");
@@ -1,20 +1,22 @@
package com.xiaoleilu.loServer.action;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Pattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.DateUtil;
import com.xiaoleilu.hutool.util.FileUtil;
import com.xiaoleilu.hutool.util.ReUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
@@ -24,6 +26,8 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.slf4j.LoggerFactory;
import static cn.hutool.core.date.DatePattern.HTTP_DATETIME_PATTERN;
/**
* 默认的主页Action,当访问主页且没有定义主页Action时,调用此Action
*
@@ -90,11 +94,28 @@ public class FileAction extends Action {
}
response.setContent(file);
if (isAudioOrVideoFile(file.getName())) {
response.setHeader("Accept-Ranges", "bytes");
} else {
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
}
return true;
}
private static boolean isAudioOrVideoFile(String name) {
try {
if (StringUtil.isNullOrEmpty(name)) return false;
String mimeType = Files.probeContentType(new File(name).toPath());
return mimeType != null && (mimeType.startsWith("audio/") || mimeType.startsWith("video/"));
} catch (IOException e) {
return false;
}
}
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private static final SimpleDateFormat HTTP_DATE_FORMATER = new SimpleDateFormat(DateUtil.HTTP_DATETIME_PATTERN, Locale.US);
private static final SimpleDateFormat HTTP_DATE_FORMATER = new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US);
/**
@@ -80,6 +80,11 @@ public class IMAction extends Action {
if (userId == null) {
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null);
} else {
if(messagesStore.getUserStatus(userId) == ProtoConstants.UserStatus.Forbidden) {
sendResponse(response, ErrorCode.ERROR_CODE_USER_BLOCKED, null);
return true;
}
ServerAPIHelper.sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), new ServerAPIHelper.Callback() {
@Override
public void onSuccess(byte[] result) {
@@ -1,9 +1,6 @@
package com.xiaoleilu.loServer.action;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.spi.IMessagesStore;
@@ -11,6 +11,7 @@ package com.xiaoleilu.loServer.action;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.hazelcast.util.StringUtil;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
@@ -28,7 +29,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.wildfirechat.common.ErrorCode;
import java.net.InetSocketAddress;
import java.util.Base64;
import java.util.Map;
import java.util.concurrent.Executor;
@Route("/route")
@@ -39,17 +42,10 @@ public class RouteAction extends Action {
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setContentType("application/octet-stream");
response.setHeader("Access-Control-Allow-Origin", "*");
FullHttpRequest fullHttpRequest = (FullHttpRequest) request.getNettyRequest();
boolean b = false;
if ("web".equalsIgnoreCase(fullHttpRequest.headers().get("p"))) {
b = true;
}
final boolean base64Response = b;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
@@ -57,15 +53,20 @@ public class RouteAction extends Action {
try {
bytes = Base64.getDecoder().decode(str);
} catch (IllegalArgumentException e) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
return true;
}
String cid = fullHttpRequest.headers().get("cid");
byte[] cbytes = Base64.getDecoder().decode(cid);
cbytes = AES.AESDecrypt(cbytes, "", true);
boolean[] invalidTime = new boolean[1];
cbytes = AES.AESDecrypt(cbytes, "", true, invalidTime);
if (cbytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, base64Response);
if(invalidTime[0]) {
sendResponse(response, ErrorCode.ERROR_CODE_TIME_INCONSISTENT, null);
} else {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
}
return true;
}
cid = new String(cbytes);
@@ -74,7 +75,7 @@ public class RouteAction extends Action {
byte[] ubytes = Base64.getDecoder().decode(uid);
ubytes = AES.AESDecrypt(ubytes, "", true);
if (ubytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
return true;
}
uid = new String(ubytes);
@@ -84,7 +85,7 @@ public class RouteAction extends Action {
if (session == null) {
ErrorCode errorCode = sessionsStore.loadActiveSession(uid, cid);
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
sendResponse(response, errorCode, null, base64Response);
sendResponse(response, errorCode, null);
return true;
}
session = sessionsStore.sessionForClientAndUser(uid, cid);
@@ -94,39 +95,51 @@ public class RouteAction extends Action {
if (session != null) {
bytes = AES.AESDecrypt(bytes, session.getSecret(), true);
} else {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return true;
}
if (bytes == null) {
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH, null);
return true;
}
if(messagesStore.getUserStatus(uid) == ProtoConstants.UserStatus.Forbidden) {
sendResponse(response, ErrorCode.ERROR_CODE_USER_BLOCKED, null);
return true;
}
try {
WFCMessage.IMHttpWrapper wrapper = WFCMessage.IMHttpWrapper.parseFrom(bytes);
String token = wrapper.getToken();
String userId = Tokenor.getUserId(token.getBytes());
String remote = ((InetSocketAddress) ctx.channel().remoteAddress())
.getAddress().getHostAddress();
String ip = computeRealIp(request.getHeaders(), remote);
LOG.info("RouteAction token={}, userId={}", token, userId);
if (userId == null) {
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_TOKEN_ERROR, null);
} else {
ServerAPIHelper.sendRequest(userId, wrapper.getClientId(), wrapper.getRequest(), wrapper.getData().toByteArray(), new ServerAPIHelper.Callback() {
@Override
public void onSuccess(byte[] result) {
sendResponse(response, null, result, base64Response);
sendResponse(response, null, result);
//save ip after success response.
sessionsStore.updateSessionIp(userId, wrapper.getClientId(), ip);
}
@Override
public void onError(ErrorCode errorCode) {
sendResponse(response, errorCode, null, base64Response);
sendResponse(response, errorCode, null);
}
@Override
public void onTimeout() {
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null, base64Response);
sendResponse(response, ErrorCode.ERROR_CODE_TIMEOUT, null);
}
@Override
@@ -139,25 +152,35 @@ public class RouteAction extends Action {
return false;
}
} catch (InvalidProtocolBufferException e) {
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null, false);
sendResponse(response, ErrorCode.ERROR_CODE_INVALID_DATA, null);
}
}
return true;
}
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents, boolean base64Response) {
private String computeRealIp(Map<String, String> h, String remote) {
String xff = h.get("X-Forwarded-For");
if (xff != null && !xff.isEmpty() && !"unknown".equalsIgnoreCase(xff)) {
int idx = xff.indexOf(',');
String first = (idx > 0 ? xff.substring(0, idx) : xff).trim();
return first;
}
String xri = h.get("X-Real-IP");
if (xri != null && !xri.isEmpty() && !"unknown".equalsIgnoreCase(xri)) {
return xri;
}
return remote;
}
private void sendResponse(Response response, ErrorCode errorCode, byte[] contents) {
response.setStatus(HttpResponseStatus.OK);
if (contents == null) {
ByteBuf ackPayload = Unpooled.buffer();
ackPayload.ensureWritable(1).writeByte(errorCode.getCode());
contents = ackPayload.array();
}
if (base64Response) {
byte[] base64Data = Base64.getEncoder().encode(contents);
response.setContent(base64Data);
} else {
response.setContent(contents);
}
response.setContent(contents);
response.send();
}
}
@@ -4,9 +4,6 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import com.hazelcast.core.HazelcastInstance;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
@@ -187,6 +187,10 @@ public class UploadFileAction extends Action {
return false;
}
if(remoteFileName != null && remoteFileName.length() > 2 && remoteFileName.startsWith("'") && remoteFileName.endsWith("'")) {
remoteFileName = remoteFileName.substring(1, remoteFileName.length()-1);
}
if (StringUtil.isNullOrEmpty(remoteFileName)) {
logger.warn("remoteFileName is empty!");
@@ -235,12 +239,17 @@ public class UploadFileAction extends Action {
int offset = 0; // 断点续传开始位置
Date nowTime=new Date();
SimpleDateFormat time=new SimpleDateFormat("yyyy/MM/dd/HH");
SimpleDateFormat time=new SimpleDateFormat("yyyy/MM/dd/HH/mm/ss");
String datePath = time.format(nowTime);
datePath = "fs/" + bucket[0] + "/" + datePath; //add bucket
String dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
String dir;
if(MediaServerConfig.FILE_STROAGE_ROOT.startsWith("/") || MediaServerConfig.FILE_STROAGE_ROOT.startsWith("~/") || MediaServerConfig.FILE_STROAGE_ROOT.startsWith("./")) {
dir = MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
} else {
dir = "./" + MediaServerConfig.FILE_STROAGE_ROOT + "/" + datePath;
}
File dirFile = new File(dir);
boolean bFile = dirFile.exists();
@@ -248,6 +257,7 @@ public class UploadFileAction extends Action {
if(!bFile) {
bFile = dirFile.mkdirs();
if (!bFile) {
logger.error("create dirs error! {}", dir);
response.setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
response.setContent("服务器错误:无法创建文件");
return false;
@@ -274,9 +284,10 @@ public class UploadFileAction extends Action {
savedThunkSize += readableBytesSize;
offset += readableBytesSize;
byteBuf.release();
byteBuf = null;
if (savedThunkSize >= remoteFileSize) {
byteBuf.release();
fileUpload.release();
response.setStatus(HttpResponseStatus.OK);
@@ -30,8 +30,12 @@ public class VersionAction extends Action {
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
response.setStatus(HttpResponseStatus.OK);
if(closeApiVersion) {
response.sendError(HttpResponseStatus.NOT_FOUND, "404 Not found!");
return true;
}
response.setStatus(HttpResponseStatus.OK);
try {
response.setContent(Utility.formatJson(gson.toJson(GitRepositoryState.getGitRepositoryState())));
@@ -51,14 +51,7 @@ public class AliasPutAction extends AdminAction {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
String channelId = new String(data);
return new Result(ErrorCode.ERROR_CODE_SUCCESS, new OutputCreateChannel(channelId));
} else {
return new Result(errorCode);
}
return new Result(errorCode);
});
return false;
} else {
@@ -53,8 +53,11 @@ public class CreateChannelAction extends AdminAction {
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
byte[] data = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(data);
String channelId = new String(data);
return new Result(ErrorCode.ERROR_CODE_SUCCESS, new OutputCreateChannel(channelId));
String str = new String(data);
String[] ss = str.split("\\|");
String channelId = ss[0];
String secret = ss[1];
return new Result(ErrorCode.ERROR_CODE_SUCCESS, new OutputCreateChannel(channelId, secret));
} else {
return new Result(errorCode);
}
@@ -48,10 +48,6 @@ public class CreateChatroomAction extends AdminAction {
inputCreateChatroom.setChatroomId(messagesStore.getShortUUID());
}
if (inputCreateChatroom.getPortrait() == null || inputCreateChatroom.getPortrait().length() == 0) {
inputCreateChatroom.setPortrait("https://avatars.io/gravatar/" + inputCreateChatroom.getChatroomId());
}
WFCMessage.ChatroomInfo info = inputCreateChatroom.toChatroomInfo();
messagesStore.createChatroom(inputCreateChatroom.getChatroomId(), info);
setResponseContent(RestResult.ok(new OutputCreateChatroom(inputCreateChatroom.getChatroomId())), response);
@@ -45,23 +45,13 @@ public class CreateRobotAction extends AdminAction {
if (inputCreateRobot != null
&& !StringUtil.isNullOrEmpty(inputCreateRobot.getName())) {
if(StringUtil.isNullOrEmpty(inputCreateRobot.getPassword())) {
inputCreateRobot.setPassword(UUIDGenerator.getUUID());
}
if(StringUtil.isNullOrEmpty(inputCreateRobot.getUserId())) {
inputCreateRobot.setUserId(messagesStore.getShortUUID());
}
if (inputCreateRobot.getPortrait() == null || inputCreateRobot.getPortrait().length() == 0) {
inputCreateRobot.setPortrait("https://avatars.io/gravatar/" + inputCreateRobot.getUserId());
}
WFCMessage.User newUser = inputCreateRobot.toUser();
try {
messagesStore.addUserInfo(newUser, inputCreateRobot.getPassword());
messagesStore.addUserInfo(newUser);
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e, IMExceptionEvent.EventType.ADMIN_API_Exception);
@@ -47,10 +47,6 @@ public class CreateUserAction extends AdminAction {
&& !StringUtil.isNullOrEmpty(inputCreateUser.getName())
&& (inputCreateUser.getType() == ProtoConstants.UserType.UserType_Normal || inputCreateUser.getType() == ProtoConstants.UserType.UserType_Admin || inputCreateUser.getType() == ProtoConstants.UserType.UserType_Super_Admin)) {
if(StringUtil.isNullOrEmpty(inputCreateUser.getPassword())) {
inputCreateUser.setPassword(UUIDGenerator.getUUID());
}
if(StringUtil.isNullOrEmpty(inputCreateUser.getUserId())) {
inputCreateUser.setUserId(messagesStore.getShortUUID());
}
@@ -85,7 +81,7 @@ public class CreateUserAction extends AdminAction {
try {
messagesStore.addUserInfo(newUserBuilder.build(), inputCreateUser.getPassword());
messagesStore.addUserInfo(newUserBuilder.build());
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e, IMExceptionEvent.EventType.ADMIN_API_Exception);
@@ -0,0 +1,59 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetUserInfo;
import cn.wildfirechat.pojos.InputGetUserList;
import cn.wildfirechat.pojos.InputOutputUserInfo;
import cn.wildfirechat.pojos.OutputGetUserList;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.User_Get_All)
@HttpMethod("POST")
public class GetAllUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserList input = getRequestBody(request.getNettyRequest(), InputGetUserList.class);
if (input != null && input.count > 0 && input.offset >= 0) {
List<WFCMessage.User> users = messagesStore.getUserInfoList(input.count, input.offset);
OutputGetUserList outputGetUserList = new OutputGetUserList();
outputGetUserList.userInfoList = new ArrayList<>();
for (WFCMessage.User user : users) {
outputGetUserList.userInfoList.add(InputOutputUserInfo.fromPbUser(user));
}
RestResult result = RestResult.ok(outputGetUserList);
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -0,0 +1,61 @@
/*
* 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 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;
}
}
@@ -0,0 +1,55 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputUserId;
import cn.wildfirechat.pojos.OutputGroupIds;
import cn.wildfirechat.pojos.StringPairPojo;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.Set;
@Route(APIPath.Get_Common_Groups)
@HttpMethod("POST")
public class GetCommonGroupsAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
StringPairPojo input = getRequestBody(request.getNettyRequest(), StringPairPojo.class);
if (input != null
&& (!StringUtil.isNullOrEmpty(input.getFirst())) && (!StringUtil.isNullOrEmpty(input.getSecond()))) {
Set<String> groupIds = messagesStore.getCommonGroupIds(input.getFirst(), input.getSecond());
OutputGroupIds outputGroupIds = new OutputGroupIds();
outputGroupIds.setGroupIds(new ArrayList<>(groupIds));
RestResult result = RestResult.ok(outputGroupIds);
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -53,10 +53,17 @@ 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());
pojoGroupInfo.setSearchable(groupInfo.getSearchable());
pojoGroupInfo.setMax_member_count(groupInfo.getMemberCount());
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);
@@ -0,0 +1,69 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetGroup;
import cn.wildfirechat.pojos.InputGetGroupMember;
import cn.wildfirechat.pojos.OutputGroupMemberList;
import cn.wildfirechat.pojos.PojoGroupMember;
import cn.wildfirechat.proto.ProtoConstants;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.Group_Member_Get)
@HttpMethod("POST")
public class GetGroupMemberAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetGroupMember input = getRequestBody(request.getNettyRequest(), InputGetGroupMember.class);
if (input != null
&& (!StringUtil.isNullOrEmpty(input.getGroupId()))
&& (!StringUtil.isNullOrEmpty(input.getMemberId()))) {
WFCMessage.GroupMember groupMember = messagesStore.getGroupMember(input.getGroupId(), input.getMemberId());
RestResult result;
if (groupMember == null || StringUtil.isNullOrEmpty(groupMember.getMemberId()) || groupMember.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Removed) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
PojoGroupMember pm = new PojoGroupMember();
pm.setMember_id(groupMember.getMemberId());
pm.setAlias(groupMember.getAlias());
pm.setType(groupMember.getType());
pm.setExtra(groupMember.getExtra());
pm.setCreateDt(groupMember.getCreateDt());
result = RestResult.ok(pm);
}
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -20,6 +20,7 @@ import io.netty.handler.codec.http.FullHttpRequest;
import cn.wildfirechat.common.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.GitRepositoryState;
import win.liyufan.im.IMTopic;
import java.util.Base64;
@@ -53,7 +54,7 @@ public class GetIMTokenAction extends AdminAction {
LOG.info("get im token success {},{},{}", userId, input.getClientId(), token.substring(0, Math.min(10, token.length())));
return new Result(errorCode1, new OutputGetIMTokenData(userId, token));
return new Result(errorCode1, new OutputGetIMTokenData(userId, token, GitRepositoryState.globalLabel));
} else {
return new Result(errorCode1);
}
@@ -0,0 +1,47 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.*;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.MemorySessionStore;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.Collection;
@Route(APIPath.User_Online_List)
@HttpMethod("POST")
public class GetOnlineUserAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
GetOnlineUserRequest getOnlineUserRequest = getRequestBody(request.getNettyRequest(), GetOnlineUserRequest.class);
if (getOnlineUserRequest.nodeId == 1) {
GetOnlineUserResult result = messagesStore.getOnlineUsers(getOnlineUserRequest.offset, getOnlineUserRequest.count);
setResponseContent(RestResult.ok(result), response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -49,7 +49,7 @@ public class GetUserAction extends AdminAction {
}
RestResult result;
if (user == null) {
if (user == null || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputOutputUserInfo.fromPbUser(user));
@@ -0,0 +1,61 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetChatroomInfo;
import cn.wildfirechat.pojos.InputUserId;
import cn.wildfirechat.pojos.OutputStringList;
import cn.wildfirechat.pojos.OutputUserChatroom;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.UserClientEntry;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Route(APIPath.Chatroom_GetUserChatroom)
@HttpMethod("POST")
public class GetUserChatroomAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputUserId inputUserId = getRequestBody(request.getNettyRequest(), InputUserId.class);
String userId = inputUserId.getUserId();
if (!StringUtil.isNullOrEmpty(userId)) {
OutputUserChatroom outputUserChatroom = messagesStore.getUserChatroom(userId);
RestResult result;
if (outputUserChatroom != null) {
result = RestResult.ok(outputUserChatroom);
} else {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
}
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -0,0 +1,55 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetUserGroupByType;
import cn.wildfirechat.pojos.InputUserId;
import cn.wildfirechat.pojos.OutputGroupIds;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.Set;
@Route(APIPath.Get_User_Groups_By_Type)
@HttpMethod("POST")
public class GetUserGroupsByTypeAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetUserGroupByType input = getRequestBody(request.getNettyRequest(), InputGetUserGroupByType.class);
if (input != null
&& (!StringUtil.isNullOrEmpty(input.getUserId()))) {
Set<String> groupIds = messagesStore.getUserGroupIds(input.getUserId(), input.getGroupMemberTypes());
OutputGroupIds outputGroupIds = new OutputGroupIds();
outputGroupIds.setGroupIds(new ArrayList<>(groupIds));
RestResult result = RestResult.ok(outputGroupIds);
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -0,0 +1,50 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.GetOnlineUserCountResult;
import cn.wildfirechat.pojos.GetUserSessionResult;
import cn.wildfirechat.pojos.InputUserId;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.MemorySessionStore;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.Collection;
@Route(APIPath.User_Online_Count)
@HttpMethod("POST")
public class GetUserOnlineCountAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
int count = messagesStore.getOnlineUserCount();
GetOnlineUserCountResult result = new GetOnlineUserCountResult();
GetOnlineUserCountResult.Node node = new GetOnlineUserCountResult.Node();
node.node = 1;
node.count = count;
result.add(node);
setResponseContent(RestResult.ok(result), response);
}
return true;
}
}
@@ -0,0 +1,57 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.GetUserSessionResult;
import cn.wildfirechat.pojos.InputGetChatroomInfo;
import cn.wildfirechat.pojos.InputUserId;
import cn.wildfirechat.pojos.OutputStringList;
import com.xiaoleilu.loServer.RestResult;
import com.xiaoleilu.loServer.annotation.HttpMethod;
import com.xiaoleilu.loServer.annotation.Route;
import com.xiaoleilu.loServer.handler.Request;
import com.xiaoleilu.loServer.handler.Response;
import io.moquette.persistence.MemorySessionStore;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.Collection;
@Route(APIPath.User_Session_List)
@HttpMethod("POST")
public class GetUserSessionsAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputUserId inputUserId = getRequestBody(request.getNettyRequest(), InputUserId.class);
String userId = inputUserId.getUserId();
if (!StringUtil.isNullOrEmpty(userId)) {
Collection<MemorySessionStore.Session> sessions = sessionsStore.sessionForUser(userId);
GetUserSessionResult result = new GetUserSessionResult();
for (MemorySessionStore.Session session : sessions) {
boolean isOnline = sessionsStore.isClientOnline(session.getClientID());
result.userSessions.add(new GetUserSessionResult.UserSession(session.getUsername(), session.getClientID(), session.getPlatform(), session.getPushType(), session.getDeviceToken(), session.getVoipDeviceToken(), isOnline));
}
setResponseContent(RestResult.ok(result), response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -0,0 +1,66 @@
/*
* 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 com.xiaoleilu.loServer.action.admin;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetUserInfo;
import cn.wildfirechat.pojos.InputOutputUserInfo;
import cn.wildfirechat.pojos.OutputUserInfoList;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.User_Get_Email_Info)
@HttpMethod("POST")
public class GetUsersByEmailAction extends AdminAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
String email = getRequestBody(request.getNettyRequest(), String.class);
if (!StringUtil.isNullOrEmpty(email)) {
List<WFCMessage.User> users = messagesStore.getUserInfosByEmail(email);
List<InputOutputUserInfo> list = new ArrayList<>();
for (WFCMessage.User user : users) {
list.add(InputOutputUserInfo.fromPbUser(user));
}
RestResult result;
if (list.isEmpty()) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
OutputUserInfoList outputUserInfoList = new OutputUserInfoList();
outputUserInfoList.userInfos = list;
result = RestResult.ok(outputUserInfoList);
}
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -25,6 +25,9 @@ import io.netty.util.internal.StringUtil;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@Route(APIPath.Msg_Recall)
@HttpMethod("POST")
public class RecallMessageAction extends AdminAction {
@@ -42,10 +45,9 @@ public class RecallMessageAction extends AdminAction {
WFCMessage.INT64Buf idBuf = WFCMessage.INT64Buf.newBuilder().setId(recallMessageData.getMessageUid()).build();
sendApiMessage(response, recallMessageData.getOperator(), IMTopic.RecallMessageTopic, idBuf.toByteArray(), result -> {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode errorCode = ErrorCode.fromCode(byteBuf.readByte());
return new Result(errorCode);
ErrorCode errorCode = ErrorCode.fromCode(result[0]);
byte[] data = Arrays.copyOfRange(result,1, result.length);
return new Result(errorCode, new String(data, StandardCharsets.UTF_8));
});
return false;
} else {
@@ -51,7 +51,7 @@ public class SendMessageAction extends AdminAction {
} else {
return new Result(errorCode);
}
});
}, sendMessageData.isUserMessage());
return false;
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
@@ -105,18 +105,6 @@ abstract public class ChannelAction extends Action {
response.send();
}
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = new String(bytes, StandardCharsets.UTF_8);
T t = gson.fromJson(content, cls);
return t;
}
return null;
}
protected void sendApiMessage(Response response, String topic, byte[] message, Callback callback) {
sendApiMessage(response, channelInfo.getOwner(), topic, message, callback);
}
@@ -0,0 +1,37 @@
/*
* 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 com.xiaoleilu.loServer.action.channel;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.pojos.InputChannelSubscribe;
import cn.wildfirechat.pojos.InputUserId;
import cn.wildfirechat.pojos.OutputStringList;
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 java.util.ArrayList;
@Route(APIPath.Channel_Is_Subscriber)
@HttpMethod("POST")
public class GetIsChannelSubscriberAction extends ChannelAction {
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputUserId input = getRequestBody(request.getNettyRequest(), InputUserId.class);
boolean isSubscriber = messagesStore.checkUserInChannel(input.getUserId(), channelInfo.getTargetId());
setResponseContent(RestResult.ok(isSubscriber), response);
}
return true;
}
}
@@ -44,7 +44,7 @@ public class GetUserAction extends ChannelAction {
RestResult result;
if (user == null || StringUtil.isNullOrEmpty(user.getName())) {
if (user == null || StringUtil.isNullOrEmpty(user.getName()) || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
if (channelInfo.getOwner().equals(inputUserId.getUserId()) ||
@@ -52,10 +52,17 @@ 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());
pojoGroupInfo.setSearchable(groupInfo.getSearchable());
pojoGroupInfo.setMax_member_count(groupInfo.getMemberCount());
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);
@@ -0,0 +1,98 @@
/*
* 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 com.xiaoleilu.loServer.action.robot;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.InputGetGroup;
import cn.wildfirechat.pojos.InputGetGroupMember;
import cn.wildfirechat.pojos.OutputGroupMemberList;
import cn.wildfirechat.pojos.PojoGroupMember;
import cn.wildfirechat.proto.ProtoConstants;
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.util.internal.StringUtil;
import java.util.ArrayList;
import java.util.List;
@Route(APIPath.Robot_Group_Member_Get)
@HttpMethod("POST")
public class GetGroupMemberAction extends RobotAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
InputGetGroupMember input = getRequestBody(request.getNettyRequest(), InputGetGroupMember.class);
String robotId = robot.getUid();
if (input != null
&& (!StringUtil.isNullOrEmpty(input.getGroupId()))
&& (!StringUtil.isNullOrEmpty(input.getMemberId()))) {
List<WFCMessage.GroupMember> members = new ArrayList<>();
ErrorCode errorCode = messagesStore.getGroupMembers(null, input.getGroupId(), 0, members);
RestResult result;
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
result = RestResult.resultOf(errorCode);
} else {
WFCMessage.GroupMember groupMember = null;
boolean isInGroup = false;
for (WFCMessage.GroupMember member : members) {
if (member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Removed) {
continue;
}
if(member.getMemberId().equals(robotId)) {
isInGroup = true;
if(groupMember != null) {
break;
}
}
if(member.getMemberId().equals(input.getMemberId())) {
groupMember = member;
if(isInGroup) {
break;
}
}
}
if(!isInGroup) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_IN_GROUP);
} else {
if(groupMember == null) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
PojoGroupMember pm = new PojoGroupMember();
pm.setMember_id(groupMember.getMemberId());
pm.setAlias(groupMember.getAlias());
pm.setType(groupMember.getType());
pm.setExtra(groupMember.getExtra());
pm.setCreateDt(groupMember.getCreateDt());
result = RestResult.ok(pm);
}
}
}
setResponseContent(result, response);
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
}
@@ -44,7 +44,7 @@ public class GetUserAction extends RobotAction {
}
RestResult result;
if (user == null) {
if (user == null || user.getDeleted() > 0) {
result = RestResult.resultOf(ErrorCode.ERROR_CODE_NOT_EXIST);
} else {
result = RestResult.ok(InputOutputUserInfo.fromPbUser(user));
@@ -0,0 +1,112 @@
/*
* 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 com.xiaoleilu.loServer.action.robot;
import cn.wildfirechat.common.APIPath;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.Conversation;
import cn.wildfirechat.pojos.ReplyMessageData;
import cn.wildfirechat.pojos.SendMessageData;
import cn.wildfirechat.pojos.SendMessageResult;
import cn.wildfirechat.proto.ProtoConstants;
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.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.jetbrains.annotations.NotNull;
import win.liyufan.im.IMTopic;
import java.util.Arrays;
@Route(APIPath.Robot_Message_Reply)
@HttpMethod("POST")
public class ReplyMessageAction extends RobotAction {
@Override
public boolean isTransactionAction() {
return true;
}
@Override
public boolean action(Request request, Response response) {
if (request.getNettyRequest() instanceof FullHttpRequest) {
ReplyMessageData replyMessageData = getRequestBody(request.getNettyRequest(), ReplyMessageData.class);
if (ReplyMessageData.isValide(replyMessageData)) {
WFCMessage.Message message = messagesStore.getMessage(replyMessageData.getMessageUid());
ErrorCode errorCode = null;
if(message == null) {
errorCode = ErrorCode.ERROR_CODE_NOT_EXIST;
} else if(!message.getContent().getMentionedTargetList().contains(robot.getUid())) {
errorCode = ErrorCode.ERROR_CODE_INVALID_MESSAGE;
} else if(message.getConversation().getType() != ProtoConstants.ConversationType.ConversationType_Private && message.getConversation().getType() != ProtoConstants.ConversationType.ConversationType_Group) {
errorCode = ErrorCode.ERROR_CODE_NOT_IMPLEMENT;
} else if(System.currentTimeMillis() - message.getServerTimestamp() > 15*60*1000) {
errorCode = ErrorCode.ERROR_CODE_RECALL_TIME_EXPIRED;
} else if(!messagesStore.isRobotMentionExternalRobot()) {
if(message.getConversation().getType() != ProtoConstants.ConversationType.ConversationType_Group) {
errorCode = ErrorCode.ERROR_CODE_NOT_IMPLEMENT;
} else {
WFCMessage.GroupMember member = messagesStore.getGroupMember(message.getConversation().getTarget(), robot.getUid());
if(member == null || member.getType() == ProtoConstants.GroupMemberType.GroupMemberType_Removed) {
errorCode = ErrorCode.ERROR_CODE_NOT_IMPLEMENT;
}
}
}
if(errorCode != null) {
response.setStatus(HttpResponseStatus.OK);
RestResult result = RestResult.resultOf(errorCode);
response.setContent(gson.toJson(result));
return true;
}
SendMessageData sendMessageData = getSendMessageData(message, replyMessageData);
WFCMessage.Message newMsg = sendMessageData.toProtoMessage().toBuilder().setMessageId(message.getMessageId()).build();
sendApiRequest(response, IMTopic.RobotReplyMessageTopic, newMsg.toByteArray(), result -> {
ByteBuf byteBuf = Unpooled.buffer();
byteBuf.writeBytes(result);
ErrorCode ec = ErrorCode.fromCode(byteBuf.readByte());
if (ec == ErrorCode.ERROR_CODE_SUCCESS) {
long messageId = byteBuf.readLong();
long timestamp = byteBuf.readLong();
sendResponse(response, null, new SendMessageResult(messageId, timestamp));
} else {
sendResponse(response, ec, null);
}
});
return false;
} else {
setResponseContent(RestResult.resultOf(ErrorCode.INVALID_PARAMETER), response);
}
}
return true;
}
private @NotNull SendMessageData getSendMessageData(WFCMessage.Message message, ReplyMessageData replyMessageData) {
SendMessageData sendMessageData = new SendMessageData();
sendMessageData.setSender(robot.getUid());
Conversation conversation = new Conversation();
conversation.setType(message.getConversation().getType());
conversation.setLine(message.getConversation().getLine());
conversation.setTarget(message.getConversation().getTarget());
sendMessageData.setConv(conversation);
sendMessageData.setPayload(replyMessageData.getPayload());
if(replyMessageData.isOnly2Sender()) {
sendMessageData.setToUsers(Arrays.asList(message.getFromUser()));
}
return sendMessageData;
}
}
@@ -106,24 +106,6 @@ abstract public class RobotAction extends Action {
response.send();
}
protected <T> T getRequestBody(HttpRequest request, Class<T> cls) {
if (request instanceof FullHttpRequest) {
FullHttpRequest fullHttpRequest = (FullHttpRequest) request;
byte[] bytes = Utils.readBytesAndRewind(fullHttpRequest.content());
String content = null;
try {
content = new String(bytes, "UTF-8");
T t = gson.fromJson(content, cls);
return t;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
Utility.printExecption(LOG, e, IMExceptionEvent.EventType.ROBOT_API_Exception);
}
}
return null;
}
protected void sendApiRequest(Response response, String topic, byte[] message, Callback callback) {
ServerAPIHelper.sendRequest(robot.getUid(), null, topic, message, new ServerAPIHelper.Callback() {
@Override
@@ -80,6 +80,7 @@ public class UpdateProfileAction extends RobotAction {
RestResult result = RestResult.ok();
if(modified) {
try {
builder.setUpdateDt(System.currentTimeMillis());
messagesStore.updateUserInfo(builder.build());
} catch (Exception e) {
e.printStackTrace();
@@ -1,31 +1,32 @@
package com.xiaoleilu.loServer.exception;
import com.xiaoleilu.hutool.util.StrUtil;
/**
* 设置异常
* @author xiaoleilu
*/
public class ServerSettingException extends RuntimeException{
private static final long serialVersionUID = -4134588858314744501L;
public ServerSettingException(Throwable e) {
super(e);
}
public ServerSettingException(String message) {
super(message);
}
public ServerSettingException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public ServerSettingException(String message, Throwable throwable) {
super(message, throwable);
}
public ServerSettingException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}
package com.xiaoleilu.loServer.exception;
import cn.hutool.core.util.StrUtil;
/**
* 设置异常
* @author xiaoleilu
*/
public class ServerSettingException extends RuntimeException{
private static final long serialVersionUID = -4134588858314744501L;
public ServerSettingException(Throwable e) {
super(e);
}
public ServerSettingException(String message) {
super(message);
}
public ServerSettingException(String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params));
}
public ServerSettingException(String message, Throwable throwable) {
super(message, throwable);
}
public ServerSettingException(Throwable throwable, String messageTemplate, Object... params) {
super(StrUtil.format(messageTemplate, params), throwable);
}
}
@@ -1,7 +1,7 @@
package com.xiaoleilu.loServer.handler;
import cn.hutool.core.lang.Singleton;
import cn.wildfirechat.common.IMExceptionEvent;
import com.xiaoleilu.hutool.lang.Singleton;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.UnknownErrorAction;
@@ -15,6 +15,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.internal.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import win.liyufan.im.Utility;
@@ -34,7 +35,6 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest fullHttpRequest) throws Exception {
Logger.info("Http request whit url {}", fullHttpRequest.uri());
final Request request = Request.build(ctx, fullHttpRequest);
final Response response = Response.build(ctx, request);
@@ -109,7 +109,7 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
* @param response 响应对象
*/
private void doAction(ChannelHandlerContext ctx, Request request, Response response){
if(("/route".equalsIgnoreCase(request.getPath()) || request.getPath().startsWith("/fs/"))
if(("/route".equalsIgnoreCase(request.getPath()) || (!StringUtil.isNullOrEmpty(request.getPath()) && request.getPath().startsWith("/fs/")))
&& "OPTIONS".equalsIgnoreCase(request.getMethod())){
handleOptions(ctx, request, response);
return;
@@ -124,6 +124,9 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
Action action;
if (isValidePath(request.getPath())) {
action = ServerSetting.getAction(request.getPath(), request.getMethod().toUpperCase());
if (action != null) {
Logger.info("Http request whit url {}", request.getPath());
}
} else {
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
}
@@ -137,7 +140,11 @@ abstract public class ActionHandler extends SimpleChannelInboundHandler<FullHttp
action = ServerSetting.getAction(ServerSetting.MAPPING_ALL, request.getMethod());
if(null == action){
// 非Action方法,调用静态文件读取
action = Singleton.get(FileAction.class);
if(request.getPath().startsWith("/fs")) {
action = Singleton.get(FileAction.class);
} else {
action = ServerSetting.getErrorAction(ServerSetting.MAPPING_ERROR);
}
}
}
action.ctx = ctx;
@@ -8,7 +8,6 @@
package com.xiaoleilu.loServer.handler;
import com.xiaoleilu.hutool.lang.Singleton;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.FileAction;
@@ -1,28 +1,27 @@
package com.xiaoleilu.loServer.handler;
import com.xiaoleilu.hutool.log.StaticLog;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.FileRegion;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpContentCompressor;
/**
* 解决大文件传输与Gzip压缩冲突问题
* @author Looly
*
*/
public class HttpChunkContentCompressor extends HttpContentCompressor {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
StaticLog.debug("Write object [{}]", msg);
if (msg instanceof FileRegion || msg instanceof DefaultHttpResponse) {
//文件传输不经过Gzip压缩
ctx.write(msg, promise);
}else{
super.write(ctx, msg, promise);
}
}
}
package com.xiaoleilu.loServer.handler;
import cn.hutool.log.StaticLog;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.FileRegion;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpContentCompressor;
/**
* 解决大文件传输与Gzip压缩冲突问题
* @author Looly
*
*/
public class HttpChunkContentCompressor extends HttpContentCompressor {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
StaticLog.debug("Write object [{}]", msg);
if (msg instanceof FileRegion || msg instanceof DefaultHttpResponse) {
//文件传输不经过Gzip压缩
ctx.write(msg, promise);
}else{
super.write(ctx, msg, promise);
}
}
}
@@ -1,7 +1,6 @@
package com.xiaoleilu.loServer.handler;
import io.netty.util.internal.StringUtil;
import com.xiaoleilu.hutool.util.FileUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
@@ -8,7 +8,6 @@
package com.xiaoleilu.loServer.handler;
import com.xiaoleilu.hutool.lang.Singleton;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.action.Action;
import com.xiaoleilu.loServer.action.FileAction;
@@ -10,15 +10,12 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.xiaoleilu.hutool.http.HttpUtil;
import com.xiaoleilu.hutool.lang.Conver;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.CharsetUtil;
import com.xiaoleilu.hutool.util.DateUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import com.xiaoleilu.hutool.util.URLUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.net.NetUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames;
@@ -261,7 +258,7 @@ public class Request {
* @return 获得Integer类型请求参数
*/
public Integer getIntParam(String name, Integer defaultValue) {
return Conver.toInt(getParam(name), defaultValue);
return Convert.toInt(getParam(name), defaultValue);
}
/**
@@ -270,7 +267,7 @@ public class Request {
* @return 获得long类型请求参数
*/
public Long getLongParam(String name, Long defaultValue) {
return Conver.toLong(getParam(name), defaultValue);
return Convert.toLong(getParam(name), defaultValue);
}
/**
@@ -279,7 +276,7 @@ public class Request {
* @return 获得Double类型请求参数
*/
public Double getDoubleParam(String name, Double defaultValue) {
return Conver.toDouble(getParam(name), defaultValue);
return Convert.toDouble(getParam(name), defaultValue);
}
/**
@@ -288,7 +285,7 @@ public class Request {
* @return 获得Float类型请求参数
*/
public Float getFloatParam(String name, Float defaultValue) {
return Conver.toFloat(getParam(name), defaultValue);
return Convert.toFloat(getParam(name), defaultValue);
}
/**
@@ -297,7 +294,7 @@ public class Request {
* @return 获得Boolean类型请求参数
*/
public Boolean getBoolParam(String name, Boolean defaultValue) {
return Conver.toBool(getParam(name), defaultValue);
return Convert.toBool(getParam(name), defaultValue);
}
/**
@@ -477,7 +474,7 @@ public class Request {
protected void putIp(ChannelHandlerContext ctx) {
String ip = getHeader("X-Forwarded-For");
if (StrUtil.isNotBlank(ip)) {
ip = HttpUtil.getMultistageReverseProxyIp(ip);
ip = NetUtil.getMultistageReverseProxyIp(ip);
} else {
final InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
ip = insocket.getAddress().getHostAddress();
@@ -13,12 +13,9 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import com.xiaoleilu.hutool.http.HttpUtil;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import com.xiaoleilu.hutool.util.CharsetUtil;
import com.xiaoleilu.hutool.util.DateUtil;
import com.xiaoleilu.hutool.util.StrUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.xiaoleilu.loServer.ServerSetting;
import com.xiaoleilu.loServer.listener.FileProgressiveFutureListener;
@@ -44,6 +41,8 @@ import io.netty.handler.codec.http.cookie.ServerCookieEncoder;
import org.apache.tika.Tika;
import org.slf4j.LoggerFactory;
import static cn.hutool.core.date.DatePattern.HTTP_DATETIME_PATTERN;
/**
* 响应对象
*
@@ -332,7 +331,7 @@ public class Response {
* @param httpCacheSeconds 缓存时间,单位秒
*/
public void setDateAndCache(long lastModify, int httpCacheSeconds) {
SimpleDateFormat formatter = new SimpleDateFormat(DateUtil.HTTP_DATETIME_PATTERN, Locale.US);
SimpleDateFormat formatter = new SimpleDateFormat(HTTP_DATETIME_PATTERN, Locale.US);
formatter.setTimeZone(TimeZone.getTimeZone("GMT"));
// Date header
@@ -2,10 +2,7 @@ package com.xiaoleilu.loServer.listener;
import java.io.RandomAccessFile;
import com.xiaoleilu.hutool.io.IoUtil;
import com.xiaoleilu.hutool.log.Log;
import com.xiaoleilu.hutool.log.StaticLog;
import cn.hutool.core.io.IoUtil;
import io.netty.channel.ChannelProgressiveFuture;
import io.netty.channel.ChannelProgressiveFutureListener;
import org.slf4j.LoggerFactory;
@@ -29,6 +29,8 @@ public final class BrokerConstants {
public static final String HTTP_SERVER_PORT = "http_port";
public static final String HTTP_LOCAL_PORT = "local_port";
public static final String ENCRYPT_AES256 = "encrypt.use_aes256";
public static final String HTTP_ADMIN_PORT = "http.admin.port";
public static final String DEFAULT_MOQUETTE_STORE_MAP_DB_FILENAME = "moquette_store.mapdb";
public static final String DEFAULT_PERSISTENT_PATH = System.getProperty("user.dir") + File.separator
@@ -88,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";
@@ -110,13 +113,18 @@ public final class BrokerConstants {
public static final String TOKEN_SECRET_KEY = "token.key";
public static final String TOKEN_EXPIRE_TIME = "token.expire_time";
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";
public static final String SENSITIVE_Filter_Type = "sensitive.filter.type";
public static final String SENSITIVE_Only_Message = "sensitive.only_message";
public static final String SENSITIVE_Remote_Server_URL = "sensitive.remote_server_url";
public static final String SENSITIVE_Remote_Message_Type = "sensitive.remote_sensitive_message_type";
public static final String SENSITIVE_Remote_Fail_When_Matched = "sensitive.remote_fail_when_matched";
@@ -124,11 +132,13 @@ public final class BrokerConstants {
public static final String MESSAGE_Forward_Url = "message.forward.url";
public static final String MESSAGE_Sensitive_Forward_Url = "message.sensitive.forward.url";
public static final String MESSAGE_Forward_Types = "message.forward.types";
public static final String MESSAGE_Forward_Exclude_Types = "message.forward.exclude_types";
public static final String MESSAGE_MentionMsg_Forward_Url = "message.mentionmsg.forward.url";
public static final String MESSAGE_RecallMsg_Forward_Url = "message.recallmsg.forward.url";
public static final String SERVER_MULTI_ENDPOINT = "server.multi_endpoint";
public static final String SERVER_MULTI_PC_ENDPOINT = "server.multi_pc_endpoint";
public static final String SERVER_MULTI_PAD_ENDPOINT = "server.multi_pad_endpoint";
public static final String SERVER_MULTI_PLATFROM_NOTIFICATION = "server.multi_platform_notification";
public static final String SERVER_MOBILE_DEFAULT_SILENT_WHEN_PC_ONLINE = "server.mobile_default_silent_when_pc_online";
public static final String SERVER_CLIENT_SUPPORT_KICKOFF_EVENT = "server.client_support_kickoff_event";
@@ -141,31 +151,65 @@ public final class BrokerConstants {
public static final String MESSAGE_Max_Queue = "message.max_queue";
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";
public static final String MESSAGE_Push_Expired_Days = "message.push_expired_days";
public static final String MESSAGE_Force_Push_Types = "message.force_push_types";
public static final String MESSAGE_Forward_With_Client_Info = "message.forward_with_client_info";
public static final String MESSAGE_Forward_With_Sender_Info = "message.forward_with_sender_info";
public static final String MESSAGE_Forward_With_Target_Info = "message.forward_with_target_info";
public static final String BROADCAST_Target_From_User_Table = "message.broadcast.target_from_user_table";
public static final String MESSAGES_ALLOW_SEND_TO_FORBIDDEN_USER = "message.allow_send_to_forbidden_user";
public static final String ROBOT_Callback_With_Client_Info = "robot.callback_with_client_info";
public static final String ROBOT_Callback_With_Sender_Info = "robot.callback_with_sender_info";
public static final String ROBOT_Callback_With_Target_Info = "robot.callback_with_target_info";
public static final String ROBOT_Mention_External_Robot = "robot.mention_external_robot";
public static final String CHANNEL_Callback_With_Client_Info = "channel.callback_with_client_info";
public static final String CHANNEL_Callback_With_Sender_Info = "channel.callback_with_sender_info";
public static final String CHANNEL_Callback_With_Target_Info = "channel.callback_with_target_info";
public static final String CHANNEL_New_Callback_Feature = "channel.new_callback_feature";
public static final String FRIEND_Disable_Search = "friend.disable_search";
public static final String FRIEND_Disable_NickName_Search = "friend.disable_nick_name_search";
public static final String FRIEND_Disable_UserId_Search = "friend.disable_user_id_search";
public static final String FRIEND_Disable_Friend_Request = "friend.disable_friend_request";
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";
public static final String CHATROOM_Participant_Idle_Time = "chatroom.participant_idle_time";
public static final String CHATROOM_Rejoin_When_Active = "chatroom.rejoin_when_active";
public static final String CHATROOM_Create_When_Not_Exist = "chatroom.create_when_not_exist";
public static final String CHATROOM_Kickoff_Other_Platform = "chatroom.kickoff_other_platform";
public static final String GROUP_Allow_Owner_Recall_Self_Msg = "group.allow_owner_recall_self_msg";
public static final String GROUP_Allow_Manager_Recall_Self_Msg = "group.allow_manager_recall_self_msg";
public static final String GROUP_Allow_Client_Custom_Operation_Notification = "group.allow_client_custom_operation_notification";
public static final String GROUP_Allow_Robot_Custom_Operation_Notification = "group.allow_robot_custom_operation_notificatio";
public static final String GROUP_Visible_Quit_Kickoff_Notification = "group.visible_quit_or_kickoff_notification";
public static final String GROUP_Forbidden_Client_Operation = "group.forbidden_client_operation";
public static final String GROUP_Disable_Stranger_Invite = "group.disable_stranger_invite";
public static final String GROUP_ADD_MEMBER_ALLOW_PART_SUCCESS = "group.add_member_allow_part_success";
public static final String GROUP_INFO_MARK_DELETION = "group.enable_mark_deletion";
public static final String USER_HIDE_PROPERTIES = "user.hide_properties";
public static final String USER_KEEP_DISPLAY_NAME_WHEN_DESTROY = "user.keep_display_name_when_destroy";
@@ -189,6 +233,9 @@ public final class BrokerConstants {
public static final String HTTP_CHANNEL_RATE_LIMIT = "http.channel.rate_limit";
public static final String CLIENT_REQUEST_RATE_LIMIT = "client.request_rate_limit";
public static final String HTTP_CLOSE_API_VERSION = "http.close_api_version";
private BrokerConstants() {
}
}
@@ -24,14 +24,17 @@ 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) {
publisher.publishNotification(IMTopic.NotifyFriendRequestTopic, request.getTargetUid(), head[0], fromUser, request.getReason());
} else if(user != null && user.getType() == ProtoConstants.UserType.UserType_Robot) {
WFCMessage.HandleFriendRequest handleFriendRequest = WFCMessage.HandleFriendRequest.newBuilder().setTargetUid(fromUser).setStatus(ProtoConstants.FriendRequestStatus.RequestStatus_Accepted).build();
mServer.onApiMessage(request.getTargetUid(), null, handleFriendRequest.toByteArray(), 0, fromUser, HandleFriendRequestTopic, requestSourceType);
if(m_messagesStore.isRobotAutoAcceptFriendRequest()) {
WFCMessage.HandleFriendRequest handleFriendRequest = WFCMessage.HandleFriendRequest.newBuilder().setTargetUid(fromUser).setStatus(ProtoConstants.FriendRequestStatus.RequestStatus_Accepted).build();
mServer.onApiMessage(request.getTargetUid(), null, handleFriendRequest.toByteArray(), 0, fromUser, HandleFriendRequestTopic, requestSourceType);
}
}
}
return errorCode;
@@ -16,6 +16,10 @@ import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(value = IMTopic.AddGroupMemberTopic)
@@ -24,16 +28,48 @@ public class AddGroupMember extends GroupHandler<WFCMessage.AddGroupMemberReques
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.AddGroupMemberRequest request, Qos1PublishHandler.IMCallback callback) {
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
ErrorCode errorCode = m_messagesStore.addGroupMembers(fromUser, isAdmin, request.getGroupId(), request.getAddedMemberList(), request.getExtra());
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if(request.getAddedMemberList().size() == 1 && request.getAddedMember(0).getMemberId().equals(fromUser)) {
if ((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Join_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
} else {
if ((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Invite_Group_Member) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
}
Map<String, Integer> failedMembers = new HashMap<>();
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
ErrorCode errorCode = m_messagesStore.canAddGroupMembers(fromUser, request.getAddedMemberList(), failedMembers);
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
}
ErrorCode errorCode = m_messagesStore.addGroupMembers(fromUser, isAdmin, request.getGroupId(), request.getAddedMemberList(), request.getExtra(), failedMembers);
if (errorCode == ERROR_CODE_SUCCESS) {
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();
List<String> added = getMemberIdList(request.getAddedMemberList());
added.removeAll(failedMembers.keySet());
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, added).setExtra(request.getExtra()).getAddGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
}
if (errorCode == ERROR_CODE_SUCCESS && !failedMembers.isEmpty()) {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").setMi(failedMembers).getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_REJECT_JOIN_GROUP);
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
if(errorCode == ERROR_CODE_SUCCESS && !failedMembers.isEmpty()) {
return ErrorCode.ERROR_CODE_PARTLY_SUCCESS;
}
return errorCode;
}
}
@@ -0,0 +1,23 @@
package io.moquette.imhandler;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import java.util.List;
@Handler(IMTopic.ListenedChannelListTopic)
public class ChannelListenedListHandler extends IMHandler<Void> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, Void request, Qos1PublishHandler.IMCallback callback) {
List<String> channelInfoList = m_messagesStore.getListenedChannels(fromUser);
WFCMessage.IDListBuf.Builder builder = WFCMessage.IDListBuf.newBuilder();
builder.addAllId(channelInfoList);
byte[] data = builder.build().toByteArray();
ackPayload.ensureWritable(data.length).writeBytes(data);
return ErrorCode.ERROR_CODE_SUCCESS;
}
}
@@ -16,6 +16,7 @@ import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import static cn.wildfirechat.proto.ProtoConstants.RequestSourceType.Request_From_Admin;
import static win.liyufan.im.IMTopic.PutUserSettingTopic;
import static win.liyufan.im.UserSettingScope.kUserSettingMyChannels;
@@ -42,7 +43,13 @@ public class CreateChannelHandler extends GroupHandler<WFCMessage.ChannelInfo> {
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
WFCMessage.ModifyUserSettingReq modifyUserSettingReq = WFCMessage.ModifyUserSettingReq.newBuilder().setScope(kUserSettingMyChannels).setKey(request.getTargetId()).setValue("1").build();
mServer.onApiMessage(fromUser, null, modifyUserSettingReq.toByteArray(), 0, fromUser, PutUserSettingTopic, requestSourceType);
byte[] data = request.getTargetId().getBytes();
byte[] data;
if (requestSourceType == Request_From_Admin) {
//When server api create channel, need return channelId and secret together.
data = (request.getTargetId() + "|" + request.getSecret()).getBytes();
} else {
data = request.getTargetId().getBytes();
}
ackPayload.ensureWritable(data.length).writeBytes(data);
return ErrorCode.ERROR_CODE_SUCCESS;
} else {
@@ -17,17 +17,21 @@ import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Handler(value = IMTopic.CreateGroupTopic)
public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.CreateGroupRequest request, Qos1PublishHandler.IMCallback callback) {
if(request.getGroup().getGroupInfo().getType() < 0 || request.getGroup().getGroupInfo().getType() > 2) {
if(request.getGroup().getGroupInfo().getType() < 0 || request.getGroup().getGroupInfo().getType() > 3) {
return ErrorCode.ERROR_CODE_INVALID_DATA;
}
if (!StringUtil.isNullOrEmpty(request.getGroup().getGroupInfo().getTargetId())) {
WFCMessage.GroupInfo existGroupInfo = m_messagesStore.getGroupInfo(request.getGroup().getGroupInfo().getTargetId());
if (existGroupInfo != null) {
if (existGroupInfo != null && existGroupInfo.getDeleted() == 0) {
return ErrorCode.ERROR_CODE_GROUP_ALREADY_EXIST;
}
}
@@ -40,17 +44,65 @@ public class CreateGroupHandler extends GroupHandler<WFCMessage.CreateGroupReque
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
WFCMessage.GroupInfo groupInfo = m_messagesStore.createGroup(fromUser, request.getGroup().getGroupInfo(), request.getGroup().getMembersList(), request.getMemberExtra(), isAdmin);
if (groupInfo != null) {
if(!isAdmin && request.getGroup().getGroupInfo().getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Create_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
Map<String, Integer> failedMembers = new HashMap<>();
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
ErrorCode errorCode = m_messagesStore.canAddGroupMembers(fromUser, request.getGroup().getMembersList(), failedMembers);
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
}
if (request.getGroup().getGroupInfo().getTargetId().length() > 64
|| request.getGroup().getGroupInfo().getName().length() > 64
|| request.getGroup().getGroupInfo().getPortrait().length() > 1024) {
return ErrorCode.INVALID_PARAMETER;
}
if(!isAdmin && !StringUtil.isNullOrEmpty(request.getGroup().getGroupInfo().getName())) {
if(!m_messagesStore.isSensitiveOnlyMessage()) {
Set<String> matched = m_messagesStore.handleSensitiveWord(request.getGroup().getGroupInfo().getName());
if (matched != null && !matched.isEmpty()) {
return ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
}
}
if(!m_messagesStore.isAllowName(request.getGroup().getGroupInfo().getName())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
WFCMessage.GroupInfo groupInfo = m_messagesStore.createGroup(fromUser, request.getGroup().getGroupInfo(), request.getGroup().getMembersList(), request.getMemberExtra(), isAdmin, failedMembers);
if (groupInfo != null && groupInfo.getDeleted() == 0) {
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);
}
if(!failedMembers.isEmpty()) {
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(groupInfo.getTargetId(), fromUser, null, "").setMi(failedMembers).getGroupNotifyContent(ProtoConstants.MESSAGE_CONTENT_TYPE_REJECT_JOIN_GROUP);
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), content);
}
}
byte[] data = groupInfo.getTargetId().getBytes();
ackPayload.ensureWritable(data.length).writeBytes(data);
return ErrorCode.ERROR_CODE_SUCCESS;
if(failedMembers.isEmpty()) {
return ErrorCode.ERROR_CODE_SUCCESS;
} else {
return ErrorCode.ERROR_CODE_PARTLY_SUCCESS;
}
}
}
@@ -26,7 +26,6 @@ public class DismissGroupHandler extends GroupHandler<WFCMessage.DismissGroupReq
ErrorCode errorCode;
if (groupInfo == null) {
errorCode = m_messagesStore.dismissGroup(fromUser, request.getGroupId(), isAdmin);
} else if (isAdmin || (groupInfo.getType() == ProtoConstants.GroupType.GroupType_Normal || groupInfo.getType() == ProtoConstants.GroupType.GroupType_Restricted)
&& groupInfo.getOwner() != null && groupInfo.getOwner().equals(fromUser)) {
@@ -38,6 +37,13 @@ public class DismissGroupHandler extends GroupHandler<WFCMessage.DismissGroupReq
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Dismiss_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
//send notify message first, then dismiss group
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
@@ -0,0 +1,33 @@
/*
* 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 io.moquette.imhandler;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import java.util.Set;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.GetCommonGroupsTopic)
public class GetCommonGroupsHandler extends IMHandler<WFCMessage.IDBuf> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.IDBuf request, Qos1PublishHandler.IMCallback callback) {
WFCMessage.PullUserResult.Builder resultBuilder = WFCMessage.PullUserResult.newBuilder();
Set<String> strings = m_messagesStore.getCommonGroupIds(fromUser, request.getId());
WFCMessage.IDListBuf idListBuf = WFCMessage.IDListBuf.newBuilder().addAllId(strings).build();
byte[] data = idListBuf.toByteArray();
ackPayload.ensureWritable(data.length).writeBytes(data);
return ERROR_CODE_SUCCESS;
}
}
@@ -21,7 +21,7 @@ import java.util.List;
public class GetGroupInfoHandler extends IMHandler<WFCMessage.PullUserRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.PullUserRequest request, Qos1PublishHandler.IMCallback callback) {
List<WFCMessage.GroupInfo> infos = m_messagesStore.getGroupInfos(request.getRequestList());
List<WFCMessage.GroupInfo> infos = m_messagesStore.getGroupInfos(request.getRequestList(), fromUser, requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin);
WFCMessage.PullGroupInfoResult result = WFCMessage.PullGroupInfoResult.newBuilder().addAllInfo(infos).build();
byte[] data = result.toByteArray();
@@ -0,0 +1,33 @@
/*
* 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 io.moquette.imhandler;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import java.util.Set;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.GetMyGroupsTopic)
public class GetMyGroupsHandler extends IMHandler<Void> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, Void request, Qos1PublishHandler.IMCallback callback) {
WFCMessage.PullUserResult.Builder resultBuilder = WFCMessage.PullUserResult.newBuilder();
Set<String> strings = m_messagesStore.getUserGroupIds(fromUser);
WFCMessage.IDListBuf idListBuf = WFCMessage.IDListBuf.newBuilder().addAllId(strings).build();
byte[] data = idListBuf.toByteArray();
ackPayload.ensureWritable(data.length).writeBytes(data);
return ERROR_CODE_SUCCESS;
}
}
@@ -10,7 +10,6 @@ package io.moquette.imhandler;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.xiaoleilu.hutool.system.UserInfo;
import io.moquette.persistence.MemorySessionStore;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.moquette.spi.impl.security.TokenAuthenticator;
@@ -29,6 +29,10 @@ public class KickoffGroupMember extends GroupHandler<WFCMessage.RemoveGroupMembe
errorCode = ErrorCode.ERROR_CODE_NOT_EXIST;
} else {
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
if (!isAdmin && groupInfo.getType() == ProtoConstants.GroupType.GroupType_Organization) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
boolean isAllow = isAdmin;
if (!isAllow) {
if (groupInfo.getOwner() != null) {
@@ -58,6 +62,13 @@ public class KickoffGroupMember extends GroupHandler<WFCMessage.RemoveGroupMembe
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Kickoff_Group_Member) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
//send notify message first, then kickoff the member
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
sendGroupNotification(fromUser, groupInfo.getTargetId(), request.getToLineList(), request.getNotifyContent());
@@ -11,11 +11,14 @@ package io.moquette.imhandler;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import cn.wildfirechat.pojos.GroupNotificationBinaryContent;
import com.hazelcast.util.StringUtil;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import cn.wildfirechat.common.ErrorCode;
import win.liyufan.im.IMTopic;
import java.util.Set;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.ModifyGroupAliasTopic)
@@ -26,10 +29,24 @@ public class ModifyGroupAliasHandler extends GroupHandler<WFCMessage.ModifyGroup
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0 && requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !m_messagesStore.isAllowClientCustomGroupNotification()) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0 && requestSourceType == ProtoConstants.RequestSourceType.Request_From_Robot && !m_messagesStore.isAllowRobotCustomGroupNotification()) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if (!isAdmin && !StringUtil.isNullOrEmpty(request.getAlias())) {
if(!m_messagesStore.isSensitiveOnlyMessage()) {
Set<String> matched = m_messagesStore.handleSensitiveWord(request.getAlias());
if (matched != null && !matched.isEmpty()) {
return ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
}
}
if(!m_messagesStore.isAllowName(request.getAlias())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
ErrorCode errorCode = m_messagesStore.modifyGroupMemberAlias(fromUser, request.getGroupId(), request.getAlias(), null, isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent()&& request.getNotifyContent().getType() > 0) {
@@ -31,6 +31,19 @@ public class ModifyGroupInfoHandler extends GroupHandler<WFCMessage.ModifyGroupI
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if(request.getType() == Modify_Group_Mute) {
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Mute_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
} else {
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Modify_Group_Info) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
}
ErrorCode errorCode= m_messagesStore.modifyGroupInfo(fromUser, request.getGroupId(), request.getType(), request.getValue(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
@@ -12,10 +12,13 @@ import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.GroupNotificationBinaryContent;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.hazelcast.util.StringUtil;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import win.liyufan.im.IMTopic;
import java.util.Set;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(IMTopic.ModifyGroupMemberAliasTopic)
@@ -29,6 +32,18 @@ public class ModifyGroupMemberAliasHandler extends GroupHandler<WFCMessage.Modif
if(request.hasNotifyContent() && request.getNotifyContent().getType() > 0 && requestSourceType == ProtoConstants.RequestSourceType.Request_From_Robot && !m_messagesStore.isAllowRobotCustomGroupNotification()) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if (!isAdmin && !StringUtil.isNullOrEmpty(request.getAlias())) {
if(!m_messagesStore.isSensitiveOnlyMessage()) {
Set<String> matched = m_messagesStore.handleSensitiveWord(request.getAlias());
if (matched != null && !matched.isEmpty()) {
return ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
}
}
if(!m_messagesStore.isAllowName(request.getAlias())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
ErrorCode errorCode = m_messagesStore.modifyGroupMemberAlias(fromUser, request.getGroupId(), request.getAlias(), request.getMemberId(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
@@ -31,18 +31,25 @@ public class QuitGroupHandler extends GroupHandler<WFCMessage.QuitGroupRequest>
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
ErrorCode errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId());
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Quit_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
ErrorCode errorCode = m_messagesStore.quitGroup(fromUser, request.getGroupId(), isAdmin);
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
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, "").getQuitGroupNotifyContent();
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getKeepMsg()>0?"1":"0", "").getQuitGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content);
}
if((m_messagesStore.getVisibleQuitKickoffNotification() & 0x01) > 0) {
Set<String> toUsers = m_messagesStore.getGroupManagers(request.getGroupId(), true);
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, null, "").getQuitVisibleGroupNotifyContent();
WFCMessage.MessageContent content = new GroupNotificationBinaryContent(request.getGroupId(), fromUser, request.getKeepMsg()>0?"1":"0", "").getQuitVisibleGroupNotifyContent();
sendGroupNotification(fromUser, request.getGroupId(), request.getToLineList(), content, toUsers);
}
}
@@ -23,7 +23,7 @@ public class RecallMessageHandler extends IMHandler<WFCMessage.INT64Buf> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.INT64Buf int64Buf, Qos1PublishHandler.IMCallback callback) {
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
ErrorCode errorCode = m_messagesStore.recallMessage(int64Buf.getId(), fromUser, clientID, isAdmin);
ErrorCode errorCode = m_messagesStore.recallMessage(int64Buf.getId(), fromUser, clientID, isAdmin, ackPayload);
if(errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
@@ -0,0 +1,348 @@
/*
* 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 io.moquette.imhandler;
import cn.wildfirechat.common.ErrorCode;
import cn.wildfirechat.pojos.MessagePayload;
import cn.wildfirechat.proto.ProtoConstants;
import cn.wildfirechat.proto.WFCMessage;
import com.hazelcast.util.StringUtil;
import io.moquette.BrokerConstants;
import io.moquette.spi.impl.Qos1PublishHandler;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import win.liyufan.im.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_NOT_IN_GROUP;
import static cn.wildfirechat.common.ErrorCode.ERROR_CODE_SUCCESS;
@Handler(value = IMTopic.RobotReplyMessageTopic)
public class ReplyMessageHandler extends IMHandler<WFCMessage.Message> {
private int mSensitiveType = 0; //命中敏感词时,0 失败,1 吞掉, 2 敏感词替换成*。
private String mForwardUrl = null;
private String mSensitiveMessageForwardUrl = null;
private Set<Integer> mForwardMessageTypes = new HashSet<>();
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 boolean mAllowSend2ForbiddenUser = false;
private String mRemoteSensitiveServerUrl = null;
private Set<Integer> mRemoteSensitiveMessageTypes;
private boolean mWaitRemoteSensitiveServerResponse = false;
public ReplyMessageHandler() {
super();
mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
if (!StringUtil.isNullOrEmpty(mForwardUrl)) {
String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types);
parseTypes(mForwardMessageTypes, forwardTypes);
String excludeTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Exclude_Types);
parseTypes(mForwardExcludeMessageTypes, excludeTypes);
}
mSensitiveMessageForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Sensitive_Forward_Url);
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
try {
mSensitiveType = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Filter_Type));
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
mBlacklistStrategy = Integer.parseInt(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Blacklist_Strategy));
} catch (Exception e) {
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)) {
mWaitRemoteSensitiveServerResponse = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Fail_When_Matched, "false"));
String types = mServer.getConfig().getProperty(BrokerConstants.SENSITIVE_Remote_Message_Type, "");
mRemoteSensitiveMessageTypes = new HashSet<>();
if(!StringUtil.isNullOrEmpty(types)) {
String[] ts = types.split(",");
for (String t:ts) {
mRemoteSensitiveMessageTypes.add(Integer.parseInt(t.trim()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
mNoForwardAdminMessage = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGE_NO_Forward_Admin_Message, "false"));
} catch (Exception e) {
e.printStackTrace();
Utility.printExecption(LOG, e);
}
try {
mAllowSend2ForbiddenUser = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGES_ALLOW_SEND_TO_FORBIDDEN_USER, "false"));
} catch (Exception e) {}
}
private void parseTypes(Collection<Integer> collection, String types) {
try {
if (!StringUtil.isNullOrEmpty(types)) {
String[] tss = types.split(",");
for (String ts:tss) {
if(ts.contains("-")) {
String[] ss = ts.split("-");
if(ss.length == 2) {
int begin = Integer.parseInt(ss[0]);
int end = Integer.parseInt(ss[1]);
for (int i = begin; i <= end ; i++) {
collection.add(i);
}
}
} else {
collection.add(Integer.parseInt(ts.trim()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.Message message, Qos1PublishHandler.IMCallback callback) {
if(requestSourceType != ProtoConstants.RequestSourceType.Request_From_Robot) {
return ErrorCode.ERROR_CODE_NOT_IMPLEMENT;
}
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
if (message != null) {
if(message.getContent().getContent().length() +
message.getContent().getSearchableContent().length() +
message.getContent().getData().size() +
message.getContent().getExtra().length() +
message.getContent().getPushContent().length() +
message.getContent().getPushData().length() > 64 * 1024) {
return ErrorCode.ERROR_CODE_MESSAGE_TOO_LARGE;
}
boolean ignoreMsg = false;
// 不能在端上直接发送撤回和群通知
if (message.getContent().getType() == 80 || message.getContent().getType() == 81 || (message.getContent().getType() >= 100 && message.getContent().getType() < 200)) {
return ErrorCode.INVALID_PARAMETER;
}
if(m_messagesStore.getClientForbiddenSendTypes().contains(message.getContent().getType())) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
int userStatus = m_messagesStore.getUserStatus(fromUser);
if (userStatus == ProtoConstants.UserStatus.Muted || userStatus == ProtoConstants.UserStatus.Forbidden) {
if(!m_messagesStore.getGlobalMuteExceptionTypes().contains(message.getContent().getType())) {
return ErrorCode.ERROR_CODE_FORBIDDEN_SEND_MSG;
}
}
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private) {
if(!m_messagesStore.getBlackListExceptionTypes().contains(message.getContent().getType())) {
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;
errorCode = ErrorCode.ERROR_CODE_SUCCESS;
} else {
return errorCode;
}
}
if(!mBlacklistAllowSend2Black) {
errorCode = m_messagesStore.isBlacked(fromUser, message.getConversation().getTarget());
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
}
}
if (!mAllowSend2ForbiddenUser) {
userStatus = m_messagesStore.getUserStatus(message.getConversation().getTarget());
if (userStatus == ProtoConstants.UserStatus.Forbidden) {
return ErrorCode.ERROR_CODE_USER_FORBIDDEN;
}
}
}
if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group ) {
if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) {
errorCode = m_messagesStore.canSendMessageInGroup(fromUser, message.getConversation().getTarget());
if(errorCode == ERROR_CODE_NOT_IN_GROUP) {
errorCode = ERROR_CODE_SUCCESS;
}
if (errorCode != ErrorCode.ERROR_CODE_SUCCESS) {
return errorCode;
}
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_ChatRoom) {
if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) {
if (!m_messagesStore.checkUserClientInChatroom(fromUser, clientID, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHATROOM;
}
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
if(!m_messagesStore.getGroupMuteExceptionTypes().contains(message.getContent().getType())) {
if (!m_messagesStore.canSendMessageInChannel(fromUser, message.getConversation().getTarget())) {
return ErrorCode.ERROR_CODE_NOT_IN_CHANNEL;
}
}
}
if(message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Private) {
long beforeMessageId = message.getMessageId();
WFCMessage.Message replyMsg = m_messagesStore.getMessage(beforeMessageId);
message = message.toBuilder().setConversation(message.getConversation().toBuilder().setTarget(replyMsg.getFromUser() + "|" + replyMsg.getConversation().getTarget())).build();
}
long timestamp = System.currentTimeMillis();
final long messageId;
try {
messageId = MessageShardingUtil.generateId();
} catch (Exception e) {
e.printStackTrace();
return ErrorCode.ERROR_CODE_SERVER_ERROR;
}
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !mForwardExcludeMessageTypes.contains(message.getContent().getType())) {
publisher.forwardMessage(message, mForwardUrl, clientID);
}
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0) {
publisher.forwardMessage(message, mMentionForwardUrl, clientID);
}
if(StringUtil.isNullOrEmpty(mRemoteSensitiveServerUrl)) {
Set<String> matched = m_messagesStore.handleSensitiveWord(message.getContent().getSearchableContent());
if (matched != null && !matched.isEmpty()) {
m_messagesStore.storeSensitiveMessage(message);
if (!StringUtil.isNullOrEmpty(mSensitiveMessageForwardUrl)) {
publisher.forwardMessage(message, mSensitiveMessageForwardUrl, clientID);
}
if (mSensitiveType == 0) {
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
} else if (mSensitiveType == 1) {
ignoreMsg = true;
} else if (mSensitiveType == 2) {
String text = message.getContent().getSearchableContent();
for (String word : matched) {
text = text.replace(word, "***");
}
message = message.toBuilder().setContent(message.getContent().toBuilder().setSearchableContent(text).build()).build();
}
}
} else {
if(mRemoteSensitiveMessageTypes.contains(message.getContent().getType())) {
final WFCMessage.Message finalMsg = message;
publisher.forwardMessageWithCallback(message, clientID, mRemoteSensitiveServerUrl, new HttpUtils.HttpCallback() {
@Override
public void onSuccess(String content) {
if(StringUtil.isNullOrEmpty(content)) {
saveAndPublish(fromUser, clientID, finalMsg, requestSourceType);
} else {
MessagePayload payload = GsonUtil.gson.fromJson(content, MessagePayload.class);
if (payload != null && payload.getType() > 0) {
WFCMessage.Message newMsg = finalMsg.toBuilder().setContent(payload.toProtoMessageContent()).build();
saveAndPublish(fromUser, clientID, newMsg, requestSourceType);
} else {
LOG.error("Response content {} from censor is invalid payload, ignore response and send original message", content);
saveAndPublish(fromUser, clientID, finalMsg, requestSourceType);
}
}
if(mWaitRemoteSensitiveServerResponse) {
ByteBuf ackPayload = Unpooled.buffer(21);
ErrorCode errorCode = ERROR_CODE_SUCCESS;
ackPayload.writeByte(errorCode.getCode());
ackPayload.writeLong(messageId);
ackPayload.writeLong(timestamp);
callback.onIMHandled(ErrorCode.ERROR_CODE_SUCCESS, ackPayload);
}
}
@Override
public void onFailure(int statusCode, String errorMessage) {
ByteBuf ackPayload = null;
ErrorCode errorCode = ERROR_CODE_SUCCESS;
if(statusCode == 403) {
if(mWaitRemoteSensitiveServerResponse) {
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
ackPayload = Unpooled.buffer(1);
ackPayload.writeByte(errorCode.getCode());
}
} else {
LOG.warn("Failed to censor message with status {}, errorMessage {}. Send the original message", statusCode, errorMessage);
saveAndPublish(fromUser, clientID, finalMsg, requestSourceType);
if(mWaitRemoteSensitiveServerResponse) {
errorCode = ERROR_CODE_SUCCESS;
ackPayload = Unpooled.buffer(21);
ackPayload.writeByte(errorCode.getCode());
ackPayload.writeLong(messageId);
ackPayload.writeLong(timestamp);
}
}
if(mWaitRemoteSensitiveServerResponse) {
callback.onIMHandled(errorCode, ackPayload);
}
}
});
if(mWaitRemoteSensitiveServerResponse) {
ackPayload.clear();
return ErrorCode.INVALID_ASYNC_HANDLING;
} else {
ackPayload = ackPayload.capacity(20);
ackPayload.writeLong(messageId);
ackPayload.writeLong(timestamp);
return ErrorCode.ERROR_CODE_SUCCESS;
}
}
}
if (errorCode == ErrorCode.ERROR_CODE_SUCCESS) {
if(!ignoreMsg) {
saveAndPublish(fromUser, clientID, message, requestSourceType);
}
ackPayload = ackPayload.capacity(20);
ackPayload.writeLong(messageId);
ackPayload.writeLong(timestamp);
}
} else {
errorCode = ErrorCode.ERROR_CODE_INVALID_MESSAGE;
}
return errorCode;
}
}
@@ -40,6 +40,11 @@ public class RouteHandler extends IMHandler<WFCMessage.RouteRequest> {
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
if(request.getPlatform() != session.getPlatform() && m_messagesStore.existSignatures()) {
LOG.error("Session <{}, {}> platform is {} mismatch the request {}", session.getUsername(), session.getClientID(), session.getPlatform(), request.getPlatform());
return ErrorCode.ERROR_CODE_SECRECT_KEY_MISMATCH;
}
String serverIp = mServer.getServerIp();
String longPort = mServer.getLongPort();
String shortPort = mServer.getShortPort();
@@ -22,6 +22,7 @@ import cn.wildfirechat.common.ErrorCode;
import io.netty.buffer.Unpooled;
import win.liyufan.im.*;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
@@ -33,9 +34,12 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
private String mForwardUrl = null;
private String mSensitiveMessageForwardUrl = null;
private Set<Integer> mForwardMessageTypes = new HashSet<>();
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 boolean mAllowSend2ForbiddenUser = false;
private String mRemoteSensitiveServerUrl = null;
private Set<Integer> mRemoteSensitiveMessageTypes;
@@ -47,12 +51,9 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
mForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Url);
if (!StringUtil.isNullOrEmpty(mForwardUrl)) {
String forwardTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Types);
if (!StringUtil.isNullOrEmpty(forwardTypes)) {
String[] tss = forwardTypes.split(",");
for (String ts:tss) {
mForwardMessageTypes.add(Integer.parseInt(ts.trim()));
}
}
parseTypes(mForwardMessageTypes, forwardTypes);
String excludeTypes = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Forward_Exclude_Types);
parseTypes(mForwardExcludeMessageTypes, excludeTypes);
}
mSensitiveMessageForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_Sensitive_Forward_Url);
mMentionForwardUrl = mServer.getConfig().getProperty(BrokerConstants.MESSAGE_MentionMsg_Forward_Url);
@@ -70,6 +71,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)) {
@@ -95,6 +102,33 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
Utility.printExecption(LOG, e);
}
try {
mAllowSend2ForbiddenUser = Boolean.parseBoolean(mServer.getConfig().getProperty(BrokerConstants.MESSAGES_ALLOW_SEND_TO_FORBIDDEN_USER, "false"));
} catch (Exception e) {}
}
private void parseTypes(Collection<Integer> collection, String types) {
try {
if (!StringUtil.isNullOrEmpty(types)) {
String[] tss = types.split(",");
for (String ts:tss) {
if(ts.contains("-")) {
String[] ss = ts.split("-");
if(ss.length == 2) {
int begin = Integer.parseInt(ss[0]);
int end = Integer.parseInt(ss[1]);
for (int i = begin; i <= end ; i++) {
collection.add(i);
}
}
} else {
collection.add(Integer.parseInt(ts.trim()));
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@@ -102,6 +136,15 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
ErrorCode errorCode = ErrorCode.ERROR_CODE_SUCCESS;
boolean isAdmin = requestSourceType == ProtoConstants.RequestSourceType.Request_From_Admin;
if (message != null) {
if(message.getContent().getContent().length() +
message.getContent().getSearchableContent().length() +
message.getContent().getData().size() +
message.getContent().getExtra().length() +
message.getContent().getPushContent().length() +
message.getContent().getPushData().length() > 64 * 1024) {
return ErrorCode.ERROR_CODE_MESSAGE_TOO_LARGE;
}
boolean ignoreMsg = false;
if (!isAdmin) { //admin do not check the right
// 不能在端上直接发送撤回和群通知
@@ -122,7 +165,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;
@@ -131,11 +174,20 @@ 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());
if (userStatus == ProtoConstants.UserStatus.Forbidden) {
return ErrorCode.ERROR_CODE_USER_FORBIDDEN;
if (!mAllowSend2ForbiddenUser) {
userStatus = m_messagesStore.getUserStatus(message.getConversation().getTarget());
if (userStatus == ProtoConstants.UserStatus.Forbidden) {
return ErrorCode.ERROR_CODE_USER_FORBIDDEN;
}
}
}
@@ -159,6 +211,15 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
}
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Group) {
if(m_messagesStore.getGroupInfo(message.getConversation().getTarget()) == null) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
} else if (message.getConversation().getType() == ProtoConstants.ConversationType.ConversationType_Channel) {
WFCMessage.ChannelInfo channelData = m_messagesStore.getChannelInfo(message.getConversation().getTarget());
if(channelData == null || channelData.getStatus() == ProtoConstants.ChannelState.Channel_State_Mask_Deleted) {
return ErrorCode.ERROR_CODE_NOT_EXIST;
}
}
long timestamp = System.currentTimeMillis();
@@ -171,20 +232,13 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
}
message = message.toBuilder().setFromUser(fromUser).setMessageId(messageId).setServerTimestamp(timestamp).build();
OutputClient outputClient = null;
if(m_messagesStore.isForwardMessageWithClientInfo() && requestSourceType == ProtoConstants.RequestSourceType.Request_From_User && !StringUtil.isNullOrEmpty(clientID)) {
MemorySessionStore.Session session = m_sessionsStore.getSession(clientID);
if(session != null && session.getUsername().equals(fromUser)) {
outputClient = new OutputClient(session.getPlatform(), clientID);
}
}
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage)) {
publisher.forwardMessage(message, mForwardUrl, outputClient);
if (!StringUtil.isNullOrEmpty(mForwardUrl) && (mForwardMessageTypes.isEmpty() || mForwardMessageTypes.contains(message.getContent().getType())) && !(isAdmin && mNoForwardAdminMessage) && !mForwardExcludeMessageTypes.contains(message.getContent().getType())) {
publisher.forwardMessage(message, mForwardUrl, clientID);
}
if(!StringUtil.isNullOrEmpty(mMentionForwardUrl) && message.hasContent() && message.getContent().getMentionedType() != 0 && !(isAdmin && mNoForwardAdminMessage)) {
publisher.forwardMessage(message, mMentionForwardUrl, outputClient);
publisher.forwardMessage(message, mMentionForwardUrl, clientID);
}
if (!isAdmin) {
@@ -193,7 +247,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
if (matched != null && !matched.isEmpty()) {
m_messagesStore.storeSensitiveMessage(message);
if (!StringUtil.isNullOrEmpty(mSensitiveMessageForwardUrl)) {
publisher.forwardMessage(message, mSensitiveMessageForwardUrl, outputClient);
publisher.forwardMessage(message, mSensitiveMessageForwardUrl, clientID);
}
if (mSensitiveType == 0) {
errorCode = ErrorCode.ERROR_CODE_SENSITIVE_MATCHED;
@@ -210,7 +264,7 @@ public class SendMessageHandler extends IMHandler<WFCMessage.Message> {
} else {
if(mRemoteSensitiveMessageTypes.contains(message.getContent().getType())) {
final WFCMessage.Message finalMsg = message;
publisher.forwardMessageWithCallback(message, mRemoteSensitiveServerUrl, new HttpUtils.HttpCallback() {
publisher.forwardMessageWithCallback(message, clientID, mRemoteSensitiveServerUrl, new HttpUtils.HttpCallback() {
@Override
public void onSuccess(String content) {
if(StringUtil.isNullOrEmpty(content)) {
@@ -30,6 +30,13 @@ public class SetGroupManagerHandler extends GroupHandler<WFCMessage.SetGroupMana
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Set_Group_Manage) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
ErrorCode errorCode = m_messagesStore.setGroupManager(fromUser, request.getGroupId(), request.getType(), request.getUserIdList(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
@@ -30,6 +30,13 @@ public class TransferGroupHandler extends GroupHandler<WFCMessage.TransferGroupR
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
if(requestSourceType == ProtoConstants.RequestSourceType.Request_From_User) {
int forbiddenClientOperation = m_messagesStore.getGroupForbiddenClientOperation();
if((forbiddenClientOperation & ProtoConstants.ForbiddenClientGroupOperationMask.Forbidden_Transfer_Group) > 0) {
return ErrorCode.ERROR_CODE_NOT_RIGHT;
}
}
ErrorCode errorCode = m_messagesStore.transferGroup(fromUser, request.getGroupId(), request.getNewOwner(), isAdmin);
if (errorCode == ERROR_CODE_SUCCESS) {
if (request.hasNotifyContent() && request.getNotifyContent().getType() > 0) {
@@ -25,9 +25,11 @@ public class UploadDeviceTokenHandler extends IMHandler<WFCMessage.UploadDeviceT
session.setPlatform(request.getPlatform());
session.setAppName(request.getAppName());
if (request.getPlatform() == ProtoConstants.Platform.Platform_iOS && request.getPushType() == 2) {
LOG.info("Set device token, userId {}", fromUser);
session.setVoipDeviceToken(request.getDeviceToken());
m_sessionsStore.updateSessionToken(session, true);
} else {
LOG.info("Set device token, userId {}, platform {}, pushType {}", fromUser, session.getPlatform(), request.getPushType());
session.setDeviceToken(request.getDeviceToken());
session.setPushType(request.getPushType());
m_sessionsStore.updateSessionToken(session, false);
@@ -21,7 +21,7 @@ import java.util.List;
public class UserSearchHandler extends IMHandler<WFCMessage.SearchUserRequest> {
@Override
public ErrorCode action(ByteBuf ackPayload, String clientID, String fromUser, ProtoConstants.RequestSourceType requestSourceType, WFCMessage.SearchUserRequest request, Qos1PublishHandler.IMCallback callback) {
List<WFCMessage.User> users = m_messagesStore.searchUser(request.getKeyword(), request.getFuzzy(), request.getPage());
List<WFCMessage.User> users = m_messagesStore.searchUser(request.getKeyword(), request.getFuzzy(), request.getType(), request.getPage());
WFCMessage.SearchUserResult.Builder builder = WFCMessage.SearchUserResult.newBuilder();
builder.addAllEntry(users);
byte[] data = builder.build().toByteArray();
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More